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