Home | History | Annotate | Download | only in data
      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