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