SectionParser.java revision 65fda1eaa94968bb55d5ded10dcb0b3f37fb05f2
11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.ts;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.tv.TvContentRating;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.tv.TvContract.Programs.Genres;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.text.TextUtils;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.SparseArray;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Channel;
2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsiData.PatItem;
2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsiData.PmtItem;
2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor;
2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor;
3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor;
3165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.EitItem;
3265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.EttItem;
3365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor;
3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.GenreDescriptor;
3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor;
3665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.MgtItem;
3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.PsipSection;
3865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.RatingRegion;
3965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.RegionalRating;
4065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.TsDescriptor;
4165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.VctItem;
4265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Track.AtscAudioTrack;
4365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Track.AtscCaptionTrack;
4465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.util.ByteArrayBuffer;
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.ibm.icu.text.UnicodeDecompressor;
471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.UnsupportedEncodingException;
491abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.charset.Charset;
501abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList;
511abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Arrays;
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashMap;
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashSet;
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List;
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Parses ATSC PSIP sections.
581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
591abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class SectionParser {
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String TAG = "SectionParser";
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean DEBUG = false;
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_PAT = (byte) 0x00;
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_PMT = (byte) 0x02;
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_MGT = (byte) 0xc7;
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_TVCT = (byte) 0xc8;
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_CVCT = (byte) 0xc9;
681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_EIT = (byte) 0xcb;
691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_ETT = (byte) 0xcc;
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // For details of the structure for the tags of descriptors, see ATSC A/65 Table 6.25.
721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_ISO639LANGUAGE = 0x0a;
731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86;
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_CONTENT_ADVISORY = 0x87;
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_AC3_AUDIO_STREAM = 0x81;
761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME = 0xa0;
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_GENRE = 0xab;
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte COMPRESSION_TYPE_NO_COMPRESSION = (byte) 0x00;
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00;  // 0x0000 - 0x00ff
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte MODE_UTF16 = (byte) 0x3f;
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte MODE_SCSU = (byte) 0x3e;
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int MAX_SHORT_NAME_BYTES = 14;
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See ANSI/CEA-766-C.
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int RATING_REGION_US_TV = 1;
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int RATING_REGION_KR_TV = 4;
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // The following values are defined in the live channels app.
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See https://developer.android.com/reference/android/media/tv/TvContentRating.html.
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String RATING_REGION_RATING_SYSTEM_US_TV = "US_TV";
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String RATING_REGION_RATING_SYSTEM_KR_TV = "KR_TV";
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] RATING_REGION_TABLE_US_TV = {
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        "US_TV_Y", "US_TV_Y7", "US_TV_G", "US_TV_PG", "US_TV_14", "US_TV_MA"
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] RATING_REGION_TABLE_KR_TV = {
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        "KR_TV_ALL", "KR_TV_7", "KR_TV_12", "KR_TV_15", "KR_TV_19"
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /*
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * The following CRC table is from the code generated by the following command.
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * To see the details of pycrc, visit http://www.tty1.net/pycrc/index_en.html
1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int[] CRC_TABLE = {
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // A table which maps ATSC genres to TIF genres.
1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See ATSC/65 Table 6.20.
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] CANONICAL_GENRES_TABLE = {
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION, Genres.ENTERTAINMENT, Genres.MOVIES, Genres.NEWS,
1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.LIFE_STYLE, Genres.SPORTS, null, Genres.MOVIES,
1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.FAMILY_KIDS, Genres.DRAMA, null, Genres.ENTERTAINMENT, Genres.SPORTS,
1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS,
1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC, Genres.EDUCATION,
1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
1931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.COMEDY,
1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC,
1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MOVIES, Genres.ENTERTAINMENT, Genres.NEWS, Genres.DRAMA,
1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION, Genres.MOVIES, Genres.SPORTS, Genres.MOVIES,
1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.LIFE_STYLE, Genres.ARTS, Genres.LIFE_STYLE, Genres.SPORTS,
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.GAMING, Genres.LIFE_STYLE, Genres.SPORTS,
2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.LIFE_STYLE, Genres.EDUCATION, Genres.EDUCATION, Genres.LIFE_STYLE,
2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.LIFE_STYLE, Genres.MOVIES, Genres.NEWS,
2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION,
2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION,
2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.DRAMA, Genres.MUSIC, Genres.MOVIES,
2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.ANIMAL_WILDLIFE,
2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.PREMIER,
2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.ARTS,
2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MOVIES, Genres.TECH_SCIENCE, Genres.DRAMA,
2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SHOPPING, Genres.DRAMA,
2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MOVIES, Genres.ENTERTAINMENT, Genres.TECH_SCIENCE, Genres.SPORTS,
2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.TRAVEL, Genres.ENTERTAINMENT, Genres.ARTS, Genres.NEWS,
2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.ARTS, Genres.SPORTS, Genres.SPORTS, Genres.NEWS,
2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.SPORTS, Genres.SPORTS, Genres.FAMILY_KIDS,
2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.FAMILY_KIDS, Genres.MOVIES,
2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.TECH_SCIENCE, Genres.MUSIC,
2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.FAMILY_KIDS, Genres.NEWS, Genres.SPORTS,
2331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.NEWS, Genres.SPORTS, Genres.ANIMAL_WILDLIFE,
2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC, Genres.NEWS, Genres.SPORTS,
2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.NEWS, Genres.NEWS, Genres.NEWS, Genres.NEWS,
2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.MOVIES, Genres.ARTS, Genres.ANIMAL_WILDLIFE,
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC, Genres.MUSIC, Genres.MOVIES, Genres.EDUCATION,
2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.DRAMA, Genres.SPORTS, Genres.SPORTS, Genres.SPORTS,
2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS,
2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.SPORTS,
2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
2451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // A table which contains ATSC categorical genre code assignments.
2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See ATSC/65 Table 6.20.
2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] BROADCAST_GENRES_TABLE = new String[] {
2491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Education", "Entertainment", "Movie", "News",
2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Religious", "Sports", "Other", "Action",
2591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Advertisement", "Animated", "Anthology", "Automobile",
2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Awards", "Baseball", "Basketball", "Bulletin",
2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Business", "Classical", "College", "Combat",
2621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Comedy", "Commentary", "Concert", "Consumer",
2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Contemporary", "Crime", "Dance", "Documentary",
2641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Drama", "Elementary", "Erotica", "Exercise",
2651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Fantasy", "Farm", "Fashion", "Fiction",
2661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Food", "Football", "Foreign", "Fund Raiser",
2671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Game/Quiz", "Garden", "Golf", "Government",
2681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Health", "High School", "History", "Hobby",
2691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Hockey", "Home", "Horror", "Information",
2701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Instruction", "International", "Interview", "Language",
2711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Legal", "Live", "Local", "Math",
2721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Medical", "Meeting", "Military", "Miniseries",
2731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Music", "Mystery", "National", "Nature",
2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Police", "Politics", "Premier", "Prerecorded",
2751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Product", "Professional", "Public", "Racing",
2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Reading", "Repair", "Repeat", "Review",
2771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Romance", "Science", "Series", "Service",
2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Shopping", "Soap Opera", "Special", "Suspense",
2791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Talk", "Technical", "Tennis", "Travel",
2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Variety", "Video", "Weather", "Western",
2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Art", "Auto Racing", "Aviation", "Biography",
2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Boating", "Bowling", "Boxing", "Cartoon",
2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Children", "Classic Film", "Community", "Computers",
2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Country Music", "Court", "Extreme Sports", "Family",
2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Financial", "Gymnastics", "Headlines", "Horse Racing",
2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Hunting/Fishing/Outdoors", "Independent", "Jazz", "Magazine",
2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Motorcycle Racing", "Music/Film/Books", "News-International", "News-Local",
2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "News-National", "News-Regional", "Olympics", "Original",
2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Performing Arts", "Pets/Animals", "Pop", "Rock & Roll",
2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Sci-Fi", "Self Improvement", "Sitcom", "Skating",
2911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Skiing", "Soccer", "Track/Field", "True",
2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Volleyball", "Wrestling",
2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
29565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    // Audio language code map from ISO 639-2/B to 639-2/T, in order to show correct audio language.
29665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static final HashMap<String, String> ISO_LANGUAGE_CODE_MAP;
29765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    static {
29865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP = new HashMap<>();
29965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("alb", "sqi");
30065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("arm", "hye");
30165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("baq", "eus");
30265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("bur", "mya");
30365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("chi", "zho");
30465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("cze", "ces");
30565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("dut", "nld");
30665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("fre", "fra");
30765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("geo", "kat");
30865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("ger", "deu");
30965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("gre", "ell");
31065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("ice", "isl");
31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("mac", "mkd");
31265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("mao", "mri");
31365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("may", "msa");
31465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("per", "fas");
31565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("rum", "ron");
31665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("slo", "slk");
31765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("tib", "bod");
31865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("wel", "cym");
31965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("esl", "spa"); // Special entry for channel 9-1 KQED in bay area.
32065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
32165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
3221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // Containers to store the last version numbers of the PSIP sections.
3231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
3241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
3251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public interface OutputListener {
3271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onPatParsed(List<PatItem> items);
3281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onPmtParsed(int programNumber, List<PmtItem> items);
3291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onMgtParsed(List<MgtItem> items);
33065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
3311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onEitParsed(int sourceId, List<EitItem> items);
3321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onEttParsed(int sourceId, List<EttItem> descriptions);
3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
33565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final OutputListener mListener;
3361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public SectionParser(OutputListener listener) {
3381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mListener = listener;
3391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void parseSections(ByteArrayBuffer data) {
3421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 0;
3431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        while (pos + 3 <= data.length()) {
3441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data.byteAt(pos) & 0xff) == 0xff) {
3451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // Clear stuffing bytes according to H222.0 section 2.4.4.
3461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                data.setLength(0);
3471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
3481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int sectionLength =
3501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    (((data.byteAt(pos + 1) & 0x0f) << 8) | (data.byteAt(pos + 2) & 0xff)) + 3;
3511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos + sectionLength > data.length()) {
3521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
3531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
3551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "parseSections 0x" + Integer.toHexString(data.byteAt(pos) & 0xff));
3561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            parseSection(Arrays.copyOfRange(data.buffer(), pos, pos + sectionLength));
3581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += sectionLength;
3591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
3611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int i = 0; i < mParsedEttItems.size(); ++i) {
3621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int sourceId = mParsedEttItems.keyAt(i);
3631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                List<EttItem> descriptions = mParsedEttItems.valueAt(i);
3641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mListener.onEttParsed(sourceId, descriptions);
3651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mParsedEttItems.clear();
3681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void parseSection(byte[] data) {
3711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!checkSanity(data)) {
3721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "Bad CRC!");
3731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        PsipSection section = PsipSection.create(data);
3761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (section == null) {
3771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // The currentNextIndicator indicates that the section sent is currently applicable.
3811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!section.getCurrentNextIndicator()) {
3821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int versionNumber = (data[5] & 0x3e) >> 1;
3851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Integer oldVersionNumber = mSectionVersionMap.get(section);
3861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // The versionNumber shall be incremented when a change in the information carried within
3881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // the section occurs.
3891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (oldVersionNumber != null && versionNumber == oldVersionNumber) {
3901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean result = false;
3931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        switch (data[0]) {
3941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_PAT:
3951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parsePAT(data);
3961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
3971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_PMT:
3981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parsePMT(data);
3991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_MGT:
4011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseMGT(data);
4021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_TVCT:
4041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_CVCT:
4051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseVCT(data);
4061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_EIT:
4081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseEIT(data);
4091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_ETT:
4111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseETT(data);
4121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            default:
4141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (result) {
4171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mSectionVersionMap.put(section, versionNumber);
4181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parsePAT(byte[] data) {
4221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "PAT is discovered.");
4241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 8;
4261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<PatItem> results = new ArrayList<>();
4281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (; pos < data.length - 4; pos = pos + 4) {
4291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos > data.length - 4 - 4) {
4301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken PAT.");
4311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
4321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int programNo = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
4341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int pmtPid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
4351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(new PatItem(programNo, pmtPid));
4361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
4381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onPatParsed(results);
4391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
4411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parsePMT(byte[] data) {
4441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int table_id_ext = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
4451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "PMT is discovered. programNo = " + table_id_ext);
4471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 11) {
4491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken PMT.");
4501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
4511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pcrPid = (data[8] & 0x1f) << 8 | data[9];
4531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int programInfoLen = (data[10] & 0x0f) << 8 | data[11];
4541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 12;
4551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + programInfoLen);
4561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += programInfoLen;
4571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "PMT descriptors size: " + descriptors.size());
4591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<PmtItem> results = new ArrayList<>();
4611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (; pos < data.length - 4;) {
46265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (pos < 0) {
46365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                Log.e(TAG, "Broken PMT.");
46465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return false;
46565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
4661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int streamType = data[pos] & 0xff;
4671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int esPid = (data[pos + 1] & 0x1f) << 8 | (data[pos + 2] & 0xff);
4681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int esInfoLen = (data[pos + 3] & 0xf) << 8 | (data[pos + 4] & 0xff);
4691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length < pos + esInfoLen + 5) {
4701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken PMT.");
4711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
4721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            descriptors = parseDescriptors(data, pos + 5, pos + 5 + esInfoLen);
4741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
4751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
4761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            PmtItem pmtItem = new PmtItem(streamType, esPid, audioTracks, captionTracks);
4771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
4781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "PMT " + pmtItem + " descriptors size: " + descriptors.size());
4791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(pmtItem);
4811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos = pos + esInfoLen + 5;
4821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        results.add(new PmtItem(PmtItem.ES_PID_PCR, pcrPid, null, null));
4841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
4851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onPmtParsed(table_id_ext, results);
4861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
4881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseMGT(byte[] data) {
4911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for MGT, see ATSC A/65 Table 6.2.
4921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "MGT is discovered.");
4941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 10) {
4961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken MGT.");
4971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
4981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int tablesDefined = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
5001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 11;
5011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<MgtItem> results = new ArrayList<>();
5021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < tablesDefined; ++i) {
5031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 10) {
5041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken MGT.");
5051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int tableType = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
5081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int tableTypePid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
5091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsLength = ((data[pos + 9] & 0x0f) << 8) | (data[pos + 10] & 0xff);
5101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 11 + descriptorsLength;
5111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(new MgtItem(tableType, tableTypePid));
5121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if ((data[pos] & 0xf0) != 0xf0) {
5141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken MGT.");
5151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
5161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
5181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onMgtParsed(results);
5191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
5211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
5221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
5231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseVCT(byte[] data) {
5241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for VCT, see ATSC A/65 Table 6.4 and 6.8.
5251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
5261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "VCT is discovered.");
5271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 9) {
5291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken VCT.");
5301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
5311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numChannelsInSection = (data[9] & 0xff);
53365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int sectionNumber = (data[6] & 0xff);
53465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int lastSectionNumber = (data[7] & 0xff);
5351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 10;
5361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<VctItem> results = new ArrayList<>();
5371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numChannelsInSection; ++i) {
5381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 31) {
5391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.");
5401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String shortName = "";
5431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int shortNameSize = getShortNameSize(data, pos);
5441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            try {
5451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                shortName = new String(
5461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
5471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            } catch (UnsupportedEncodingException e) {
5481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.", e);
5491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos + 14] & 0xf0) != 0xf0) {
5521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.");
5531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int majorNumber = ((data[pos + 14] & 0x0f) << 6) | ((data[pos + 15] & 0xff) >> 2);
5561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int minorNumber = ((data[pos + 15] & 0x03) << 8) | (data[pos + 16] & 0xff);
5571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((majorNumber & 0x3f0) == 0x3f0) {
5581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // If the six MSBs are 111111, these indicate that there is only one-part channel
5591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // number. To see details, refer A/65 Section 6.3.2.
5601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                majorNumber = ((majorNumber & 0xf) << 10) + minorNumber;
5611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                minorNumber = 0;
5621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int channelTsid = ((data[pos + 22] & 0xff) << 8) | (data[pos + 23] & 0xff);
5641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int programNumber = ((data[pos + 24] & 0xff) << 8) | (data[pos + 25] & 0xff);
5651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean accessControlled = (data[pos + 26] & 0x20) != 0;
5661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean hidden = (data[pos + 26] & 0x10) != 0;
5671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int serviceType = (data[pos + 27] & 0x3f);
5681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int sourceId = ((data[pos + 28] & 0xff) << 8) | (data[pos + 29] & 0xff);
5691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsPos = pos + 32;
5701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsLength = ((data[pos + 30] & 0x03) << 8) | (data[pos + 31] & 0xff);
5711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 32 + descriptorsLength;
5721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length < pos) {
5731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.");
5741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<TsDescriptor> descriptors = parseDescriptors(
5771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    data, descriptorsPos, descriptorsPos + descriptorsLength);
5781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String longName = null;
5791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (TsDescriptor descriptor : descriptors) {
5801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (descriptor instanceof ExtendedChannelNameDescriptor) {
5811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    ExtendedChannelNameDescriptor extendedChannelNameDescriptor =
5821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            (ExtendedChannelNameDescriptor) descriptor;
5831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    longName = extendedChannelNameDescriptor.getLongChannelName();
5841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
5851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
5861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
5881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format(
5891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
5901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
5911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        shortName, longName, serviceType, channelTsid, programNumber, majorNumber,
5921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        minorNumber, accessControlled, hidden, descriptors.size()));
5931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!accessControlled && !hidden && (serviceType == Channel.SERVICE_TYPE_ATSC_AUDIO ||
5951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    serviceType == Channel.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION ||
5961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    serviceType == Channel.SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
5971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // Hide hidden, encrypted, or unsupported ATSC service type channels
5981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                results.add(new VctItem(shortName, longName, serviceType, channelTsid,
5991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        programNumber, majorNumber, minorNumber, sourceId));
6001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
60265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        // Skip the remaining descriptor part which we don't use.
60365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
6041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
60565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mListener.onVctParsed(results, sectionNumber, lastSectionNumber);
6061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
6081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
6091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseEIT(byte[] data) {
6111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for EIT, see ATSC A/65 Table 6.11.
6121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
6131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "EIT is discovered.");
6141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 9) {
6161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken EIT.");
6171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
6181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
6201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numEventsInSection = (data[9] & 0xff);
6211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 10;
6231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<EitItem> results = new ArrayList<>();
6241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numEventsInSection; ++i) {
6251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 9) {
6261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos] & 0xc0) != 0xc0) {
6301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
6341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            long startTime = ((data[pos + 2] & (long) 0xff) << 24) | ((data[pos + 3] & 0xff) << 16)
6351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    | ((data[pos + 4] & 0xff) << 8) | (data[pos + 5] & 0xff);
6361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int lengthInSecond = ((data[pos + 6] & 0x0f) << 16)
6371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    | ((data[pos + 7] & 0xff) << 8) | (data[pos + 8] & 0xff);
6381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int titleLength = (data[pos + 9] & 0xff);
6391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 10 + titleLength + 1) {
6401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String titleText = "";
6441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (titleLength > 0) {
6451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                titleText = extractText(data, pos + 10);
6461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos + 10 + titleLength] & 0xf0) != 0xf0) {
6481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsLength = ((data[pos + 10 + titleLength] & 0x0f) << 8)
6521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    | (data[pos + 10 + titleLength + 1] & 0xff);
6531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsPos = pos + 10 + titleLength + 2;
6541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length < descriptorsPos + descriptorsLength) {
6551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<TsDescriptor> descriptors = parseDescriptors(
6591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    data, descriptorsPos, descriptorsPos + descriptorsLength);
6601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
6611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
6621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String contentRating = generateContentRating(descriptors);
6641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String broadcastGenre = generateBroadcastGenre(descriptors);
6651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String canonicalGenre = generateCanonicalGenre(descriptors);
6661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
6671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
6681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 10 + titleLength + 2 + descriptorsLength;
6691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
6701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    startTime, lengthInSecond, contentRating, audioTracks, captionTracks,
6711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    broadcastGenre, canonicalGenre, null));
6721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
6741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onEitParsed(sourceId, results);
6751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
6771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
6781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseETT(byte[] data) {
6801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for ETT, see ATSC A/65 Table 6.13.
6811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
6821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "ETT is discovered.");
6831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 12) {
6851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ETT.");
6861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
6871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int sourceId = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
6891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int eventId = (((data[11] & 0xff) << 8) | (data[12] & 0xff)) >> 2;
6901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String text = extractText(data, 13);
6911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<EttItem> ettItems = mParsedEttItems.get(sourceId);
6921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (ettItems == null) {
6931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ettItems = new ArrayList<>();
6941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mParsedEttItems.put(sourceId, ettItems);
6951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ettItems.add(new EttItem(eventId, text));
6971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
6981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
6991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static List<AtscAudioTrack> generateAudioTracks(List<TsDescriptor> descriptors) {
7011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // The list of audio tracks sent is located at both AC3 Audio descriptor and ISO 639
7021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Language descriptor.
7031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> ac3Tracks = new ArrayList<>();
7041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
70565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof Ac3AudioDescriptor) {
7071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Ac3AudioDescriptor audioDescriptor =
7081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (Ac3AudioDescriptor) descriptor;
7091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                AtscAudioTrack audioTrack = new AtscAudioTrack();
7101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (audioDescriptor.getLanguage() != null) {
7111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTrack.language = audioDescriptor.getLanguage();
7121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
7131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack.audioType = AtscAudioTrack.AUDIOTYPE_UNDEFINED;
7141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack.channelCount = audioDescriptor.getNumChannels();
7151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack.sampleRate = audioDescriptor.getSampleRate();
7161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                ac3Tracks.add(audioTrack);
7171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof Iso639LanguageDescriptor) {
7211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Iso639LanguageDescriptor iso639LanguageDescriptor =
7221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (Iso639LanguageDescriptor) descriptor;
7231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks());
7241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate
7281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // while a ISO 639 Language descriptor only has a audio type, which describes a main use
7291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // case of its audio track.
7301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Some channels contain only AC3 audio stream descriptors with valid language values.
7311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language
7321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // descriptor per audio track, and those AC3 audio stream descriptors often have a null
7331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // value of language field.
7341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Combines two descriptors into one in order to gather more audio track specific
7351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // information as much as possible.
7361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> tracks = new ArrayList<>();
7371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!ac3Tracks.isEmpty() && !iso639LanguageTracks.isEmpty()
7381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                && ac3Tracks.size() != iso639LanguageTracks.size()) {
7391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // This shouldn't be happen. In here, it handles two cases. The first case is that the
7401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // only one type of descriptors arrives. The second case is that the two types of
7411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // descriptors have the same number of tracks.
7421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size");
7431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return tracks;
7441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size());
7461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < size; ++i) {
7471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            AtscAudioTrack audioTrack = null;
7481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (i < ac3Tracks.size()) {
7491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack = ac3Tracks.get(i);
7501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (i < iso639LanguageTracks.size()) {
7521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (audioTrack == null) {
7531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTrack = iso639LanguageTracks.get(i);
7541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                } else {
7551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i);
75665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    if (audioTrack.language == null || TextUtils.equals(audioTrack.language, "")) {
7571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        audioTrack.language = iso639LanguageTrack.language;
7581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
7591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTrack.audioType = iso639LanguageTrack.audioType;
7601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
7611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
76265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.language);
76365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (language != null) {
76465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                audioTrack.language = language;
76565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
7661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            tracks.add(audioTrack);
7671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return tracks;
7691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
7701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) {
7721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscCaptionTrack> services = new ArrayList<>();
7731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof CaptionServiceDescriptor) {
7751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                CaptionServiceDescriptor captionServiceDescriptor =
7761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (CaptionServiceDescriptor) descriptor;
7771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                services.addAll(captionServiceDescriptor.getCaptionTracks());
7781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return services;
7811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
7821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String generateContentRating(List<TsDescriptor> descriptors) {
7841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<String> contentRatings = new ArrayList<>();
7851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof ContentAdvisoryDescriptor) {
7871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                ContentAdvisoryDescriptor contentAdvisoryDescriptor =
7881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (ContentAdvisoryDescriptor) descriptor;
7891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) {
7901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    for (RegionalRating index : ratingRegion.getRegionalRatings()) {
7911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        String ratingSystem = null;
7921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        String rating = null;
7931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        switch (ratingRegion.getName()) {
7941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case RATING_REGION_US_TV:
7951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                ratingSystem = RATING_REGION_RATING_SYSTEM_US_TV;
7961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                if (index.getDimension() == 0 && index.getRating() >= 0
7971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                        && index.getRating() < RATING_REGION_TABLE_US_TV.length) {
7981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    rating = RATING_REGION_TABLE_US_TV[index.getRating()];
7991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                }
8001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                break;
8011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case RATING_REGION_KR_TV:
8021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                ratingSystem = RATING_REGION_RATING_SYSTEM_KR_TV;
8031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                if (index.getDimension() == 0 && index.getRating() >= 0
8041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                        && index.getRating() < RATING_REGION_TABLE_KR_TV.length) {
8051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    rating = RATING_REGION_TABLE_KR_TV[index.getRating()];
8061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                }
8071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                break;
8081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            default:
8091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                break;
8101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        }
8111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        if (ratingSystem != null && rating != null) {
8121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            contentRatings.add(TvContentRating
8131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    .createRating("com.android.tv", ratingSystem, rating)
8141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    .flattenToString());
8151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        }
8161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
8171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
8181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return TextUtils.join(",", contentRatings);
8211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
8221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
8241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
8251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof GenreDescriptor) {
8261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                GenreDescriptor genreDescriptor =
8271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (GenreDescriptor) descriptor;
8281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
8291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return null;
8321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
8331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
8351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
8361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof GenreDescriptor) {
8371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                GenreDescriptor genreDescriptor =
8381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (GenreDescriptor) descriptor;
8391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return Genres.encode(genreDescriptor.getCanonicalGenres());
8401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return null;
8431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
8441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) {
8461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for descriptors, see ATSC A/65 Section 6.9.
8471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<TsDescriptor> descriptors = new ArrayList<>();
8481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length < limit) {
8491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return descriptors;
8501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = offset;
8521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        while (pos + 1 < limit) {
8531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int tag = data[pos] & 0xff;
8541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int length = data[pos + 1] & 0xff;
8551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (length <= 0) {
8561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
8571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit < pos + length + 2) {
8591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
8601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
8621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format("Descriptor tag: %02x", tag));
8631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TsDescriptor descriptor = null;
8651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            switch (tag) {
8661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_CONTENT_ADVISORY:
8671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseContentAdvisory(data, pos, pos + length + 2);
8681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_CAPTION_SERVICE:
8711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseCaptionService(data, pos, pos + length + 2);
8721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME:
8751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseLongChannelName(data, pos, pos + length + 2);
8761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_GENRE:
8791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseGenre(data, pos, pos + length + 2);
8801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_AC3_AUDIO_STREAM:
8831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseAc3AudioStream(data, pos, pos + length + 2);
8841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_ISO639LANGUAGE:
8871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseIso639Language(data, pos, pos + length + 2);
8881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                default:
8911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor != null) {
8931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (DEBUG) {
8941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.d(TAG, "Descriptor parsed: " + descriptor);
8951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
8961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                descriptors.add(descriptor);
8971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += length + 2;
8991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return descriptors;
9011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
9021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
9031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) {
9041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For the details of the structure of ISO 639 language descriptor,
9051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // see ISO13818-1 second edition Section 2.6.18.
9061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
9071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> audioTracks = new ArrayList<>();
9081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        while (pos + 4 <= limit) {
9091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos + 3) {
9101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken Iso639Language.");
9111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
9121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String language = new String(data, pos, 3);
9141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int audioType = data[pos + 3] & 0xff;
9151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            AtscAudioTrack audioTrack = new AtscAudioTrack();
9161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            audioTrack.language = language;
9171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            audioTrack.audioType = audioType;
9181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            audioTracks.add(audioTrack);
9191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 4;
9201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new Iso639LanguageDescriptor(audioTracks);
9221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
9231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
9241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) {
9251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For the details of the structure of caption service descriptor,
9261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // see ATSC A/65 Section 6.9.2.
9271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 2) {
9281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken CaptionServiceDescriptor.");
9291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
9301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscCaptionTrack> services = new ArrayList<>();
9321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
9331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numberServices = data[pos] & 0x1f;
9341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
9351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit < pos + numberServices * 6) {
9361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken CaptionServiceDescriptor.");
9371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
9381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numberServices; ++i) {
9401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String language = new String(Arrays.copyOfRange(data, pos, pos + 3));
9411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 3;
9421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean ccType = (data[pos] & 0x80) != 0;
9431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!ccType) {
9441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                continue;
9451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int captionServiceNumber = data[pos] & 0x3f;
9471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
9481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean easyReader = (data[pos] & 0x80) != 0;
9491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean wideAspectRatio = (data[pos] & 0x40) != 0;
9501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            byte[] reserved = new byte[2];
9511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            reserved[0] = (byte) (data[pos] << 2);
9521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6);
9531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2);
9541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 2;
9551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            AtscCaptionTrack captionTrack = new AtscCaptionTrack();
9561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.language = language;
9571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.serviceNumber = captionServiceNumber;
9581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.easyReader = easyReader;
9591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.wideAspectRatio = wideAspectRatio;
9601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            services.add(captionTrack);
9611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new CaptionServiceDescriptor(services);
9631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
9641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
9651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) {
9661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for content advisory descriptor, see A/65 Table 6.27.
9671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 2) {
9681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ContentAdvisory");
9691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
9701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int count = data[pos + 2] & 0x3f;
9721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 3;
9731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<RatingRegion> ratingRegions = new ArrayList<>();
9741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < count; ++i) {
9751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos + 1) {
9761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken ContentAdvisory");
9771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
9781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<RegionalRating> indices = new ArrayList<>();
9801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int ratingRegion = data[pos] & 0xff;
9811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int dimensionCount = data[pos + 1] & 0xff;
9821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 2;
9831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int j = 0; j < dimensionCount; ++j) {
9841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (limit <= pos + 1) {
9851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.e(TAG, "Broken ContentAdvisory");
9861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
9871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
9881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int dimensionIndex = data[pos] & 0xff;
9891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int ratingValue = data[pos + 1] & 0x0f;
9901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                pos += 2;
9911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                indices.add(new RegionalRating(dimensionIndex, ratingValue));
9921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos) {
9941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken ContentAdvisory");
9951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
9961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int ratingDescriptionLength = data[pos] & 0xff;
9981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
9991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit < pos + ratingDescriptionLength) {
10001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken ContentAdvisory");
10011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
10021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String ratingDescription = extractText(data, pos);
10041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += ratingDescriptionLength;
10051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices));
10061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new ContentAdvisoryDescriptor(ratingRegions);
10081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
10091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
10101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static ExtendedChannelNameDescriptor parseLongChannelName(byte[] data, int pos,
10111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int limit) {
10121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 2) {
10131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ExtendedChannelName.");
10141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
10171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String text = extractText(data, pos);
10181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (text == null) {
10191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ExtendedChannelName.");
10201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new ExtendedChannelNameDescriptor(text);
10231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
10241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
10251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) {
10261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
10271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int attributeCount = data[pos] & 0x1f;
10281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + attributeCount) {
10291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken Genre.");
10301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        HashSet<String> broadcastGenreSet = new HashSet<>();
10331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        HashSet<String> canonicalGenreSet = new HashSet<>();
10341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < attributeCount; ++i) {
10351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
10361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int genreCode = data[pos] & 0xff;
10371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (genreCode < BROADCAST_GENRES_TABLE.length) {
10381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode];
10391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) {
10401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    broadcastGenreSet.add(broadcastGenre);
10411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
10421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (genreCode < CANONICAL_GENRES_TABLE.length) {
10441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode];
10451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) {
10461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    canonicalGenreSet.add(canonicalGenre);
10471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
10481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new GenreDescriptor(broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
10511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
10521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
10531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
10541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) {
10551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the AC3 audio stream descriptor, see A/52 Table A4.1.
10561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 5) {
10571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken AC3 audio stream descriptor.");
10581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
10611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5);
10621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte bsid = (byte) (data[pos] & 0x1f);
10631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2);
10651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte surroundMode = (byte) (data[pos] & 0x03);
10661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte bsmod = (byte) ((data[pos] & 0xe0) >> 5);
10681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numChannels = (data[pos] & 0x1e) >> 1;
10691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean fullSvc = (data[pos] & 0x01) != 0;
10701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte langCod = data[pos];
10721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte langCod2 = 0;
10731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (numChannels == 0) {
10741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos) {
10751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor.");
10761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
10771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
10791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            langCod2 = data[pos];
10801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 1) {
10821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken AC3 audio stream descriptor.");
10831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte mainId = 0;
10861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte priority = 0;
10871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte asvcflags = 0;
10881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (bsmod < 2) {
10901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mainId = (byte) ((data[pos] & 0xe0) >> 5);
10911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            priority = (byte) ((data[pos] & 0x18) >> 3);
10921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos] & 0x07) != 0x07) {
10931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed");
10941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
10951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
10971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            asvcflags = data[pos];
10981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // See A/52B Table A3.6 num_channels.
11011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numEncodedChannels;
11021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        switch (numChannels) {
11031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 1:
11041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 8:
11051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 1;
11061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 2:
11081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 9:
11091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 2;
11101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 3:
11121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 4:
11131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 10:
11141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 3;
11151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 5:
11171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 6:
11181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 11:
11191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 4;
11201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 7:
11221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 12:
11231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 5;
11241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 13:
11261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 6;
11271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            default:
11291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 0;
11301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 1) {
11341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
11351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
11361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags,
11371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    null, null, null);
11381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
11401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int textLen = (data[pos] & 0xfe) >> 1;
11411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean textCode = (data[pos] & 0x01) != 0;
11421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
11431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String text = "";
11441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (textLen > 0) {
11451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit < pos + textLen) {
11461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor");
11471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (textCode) {
11501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                text = new String(data, pos, textLen);
11511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            } else {
11521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                text = new String(data, pos, textLen, Charset.forName("UTF-16"));
11531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += textLen;
11551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String language = null;
11571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String language2 = null;
11581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (pos < limit) {
11591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Many AC3 audio stream descriptors skip the language fields.
11601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean languageFlag1 = (data[pos] & 0x80) != 0;
11611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean languageFlag2 = (data[pos] & 0x40) != 0;
11621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos] & 0x3f) != 0x3f) {
11631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor");
11641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) {
11671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor");
11681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
11711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (languageFlag1) {
11721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                language = new String(data, pos, 3);
11731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                pos += 3;
11741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (languageFlag2) {
11761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                language2 = new String(data, pos, 3);
11771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
11811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags, text,
11821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                language, language2);
11831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
11841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static int getShortNameSize(byte[] data, int offset) {
11861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) {
11871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data[offset + i] == 0 && data[offset + i + 1] == 0) {
11881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return i;
11891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return MAX_SHORT_NAME_BYTES;
11921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
11931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String extractText(byte[] data, int pos) {
11951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length < pos)  {
11961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
11971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numStrings = data[pos] & 0xff;
11991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos++;
12001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numStrings; ++i) {
12011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 3) {
12021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken text.");
12031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
12041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int numSegments = data[pos + 3] & 0xff;
12061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 4;
12071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int j = 0; j < numSegments; ++j) {
12081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (data.length <= pos + 2) {
12091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.e(TAG, "Broken text.");
12101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
12111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
12121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int compressionType = data[pos] & 0xff;
12131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int mode = data[pos + 1] & 0xff;
12141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int numBytes = data[pos + 2] & 0xff;
12151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (data.length < pos + 3 + numBytes) {
12161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.e(TAG, "Broken text.");
12171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
12181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
12191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                byte[] bytes = Arrays.copyOfRange(data, pos + 3, pos + 3 + numBytes);
12201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
12211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    try {
12221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        switch (mode) {
12231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case MODE_SELECTED_UNICODE_RANGE_1:
12241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                return new String(bytes, "ISO-8859-1");
12251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case MODE_SCSU:
12261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                return UnicodeDecompressor.decompress(bytes);
12271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case MODE_UTF16:
12281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                return new String(bytes, "UTF-16");
12291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        }
12301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    } catch (UnsupportedEncodingException e) {
12311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        Log.e(TAG, "Unsupported text format.", e);
12321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
12331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
12341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                pos += 3 + numBytes;
12351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return null;
12381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
12391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
12401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static boolean checkSanity(byte[] data) {
12411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 1) {
12421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
12431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
12451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (hasCRC) {
12461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int crc = 0xffffffff;
12471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for(byte b : data) {
12481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
12491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                crc = CRC_TABLE[index] ^ (crc << 8);
12501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if(crc != 0){
12521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
12531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
12561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
12571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
1258