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.data; 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 import com.android.tv.common.feature.Model; 28 import com.android.tv.tuner.data.Channel.AtscServiceType; 29 import com.android.tv.tuner.data.PsiData.PatItem; 30 import com.android.tv.tuner.data.PsiData.PmtItem; 31 import com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor; 32 import com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor; 33 import com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor; 34 import com.android.tv.tuner.data.PsipData.EitItem; 35 import com.android.tv.tuner.data.PsipData.EttItem; 36 import com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor; 37 import com.android.tv.tuner.data.PsipData.GenreDescriptor; 38 import com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor; 39 import com.android.tv.tuner.data.PsipData.MgtItem; 40 import com.android.tv.tuner.data.PsipData.ParentalRatingDescriptor; 41 import com.android.tv.tuner.data.PsipData.PsipSection; 42 import com.android.tv.tuner.data.PsipData.RatingRegion; 43 import com.android.tv.tuner.data.PsipData.RegionalRating; 44 import com.android.tv.tuner.data.PsipData.SdtItem; 45 import com.android.tv.tuner.data.PsipData.ServiceDescriptor; 46 import com.android.tv.tuner.data.PsipData.ShortEventDescriptor; 47 import com.android.tv.tuner.data.PsipData.TsDescriptor; 48 import com.android.tv.tuner.data.PsipData.VctItem; 49 import com.android.tv.tuner.data.Track.AtscAudioTrack; 50 import com.android.tv.tuner.data.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 TV 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 { onPatParsed(List<PatItem> items)628 void onPatParsed(List<PatItem> items); 629 onPmtParsed(int programNumber, List<PmtItem> items)630 void onPmtParsed(int programNumber, List<PmtItem> items); 631 onMgtParsed(List<MgtItem> items)632 void onMgtParsed(List<MgtItem> items); 633 onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber)634 void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber); 635 onEitParsed(int sourceId, List<EitItem> items)636 void onEitParsed(int sourceId, List<EitItem> items); 637 onEttParsed(int sourceId, List<EttItem> descriptions)638 void onEttParsed(int sourceId, List<EttItem> descriptions); 639 onSdtParsed(List<SdtItem> items)640 void onSdtParsed(List<SdtItem> items); 641 } 642 643 private final OutputListener mListener; 644 SectionParser(OutputListener listener)645 public SectionParser(OutputListener listener) { 646 mListener = listener; 647 } 648 parseSections(ByteArrayBuffer data)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 resetVersionNumbers()678 public void resetVersionNumbers() { 679 mSectionVersionMap.clear(); 680 } 681 parseSection(byte[] data)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 parsePAT(byte[] data)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 parsePMT(byte[] data)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 parseMGT(byte[] data)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 parseVCT(byte[] data)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 ((serviceType == AtscServiceType.SERVICE_TYPE_ATSC_AUDIO_VALUE 933 || serviceType 934 == AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION_VALUE 935 || serviceType 936 == AtscServiceType 937 .SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE_VALUE) 938 && !accessControlled 939 && !hidden) { 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 parseEIT(byte[] data)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 parseETT(byte[] data)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 parseSDT(byte[] data)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 parseDVBEIT(byte[] data)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 generateAudioTracks(List<TsDescriptor> descriptors)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 String language = null; 1216 if (audioDescriptor.getLanguage() != null) { 1217 language = audioDescriptor.getLanguage(); 1218 } 1219 if (language == null) { 1220 language = ""; 1221 } 1222 AtscAudioTrack audioTrack = 1223 AtscAudioTrack.newBuilder() 1224 .setLanguage(language) 1225 .setAudioType(AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED) 1226 .setChannelCount(audioDescriptor.getNumChannels()) 1227 .setSampleRate(audioDescriptor.getSampleRate()) 1228 .build(); 1229 ac3Tracks.add(audioTrack); 1230 } 1231 } 1232 for (TsDescriptor descriptor : descriptors) { 1233 if (descriptor instanceof Iso639LanguageDescriptor) { 1234 Iso639LanguageDescriptor iso639LanguageDescriptor = 1235 (Iso639LanguageDescriptor) descriptor; 1236 iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks()); 1237 } 1238 } 1239 1240 // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate 1241 // while a ISO 639 Language descriptor only has a audio type, which describes a main use 1242 // case of its audio track. 1243 // Some channels contain only AC3 audio stream descriptors with valid language values. 1244 // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language 1245 // descriptor per audio track, and those AC3 audio stream descriptors often have a null 1246 // value of language field. 1247 // Combines two descriptors into one in order to gather more audio track specific 1248 // information as much as possible. 1249 List<AtscAudioTrack> tracks = new ArrayList<>(); 1250 if (!ac3Tracks.isEmpty() 1251 && !iso639LanguageTracks.isEmpty() 1252 && ac3Tracks.size() != iso639LanguageTracks.size()) { 1253 // This shouldn't be happen. In here, it handles two cases. The first case is that the 1254 // only one type of descriptors arrives. The second case is that the two types of 1255 // descriptors have the same number of tracks. 1256 Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size"); 1257 return tracks; 1258 } 1259 int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size()); 1260 for (int i = 0; i < size; ++i) { 1261 AtscAudioTrack.Builder audioTrack = null; 1262 if (i < ac3Tracks.size()) { 1263 audioTrack = ac3Tracks.get(i).toBuilder(); 1264 } 1265 if (i < iso639LanguageTracks.size()) { 1266 if (audioTrack == null) { 1267 audioTrack = iso639LanguageTracks.get(i).toBuilder(); 1268 } else { 1269 AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i); 1270 if (!audioTrack.hasLanguage() 1271 || TextUtils.equals(audioTrack.getLanguage(), "")) { 1272 audioTrack.setLanguage(iso639LanguageTrack.getLanguage()); 1273 } 1274 audioTrack.setAudioType(iso639LanguageTrack.getAudioType()); 1275 } 1276 } 1277 String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.getLanguage()); 1278 if (language != null) { 1279 audioTrack = audioTrack.setLanguage(language); 1280 } 1281 tracks.add(audioTrack.build()); 1282 } 1283 return tracks; 1284 } 1285 generateCaptionTracks(List<TsDescriptor> descriptors)1286 private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) { 1287 List<AtscCaptionTrack> services = new ArrayList<>(); 1288 for (TsDescriptor descriptor : descriptors) { 1289 if (descriptor instanceof CaptionServiceDescriptor) { 1290 CaptionServiceDescriptor captionServiceDescriptor = 1291 (CaptionServiceDescriptor) descriptor; 1292 services.addAll(captionServiceDescriptor.getCaptionTracks()); 1293 } 1294 } 1295 return services; 1296 } 1297 1298 @VisibleForTesting generateContentRating(List<TsDescriptor> descriptors)1299 static String generateContentRating(List<TsDescriptor> descriptors) { 1300 Set<String> contentRatings = new ArraySet<>(); 1301 List<RatingRegion> usRatingRegions = getRatingRegions(descriptors, RATING_REGION_US_TV); 1302 List<RatingRegion> krRatingRegions = getRatingRegions(descriptors, RATING_REGION_KR_TV); 1303 for (RatingRegion region : usRatingRegions) { 1304 String contentRating = getUsRating(region); 1305 if (contentRating != null) { 1306 contentRatings.add(contentRating); 1307 } 1308 } 1309 for (RatingRegion region : krRatingRegions) { 1310 String contentRating = getKrRating(region); 1311 if (contentRating != null) { 1312 contentRatings.add(contentRating); 1313 } 1314 } 1315 return TextUtils.join(",", contentRatings); 1316 } 1317 1318 /** 1319 * Gets a list of {@link RatingRegion} in the specific region. 1320 * 1321 * @param descriptors {@link TsDescriptor} list which may contains rating information 1322 * @param region the specific region 1323 * @return a list of {@link RatingRegion} in the specific region 1324 */ getRatingRegions(List<TsDescriptor> descriptors, int region)1325 private static List<RatingRegion> getRatingRegions(List<TsDescriptor> descriptors, int region) { 1326 List<RatingRegion> ratingRegions = new ArrayList<>(); 1327 for (TsDescriptor descriptor : descriptors) { 1328 if (!(descriptor instanceof ContentAdvisoryDescriptor)) { 1329 continue; 1330 } 1331 ContentAdvisoryDescriptor contentAdvisoryDescriptor = 1332 (ContentAdvisoryDescriptor) descriptor; 1333 for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) { 1334 if (ratingRegion.getName() == region) { 1335 ratingRegions.add(ratingRegion); 1336 } 1337 } 1338 } 1339 return ratingRegions; 1340 } 1341 1342 /** 1343 * Gets US content rating and subratings (if any). 1344 * 1345 * @param ratingRegion a {@link RatingRegion} instance which may contain rating information. 1346 * @return A string representing the US content rating and subratings. The format of the string 1347 * is defined in {@link TvContentRating}. null, if no such a string exists. 1348 */ getUsRating(RatingRegion ratingRegion)1349 private static String getUsRating(RatingRegion ratingRegion) { 1350 if (ratingRegion.getName() != RATING_REGION_US_TV) { 1351 return null; 1352 } 1353 List<RegionalRating> regionalRatings = ratingRegion.getRegionalRatings(); 1354 String rating = null; 1355 int ratingIndex = VALUE_US_TV_NONE; 1356 List<String> subratings = new ArrayList<>(); 1357 for (RegionalRating index : regionalRatings) { 1358 // See Table 3 of ANSI-CEA-766-D 1359 int dimension = index.getDimension(); 1360 int value = index.getRating(); 1361 switch (dimension) { 1362 // According to Table 6.27 of ATSC A65, 1363 // the dimensions shall be in increasing order. 1364 // Therefore, rating and ratingIndex are assigned before any corresponding 1365 // subrating. 1366 case DIMENSION_US_TV_RATING: 1367 if (value >= VALUE_US_TV_G && value < RATING_REGION_TABLE_US_TV.length) { 1368 rating = RATING_REGION_TABLE_US_TV[value]; 1369 ratingIndex = value; 1370 } 1371 break; 1372 case DIMENSION_US_TV_D: 1373 if (value == 1 1374 && (ratingIndex == VALUE_US_TV_PG || ratingIndex == VALUE_US_TV_14)) { 1375 // US_TV_D is applicable to US_TV_PG and US_TV_14 1376 subratings.add(RATING_REGION_TABLE_US_TV_SUBRATING[dimension - 1]); 1377 } 1378 break; 1379 case DIMENSION_US_TV_L: 1380 case DIMENSION_US_TV_S: 1381 case DIMENSION_US_TV_V: 1382 if (value == 1 1383 && ratingIndex >= VALUE_US_TV_PG 1384 && ratingIndex <= VALUE_US_TV_MA) { 1385 // US_TV_L, US_TV_S, and US_TV_V are applicable to 1386 // US_TV_PG, US_TV_14 and US_TV_MA 1387 subratings.add(RATING_REGION_TABLE_US_TV_SUBRATING[dimension - 1]); 1388 } 1389 break; 1390 case DIMENSION_US_TV_Y: 1391 if (rating == null) { 1392 if (value == VALUE_US_TV_Y) { 1393 rating = STRING_US_TV_Y; 1394 } else if (value == VALUE_US_TV_Y7) { 1395 rating = STRING_US_TV_Y7; 1396 } 1397 } 1398 break; 1399 case DIMENSION_US_TV_FV: 1400 if (STRING_US_TV_Y7.equals(rating) && value == 1) { 1401 // US_TV_FV is applicable to US_TV_Y7 1402 subratings.add(STRING_US_TV_FV); 1403 } 1404 break; 1405 case DIMENSION_US_MV_RATING: 1406 if (value >= VALUE_US_MV_G && value <= VALUE_US_MV_X) { 1407 if (value == VALUE_US_MV_X) { 1408 // US_MV_X was replaced by US_MV_NC17 in 1990, 1409 // and it's not supported by TvContentRating 1410 value = VALUE_US_MV_NC17; 1411 } 1412 if (rating != null) { 1413 // According to Table 3 of ANSI-CEA-766-D, 1414 // DIMENSION_US_TV_RATING and DIMENSION_US_MV_RATING shall not be 1415 // present in the same descriptor. 1416 Log.w( 1417 TAG, 1418 "DIMENSION_US_TV_RATING and DIMENSION_US_MV_RATING are " 1419 + "present in the same descriptor"); 1420 } else { 1421 return TvContentRating.createRating( 1422 RATING_DOMAIN, 1423 RATING_REGION_RATING_SYSTEM_US_MV, 1424 RATING_REGION_TABLE_US_MV[value - 2]) 1425 .flattenToString(); 1426 } 1427 } 1428 break; 1429 1430 default: 1431 break; 1432 } 1433 } 1434 if (rating == null) { 1435 return null; 1436 } 1437 1438 String[] subratingArray = subratings.toArray(new String[subratings.size()]); 1439 return TvContentRating.createRating( 1440 RATING_DOMAIN, RATING_REGION_RATING_SYSTEM_US_TV, rating, subratingArray) 1441 .flattenToString(); 1442 } 1443 1444 /** 1445 * Gets KR(South Korea) content rating. 1446 * 1447 * @param ratingRegion a {@link RatingRegion} instance which may contain rating information. 1448 * @return A string representing the KR content rating. The format of the string is defined in 1449 * {@link TvContentRating}. null, if no such a string exists. 1450 */ getKrRating(RatingRegion ratingRegion)1451 private static String getKrRating(RatingRegion ratingRegion) { 1452 if (ratingRegion.getName() != RATING_REGION_KR_TV) { 1453 return null; 1454 } 1455 List<RegionalRating> regionalRatings = ratingRegion.getRegionalRatings(); 1456 String rating = null; 1457 for (RegionalRating index : regionalRatings) { 1458 if (index.getDimension() == 0 1459 && index.getRating() >= 0 1460 && index.getRating() < RATING_REGION_TABLE_KR_TV.length) { 1461 rating = RATING_REGION_TABLE_KR_TV[index.getRating()]; 1462 break; 1463 } 1464 } 1465 if (rating == null) { 1466 return null; 1467 } 1468 return TvContentRating.createRating( 1469 RATING_DOMAIN, RATING_REGION_RATING_SYSTEM_KR_TV, rating) 1470 .flattenToString(); 1471 } 1472 generateBroadcastGenre(List<TsDescriptor> descriptors)1473 private static String generateBroadcastGenre(List<TsDescriptor> descriptors) { 1474 for (TsDescriptor descriptor : descriptors) { 1475 if (descriptor instanceof GenreDescriptor) { 1476 GenreDescriptor genreDescriptor = (GenreDescriptor) descriptor; 1477 return TextUtils.join(",", genreDescriptor.getBroadcastGenres()); 1478 } 1479 } 1480 return null; 1481 } 1482 generateCanonicalGenre(List<TsDescriptor> descriptors)1483 private static String generateCanonicalGenre(List<TsDescriptor> descriptors) { 1484 for (TsDescriptor descriptor : descriptors) { 1485 if (descriptor instanceof GenreDescriptor) { 1486 GenreDescriptor genreDescriptor = (GenreDescriptor) descriptor; 1487 return Genres.encode(genreDescriptor.getCanonicalGenres()); 1488 } 1489 } 1490 return null; 1491 } 1492 generateServiceDescriptors( List<TsDescriptor> descriptors)1493 private static List<ServiceDescriptor> generateServiceDescriptors( 1494 List<TsDescriptor> descriptors) { 1495 List<ServiceDescriptor> serviceDescriptors = new ArrayList<>(); 1496 for (TsDescriptor descriptor : descriptors) { 1497 if (descriptor instanceof ServiceDescriptor) { 1498 ServiceDescriptor serviceDescriptor = (ServiceDescriptor) descriptor; 1499 serviceDescriptors.add(serviceDescriptor); 1500 } 1501 } 1502 return serviceDescriptors; 1503 } 1504 generateShortEventName(List<TsDescriptor> descriptors)1505 private static String generateShortEventName(List<TsDescriptor> descriptors) { 1506 for (TsDescriptor descriptor : descriptors) { 1507 if (descriptor instanceof ShortEventDescriptor) { 1508 ShortEventDescriptor shortEventDescriptor = (ShortEventDescriptor) descriptor; 1509 return shortEventDescriptor.getEventName(); 1510 } 1511 } 1512 return ""; 1513 } 1514 parseDescriptors(byte[] data, int offset, int limit)1515 private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) { 1516 // For details of the structure for descriptors, see ATSC A/65 Section 6.9. 1517 List<TsDescriptor> descriptors = new ArrayList<>(); 1518 if (data.length < limit) { 1519 return descriptors; 1520 } 1521 int pos = offset; 1522 while (pos + 1 < limit) { 1523 int tag = data[pos] & 0xff; 1524 int length = data[pos + 1] & 0xff; 1525 if (length <= 0) { 1526 break; 1527 } 1528 if (limit < pos + length + 2) { 1529 break; 1530 } 1531 if (DEBUG) { 1532 Log.d(TAG, String.format("Descriptor tag: %02x", tag)); 1533 } 1534 TsDescriptor descriptor = null; 1535 switch (tag) { 1536 case DESCRIPTOR_TAG_CONTENT_ADVISORY: 1537 descriptor = parseContentAdvisory(data, pos, pos + length + 2); 1538 break; 1539 1540 case DESCRIPTOR_TAG_CAPTION_SERVICE: 1541 descriptor = parseCaptionService(data, pos, pos + length + 2); 1542 break; 1543 1544 case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME: 1545 descriptor = parseLongChannelName(data, pos, pos + length + 2); 1546 break; 1547 1548 case DESCRIPTOR_TAG_GENRE: 1549 descriptor = parseGenre(data, pos, pos + length + 2); 1550 break; 1551 1552 case DESCRIPTOR_TAG_AC3_AUDIO_STREAM: 1553 descriptor = parseAc3AudioStream(data, pos, pos + length + 2); 1554 break; 1555 1556 case DESCRIPTOR_TAG_ISO639LANGUAGE: 1557 descriptor = parseIso639Language(data, pos, pos + length + 2); 1558 break; 1559 1560 case DVB_DESCRIPTOR_TAG_SERVICE: 1561 descriptor = parseDvbService(data, pos, pos + length + 2); 1562 break; 1563 1564 case DVB_DESCRIPTOR_TAG_SHORT_EVENT: 1565 descriptor = parseDvbShortEvent(data, pos, pos + length + 2); 1566 break; 1567 1568 case DVB_DESCRIPTOR_TAG_CONTENT: 1569 descriptor = parseDvbContent(data, pos, pos + length + 2); 1570 break; 1571 1572 case DVB_DESCRIPTOR_TAG_PARENTAL_RATING: 1573 descriptor = parseDvbParentalRating(data, pos, pos + length + 2); 1574 break; 1575 1576 default: 1577 } 1578 if (descriptor != null) { 1579 if (DEBUG) { 1580 Log.d(TAG, "Descriptor parsed: " + descriptor); 1581 } 1582 descriptors.add(descriptor); 1583 } 1584 pos += length + 2; 1585 } 1586 return descriptors; 1587 } 1588 parseIso639Language(byte[] data, int pos, int limit)1589 private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) { 1590 // For the details of the structure of ISO 639 language descriptor, 1591 // see ISO13818-1 second edition Section 2.6.18. 1592 pos += 2; 1593 List<AtscAudioTrack> audioTracks = new ArrayList<>(); 1594 while (pos + 4 <= limit) { 1595 if (limit <= pos + 3) { 1596 Log.e(TAG, "Broken Iso639Language."); 1597 return null; 1598 } 1599 String language = new String(data, pos, 3); 1600 int audioTypeInt = data[pos + 3] & 0xff; 1601 AtscAudioTrack.AudioType audioType = AtscAudioTrack.AudioType.forNumber(audioTypeInt); 1602 if (audioType == null) { 1603 audioType = AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED; 1604 } 1605 AtscAudioTrack audioTrack = 1606 AtscAudioTrack.newBuilder() 1607 .setLanguage(language) 1608 .setAudioType(audioType) 1609 .build(); 1610 audioTracks.add(audioTrack); 1611 pos += 4; 1612 } 1613 return new Iso639LanguageDescriptor(audioTracks); 1614 } 1615 parseCaptionService(byte[] data, int pos, int limit)1616 private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) { 1617 // For the details of the structure of caption service descriptor, 1618 // see ATSC A/65 Section 6.9.2. 1619 if (limit <= pos + 2) { 1620 Log.e(TAG, "Broken CaptionServiceDescriptor."); 1621 return null; 1622 } 1623 List<AtscCaptionTrack> services = new ArrayList<>(); 1624 pos += 2; 1625 int numberServices = data[pos] & 0x1f; 1626 ++pos; 1627 if (limit < pos + numberServices * 6) { 1628 Log.e(TAG, "Broken CaptionServiceDescriptor."); 1629 return null; 1630 } 1631 for (int i = 0; i < numberServices; ++i) { 1632 String language = new String(Arrays.copyOfRange(data, pos, pos + 3)); 1633 pos += 3; 1634 boolean ccType = (data[pos] & 0x80) != 0; 1635 if (!ccType) { 1636 pos += 3; 1637 continue; 1638 } 1639 int captionServiceNumber = data[pos] & 0x3f; 1640 ++pos; 1641 boolean easyReader = (data[pos] & 0x80) != 0; 1642 boolean wideAspectRatio = (data[pos] & 0x40) != 0; 1643 byte[] reserved = new byte[2]; 1644 reserved[0] = (byte) (data[pos] << 2); 1645 reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6); 1646 reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2); 1647 pos += 2; 1648 AtscCaptionTrack captionTrack = 1649 AtscCaptionTrack.newBuilder() 1650 .setLanguage(language) 1651 .setServiceNumber(captionServiceNumber) 1652 .setEasyReader(easyReader) 1653 .setWideAspectRatio(wideAspectRatio) 1654 .build(); 1655 services.add(captionTrack); 1656 } 1657 return new CaptionServiceDescriptor(services); 1658 } 1659 parseContentAdvisory(byte[] data, int pos, int limit)1660 private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) { 1661 // For details of the structure for content advisory descriptor, see A/65 Table 6.27. 1662 if (limit <= pos + 2) { 1663 Log.e(TAG, "Broken ContentAdvisory"); 1664 return null; 1665 } 1666 int count = data[pos + 2] & 0x3f; 1667 pos += 3; 1668 List<RatingRegion> ratingRegions = new ArrayList<>(); 1669 for (int i = 0; i < count; ++i) { 1670 if (limit <= pos + 1) { 1671 Log.e(TAG, "Broken ContentAdvisory"); 1672 return null; 1673 } 1674 List<RegionalRating> indices = new ArrayList<>(); 1675 int ratingRegion = data[pos] & 0xff; 1676 int dimensionCount = data[pos + 1] & 0xff; 1677 pos += 2; 1678 int previousDimension = -1; 1679 for (int j = 0; j < dimensionCount; ++j) { 1680 if (limit <= pos + 1) { 1681 Log.e(TAG, "Broken ContentAdvisory"); 1682 return null; 1683 } 1684 int dimensionIndex = data[pos] & 0xff; 1685 int ratingValue = data[pos + 1] & 0x0f; 1686 if (dimensionIndex <= previousDimension) { 1687 // According to Table 6.27 of ATSC A65, 1688 // the indices shall be in increasing order. 1689 Log.e(TAG, "Broken ContentAdvisory"); 1690 return null; 1691 } 1692 previousDimension = dimensionIndex; 1693 pos += 2; 1694 indices.add(new RegionalRating(dimensionIndex, ratingValue)); 1695 } 1696 if (limit <= pos) { 1697 Log.e(TAG, "Broken ContentAdvisory"); 1698 return null; 1699 } 1700 int ratingDescriptionLength = data[pos] & 0xff; 1701 ++pos; 1702 if (limit < pos + ratingDescriptionLength) { 1703 Log.e(TAG, "Broken ContentAdvisory"); 1704 return null; 1705 } 1706 String ratingDescription = extractText(data, pos); 1707 pos += ratingDescriptionLength; 1708 ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices)); 1709 } 1710 return new ContentAdvisoryDescriptor(ratingRegions); 1711 } 1712 parseLongChannelName( byte[] data, int pos, int limit)1713 private static ExtendedChannelNameDescriptor parseLongChannelName( 1714 byte[] data, int pos, int limit) { 1715 if (limit <= pos + 2) { 1716 Log.e(TAG, "Broken ExtendedChannelName."); 1717 return null; 1718 } 1719 pos += 2; 1720 String text = extractText(data, pos); 1721 if (text == null) { 1722 Log.e(TAG, "Broken ExtendedChannelName."); 1723 return null; 1724 } 1725 return new ExtendedChannelNameDescriptor(text); 1726 } 1727 parseGenre(byte[] data, int pos, int limit)1728 private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) { 1729 pos += 2; 1730 int attributeCount = data[pos] & 0x1f; 1731 if (limit <= pos + attributeCount) { 1732 Log.e(TAG, "Broken Genre."); 1733 return null; 1734 } 1735 HashSet<String> broadcastGenreSet = new HashSet<>(); 1736 HashSet<String> canonicalGenreSet = new HashSet<>(); 1737 for (int i = 0; i < attributeCount; ++i) { 1738 ++pos; 1739 int genreCode = data[pos] & 0xff; 1740 if (genreCode < BROADCAST_GENRES_TABLE.length) { 1741 String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode]; 1742 if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) { 1743 broadcastGenreSet.add(broadcastGenre); 1744 } 1745 } 1746 if (genreCode < CANONICAL_GENRES_TABLE.length) { 1747 String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode]; 1748 if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) { 1749 canonicalGenreSet.add(canonicalGenre); 1750 } 1751 } 1752 } 1753 return new GenreDescriptor( 1754 broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]), 1755 canonicalGenreSet.toArray(new String[canonicalGenreSet.size()])); 1756 } 1757 parseAc3AudioStream(byte[] data, int pos, int limit)1758 private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) { 1759 // For details of the AC3 audio stream descriptor, see A/52 Table A4.1. 1760 if (limit <= pos + 5) { 1761 Log.e(TAG, "Broken AC3 audio stream descriptor."); 1762 return null; 1763 } 1764 pos += 2; 1765 byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5); 1766 byte bsid = (byte) (data[pos] & 0x1f); 1767 ++pos; 1768 byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2); 1769 byte surroundMode = (byte) (data[pos] & 0x03); 1770 ++pos; 1771 byte bsmod = (byte) ((data[pos] & 0xe0) >> 5); 1772 int numChannels = (data[pos] & 0x1e) >> 1; 1773 boolean fullSvc = (data[pos] & 0x01) != 0; 1774 ++pos; 1775 byte langCod = data[pos]; 1776 byte langCod2 = 0; 1777 if (numChannels == 0) { 1778 if (limit <= pos) { 1779 Log.e(TAG, "Broken AC3 audio stream descriptor."); 1780 return null; 1781 } 1782 ++pos; 1783 langCod2 = data[pos]; 1784 } 1785 if (limit <= pos + 1) { 1786 Log.e(TAG, "Broken AC3 audio stream descriptor."); 1787 return null; 1788 } 1789 byte mainId = 0; 1790 byte priority = 0; 1791 byte asvcflags = 0; 1792 ++pos; 1793 if (bsmod < 2) { 1794 mainId = (byte) ((data[pos] & 0xe0) >> 5); 1795 priority = (byte) ((data[pos] & 0x18) >> 3); 1796 if ((data[pos] & 0x07) != 0x07) { 1797 Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed"); 1798 return null; 1799 } 1800 } else { 1801 asvcflags = data[pos]; 1802 } 1803 1804 // See A/52B Table A3.6 num_channels. 1805 int numEncodedChannels; 1806 switch (numChannels) { 1807 case 1: 1808 case 8: 1809 numEncodedChannels = 1; 1810 break; 1811 case 2: 1812 case 9: 1813 numEncodedChannels = 2; 1814 break; 1815 case 3: 1816 case 4: 1817 case 10: 1818 numEncodedChannels = 3; 1819 break; 1820 case 5: 1821 case 6: 1822 case 11: 1823 numEncodedChannels = 4; 1824 break; 1825 case 7: 1826 case 12: 1827 numEncodedChannels = 5; 1828 break; 1829 case 13: 1830 numEncodedChannels = 6; 1831 break; 1832 default: 1833 numEncodedChannels = 0; 1834 break; 1835 } 1836 1837 if (limit <= pos + 1) { 1838 Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor."); 1839 return new Ac3AudioDescriptor( 1840 sampleRateCode, 1841 bsid, 1842 bitRateCode, 1843 surroundMode, 1844 bsmod, 1845 numEncodedChannels, 1846 fullSvc, 1847 langCod, 1848 langCod2, 1849 mainId, 1850 priority, 1851 asvcflags, 1852 null, 1853 null, 1854 null); 1855 } 1856 ++pos; 1857 int textLen = (data[pos] & 0xfe) >> 1; 1858 boolean textCode = (data[pos] & 0x01) != 0; 1859 ++pos; 1860 String text = ""; 1861 if (textLen > 0) { 1862 if (limit < pos + textLen) { 1863 Log.e(TAG, "Broken AC3 audio stream descriptor"); 1864 return null; 1865 } 1866 if (textCode) { 1867 text = new String(data, pos, textLen); 1868 } else { 1869 text = new String(data, pos, textLen, Charset.forName("UTF-16")); 1870 } 1871 pos += textLen; 1872 } 1873 String language = null; 1874 String language2 = null; 1875 if (pos < limit) { 1876 // Many AC3 audio stream descriptors skip the language fields. 1877 boolean languageFlag1 = (data[pos] & 0x80) != 0; 1878 boolean languageFlag2 = (data[pos] & 0x40) != 0; 1879 if ((data[pos] & 0x3f) != 0x3f) { 1880 Log.e(TAG, "Broken AC3 audio stream descriptor"); 1881 return null; 1882 } 1883 if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) { 1884 Log.e(TAG, "Broken AC3 audio stream descriptor"); 1885 return null; 1886 } 1887 ++pos; 1888 if (languageFlag1) { 1889 language = new String(data, pos, 3); 1890 pos += 3; 1891 } 1892 if (languageFlag2) { 1893 language2 = new String(data, pos, 3); 1894 } 1895 } 1896 1897 return new Ac3AudioDescriptor( 1898 sampleRateCode, 1899 bsid, 1900 bitRateCode, 1901 surroundMode, 1902 bsmod, 1903 numEncodedChannels, 1904 fullSvc, 1905 langCod, 1906 langCod2, 1907 mainId, 1908 priority, 1909 asvcflags, 1910 text, 1911 language, 1912 language2); 1913 } 1914 parseDvbService(byte[] data, int pos, int limit)1915 private static TsDescriptor parseDvbService(byte[] data, int pos, int limit) { 1916 // For details of DVB service descriptors, see DVB Document A038 Table 86. 1917 if (limit < pos + 5) { 1918 Log.e(TAG, "Broken service descriptor."); 1919 return null; 1920 } 1921 pos += 2; 1922 int serviceType = data[pos] & 0xff; 1923 pos++; 1924 int serviceProviderNameLength = data[pos] & 0xff; 1925 pos++; 1926 String serviceProviderName = extractTextFromDvb(data, pos, serviceProviderNameLength); 1927 pos += serviceProviderNameLength; 1928 int serviceNameLength = data[pos] & 0xff; 1929 pos++; 1930 String serviceName = extractTextFromDvb(data, pos, serviceNameLength); 1931 return new ServiceDescriptor(serviceType, serviceProviderName, serviceName); 1932 } 1933 parseDvbShortEvent(byte[] data, int pos, int limit)1934 private static TsDescriptor parseDvbShortEvent(byte[] data, int pos, int limit) { 1935 // For details of DVB service descriptors, see DVB Document A038 Table 91. 1936 if (limit < pos + 7) { 1937 Log.e(TAG, "Broken short event descriptor."); 1938 return null; 1939 } 1940 pos += 2; 1941 String language = new String(data, pos, 3); 1942 int eventNameLength = data[pos + 3] & 0xff; 1943 pos += 4; 1944 if (pos + eventNameLength > limit) { 1945 Log.e(TAG, "Broken short event descriptor."); 1946 return null; 1947 } 1948 String eventName = new String(data, pos, eventNameLength); 1949 pos += eventNameLength; 1950 int textLength = data[pos] & 0xff; 1951 if (pos + textLength > limit) { 1952 Log.e(TAG, "Broken short event descriptor."); 1953 return null; 1954 } 1955 pos++; 1956 String text = new String(data, pos, textLength); 1957 return new ShortEventDescriptor(language, eventName, text); 1958 } 1959 parseDvbContent(byte[] data, int pos, int limit)1960 private static TsDescriptor parseDvbContent(byte[] data, int pos, int limit) { 1961 // TODO: According to DVB Document A038 Table 27 to add a parser for content descriptor to 1962 // get content genre. 1963 return null; 1964 } 1965 parseDvbParentalRating(byte[] data, int pos, int limit)1966 private static TsDescriptor parseDvbParentalRating(byte[] data, int pos, int limit) { 1967 // For details of DVB service descriptors, see DVB Document A038 Table 81. 1968 HashMap<String, Integer> ratings = new HashMap<>(); 1969 pos += 2; 1970 while (pos + 4 <= limit) { 1971 String countryCode = new String(data, pos, 3); 1972 int rating = data[pos + 3] & 0xff; 1973 pos += 4; 1974 if (rating > 15) { 1975 // Rating > 15 means that the ratings is defined by broadcaster. 1976 continue; 1977 } 1978 ratings.put(countryCode, rating + 3); 1979 } 1980 return new ParentalRatingDescriptor(ratings); 1981 } 1982 getShortNameSize(byte[] data, int offset)1983 private static int getShortNameSize(byte[] data, int offset) { 1984 for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) { 1985 if (data[offset + i] == 0 && data[offset + i + 1] == 0) { 1986 return i; 1987 } 1988 } 1989 return MAX_SHORT_NAME_BYTES; 1990 } 1991 extractText(byte[] data, int pos)1992 private static String extractText(byte[] data, int pos) { 1993 if (data.length < pos) { 1994 return null; 1995 } 1996 int numStrings = data[pos] & 0xff; 1997 pos++; 1998 for (int i = 0; i < numStrings; ++i) { 1999 if (data.length <= pos + 3) { 2000 Log.e(TAG, "Broken text."); 2001 return null; 2002 } 2003 int numSegments = data[pos + 3] & 0xff; 2004 pos += 4; 2005 for (int j = 0; j < numSegments; ++j) { 2006 if (data.length <= pos + 2) { 2007 Log.e(TAG, "Broken text."); 2008 return null; 2009 } 2010 int compressionType = data[pos] & 0xff; 2011 int mode = data[pos + 1] & 0xff; 2012 int numBytes = data[pos + 2] & 0xff; 2013 if (data.length < pos + 3 + numBytes) { 2014 Log.e(TAG, "Broken text."); 2015 return null; 2016 } 2017 if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) { 2018 switch (mode) { 2019 case MODE_SELECTED_UNICODE_RANGE_1: 2020 return new String(data, pos + 3, numBytes, StandardCharsets.ISO_8859_1); 2021 case MODE_SCSU: 2022 if (SCSU_CHARSET != null) { 2023 return new String(data, pos + 3, numBytes, SCSU_CHARSET); 2024 } else { 2025 Log.w(TAG, "SCSU not supported"); 2026 return null; 2027 } 2028 case MODE_UTF16: 2029 return new String(data, pos + 3, numBytes, StandardCharsets.UTF_16); 2030 default: 2031 Log.w(TAG, "Unsupported text mode " + mode); 2032 return null; 2033 } 2034 } 2035 pos += 3 + numBytes; 2036 } 2037 } 2038 return null; 2039 } 2040 extractTextFromDvb(byte[] data, int pos, int length)2041 private static String extractTextFromDvb(byte[] data, int pos, int length) { 2042 // For details of DVB character set selection, see DVB Document A038 Annex A. 2043 if (data.length < pos + length) { 2044 return null; 2045 } 2046 try { 2047 String charsetPrefix = "ISO-8859-"; 2048 switch (data[0]) { 2049 case 0x01: 2050 case 0x02: 2051 case 0x03: 2052 case 0x04: 2053 case 0x05: 2054 case 0x06: 2055 case 0x07: 2056 case 0x09: 2057 case 0x0A: 2058 case 0x0B: 2059 String charset = charsetPrefix + String.valueOf(data[0] & 0xff + 4); 2060 return new String(data, pos, length, charset); 2061 case 0x10: 2062 if (length < 3) { 2063 Log.e(TAG, "Broken DVB text"); 2064 return null; 2065 } 2066 int codeTable = data[pos + 2] & 0xff; 2067 if (data[pos + 1] == 0 && codeTable > 0 && codeTable < 15) { 2068 return new String( 2069 data, pos, length, charsetPrefix + String.valueOf(codeTable)); 2070 } else { 2071 return new String(data, pos, length, "ISO-8859-1"); 2072 } 2073 case 0x11: 2074 case 0x14: 2075 case 0x15: 2076 return new String(data, pos, length, "UTF-16BE"); 2077 case 0x12: 2078 return new String(data, pos, length, "EUC-KR"); 2079 case 0x13: 2080 return new String(data, pos, length, "GB2312"); 2081 default: 2082 return new String(data, pos, length, "ISO-8859-1"); 2083 } 2084 } catch (UnsupportedEncodingException e) { 2085 Log.e(TAG, "Unsupported text format.", e); 2086 } 2087 return new String(data, pos, length); 2088 } 2089 checkSanity(byte[] data)2090 private static boolean checkSanity(byte[] data) { 2091 // Skipping CRC checking on Archer since TS data here was modified without updating CRC 2092 // value. For details, see b/28616908. 2093 if (Model.ARCHER.isEnabled()) { 2094 return true; 2095 } 2096 if (data.length <= 1) { 2097 return false; 2098 } 2099 boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator 2100 if (hasCRC) { 2101 int crc = 0xffffffff; 2102 for (byte b : data) { 2103 int index = ((crc >> 24) ^ (b & 0xff)) & 0xff; 2104 crc = CRC_TABLE[index] ^ (crc << 8); 2105 } 2106 if (crc != 0) { 2107 return false; 2108 } 2109 } 2110 return true; 2111 } 2112 } 2113