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.data; 18 19 import android.support.annotation.NonNull; 20 import android.text.TextUtils; 21 import android.text.format.DateUtils; 22 23 import com.android.usbtuner.data.Track.AtscAudioTrack; 24 import com.android.usbtuner.data.Track.AtscCaptionTrack; 25 import com.android.usbtuner.ts.SectionParser; 26 import com.android.usbtuner.util.ConvertUtils; 27 import com.android.usbtuner.util.IsoUtils; 28 import com.android.usbtuner.util.StringUtils; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * Collection of ATSC PSIP table items. 35 */ 36 public class PsipData { 37 38 private PsipData() { 39 } 40 41 public static class PsipSection { 42 private final int mTableId; 43 private final int mTableIdExtension; 44 private final int mSectionNumber; 45 private final boolean mCurrentNextIndicator; 46 47 public static PsipSection create(byte[] data) { 48 if (data.length < 9) { 49 return null; 50 } 51 int tableId = data[0] & 0xff; 52 int tableIdExtension = (data[3] & 0xff) << 8 | (data[4] & 0xff); 53 int sectionNumber = data[6] & 0xff; 54 boolean currentNextIndicator = (data[5] & 0x01) != 0; 55 return new PsipSection(tableId, tableIdExtension, sectionNumber, currentNextIndicator); 56 } 57 58 private PsipSection(int tableId, int tableIdExtension, int sectionNumber, 59 boolean currentNextIndicator) { 60 mTableId = tableId; 61 mTableIdExtension = tableIdExtension; 62 mSectionNumber = sectionNumber; 63 mCurrentNextIndicator = currentNextIndicator; 64 } 65 66 public int getTableId() { 67 return mTableId; 68 } 69 70 public int getTableIdExtension() { 71 return mTableIdExtension; 72 } 73 74 public int getSectionNumber() { 75 return mSectionNumber; 76 } 77 78 // This is for indicating that the section sent is applicable. 79 // We only consider a situation where currentNextIndicator is expected to have a true value. 80 // So, we are not going to compare this variable in hashCode() and equals() methods. 81 public boolean getCurrentNextIndicator() { 82 return mCurrentNextIndicator; 83 } 84 85 @Override 86 public int hashCode() { 87 int result = 17; 88 result = 31 * result + mTableId; 89 result = 31 * result + mTableIdExtension; 90 result = 31 * result + mSectionNumber; 91 return result; 92 } 93 94 @Override 95 public boolean equals(Object obj) { 96 if (obj instanceof PsipSection) { 97 PsipSection another = (PsipSection) obj; 98 return mTableId == another.getTableId() 99 && mTableIdExtension == another.getTableIdExtension() 100 && mSectionNumber == another.getSectionNumber(); 101 } 102 return false; 103 } 104 } 105 106 /** 107 * {@link TvTracksInterface} for serving the audio and caption tracks. 108 */ 109 public interface TvTracksInterface { 110 /** 111 * Set the flag that tells the caption tracks have been found in this section container. 112 */ 113 void setHasCaptionTrack(); 114 115 /** 116 * Returns whether or not the caption tracks have been found in this section container. 117 * If true, zero caption track will be interpreted as a clearance of the caption tracks. 118 */ 119 boolean hasCaptionTrack(); 120 121 /** 122 * Returns the audio tracks received. 123 */ 124 List<AtscAudioTrack> getAudioTracks(); 125 126 /** 127 * Returns the caption tracks received. 128 */ 129 List<AtscCaptionTrack> getCaptionTracks(); 130 } 131 132 public static class MgtItem { 133 public static final int TABLE_TYPE_EIT_RANGE_START = 0x0100; 134 public static final int TABLE_TYPE_EIT_RANGE_END = 0x017f; 135 public static final int TABLE_TYPE_CHANNEL_ETT = 0x0004; 136 public static final int TABLE_TYPE_ETT_RANGE_START = 0x0200; 137 public static final int TABLE_TYPE_ETT_RANGE_END = 0x027f; 138 139 private final int mTableType; 140 private final int mTableTypePid; 141 142 public MgtItem(int tableType, int tableTypePid) { 143 mTableType = tableType; 144 mTableTypePid = tableTypePid; 145 } 146 147 public int getTableType() { 148 return mTableType; 149 } 150 151 public int getTableTypePid() { 152 return mTableTypePid; 153 } 154 } 155 156 public static class VctItem { 157 private final String mShortName; 158 private final String mLongName; 159 private final int mServiceType; 160 private final int mChannelTsid; 161 private final int mProgramNumber; 162 private final int mMajorChannelNumber; 163 private final int mMinorChannelNumber; 164 private final int mSourceId; 165 private String mDescription; 166 167 public VctItem(String shortName, String longName, int serviceType, int channelTsid, 168 int programNumber, int majorChannelNumber, int minorChannelNumber, int sourceId) { 169 mShortName = shortName; 170 mLongName = longName; 171 mServiceType = serviceType; 172 mChannelTsid = channelTsid; 173 mProgramNumber = programNumber; 174 mMajorChannelNumber = majorChannelNumber; 175 mMinorChannelNumber = minorChannelNumber; 176 mSourceId = sourceId; 177 } 178 179 public String getShortName() { 180 return mShortName; 181 } 182 183 public String getLongName() { 184 return mLongName; 185 } 186 187 public int getServiceType() { 188 return mServiceType; 189 } 190 191 public int getChannelTsid() { 192 return mChannelTsid; 193 } 194 195 public int getProgramNumber() { 196 return mProgramNumber; 197 } 198 199 public int getMajorChannelNumber() { 200 return mMajorChannelNumber; 201 } 202 203 public int getMinorChannelNumber() { 204 return mMinorChannelNumber; 205 } 206 207 public int getSourceId() { 208 return mSourceId; 209 } 210 211 @Override 212 public String toString() { 213 return String.format("ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x " 214 + "ProgramNumber:%d %d-%d SourceId: %x", 215 mShortName, mLongName, mServiceType, mChannelTsid, 216 mProgramNumber, mMajorChannelNumber, mMinorChannelNumber, mSourceId); 217 } 218 219 public void setDescription(String description) { 220 mDescription = description; 221 } 222 223 public String getDescription() { 224 return mDescription; 225 } 226 } 227 228 /** 229 * A base class for descriptors of Ts packets. 230 */ 231 public abstract static class TsDescriptor { 232 public abstract int getTag(); 233 } 234 235 public static class ContentAdvisoryDescriptor extends TsDescriptor { 236 private final List<RatingRegion> mRatingRegions; 237 238 public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) { 239 mRatingRegions = ratingRegions; 240 } 241 242 @Override 243 public int getTag() { 244 return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY; 245 } 246 247 public List<RatingRegion> getRatingRegions() { 248 return mRatingRegions; 249 } 250 } 251 252 public static class CaptionServiceDescriptor extends TsDescriptor { 253 private final List<AtscCaptionTrack> mCaptionTracks; 254 255 public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) { 256 mCaptionTracks = captionTracks; 257 } 258 259 @Override 260 public int getTag() { 261 return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE; 262 } 263 264 public List<AtscCaptionTrack> getCaptionTracks() { 265 return mCaptionTracks; 266 } 267 } 268 269 public static class ExtendedChannelNameDescriptor extends TsDescriptor { 270 private final String mLongChannelName; 271 272 public ExtendedChannelNameDescriptor(String longChannelName) { 273 mLongChannelName = longChannelName; 274 } 275 276 @Override 277 public int getTag() { 278 return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME; 279 } 280 281 public String getLongChannelName() { 282 return mLongChannelName; 283 } 284 } 285 286 public static class GenreDescriptor extends TsDescriptor { 287 private final String[] mBroadcastGenres; 288 private final String[] mCanonicalGenres; 289 290 public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) { 291 mBroadcastGenres = broadcastGenres; 292 mCanonicalGenres = canonicalGenres; 293 } 294 295 @Override 296 public int getTag() { 297 return SectionParser.DESCRIPTOR_TAG_GENRE; 298 } 299 300 public String[] getBroadcastGenres() { 301 return mBroadcastGenres; 302 } 303 304 public String[] getCanonicalGenres() { 305 return mCanonicalGenres; 306 } 307 } 308 309 public static class Ac3AudioDescriptor extends TsDescriptor { 310 // See A/52 Annex A. Table A4.2 311 private static final byte SAMPLE_RATE_CODE_48000HZ = 0; 312 private static final byte SAMPLE_RATE_CODE_44100HZ = 1; 313 private static final byte SAMPLE_RATE_CODE_32000HZ = 2; 314 315 private final byte mSampleRateCode; 316 private final byte mBsid; 317 private final byte mBitRateCode; 318 private final byte mSurroundMode; 319 private final byte mBsmod; 320 private final int mNumChannels; 321 private final boolean mFullSvc; 322 private final byte mLangCod; 323 private final byte mLangCod2; 324 private final byte mMainId; 325 private final byte mPriority; 326 private final byte mAsvcflags; 327 private final String mText; 328 private final String mLanguage; 329 private final String mLanguage2; 330 331 public Ac3AudioDescriptor(byte sampleRateCode, byte bsid, byte bitRateCode, 332 byte surroundMode, byte bsmod, int numChannels, boolean fullSvc, byte langCod, 333 byte langCod2, byte mainId, byte priority, byte asvcflags, String text, 334 String language, String language2) { 335 mSampleRateCode = sampleRateCode; 336 mBsid = bsid; 337 mBitRateCode = bitRateCode; 338 mSurroundMode = surroundMode; 339 mBsmod = bsmod; 340 mNumChannels = numChannels; 341 mFullSvc = fullSvc; 342 mLangCod = langCod; 343 mLangCod2 = langCod2; 344 mMainId = mainId; 345 mPriority = priority; 346 mAsvcflags = asvcflags; 347 mText = text; 348 mLanguage = language; 349 mLanguage2 = language2; 350 } 351 352 @Override 353 public int getTag() { 354 return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM; 355 } 356 357 public byte getSampleRateCode() { 358 return mSampleRateCode; 359 } 360 361 public int getSampleRate() { 362 switch (mSampleRateCode) { 363 case SAMPLE_RATE_CODE_48000HZ: 364 return 48000; 365 case SAMPLE_RATE_CODE_44100HZ: 366 return 44100; 367 case SAMPLE_RATE_CODE_32000HZ: 368 return 32000; 369 default: 370 return 0; 371 } 372 } 373 374 public byte getBsid() { 375 return mBsid; 376 } 377 378 public byte getBitRateCode() { 379 return mBitRateCode; 380 } 381 382 public byte getSurroundMode() { 383 return mSurroundMode; 384 } 385 386 public byte getBsmod() { 387 return mBsmod; 388 } 389 390 public int getNumChannels() { 391 return mNumChannels; 392 } 393 394 public boolean isFullSvc() { 395 return mFullSvc; 396 } 397 398 public byte getLangCod() { 399 return mLangCod; 400 } 401 402 public byte getLangCod2() { 403 return mLangCod2; 404 } 405 406 public byte getMainId() { 407 return mMainId; 408 } 409 410 public byte getPriority() { 411 return mPriority; 412 } 413 414 public byte getAsvcflags() { 415 return mAsvcflags; 416 } 417 418 public String getText() { 419 return mText; 420 } 421 422 public String getLanguage() { 423 return mLanguage; 424 } 425 426 public String getLanguage2() { 427 return mLanguage2; 428 } 429 430 @Override 431 public String toString() { 432 return String.format("AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, " 433 + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, " 434 + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s" 435 + ", language2: %s", mSampleRateCode, mBsid, mBitRateCode, mSurroundMode, 436 mBsmod, mNumChannels, mFullSvc, mLangCod, mLangCod2, mMainId, mPriority, 437 mAsvcflags, mText, mLanguage, mLanguage2); 438 } 439 } 440 441 public static class Iso639LanguageDescriptor extends TsDescriptor { 442 private final List<AtscAudioTrack> mAudioTracks; 443 444 public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) { 445 mAudioTracks = audioTracks; 446 } 447 448 @Override 449 public int getTag() { 450 return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE; 451 } 452 453 public List<AtscAudioTrack> getAudioTracks() { 454 return mAudioTracks; 455 } 456 457 @Override 458 public String toString() { 459 return String.format("%s %s", getClass().getName(), mAudioTracks); 460 } 461 } 462 463 public static class RatingRegion { 464 private final int mName; 465 private final String mDescription; 466 private final List<RegionalRating> mRegionalRatings; 467 468 public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) { 469 mName = name; 470 mDescription = description; 471 mRegionalRatings = regionalRatings; 472 } 473 474 public int getName() { 475 return mName; 476 } 477 478 public String getDescription() { 479 return mDescription; 480 } 481 482 public List<RegionalRating> getRegionalRatings() { 483 return mRegionalRatings; 484 } 485 } 486 487 public static class RegionalRating { 488 private int mDimension; 489 private int mRating; 490 491 public RegionalRating(int dimension, int rating) { 492 mDimension = dimension; 493 mRating = rating; 494 } 495 496 public int getDimension() { 497 return mDimension; 498 } 499 500 public int getRating() { 501 return mRating; 502 } 503 } 504 505 public static class EitItem implements Comparable<EitItem>, TvTracksInterface { 506 public static long INVALID_PROGRAM_ID = -1; 507 508 // A program id is a primary key of TvContract.Programs table. So it must be positive. 509 private final long mProgramId; 510 private final int mEventId; 511 private final String mTitleText; 512 private String mDescription; 513 private final long mStartTime; 514 private final int mLengthInSecond; 515 private final String mContentRating; 516 private final List<AtscAudioTrack> mAudioTracks; 517 private final List<AtscCaptionTrack> mCaptionTracks; 518 private boolean mHasCaptionTrack; 519 private final String mBroadcastGenre; 520 private final String mCanonicalGenre; 521 522 public EitItem(long programId, int eventId, String titleText, long startTime, 523 int lengthInSecond, String contentRating, List<AtscAudioTrack> audioTracks, 524 List<AtscCaptionTrack> captionTracks, String broadcastGenre, String canonicalGenre, 525 String description) { 526 mProgramId = programId; 527 mEventId = eventId; 528 mTitleText = titleText; 529 mStartTime = startTime; 530 mLengthInSecond = lengthInSecond; 531 mContentRating = contentRating; 532 mAudioTracks = audioTracks; 533 mCaptionTracks = captionTracks; 534 mBroadcastGenre = broadcastGenre; 535 mCanonicalGenre = canonicalGenre; 536 mDescription = description; 537 } 538 539 public long getProgramId() { 540 return mProgramId; 541 } 542 543 public int getEventId() { 544 return mEventId; 545 } 546 547 public String getTitleText() { 548 return mTitleText; 549 } 550 551 public void setDescription(String description) { 552 mDescription = description; 553 } 554 555 public String getDescription() { 556 return mDescription; 557 } 558 559 public long getStartTime() { 560 return mStartTime; 561 } 562 563 public int getLengthInSecond() { 564 return mLengthInSecond; 565 } 566 567 public long getStartTimeUtcMillis() { 568 return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS; 569 } 570 571 public long getEndTimeUtcMillis() { 572 return ConvertUtils.convertGPSTimeToUnixEpoch( 573 mStartTime + mLengthInSecond) * DateUtils.SECOND_IN_MILLIS; 574 } 575 576 public String getContentRating() { 577 return mContentRating; 578 } 579 580 @Override 581 public List<AtscAudioTrack> getAudioTracks() { 582 return mAudioTracks; 583 } 584 585 @Override 586 public List<AtscCaptionTrack> getCaptionTracks() { 587 return mCaptionTracks; 588 } 589 590 public String getBroadcastGenre() { 591 return mBroadcastGenre; 592 } 593 594 public String getCanonicalGenre() { 595 return mCanonicalGenre; 596 } 597 598 @Override 599 public void setHasCaptionTrack() { 600 mHasCaptionTrack = true; 601 } 602 603 @Override 604 public boolean hasCaptionTrack() { 605 return mHasCaptionTrack; 606 } 607 608 @Override 609 public int compareTo(@NonNull EitItem item) { 610 // The list of caption tracks and the program ids are not compared in here because the 611 // channels in TIF have the concept of the caption and audio tracks while the programs 612 // do not and the programs in TIF only have a program id since they are the rows of 613 // Content Provider. 614 int ret = mEventId - item.getEventId(); 615 if (ret != 0) { 616 return ret; 617 } 618 ret = StringUtils.compare(mTitleText, item.getTitleText()); 619 if (ret != 0) { 620 return ret; 621 } 622 if (mStartTime > item.getStartTime()) { 623 return 1; 624 } else if (mStartTime < item.getStartTime()) { 625 return -1; 626 } 627 if (mLengthInSecond > item.getLengthInSecond()) { 628 return 1; 629 } else if (mLengthInSecond < item.getLengthInSecond()) { 630 return -1; 631 } 632 633 // Compares content ratings 634 ret = StringUtils.compare(mContentRating, item.getContentRating()); 635 if (ret != 0) { 636 return ret; 637 } 638 639 // Compares broadcast genres 640 ret = StringUtils.compare(mBroadcastGenre, item.getBroadcastGenre()); 641 if (ret != 0) { 642 return ret; 643 } 644 // Compares canonical genres 645 ret = StringUtils.compare(mCanonicalGenre, item.getCanonicalGenre()); 646 if (ret != 0) { 647 return ret; 648 } 649 650 // Compares descriptions 651 return StringUtils.compare(mDescription, item.getDescription()); 652 } 653 654 public String getAudioLanguage() { 655 if (mAudioTracks == null) { 656 return ""; 657 } 658 ArrayList<String> languages = new ArrayList<>(); 659 for (AtscAudioTrack audioTrack : mAudioTracks) { 660 if (IsoUtils.isValidIso3Language(audioTrack.language)) { 661 languages.add(audioTrack.language); 662 } 663 } 664 return TextUtils.join(",", languages); 665 } 666 667 @Override 668 public String toString() { 669 return String.format("EitItem programId: %d, eventId: %d, title: %s, startTime: %10d" 670 + "length: %6d, rating: %s, audio tracks: %d, caption tracks: %d, " 671 + "genres (broadcast: %s, canonical: %s), description: %s", 672 mProgramId, mEventId, mTitleText, mStartTime, mLengthInSecond, mContentRating, 673 mAudioTracks != null ? mAudioTracks.size() : 0, 674 mCaptionTracks != null ? mCaptionTracks.size() : 0, 675 mBroadcastGenre, mCanonicalGenre, mDescription); 676 } 677 } 678 679 public static class EttItem { 680 public final int eventId; 681 public final String text; 682 683 public EttItem(int eventId, String text) { 684 this.eventId = eventId; 685 this.text = text; 686 } 687 } 688 } 689