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 
PsipData()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 
create(byte[] data)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 
PsipSection(int tableId, int tableIdExtension, int sectionNumber, boolean currentNextIndicator)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 
getTableId()66         public int getTableId() {
67             return mTableId;
68         }
69 
getTableIdExtension()70         public int getTableIdExtension() {
71             return mTableIdExtension;
72         }
73 
getSectionNumber()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.
getCurrentNextIndicator()81         public boolean getCurrentNextIndicator() {
82             return mCurrentNextIndicator;
83         }
84 
85         @Override
hashCode()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
equals(Object obj)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          */
setHasCaptionTrack()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          */
hasCaptionTrack()119         boolean hasCaptionTrack();
120 
121         /**
122          * Returns the audio tracks received.
123          */
getAudioTracks()124         List<AtscAudioTrack> getAudioTracks();
125 
126         /**
127          * Returns the caption tracks received.
128          */
getCaptionTracks()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 
MgtItem(int tableType, int tableTypePid)142         public MgtItem(int tableType, int tableTypePid) {
143             mTableType = tableType;
144             mTableTypePid = tableTypePid;
145         }
146 
getTableType()147         public int getTableType() {
148             return mTableType;
149         }
150 
getTableTypePid()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 
VctItem(String shortName, String longName, int serviceType, int channelTsid, int programNumber, int majorChannelNumber, int minorChannelNumber, int sourceId)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 
getShortName()179         public String getShortName() {
180             return mShortName;
181         }
182 
getLongName()183         public String getLongName() {
184             return mLongName;
185         }
186 
getServiceType()187         public int getServiceType() {
188             return mServiceType;
189         }
190 
getChannelTsid()191         public int getChannelTsid() {
192             return mChannelTsid;
193         }
194 
getProgramNumber()195         public int getProgramNumber() {
196             return mProgramNumber;
197         }
198 
getMajorChannelNumber()199         public int getMajorChannelNumber() {
200             return mMajorChannelNumber;
201         }
202 
getMinorChannelNumber()203         public int getMinorChannelNumber() {
204             return mMinorChannelNumber;
205         }
206 
getSourceId()207         public int getSourceId() {
208             return mSourceId;
209         }
210 
211         @Override
toString()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 
setDescription(String description)220         public void setDescription(String description) {
221             mDescription = description;
222         }
223 
getDescription()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 {
getTag()233         public abstract int getTag();
234     }
235 
236     public static class ContentAdvisoryDescriptor extends TsDescriptor {
237         private final List<RatingRegion> mRatingRegions;
238 
ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions)239         public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) {
240             mRatingRegions = ratingRegions;
241         }
242 
243         @Override
getTag()244         public int getTag() {
245             return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY;
246         }
247 
getRatingRegions()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 
CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks)256         public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) {
257             mCaptionTracks = captionTracks;
258         }
259 
260         @Override
getTag()261         public int getTag() {
262             return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE;
263         }
264 
getCaptionTracks()265         public List<AtscCaptionTrack> getCaptionTracks() {
266             return mCaptionTracks;
267         }
268     }
269 
270     public static class ExtendedChannelNameDescriptor extends TsDescriptor {
271         private final String mLongChannelName;
272 
ExtendedChannelNameDescriptor(String longChannelName)273         public ExtendedChannelNameDescriptor(String longChannelName) {
274             mLongChannelName = longChannelName;
275         }
276 
277         @Override
getTag()278         public int getTag() {
279             return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME;
280         }
281 
getLongChannelName()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 
GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres)291         public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) {
292             mBroadcastGenres = broadcastGenres;
293             mCanonicalGenres = canonicalGenres;
294         }
295 
296         @Override
getTag()297         public int getTag() {
298             return SectionParser.DESCRIPTOR_TAG_GENRE;
299         }
300 
getBroadcastGenres()301         public String[] getBroadcastGenres() {
302             return mBroadcastGenres;
303         }
304 
getCanonicalGenres()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 
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)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
getTag()354         public int getTag() {
355             return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM;
356         }
357 
getSampleRateCode()358         public byte getSampleRateCode() {
359             return mSampleRateCode;
360         }
361 
getSampleRate()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 
getBsid()375         public byte getBsid() {
376             return mBsid;
377         }
378 
getBitRateCode()379         public byte getBitRateCode() {
380             return mBitRateCode;
381         }
382 
getSurroundMode()383         public byte getSurroundMode() {
384             return mSurroundMode;
385         }
386 
getBsmod()387         public byte getBsmod() {
388             return mBsmod;
389         }
390 
getNumChannels()391         public int getNumChannels() {
392             return mNumChannels;
393         }
394 
isFullSvc()395         public boolean isFullSvc() {
396             return mFullSvc;
397         }
398 
getLangCod()399         public byte getLangCod() {
400             return mLangCod;
401         }
402 
getLangCod2()403         public byte getLangCod2() {
404             return mLangCod2;
405         }
406 
getMainId()407         public byte getMainId() {
408             return mMainId;
409         }
410 
getPriority()411         public byte getPriority() {
412             return mPriority;
413         }
414 
getAsvcflags()415         public byte getAsvcflags() {
416             return mAsvcflags;
417         }
418 
getText()419         public String getText() {
420             return mText;
421         }
422 
getLanguage()423         public String getLanguage() {
424             return mLanguage;
425         }
426 
getLanguage2()427         public String getLanguage2() {
428             return mLanguage2;
429         }
430 
431         @Override
toString()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 
Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks)446         public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) {
447             mAudioTracks = audioTracks;
448         }
449 
450         @Override
getTag()451         public int getTag() {
452             return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE;
453         }
454 
getAudioTracks()455         public List<AtscAudioTrack> getAudioTracks() {
456             return mAudioTracks;
457         }
458 
459         @Override
toString()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 
RatingRegion(int name, String description, List<RegionalRating> regionalRatings)470         public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) {
471             mName = name;
472             mDescription = description;
473             mRegionalRatings = regionalRatings;
474         }
475 
getName()476         public int getName() {
477             return mName;
478         }
479 
getDescription()480         public String getDescription() {
481             return mDescription;
482         }
483 
getRegionalRatings()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 
RegionalRating(int dimension, int rating)493         public RegionalRating(int dimension, int rating) {
494             mDimension = dimension;
495             mRating = rating;
496         }
497 
getDimension()498         public int getDimension() {
499             return mDimension;
500         }
501 
getRating()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 
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)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 
getProgramId()541         public long getProgramId() {
542             return mProgramId;
543         }
544 
getEventId()545         public int getEventId() {
546             return mEventId;
547         }
548 
getTitleText()549         public String getTitleText() {
550             return mTitleText;
551         }
552 
setDescription(String description)553         public void setDescription(String description) {
554             mDescription = description;
555         }
556 
getDescription()557         public String getDescription() {
558             return mDescription;
559         }
560 
getStartTime()561         public long getStartTime() {
562             return mStartTime;
563         }
564 
getLengthInSecond()565         public int getLengthInSecond() {
566             return mLengthInSecond;
567         }
568 
getStartTimeUtcMillis()569         public long getStartTimeUtcMillis() {
570             return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS;
571         }
572 
getEndTimeUtcMillis()573         public long getEndTimeUtcMillis() {
574             return ConvertUtils.convertGPSTimeToUnixEpoch(
575                     mStartTime + mLengthInSecond) * DateUtils.SECOND_IN_MILLIS;
576         }
577 
getContentRating()578         public String getContentRating() {
579             return mContentRating;
580         }
581 
582         @Override
getAudioTracks()583         public List<AtscAudioTrack> getAudioTracks() {
584             return mAudioTracks;
585         }
586 
587         @Override
getCaptionTracks()588         public List<AtscCaptionTrack> getCaptionTracks() {
589             return mCaptionTracks;
590         }
591 
getBroadcastGenre()592         public String getBroadcastGenre() {
593             return mBroadcastGenre;
594         }
595 
getCanonicalGenre()596         public String getCanonicalGenre() {
597             return mCanonicalGenre;
598         }
599 
600         @Override
setHasCaptionTrack()601         public void setHasCaptionTrack() {
602             mHasCaptionTrack = true;
603         }
604 
605         @Override
hasCaptionTrack()606         public boolean hasCaptionTrack() {
607             return mHasCaptionTrack;
608         }
609 
610         @Override
compareTo(@onNull EitItem item)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 
getAudioLanguage()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
toString()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 
EttItem(int eventId, String text)684         public EttItem(int eventId, String text) {
685             this.eventId = eventId;
686             this.text = text;
687         }
688     }
689 }
690