SectionParser.java revision 721bd0da688cd552737fbb753a00597f95103b95
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
17package com.android.tv.tuner.ts;
18
19import android.media.tv.TvContentRating;
20import android.media.tv.TvContract.Programs.Genres;
21import android.text.TextUtils;
22import android.util.Log;
23import android.util.SparseArray;
24
25import com.android.tv.tuner.data.nano.Channel;
26import com.android.tv.tuner.data.PsiData.PatItem;
27import com.android.tv.tuner.data.PsiData.PmtItem;
28import com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor;
29import com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor;
30import com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor;
31import com.android.tv.tuner.data.PsipData.EitItem;
32import com.android.tv.tuner.data.PsipData.EttItem;
33import com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor;
34import com.android.tv.tuner.data.PsipData.GenreDescriptor;
35import com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor;
36import com.android.tv.tuner.data.PsipData.MgtItem;
37import com.android.tv.tuner.data.PsipData.PsipSection;
38import com.android.tv.tuner.data.PsipData.RatingRegion;
39import com.android.tv.tuner.data.PsipData.RegionalRating;
40import com.android.tv.tuner.data.PsipData.TsDescriptor;
41import com.android.tv.tuner.data.PsipData.VctItem;
42import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
43import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
44import com.android.tv.tuner.util.ByteArrayBuffer;
45
46import com.ibm.icu.text.UnicodeDecompressor;
47
48import java.io.UnsupportedEncodingException;
49import java.nio.charset.Charset;
50import java.util.ArrayList;
51import java.util.Arrays;
52import java.util.HashMap;
53import java.util.HashSet;
54import java.util.List;
55
56/**
57 * Parses ATSC PSIP sections.
58 */
59public 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 {
327        void onPatParsed(List<PatItem> items);
328        void onPmtParsed(int programNumber, List<PmtItem> items);
329        void onMgtParsed(List<MgtItem> items);
330        void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
331        void onEitParsed(int sourceId, List<EitItem> items);
332        void onEttParsed(int sourceId, List<EttItem> descriptions);
333    }
334
335    private final OutputListener mListener;
336
337    public SectionParser(OutputListener listener) {
338        mListener = listener;
339    }
340
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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