• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PsipData()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 
create(byte[] data)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 
PsipSection(int tableId, int tableIdExtension, int sectionNumber, boolean currentNextIndicator)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 
getTableId()67         public int getTableId() {
68             return mTableId;
69         }
70 
getTableIdExtension()71         public int getTableIdExtension() {
72             return mTableIdExtension;
73         }
74 
getSectionNumber()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.
getCurrentNextIndicator()82         public boolean getCurrentNextIndicator() {
83             return mCurrentNextIndicator;
84         }
85 
86         @Override
hashCode()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
equals(Object obj)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          */
setHasCaptionTrack()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          */
hasCaptionTrack()120         boolean hasCaptionTrack();
121 
122         /**
123          * Returns the audio tracks received.
124          */
getAudioTracks()125         List<AtscAudioTrack> getAudioTracks();
126 
127         /**
128          * Returns the caption tracks received.
129          */
getCaptionTracks()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 
MgtItem(int tableType, int tableTypePid)143         public MgtItem(int tableType, int tableTypePid) {
144             mTableType = tableType;
145             mTableTypePid = tableTypePid;
146         }
147 
getTableType()148         public int getTableType() {
149             return mTableType;
150         }
151 
getTableTypePid()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 
VctItem(String shortName, String longName, int serviceType, int channelTsid, int programNumber, int majorChannelNumber, int minorChannelNumber, int sourceId)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 
getShortName()180         public String getShortName() {
181             return mShortName;
182         }
183 
getLongName()184         public String getLongName() {
185             return mLongName;
186         }
187 
getServiceType()188         public int getServiceType() {
189             return mServiceType;
190         }
191 
getChannelTsid()192         public int getChannelTsid() {
193             return mChannelTsid;
194         }
195 
getProgramNumber()196         public int getProgramNumber() {
197             return mProgramNumber;
198         }
199 
getMajorChannelNumber()200         public int getMajorChannelNumber() {
201             return mMajorChannelNumber;
202         }
203 
getMinorChannelNumber()204         public int getMinorChannelNumber() {
205             return mMinorChannelNumber;
206         }
207 
getSourceId()208         public int getSourceId() {
209             return mSourceId;
210         }
211 
212         @Override
toString()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 
setDescription(String description)221         public void setDescription(String description) {
222             mDescription = description;
223         }
224 
getDescription()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 
SdtItem(String serviceName, String serviceProviderName, int serviceType, int serviceId, int originalNetWorkId)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 
getServiceName()246         public String getServiceName() {
247             return mServiceName;
248         }
249 
getServiceProviderName()250         public String getServiceProviderName() {
251             return mServiceProviderName;
252         }
253 
getServiceType()254         public int getServiceType() {
255             return mServiceType;
256         }
257 
getServiceId()258         public int getServiceId() {
259             return mServiceId;
260         }
261 
getOriginalNetworkId()262         public int getOriginalNetworkId() {
263             return mOriginalNetWorkId;
264         }
265 
266         @Override
toString()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 {
getTag()278         public abstract int getTag();
279     }
280 
281     public static class ContentAdvisoryDescriptor extends TsDescriptor {
282         private final List<RatingRegion> mRatingRegions;
283 
ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions)284         public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) {
285             mRatingRegions = ratingRegions;
286         }
287 
288         @Override
getTag()289         public int getTag() {
290             return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY;
291         }
292 
getRatingRegions()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 
CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks)301         public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) {
302             mCaptionTracks = captionTracks;
303         }
304 
305         @Override
getTag()306         public int getTag() {
307             return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE;
308         }
309 
getCaptionTracks()310         public List<AtscCaptionTrack> getCaptionTracks() {
311             return mCaptionTracks;
312         }
313     }
314 
315     public static class ExtendedChannelNameDescriptor extends TsDescriptor {
316         private final String mLongChannelName;
317 
ExtendedChannelNameDescriptor(String longChannelName)318         public ExtendedChannelNameDescriptor(String longChannelName) {
319             mLongChannelName = longChannelName;
320         }
321 
322         @Override
getTag()323         public int getTag() {
324             return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME;
325         }
326 
getLongChannelName()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 
GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres)336         public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) {
337             mBroadcastGenres = broadcastGenres;
338             mCanonicalGenres = canonicalGenres;
339         }
340 
341         @Override
getTag()342         public int getTag() {
343             return SectionParser.DESCRIPTOR_TAG_GENRE;
344         }
345 
getBroadcastGenres()346         public String[] getBroadcastGenres() {
347             return mBroadcastGenres;
348         }
349 
getCanonicalGenres()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 
Ac3AudioDescriptor(byte sampleRateCode, byte bsid, byte bitRateCode, byte surroundMode, byte bsmod, int numChannels, boolean fullSvc, byte langCod, byte langCod2, byte mainId, byte priority, byte asvcflags, String text, String language, String language2)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
getTag()399         public int getTag() {
400             return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM;
401         }
402 
getSampleRateCode()403         public byte getSampleRateCode() {
404             return mSampleRateCode;
405         }
406 
getSampleRate()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 
getBsid()420         public byte getBsid() {
421             return mBsid;
422         }
423 
getBitRateCode()424         public byte getBitRateCode() {
425             return mBitRateCode;
426         }
427 
getSurroundMode()428         public byte getSurroundMode() {
429             return mSurroundMode;
430         }
431 
getBsmod()432         public byte getBsmod() {
433             return mBsmod;
434         }
435 
getNumChannels()436         public int getNumChannels() {
437             return mNumChannels;
438         }
439 
isFullSvc()440         public boolean isFullSvc() {
441             return mFullSvc;
442         }
443 
getLangCod()444         public byte getLangCod() {
445             return mLangCod;
446         }
447 
getLangCod2()448         public byte getLangCod2() {
449             return mLangCod2;
450         }
451 
getMainId()452         public byte getMainId() {
453             return mMainId;
454         }
455 
getPriority()456         public byte getPriority() {
457             return mPriority;
458         }
459 
getAsvcflags()460         public byte getAsvcflags() {
461             return mAsvcflags;
462         }
463 
getText()464         public String getText() {
465             return mText;
466         }
467 
getLanguage()468         public String getLanguage() {
469             return mLanguage;
470         }
471 
getLanguage2()472         public String getLanguage2() {
473             return mLanguage2;
474         }
475 
476         @Override
toString()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 
Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks)491         public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) {
492             mAudioTracks = audioTracks;
493         }
494 
495         @Override
getTag()496         public int getTag() {
497             return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE;
498         }
499 
getAudioTracks()500         public List<AtscAudioTrack> getAudioTracks() {
501             return mAudioTracks;
502         }
503 
504         @Override
toString()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 
ServiceDescriptor(int serviceType, String serviceProviderName, String serviceName)515         public ServiceDescriptor(int serviceType, String serviceProviderName, String serviceName) {
516             mServiceType = serviceType;
517             mServiceProviderName = serviceProviderName;
518             mServiceName = serviceName;
519         }
520 
521         @Override
getTag()522         public int getTag() {
523             return SectionParser.DVB_DESCRIPTOR_TAG_SERVICE;
524         }
525 
getServiceType()526         public int getServiceType() {
527             return mServiceType;
528         }
529 
getServiceProviderName()530         public String getServiceProviderName() {
531             return mServiceProviderName;
532         }
533 
getServiceName()534         public String getServiceName() {
535             return mServiceName;
536         }
537 
538         @Override
toString()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 
ShortEventDescriptor(String language, String eventName, String text)552         public ShortEventDescriptor(String language, String eventName, String text) {
553             mLanguage = language;
554             mEventName = eventName;
555             mText = text;
556         }
557 
getEventName()558         public String getEventName() {
559             return mEventName;
560         }
561 
562         @Override
getTag()563         public int getTag() {
564             return SectionParser.DVB_DESCRIPTOR_TAG_SHORT_EVENT;
565         }
566 
567         @Override
toString()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 
ParentalRatingDescriptor(HashMap<String, Integer> ratings)577         public ParentalRatingDescriptor(HashMap<String, Integer> ratings) {
578             mRatings = ratings;
579         }
580 
581         @Override
getTag()582         public int getTag() {
583             return SectionParser.DVB_DESCRIPTOR_TAG_PARENTAL_RATING;
584         }
585 
getRatings()586         public HashMap<String, Integer> getRatings() {
587             return mRatings;
588         }
589 
590         @Override
toString()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 
RatingRegion(int name, String description, List<RegionalRating> regionalRatings)601         public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) {
602             mName = name;
603             mDescription = description;
604             mRegionalRatings = regionalRatings;
605         }
606 
getName()607         public int getName() {
608             return mName;
609         }
610 
getDescription()611         public String getDescription() {
612             return mDescription;
613         }
614 
getRegionalRatings()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 
RegionalRating(int dimension, int rating)624         public RegionalRating(int dimension, int rating) {
625             mDimension = dimension;
626             mRating = rating;
627         }
628 
getDimension()629         public int getDimension() {
630             return mDimension;
631         }
632 
getRating()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 
EitItem(long programId, int eventId, String titleText, long startTime, int lengthInSecond, String contentRating, List<AtscAudioTrack> audioTracks, List<AtscCaptionTrack> captionTracks, String broadcastGenre, String canonicalGenre, String description)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 
getProgramId()672         public long getProgramId() {
673             return mProgramId;
674         }
675 
getEventId()676         public int getEventId() {
677             return mEventId;
678         }
679 
getTitleText()680         public String getTitleText() {
681             return mTitleText;
682         }
683 
setDescription(String description)684         public void setDescription(String description) {
685             mDescription = description;
686         }
687 
getDescription()688         public String getDescription() {
689             return mDescription;
690         }
691 
getStartTime()692         public long getStartTime() {
693             return mStartTime;
694         }
695 
getLengthInSecond()696         public int getLengthInSecond() {
697             return mLengthInSecond;
698         }
699 
getStartTimeUtcMillis()700         public long getStartTimeUtcMillis() {
701             return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS;
702         }
703 
getEndTimeUtcMillis()704         public long getEndTimeUtcMillis() {
705             return ConvertUtils.convertGPSTimeToUnixEpoch(
706                     mStartTime + mLengthInSecond) * DateUtils.SECOND_IN_MILLIS;
707         }
708 
getContentRating()709         public String getContentRating() {
710             return mContentRating;
711         }
712 
713         @Override
getAudioTracks()714         public List<AtscAudioTrack> getAudioTracks() {
715             return mAudioTracks;
716         }
717 
718         @Override
getCaptionTracks()719         public List<AtscCaptionTrack> getCaptionTracks() {
720             return mCaptionTracks;
721         }
722 
getBroadcastGenre()723         public String getBroadcastGenre() {
724             return mBroadcastGenre;
725         }
726 
getCanonicalGenre()727         public String getCanonicalGenre() {
728             return mCanonicalGenre;
729         }
730 
731         @Override
setHasCaptionTrack()732         public void setHasCaptionTrack() {
733             mHasCaptionTrack = true;
734         }
735 
736         @Override
hasCaptionTrack()737         public boolean hasCaptionTrack() {
738             return mHasCaptionTrack;
739         }
740 
741         @Override
compareTo(@onNull EitItem item)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 
getAudioLanguage()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
toString()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 
EttItem(int eventId, String text)815         public EttItem(int eventId, String text) {
816             this.eventId = eventId;
817             this.text = text;
818         }
819     }
820 }
821