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