1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.tuner.data; 18 19 import android.support.annotation.NonNull; 20 import android.text.TextUtils; 21 import android.text.format.DateUtils; 22 import com.android.tv.common.util.StringUtils; 23 import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; 24 import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; 25 import com.android.tv.tuner.ts.SectionParser; 26 import com.android.tv.tuner.util.ConvertUtils; 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.List; 30 import java.util.Locale; 31 32 /** Collection of ATSC PSIP table items. */ 33 public class PsipData { 34 35 private PsipData() {} 36 37 public static class PsipSection { 38 private final int mTableId; 39 private final int mTableIdExtension; 40 private final int mSectionNumber; 41 private final boolean mCurrentNextIndicator; 42 43 public static PsipSection create(byte[] data) { 44 if (data.length < 9) { 45 return null; 46 } 47 int tableId = data[0] & 0xff; 48 int tableIdExtension = (data[3] & 0xff) << 8 | (data[4] & 0xff); 49 int sectionNumber = data[6] & 0xff; 50 boolean currentNextIndicator = (data[5] & 0x01) != 0; 51 return new PsipSection(tableId, tableIdExtension, sectionNumber, currentNextIndicator); 52 } 53 54 private PsipSection( 55 int tableId, 56 int tableIdExtension, 57 int sectionNumber, 58 boolean currentNextIndicator) { 59 mTableId = tableId; 60 mTableIdExtension = tableIdExtension; 61 mSectionNumber = sectionNumber; 62 mCurrentNextIndicator = currentNextIndicator; 63 } 64 65 public int getTableId() { 66 return mTableId; 67 } 68 69 public int getTableIdExtension() { 70 return mTableIdExtension; 71 } 72 73 public int getSectionNumber() { 74 return mSectionNumber; 75 } 76 77 // This is for indicating that the section sent is applicable. 78 // We only consider a situation where currentNextIndicator is expected to have a true value. 79 // So, we are not going to compare this variable in hashCode() and equals() methods. 80 public boolean getCurrentNextIndicator() { 81 return mCurrentNextIndicator; 82 } 83 84 @Override 85 public int hashCode() { 86 int result = 17; 87 result = 31 * result + mTableId; 88 result = 31 * result + mTableIdExtension; 89 result = 31 * result + mSectionNumber; 90 return result; 91 } 92 93 @Override 94 public boolean equals(Object obj) { 95 if (obj instanceof PsipSection) { 96 PsipSection another = (PsipSection) obj; 97 return mTableId == another.getTableId() 98 && mTableIdExtension == another.getTableIdExtension() 99 && mSectionNumber == another.getSectionNumber(); 100 } 101 return false; 102 } 103 } 104 105 /** {@link TvTracksInterface} for serving the audio and caption tracks. */ 106 public interface TvTracksInterface { 107 /** Set the flag that tells the caption tracks have been found in this section container. */ 108 void setHasCaptionTrack(); 109 110 /** 111 * Returns whether or not the caption tracks have been found in this section container. If 112 * true, zero caption track will be interpreted as a clearance of the caption tracks. 113 */ 114 boolean hasCaptionTrack(); 115 116 /** Returns the audio tracks received. */ 117 List<AtscAudioTrack> getAudioTracks(); 118 119 /** Returns the caption tracks received. */ 120 List<AtscCaptionTrack> getCaptionTracks(); 121 } 122 123 public static class MgtItem { 124 public static final int TABLE_TYPE_EIT_RANGE_START = 0x0100; 125 public static final int TABLE_TYPE_EIT_RANGE_END = 0x017f; 126 public static final int TABLE_TYPE_CHANNEL_ETT = 0x0004; 127 public static final int TABLE_TYPE_ETT_RANGE_START = 0x0200; 128 public static final int TABLE_TYPE_ETT_RANGE_END = 0x027f; 129 130 private final int mTableType; 131 private final int mTableTypePid; 132 133 public MgtItem(int tableType, int tableTypePid) { 134 mTableType = tableType; 135 mTableTypePid = tableTypePid; 136 } 137 138 public int getTableType() { 139 return mTableType; 140 } 141 142 public int getTableTypePid() { 143 return mTableTypePid; 144 } 145 } 146 147 public static class VctItem { 148 private final String mShortName; 149 private final String mLongName; 150 private final int mServiceType; 151 private final int mChannelTsid; 152 private final int mProgramNumber; 153 private final int mMajorChannelNumber; 154 private final int mMinorChannelNumber; 155 private final int mSourceId; 156 private String mDescription; 157 158 public VctItem( 159 String shortName, 160 String longName, 161 int serviceType, 162 int channelTsid, 163 int programNumber, 164 int majorChannelNumber, 165 int minorChannelNumber, 166 int sourceId) { 167 mShortName = shortName; 168 mLongName = longName; 169 mServiceType = serviceType; 170 mChannelTsid = channelTsid; 171 mProgramNumber = programNumber; 172 mMajorChannelNumber = majorChannelNumber; 173 mMinorChannelNumber = minorChannelNumber; 174 mSourceId = sourceId; 175 } 176 177 public String getShortName() { 178 return mShortName; 179 } 180 181 public String getLongName() { 182 return mLongName; 183 } 184 185 public int getServiceType() { 186 return mServiceType; 187 } 188 189 public int getChannelTsid() { 190 return mChannelTsid; 191 } 192 193 public int getProgramNumber() { 194 return mProgramNumber; 195 } 196 197 public int getMajorChannelNumber() { 198 return mMajorChannelNumber; 199 } 200 201 public int getMinorChannelNumber() { 202 return mMinorChannelNumber; 203 } 204 205 public int getSourceId() { 206 return mSourceId; 207 } 208 209 @Override 210 public String toString() { 211 return String.format( 212 Locale.US, 213 "ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x " 214 + "ProgramNumber:%d %d-%d SourceId: %x", 215 mShortName, 216 mLongName, 217 mServiceType, 218 mChannelTsid, 219 mProgramNumber, 220 mMajorChannelNumber, 221 mMinorChannelNumber, 222 mSourceId); 223 } 224 225 public void setDescription(String description) { 226 mDescription = description; 227 } 228 229 public String getDescription() { 230 return mDescription; 231 } 232 } 233 234 public static class SdtItem { 235 private final String mServiceName; 236 private final String mServiceProviderName; 237 private final int mServiceType; 238 private final int mServiceId; 239 private final int mOriginalNetWorkId; 240 241 public SdtItem( 242 String serviceName, 243 String serviceProviderName, 244 int serviceType, 245 int serviceId, 246 int originalNetWorkId) { 247 mServiceName = serviceName; 248 mServiceProviderName = serviceProviderName; 249 mServiceType = serviceType; 250 mServiceId = serviceId; 251 mOriginalNetWorkId = originalNetWorkId; 252 } 253 254 public String getServiceName() { 255 return mServiceName; 256 } 257 258 public String getServiceProviderName() { 259 return mServiceProviderName; 260 } 261 262 public int getServiceType() { 263 return mServiceType; 264 } 265 266 public int getServiceId() { 267 return mServiceId; 268 } 269 270 public int getOriginalNetworkId() { 271 return mOriginalNetWorkId; 272 } 273 274 @Override 275 public String toString() { 276 return String.format( 277 "ServiceName: %s ServiceProviderName:%s ServiceType:%d " 278 + "OriginalNetworkId:%d", 279 mServiceName, mServiceProviderName, mServiceType, mOriginalNetWorkId); 280 } 281 } 282 283 /** A base class for descriptors of Ts packets. */ 284 public abstract static class TsDescriptor { 285 public abstract int getTag(); 286 } 287 288 public static class ContentAdvisoryDescriptor extends TsDescriptor { 289 private final List<RatingRegion> mRatingRegions; 290 291 public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) { 292 mRatingRegions = ratingRegions; 293 } 294 295 @Override 296 public int getTag() { 297 return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY; 298 } 299 300 public List<RatingRegion> getRatingRegions() { 301 return mRatingRegions; 302 } 303 } 304 305 public static class CaptionServiceDescriptor extends TsDescriptor { 306 private final List<AtscCaptionTrack> mCaptionTracks; 307 308 public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) { 309 mCaptionTracks = captionTracks; 310 } 311 312 @Override 313 public int getTag() { 314 return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE; 315 } 316 317 public List<AtscCaptionTrack> getCaptionTracks() { 318 return mCaptionTracks; 319 } 320 } 321 322 public static class ExtendedChannelNameDescriptor extends TsDescriptor { 323 private final String mLongChannelName; 324 325 public ExtendedChannelNameDescriptor(String longChannelName) { 326 mLongChannelName = longChannelName; 327 } 328 329 @Override 330 public int getTag() { 331 return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME; 332 } 333 334 public String getLongChannelName() { 335 return mLongChannelName; 336 } 337 } 338 339 public static class GenreDescriptor extends TsDescriptor { 340 private final String[] mBroadcastGenres; 341 private final String[] mCanonicalGenres; 342 343 public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) { 344 mBroadcastGenres = broadcastGenres; 345 mCanonicalGenres = canonicalGenres; 346 } 347 348 @Override 349 public int getTag() { 350 return SectionParser.DESCRIPTOR_TAG_GENRE; 351 } 352 353 public String[] getBroadcastGenres() { 354 return mBroadcastGenres; 355 } 356 357 public String[] getCanonicalGenres() { 358 return mCanonicalGenres; 359 } 360 } 361 362 public static class Ac3AudioDescriptor extends TsDescriptor { 363 // See A/52 Annex A. Table A4.2 364 private static final byte SAMPLE_RATE_CODE_48000HZ = 0; 365 private static final byte SAMPLE_RATE_CODE_44100HZ = 1; 366 private static final byte SAMPLE_RATE_CODE_32000HZ = 2; 367 368 private final byte mSampleRateCode; 369 private final byte mBsid; 370 private final byte mBitRateCode; 371 private final byte mSurroundMode; 372 private final byte mBsmod; 373 private final int mNumChannels; 374 private final boolean mFullSvc; 375 private final byte mLangCod; 376 private final byte mLangCod2; 377 private final byte mMainId; 378 private final byte mPriority; 379 private final byte mAsvcflags; 380 private final String mText; 381 private final String mLanguage; 382 private final String mLanguage2; 383 384 public Ac3AudioDescriptor( 385 byte sampleRateCode, 386 byte bsid, 387 byte bitRateCode, 388 byte surroundMode, 389 byte bsmod, 390 int numChannels, 391 boolean fullSvc, 392 byte langCod, 393 byte langCod2, 394 byte mainId, 395 byte priority, 396 byte asvcflags, 397 String text, 398 String language, 399 String language2) { 400 mSampleRateCode = sampleRateCode; 401 mBsid = bsid; 402 mBitRateCode = bitRateCode; 403 mSurroundMode = surroundMode; 404 mBsmod = bsmod; 405 mNumChannels = numChannels; 406 mFullSvc = fullSvc; 407 mLangCod = langCod; 408 mLangCod2 = langCod2; 409 mMainId = mainId; 410 mPriority = priority; 411 mAsvcflags = asvcflags; 412 mText = text; 413 mLanguage = language; 414 mLanguage2 = language2; 415 } 416 417 @Override 418 public int getTag() { 419 return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM; 420 } 421 422 public byte getSampleRateCode() { 423 return mSampleRateCode; 424 } 425 426 public int getSampleRate() { 427 switch (mSampleRateCode) { 428 case SAMPLE_RATE_CODE_48000HZ: 429 return 48000; 430 case SAMPLE_RATE_CODE_44100HZ: 431 return 44100; 432 case SAMPLE_RATE_CODE_32000HZ: 433 return 32000; 434 default: 435 return 0; 436 } 437 } 438 439 public byte getBsid() { 440 return mBsid; 441 } 442 443 public byte getBitRateCode() { 444 return mBitRateCode; 445 } 446 447 public byte getSurroundMode() { 448 return mSurroundMode; 449 } 450 451 public byte getBsmod() { 452 return mBsmod; 453 } 454 455 public int getNumChannels() { 456 return mNumChannels; 457 } 458 459 public boolean isFullSvc() { 460 return mFullSvc; 461 } 462 463 public byte getLangCod() { 464 return mLangCod; 465 } 466 467 public byte getLangCod2() { 468 return mLangCod2; 469 } 470 471 public byte getMainId() { 472 return mMainId; 473 } 474 475 public byte getPriority() { 476 return mPriority; 477 } 478 479 public byte getAsvcflags() { 480 return mAsvcflags; 481 } 482 483 public String getText() { 484 return mText; 485 } 486 487 public String getLanguage() { 488 return mLanguage; 489 } 490 491 public String getLanguage2() { 492 return mLanguage2; 493 } 494 495 @Override 496 public String toString() { 497 return String.format( 498 Locale.US, 499 "AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, " 500 + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, " 501 + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s" 502 + ", language2: %s", 503 mSampleRateCode, 504 mBsid, 505 mBitRateCode, 506 mSurroundMode, 507 mBsmod, 508 mNumChannels, 509 mFullSvc, 510 mLangCod, 511 mLangCod2, 512 mMainId, 513 mPriority, 514 mAsvcflags, 515 mText, 516 mLanguage, 517 mLanguage2); 518 } 519 } 520 521 public static class Iso639LanguageDescriptor extends TsDescriptor { 522 private final List<AtscAudioTrack> mAudioTracks; 523 524 public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) { 525 mAudioTracks = audioTracks; 526 } 527 528 @Override 529 public int getTag() { 530 return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE; 531 } 532 533 public List<AtscAudioTrack> getAudioTracks() { 534 return mAudioTracks; 535 } 536 537 @Override 538 public String toString() { 539 return String.format("%s %s", getClass().getName(), mAudioTracks); 540 } 541 } 542 543 public static class ServiceDescriptor extends TsDescriptor { 544 private final int mServiceType; 545 private final String mServiceProviderName; 546 private final String mServiceName; 547 548 public ServiceDescriptor(int serviceType, String serviceProviderName, String serviceName) { 549 mServiceType = serviceType; 550 mServiceProviderName = serviceProviderName; 551 mServiceName = serviceName; 552 } 553 554 @Override 555 public int getTag() { 556 return SectionParser.DVB_DESCRIPTOR_TAG_SERVICE; 557 } 558 559 public int getServiceType() { 560 return mServiceType; 561 } 562 563 public String getServiceProviderName() { 564 return mServiceProviderName; 565 } 566 567 public String getServiceName() { 568 return mServiceName; 569 } 570 571 @Override 572 public String toString() { 573 return String.format( 574 "Service descriptor, service type: %d, " 575 + "service provider name: %s, " 576 + "service name: %s", 577 mServiceType, mServiceProviderName, mServiceName); 578 } 579 } 580 581 public static class ShortEventDescriptor extends TsDescriptor { 582 private final String mLanguage; 583 private final String mEventName; 584 private final String mText; 585 586 public ShortEventDescriptor(String language, String eventName, String text) { 587 mLanguage = language; 588 mEventName = eventName; 589 mText = text; 590 } 591 592 public String getEventName() { 593 return mEventName; 594 } 595 596 @Override 597 public int getTag() { 598 return SectionParser.DVB_DESCRIPTOR_TAG_SHORT_EVENT; 599 } 600 601 @Override 602 public String toString() { 603 return String.format( 604 "ShortEvent Descriptor, language:%s, event name: %s, " + "text:%s", 605 mLanguage, mEventName, mText); 606 } 607 } 608 609 public static class ParentalRatingDescriptor extends TsDescriptor { 610 private final HashMap<String, Integer> mRatings; 611 612 public ParentalRatingDescriptor(HashMap<String, Integer> ratings) { 613 mRatings = ratings; 614 } 615 616 @Override 617 public int getTag() { 618 return SectionParser.DVB_DESCRIPTOR_TAG_PARENTAL_RATING; 619 } 620 621 public HashMap<String, Integer> getRatings() { 622 return mRatings; 623 } 624 625 @Override 626 public String toString() { 627 return String.format("Parental rating descriptor, ratings:" + mRatings); 628 } 629 } 630 631 public static class RatingRegion { 632 private final int mName; 633 private final String mDescription; 634 private final List<RegionalRating> mRegionalRatings; 635 636 public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) { 637 mName = name; 638 mDescription = description; 639 mRegionalRatings = regionalRatings; 640 } 641 642 public int getName() { 643 return mName; 644 } 645 646 public String getDescription() { 647 return mDescription; 648 } 649 650 public List<RegionalRating> getRegionalRatings() { 651 return mRegionalRatings; 652 } 653 } 654 655 public static class RegionalRating { 656 private final int mDimension; 657 private final int mRating; 658 659 public RegionalRating(int dimension, int rating) { 660 mDimension = dimension; 661 mRating = rating; 662 } 663 664 public int getDimension() { 665 return mDimension; 666 } 667 668 public int getRating() { 669 return mRating; 670 } 671 } 672 673 public static class EitItem implements Comparable<EitItem>, TvTracksInterface { 674 public static final long INVALID_PROGRAM_ID = -1; 675 676 // A program id is a primary key of TvContract.Programs table. So it must be positive. 677 private final long mProgramId; 678 private final int mEventId; 679 private final String mTitleText; 680 private String mDescription; 681 private final long mStartTime; 682 private final int mLengthInSecond; 683 private final String mContentRating; 684 private final List<AtscAudioTrack> mAudioTracks; 685 private final List<AtscCaptionTrack> mCaptionTracks; 686 private boolean mHasCaptionTrack; 687 private final String mBroadcastGenre; 688 private final String mCanonicalGenre; 689 690 public EitItem( 691 long programId, 692 int eventId, 693 String titleText, 694 long startTime, 695 int lengthInSecond, 696 String contentRating, 697 List<AtscAudioTrack> audioTracks, 698 List<AtscCaptionTrack> captionTracks, 699 String broadcastGenre, 700 String canonicalGenre, 701 String description) { 702 mProgramId = programId; 703 mEventId = eventId; 704 mTitleText = titleText; 705 mStartTime = startTime; 706 mLengthInSecond = lengthInSecond; 707 mContentRating = contentRating; 708 mAudioTracks = audioTracks; 709 mCaptionTracks = captionTracks; 710 mBroadcastGenre = broadcastGenre; 711 mCanonicalGenre = canonicalGenre; 712 mDescription = description; 713 } 714 715 public long getProgramId() { 716 return mProgramId; 717 } 718 719 public int getEventId() { 720 return mEventId; 721 } 722 723 public String getTitleText() { 724 return mTitleText; 725 } 726 727 public void setDescription(String description) { 728 mDescription = description; 729 } 730 731 public String getDescription() { 732 return mDescription; 733 } 734 735 public long getStartTime() { 736 return mStartTime; 737 } 738 739 public int getLengthInSecond() { 740 return mLengthInSecond; 741 } 742 743 public long getStartTimeUtcMillis() { 744 return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS; 745 } 746 747 public long getEndTimeUtcMillis() { 748 return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime + mLengthInSecond) 749 * DateUtils.SECOND_IN_MILLIS; 750 } 751 752 public String getContentRating() { 753 return mContentRating; 754 } 755 756 @Override 757 public List<AtscAudioTrack> getAudioTracks() { 758 return mAudioTracks; 759 } 760 761 @Override 762 public List<AtscCaptionTrack> getCaptionTracks() { 763 return mCaptionTracks; 764 } 765 766 public String getBroadcastGenre() { 767 return mBroadcastGenre; 768 } 769 770 public String getCanonicalGenre() { 771 return mCanonicalGenre; 772 } 773 774 @Override 775 public void setHasCaptionTrack() { 776 mHasCaptionTrack = true; 777 } 778 779 @Override 780 public boolean hasCaptionTrack() { 781 return mHasCaptionTrack; 782 } 783 784 @Override 785 public int compareTo(@NonNull EitItem item) { 786 // The list of caption tracks and the program ids are not compared in here because the 787 // channels in TIF have the concept of the caption and audio tracks while the programs 788 // do not and the programs in TIF only have a program id since they are the rows of 789 // Content Provider. 790 int ret = mEventId - item.getEventId(); 791 if (ret != 0) { 792 return ret; 793 } 794 ret = StringUtils.compare(mTitleText, item.getTitleText()); 795 if (ret != 0) { 796 return ret; 797 } 798 if (mStartTime > item.getStartTime()) { 799 return 1; 800 } else if (mStartTime < item.getStartTime()) { 801 return -1; 802 } 803 if (mLengthInSecond > item.getLengthInSecond()) { 804 return 1; 805 } else if (mLengthInSecond < item.getLengthInSecond()) { 806 return -1; 807 } 808 809 // Compares content ratings 810 ret = StringUtils.compare(mContentRating, item.getContentRating()); 811 if (ret != 0) { 812 return ret; 813 } 814 815 // Compares broadcast genres 816 ret = StringUtils.compare(mBroadcastGenre, item.getBroadcastGenre()); 817 if (ret != 0) { 818 return ret; 819 } 820 // Compares canonical genres 821 ret = StringUtils.compare(mCanonicalGenre, item.getCanonicalGenre()); 822 if (ret != 0) { 823 return ret; 824 } 825 826 // Compares descriptions 827 return StringUtils.compare(mDescription, item.getDescription()); 828 } 829 830 public String getAudioLanguage() { 831 if (mAudioTracks == null) { 832 return ""; 833 } 834 ArrayList<String> languages = new ArrayList<>(); 835 for (AtscAudioTrack audioTrack : mAudioTracks) { 836 languages.add(audioTrack.language); 837 } 838 return TextUtils.join(",", languages); 839 } 840 841 @Override 842 public String toString() { 843 return String.format( 844 Locale.US, 845 "EitItem programId: %d, eventId: %d, title: %s, startTime: %10d, " 846 + "length: %6d, rating: %s, audio tracks: %d, caption tracks: %d, " 847 + "genres (broadcast: %s, canonical: %s), description: %s", 848 mProgramId, 849 mEventId, 850 mTitleText, 851 mStartTime, 852 mLengthInSecond, 853 mContentRating, 854 mAudioTracks != null ? mAudioTracks.size() : 0, 855 mCaptionTracks != null ? mCaptionTracks.size() : 0, 856 mBroadcastGenre, 857 mCanonicalGenre, 858 mDescription); 859 } 860 } 861 862 public static class EttItem { 863 public final int eventId; 864 public final String text; 865 866 public EttItem(int eventId, String text) { 867 this.eventId = eventId; 868 this.text = text; 869 } 870 } 871 } 872