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