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.support.annotation.VisibleForTesting;
22import android.text.TextUtils;
23import android.util.ArraySet;
24import android.util.Log;
25import android.util.SparseArray;
26
27import com.android.tv.tuner.data.PsiData.PatItem;
28import com.android.tv.tuner.data.PsiData.PmtItem;
29import com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor;
30import com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor;
31import com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor;
32import com.android.tv.tuner.data.PsipData.EitItem;
33import com.android.tv.tuner.data.PsipData.EttItem;
34import com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor;
35import com.android.tv.tuner.data.PsipData.GenreDescriptor;
36import com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor;
37import com.android.tv.tuner.data.PsipData.MgtItem;
38import com.android.tv.tuner.data.PsipData.ParentalRatingDescriptor;
39import com.android.tv.tuner.data.PsipData.PsipSection;
40import com.android.tv.tuner.data.PsipData.RatingRegion;
41import com.android.tv.tuner.data.PsipData.RegionalRating;
42import com.android.tv.tuner.data.PsipData.SdtItem;
43import com.android.tv.tuner.data.PsipData.ServiceDescriptor;
44import com.android.tv.tuner.data.PsipData.ShortEventDescriptor;
45import com.android.tv.tuner.data.PsipData.TsDescriptor;
46import com.android.tv.tuner.data.PsipData.VctItem;
47import com.android.tv.tuner.data.nano.Channel;
48import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
49import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
50import com.android.tv.tuner.util.ByteArrayBuffer;
51
52import com.android.tv.tuner.util.ConvertUtils;
53import com.ibm.icu.text.UnicodeDecompressor;
54
55import java.io.UnsupportedEncodingException;
56import java.nio.charset.Charset;
57import java.util.Calendar;
58import java.util.ArrayList;
59import java.util.Arrays;
60import java.util.HashMap;
61import java.util.HashSet;
62import java.util.List;
63import java.util.Set;
64
65/**
66 * Parses ATSC PSIP sections.
67 */
68public class SectionParser {
69    private static final String TAG = "SectionParser";
70    private static final boolean DEBUG = false;
71
72    private static final byte TABLE_ID_PAT = (byte) 0x00;
73    private static final byte TABLE_ID_PMT = (byte) 0x02;
74    private static final byte TABLE_ID_MGT = (byte) 0xc7;
75    private static final byte TABLE_ID_TVCT = (byte) 0xc8;
76    private static final byte TABLE_ID_CVCT = (byte) 0xc9;
77    private static final byte TABLE_ID_EIT = (byte) 0xcb;
78    private static final byte TABLE_ID_ETT = (byte) 0xcc;
79
80    // Table id for DVB
81    private static final byte TABLE_ID_SDT = (byte) 0x42;
82    private static final byte TABLE_ID_DVB_ACTUAL_P_F_EIT = (byte) 0x4e;
83    private static final byte TABLE_ID_DVB_OTHER_P_F_EIT = (byte) 0x4f;
84    private static final byte TABLE_ID_DVB_ACTUAL_SCHEDULE_EIT = (byte) 0x50;
85    private static final byte TABLE_ID_DVB_OTHER_SCHEDULE_EIT = (byte) 0x60;
86
87    // For details of the structure for the tags of descriptors, see ATSC A/65 Table 6.25.
88    public static final int DESCRIPTOR_TAG_ISO639LANGUAGE = 0x0a;
89    public static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86;
90    public static final int DESCRIPTOR_TAG_CONTENT_ADVISORY = 0x87;
91    public static final int DESCRIPTOR_TAG_AC3_AUDIO_STREAM = 0x81;
92    public static final int DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME = 0xa0;
93    public static final int DESCRIPTOR_TAG_GENRE = 0xab;
94
95    // For details of the structure for the tags of DVB descriptors, see DVB Document A038 Table 12.
96    public static final int DVB_DESCRIPTOR_TAG_SERVICE = 0x48;
97    public static final int DVB_DESCRIPTOR_TAG_SHORT_EVENT = 0X4d;
98    public static final int DVB_DESCRIPTOR_TAG_CONTENT = 0x54;
99    public static final int DVB_DESCRIPTOR_TAG_PARENTAL_RATING = 0x55;
100
101    private static final byte COMPRESSION_TYPE_NO_COMPRESSION = (byte) 0x00;
102    private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00;  // 0x0000 - 0x00ff
103    private static final byte MODE_UTF16 = (byte) 0x3f;
104    private static final byte MODE_SCSU = (byte) 0x3e;
105    private static final int MAX_SHORT_NAME_BYTES = 14;
106
107    // See ANSI/CEA-766-C.
108    private static final int RATING_REGION_US_TV = 1;
109    private static final int RATING_REGION_KR_TV = 4;
110
111    // The following values are defined in the live channels app.
112    // See https://developer.android.com/reference/android/media/tv/TvContentRating.html.
113    private static final String RATING_DOMAIN = "com.android.tv";
114    private static final String RATING_REGION_RATING_SYSTEM_US_TV = "US_TV";
115    private static final String RATING_REGION_RATING_SYSTEM_US_MV = "US_MV";
116    private static final String RATING_REGION_RATING_SYSTEM_KR_TV = "KR_TV";
117
118    private static final String[] RATING_REGION_TABLE_US_TV = {
119        "US_TV_Y", "US_TV_Y7", "US_TV_G", "US_TV_PG", "US_TV_14", "US_TV_MA"
120    };
121
122    private static final String[] RATING_REGION_TABLE_US_MV = {
123        "US_MV_G", "US_MV_PG", "US_MV_PG13", "US_MV_R", "US_MV_NC17"
124    };
125
126    private static final String[] RATING_REGION_TABLE_KR_TV = {
127        "KR_TV_ALL", "KR_TV_7", "KR_TV_12", "KR_TV_15", "KR_TV_19"
128    };
129
130    private static final String[] RATING_REGION_TABLE_US_TV_SUBRATING = {
131        "US_TV_D", "US_TV_L", "US_TV_S", "US_TV_V", "US_TV_FV"
132    };
133
134    // According to ANSI-CEA-766-D
135    private static final int VALUE_US_TV_Y = 1;
136    private static final int VALUE_US_TV_Y7 = 2;
137    private static final int VALUE_US_TV_NONE = 1;
138    private static final int VALUE_US_TV_G = 2;
139    private static final int VALUE_US_TV_PG = 3;
140    private static final int VALUE_US_TV_14 = 4;
141    private static final int VALUE_US_TV_MA = 5;
142
143    private static final int DIMENSION_US_TV_RATING = 0;
144    private static final int DIMENSION_US_TV_D = 1;
145    private static final int DIMENSION_US_TV_L = 2;
146    private static final int DIMENSION_US_TV_S = 3;
147    private static final int DIMENSION_US_TV_V = 4;
148    private static final int DIMENSION_US_TV_Y = 5;
149    private static final int DIMENSION_US_TV_FV = 6;
150    private static final int DIMENSION_US_MV_RATING = 7;
151
152    private static final int VALUE_US_MV_G = 2;
153    private static final int VALUE_US_MV_PG = 3;
154    private static final int VALUE_US_MV_PG13 = 4;
155    private static final int VALUE_US_MV_R = 5;
156    private static final int VALUE_US_MV_NC17 = 6;
157    private static final int VALUE_US_MV_X = 7;
158
159    private static final String STRING_US_TV_Y = "US_TV_Y";
160    private static final String STRING_US_TV_Y7 = "US_TV_Y7";
161    private static final String STRING_US_TV_FV = "US_TV_FV";
162
163
164    /*
165     * The following CRC table is from the code generated by the following command.
166     * $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
167     * To see the details of pycrc, visit http://www.tty1.net/pycrc/index_en.html
168     */
169    public static final int[] CRC_TABLE = {
170        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
171        0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
172        0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
173        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
174        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
175        0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
176        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
177        0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
178        0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
179        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
180        0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
181        0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
182        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
183        0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
184        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
185        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
186        0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
187        0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
188        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
189        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
190        0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
191        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
192        0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
193        0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
194        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
195        0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
196        0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
197        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
198        0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
199        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
200        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
201        0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
202        0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
203        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
204        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
205        0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
206        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
207        0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
208        0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
209        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
210        0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
211        0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
212        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
213        0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
214        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
215        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
216        0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
217        0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
218        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
219        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
220        0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
221        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
222        0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
223        0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
224        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
225        0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
226        0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
227        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
228        0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
229        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
230        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
231        0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
232        0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
233        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
234    };
235
236    // A table which maps ATSC genres to TIF genres.
237    // See ATSC/65 Table 6.20.
238    private static final String[] CANONICAL_GENRES_TABLE = {
239        null, null, null, null,
240        null, null, null, null,
241        null, null, null, null,
242        null, null, null, null,
243        null, null, null, null,
244        null, null, null, null,
245        null, null, null, null,
246        null, null, null, null,
247        Genres.EDUCATION, Genres.ENTERTAINMENT, Genres.MOVIES, Genres.NEWS,
248        Genres.LIFE_STYLE, Genres.SPORTS, null, Genres.MOVIES,
249        null,
250        Genres.FAMILY_KIDS, Genres.DRAMA, null, Genres.ENTERTAINMENT, Genres.SPORTS,
251        Genres.SPORTS,
252        null, null,
253        Genres.MUSIC, Genres.EDUCATION,
254        null,
255        Genres.COMEDY,
256        null,
257        Genres.MUSIC,
258        null, null,
259        Genres.MOVIES, Genres.ENTERTAINMENT, Genres.NEWS, Genres.DRAMA,
260        Genres.EDUCATION, Genres.MOVIES, Genres.SPORTS, Genres.MOVIES,
261        null,
262        Genres.LIFE_STYLE, Genres.ARTS, Genres.LIFE_STYLE, Genres.SPORTS,
263        null, null,
264        Genres.GAMING, Genres.LIFE_STYLE, Genres.SPORTS,
265        null,
266        Genres.LIFE_STYLE, Genres.EDUCATION, Genres.EDUCATION, Genres.LIFE_STYLE,
267        Genres.SPORTS, Genres.LIFE_STYLE, Genres.MOVIES, Genres.NEWS,
268        null, null, null,
269        Genres.EDUCATION,
270        null, null, null,
271        Genres.EDUCATION,
272        null, null, null,
273        Genres.DRAMA, Genres.MUSIC, Genres.MOVIES,
274        null,
275        Genres.ANIMAL_WILDLIFE,
276        null, null,
277        Genres.PREMIER,
278        null, null, null, null,
279        Genres.SPORTS, Genres.ARTS,
280        null, null, null,
281        Genres.MOVIES, Genres.TECH_SCIENCE, Genres.DRAMA,
282        null,
283        Genres.SHOPPING, Genres.DRAMA,
284        null,
285        Genres.MOVIES, Genres.ENTERTAINMENT, Genres.TECH_SCIENCE, Genres.SPORTS,
286        Genres.TRAVEL, Genres.ENTERTAINMENT, Genres.ARTS, Genres.NEWS,
287        null,
288        Genres.ARTS, Genres.SPORTS, Genres.SPORTS, Genres.NEWS,
289        Genres.SPORTS, Genres.SPORTS, Genres.SPORTS, Genres.FAMILY_KIDS,
290        Genres.FAMILY_KIDS, Genres.MOVIES,
291        null,
292        Genres.TECH_SCIENCE, Genres.MUSIC,
293        null,
294        Genres.SPORTS, Genres.FAMILY_KIDS, Genres.NEWS, Genres.SPORTS,
295        Genres.NEWS, Genres.SPORTS, Genres.ANIMAL_WILDLIFE,
296        null,
297        Genres.MUSIC, Genres.NEWS, Genres.SPORTS,
298        null,
299        Genres.NEWS, Genres.NEWS, Genres.NEWS, Genres.NEWS,
300        Genres.SPORTS, Genres.MOVIES, Genres.ARTS, Genres.ANIMAL_WILDLIFE,
301        Genres.MUSIC, Genres.MUSIC, Genres.MOVIES, Genres.EDUCATION,
302        Genres.DRAMA, Genres.SPORTS, Genres.SPORTS, Genres.SPORTS,
303        Genres.SPORTS,
304        null,
305        Genres.SPORTS, Genres.SPORTS,
306    };
307
308    // A table which contains ATSC categorical genre code assignments.
309    // See ATSC/65 Table 6.20.
310    private static final String[] BROADCAST_GENRES_TABLE = new String[] {
311            null, null, null, null,
312            null, null, null, null,
313            null, null, null, null,
314            null, null, null, null,
315            null, null, null, null,
316            null, null, null, null,
317            null, null, null, null,
318            null, null, null, null,
319            "Education", "Entertainment", "Movie", "News",
320            "Religious", "Sports", "Other", "Action",
321            "Advertisement", "Animated", "Anthology", "Automobile",
322            "Awards", "Baseball", "Basketball", "Bulletin",
323            "Business", "Classical", "College", "Combat",
324            "Comedy", "Commentary", "Concert", "Consumer",
325            "Contemporary", "Crime", "Dance", "Documentary",
326            "Drama", "Elementary", "Erotica", "Exercise",
327            "Fantasy", "Farm", "Fashion", "Fiction",
328            "Food", "Football", "Foreign", "Fund Raiser",
329            "Game/Quiz", "Garden", "Golf", "Government",
330            "Health", "High School", "History", "Hobby",
331            "Hockey", "Home", "Horror", "Information",
332            "Instruction", "International", "Interview", "Language",
333            "Legal", "Live", "Local", "Math",
334            "Medical", "Meeting", "Military", "Miniseries",
335            "Music", "Mystery", "National", "Nature",
336            "Police", "Politics", "Premier", "Prerecorded",
337            "Product", "Professional", "Public", "Racing",
338            "Reading", "Repair", "Repeat", "Review",
339            "Romance", "Science", "Series", "Service",
340            "Shopping", "Soap Opera", "Special", "Suspense",
341            "Talk", "Technical", "Tennis", "Travel",
342            "Variety", "Video", "Weather", "Western",
343            "Art", "Auto Racing", "Aviation", "Biography",
344            "Boating", "Bowling", "Boxing", "Cartoon",
345            "Children", "Classic Film", "Community", "Computers",
346            "Country Music", "Court", "Extreme Sports", "Family",
347            "Financial", "Gymnastics", "Headlines", "Horse Racing",
348            "Hunting/Fishing/Outdoors", "Independent", "Jazz", "Magazine",
349            "Motorcycle Racing", "Music/Film/Books", "News-International", "News-Local",
350            "News-National", "News-Regional", "Olympics", "Original",
351            "Performing Arts", "Pets/Animals", "Pop", "Rock & Roll",
352            "Sci-Fi", "Self Improvement", "Sitcom", "Skating",
353            "Skiing", "Soccer", "Track/Field", "True",
354            "Volleyball", "Wrestling",
355    };
356
357    // Audio language code map from ISO 639-2/B to 639-2/T, in order to show correct audio language.
358    private static final HashMap<String, String> ISO_LANGUAGE_CODE_MAP;
359    static {
360        ISO_LANGUAGE_CODE_MAP = new HashMap<>();
361        ISO_LANGUAGE_CODE_MAP.put("alb", "sqi");
362        ISO_LANGUAGE_CODE_MAP.put("arm", "hye");
363        ISO_LANGUAGE_CODE_MAP.put("baq", "eus");
364        ISO_LANGUAGE_CODE_MAP.put("bur", "mya");
365        ISO_LANGUAGE_CODE_MAP.put("chi", "zho");
366        ISO_LANGUAGE_CODE_MAP.put("cze", "ces");
367        ISO_LANGUAGE_CODE_MAP.put("dut", "nld");
368        ISO_LANGUAGE_CODE_MAP.put("fre", "fra");
369        ISO_LANGUAGE_CODE_MAP.put("geo", "kat");
370        ISO_LANGUAGE_CODE_MAP.put("ger", "deu");
371        ISO_LANGUAGE_CODE_MAP.put("gre", "ell");
372        ISO_LANGUAGE_CODE_MAP.put("ice", "isl");
373        ISO_LANGUAGE_CODE_MAP.put("mac", "mkd");
374        ISO_LANGUAGE_CODE_MAP.put("mao", "mri");
375        ISO_LANGUAGE_CODE_MAP.put("may", "msa");
376        ISO_LANGUAGE_CODE_MAP.put("per", "fas");
377        ISO_LANGUAGE_CODE_MAP.put("rum", "ron");
378        ISO_LANGUAGE_CODE_MAP.put("slo", "slk");
379        ISO_LANGUAGE_CODE_MAP.put("tib", "bod");
380        ISO_LANGUAGE_CODE_MAP.put("wel", "cym");
381        ISO_LANGUAGE_CODE_MAP.put("esl", "spa"); // Special entry for channel 9-1 KQED in bay area.
382    }
383
384    // Containers to store the last version numbers of the PSIP sections.
385    private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
386    private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
387
388    public interface OutputListener {
389        void onPatParsed(List<PatItem> items);
390        void onPmtParsed(int programNumber, List<PmtItem> items);
391        void onMgtParsed(List<MgtItem> items);
392        void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
393        void onEitParsed(int sourceId, List<EitItem> items);
394        void onEttParsed(int sourceId, List<EttItem> descriptions);
395        void onSdtParsed(List<SdtItem> items);
396    }
397
398    private final OutputListener mListener;
399
400    public SectionParser(OutputListener listener) {
401        mListener = listener;
402    }
403
404    public void parseSections(ByteArrayBuffer data) {
405        int pos = 0;
406        while (pos + 3 <= data.length()) {
407            if ((data.byteAt(pos) & 0xff) == 0xff) {
408                // Clear stuffing bytes according to H222.0 section 2.4.4.
409                data.setLength(0);
410                break;
411            }
412            int sectionLength =
413                    (((data.byteAt(pos + 1) & 0x0f) << 8) | (data.byteAt(pos + 2) & 0xff)) + 3;
414            if (pos + sectionLength > data.length()) {
415                break;
416            }
417            if (DEBUG) {
418                Log.d(TAG, "parseSections 0x" + Integer.toHexString(data.byteAt(pos) & 0xff));
419            }
420            parseSection(Arrays.copyOfRange(data.buffer(), pos, pos + sectionLength));
421            pos += sectionLength;
422        }
423        if (mListener != null) {
424            for (int i = 0; i < mParsedEttItems.size(); ++i) {
425                int sourceId = mParsedEttItems.keyAt(i);
426                List<EttItem> descriptions = mParsedEttItems.valueAt(i);
427                mListener.onEttParsed(sourceId, descriptions);
428            }
429        }
430        mParsedEttItems.clear();
431    }
432
433    public void resetVersionNumbers() {
434        mSectionVersionMap.clear();
435    }
436
437    private void parseSection(byte[] data) {
438        if (!checkSanity(data)) {
439            Log.d(TAG, "Bad CRC!");
440            return;
441        }
442        PsipSection section = PsipSection.create(data);
443        if (section == null) {
444            return;
445        }
446
447        // The currentNextIndicator indicates that the section sent is currently applicable.
448        if (!section.getCurrentNextIndicator()) {
449            return;
450        }
451        int versionNumber = (data[5] & 0x3e) >> 1;
452        Integer oldVersionNumber = mSectionVersionMap.get(section);
453
454        // The versionNumber shall be incremented when a change in the information carried within
455        // the section occurs.
456        if (oldVersionNumber != null && versionNumber == oldVersionNumber) {
457            return;
458        }
459        boolean result = false;
460        switch (data[0]) {
461            case TABLE_ID_PAT:
462                result = parsePAT(data);
463                break;
464            case TABLE_ID_PMT:
465                result = parsePMT(data);
466                break;
467            case TABLE_ID_MGT:
468                result = parseMGT(data);
469                break;
470            case TABLE_ID_TVCT:
471            case TABLE_ID_CVCT:
472                result = parseVCT(data);
473                break;
474            case TABLE_ID_EIT:
475                result = parseEIT(data);
476                break;
477            case TABLE_ID_ETT:
478                result = parseETT(data);
479                break;
480            case TABLE_ID_SDT:
481                result = parseSDT(data);
482                break;
483            case TABLE_ID_DVB_ACTUAL_P_F_EIT:
484            case TABLE_ID_DVB_ACTUAL_SCHEDULE_EIT:
485                result = parseDVBEIT(data);
486                break;
487            default:
488                break;
489        }
490        if (result) {
491            mSectionVersionMap.put(section, versionNumber);
492        }
493    }
494
495    private boolean parsePAT(byte[] data) {
496        if (DEBUG) {
497            Log.d(TAG, "PAT is discovered.");
498        }
499        int pos = 8;
500
501        List<PatItem> results = new ArrayList<>();
502        for (; pos < data.length - 4; pos = pos + 4) {
503            if (pos > data.length - 4 - 4) {
504                Log.e(TAG, "Broken PAT.");
505                return false;
506            }
507            int programNo = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
508            int pmtPid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
509            results.add(new PatItem(programNo, pmtPid));
510        }
511        if (mListener != null) {
512            mListener.onPatParsed(results);
513        }
514        return true;
515    }
516
517    private boolean parsePMT(byte[] data) {
518        int table_id_ext = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
519        if (DEBUG) {
520            Log.d(TAG, "PMT is discovered. programNo = " + table_id_ext);
521        }
522        if (data.length <= 11) {
523            Log.e(TAG, "Broken PMT.");
524            return false;
525        }
526        int pcrPid = (data[8] & 0x1f) << 8 | data[9];
527        int programInfoLen = (data[10] & 0x0f) << 8 | data[11];
528        int pos = 12;
529        List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + programInfoLen);
530        pos += programInfoLen;
531        if (DEBUG) {
532            Log.d(TAG, "PMT descriptors size: " + descriptors.size());
533        }
534        List<PmtItem> results = new ArrayList<>();
535        for (; pos < data.length - 4;) {
536            if (pos < 0) {
537                Log.e(TAG, "Broken PMT.");
538                return false;
539            }
540            int streamType = data[pos] & 0xff;
541            int esPid = (data[pos + 1] & 0x1f) << 8 | (data[pos + 2] & 0xff);
542            int esInfoLen = (data[pos + 3] & 0xf) << 8 | (data[pos + 4] & 0xff);
543            if (data.length < pos + esInfoLen + 5) {
544                Log.e(TAG, "Broken PMT.");
545                return false;
546            }
547            descriptors = parseDescriptors(data, pos + 5, pos + 5 + esInfoLen);
548            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
549            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
550            PmtItem pmtItem = new PmtItem(streamType, esPid, audioTracks, captionTracks);
551            if (DEBUG) {
552                Log.d(TAG, "PMT " + pmtItem + " descriptors size: " + descriptors.size());
553            }
554            results.add(pmtItem);
555            pos = pos + esInfoLen + 5;
556        }
557        results.add(new PmtItem(PmtItem.ES_PID_PCR, pcrPid, null, null));
558        if (mListener != null) {
559            mListener.onPmtParsed(table_id_ext, results);
560        }
561        return true;
562    }
563
564    private boolean parseMGT(byte[] data) {
565        // For details of the structure for MGT, see ATSC A/65 Table 6.2.
566        if (DEBUG) {
567            Log.d(TAG, "MGT is discovered.");
568        }
569        if (data.length <= 10) {
570            Log.e(TAG, "Broken MGT.");
571            return false;
572        }
573        int tablesDefined = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
574        int pos = 11;
575        List<MgtItem> results = new ArrayList<>();
576        for (int i = 0; i < tablesDefined; ++i) {
577            if (data.length <= pos + 10) {
578                Log.e(TAG, "Broken MGT.");
579                return false;
580            }
581            int tableType = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
582            int tableTypePid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
583            int descriptorsLength = ((data[pos + 9] & 0x0f) << 8) | (data[pos + 10] & 0xff);
584            pos += 11 + descriptorsLength;
585            results.add(new MgtItem(tableType, tableTypePid));
586        }
587        // Skip the remaining descriptor part which we don't use.
588
589        if (mListener != null) {
590            mListener.onMgtParsed(results);
591        }
592        return true;
593    }
594
595    private boolean parseVCT(byte[] data) {
596        // For details of the structure for VCT, see ATSC A/65 Table 6.4 and 6.8.
597        if (DEBUG) {
598            Log.d(TAG, "VCT is discovered.");
599        }
600        if (data.length <= 9) {
601            Log.e(TAG, "Broken VCT.");
602            return false;
603        }
604        int numChannelsInSection = (data[9] & 0xff);
605        int sectionNumber = (data[6] & 0xff);
606        int lastSectionNumber = (data[7] & 0xff);
607        if (sectionNumber > lastSectionNumber) {
608            // According to section 6.3.1 of the spec ATSC A/65,
609            // last section number is the largest section number.
610            Log.w(TAG, "Invalid VCT. Section Number " + sectionNumber + " > Last Section Number "
611                    + lastSectionNumber);
612            return false;
613        }
614        int pos = 10;
615        List<VctItem> results = new ArrayList<>();
616        for (int i = 0; i < numChannelsInSection; ++i) {
617            if (data.length <= pos + 31) {
618                Log.e(TAG, "Broken VCT.");
619                return false;
620            }
621            String shortName = "";
622            int shortNameSize = getShortNameSize(data, pos);
623            try {
624                shortName = new String(
625                        Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
626            } catch (UnsupportedEncodingException e) {
627                Log.e(TAG, "Broken VCT.", e);
628                return false;
629            }
630            if ((data[pos + 14] & 0xf0) != 0xf0) {
631                Log.e(TAG, "Broken VCT.");
632                return false;
633            }
634            int majorNumber = ((data[pos + 14] & 0x0f) << 6) | ((data[pos + 15] & 0xff) >> 2);
635            int minorNumber = ((data[pos + 15] & 0x03) << 8) | (data[pos + 16] & 0xff);
636            if ((majorNumber & 0x3f0) == 0x3f0) {
637                // If the six MSBs are 111111, these indicate that there is only one-part channel
638                // number. To see details, refer A/65 Section 6.3.2.
639                majorNumber = ((majorNumber & 0xf) << 10) + minorNumber;
640                minorNumber = 0;
641            }
642            int channelTsid = ((data[pos + 22] & 0xff) << 8) | (data[pos + 23] & 0xff);
643            int programNumber = ((data[pos + 24] & 0xff) << 8) | (data[pos + 25] & 0xff);
644            boolean accessControlled = (data[pos + 26] & 0x20) != 0;
645            boolean hidden = (data[pos + 26] & 0x10) != 0;
646            int serviceType = (data[pos + 27] & 0x3f);
647            int sourceId = ((data[pos + 28] & 0xff) << 8) | (data[pos + 29] & 0xff);
648            int descriptorsPos = pos + 32;
649            int descriptorsLength = ((data[pos + 30] & 0x03) << 8) | (data[pos + 31] & 0xff);
650            pos += 32 + descriptorsLength;
651            if (data.length < pos) {
652                Log.e(TAG, "Broken VCT.");
653                return false;
654            }
655            List<TsDescriptor> descriptors = parseDescriptors(
656                    data, descriptorsPos, descriptorsPos + descriptorsLength);
657            String longName = null;
658            for (TsDescriptor descriptor : descriptors) {
659                if (descriptor instanceof ExtendedChannelNameDescriptor) {
660                    ExtendedChannelNameDescriptor extendedChannelNameDescriptor =
661                            (ExtendedChannelNameDescriptor) descriptor;
662                    longName = extendedChannelNameDescriptor.getLongChannelName();
663                    break;
664                }
665            }
666            if (DEBUG) {
667                Log.d(TAG, String.format(
668                        "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
669                                + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
670                        shortName, longName, serviceType, channelTsid, programNumber, majorNumber,
671                        minorNumber, accessControlled, hidden, descriptors.size()));
672            }
673            if (!accessControlled && !hidden && (serviceType == Channel.SERVICE_TYPE_ATSC_AUDIO ||
674                    serviceType == Channel.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION ||
675                    serviceType == Channel.SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
676                // Hide hidden, encrypted, or unsupported ATSC service type channels
677                results.add(new VctItem(shortName, longName, serviceType, channelTsid,
678                        programNumber, majorNumber, minorNumber, sourceId));
679            }
680        }
681        // Skip the remaining descriptor part which we don't use.
682
683        if (mListener != null) {
684            mListener.onVctParsed(results, sectionNumber, lastSectionNumber);
685        }
686        return true;
687    }
688
689    private boolean parseEIT(byte[] data) {
690        // For details of the structure for EIT, see ATSC A/65 Table 6.11.
691        if (DEBUG) {
692            Log.d(TAG, "EIT is discovered.");
693        }
694        if (data.length <= 9) {
695            Log.e(TAG, "Broken EIT.");
696            return false;
697        }
698        int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
699        int numEventsInSection = (data[9] & 0xff);
700
701        int pos = 10;
702        List<EitItem> results = new ArrayList<>();
703        for (int i = 0; i < numEventsInSection; ++i) {
704            if (data.length <= pos + 9) {
705                Log.e(TAG, "Broken EIT.");
706                return false;
707            }
708            if ((data[pos] & 0xc0) != 0xc0) {
709                Log.e(TAG, "Broken EIT.");
710                return false;
711            }
712            int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
713            long startTime = ((data[pos + 2] & (long) 0xff) << 24) | ((data[pos + 3] & 0xff) << 16)
714                    | ((data[pos + 4] & 0xff) << 8) | (data[pos + 5] & 0xff);
715            int lengthInSecond = ((data[pos + 6] & 0x0f) << 16)
716                    | ((data[pos + 7] & 0xff) << 8) | (data[pos + 8] & 0xff);
717            int titleLength = (data[pos + 9] & 0xff);
718            if (data.length <= pos + 10 + titleLength + 1) {
719                Log.e(TAG, "Broken EIT.");
720                return false;
721            }
722            String titleText = "";
723            if (titleLength > 0) {
724                titleText = extractText(data, pos + 10);
725            }
726            if ((data[pos + 10 + titleLength] & 0xf0) != 0xf0) {
727                Log.e(TAG, "Broken EIT.");
728                return false;
729            }
730            int descriptorsLength = ((data[pos + 10 + titleLength] & 0x0f) << 8)
731                    | (data[pos + 10 + titleLength + 1] & 0xff);
732            int descriptorsPos = pos + 10 + titleLength + 2;
733            if (data.length < descriptorsPos + descriptorsLength) {
734                Log.e(TAG, "Broken EIT.");
735                return false;
736            }
737            List<TsDescriptor> descriptors = parseDescriptors(
738                    data, descriptorsPos, descriptorsPos + descriptorsLength);
739            if (DEBUG) {
740                Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
741            }
742            String contentRating = generateContentRating(descriptors);
743            String broadcastGenre = generateBroadcastGenre(descriptors);
744            String canonicalGenre = generateCanonicalGenre(descriptors);
745            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
746            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
747            pos += 10 + titleLength + 2 + descriptorsLength;
748            results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
749                    startTime, lengthInSecond, contentRating, audioTracks, captionTracks,
750                    broadcastGenre, canonicalGenre, null));
751        }
752        if (mListener != null) {
753            mListener.onEitParsed(sourceId, results);
754        }
755        return true;
756    }
757
758    private boolean parseETT(byte[] data) {
759        // For details of the structure for ETT, see ATSC A/65 Table 6.13.
760        if (DEBUG) {
761            Log.d(TAG, "ETT is discovered.");
762        }
763        if (data.length <= 12) {
764            Log.e(TAG, "Broken ETT.");
765            return false;
766        }
767        int sourceId = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
768        int eventId = (((data[11] & 0xff) << 8) | (data[12] & 0xff)) >> 2;
769        String text = extractText(data, 13);
770        List<EttItem> ettItems = mParsedEttItems.get(sourceId);
771        if (ettItems == null) {
772            ettItems = new ArrayList<>();
773            mParsedEttItems.put(sourceId, ettItems);
774        }
775        ettItems.add(new EttItem(eventId, text));
776        return true;
777    }
778
779    private boolean parseSDT(byte[] data) {
780        // For details of the structure for SDT, see DVB Document A038 Table 5.
781        if (DEBUG) {
782            Log.d(TAG, "SDT id discovered");
783        }
784        if (data.length <= 11) {
785            Log.e(TAG, "Broken SDT.");
786            return false;
787        }
788        if ((data[1] & 0x80) >> 7 != 1) {
789            Log.e(TAG, "Broken SDT, section syntax indicator error.");
790            return false;
791        }
792        int sectionLength = ((data[1] & 0x0f) << 8) | (data[2] & 0xff);
793        int transportStreamId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
794        int originalNetworkId = ((data[8] & 0xff) << 8) | (data[9] & 0xff);
795        int pos = 11;
796        if (sectionLength + 3 > data.length) {
797            Log.e(TAG, "Broken SDT.");
798        }
799        List<SdtItem> sdtItems = new ArrayList<>();
800        while (pos + 9 < data.length) {
801            int serviceId = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
802            int descriptorsLength = ((data[pos + 3] & 0x0f) << 8) | (data[pos + 4] & 0xff);
803            pos += 5;
804            List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + descriptorsLength);
805            List<ServiceDescriptor> serviceDescriptors = generateServiceDescriptors(descriptors);
806            String serviceName = "";
807            String serviceProviderName = "";
808            int serviceType = 0;
809            for (ServiceDescriptor serviceDescriptor : serviceDescriptors) {
810                serviceName = serviceDescriptor.getServiceName();
811                serviceProviderName = serviceDescriptor.getServiceProviderName();
812                serviceType = serviceDescriptor.getServiceType();
813            }
814            if (serviceDescriptors.size() > 0) {
815                sdtItems.add(new SdtItem(serviceName, serviceProviderName, serviceType, serviceId,
816                        originalNetworkId));
817            }
818            pos += descriptorsLength;
819        }
820        if (mListener != null) {
821            mListener.onSdtParsed(sdtItems);
822        }
823        return true;
824    }
825
826    private boolean parseDVBEIT(byte[] data) {
827        // For details of the structure for DVB ETT, see DVB Document A038 Table 7.
828        if (DEBUG) {
829            Log.d(TAG, "DVB EIT is discovered.");
830        }
831        if (data.length < 18) {
832            Log.e(TAG, "Broken DVB EIT.");
833            return false;
834        }
835        int sectionLength = ((data[1] & 0x0f) << 8) | (data[2] & 0xff);
836        int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
837        int transportStreamId = ((data[8] & 0xff) << 8) | (data[9] & 0xff);
838        int originalNetworkId = ((data[10] & 0xff) << 8) | (data[11] & 0xff);
839
840        int pos = 14;
841        List<EitItem> results = new ArrayList<>();
842        while (pos + 12 < data.length) {
843            int eventId = ((data[pos] & 0xff) << 8) + (data[pos + 1] & 0xff);
844            float modifiedJulianDate = ((data[pos + 2] &  0xff) << 8) | (data[pos + 3] & 0xff);
845            int startYear = (int) ((modifiedJulianDate - 15078.2f) / 365.25f);
846            int mjdMonth = (int) ((modifiedJulianDate - 14956.1f
847                    - (int) (startYear * 365.25f)) / 30.6001f);
848            int startDay = (int) modifiedJulianDate - 14956 - (int) (startYear * 365.25f)
849                    - (int) (mjdMonth * 30.6001f);
850            int startMonth = mjdMonth - 1;
851            if (mjdMonth == 14 || mjdMonth == 15) {
852                startYear += 1;
853                startMonth -= 12;
854            }
855            int startHour = ((data[pos + 4] & 0xf0) >> 4) * 10 + (data[pos + 4] & 0x0f);
856            int startMinute = ((data[pos + 5] & 0xf0) >> 4) * 10 + (data[pos + 5] & 0x0f);
857            int startSecond = ((data[pos + 6] & 0xf0) >> 4) * 10 + (data[pos + 6] & 0x0f);
858            Calendar calendar = Calendar.getInstance();
859            startYear += 1900;
860            calendar.set(startYear, startMonth, startDay, startHour, startMinute, startSecond);
861            long startTime = ConvertUtils.convertUnixEpochToGPSTime(
862                    calendar.getTimeInMillis() / 1000);
863            int durationInSecond = (((data[pos + 7] & 0xf0) >> 4) * 10
864                    + (data[pos + 7] & 0x0f)) * 3600
865                    + (((data[pos + 8] & 0xf0) >> 4) * 10 + (data[pos + 8] & 0x0f)) * 60
866                    + (((data[pos + 9] & 0xf0) >> 4) * 10 + (data[pos + 9] & 0x0f));
867            int descriptorsLength = ((data[pos + 10] & 0x0f) << 8)
868                    | (data[pos + 10 + 1] & 0xff);
869            int descriptorsPos = pos + 10 + 2;
870            if (data.length < descriptorsPos + descriptorsLength) {
871                Log.e(TAG, "Broken EIT.");
872                return false;
873            }
874            List<TsDescriptor> descriptors = parseDescriptors(
875                    data, descriptorsPos, descriptorsPos + descriptorsLength);
876            if (DEBUG) {
877                Log.d(TAG, String.format("DVB EIT descriptors size: %d", descriptors.size()));
878            }
879            // TODO: Add logic to generating content rating for dvb. See DVB document 6.2.28 for
880            // details. Content rating here will be null
881            String contentRating = generateContentRating(descriptors);
882            // TODO: Add logic for generating genre for dvb. See DVB document 6.2.9 for details.
883            // Genre here will be null here.
884            String broadcastGenre = generateBroadcastGenre(descriptors);
885            String canonicalGenre = generateCanonicalGenre(descriptors);
886            String titleText = generateShortEventName(descriptors);
887            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
888            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
889            pos += 12 + descriptorsLength;
890            results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
891                    startTime, durationInSecond, contentRating, audioTracks, captionTracks,
892                    broadcastGenre, canonicalGenre, null));
893        }
894        if (mListener != null) {
895            mListener.onEitParsed(sourceId, results);
896        }
897        return true;
898    }
899
900    private static List<AtscAudioTrack> generateAudioTracks(List<TsDescriptor> descriptors) {
901        // The list of audio tracks sent is located at both AC3 Audio descriptor and ISO 639
902        // Language descriptor.
903        List<AtscAudioTrack> ac3Tracks = new ArrayList<>();
904        List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
905        for (TsDescriptor descriptor : descriptors) {
906            if (descriptor instanceof Ac3AudioDescriptor) {
907                Ac3AudioDescriptor audioDescriptor =
908                        (Ac3AudioDescriptor) descriptor;
909                AtscAudioTrack audioTrack = new AtscAudioTrack();
910                if (audioDescriptor.getLanguage() != null) {
911                    audioTrack.language = audioDescriptor.getLanguage();
912                }
913                if (audioTrack.language == null) {
914                    audioTrack.language = "";
915                }
916                audioTrack.audioType = AtscAudioTrack.AUDIOTYPE_UNDEFINED;
917                audioTrack.channelCount = audioDescriptor.getNumChannels();
918                audioTrack.sampleRate = audioDescriptor.getSampleRate();
919                ac3Tracks.add(audioTrack);
920            }
921        }
922        for (TsDescriptor descriptor : descriptors) {
923            if (descriptor instanceof Iso639LanguageDescriptor) {
924                Iso639LanguageDescriptor iso639LanguageDescriptor =
925                        (Iso639LanguageDescriptor) descriptor;
926                iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks());
927            }
928        }
929
930        // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate
931        // while a ISO 639 Language descriptor only has a audio type, which describes a main use
932        // case of its audio track.
933        // Some channels contain only AC3 audio stream descriptors with valid language values.
934        // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language
935        // descriptor per audio track, and those AC3 audio stream descriptors often have a null
936        // value of language field.
937        // Combines two descriptors into one in order to gather more audio track specific
938        // information as much as possible.
939        List<AtscAudioTrack> tracks = new ArrayList<>();
940        if (!ac3Tracks.isEmpty() && !iso639LanguageTracks.isEmpty()
941                && ac3Tracks.size() != iso639LanguageTracks.size()) {
942            // This shouldn't be happen. In here, it handles two cases. The first case is that the
943            // only one type of descriptors arrives. The second case is that the two types of
944            // descriptors have the same number of tracks.
945            Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size");
946            return tracks;
947        }
948        int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size());
949        for (int i = 0; i < size; ++i) {
950            AtscAudioTrack audioTrack = null;
951            if (i < ac3Tracks.size()) {
952                audioTrack = ac3Tracks.get(i);
953            }
954            if (i < iso639LanguageTracks.size()) {
955                if (audioTrack == null) {
956                    audioTrack = iso639LanguageTracks.get(i);
957                } else {
958                    AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i);
959                    if (audioTrack.language == null || TextUtils.equals(audioTrack.language, "")) {
960                        audioTrack.language = iso639LanguageTrack.language;
961                    }
962                    audioTrack.audioType = iso639LanguageTrack.audioType;
963                }
964            }
965            String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.language);
966            if (language != null) {
967                audioTrack.language = language;
968            }
969            tracks.add(audioTrack);
970        }
971        return tracks;
972    }
973
974    private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) {
975        List<AtscCaptionTrack> services = new ArrayList<>();
976        for (TsDescriptor descriptor : descriptors) {
977            if (descriptor instanceof CaptionServiceDescriptor) {
978                CaptionServiceDescriptor captionServiceDescriptor =
979                        (CaptionServiceDescriptor) descriptor;
980                services.addAll(captionServiceDescriptor.getCaptionTracks());
981            }
982        }
983        return services;
984    }
985
986    @VisibleForTesting
987    static String generateContentRating(List<TsDescriptor> descriptors) {
988        Set<String> contentRatings = new ArraySet<>();
989        List<RatingRegion> usRatingRegions = getRatingRegions(descriptors, RATING_REGION_US_TV);
990        List<RatingRegion> krRatingRegions = getRatingRegions(descriptors, RATING_REGION_KR_TV);
991        for (RatingRegion region : usRatingRegions) {
992            String contentRating = getUsRating(region);
993            if (contentRating != null) {
994                contentRatings.add(contentRating);
995            }
996        }
997        for (RatingRegion region : krRatingRegions) {
998            String contentRating = getKrRating(region);
999            if (contentRating != null) {
1000                contentRatings.add(contentRating);
1001            }
1002        }
1003        return TextUtils.join(",", contentRatings);
1004    }
1005
1006    /**
1007     * Gets a list of {@link RatingRegion} in the specific region.
1008     *
1009     * @param descriptors {@link TsDescriptor} list which may contains rating information
1010     * @param region the specific region
1011     * @return a list of {@link RatingRegion} in the specific region
1012     */
1013    private static List<RatingRegion> getRatingRegions(List<TsDescriptor> descriptors, int region) {
1014        List<RatingRegion> ratingRegions = new ArrayList<>();
1015        for (TsDescriptor descriptor : descriptors) {
1016            if (!(descriptor instanceof ContentAdvisoryDescriptor)) {
1017                continue;
1018            }
1019            ContentAdvisoryDescriptor contentAdvisoryDescriptor =
1020                    (ContentAdvisoryDescriptor) descriptor;
1021            for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) {
1022                if (ratingRegion.getName() == region) {
1023                    ratingRegions.add(ratingRegion);
1024                }
1025            }
1026        }
1027        return ratingRegions;
1028    }
1029
1030    /**
1031     * Gets US content rating and subratings (if any).
1032     *
1033     * @param ratingRegion a {@link RatingRegion} instance which may contain rating information.
1034     * @return A string representing the US content rating and subratings. The format of the string
1035     *     is defined in {@link TvContentRating}. null, if no such a string exists.
1036     */
1037    private static String getUsRating(RatingRegion ratingRegion) {
1038        if (ratingRegion.getName() != RATING_REGION_US_TV) {
1039            return null;
1040        }
1041        List<RegionalRating> regionalRatings = ratingRegion.getRegionalRatings();
1042        String rating = null;
1043        int ratingIndex = VALUE_US_TV_NONE;
1044        List<String> subratings = new ArrayList<>();
1045        for (RegionalRating index : regionalRatings) {
1046            // See Table 3 of ANSI-CEA-766-D
1047            int dimension = index.getDimension();
1048            int value = index.getRating();
1049            switch (dimension) {
1050                    // According to Table 6.27 of ATSC A65,
1051                    // the dimensions shall be in increasing order.
1052                    // Therefore, rating and ratingIndex are assigned before any corresponding
1053                    // subrating.
1054                case DIMENSION_US_TV_RATING:
1055                    if (value >= VALUE_US_TV_G && value < RATING_REGION_TABLE_US_TV.length) {
1056                        rating = RATING_REGION_TABLE_US_TV[value];
1057                        ratingIndex = value;
1058                    }
1059                    break;
1060                case DIMENSION_US_TV_D:
1061                    if (value == 1
1062                            && (ratingIndex == VALUE_US_TV_PG || ratingIndex == VALUE_US_TV_14)) {
1063                        // US_TV_D is applicable to US_TV_PG and US_TV_14
1064                        subratings.add(RATING_REGION_TABLE_US_TV_SUBRATING[dimension - 1]);
1065                    }
1066                    break;
1067                case DIMENSION_US_TV_L:
1068                case DIMENSION_US_TV_S:
1069                case DIMENSION_US_TV_V:
1070                    if (value == 1
1071                            && ratingIndex >= VALUE_US_TV_PG
1072                            && ratingIndex <= VALUE_US_TV_MA) {
1073                        // US_TV_L, US_TV_S, and US_TV_V are applicable to
1074                        // US_TV_PG, US_TV_14 and US_TV_MA
1075                        subratings.add(RATING_REGION_TABLE_US_TV_SUBRATING[dimension - 1]);
1076                    }
1077                    break;
1078                case DIMENSION_US_TV_Y:
1079                    if (rating == null) {
1080                        if (value == VALUE_US_TV_Y) {
1081                            rating = STRING_US_TV_Y;
1082                        } else if (value == VALUE_US_TV_Y7) {
1083                            rating = STRING_US_TV_Y7;
1084                        }
1085                    }
1086                    break;
1087                case DIMENSION_US_TV_FV:
1088                    if (STRING_US_TV_Y7.equals(rating) && value == 1) {
1089                        // US_TV_FV is applicable to US_TV_Y7
1090                        subratings.add(STRING_US_TV_FV);
1091                    }
1092                    break;
1093                case DIMENSION_US_MV_RATING:
1094                    if (value >= VALUE_US_MV_G && value <= VALUE_US_MV_X) {
1095                        if (value == VALUE_US_MV_X) {
1096                            // US_MV_X was replaced by US_MV_NC17 in 1990,
1097                            // and it's not supported by TvContentRating
1098                            value = VALUE_US_MV_NC17;
1099                        }
1100                        if (rating != null) {
1101                            // According to Table 3 of ANSI-CEA-766-D,
1102                            // DIMENSION_US_TV_RATING and DIMENSION_US_MV_RATING shall not be
1103                            // present in the same descriptor.
1104                            Log.w(
1105                                    TAG,
1106                                    "DIMENSION_US_TV_RATING and DIMENSION_US_MV_RATING are "
1107                                            + "present in the same descriptor");
1108                        } else {
1109                            return TvContentRating.createRating(
1110                                            RATING_DOMAIN,
1111                                            RATING_REGION_RATING_SYSTEM_US_MV,
1112                                            RATING_REGION_TABLE_US_MV[value - 2])
1113                                    .flattenToString();
1114                        }
1115                    }
1116                    break;
1117
1118                default:
1119                    break;
1120            }
1121        }
1122        if (rating == null) {
1123            return null;
1124        }
1125
1126        String[] subratingArray = subratings.toArray(new String[subratings.size()]);
1127        return TvContentRating.createRating(
1128                        RATING_DOMAIN, RATING_REGION_RATING_SYSTEM_US_TV, rating, subratingArray)
1129                .flattenToString();
1130    }
1131
1132    /**
1133     * Gets KR(South Korea) content rating.
1134     *
1135     * @param ratingRegion a {@link RatingRegion} instance which may contain rating information.
1136     * @return A string representing the KR content rating. The format of the string is defined in
1137     *     {@link TvContentRating}. null, if no such a string exists.
1138     */
1139    private static String getKrRating(RatingRegion ratingRegion) {
1140        if (ratingRegion.getName() != RATING_REGION_KR_TV) {
1141            return null;
1142        }
1143        List<RegionalRating> regionalRatings = ratingRegion.getRegionalRatings();
1144        String rating = null;
1145        for (RegionalRating index : regionalRatings) {
1146            if (index.getDimension() == 0
1147                    && index.getRating() >= 0
1148                    && index.getRating() < RATING_REGION_TABLE_KR_TV.length) {
1149                rating = RATING_REGION_TABLE_KR_TV[index.getRating()];
1150                break;
1151            }
1152        }
1153        if (rating == null) {
1154            return null;
1155        }
1156        return TvContentRating.createRating(
1157                        RATING_DOMAIN, RATING_REGION_RATING_SYSTEM_KR_TV, rating)
1158                .flattenToString();
1159    }
1160
1161    private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
1162        for (TsDescriptor descriptor : descriptors) {
1163            if (descriptor instanceof GenreDescriptor) {
1164                GenreDescriptor genreDescriptor =
1165                        (GenreDescriptor) descriptor;
1166                return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
1167            }
1168        }
1169        return null;
1170    }
1171
1172    private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
1173        for (TsDescriptor descriptor : descriptors) {
1174            if (descriptor instanceof GenreDescriptor) {
1175                GenreDescriptor genreDescriptor =
1176                        (GenreDescriptor) descriptor;
1177                return Genres.encode(genreDescriptor.getCanonicalGenres());
1178            }
1179        }
1180        return null;
1181    }
1182
1183    private static List<ServiceDescriptor> generateServiceDescriptors(
1184            List<TsDescriptor> descriptors) {
1185        List<ServiceDescriptor> serviceDescriptors = new ArrayList<>();
1186        for (TsDescriptor descriptor : descriptors) {
1187            if (descriptor instanceof ServiceDescriptor) {
1188                ServiceDescriptor serviceDescriptor = (ServiceDescriptor) descriptor;
1189                serviceDescriptors.add(serviceDescriptor);
1190            }
1191        }
1192        return serviceDescriptors;
1193    }
1194
1195    private static String generateShortEventName(List<TsDescriptor> descriptors) {
1196        for (TsDescriptor descriptor : descriptors) {
1197            if (descriptor instanceof ShortEventDescriptor) {
1198                ShortEventDescriptor shortEventDescriptor = (ShortEventDescriptor) descriptor;
1199                return shortEventDescriptor.getEventName();
1200            }
1201        }
1202        return "";
1203    }
1204
1205    private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) {
1206        // For details of the structure for descriptors, see ATSC A/65 Section 6.9.
1207        List<TsDescriptor> descriptors = new ArrayList<>();
1208        if (data.length < limit) {
1209            return descriptors;
1210        }
1211        int pos = offset;
1212        while (pos + 1 < limit) {
1213            int tag = data[pos] & 0xff;
1214            int length = data[pos + 1] & 0xff;
1215            if (length <= 0) {
1216                break;
1217            }
1218            if (limit < pos + length + 2) {
1219                break;
1220            }
1221            if (DEBUG) {
1222                Log.d(TAG, String.format("Descriptor tag: %02x", tag));
1223            }
1224            TsDescriptor descriptor = null;
1225            switch (tag) {
1226                case DESCRIPTOR_TAG_CONTENT_ADVISORY:
1227                    descriptor = parseContentAdvisory(data, pos, pos + length + 2);
1228                    break;
1229
1230                case DESCRIPTOR_TAG_CAPTION_SERVICE:
1231                    descriptor = parseCaptionService(data, pos, pos + length + 2);
1232                    break;
1233
1234                case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME:
1235                    descriptor = parseLongChannelName(data, pos, pos + length + 2);
1236                    break;
1237
1238                case DESCRIPTOR_TAG_GENRE:
1239                    descriptor = parseGenre(data, pos, pos + length + 2);
1240                    break;
1241
1242                case DESCRIPTOR_TAG_AC3_AUDIO_STREAM:
1243                    descriptor = parseAc3AudioStream(data, pos, pos + length + 2);
1244                    break;
1245
1246                case DESCRIPTOR_TAG_ISO639LANGUAGE:
1247                    descriptor = parseIso639Language(data, pos, pos + length + 2);
1248                    break;
1249
1250                case DVB_DESCRIPTOR_TAG_SERVICE:
1251                    descriptor = parseDvbService(data, pos, pos + length + 2);
1252                    break;
1253
1254                case DVB_DESCRIPTOR_TAG_SHORT_EVENT:
1255                    descriptor = parseDvbShortEvent(data, pos, pos + length + 2);
1256                    break;
1257
1258                case DVB_DESCRIPTOR_TAG_CONTENT:
1259                    descriptor = parseDvbContent(data, pos, pos + length + 2);
1260                    break;
1261
1262                case DVB_DESCRIPTOR_TAG_PARENTAL_RATING:
1263                    descriptor = parseDvbParentalRating(data, pos, pos + length + 2);
1264                    break;
1265
1266                default:
1267            }
1268            if (descriptor != null) {
1269                if (DEBUG) {
1270                    Log.d(TAG, "Descriptor parsed: " + descriptor);
1271                }
1272                descriptors.add(descriptor);
1273            }
1274            pos += length + 2;
1275        }
1276        return descriptors;
1277    }
1278
1279    private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) {
1280        // For the details of the structure of ISO 639 language descriptor,
1281        // see ISO13818-1 second edition Section 2.6.18.
1282        pos += 2;
1283        List<AtscAudioTrack> audioTracks = new ArrayList<>();
1284        while (pos + 4 <= limit) {
1285            if (limit <= pos + 3) {
1286                Log.e(TAG, "Broken Iso639Language.");
1287                return null;
1288            }
1289            String language = new String(data, pos, 3);
1290            int audioType = data[pos + 3] & 0xff;
1291            AtscAudioTrack audioTrack = new AtscAudioTrack();
1292            audioTrack.language = language;
1293            audioTrack.audioType = audioType;
1294            audioTracks.add(audioTrack);
1295            pos += 4;
1296        }
1297        return new Iso639LanguageDescriptor(audioTracks);
1298    }
1299
1300    private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) {
1301        // For the details of the structure of caption service descriptor,
1302        // see ATSC A/65 Section 6.9.2.
1303        if (limit <= pos + 2) {
1304            Log.e(TAG, "Broken CaptionServiceDescriptor.");
1305            return null;
1306        }
1307        List<AtscCaptionTrack> services = new ArrayList<>();
1308        pos += 2;
1309        int numberServices = data[pos] & 0x1f;
1310        ++pos;
1311        if (limit < pos + numberServices * 6) {
1312            Log.e(TAG, "Broken CaptionServiceDescriptor.");
1313            return null;
1314        }
1315        for (int i = 0; i < numberServices; ++i) {
1316            String language = new String(Arrays.copyOfRange(data, pos, pos + 3));
1317            pos += 3;
1318            boolean ccType = (data[pos] & 0x80) != 0;
1319            if (!ccType) {
1320                pos +=3;
1321                continue;
1322            }
1323            int captionServiceNumber = data[pos] & 0x3f;
1324            ++pos;
1325            boolean easyReader = (data[pos] & 0x80) != 0;
1326            boolean wideAspectRatio = (data[pos] & 0x40) != 0;
1327            byte[] reserved = new byte[2];
1328            reserved[0] = (byte) (data[pos] << 2);
1329            reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6);
1330            reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2);
1331            pos += 2;
1332            AtscCaptionTrack captionTrack = new AtscCaptionTrack();
1333            captionTrack.language = language;
1334            captionTrack.serviceNumber = captionServiceNumber;
1335            captionTrack.easyReader = easyReader;
1336            captionTrack.wideAspectRatio = wideAspectRatio;
1337            services.add(captionTrack);
1338        }
1339        return new CaptionServiceDescriptor(services);
1340    }
1341
1342    private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) {
1343        // For details of the structure for content advisory descriptor, see A/65 Table 6.27.
1344        if (limit <= pos + 2) {
1345            Log.e(TAG, "Broken ContentAdvisory");
1346            return null;
1347        }
1348        int count = data[pos + 2] & 0x3f;
1349        pos += 3;
1350        List<RatingRegion> ratingRegions = new ArrayList<>();
1351        for (int i = 0; i < count; ++i) {
1352            if (limit <= pos + 1) {
1353                Log.e(TAG, "Broken ContentAdvisory");
1354                return null;
1355            }
1356            List<RegionalRating> indices = new ArrayList<>();
1357            int ratingRegion = data[pos] & 0xff;
1358            int dimensionCount = data[pos + 1] & 0xff;
1359            pos += 2;
1360            int previousDimension = -1;
1361            for (int j = 0; j < dimensionCount; ++j) {
1362                if (limit <= pos + 1) {
1363                    Log.e(TAG, "Broken ContentAdvisory");
1364                    return null;
1365                }
1366                int dimensionIndex = data[pos] & 0xff;
1367                int ratingValue = data[pos + 1] & 0x0f;
1368                if (dimensionIndex <= previousDimension) {
1369                    // According to Table 6.27 of ATSC A65,
1370                    // the indices shall be in increasing order.
1371                    Log.e(TAG, "Broken ContentAdvisory");
1372                    return null;
1373                }
1374                previousDimension = dimensionIndex;
1375                pos += 2;
1376                indices.add(new RegionalRating(dimensionIndex, ratingValue));
1377            }
1378            if (limit <= pos) {
1379                Log.e(TAG, "Broken ContentAdvisory");
1380                return null;
1381            }
1382            int ratingDescriptionLength = data[pos] & 0xff;
1383            ++pos;
1384            if (limit < pos + ratingDescriptionLength) {
1385                Log.e(TAG, "Broken ContentAdvisory");
1386                return null;
1387            }
1388            String ratingDescription = extractText(data, pos);
1389            pos += ratingDescriptionLength;
1390            ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices));
1391        }
1392        return new ContentAdvisoryDescriptor(ratingRegions);
1393    }
1394
1395    private static ExtendedChannelNameDescriptor parseLongChannelName(byte[] data, int pos,
1396            int limit) {
1397        if (limit <= pos + 2) {
1398            Log.e(TAG, "Broken ExtendedChannelName.");
1399            return null;
1400        }
1401        pos += 2;
1402        String text = extractText(data, pos);
1403        if (text == null) {
1404            Log.e(TAG, "Broken ExtendedChannelName.");
1405            return null;
1406        }
1407        return new ExtendedChannelNameDescriptor(text);
1408    }
1409
1410    private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) {
1411        pos += 2;
1412        int attributeCount = data[pos] & 0x1f;
1413        if (limit <= pos + attributeCount) {
1414            Log.e(TAG, "Broken Genre.");
1415            return null;
1416        }
1417        HashSet<String> broadcastGenreSet = new HashSet<>();
1418        HashSet<String> canonicalGenreSet = new HashSet<>();
1419        for (int i = 0; i < attributeCount; ++i) {
1420            ++pos;
1421            int genreCode = data[pos] & 0xff;
1422            if (genreCode < BROADCAST_GENRES_TABLE.length) {
1423                String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode];
1424                if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) {
1425                    broadcastGenreSet.add(broadcastGenre);
1426                }
1427            }
1428            if (genreCode < CANONICAL_GENRES_TABLE.length) {
1429                String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode];
1430                if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) {
1431                    canonicalGenreSet.add(canonicalGenre);
1432                }
1433            }
1434        }
1435        return new GenreDescriptor(broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
1436                canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
1437    }
1438
1439    private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) {
1440        // For details of the AC3 audio stream descriptor, see A/52 Table A4.1.
1441        if (limit <= pos + 5) {
1442            Log.e(TAG, "Broken AC3 audio stream descriptor.");
1443            return null;
1444        }
1445        pos += 2;
1446        byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5);
1447        byte bsid = (byte) (data[pos] & 0x1f);
1448        ++pos;
1449        byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2);
1450        byte surroundMode = (byte) (data[pos] & 0x03);
1451        ++pos;
1452        byte bsmod = (byte) ((data[pos] & 0xe0) >> 5);
1453        int numChannels = (data[pos] & 0x1e) >> 1;
1454        boolean fullSvc = (data[pos] & 0x01) != 0;
1455        ++pos;
1456        byte langCod = data[pos];
1457        byte langCod2 = 0;
1458        if (numChannels == 0) {
1459            if (limit <= pos) {
1460                Log.e(TAG, "Broken AC3 audio stream descriptor.");
1461                return null;
1462            }
1463            ++pos;
1464            langCod2 = data[pos];
1465        }
1466        if (limit <= pos + 1) {
1467            Log.e(TAG, "Broken AC3 audio stream descriptor.");
1468            return null;
1469        }
1470        byte mainId = 0;
1471        byte priority = 0;
1472        byte asvcflags = 0;
1473        ++pos;
1474        if (bsmod < 2) {
1475            mainId = (byte) ((data[pos] & 0xe0) >> 5);
1476            priority = (byte) ((data[pos] & 0x18) >> 3);
1477            if ((data[pos] & 0x07) != 0x07) {
1478                Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed");
1479                return null;
1480            }
1481        } else {
1482            asvcflags = data[pos];
1483        }
1484
1485        // See A/52B Table A3.6 num_channels.
1486        int numEncodedChannels;
1487        switch (numChannels) {
1488            case 1:
1489            case 8:
1490                numEncodedChannels = 1;
1491                break;
1492            case 2:
1493            case 9:
1494                numEncodedChannels = 2;
1495                break;
1496            case 3:
1497            case 4:
1498            case 10:
1499                numEncodedChannels = 3;
1500                break;
1501            case 5:
1502            case 6:
1503            case 11:
1504                numEncodedChannels = 4;
1505                break;
1506            case 7:
1507            case 12:
1508                numEncodedChannels = 5;
1509                break;
1510            case 13:
1511                numEncodedChannels = 6;
1512                break;
1513            default:
1514                numEncodedChannels = 0;
1515                break;
1516        }
1517
1518        if (limit <= pos + 1) {
1519            Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
1520            return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
1521                    numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags,
1522                    null, null, null);
1523        }
1524        ++pos;
1525        int textLen = (data[pos] & 0xfe) >> 1;
1526        boolean textCode = (data[pos] & 0x01) != 0;
1527        ++pos;
1528        String text = "";
1529        if (textLen > 0) {
1530            if (limit < pos + textLen) {
1531                Log.e(TAG, "Broken AC3 audio stream descriptor");
1532                return null;
1533            }
1534            if (textCode) {
1535                text = new String(data, pos, textLen);
1536            } else {
1537                text = new String(data, pos, textLen, Charset.forName("UTF-16"));
1538            }
1539            pos += textLen;
1540        }
1541        String language = null;
1542        String language2 = null;
1543        if (pos < limit) {
1544            // Many AC3 audio stream descriptors skip the language fields.
1545            boolean languageFlag1 = (data[pos] & 0x80) != 0;
1546            boolean languageFlag2 = (data[pos] & 0x40) != 0;
1547            if ((data[pos] & 0x3f) != 0x3f) {
1548                Log.e(TAG, "Broken AC3 audio stream descriptor");
1549                return null;
1550            }
1551            if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) {
1552                Log.e(TAG, "Broken AC3 audio stream descriptor");
1553                return null;
1554            }
1555            ++pos;
1556            if (languageFlag1) {
1557                language = new String(data, pos, 3);
1558                pos += 3;
1559            }
1560            if (languageFlag2) {
1561                language2 = new String(data, pos, 3);
1562            }
1563        }
1564
1565        return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
1566                numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags, text,
1567                language, language2);
1568    }
1569
1570    private static TsDescriptor parseDvbService(byte[] data, int pos, int limit) {
1571        // For details of DVB service descriptors, see DVB Document A038 Table 86.
1572        if (limit < pos + 5) {
1573            Log.e(TAG, "Broken service descriptor.");
1574            return null;
1575        }
1576        pos += 2;
1577        int serviceType = data[pos] & 0xff;
1578        pos++;
1579        int serviceProviderNameLength = data[pos] & 0xff;
1580        pos++;
1581        String serviceProviderName = extractTextFromDvb(data, pos, serviceProviderNameLength);
1582        pos += serviceProviderNameLength;
1583        int serviceNameLength = data[pos] & 0xff;
1584        pos++;
1585        String serviceName = extractTextFromDvb(data, pos, serviceNameLength);
1586        return new ServiceDescriptor(serviceType, serviceProviderName, serviceName);
1587    }
1588
1589    private static TsDescriptor parseDvbShortEvent(byte[] data, int pos, int limit) {
1590        // For details of DVB service descriptors, see DVB Document A038 Table 91.
1591        if (limit < pos + 7) {
1592            Log.e(TAG, "Broken short event descriptor.");
1593            return null;
1594        }
1595        pos += 2;
1596        String language = new String(data, pos, 3);
1597        int eventNameLength = data[pos + 3] & 0xff;
1598        pos += 4;
1599        if (pos + eventNameLength > limit) {
1600            Log.e(TAG, "Broken short event descriptor.");
1601            return null;
1602        }
1603        String eventName = new String(data, pos, eventNameLength);
1604        pos += eventNameLength;
1605        int textLength = data[pos] & 0xff;
1606        if (pos + textLength > limit) {
1607            Log.e(TAG, "Broken short event descriptor.");
1608            return null;
1609        }
1610        pos++;
1611        String text = new String(data, pos, textLength);
1612        return new ShortEventDescriptor(language, eventName, text);
1613    }
1614
1615    private static TsDescriptor parseDvbContent(byte[] data, int pos, int limit) {
1616        // TODO: According to DVB Document A038 Table 27 to add a parser for content descriptor to
1617        // get content genre.
1618        return null;
1619    }
1620
1621    private static TsDescriptor parseDvbParentalRating(byte[] data, int pos, int limit) {
1622        // For details of DVB service descriptors, see DVB Document A038 Table 81.
1623        HashMap<String, Integer> ratings = new HashMap<>();
1624        pos += 2;
1625        while (pos + 4 <= limit) {
1626            String countryCode = new String(data, pos, 3);
1627            int rating = data[pos + 3] & 0xff;
1628            pos += 4;
1629            if (rating > 15) {
1630                // Rating > 15 means that the ratings is defined by broadcaster.
1631                continue;
1632            }
1633            ratings.put(countryCode, rating + 3);
1634        }
1635        return new ParentalRatingDescriptor(ratings);
1636    }
1637
1638    private static int getShortNameSize(byte[] data, int offset) {
1639        for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) {
1640            if (data[offset + i] == 0 && data[offset + i + 1] == 0) {
1641                return i;
1642            }
1643        }
1644        return MAX_SHORT_NAME_BYTES;
1645    }
1646
1647    private static String extractText(byte[] data, int pos) {
1648        if (data.length < pos)  {
1649            return null;
1650        }
1651        int numStrings = data[pos] & 0xff;
1652        pos++;
1653        for (int i = 0; i < numStrings; ++i) {
1654            if (data.length <= pos + 3) {
1655                Log.e(TAG, "Broken text.");
1656                return null;
1657            }
1658            int numSegments = data[pos + 3] & 0xff;
1659            pos += 4;
1660            for (int j = 0; j < numSegments; ++j) {
1661                if (data.length <= pos + 2) {
1662                    Log.e(TAG, "Broken text.");
1663                    return null;
1664                }
1665                int compressionType = data[pos] & 0xff;
1666                int mode = data[pos + 1] & 0xff;
1667                int numBytes = data[pos + 2] & 0xff;
1668                if (data.length < pos + 3 + numBytes) {
1669                    Log.e(TAG, "Broken text.");
1670                    return null;
1671                }
1672                byte[] bytes = Arrays.copyOfRange(data, pos + 3, pos + 3 + numBytes);
1673                if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
1674                    try {
1675                        switch (mode) {
1676                            case MODE_SELECTED_UNICODE_RANGE_1:
1677                                return new String(bytes, "ISO-8859-1");
1678                            case MODE_SCSU:
1679                                return UnicodeDecompressor.decompress(bytes);
1680                            case MODE_UTF16:
1681                                return new String(bytes, "UTF-16");
1682                        }
1683                    } catch (UnsupportedEncodingException e) {
1684                        Log.e(TAG, "Unsupported text format.", e);
1685                    }
1686                }
1687                pos += 3 + numBytes;
1688            }
1689        }
1690        return null;
1691    }
1692
1693    private static String extractTextFromDvb(byte[] data, int pos, int length) {
1694        // For details of DVB character set selection, see DVB Document A038 Annex A.
1695        if (data.length < pos + length) {
1696            return null;
1697        }
1698        try {
1699            String charsetPrefix = "ISO-8859-";
1700            switch (data[0]) {
1701                case 0x01:
1702                case 0x02:
1703                case 0x03:
1704                case 0x04:
1705                case 0x05:
1706                case 0x06:
1707                case 0x07:
1708                case 0x09:
1709                case 0x0A:
1710                case 0x0B:
1711                    String charset = charsetPrefix + String.valueOf(data[0] & 0xff + 4);
1712                    return new String(data, pos, length, charset);
1713                case 0x10:
1714                    if (length < 3) {
1715                        Log.e(TAG, "Broken DVB text");
1716                        return null;
1717                    }
1718                    int codeTable = data[pos + 2] & 0xff;
1719                    if (data[pos + 1] == 0 && codeTable > 0 && codeTable < 15) {
1720                        return new String(
1721                                data, pos, length, charsetPrefix + String.valueOf(codeTable));
1722                    } else {
1723                        return new String(data, pos, length, "ISO-8859-1");
1724                    }
1725                case 0x11:
1726                case 0x14:
1727                case 0x15:
1728                    return new String(data, pos, length, "UTF-16BE");
1729                case 0x12:
1730                    return new String(data, pos, length, "EUC-KR");
1731                case 0x13:
1732                    return new String(data, pos, length, "GB2312");
1733                default:
1734                    return new String(data, pos, length, "ISO-8859-1");
1735            }
1736        } catch (UnsupportedEncodingException e) {
1737            Log.e(TAG, "Unsupported text format.", e);
1738        }
1739        return new String(data, pos, length);
1740    }
1741
1742    private static boolean checkSanity(byte[] data) {
1743        if (data.length <= 1) {
1744            return false;
1745        }
1746        boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
1747        if (hasCRC) {
1748            int crc = 0xffffffff;
1749            for(byte b : data) {
1750                int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
1751                crc = CRC_TABLE[index] ^ (crc << 8);
1752            }
1753            if(crc != 0){
1754                return false;
1755            }
1756        }
1757        return true;
1758    }
1759}
1760