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