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