1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tv.tuner.ts;
18 
19 import android.media.tv.TvContentRating;
20 import android.media.tv.TvContract.Programs.Genres;
21 import android.text.TextUtils;
22 import android.util.Log;
23 import android.util.SparseArray;
24 
25 import com.android.tv.tuner.data.nano.Channel;
26 import com.android.tv.tuner.data.PsiData.PatItem;
27 import com.android.tv.tuner.data.PsiData.PmtItem;
28 import com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor;
29 import com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor;
30 import com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor;
31 import com.android.tv.tuner.data.PsipData.EitItem;
32 import com.android.tv.tuner.data.PsipData.EttItem;
33 import com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor;
34 import com.android.tv.tuner.data.PsipData.GenreDescriptor;
35 import com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor;
36 import com.android.tv.tuner.data.PsipData.MgtItem;
37 import com.android.tv.tuner.data.PsipData.PsipSection;
38 import com.android.tv.tuner.data.PsipData.RatingRegion;
39 import com.android.tv.tuner.data.PsipData.RegionalRating;
40 import com.android.tv.tuner.data.PsipData.TsDescriptor;
41 import com.android.tv.tuner.data.PsipData.VctItem;
42 import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
43 import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
44 import com.android.tv.tuner.util.ByteArrayBuffer;
45 
46 import com.ibm.icu.text.UnicodeDecompressor;
47 
48 import java.io.UnsupportedEncodingException;
49 import java.nio.charset.Charset;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.List;
55 
56 /**
57  * Parses ATSC PSIP sections.
58  */
59 public class SectionParser {
60     private static final String TAG = "SectionParser";
61     private static final boolean DEBUG = false;
62 
63     private static final byte TABLE_ID_PAT = (byte) 0x00;
64     private static final byte TABLE_ID_PMT = (byte) 0x02;
65     private static final byte TABLE_ID_MGT = (byte) 0xc7;
66     private static final byte TABLE_ID_TVCT = (byte) 0xc8;
67     private static final byte TABLE_ID_CVCT = (byte) 0xc9;
68     private static final byte TABLE_ID_EIT = (byte) 0xcb;
69     private static final byte TABLE_ID_ETT = (byte) 0xcc;
70 
71     // For details of the structure for the tags of descriptors, see ATSC A/65 Table 6.25.
72     public static final int DESCRIPTOR_TAG_ISO639LANGUAGE = 0x0a;
73     public static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86;
74     public static final int DESCRIPTOR_TAG_CONTENT_ADVISORY = 0x87;
75     public static final int DESCRIPTOR_TAG_AC3_AUDIO_STREAM = 0x81;
76     public static final int DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME = 0xa0;
77     public static final int DESCRIPTOR_TAG_GENRE = 0xab;
78 
79     private static final byte COMPRESSION_TYPE_NO_COMPRESSION = (byte) 0x00;
80     private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00;  // 0x0000 - 0x00ff
81     private static final byte MODE_UTF16 = (byte) 0x3f;
82     private static final byte MODE_SCSU = (byte) 0x3e;
83     private static final int MAX_SHORT_NAME_BYTES = 14;
84 
85     // See ANSI/CEA-766-C.
86     private static final int RATING_REGION_US_TV = 1;
87     private static final int RATING_REGION_KR_TV = 4;
88 
89     // The following values are defined in the live channels app.
90     // See https://developer.android.com/reference/android/media/tv/TvContentRating.html.
91     private static final String RATING_REGION_RATING_SYSTEM_US_TV = "US_TV";
92     private static final String RATING_REGION_RATING_SYSTEM_KR_TV = "KR_TV";
93 
94     private static final String[] RATING_REGION_TABLE_US_TV = {
95         "US_TV_Y", "US_TV_Y7", "US_TV_G", "US_TV_PG", "US_TV_14", "US_TV_MA"
96     };
97 
98     private static final String[] RATING_REGION_TABLE_KR_TV = {
99         "KR_TV_ALL", "KR_TV_7", "KR_TV_12", "KR_TV_15", "KR_TV_19"
100     };
101 
102     /*
103      * The following CRC table is from the code generated by the following command.
104      * $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
105      * To see the details of pycrc, visit http://www.tty1.net/pycrc/index_en.html
106      */
107     public static final int[] CRC_TABLE = {
108         0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
109         0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
110         0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
111         0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
112         0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
113         0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
114         0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
115         0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
116         0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
117         0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
118         0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
119         0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
120         0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
121         0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
122         0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
123         0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
124         0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
125         0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
126         0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
127         0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
128         0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
129         0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
130         0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
131         0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
132         0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
133         0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
134         0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
135         0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
136         0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
137         0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
138         0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
139         0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
140         0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
141         0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
142         0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
143         0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
144         0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
145         0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
146         0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
147         0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
148         0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
149         0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
150         0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
151         0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
152         0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
153         0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
154         0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
155         0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
156         0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
157         0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
158         0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
159         0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
160         0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
161         0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
162         0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
163         0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
164         0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
165         0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
166         0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
167         0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
168         0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
169         0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
170         0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
171         0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
172     };
173 
174     // A table which maps ATSC genres to TIF genres.
175     // See ATSC/65 Table 6.20.
176     private static final String[] CANONICAL_GENRES_TABLE = {
177         null, null, null, null,
178         null, null, null, null,
179         null, null, null, null,
180         null, null, null, null,
181         null, null, null, null,
182         null, null, null, null,
183         null, null, null, null,
184         null, null, null, null,
185         Genres.EDUCATION, Genres.ENTERTAINMENT, Genres.MOVIES, Genres.NEWS,
186         Genres.LIFE_STYLE, Genres.SPORTS, null, Genres.MOVIES,
187         null,
188         Genres.FAMILY_KIDS, Genres.DRAMA, null, Genres.ENTERTAINMENT, Genres.SPORTS,
189         Genres.SPORTS,
190         null, null,
191         Genres.MUSIC, Genres.EDUCATION,
192         null,
193         Genres.COMEDY,
194         null,
195         Genres.MUSIC,
196         null, null,
197         Genres.MOVIES, Genres.ENTERTAINMENT, Genres.NEWS, Genres.DRAMA,
198         Genres.EDUCATION, Genres.MOVIES, Genres.SPORTS, Genres.MOVIES,
199         null,
200         Genres.LIFE_STYLE, Genres.ARTS, Genres.LIFE_STYLE, Genres.SPORTS,
201         null, null,
202         Genres.GAMING, Genres.LIFE_STYLE, Genres.SPORTS,
203         null,
204         Genres.LIFE_STYLE, Genres.EDUCATION, Genres.EDUCATION, Genres.LIFE_STYLE,
205         Genres.SPORTS, Genres.LIFE_STYLE, Genres.MOVIES, Genres.NEWS,
206         null, null, null,
207         Genres.EDUCATION,
208         null, null, null,
209         Genres.EDUCATION,
210         null, null, null,
211         Genres.DRAMA, Genres.MUSIC, Genres.MOVIES,
212         null,
213         Genres.ANIMAL_WILDLIFE,
214         null, null,
215         Genres.PREMIER,
216         null, null, null, null,
217         Genres.SPORTS, Genres.ARTS,
218         null, null, null,
219         Genres.MOVIES, Genres.TECH_SCIENCE, Genres.DRAMA,
220         null,
221         Genres.SHOPPING, Genres.DRAMA,
222         null,
223         Genres.MOVIES, Genres.ENTERTAINMENT, Genres.TECH_SCIENCE, Genres.SPORTS,
224         Genres.TRAVEL, Genres.ENTERTAINMENT, Genres.ARTS, Genres.NEWS,
225         null,
226         Genres.ARTS, Genres.SPORTS, Genres.SPORTS, Genres.NEWS,
227         Genres.SPORTS, Genres.SPORTS, Genres.SPORTS, Genres.FAMILY_KIDS,
228         Genres.FAMILY_KIDS, Genres.MOVIES,
229         null,
230         Genres.TECH_SCIENCE, Genres.MUSIC,
231         null,
232         Genres.SPORTS, Genres.FAMILY_KIDS, Genres.NEWS, Genres.SPORTS,
233         Genres.NEWS, Genres.SPORTS, Genres.ANIMAL_WILDLIFE,
234         null,
235         Genres.MUSIC, Genres.NEWS, Genres.SPORTS,
236         null,
237         Genres.NEWS, Genres.NEWS, Genres.NEWS, Genres.NEWS,
238         Genres.SPORTS, Genres.MOVIES, Genres.ARTS, Genres.ANIMAL_WILDLIFE,
239         Genres.MUSIC, Genres.MUSIC, Genres.MOVIES, Genres.EDUCATION,
240         Genres.DRAMA, Genres.SPORTS, Genres.SPORTS, Genres.SPORTS,
241         Genres.SPORTS,
242         null,
243         Genres.SPORTS, Genres.SPORTS,
244     };
245 
246     // A table which contains ATSC categorical genre code assignments.
247     // See ATSC/65 Table 6.20.
248     private static final String[] BROADCAST_GENRES_TABLE = new String[] {
249             null, null, null, null,
250             null, null, null, null,
251             null, null, null, null,
252             null, null, null, null,
253             null, null, null, null,
254             null, null, null, null,
255             null, null, null, null,
256             null, null, null, null,
257             "Education", "Entertainment", "Movie", "News",
258             "Religious", "Sports", "Other", "Action",
259             "Advertisement", "Animated", "Anthology", "Automobile",
260             "Awards", "Baseball", "Basketball", "Bulletin",
261             "Business", "Classical", "College", "Combat",
262             "Comedy", "Commentary", "Concert", "Consumer",
263             "Contemporary", "Crime", "Dance", "Documentary",
264             "Drama", "Elementary", "Erotica", "Exercise",
265             "Fantasy", "Farm", "Fashion", "Fiction",
266             "Food", "Football", "Foreign", "Fund Raiser",
267             "Game/Quiz", "Garden", "Golf", "Government",
268             "Health", "High School", "History", "Hobby",
269             "Hockey", "Home", "Horror", "Information",
270             "Instruction", "International", "Interview", "Language",
271             "Legal", "Live", "Local", "Math",
272             "Medical", "Meeting", "Military", "Miniseries",
273             "Music", "Mystery", "National", "Nature",
274             "Police", "Politics", "Premier", "Prerecorded",
275             "Product", "Professional", "Public", "Racing",
276             "Reading", "Repair", "Repeat", "Review",
277             "Romance", "Science", "Series", "Service",
278             "Shopping", "Soap Opera", "Special", "Suspense",
279             "Talk", "Technical", "Tennis", "Travel",
280             "Variety", "Video", "Weather", "Western",
281             "Art", "Auto Racing", "Aviation", "Biography",
282             "Boating", "Bowling", "Boxing", "Cartoon",
283             "Children", "Classic Film", "Community", "Computers",
284             "Country Music", "Court", "Extreme Sports", "Family",
285             "Financial", "Gymnastics", "Headlines", "Horse Racing",
286             "Hunting/Fishing/Outdoors", "Independent", "Jazz", "Magazine",
287             "Motorcycle Racing", "Music/Film/Books", "News-International", "News-Local",
288             "News-National", "News-Regional", "Olympics", "Original",
289             "Performing Arts", "Pets/Animals", "Pop", "Rock & Roll",
290             "Sci-Fi", "Self Improvement", "Sitcom", "Skating",
291             "Skiing", "Soccer", "Track/Field", "True",
292             "Volleyball", "Wrestling",
293     };
294 
295     // Audio language code map from ISO 639-2/B to 639-2/T, in order to show correct audio language.
296     private static final HashMap<String, String> ISO_LANGUAGE_CODE_MAP;
297     static {
298         ISO_LANGUAGE_CODE_MAP = new HashMap<>();
299         ISO_LANGUAGE_CODE_MAP.put("alb", "sqi");
300         ISO_LANGUAGE_CODE_MAP.put("arm", "hye");
301         ISO_LANGUAGE_CODE_MAP.put("baq", "eus");
302         ISO_LANGUAGE_CODE_MAP.put("bur", "mya");
303         ISO_LANGUAGE_CODE_MAP.put("chi", "zho");
304         ISO_LANGUAGE_CODE_MAP.put("cze", "ces");
305         ISO_LANGUAGE_CODE_MAP.put("dut", "nld");
306         ISO_LANGUAGE_CODE_MAP.put("fre", "fra");
307         ISO_LANGUAGE_CODE_MAP.put("geo", "kat");
308         ISO_LANGUAGE_CODE_MAP.put("ger", "deu");
309         ISO_LANGUAGE_CODE_MAP.put("gre", "ell");
310         ISO_LANGUAGE_CODE_MAP.put("ice", "isl");
311         ISO_LANGUAGE_CODE_MAP.put("mac", "mkd");
312         ISO_LANGUAGE_CODE_MAP.put("mao", "mri");
313         ISO_LANGUAGE_CODE_MAP.put("may", "msa");
314         ISO_LANGUAGE_CODE_MAP.put("per", "fas");
315         ISO_LANGUAGE_CODE_MAP.put("rum", "ron");
316         ISO_LANGUAGE_CODE_MAP.put("slo", "slk");
317         ISO_LANGUAGE_CODE_MAP.put("tib", "bod");
318         ISO_LANGUAGE_CODE_MAP.put("wel", "cym");
319         ISO_LANGUAGE_CODE_MAP.put("esl", "spa"); // Special entry for channel 9-1 KQED in bay area.
320     }
321 
322     // Containers to store the last version numbers of the PSIP sections.
323     private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
324     private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
325 
326     public interface OutputListener {
onPatParsed(List<PatItem> items)327         void onPatParsed(List<PatItem> items);
onPmtParsed(int programNumber, List<PmtItem> items)328         void onPmtParsed(int programNumber, List<PmtItem> items);
onMgtParsed(List<MgtItem> items)329         void onMgtParsed(List<MgtItem> items);
onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber)330         void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
onEitParsed(int sourceId, List<EitItem> items)331         void onEitParsed(int sourceId, List<EitItem> items);
onEttParsed(int sourceId, List<EttItem> descriptions)332         void onEttParsed(int sourceId, List<EttItem> descriptions);
333     }
334 
335     private final OutputListener mListener;
336 
SectionParser(OutputListener listener)337     public SectionParser(OutputListener listener) {
338         mListener = listener;
339     }
340 
parseSections(ByteArrayBuffer data)341     public void parseSections(ByteArrayBuffer data) {
342         int pos = 0;
343         while (pos + 3 <= data.length()) {
344             if ((data.byteAt(pos) & 0xff) == 0xff) {
345                 // Clear stuffing bytes according to H222.0 section 2.4.4.
346                 data.setLength(0);
347                 break;
348             }
349             int sectionLength =
350                     (((data.byteAt(pos + 1) & 0x0f) << 8) | (data.byteAt(pos + 2) & 0xff)) + 3;
351             if (pos + sectionLength > data.length()) {
352                 break;
353             }
354             if (DEBUG) {
355                 Log.d(TAG, "parseSections 0x" + Integer.toHexString(data.byteAt(pos) & 0xff));
356             }
357             parseSection(Arrays.copyOfRange(data.buffer(), pos, pos + sectionLength));
358             pos += sectionLength;
359         }
360         if (mListener != null) {
361             for (int i = 0; i < mParsedEttItems.size(); ++i) {
362                 int sourceId = mParsedEttItems.keyAt(i);
363                 List<EttItem> descriptions = mParsedEttItems.valueAt(i);
364                 mListener.onEttParsed(sourceId, descriptions);
365             }
366         }
367         mParsedEttItems.clear();
368     }
369 
parseSection(byte[] data)370     private void parseSection(byte[] data) {
371         if (!checkSanity(data)) {
372             Log.d(TAG, "Bad CRC!");
373             return;
374         }
375         PsipSection section = PsipSection.create(data);
376         if (section == null) {
377             return;
378         }
379 
380         // The currentNextIndicator indicates that the section sent is currently applicable.
381         if (!section.getCurrentNextIndicator()) {
382             return;
383         }
384         int versionNumber = (data[5] & 0x3e) >> 1;
385         Integer oldVersionNumber = mSectionVersionMap.get(section);
386 
387         // The versionNumber shall be incremented when a change in the information carried within
388         // the section occurs.
389         if (oldVersionNumber != null && versionNumber == oldVersionNumber) {
390             return;
391         }
392         boolean result = false;
393         switch (data[0]) {
394             case TABLE_ID_PAT:
395                 result = parsePAT(data);
396                 break;
397             case TABLE_ID_PMT:
398                 result = parsePMT(data);
399                 break;
400             case TABLE_ID_MGT:
401                 result = parseMGT(data);
402                 break;
403             case TABLE_ID_TVCT:
404             case TABLE_ID_CVCT:
405                 result = parseVCT(data);
406                 break;
407             case TABLE_ID_EIT:
408                 result = parseEIT(data);
409                 break;
410             case TABLE_ID_ETT:
411                 result = parseETT(data);
412                 break;
413             default:
414                 break;
415         }
416         if (result) {
417             mSectionVersionMap.put(section, versionNumber);
418         }
419     }
420 
parsePAT(byte[] data)421     private boolean parsePAT(byte[] data) {
422         if (DEBUG) {
423             Log.d(TAG, "PAT is discovered.");
424         }
425         int pos = 8;
426 
427         List<PatItem> results = new ArrayList<>();
428         for (; pos < data.length - 4; pos = pos + 4) {
429             if (pos > data.length - 4 - 4) {
430                 Log.e(TAG, "Broken PAT.");
431                 return false;
432             }
433             int programNo = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
434             int pmtPid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
435             results.add(new PatItem(programNo, pmtPid));
436         }
437         if (mListener != null) {
438             mListener.onPatParsed(results);
439         }
440         return true;
441     }
442 
parsePMT(byte[] data)443     private boolean parsePMT(byte[] data) {
444         int table_id_ext = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
445         if (DEBUG) {
446             Log.d(TAG, "PMT is discovered. programNo = " + table_id_ext);
447         }
448         if (data.length <= 11) {
449             Log.e(TAG, "Broken PMT.");
450             return false;
451         }
452         int pcrPid = (data[8] & 0x1f) << 8 | data[9];
453         int programInfoLen = (data[10] & 0x0f) << 8 | data[11];
454         int pos = 12;
455         List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + programInfoLen);
456         pos += programInfoLen;
457         if (DEBUG) {
458             Log.d(TAG, "PMT descriptors size: " + descriptors.size());
459         }
460         List<PmtItem> results = new ArrayList<>();
461         for (; pos < data.length - 4;) {
462             if (pos < 0) {
463                 Log.e(TAG, "Broken PMT.");
464                 return false;
465             }
466             int streamType = data[pos] & 0xff;
467             int esPid = (data[pos + 1] & 0x1f) << 8 | (data[pos + 2] & 0xff);
468             int esInfoLen = (data[pos + 3] & 0xf) << 8 | (data[pos + 4] & 0xff);
469             if (data.length < pos + esInfoLen + 5) {
470                 Log.e(TAG, "Broken PMT.");
471                 return false;
472             }
473             descriptors = parseDescriptors(data, pos + 5, pos + 5 + esInfoLen);
474             List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
475             List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
476             PmtItem pmtItem = new PmtItem(streamType, esPid, audioTracks, captionTracks);
477             if (DEBUG) {
478                 Log.d(TAG, "PMT " + pmtItem + " descriptors size: " + descriptors.size());
479             }
480             results.add(pmtItem);
481             pos = pos + esInfoLen + 5;
482         }
483         results.add(new PmtItem(PmtItem.ES_PID_PCR, pcrPid, null, null));
484         if (mListener != null) {
485             mListener.onPmtParsed(table_id_ext, results);
486         }
487         return true;
488     }
489 
parseMGT(byte[] data)490     private boolean parseMGT(byte[] data) {
491         // For details of the structure for MGT, see ATSC A/65 Table 6.2.
492         if (DEBUG) {
493             Log.d(TAG, "MGT is discovered.");
494         }
495         if (data.length <= 10) {
496             Log.e(TAG, "Broken MGT.");
497             return false;
498         }
499         int tablesDefined = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
500         int pos = 11;
501         List<MgtItem> results = new ArrayList<>();
502         for (int i = 0; i < tablesDefined; ++i) {
503             if (data.length <= pos + 10) {
504                 Log.e(TAG, "Broken MGT.");
505                 return false;
506             }
507             int tableType = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
508             int tableTypePid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
509             int descriptorsLength = ((data[pos + 9] & 0x0f) << 8) | (data[pos + 10] & 0xff);
510             pos += 11 + descriptorsLength;
511             results.add(new MgtItem(tableType, tableTypePid));
512         }
513         if ((data[pos] & 0xf0) != 0xf0) {
514             Log.e(TAG, "Broken MGT.");
515             return false;
516         }
517         if (mListener != null) {
518             mListener.onMgtParsed(results);
519         }
520         return true;
521     }
522 
parseVCT(byte[] data)523     private boolean parseVCT(byte[] data) {
524         // For details of the structure for VCT, see ATSC A/65 Table 6.4 and 6.8.
525         if (DEBUG) {
526             Log.d(TAG, "VCT is discovered.");
527         }
528         if (data.length <= 9) {
529             Log.e(TAG, "Broken VCT.");
530             return false;
531         }
532         int numChannelsInSection = (data[9] & 0xff);
533         int sectionNumber = (data[6] & 0xff);
534         int lastSectionNumber = (data[7] & 0xff);
535         if (sectionNumber > lastSectionNumber) {
536             // According to section 6.3.1 of the spec ATSC A/65,
537             // last section number is the largest section number.
538             Log.w(TAG, "Invalid VCT. Section Number " + sectionNumber + " > Last Section Number "
539                     + lastSectionNumber);
540             return false;
541         }
542         int pos = 10;
543         List<VctItem> results = new ArrayList<>();
544         for (int i = 0; i < numChannelsInSection; ++i) {
545             if (data.length <= pos + 31) {
546                 Log.e(TAG, "Broken VCT.");
547                 return false;
548             }
549             String shortName = "";
550             int shortNameSize = getShortNameSize(data, pos);
551             try {
552                 shortName = new String(
553                         Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
554             } catch (UnsupportedEncodingException e) {
555                 Log.e(TAG, "Broken VCT.", e);
556                 return false;
557             }
558             if ((data[pos + 14] & 0xf0) != 0xf0) {
559                 Log.e(TAG, "Broken VCT.");
560                 return false;
561             }
562             int majorNumber = ((data[pos + 14] & 0x0f) << 6) | ((data[pos + 15] & 0xff) >> 2);
563             int minorNumber = ((data[pos + 15] & 0x03) << 8) | (data[pos + 16] & 0xff);
564             if ((majorNumber & 0x3f0) == 0x3f0) {
565                 // If the six MSBs are 111111, these indicate that there is only one-part channel
566                 // number. To see details, refer A/65 Section 6.3.2.
567                 majorNumber = ((majorNumber & 0xf) << 10) + minorNumber;
568                 minorNumber = 0;
569             }
570             int channelTsid = ((data[pos + 22] & 0xff) << 8) | (data[pos + 23] & 0xff);
571             int programNumber = ((data[pos + 24] & 0xff) << 8) | (data[pos + 25] & 0xff);
572             boolean accessControlled = (data[pos + 26] & 0x20) != 0;
573             boolean hidden = (data[pos + 26] & 0x10) != 0;
574             int serviceType = (data[pos + 27] & 0x3f);
575             int sourceId = ((data[pos + 28] & 0xff) << 8) | (data[pos + 29] & 0xff);
576             int descriptorsPos = pos + 32;
577             int descriptorsLength = ((data[pos + 30] & 0x03) << 8) | (data[pos + 31] & 0xff);
578             pos += 32 + descriptorsLength;
579             if (data.length < pos) {
580                 Log.e(TAG, "Broken VCT.");
581                 return false;
582             }
583             List<TsDescriptor> descriptors = parseDescriptors(
584                     data, descriptorsPos, descriptorsPos + descriptorsLength);
585             String longName = null;
586             for (TsDescriptor descriptor : descriptors) {
587                 if (descriptor instanceof ExtendedChannelNameDescriptor) {
588                     ExtendedChannelNameDescriptor extendedChannelNameDescriptor =
589                             (ExtendedChannelNameDescriptor) descriptor;
590                     longName = extendedChannelNameDescriptor.getLongChannelName();
591                     break;
592                 }
593             }
594             if (DEBUG) {
595                 Log.d(TAG, String.format(
596                         "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
597                                 + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
598                         shortName, longName, serviceType, channelTsid, programNumber, majorNumber,
599                         minorNumber, accessControlled, hidden, descriptors.size()));
600             }
601             if (!accessControlled && !hidden && (serviceType == Channel.SERVICE_TYPE_ATSC_AUDIO ||
602                     serviceType == Channel.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION ||
603                     serviceType == Channel.SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
604                 // Hide hidden, encrypted, or unsupported ATSC service type channels
605                 results.add(new VctItem(shortName, longName, serviceType, channelTsid,
606                         programNumber, majorNumber, minorNumber, sourceId));
607             }
608         }
609         // Skip the remaining descriptor part which we don't use.
610 
611         if (mListener != null) {
612             mListener.onVctParsed(results, sectionNumber, lastSectionNumber);
613         }
614         return true;
615     }
616 
parseEIT(byte[] data)617     private boolean parseEIT(byte[] data) {
618         // For details of the structure for EIT, see ATSC A/65 Table 6.11.
619         if (DEBUG) {
620             Log.d(TAG, "EIT is discovered.");
621         }
622         if (data.length <= 9) {
623             Log.e(TAG, "Broken EIT.");
624             return false;
625         }
626         int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
627         int numEventsInSection = (data[9] & 0xff);
628 
629         int pos = 10;
630         List<EitItem> results = new ArrayList<>();
631         for (int i = 0; i < numEventsInSection; ++i) {
632             if (data.length <= pos + 9) {
633                 Log.e(TAG, "Broken EIT.");
634                 return false;
635             }
636             if ((data[pos] & 0xc0) != 0xc0) {
637                 Log.e(TAG, "Broken EIT.");
638                 return false;
639             }
640             int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
641             long startTime = ((data[pos + 2] & (long) 0xff) << 24) | ((data[pos + 3] & 0xff) << 16)
642                     | ((data[pos + 4] & 0xff) << 8) | (data[pos + 5] & 0xff);
643             int lengthInSecond = ((data[pos + 6] & 0x0f) << 16)
644                     | ((data[pos + 7] & 0xff) << 8) | (data[pos + 8] & 0xff);
645             int titleLength = (data[pos + 9] & 0xff);
646             if (data.length <= pos + 10 + titleLength + 1) {
647                 Log.e(TAG, "Broken EIT.");
648                 return false;
649             }
650             String titleText = "";
651             if (titleLength > 0) {
652                 titleText = extractText(data, pos + 10);
653             }
654             if ((data[pos + 10 + titleLength] & 0xf0) != 0xf0) {
655                 Log.e(TAG, "Broken EIT.");
656                 return false;
657             }
658             int descriptorsLength = ((data[pos + 10 + titleLength] & 0x0f) << 8)
659                     | (data[pos + 10 + titleLength + 1] & 0xff);
660             int descriptorsPos = pos + 10 + titleLength + 2;
661             if (data.length < descriptorsPos + descriptorsLength) {
662                 Log.e(TAG, "Broken EIT.");
663                 return false;
664             }
665             List<TsDescriptor> descriptors = parseDescriptors(
666                     data, descriptorsPos, descriptorsPos + descriptorsLength);
667             if (DEBUG) {
668                 Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
669             }
670             String contentRating = generateContentRating(descriptors);
671             String broadcastGenre = generateBroadcastGenre(descriptors);
672             String canonicalGenre = generateCanonicalGenre(descriptors);
673             List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
674             List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
675             pos += 10 + titleLength + 2 + descriptorsLength;
676             results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
677                     startTime, lengthInSecond, contentRating, audioTracks, captionTracks,
678                     broadcastGenre, canonicalGenre, null));
679         }
680         if (mListener != null) {
681             mListener.onEitParsed(sourceId, results);
682         }
683         return true;
684     }
685 
parseETT(byte[] data)686     private boolean parseETT(byte[] data) {
687         // For details of the structure for ETT, see ATSC A/65 Table 6.13.
688         if (DEBUG) {
689             Log.d(TAG, "ETT is discovered.");
690         }
691         if (data.length <= 12) {
692             Log.e(TAG, "Broken ETT.");
693             return false;
694         }
695         int sourceId = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
696         int eventId = (((data[11] & 0xff) << 8) | (data[12] & 0xff)) >> 2;
697         String text = extractText(data, 13);
698         List<EttItem> ettItems = mParsedEttItems.get(sourceId);
699         if (ettItems == null) {
700             ettItems = new ArrayList<>();
701             mParsedEttItems.put(sourceId, ettItems);
702         }
703         ettItems.add(new EttItem(eventId, text));
704         return true;
705     }
706 
generateAudioTracks(List<TsDescriptor> descriptors)707     private static List<AtscAudioTrack> generateAudioTracks(List<TsDescriptor> descriptors) {
708         // The list of audio tracks sent is located at both AC3 Audio descriptor and ISO 639
709         // Language descriptor.
710         List<AtscAudioTrack> ac3Tracks = new ArrayList<>();
711         List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
712         for (TsDescriptor descriptor : descriptors) {
713             if (descriptor instanceof Ac3AudioDescriptor) {
714                 Ac3AudioDescriptor audioDescriptor =
715                         (Ac3AudioDescriptor) descriptor;
716                 AtscAudioTrack audioTrack = new AtscAudioTrack();
717                 if (audioDescriptor.getLanguage() != null) {
718                     audioTrack.language = audioDescriptor.getLanguage();
719                 }
720                 audioTrack.audioType = AtscAudioTrack.AUDIOTYPE_UNDEFINED;
721                 audioTrack.channelCount = audioDescriptor.getNumChannels();
722                 audioTrack.sampleRate = audioDescriptor.getSampleRate();
723                 ac3Tracks.add(audioTrack);
724             }
725         }
726         for (TsDescriptor descriptor : descriptors) {
727             if (descriptor instanceof Iso639LanguageDescriptor) {
728                 Iso639LanguageDescriptor iso639LanguageDescriptor =
729                         (Iso639LanguageDescriptor) descriptor;
730                 iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks());
731             }
732         }
733 
734         // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate
735         // while a ISO 639 Language descriptor only has a audio type, which describes a main use
736         // case of its audio track.
737         // Some channels contain only AC3 audio stream descriptors with valid language values.
738         // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language
739         // descriptor per audio track, and those AC3 audio stream descriptors often have a null
740         // value of language field.
741         // Combines two descriptors into one in order to gather more audio track specific
742         // information as much as possible.
743         List<AtscAudioTrack> tracks = new ArrayList<>();
744         if (!ac3Tracks.isEmpty() && !iso639LanguageTracks.isEmpty()
745                 && ac3Tracks.size() != iso639LanguageTracks.size()) {
746             // This shouldn't be happen. In here, it handles two cases. The first case is that the
747             // only one type of descriptors arrives. The second case is that the two types of
748             // descriptors have the same number of tracks.
749             Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size");
750             return tracks;
751         }
752         int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size());
753         for (int i = 0; i < size; ++i) {
754             AtscAudioTrack audioTrack = null;
755             if (i < ac3Tracks.size()) {
756                 audioTrack = ac3Tracks.get(i);
757             }
758             if (i < iso639LanguageTracks.size()) {
759                 if (audioTrack == null) {
760                     audioTrack = iso639LanguageTracks.get(i);
761                 } else {
762                     AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i);
763                     if (audioTrack.language == null || TextUtils.equals(audioTrack.language, "")) {
764                         audioTrack.language = iso639LanguageTrack.language;
765                     }
766                     audioTrack.audioType = iso639LanguageTrack.audioType;
767                 }
768             }
769             String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.language);
770             if (language != null) {
771                 audioTrack.language = language;
772             }
773             tracks.add(audioTrack);
774         }
775         return tracks;
776     }
777 
generateCaptionTracks(List<TsDescriptor> descriptors)778     private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) {
779         List<AtscCaptionTrack> services = new ArrayList<>();
780         for (TsDescriptor descriptor : descriptors) {
781             if (descriptor instanceof CaptionServiceDescriptor) {
782                 CaptionServiceDescriptor captionServiceDescriptor =
783                         (CaptionServiceDescriptor) descriptor;
784                 services.addAll(captionServiceDescriptor.getCaptionTracks());
785             }
786         }
787         return services;
788     }
789 
generateContentRating(List<TsDescriptor> descriptors)790     private static String generateContentRating(List<TsDescriptor> descriptors) {
791         List<String> contentRatings = new ArrayList<>();
792         for (TsDescriptor descriptor : descriptors) {
793             if (descriptor instanceof ContentAdvisoryDescriptor) {
794                 ContentAdvisoryDescriptor contentAdvisoryDescriptor =
795                         (ContentAdvisoryDescriptor) descriptor;
796                 for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) {
797                     for (RegionalRating index : ratingRegion.getRegionalRatings()) {
798                         String ratingSystem = null;
799                         String rating = null;
800                         switch (ratingRegion.getName()) {
801                             case RATING_REGION_US_TV:
802                                 ratingSystem = RATING_REGION_RATING_SYSTEM_US_TV;
803                                 if (index.getDimension() == 0 && index.getRating() >= 0
804                                         && index.getRating() < RATING_REGION_TABLE_US_TV.length) {
805                                     rating = RATING_REGION_TABLE_US_TV[index.getRating()];
806                                 }
807                                 break;
808                             case RATING_REGION_KR_TV:
809                                 ratingSystem = RATING_REGION_RATING_SYSTEM_KR_TV;
810                                 if (index.getDimension() == 0 && index.getRating() >= 0
811                                         && index.getRating() < RATING_REGION_TABLE_KR_TV.length) {
812                                     rating = RATING_REGION_TABLE_KR_TV[index.getRating()];
813                                 }
814                                 break;
815                             default:
816                                 break;
817                         }
818                         if (ratingSystem != null && rating != null) {
819                             contentRatings.add(TvContentRating
820                                     .createRating("com.android.tv", ratingSystem, rating)
821                                     .flattenToString());
822                         }
823                     }
824                 }
825             }
826         }
827         return TextUtils.join(",", contentRatings);
828     }
829 
generateBroadcastGenre(List<TsDescriptor> descriptors)830     private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
831         for (TsDescriptor descriptor : descriptors) {
832             if (descriptor instanceof GenreDescriptor) {
833                 GenreDescriptor genreDescriptor =
834                         (GenreDescriptor) descriptor;
835                 return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
836             }
837         }
838         return null;
839     }
840 
generateCanonicalGenre(List<TsDescriptor> descriptors)841     private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
842         for (TsDescriptor descriptor : descriptors) {
843             if (descriptor instanceof GenreDescriptor) {
844                 GenreDescriptor genreDescriptor =
845                         (GenreDescriptor) descriptor;
846                 return Genres.encode(genreDescriptor.getCanonicalGenres());
847             }
848         }
849         return null;
850     }
851 
parseDescriptors(byte[] data, int offset, int limit)852     private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) {
853         // For details of the structure for descriptors, see ATSC A/65 Section 6.9.
854         List<TsDescriptor> descriptors = new ArrayList<>();
855         if (data.length < limit) {
856             return descriptors;
857         }
858         int pos = offset;
859         while (pos + 1 < limit) {
860             int tag = data[pos] & 0xff;
861             int length = data[pos + 1] & 0xff;
862             if (length <= 0) {
863                 break;
864             }
865             if (limit < pos + length + 2) {
866                 break;
867             }
868             if (DEBUG) {
869                 Log.d(TAG, String.format("Descriptor tag: %02x", tag));
870             }
871             TsDescriptor descriptor = null;
872             switch (tag) {
873                 case DESCRIPTOR_TAG_CONTENT_ADVISORY:
874                     descriptor = parseContentAdvisory(data, pos, pos + length + 2);
875                     break;
876 
877                 case DESCRIPTOR_TAG_CAPTION_SERVICE:
878                     descriptor = parseCaptionService(data, pos, pos + length + 2);
879                     break;
880 
881                 case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME:
882                     descriptor = parseLongChannelName(data, pos, pos + length + 2);
883                     break;
884 
885                 case DESCRIPTOR_TAG_GENRE:
886                     descriptor = parseGenre(data, pos, pos + length + 2);
887                     break;
888 
889                 case DESCRIPTOR_TAG_AC3_AUDIO_STREAM:
890                     descriptor = parseAc3AudioStream(data, pos, pos + length + 2);
891                     break;
892 
893                 case DESCRIPTOR_TAG_ISO639LANGUAGE:
894                     descriptor = parseIso639Language(data, pos, pos + length + 2);
895                     break;
896 
897                 default:
898             }
899             if (descriptor != null) {
900                 if (DEBUG) {
901                     Log.d(TAG, "Descriptor parsed: " + descriptor);
902                 }
903                 descriptors.add(descriptor);
904             }
905             pos += length + 2;
906         }
907         return descriptors;
908     }
909 
parseIso639Language(byte[] data, int pos, int limit)910     private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) {
911         // For the details of the structure of ISO 639 language descriptor,
912         // see ISO13818-1 second edition Section 2.6.18.
913         pos += 2;
914         List<AtscAudioTrack> audioTracks = new ArrayList<>();
915         while (pos + 4 <= limit) {
916             if (limit <= pos + 3) {
917                 Log.e(TAG, "Broken Iso639Language.");
918                 return null;
919             }
920             String language = new String(data, pos, 3);
921             int audioType = data[pos + 3] & 0xff;
922             AtscAudioTrack audioTrack = new AtscAudioTrack();
923             audioTrack.language = language;
924             audioTrack.audioType = audioType;
925             audioTracks.add(audioTrack);
926             pos += 4;
927         }
928         return new Iso639LanguageDescriptor(audioTracks);
929     }
930 
parseCaptionService(byte[] data, int pos, int limit)931     private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) {
932         // For the details of the structure of caption service descriptor,
933         // see ATSC A/65 Section 6.9.2.
934         if (limit <= pos + 2) {
935             Log.e(TAG, "Broken CaptionServiceDescriptor.");
936             return null;
937         }
938         List<AtscCaptionTrack> services = new ArrayList<>();
939         pos += 2;
940         int numberServices = data[pos] & 0x1f;
941         ++pos;
942         if (limit < pos + numberServices * 6) {
943             Log.e(TAG, "Broken CaptionServiceDescriptor.");
944             return null;
945         }
946         for (int i = 0; i < numberServices; ++i) {
947             String language = new String(Arrays.copyOfRange(data, pos, pos + 3));
948             pos += 3;
949             boolean ccType = (data[pos] & 0x80) != 0;
950             if (!ccType) {
951                 continue;
952             }
953             int captionServiceNumber = data[pos] & 0x3f;
954             ++pos;
955             boolean easyReader = (data[pos] & 0x80) != 0;
956             boolean wideAspectRatio = (data[pos] & 0x40) != 0;
957             byte[] reserved = new byte[2];
958             reserved[0] = (byte) (data[pos] << 2);
959             reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6);
960             reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2);
961             pos += 2;
962             AtscCaptionTrack captionTrack = new AtscCaptionTrack();
963             captionTrack.language = language;
964             captionTrack.serviceNumber = captionServiceNumber;
965             captionTrack.easyReader = easyReader;
966             captionTrack.wideAspectRatio = wideAspectRatio;
967             services.add(captionTrack);
968         }
969         return new CaptionServiceDescriptor(services);
970     }
971 
parseContentAdvisory(byte[] data, int pos, int limit)972     private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) {
973         // For details of the structure for content advisory descriptor, see A/65 Table 6.27.
974         if (limit <= pos + 2) {
975             Log.e(TAG, "Broken ContentAdvisory");
976             return null;
977         }
978         int count = data[pos + 2] & 0x3f;
979         pos += 3;
980         List<RatingRegion> ratingRegions = new ArrayList<>();
981         for (int i = 0; i < count; ++i) {
982             if (limit <= pos + 1) {
983                 Log.e(TAG, "Broken ContentAdvisory");
984                 return null;
985             }
986             List<RegionalRating> indices = new ArrayList<>();
987             int ratingRegion = data[pos] & 0xff;
988             int dimensionCount = data[pos + 1] & 0xff;
989             pos += 2;
990             for (int j = 0; j < dimensionCount; ++j) {
991                 if (limit <= pos + 1) {
992                     Log.e(TAG, "Broken ContentAdvisory");
993                     return null;
994                 }
995                 int dimensionIndex = data[pos] & 0xff;
996                 int ratingValue = data[pos + 1] & 0x0f;
997                 pos += 2;
998                 indices.add(new RegionalRating(dimensionIndex, ratingValue));
999             }
1000             if (limit <= pos) {
1001                 Log.e(TAG, "Broken ContentAdvisory");
1002                 return null;
1003             }
1004             int ratingDescriptionLength = data[pos] & 0xff;
1005             ++pos;
1006             if (limit < pos + ratingDescriptionLength) {
1007                 Log.e(TAG, "Broken ContentAdvisory");
1008                 return null;
1009             }
1010             String ratingDescription = extractText(data, pos);
1011             pos += ratingDescriptionLength;
1012             ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices));
1013         }
1014         return new ContentAdvisoryDescriptor(ratingRegions);
1015     }
1016 
parseLongChannelName(byte[] data, int pos, int limit)1017     private static ExtendedChannelNameDescriptor parseLongChannelName(byte[] data, int pos,
1018             int limit) {
1019         if (limit <= pos + 2) {
1020             Log.e(TAG, "Broken ExtendedChannelName.");
1021             return null;
1022         }
1023         pos += 2;
1024         String text = extractText(data, pos);
1025         if (text == null) {
1026             Log.e(TAG, "Broken ExtendedChannelName.");
1027             return null;
1028         }
1029         return new ExtendedChannelNameDescriptor(text);
1030     }
1031 
parseGenre(byte[] data, int pos, int limit)1032     private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) {
1033         pos += 2;
1034         int attributeCount = data[pos] & 0x1f;
1035         if (limit <= pos + attributeCount) {
1036             Log.e(TAG, "Broken Genre.");
1037             return null;
1038         }
1039         HashSet<String> broadcastGenreSet = new HashSet<>();
1040         HashSet<String> canonicalGenreSet = new HashSet<>();
1041         for (int i = 0; i < attributeCount; ++i) {
1042             ++pos;
1043             int genreCode = data[pos] & 0xff;
1044             if (genreCode < BROADCAST_GENRES_TABLE.length) {
1045                 String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode];
1046                 if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) {
1047                     broadcastGenreSet.add(broadcastGenre);
1048                 }
1049             }
1050             if (genreCode < CANONICAL_GENRES_TABLE.length) {
1051                 String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode];
1052                 if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) {
1053                     canonicalGenreSet.add(canonicalGenre);
1054                 }
1055             }
1056         }
1057         return new GenreDescriptor(broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
1058                 canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
1059     }
1060 
parseAc3AudioStream(byte[] data, int pos, int limit)1061     private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) {
1062         // For details of the AC3 audio stream descriptor, see A/52 Table A4.1.
1063         if (limit <= pos + 5) {
1064             Log.e(TAG, "Broken AC3 audio stream descriptor.");
1065             return null;
1066         }
1067         pos += 2;
1068         byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5);
1069         byte bsid = (byte) (data[pos] & 0x1f);
1070         ++pos;
1071         byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2);
1072         byte surroundMode = (byte) (data[pos] & 0x03);
1073         ++pos;
1074         byte bsmod = (byte) ((data[pos] & 0xe0) >> 5);
1075         int numChannels = (data[pos] & 0x1e) >> 1;
1076         boolean fullSvc = (data[pos] & 0x01) != 0;
1077         ++pos;
1078         byte langCod = data[pos];
1079         byte langCod2 = 0;
1080         if (numChannels == 0) {
1081             if (limit <= pos) {
1082                 Log.e(TAG, "Broken AC3 audio stream descriptor.");
1083                 return null;
1084             }
1085             ++pos;
1086             langCod2 = data[pos];
1087         }
1088         if (limit <= pos + 1) {
1089             Log.e(TAG, "Broken AC3 audio stream descriptor.");
1090             return null;
1091         }
1092         byte mainId = 0;
1093         byte priority = 0;
1094         byte asvcflags = 0;
1095         ++pos;
1096         if (bsmod < 2) {
1097             mainId = (byte) ((data[pos] & 0xe0) >> 5);
1098             priority = (byte) ((data[pos] & 0x18) >> 3);
1099             if ((data[pos] & 0x07) != 0x07) {
1100                 Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed");
1101                 return null;
1102             }
1103         } else {
1104             asvcflags = data[pos];
1105         }
1106 
1107         // See A/52B Table A3.6 num_channels.
1108         int numEncodedChannels;
1109         switch (numChannels) {
1110             case 1:
1111             case 8:
1112                 numEncodedChannels = 1;
1113                 break;
1114             case 2:
1115             case 9:
1116                 numEncodedChannels = 2;
1117                 break;
1118             case 3:
1119             case 4:
1120             case 10:
1121                 numEncodedChannels = 3;
1122                 break;
1123             case 5:
1124             case 6:
1125             case 11:
1126                 numEncodedChannels = 4;
1127                 break;
1128             case 7:
1129             case 12:
1130                 numEncodedChannels = 5;
1131                 break;
1132             case 13:
1133                 numEncodedChannels = 6;
1134                 break;
1135             default:
1136                 numEncodedChannels = 0;
1137                 break;
1138         }
1139 
1140         if (limit <= pos + 1) {
1141             Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
1142             return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
1143                     numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags,
1144                     null, null, null);
1145         }
1146         ++pos;
1147         int textLen = (data[pos] & 0xfe) >> 1;
1148         boolean textCode = (data[pos] & 0x01) != 0;
1149         ++pos;
1150         String text = "";
1151         if (textLen > 0) {
1152             if (limit < pos + textLen) {
1153                 Log.e(TAG, "Broken AC3 audio stream descriptor");
1154                 return null;
1155             }
1156             if (textCode) {
1157                 text = new String(data, pos, textLen);
1158             } else {
1159                 text = new String(data, pos, textLen, Charset.forName("UTF-16"));
1160             }
1161             pos += textLen;
1162         }
1163         String language = null;
1164         String language2 = null;
1165         if (pos < limit) {
1166             // Many AC3 audio stream descriptors skip the language fields.
1167             boolean languageFlag1 = (data[pos] & 0x80) != 0;
1168             boolean languageFlag2 = (data[pos] & 0x40) != 0;
1169             if ((data[pos] & 0x3f) != 0x3f) {
1170                 Log.e(TAG, "Broken AC3 audio stream descriptor");
1171                 return null;
1172             }
1173             if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) {
1174                 Log.e(TAG, "Broken AC3 audio stream descriptor");
1175                 return null;
1176             }
1177             ++pos;
1178             if (languageFlag1) {
1179                 language = new String(data, pos, 3);
1180                 pos += 3;
1181             }
1182             if (languageFlag2) {
1183                 language2 = new String(data, pos, 3);
1184             }
1185         }
1186 
1187         return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
1188                 numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags, text,
1189                 language, language2);
1190     }
1191 
getShortNameSize(byte[] data, int offset)1192     private static int getShortNameSize(byte[] data, int offset) {
1193         for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) {
1194             if (data[offset + i] == 0 && data[offset + i + 1] == 0) {
1195                 return i;
1196             }
1197         }
1198         return MAX_SHORT_NAME_BYTES;
1199     }
1200 
extractText(byte[] data, int pos)1201     private static String extractText(byte[] data, int pos) {
1202         if (data.length < pos)  {
1203             return null;
1204         }
1205         int numStrings = data[pos] & 0xff;
1206         pos++;
1207         for (int i = 0; i < numStrings; ++i) {
1208             if (data.length <= pos + 3) {
1209                 Log.e(TAG, "Broken text.");
1210                 return null;
1211             }
1212             int numSegments = data[pos + 3] & 0xff;
1213             pos += 4;
1214             for (int j = 0; j < numSegments; ++j) {
1215                 if (data.length <= pos + 2) {
1216                     Log.e(TAG, "Broken text.");
1217                     return null;
1218                 }
1219                 int compressionType = data[pos] & 0xff;
1220                 int mode = data[pos + 1] & 0xff;
1221                 int numBytes = data[pos + 2] & 0xff;
1222                 if (data.length < pos + 3 + numBytes) {
1223                     Log.e(TAG, "Broken text.");
1224                     return null;
1225                 }
1226                 byte[] bytes = Arrays.copyOfRange(data, pos + 3, pos + 3 + numBytes);
1227                 if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
1228                     try {
1229                         switch (mode) {
1230                             case MODE_SELECTED_UNICODE_RANGE_1:
1231                                 return new String(bytes, "ISO-8859-1");
1232                             case MODE_SCSU:
1233                                 return UnicodeDecompressor.decompress(bytes);
1234                             case MODE_UTF16:
1235                                 return new String(bytes, "UTF-16");
1236                         }
1237                     } catch (UnsupportedEncodingException e) {
1238                         Log.e(TAG, "Unsupported text format.", e);
1239                     }
1240                 }
1241                 pos += 3 + numBytes;
1242             }
1243         }
1244         return null;
1245     }
1246 
checkSanity(byte[] data)1247     private static boolean checkSanity(byte[] data) {
1248         if (data.length <= 1) {
1249             return false;
1250         }
1251         boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
1252         if (hasCRC) {
1253             int crc = 0xffffffff;
1254             for(byte b : data) {
1255                 int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
1256                 crc = CRC_TABLE[index] ^ (crc << 8);
1257             }
1258             if(crc != 0){
1259                 return false;
1260             }
1261         }
1262         return true;
1263     }
1264 }
1265