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.tuner.util.StringUtils; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Locale; 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 214 .format(Locale.US, "ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x " 215 + "ProgramNumber:%d %d-%d SourceId: %x", 216 mShortName, mLongName, mServiceType, mChannelTsid, 217 mProgramNumber, mMajorChannelNumber, mMinorChannelNumber, mSourceId); 218 } 219 220 public void setDescription(String description) { 221 mDescription = description; 222 } 223 224 public String getDescription() { 225 return mDescription; 226 } 227 } 228 229 /** 230 * A base class for descriptors of Ts packets. 231 */ 232 public abstract static class TsDescriptor { 233 public abstract int getTag(); 234 } 235 236 public static class ContentAdvisoryDescriptor extends TsDescriptor { 237 private final List<RatingRegion> mRatingRegions; 238 239 public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) { 240 mRatingRegions = ratingRegions; 241 } 242 243 @Override 244 public int getTag() { 245 return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY; 246 } 247 248 public List<RatingRegion> getRatingRegions() { 249 return mRatingRegions; 250 } 251 } 252 253 public static class CaptionServiceDescriptor extends TsDescriptor { 254 private final List<AtscCaptionTrack> mCaptionTracks; 255 256 public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) { 257 mCaptionTracks = captionTracks; 258 } 259 260 @Override 261 public int getTag() { 262 return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE; 263 } 264 265 public List<AtscCaptionTrack> getCaptionTracks() { 266 return mCaptionTracks; 267 } 268 } 269 270 public static class ExtendedChannelNameDescriptor extends TsDescriptor { 271 private final String mLongChannelName; 272 273 public ExtendedChannelNameDescriptor(String longChannelName) { 274 mLongChannelName = longChannelName; 275 } 276 277 @Override 278 public int getTag() { 279 return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME; 280 } 281 282 public String getLongChannelName() { 283 return mLongChannelName; 284 } 285 } 286 287 public static class GenreDescriptor extends TsDescriptor { 288 private final String[] mBroadcastGenres; 289 private final String[] mCanonicalGenres; 290 291 public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) { 292 mBroadcastGenres = broadcastGenres; 293 mCanonicalGenres = canonicalGenres; 294 } 295 296 @Override 297 public int getTag() { 298 return SectionParser.DESCRIPTOR_TAG_GENRE; 299 } 300 301 public String[] getBroadcastGenres() { 302 return mBroadcastGenres; 303 } 304 305 public String[] getCanonicalGenres() { 306 return mCanonicalGenres; 307 } 308 } 309 310 public static class Ac3AudioDescriptor extends TsDescriptor { 311 // See A/52 Annex A. Table A4.2 312 private static final byte SAMPLE_RATE_CODE_48000HZ = 0; 313 private static final byte SAMPLE_RATE_CODE_44100HZ = 1; 314 private static final byte SAMPLE_RATE_CODE_32000HZ = 2; 315 316 private final byte mSampleRateCode; 317 private final byte mBsid; 318 private final byte mBitRateCode; 319 private final byte mSurroundMode; 320 private final byte mBsmod; 321 private final int mNumChannels; 322 private final boolean mFullSvc; 323 private final byte mLangCod; 324 private final byte mLangCod2; 325 private final byte mMainId; 326 private final byte mPriority; 327 private final byte mAsvcflags; 328 private final String mText; 329 private final String mLanguage; 330 private final String mLanguage2; 331 332 public Ac3AudioDescriptor(byte sampleRateCode, byte bsid, byte bitRateCode, 333 byte surroundMode, byte bsmod, int numChannels, boolean fullSvc, byte langCod, 334 byte langCod2, byte mainId, byte priority, byte asvcflags, String text, 335 String language, String language2) { 336 mSampleRateCode = sampleRateCode; 337 mBsid = bsid; 338 mBitRateCode = bitRateCode; 339 mSurroundMode = surroundMode; 340 mBsmod = bsmod; 341 mNumChannels = numChannels; 342 mFullSvc = fullSvc; 343 mLangCod = langCod; 344 mLangCod2 = langCod2; 345 mMainId = mainId; 346 mPriority = priority; 347 mAsvcflags = asvcflags; 348 mText = text; 349 mLanguage = language; 350 mLanguage2 = language2; 351 } 352 353 @Override 354 public int getTag() { 355 return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM; 356 } 357 358 public byte getSampleRateCode() { 359 return mSampleRateCode; 360 } 361 362 public int getSampleRate() { 363 switch (mSampleRateCode) { 364 case SAMPLE_RATE_CODE_48000HZ: 365 return 48000; 366 case SAMPLE_RATE_CODE_44100HZ: 367 return 44100; 368 case SAMPLE_RATE_CODE_32000HZ: 369 return 32000; 370 default: 371 return 0; 372 } 373 } 374 375 public byte getBsid() { 376 return mBsid; 377 } 378 379 public byte getBitRateCode() { 380 return mBitRateCode; 381 } 382 383 public byte getSurroundMode() { 384 return mSurroundMode; 385 } 386 387 public byte getBsmod() { 388 return mBsmod; 389 } 390 391 public int getNumChannels() { 392 return mNumChannels; 393 } 394 395 public boolean isFullSvc() { 396 return mFullSvc; 397 } 398 399 public byte getLangCod() { 400 return mLangCod; 401 } 402 403 public byte getLangCod2() { 404 return mLangCod2; 405 } 406 407 public byte getMainId() { 408 return mMainId; 409 } 410 411 public byte getPriority() { 412 return mPriority; 413 } 414 415 public byte getAsvcflags() { 416 return mAsvcflags; 417 } 418 419 public String getText() { 420 return mText; 421 } 422 423 public String getLanguage() { 424 return mLanguage; 425 } 426 427 public String getLanguage2() { 428 return mLanguage2; 429 } 430 431 @Override 432 public String toString() { 433 return String.format(Locale.US, 434 "AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, " 435 + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, " 436 + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s" 437 + ", language2: %s", mSampleRateCode, mBsid, mBitRateCode, mSurroundMode, 438 mBsmod, mNumChannels, mFullSvc, mLangCod, mLangCod2, mMainId, mPriority, 439 mAsvcflags, mText, mLanguage, mLanguage2); 440 } 441 } 442 443 public static class Iso639LanguageDescriptor extends TsDescriptor { 444 private final List<AtscAudioTrack> mAudioTracks; 445 446 public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) { 447 mAudioTracks = audioTracks; 448 } 449 450 @Override 451 public int getTag() { 452 return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE; 453 } 454 455 public List<AtscAudioTrack> getAudioTracks() { 456 return mAudioTracks; 457 } 458 459 @Override 460 public String toString() { 461 return String.format("%s %s", getClass().getName(), mAudioTracks); 462 } 463 } 464 465 public static class RatingRegion { 466 private final int mName; 467 private final String mDescription; 468 private final List<RegionalRating> mRegionalRatings; 469 470 public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) { 471 mName = name; 472 mDescription = description; 473 mRegionalRatings = regionalRatings; 474 } 475 476 public int getName() { 477 return mName; 478 } 479 480 public String getDescription() { 481 return mDescription; 482 } 483 484 public List<RegionalRating> getRegionalRatings() { 485 return mRegionalRatings; 486 } 487 } 488 489 public static class RegionalRating { 490 private final int mDimension; 491 private final int mRating; 492 493 public RegionalRating(int dimension, int rating) { 494 mDimension = dimension; 495 mRating = rating; 496 } 497 498 public int getDimension() { 499 return mDimension; 500 } 501 502 public int getRating() { 503 return mRating; 504 } 505 } 506 507 public static class EitItem implements Comparable<EitItem>, TvTracksInterface { 508 public static final long INVALID_PROGRAM_ID = -1; 509 510 // A program id is a primary key of TvContract.Programs table. So it must be positive. 511 private final long mProgramId; 512 private final int mEventId; 513 private final String mTitleText; 514 private String mDescription; 515 private final long mStartTime; 516 private final int mLengthInSecond; 517 private final String mContentRating; 518 private final List<AtscAudioTrack> mAudioTracks; 519 private final List<AtscCaptionTrack> mCaptionTracks; 520 private boolean mHasCaptionTrack; 521 private final String mBroadcastGenre; 522 private final String mCanonicalGenre; 523 524 public EitItem(long programId, int eventId, String titleText, long startTime, 525 int lengthInSecond, String contentRating, List<AtscAudioTrack> audioTracks, 526 List<AtscCaptionTrack> captionTracks, String broadcastGenre, String canonicalGenre, 527 String description) { 528 mProgramId = programId; 529 mEventId = eventId; 530 mTitleText = titleText; 531 mStartTime = startTime; 532 mLengthInSecond = lengthInSecond; 533 mContentRating = contentRating; 534 mAudioTracks = audioTracks; 535 mCaptionTracks = captionTracks; 536 mBroadcastGenre = broadcastGenre; 537 mCanonicalGenre = canonicalGenre; 538 mDescription = description; 539 } 540 541 public long getProgramId() { 542 return mProgramId; 543 } 544 545 public int getEventId() { 546 return mEventId; 547 } 548 549 public String getTitleText() { 550 return mTitleText; 551 } 552 553 public void setDescription(String description) { 554 mDescription = description; 555 } 556 557 public String getDescription() { 558 return mDescription; 559 } 560 561 public long getStartTime() { 562 return mStartTime; 563 } 564 565 public int getLengthInSecond() { 566 return mLengthInSecond; 567 } 568 569 public long getStartTimeUtcMillis() { 570 return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS; 571 } 572 573 public long getEndTimeUtcMillis() { 574 return ConvertUtils.convertGPSTimeToUnixEpoch( 575 mStartTime + mLengthInSecond) * DateUtils.SECOND_IN_MILLIS; 576 } 577 578 public String getContentRating() { 579 return mContentRating; 580 } 581 582 @Override 583 public List<AtscAudioTrack> getAudioTracks() { 584 return mAudioTracks; 585 } 586 587 @Override 588 public List<AtscCaptionTrack> getCaptionTracks() { 589 return mCaptionTracks; 590 } 591 592 public String getBroadcastGenre() { 593 return mBroadcastGenre; 594 } 595 596 public String getCanonicalGenre() { 597 return mCanonicalGenre; 598 } 599 600 @Override 601 public void setHasCaptionTrack() { 602 mHasCaptionTrack = true; 603 } 604 605 @Override 606 public boolean hasCaptionTrack() { 607 return mHasCaptionTrack; 608 } 609 610 @Override 611 public int compareTo(@NonNull EitItem item) { 612 // The list of caption tracks and the program ids are not compared in here because the 613 // channels in TIF have the concept of the caption and audio tracks while the programs 614 // do not and the programs in TIF only have a program id since they are the rows of 615 // Content Provider. 616 int ret = mEventId - item.getEventId(); 617 if (ret != 0) { 618 return ret; 619 } 620 ret = StringUtils.compare(mTitleText, item.getTitleText()); 621 if (ret != 0) { 622 return ret; 623 } 624 if (mStartTime > item.getStartTime()) { 625 return 1; 626 } else if (mStartTime < item.getStartTime()) { 627 return -1; 628 } 629 if (mLengthInSecond > item.getLengthInSecond()) { 630 return 1; 631 } else if (mLengthInSecond < item.getLengthInSecond()) { 632 return -1; 633 } 634 635 // Compares content ratings 636 ret = StringUtils.compare(mContentRating, item.getContentRating()); 637 if (ret != 0) { 638 return ret; 639 } 640 641 // Compares broadcast genres 642 ret = StringUtils.compare(mBroadcastGenre, item.getBroadcastGenre()); 643 if (ret != 0) { 644 return ret; 645 } 646 // Compares canonical genres 647 ret = StringUtils.compare(mCanonicalGenre, item.getCanonicalGenre()); 648 if (ret != 0) { 649 return ret; 650 } 651 652 // Compares descriptions 653 return StringUtils.compare(mDescription, item.getDescription()); 654 } 655 656 public String getAudioLanguage() { 657 if (mAudioTracks == null) { 658 return ""; 659 } 660 ArrayList<String> languages = new ArrayList<>(); 661 for (AtscAudioTrack audioTrack : mAudioTracks) { 662 languages.add(audioTrack.language); 663 } 664 return TextUtils.join(",", languages); 665 } 666 667 @Override 668 public String toString() { 669 return String.format(Locale.US, 670 "EitItem programId: %d, eventId: %d, title: %s, startTime: %10d, " 671 + "length: %6d, rating: %s, audio tracks: %d, caption tracks: %d, " 672 + "genres (broadcast: %s, canonical: %s), description: %s", 673 mProgramId, mEventId, mTitleText, mStartTime, mLengthInSecond, mContentRating, 674 mAudioTracks != null ? mAudioTracks.size() : 0, 675 mCaptionTracks != null ? mCaptionTracks.size() : 0, 676 mBroadcastGenre, mCanonicalGenre, mDescription); 677 } 678 } 679 680 public static class EttItem { 681 public final int eventId; 682 public final String text; 683 684 public EttItem(int eventId, String text) { 685 this.eventId = eventId; 686 this.text = text; 687 } 688 } 689 } 690