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.usbtuner.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.usbtuner.data.Channel;
26import com.android.usbtuner.data.PsiData.PatItem;
27import com.android.usbtuner.data.PsiData.PmtItem;
28import com.android.usbtuner.data.PsipData.Ac3AudioDescriptor;
29import com.android.usbtuner.data.PsipData.CaptionServiceDescriptor;
30import com.android.usbtuner.data.PsipData.ContentAdvisoryDescriptor;
31import com.android.usbtuner.data.PsipData.EitItem;
32import com.android.usbtuner.data.PsipData.EttItem;
33import com.android.usbtuner.data.PsipData.ExtendedChannelNameDescriptor;
34import com.android.usbtuner.data.PsipData.GenreDescriptor;
35import com.android.usbtuner.data.PsipData.Iso639LanguageDescriptor;
36import com.android.usbtuner.data.PsipData.MgtItem;
37import com.android.usbtuner.data.PsipData.PsipSection;
38import com.android.usbtuner.data.PsipData.RatingRegion;
39import com.android.usbtuner.data.PsipData.RegionalRating;
40import com.android.usbtuner.data.PsipData.TsDescriptor;
41import com.android.usbtuner.data.PsipData.VctItem;
42import com.android.usbtuner.data.Track.AtscAudioTrack;
43import com.android.usbtuner.data.Track.AtscCaptionTrack;
44import com.android.usbtuner.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    // Containers to store the last version numbers of the PSIP sections.
296    private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
297    private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
298
299    public interface OutputListener {
300        void onPatParsed(List<PatItem> items);
301        void onPmtParsed(int programNumber, List<PmtItem> items);
302        void onMgtParsed(List<MgtItem> items);
303        void onVctParsed(List<VctItem> items);
304        void onEitParsed(int sourceId, List<EitItem> items);
305        void onEttParsed(int sourceId, List<EttItem> descriptions);
306    }
307
308    private OutputListener mListener;
309
310    public SectionParser(OutputListener listener) {
311        mListener = listener;
312    }
313
314    public void parseSections(ByteArrayBuffer data) {
315        int pos = 0;
316        while (pos + 3 <= data.length()) {
317            if ((data.byteAt(pos) & 0xff) == 0xff) {
318                // Clear stuffing bytes according to H222.0 section 2.4.4.
319                data.setLength(0);
320                break;
321            }
322            int sectionLength =
323                    (((data.byteAt(pos + 1) & 0x0f) << 8) | (data.byteAt(pos + 2) & 0xff)) + 3;
324            if (pos + sectionLength > data.length()) {
325                break;
326            }
327            if (DEBUG) {
328                Log.d(TAG, "parseSections 0x" + Integer.toHexString(data.byteAt(pos) & 0xff));
329            }
330            parseSection(Arrays.copyOfRange(data.buffer(), pos, pos + sectionLength));
331            pos += sectionLength;
332        }
333        if (mListener != null) {
334            for (int i = 0; i < mParsedEttItems.size(); ++i) {
335                int sourceId = mParsedEttItems.keyAt(i);
336                List<EttItem> descriptions = mParsedEttItems.valueAt(i);
337                mListener.onEttParsed(sourceId, descriptions);
338            }
339        }
340        mParsedEttItems.clear();
341    }
342
343    private void parseSection(byte[] data) {
344        if (!checkSanity(data)) {
345            Log.d(TAG, "Bad CRC!");
346            return;
347        }
348        PsipSection section = PsipSection.create(data);
349        if (section == null) {
350            return;
351        }
352
353        // The currentNextIndicator indicates that the section sent is currently applicable.
354        if (!section.getCurrentNextIndicator()) {
355            return;
356        }
357        int versionNumber = (data[5] & 0x3e) >> 1;
358        Integer oldVersionNumber = mSectionVersionMap.get(section);
359
360        // The versionNumber shall be incremented when a change in the information carried within
361        // the section occurs.
362        if (oldVersionNumber != null && versionNumber == oldVersionNumber) {
363            return;
364        }
365        boolean result = false;
366        switch (data[0]) {
367            case TABLE_ID_PAT:
368                result = parsePAT(data);
369                break;
370            case TABLE_ID_PMT:
371                result = parsePMT(data);
372                break;
373            case TABLE_ID_MGT:
374                result = parseMGT(data);
375                break;
376            case TABLE_ID_TVCT:
377            case TABLE_ID_CVCT:
378                result = parseVCT(data);
379                break;
380            case TABLE_ID_EIT:
381                result = parseEIT(data);
382                break;
383            case TABLE_ID_ETT:
384                result = parseETT(data);
385                break;
386            default:
387                break;
388        }
389        if (result) {
390            mSectionVersionMap.put(section, versionNumber);
391        }
392    }
393
394    private boolean parsePAT(byte[] data) {
395        if (DEBUG) {
396            Log.d(TAG, "PAT is discovered.");
397        }
398        int pos = 8;
399
400        List<PatItem> results = new ArrayList<>();
401        for (; pos < data.length - 4; pos = pos + 4) {
402            if (pos > data.length - 4 - 4) {
403                Log.e(TAG, "Broken PAT.");
404                return false;
405            }
406            int programNo = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
407            int pmtPid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
408            results.add(new PatItem(programNo, pmtPid));
409        }
410        if (mListener != null) {
411            mListener.onPatParsed(results);
412        }
413        return true;
414    }
415
416    private boolean parsePMT(byte[] data) {
417        int table_id_ext = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
418        if (DEBUG) {
419            Log.d(TAG, "PMT is discovered. programNo = " + table_id_ext);
420        }
421        if (data.length <= 11) {
422            Log.e(TAG, "Broken PMT.");
423            return false;
424        }
425        int pcrPid = (data[8] & 0x1f) << 8 | data[9];
426        int programInfoLen = (data[10] & 0x0f) << 8 | data[11];
427        int pos = 12;
428        List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + programInfoLen);
429        pos += programInfoLen;
430        if (DEBUG) {
431            Log.d(TAG, "PMT descriptors size: " + descriptors.size());
432        }
433        List<PmtItem> results = new ArrayList<>();
434        for (; pos < data.length - 4;) {
435            int streamType = data[pos] & 0xff;
436            int esPid = (data[pos + 1] & 0x1f) << 8 | (data[pos + 2] & 0xff);
437            int esInfoLen = (data[pos + 3] & 0xf) << 8 | (data[pos + 4] & 0xff);
438            if (data.length < pos + esInfoLen + 5) {
439                Log.e(TAG, "Broken PMT.");
440                return false;
441            }
442            descriptors = parseDescriptors(data, pos + 5, pos + 5 + esInfoLen);
443            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
444            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
445            PmtItem pmtItem = new PmtItem(streamType, esPid, audioTracks, captionTracks);
446            if (DEBUG) {
447                Log.d(TAG, "PMT " + pmtItem + " descriptors size: " + descriptors.size());
448            }
449            results.add(pmtItem);
450            pos = pos + esInfoLen + 5;
451        }
452        results.add(new PmtItem(PmtItem.ES_PID_PCR, pcrPid, null, null));
453        if (mListener != null) {
454            mListener.onPmtParsed(table_id_ext, results);
455        }
456        return true;
457    }
458
459    private boolean parseMGT(byte[] data) {
460        // For details of the structure for MGT, see ATSC A/65 Table 6.2.
461        if (DEBUG) {
462            Log.d(TAG, "MGT is discovered.");
463        }
464        if (data.length <= 10) {
465            Log.e(TAG, "Broken MGT.");
466            return false;
467        }
468        int tablesDefined = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
469        int pos = 11;
470        List<MgtItem> results = new ArrayList<>();
471        for (int i = 0; i < tablesDefined; ++i) {
472            if (data.length <= pos + 10) {
473                Log.e(TAG, "Broken MGT.");
474                return false;
475            }
476            int tableType = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
477            int tableTypePid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
478            int descriptorsLength = ((data[pos + 9] & 0x0f) << 8) | (data[pos + 10] & 0xff);
479            pos += 11 + descriptorsLength;
480            results.add(new MgtItem(tableType, tableTypePid));
481        }
482        if ((data[pos] & 0xf0) != 0xf0) {
483            Log.e(TAG, "Broken MGT.");
484            return false;
485        }
486        if (mListener != null) {
487            mListener.onMgtParsed(results);
488        }
489        return true;
490    }
491
492    private boolean parseVCT(byte[] data) {
493        // For details of the structure for VCT, see ATSC A/65 Table 6.4 and 6.8.
494        if (DEBUG) {
495            Log.d(TAG, "VCT is discovered.");
496        }
497        if (data.length <= 9) {
498            Log.e(TAG, "Broken VCT.");
499            return false;
500        }
501        int numChannelsInSection = (data[9] & 0xff);
502        int pos = 10;
503        List<VctItem> results = new ArrayList<>();
504        for (int i = 0; i < numChannelsInSection; ++i) {
505            if (data.length <= pos + 31) {
506                Log.e(TAG, "Broken VCT.");
507                return false;
508            }
509            String shortName = "";
510            int shortNameSize = getShortNameSize(data, pos);
511            try {
512                shortName = new String(
513                        Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
514            } catch (UnsupportedEncodingException e) {
515                Log.e(TAG, "Broken VCT.", e);
516                return false;
517            }
518            if ((data[pos + 14] & 0xf0) != 0xf0) {
519                Log.e(TAG, "Broken VCT.");
520                return false;
521            }
522            int majorNumber = ((data[pos + 14] & 0x0f) << 6) | ((data[pos + 15] & 0xff) >> 2);
523            int minorNumber = ((data[pos + 15] & 0x03) << 8) | (data[pos + 16] & 0xff);
524            if ((majorNumber & 0x3f0) == 0x3f0) {
525                // If the six MSBs are 111111, these indicate that there is only one-part channel
526                // number. To see details, refer A/65 Section 6.3.2.
527                majorNumber = ((majorNumber & 0xf) << 10) + minorNumber;
528                minorNumber = 0;
529            }
530            int channelTsid = ((data[pos + 22] & 0xff) << 8) | (data[pos + 23] & 0xff);
531            int programNumber = ((data[pos + 24] & 0xff) << 8) | (data[pos + 25] & 0xff);
532            boolean accessControlled = (data[pos + 26] & 0x20) != 0;
533            boolean hidden = (data[pos + 26] & 0x10) != 0;
534            int serviceType = (data[pos + 27] & 0x3f);
535            int sourceId = ((data[pos + 28] & 0xff) << 8) | (data[pos + 29] & 0xff);
536            int descriptorsPos = pos + 32;
537            int descriptorsLength = ((data[pos + 30] & 0x03) << 8) | (data[pos + 31] & 0xff);
538            pos += 32 + descriptorsLength;
539            if (data.length < pos) {
540                Log.e(TAG, "Broken VCT.");
541                return false;
542            }
543            List<TsDescriptor> descriptors = parseDescriptors(
544                    data, descriptorsPos, descriptorsPos + descriptorsLength);
545            String longName = null;
546            for (TsDescriptor descriptor : descriptors) {
547                if (descriptor instanceof ExtendedChannelNameDescriptor) {
548                    ExtendedChannelNameDescriptor extendedChannelNameDescriptor =
549                            (ExtendedChannelNameDescriptor) descriptor;
550                    longName = extendedChannelNameDescriptor.getLongChannelName();
551                    break;
552                }
553            }
554            if (DEBUG) {
555                Log.d(TAG, String.format(
556                        "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
557                                + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
558                        shortName, longName, serviceType, channelTsid, programNumber, majorNumber,
559                        minorNumber, accessControlled, hidden, descriptors.size()));
560            }
561            if (!accessControlled && !hidden && (serviceType == Channel.SERVICE_TYPE_ATSC_AUDIO ||
562                    serviceType == Channel.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION ||
563                    serviceType == Channel.SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
564                // Hide hidden, encrypted, or unsupported ATSC service type channels
565                results.add(new VctItem(shortName, longName, serviceType, channelTsid,
566                        programNumber, majorNumber, minorNumber, sourceId));
567            }
568        }
569        if ((data[pos] & 0xfc) != 0xfc) {
570            Log.e(TAG, "Broken VCT.");
571            return false;
572        }
573        if (mListener != null) {
574            mListener.onVctParsed(results);
575        }
576        return true;
577    }
578
579    private boolean parseEIT(byte[] data) {
580        // For details of the structure for EIT, see ATSC A/65 Table 6.11.
581        if (DEBUG) {
582            Log.d(TAG, "EIT is discovered.");
583        }
584        if (data.length <= 9) {
585            Log.e(TAG, "Broken EIT.");
586            return false;
587        }
588        int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
589        int numEventsInSection = (data[9] & 0xff);
590
591        int pos = 10;
592        List<EitItem> results = new ArrayList<>();
593        for (int i = 0; i < numEventsInSection; ++i) {
594            if (data.length <= pos + 9) {
595                Log.e(TAG, "Broken EIT.");
596                return false;
597            }
598            if ((data[pos] & 0xc0) != 0xc0) {
599                Log.e(TAG, "Broken EIT.");
600                return false;
601            }
602            int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
603            long startTime = ((data[pos + 2] & (long) 0xff) << 24) | ((data[pos + 3] & 0xff) << 16)
604                    | ((data[pos + 4] & 0xff) << 8) | (data[pos + 5] & 0xff);
605            int lengthInSecond = ((data[pos + 6] & 0x0f) << 16)
606                    | ((data[pos + 7] & 0xff) << 8) | (data[pos + 8] & 0xff);
607            int titleLength = (data[pos + 9] & 0xff);
608            if (data.length <= pos + 10 + titleLength + 1) {
609                Log.e(TAG, "Broken EIT.");
610                return false;
611            }
612            String titleText = "";
613            if (titleLength > 0) {
614                titleText = extractText(data, pos + 10);
615            }
616            if ((data[pos + 10 + titleLength] & 0xf0) != 0xf0) {
617                Log.e(TAG, "Broken EIT.");
618                return false;
619            }
620            int descriptorsLength = ((data[pos + 10 + titleLength] & 0x0f) << 8)
621                    | (data[pos + 10 + titleLength + 1] & 0xff);
622            int descriptorsPos = pos + 10 + titleLength + 2;
623            if (data.length < descriptorsPos + descriptorsLength) {
624                Log.e(TAG, "Broken EIT.");
625                return false;
626            }
627            List<TsDescriptor> descriptors = parseDescriptors(
628                    data, descriptorsPos, descriptorsPos + descriptorsLength);
629            if (DEBUG) {
630                Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
631            }
632            String contentRating = generateContentRating(descriptors);
633            String broadcastGenre = generateBroadcastGenre(descriptors);
634            String canonicalGenre = generateCanonicalGenre(descriptors);
635            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
636            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
637            pos += 10 + titleLength + 2 + descriptorsLength;
638            results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
639                    startTime, lengthInSecond, contentRating, audioTracks, captionTracks,
640                    broadcastGenre, canonicalGenre, null));
641        }
642        if (mListener != null) {
643            mListener.onEitParsed(sourceId, results);
644        }
645        return true;
646    }
647
648    private boolean parseETT(byte[] data) {
649        // For details of the structure for ETT, see ATSC A/65 Table 6.13.
650        if (DEBUG) {
651            Log.d(TAG, "ETT is discovered.");
652        }
653        if (data.length <= 12) {
654            Log.e(TAG, "Broken ETT.");
655            return false;
656        }
657        int sourceId = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
658        int eventId = (((data[11] & 0xff) << 8) | (data[12] & 0xff)) >> 2;
659        String text = extractText(data, 13);
660        List<EttItem> ettItems = mParsedEttItems.get(sourceId);
661        if (ettItems == null) {
662            ettItems = new ArrayList<>();
663            mParsedEttItems.put(sourceId, ettItems);
664        }
665        ettItems.add(new EttItem(eventId, text));
666        return true;
667    }
668
669    private static List<AtscAudioTrack> generateAudioTracks(List<TsDescriptor> descriptors) {
670        // The list of audio tracks sent is located at both AC3 Audio descriptor and ISO 639
671        // Language descriptor.
672        List<AtscAudioTrack> ac3Tracks = new ArrayList<>();
673        List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
674         for (TsDescriptor descriptor : descriptors) {
675            if (descriptor instanceof Ac3AudioDescriptor) {
676                Ac3AudioDescriptor audioDescriptor =
677                        (Ac3AudioDescriptor) descriptor;
678                AtscAudioTrack audioTrack = new AtscAudioTrack();
679                if (audioDescriptor.getLanguage() != null) {
680                    audioTrack.language = audioDescriptor.getLanguage();
681                }
682                audioTrack.audioType = AtscAudioTrack.AUDIOTYPE_UNDEFINED;
683                audioTrack.channelCount = audioDescriptor.getNumChannels();
684                audioTrack.sampleRate = audioDescriptor.getSampleRate();
685                ac3Tracks.add(audioTrack);
686            }
687        }
688        for (TsDescriptor descriptor : descriptors) {
689            if (descriptor instanceof Iso639LanguageDescriptor) {
690                Iso639LanguageDescriptor iso639LanguageDescriptor =
691                        (Iso639LanguageDescriptor) descriptor;
692                iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks());
693            }
694        }
695
696        // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate
697        // while a ISO 639 Language descriptor only has a audio type, which describes a main use
698        // case of its audio track.
699        // Some channels contain only AC3 audio stream descriptors with valid language values.
700        // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language
701        // descriptor per audio track, and those AC3 audio stream descriptors often have a null
702        // value of language field.
703        // Combines two descriptors into one in order to gather more audio track specific
704        // information as much as possible.
705        List<AtscAudioTrack> tracks = new ArrayList<>();
706        if (!ac3Tracks.isEmpty() && !iso639LanguageTracks.isEmpty()
707                && ac3Tracks.size() != iso639LanguageTracks.size()) {
708            // This shouldn't be happen. In here, it handles two cases. The first case is that the
709            // only one type of descriptors arrives. The second case is that the two types of
710            // descriptors have the same number of tracks.
711            Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size");
712            return tracks;
713        }
714        int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size());
715        for (int i = 0; i < size; ++i) {
716            AtscAudioTrack audioTrack = null;
717            if (i < ac3Tracks.size()) {
718                audioTrack = ac3Tracks.get(i);
719            }
720            if (i < iso639LanguageTracks.size()) {
721                if (audioTrack == null) {
722                    audioTrack = iso639LanguageTracks.get(i);
723                } else {
724                    AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i);
725                    if (audioTrack.language == null) {
726                        audioTrack.language = iso639LanguageTrack.language;
727                    }
728                    audioTrack.audioType = iso639LanguageTrack.audioType;
729                }
730            }
731            tracks.add(audioTrack);
732        }
733        return tracks;
734    }
735
736    private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) {
737        List<AtscCaptionTrack> services = new ArrayList<>();
738        for (TsDescriptor descriptor : descriptors) {
739            if (descriptor instanceof CaptionServiceDescriptor) {
740                CaptionServiceDescriptor captionServiceDescriptor =
741                        (CaptionServiceDescriptor) descriptor;
742                services.addAll(captionServiceDescriptor.getCaptionTracks());
743            }
744        }
745        return services;
746    }
747
748    private static String generateContentRating(List<TsDescriptor> descriptors) {
749        List<String> contentRatings = new ArrayList<>();
750        for (TsDescriptor descriptor : descriptors) {
751            if (descriptor instanceof ContentAdvisoryDescriptor) {
752                ContentAdvisoryDescriptor contentAdvisoryDescriptor =
753                        (ContentAdvisoryDescriptor) descriptor;
754                for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) {
755                    for (RegionalRating index : ratingRegion.getRegionalRatings()) {
756                        String ratingSystem = null;
757                        String rating = null;
758                        switch (ratingRegion.getName()) {
759                            case RATING_REGION_US_TV:
760                                ratingSystem = RATING_REGION_RATING_SYSTEM_US_TV;
761                                if (index.getDimension() == 0 && index.getRating() >= 0
762                                        && index.getRating() < RATING_REGION_TABLE_US_TV.length) {
763                                    rating = RATING_REGION_TABLE_US_TV[index.getRating()];
764                                }
765                                break;
766                            case RATING_REGION_KR_TV:
767                                ratingSystem = RATING_REGION_RATING_SYSTEM_KR_TV;
768                                if (index.getDimension() == 0 && index.getRating() >= 0
769                                        && index.getRating() < RATING_REGION_TABLE_KR_TV.length) {
770                                    rating = RATING_REGION_TABLE_KR_TV[index.getRating()];
771                                }
772                                break;
773                            default:
774                                break;
775                        }
776                        if (ratingSystem != null && rating != null) {
777                            contentRatings.add(TvContentRating
778                                    .createRating("com.android.tv", ratingSystem, rating)
779                                    .flattenToString());
780                        }
781                    }
782                }
783            }
784        }
785        return TextUtils.join(",", contentRatings);
786    }
787
788    private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
789        for (TsDescriptor descriptor : descriptors) {
790            if (descriptor instanceof GenreDescriptor) {
791                GenreDescriptor genreDescriptor =
792                        (GenreDescriptor) descriptor;
793                return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
794            }
795        }
796        return null;
797    }
798
799    private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
800        for (TsDescriptor descriptor : descriptors) {
801            if (descriptor instanceof GenreDescriptor) {
802                GenreDescriptor genreDescriptor =
803                        (GenreDescriptor) descriptor;
804                return Genres.encode(genreDescriptor.getCanonicalGenres());
805            }
806        }
807        return null;
808    }
809
810    private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) {
811        // For details of the structure for descriptors, see ATSC A/65 Section 6.9.
812        List<TsDescriptor> descriptors = new ArrayList<>();
813        if (data.length < limit) {
814            return descriptors;
815        }
816        int pos = offset;
817        while (pos + 1 < limit) {
818            int tag = data[pos] & 0xff;
819            int length = data[pos + 1] & 0xff;
820            if (length <= 0) {
821                break;
822            }
823            if (limit < pos + length + 2) {
824                break;
825            }
826            if (DEBUG) {
827                Log.d(TAG, String.format("Descriptor tag: %02x", tag));
828            }
829            TsDescriptor descriptor = null;
830            switch (tag) {
831                case DESCRIPTOR_TAG_CONTENT_ADVISORY:
832                    descriptor = parseContentAdvisory(data, pos, pos + length + 2);
833                    break;
834
835                case DESCRIPTOR_TAG_CAPTION_SERVICE:
836                    descriptor = parseCaptionService(data, pos, pos + length + 2);
837                    break;
838
839                case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME:
840                    descriptor = parseLongChannelName(data, pos, pos + length + 2);
841                    break;
842
843                case DESCRIPTOR_TAG_GENRE:
844                    descriptor = parseGenre(data, pos, pos + length + 2);
845                    break;
846
847                case DESCRIPTOR_TAG_AC3_AUDIO_STREAM:
848                    descriptor = parseAc3AudioStream(data, pos, pos + length + 2);
849                    break;
850
851                case DESCRIPTOR_TAG_ISO639LANGUAGE:
852                    descriptor = parseIso639Language(data, pos, pos + length + 2);
853                    break;
854
855                default:
856            }
857            if (descriptor != null) {
858                if (DEBUG) {
859                    Log.d(TAG, "Descriptor parsed: " + descriptor);
860                }
861                descriptors.add(descriptor);
862            }
863            pos += length + 2;
864        }
865        return descriptors;
866    }
867
868    private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) {
869        // For the details of the structure of ISO 639 language descriptor,
870        // see ISO13818-1 second edition Section 2.6.18.
871        pos += 2;
872        List<AtscAudioTrack> audioTracks = new ArrayList<>();
873        while (pos + 4 <= limit) {
874            if (limit <= pos + 3) {
875                Log.e(TAG, "Broken Iso639Language.");
876                return null;
877            }
878            String language = new String(data, pos, 3);
879            int audioType = data[pos + 3] & 0xff;
880            AtscAudioTrack audioTrack = new AtscAudioTrack();
881            audioTrack.language = language;
882            audioTrack.audioType = audioType;
883            audioTracks.add(audioTrack);
884            pos += 4;
885        }
886        return new Iso639LanguageDescriptor(audioTracks);
887    }
888
889    private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) {
890        // For the details of the structure of caption service descriptor,
891        // see ATSC A/65 Section 6.9.2.
892        if (limit <= pos + 2) {
893            Log.e(TAG, "Broken CaptionServiceDescriptor.");
894            return null;
895        }
896        List<AtscCaptionTrack> services = new ArrayList<>();
897        pos += 2;
898        int numberServices = data[pos] & 0x1f;
899        ++pos;
900        if (limit < pos + numberServices * 6) {
901            Log.e(TAG, "Broken CaptionServiceDescriptor.");
902            return null;
903        }
904        for (int i = 0; i < numberServices; ++i) {
905            String language = new String(Arrays.copyOfRange(data, pos, pos + 3));
906            pos += 3;
907            boolean ccType = (data[pos] & 0x80) != 0;
908            if (!ccType) {
909                continue;
910            }
911            int captionServiceNumber = data[pos] & 0x3f;
912            ++pos;
913            boolean easyReader = (data[pos] & 0x80) != 0;
914            boolean wideAspectRatio = (data[pos] & 0x40) != 0;
915            byte[] reserved = new byte[2];
916            reserved[0] = (byte) (data[pos] << 2);
917            reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6);
918            reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2);
919            pos += 2;
920            AtscCaptionTrack captionTrack = new AtscCaptionTrack();
921            captionTrack.language = language;
922            captionTrack.serviceNumber = captionServiceNumber;
923            captionTrack.easyReader = easyReader;
924            captionTrack.wideAspectRatio = wideAspectRatio;
925            services.add(captionTrack);
926        }
927        return new CaptionServiceDescriptor(services);
928    }
929
930    private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) {
931        // For details of the structure for content advisory descriptor, see A/65 Table 6.27.
932        if (limit <= pos + 2) {
933            Log.e(TAG, "Broken ContentAdvisory");
934            return null;
935        }
936        int count = data[pos + 2] & 0x3f;
937        pos += 3;
938        List<RatingRegion> ratingRegions = new ArrayList<>();
939        for (int i = 0; i < count; ++i) {
940            if (limit <= pos + 1) {
941                Log.e(TAG, "Broken ContentAdvisory");
942                return null;
943            }
944            List<RegionalRating> indices = new ArrayList<>();
945            int ratingRegion = data[pos] & 0xff;
946            int dimensionCount = data[pos + 1] & 0xff;
947            pos += 2;
948            for (int j = 0; j < dimensionCount; ++j) {
949                if (limit <= pos + 1) {
950                    Log.e(TAG, "Broken ContentAdvisory");
951                    return null;
952                }
953                int dimensionIndex = data[pos] & 0xff;
954                int ratingValue = data[pos + 1] & 0x0f;
955                pos += 2;
956                indices.add(new RegionalRating(dimensionIndex, ratingValue));
957            }
958            if (limit <= pos) {
959                Log.e(TAG, "Broken ContentAdvisory");
960                return null;
961            }
962            int ratingDescriptionLength = data[pos] & 0xff;
963            ++pos;
964            if (limit < pos + ratingDescriptionLength) {
965                Log.e(TAG, "Broken ContentAdvisory");
966                return null;
967            }
968            String ratingDescription = extractText(data, pos);
969            pos += ratingDescriptionLength;
970            ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices));
971        }
972        return new ContentAdvisoryDescriptor(ratingRegions);
973    }
974
975    private static ExtendedChannelNameDescriptor parseLongChannelName(byte[] data, int pos,
976            int limit) {
977        if (limit <= pos + 2) {
978            Log.e(TAG, "Broken ExtendedChannelName.");
979            return null;
980        }
981        pos += 2;
982        String text = extractText(data, pos);
983        if (text == null) {
984            Log.e(TAG, "Broken ExtendedChannelName.");
985            return null;
986        }
987        return new ExtendedChannelNameDescriptor(text);
988    }
989
990    private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) {
991        pos += 2;
992        int attributeCount = data[pos] & 0x1f;
993        if (limit <= pos + attributeCount) {
994            Log.e(TAG, "Broken Genre.");
995            return null;
996        }
997        HashSet<String> broadcastGenreSet = new HashSet<>();
998        HashSet<String> canonicalGenreSet = new HashSet<>();
999        for (int i = 0; i < attributeCount; ++i) {
1000            ++pos;
1001            int genreCode = data[pos] & 0xff;
1002            if (genreCode < BROADCAST_GENRES_TABLE.length) {
1003                String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode];
1004                if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) {
1005                    broadcastGenreSet.add(broadcastGenre);
1006                }
1007            }
1008            if (genreCode < CANONICAL_GENRES_TABLE.length) {
1009                String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode];
1010                if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) {
1011                    canonicalGenreSet.add(canonicalGenre);
1012                }
1013            }
1014        }
1015        return new GenreDescriptor(broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
1016                canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
1017    }
1018
1019    private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) {
1020        // For details of the AC3 audio stream descriptor, see A/52 Table A4.1.
1021        if (limit <= pos + 5) {
1022            Log.e(TAG, "Broken AC3 audio stream descriptor.");
1023            return null;
1024        }
1025        pos += 2;
1026        byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5);
1027        byte bsid = (byte) (data[pos] & 0x1f);
1028        ++pos;
1029        byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2);
1030        byte surroundMode = (byte) (data[pos] & 0x03);
1031        ++pos;
1032        byte bsmod = (byte) ((data[pos] & 0xe0) >> 5);
1033        int numChannels = (data[pos] & 0x1e) >> 1;
1034        boolean fullSvc = (data[pos] & 0x01) != 0;
1035        ++pos;
1036        byte langCod = data[pos];
1037        byte langCod2 = 0;
1038        if (numChannels == 0) {
1039            if (limit <= pos) {
1040                Log.e(TAG, "Broken AC3 audio stream descriptor.");
1041                return null;
1042            }
1043            ++pos;
1044            langCod2 = data[pos];
1045        }
1046        if (limit <= pos + 1) {
1047            Log.e(TAG, "Broken AC3 audio stream descriptor.");
1048            return null;
1049        }
1050        byte mainId = 0;
1051        byte priority = 0;
1052        byte asvcflags = 0;
1053        ++pos;
1054        if (bsmod < 2) {
1055            mainId = (byte) ((data[pos] & 0xe0) >> 5);
1056            priority = (byte) ((data[pos] & 0x18) >> 3);
1057            if ((data[pos] & 0x07) != 0x07) {
1058                Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed");
1059                return null;
1060            }
1061        } else {
1062            asvcflags = data[pos];
1063        }
1064
1065        // See A/52B Table A3.6 num_channels.
1066        int numEncodedChannels;
1067        switch (numChannels) {
1068            case 1:
1069            case 8:
1070                numEncodedChannels = 1;
1071                break;
1072            case 2:
1073            case 9:
1074                numEncodedChannels = 2;
1075                break;
1076            case 3:
1077            case 4:
1078            case 10:
1079                numEncodedChannels = 3;
1080                break;
1081            case 5:
1082            case 6:
1083            case 11:
1084                numEncodedChannels = 4;
1085                break;
1086            case 7:
1087            case 12:
1088                numEncodedChannels = 5;
1089                break;
1090            case 13:
1091                numEncodedChannels = 6;
1092                break;
1093            default:
1094                numEncodedChannels = 0;
1095                break;
1096        }
1097
1098        if (limit <= pos + 1) {
1099            Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
1100            return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
1101                    numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags,
1102                    null, null, null);
1103        }
1104        ++pos;
1105        int textLen = (data[pos] & 0xfe) >> 1;
1106        boolean textCode = (data[pos] & 0x01) != 0;
1107        ++pos;
1108        String text = "";
1109        if (textLen > 0) {
1110            if (limit < pos + textLen) {
1111                Log.e(TAG, "Broken AC3 audio stream descriptor");
1112                return null;
1113            }
1114            if (textCode) {
1115                text = new String(data, pos, textLen);
1116            } else {
1117                text = new String(data, pos, textLen, Charset.forName("UTF-16"));
1118            }
1119            pos += textLen;
1120        }
1121        String language = null;
1122        String language2 = null;
1123        if (pos < limit) {
1124            // Many AC3 audio stream descriptors skip the language fields.
1125            boolean languageFlag1 = (data[pos] & 0x80) != 0;
1126            boolean languageFlag2 = (data[pos] & 0x40) != 0;
1127            if ((data[pos] & 0x3f) != 0x3f) {
1128                Log.e(TAG, "Broken AC3 audio stream descriptor");
1129                return null;
1130            }
1131            if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) {
1132                Log.e(TAG, "Broken AC3 audio stream descriptor");
1133                return null;
1134            }
1135            ++pos;
1136            if (languageFlag1) {
1137                language = new String(data, pos, 3);
1138                pos += 3;
1139            }
1140            if (languageFlag2) {
1141                language2 = new String(data, pos, 3);
1142            }
1143        }
1144
1145        return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
1146                numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags, text,
1147                language, language2);
1148    }
1149
1150    private static int getShortNameSize(byte[] data, int offset) {
1151        for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) {
1152            if (data[offset + i] == 0 && data[offset + i + 1] == 0) {
1153                return i;
1154            }
1155        }
1156        return MAX_SHORT_NAME_BYTES;
1157    }
1158
1159    private static String extractText(byte[] data, int pos) {
1160        if (data.length < pos)  {
1161            return null;
1162        }
1163        int numStrings = data[pos] & 0xff;
1164        pos++;
1165        for (int i = 0; i < numStrings; ++i) {
1166            if (data.length <= pos + 3) {
1167                Log.e(TAG, "Broken text.");
1168                return null;
1169            }
1170            int numSegments = data[pos + 3] & 0xff;
1171            pos += 4;
1172            for (int j = 0; j < numSegments; ++j) {
1173                if (data.length <= pos + 2) {
1174                    Log.e(TAG, "Broken text.");
1175                    return null;
1176                }
1177                int compressionType = data[pos] & 0xff;
1178                int mode = data[pos + 1] & 0xff;
1179                int numBytes = data[pos + 2] & 0xff;
1180                if (data.length < pos + 3 + numBytes) {
1181                    Log.e(TAG, "Broken text.");
1182                    return null;
1183                }
1184                byte[] bytes = Arrays.copyOfRange(data, pos + 3, pos + 3 + numBytes);
1185                if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
1186                    try {
1187                        switch (mode) {
1188                            case MODE_SELECTED_UNICODE_RANGE_1:
1189                                return new String(bytes, "ISO-8859-1");
1190                            case MODE_SCSU:
1191                                return UnicodeDecompressor.decompress(bytes);
1192                            case MODE_UTF16:
1193                                return new String(bytes, "UTF-16");
1194                        }
1195                    } catch (UnsupportedEncodingException e) {
1196                        Log.e(TAG, "Unsupported text format.", e);
1197                    }
1198                }
1199                pos += 3 + numBytes;
1200            }
1201        }
1202        return null;
1203    }
1204
1205    private static boolean checkSanity(byte[] data) {
1206        if (data.length <= 1) {
1207            return false;
1208        }
1209        boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
1210        if (hasCRC) {
1211            int crc = 0xffffffff;
1212            for(byte b : data) {
1213                int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
1214                crc = CRC_TABLE[index] ^ (crc << 8);
1215            }
1216            if(crc != 0){
1217                return false;
1218            }
1219        }
1220        return true;
1221    }
1222}
1223