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