• 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.util.Log;
21 
22 import com.android.tv.tuner.data.nano.Channel;
23 import com.android.tv.tuner.data.nano.Channel.TunerChannelProto;
24 import com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
25 import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
26 import com.android.tv.tuner.util.Ints;
27 import com.android.tv.util.StringUtils;
28 import com.google.protobuf.nano.MessageNano;
29 
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.Objects;
36 
37 /**
38  * A class that represents a single channel accessible through a tuner.
39  */
40 public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracksInterface {
41     private static final String TAG = "TunerChannel";
42 
43     /**
44      * Channel number separator between major number and minor number.
45      */
46     public static final char CHANNEL_NUMBER_SEPARATOR = '-';
47 
48     // See ATSC Code Points Registry.
49     private static final String[] ATSC_SERVICE_TYPE_NAMES = new String[] {
50             "ATSC Reserved",
51             "Analog television channels",
52             "ATSC_digital_television",
53             "ATSC_audio",
54             "ATSC_data_only_service",
55             "Software Download",
56             "Unassociated/Small Screen Service",
57             "Parameterized Service",
58             "ATSC NRT Service",
59             "Extended Parameterized Service" };
60     private static final String ATSC_SERVICE_TYPE_NAME_RESERVED =
61             ATSC_SERVICE_TYPE_NAMES[Channel.SERVICE_TYPE_ATSC_RESERVED];
62 
63     public static final int INVALID_FREQUENCY = -1;
64 
65     // According to RFC4259, The number of available PIDs ranges from 0 to 8191.
66     public static final int INVALID_PID = -1;
67 
68     // According to ISO13818-1, Mpeg2 StreamType has a range from 0x00 to 0xff.
69     public static final int INVALID_STREAMTYPE = -1;
70 
71     // @GuardedBy(this) Writing operations and toByteArray will be guarded. b/34197766
72     private final TunerChannelProto mProto;
73 
TunerChannel(PsipData.VctItem channel, int programNumber, List<PsiData.PmtItem> pmtItems, int type)74     private TunerChannel(PsipData.VctItem channel, int programNumber,
75             List<PsiData.PmtItem> pmtItems, int type) {
76         mProto = new TunerChannelProto();
77         if (channel == null) {
78             mProto.shortName = "";
79             mProto.tsid = 0;
80             mProto.programNumber = programNumber;
81             mProto.virtualMajor = 0;
82             mProto.virtualMinor = 0;
83         } else {
84             mProto.shortName = channel.getShortName();
85             if (channel.getLongName() != null) {
86                 mProto.longName = channel.getLongName();
87             }
88             mProto.tsid = channel.getChannelTsid();
89             mProto.programNumber = channel.getProgramNumber();
90             mProto.virtualMajor = channel.getMajorChannelNumber();
91             mProto.virtualMinor = channel.getMinorChannelNumber();
92             if (channel.getDescription() != null) {
93                 mProto.description = channel.getDescription();
94             }
95             mProto.serviceType = channel.getServiceType();
96         }
97         initProto(pmtItems, type);
98     }
99 
initProto(List<PsiData.PmtItem> pmtItems, int type)100     private void initProto(List<PsiData.PmtItem> pmtItems, int type) {
101         mProto.type = type;
102         mProto.channelId = -1L;
103         mProto.frequency = INVALID_FREQUENCY;
104         mProto.videoPid = INVALID_PID;
105         mProto.videoStreamType = INVALID_STREAMTYPE;
106         List<Integer> audioPids = new ArrayList<>();
107         List<Integer> audioStreamTypes = new ArrayList<>();
108         for (PsiData.PmtItem pmt : pmtItems) {
109             switch (pmt.getStreamType()) {
110                 // MPEG ES stream video types
111                 case Channel.MPEG1:
112                 case Channel.MPEG2:
113                 case Channel.H263:
114                 case Channel.H264:
115                 case Channel.H265:
116                     mProto.videoPid = pmt.getEsPid();
117                     mProto.videoStreamType = pmt.getStreamType();
118                     break;
119 
120                 // MPEG ES stream audio types
121                 case Channel.MPEG1AUDIO:
122                 case Channel.MPEG2AUDIO:
123                 case Channel.MPEG2AACAUDIO:
124                 case Channel.MPEG4LATMAACAUDIO:
125                 case Channel.A52AC3AUDIO:
126                 case Channel.EAC3AUDIO:
127                     audioPids.add(pmt.getEsPid());
128                     audioStreamTypes.add(pmt.getStreamType());
129                     break;
130 
131                 // Non MPEG ES stream types
132                 case 0x100: // PmtItem.ES_PID_PCR:
133                     mProto.pcrPid = pmt.getEsPid();
134                     break;
135             }
136         }
137         mProto.audioPids = Ints.toArray(audioPids);
138         mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
139         mProto.audioTrackIndex = (audioPids.size() > 0) ? 0 : -1;
140     }
141 
TunerChannel(int programNumber, int type, PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems)142     private TunerChannel(int programNumber, int type, PsipData.SdtItem channel,
143             List<PsiData.PmtItem> pmtItems) {
144         mProto = new TunerChannelProto();
145         mProto.tsid = 0;
146         mProto.virtualMajor = 0;
147         mProto.virtualMinor = 0;
148         if (channel == null) {
149             mProto.shortName = "";
150             mProto.programNumber = programNumber;
151         } else {
152             mProto.shortName = channel.getServiceName();
153             mProto.programNumber = channel.getServiceId();
154             mProto.serviceType = channel.getServiceType();
155         }
156         initProto(pmtItems, type);
157     }
158 
159     /**
160      * Initialize tuner channel with VCT items and PMT items.
161      */
TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems)162     public TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
163         this(channel, 0, pmtItems, Channel.TYPE_TUNER);
164     }
165 
166     /**
167      * Initialize tuner channel with program number and PMT items.
168      */
TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems)169     public TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems) {
170         this(null, programNumber, pmtItems, Channel.TYPE_TUNER);
171     }
172 
173     /**
174      * Initialize tuner channel with SDT items and PMT items.
175      */
TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems)176     public TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
177         this(0, Channel.TYPE_TUNER, channel, pmtItems);
178     }
179 
TunerChannel(TunerChannelProto tunerChannelProto)180     private TunerChannel(TunerChannelProto tunerChannelProto) {
181         mProto = tunerChannelProto;
182     }
183 
forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems)184     public static TunerChannel forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
185         return new TunerChannel(channel, 0, pmtItems, Channel.TYPE_FILE);
186     }
187 
forDvbFile( PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems)188     public static TunerChannel forDvbFile(
189             PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
190         return new TunerChannel(0, Channel.TYPE_FILE, channel, pmtItems);
191     }
192 
193     /**
194      * Create a TunerChannel object suitable for network tuners
195      * @param major Channel number major
196      * @param minor Channel number minor
197      * @param programNumber Program number
198      * @param shortName Short name
199      * @param recordingProhibited Recording prohibition info
200      * @param videoFormat Video format. Should be {@code null} or one of the followings:
201      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_240P},
202      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_360P},
203      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_480I},
204      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_480P},
205      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_576I},
206      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_576P},
207      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_720P},
208      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080I},
209      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080P},
210      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_2160P},
211      *                    {@link android.media.tv.TvContract.Channels#VIDEO_FORMAT_4320P}
212      * @return a TunerChannel object
213      */
forNetwork(int major, int minor, int programNumber, String shortName, boolean recordingProhibited, String videoFormat)214     public static TunerChannel forNetwork(int major, int minor, int programNumber,
215             String shortName, boolean recordingProhibited, String videoFormat) {
216         TunerChannel tunerChannel = new TunerChannel(
217                 null, programNumber, Collections.EMPTY_LIST, Channel.TYPE_NETWORK);
218         tunerChannel.setVirtualMajor(major);
219         tunerChannel.setVirtualMinor(minor);
220         tunerChannel.setShortName(shortName);
221         // Set audio and video pids in order to work around the audio-only channel check.
222         tunerChannel.setAudioPids(new ArrayList<>(Arrays.asList(0)));
223         tunerChannel.selectAudioTrack(0);
224         tunerChannel.setVideoPid(0);
225         tunerChannel.setRecordingProhibited(recordingProhibited);
226         if (videoFormat != null) {
227             tunerChannel.setVideoFormat(videoFormat);
228         }
229         return tunerChannel;
230     }
231 
getName()232     public String getName() {
233         return (!mProto.shortName.isEmpty()) ? mProto.shortName : mProto.longName;
234     }
235 
getShortName()236     public String getShortName() {
237         return mProto.shortName;
238     }
239 
getProgramNumber()240     public int getProgramNumber() {
241         return mProto.programNumber;
242     }
243 
getServiceType()244     public int getServiceType() {
245         return mProto.serviceType;
246     }
247 
getServiceTypeName()248     public String getServiceTypeName() {
249         int serviceType = mProto.serviceType;
250         if (serviceType >= 0 && serviceType < ATSC_SERVICE_TYPE_NAMES.length) {
251             return ATSC_SERVICE_TYPE_NAMES[serviceType];
252         }
253         return ATSC_SERVICE_TYPE_NAME_RESERVED;
254     }
255 
getVirtualMajor()256     public int getVirtualMajor() {
257         return mProto.virtualMajor;
258     }
259 
getVirtualMinor()260     public int getVirtualMinor() {
261         return mProto.virtualMinor;
262     }
263 
getFrequency()264     public int getFrequency() {
265         return mProto.frequency;
266     }
267 
getModulation()268     public String getModulation() {
269         return mProto.modulation;
270     }
271 
getTsid()272     public int getTsid() {
273         return mProto.tsid;
274     }
275 
getVideoPid()276     public int getVideoPid() {
277         return mProto.videoPid;
278     }
279 
setVideoPid(int videoPid)280     synchronized public void setVideoPid(int videoPid) {
281         mProto.videoPid = videoPid;
282     }
283 
getVideoStreamType()284     public int getVideoStreamType() {
285         return mProto.videoStreamType;
286     }
287 
getAudioPid()288     public int getAudioPid() {
289         if (mProto.audioTrackIndex == -1) {
290             return INVALID_PID;
291         }
292         return mProto.audioPids[mProto.audioTrackIndex];
293     }
294 
getAudioStreamType()295     public int getAudioStreamType() {
296         if (mProto.audioTrackIndex == -1) {
297             return INVALID_STREAMTYPE;
298         }
299         return mProto.audioStreamTypes[mProto.audioTrackIndex];
300     }
301 
getAudioPids()302     public List<Integer> getAudioPids() {
303         return Ints.asList(mProto.audioPids);
304     }
305 
setAudioPids(List<Integer> audioPids)306     synchronized public void setAudioPids(List<Integer> audioPids) {
307         mProto.audioPids = Ints.toArray(audioPids);
308     }
309 
getAudioStreamTypes()310     public List<Integer> getAudioStreamTypes() {
311         return Ints.asList(mProto.audioStreamTypes);
312     }
313 
setAudioStreamTypes(List<Integer> audioStreamTypes)314     synchronized public void setAudioStreamTypes(List<Integer> audioStreamTypes) {
315         mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
316     }
317 
getPcrPid()318     public int getPcrPid() {
319         return mProto.pcrPid;
320     }
321 
getType()322     public int getType() {
323         return mProto.type;
324     }
325 
setFilepath(String filepath)326     synchronized public void setFilepath(String filepath) {
327         mProto.filepath = filepath == null ? "" : filepath;
328     }
329 
getFilepath()330     public String getFilepath() {
331         return mProto.filepath;
332     }
333 
setVirtualMajor(int virtualMajor)334     synchronized public void setVirtualMajor(int virtualMajor) {
335         mProto.virtualMajor = virtualMajor;
336     }
337 
setVirtualMinor(int virtualMinor)338     synchronized public void setVirtualMinor(int virtualMinor) {
339         mProto.virtualMinor = virtualMinor;
340     }
341 
setShortName(String shortName)342     synchronized public void setShortName(String shortName) {
343         mProto.shortName = shortName == null ? "" : shortName;
344     }
345 
setFrequency(int frequency)346     synchronized public void setFrequency(int frequency) {
347         mProto.frequency = frequency;
348     }
349 
setModulation(String modulation)350     synchronized public void setModulation(String modulation) {
351         mProto.modulation = modulation == null ? "" : modulation;
352     }
353 
hasVideo()354     public boolean hasVideo() {
355         return mProto.videoPid != INVALID_PID;
356     }
357 
hasAudio()358     public boolean hasAudio() {
359         return getAudioPid() != INVALID_PID;
360     }
361 
getChannelId()362     public long getChannelId() {
363         return mProto.channelId;
364     }
365 
setChannelId(long channelId)366     synchronized public void setChannelId(long channelId) {
367         mProto.channelId = channelId;
368     }
369 
getDisplayNumber()370     public String getDisplayNumber() {
371         return getDisplayNumber(true);
372     }
373 
getDisplayNumber(boolean ignoreZeroMinorNumber)374     public String getDisplayNumber(boolean ignoreZeroMinorNumber) {
375         if (mProto.virtualMajor != 0 && (mProto.virtualMinor != 0 || !ignoreZeroMinorNumber)) {
376             return String.format("%d%c%d", mProto.virtualMajor, CHANNEL_NUMBER_SEPARATOR,
377                     mProto.virtualMinor);
378         } else if (mProto.virtualMajor != 0) {
379             return Integer.toString(mProto.virtualMajor);
380         } else {
381             return Integer.toString(mProto.programNumber);
382         }
383     }
384 
getDescription()385     public String getDescription() {
386         return mProto.description;
387     }
388 
389     @Override
setHasCaptionTrack()390     synchronized public void setHasCaptionTrack() {
391         mProto.hasCaptionTrack = true;
392     }
393 
394     @Override
hasCaptionTrack()395     public boolean hasCaptionTrack() {
396         return mProto.hasCaptionTrack;
397     }
398 
399     @Override
getAudioTracks()400     public List<AtscAudioTrack> getAudioTracks() {
401         return Collections.unmodifiableList(Arrays.asList(mProto.audioTracks));
402     }
403 
setAudioTracks(List<AtscAudioTrack> audioTracks)404     synchronized public void setAudioTracks(List<AtscAudioTrack> audioTracks) {
405         mProto.audioTracks = audioTracks.toArray(new AtscAudioTrack[audioTracks.size()]);
406     }
407 
408     @Override
getCaptionTracks()409     public List<AtscCaptionTrack> getCaptionTracks() {
410         return Collections.unmodifiableList(Arrays.asList(mProto.captionTracks));
411     }
412 
setCaptionTracks(List<AtscCaptionTrack> captionTracks)413     synchronized public void setCaptionTracks(List<AtscCaptionTrack> captionTracks) {
414         mProto.captionTracks = captionTracks.toArray(new AtscCaptionTrack[captionTracks.size()]);
415     }
416 
selectAudioTrack(int index)417     synchronized public void selectAudioTrack(int index) {
418         if (0 <= index && index < mProto.audioPids.length) {
419             mProto.audioTrackIndex = index;
420         } else {
421             mProto.audioTrackIndex = -1;
422         }
423     }
424 
setRecordingProhibited(boolean recordingProhibited)425     synchronized public void setRecordingProhibited(boolean recordingProhibited) {
426         mProto.recordingProhibited = recordingProhibited;
427     }
428 
isRecordingProhibited()429     public boolean isRecordingProhibited() {
430         return mProto.recordingProhibited;
431     }
432 
setVideoFormat(String videoFormat)433     synchronized public void setVideoFormat(String videoFormat) {
434         mProto.videoFormat = videoFormat == null ? "" : videoFormat;
435     }
436 
getVideoFormat()437     public String getVideoFormat() {
438         return mProto.videoFormat;
439     }
440 
441     @Override
toString()442     public String toString() {
443         switch (mProto.type) {
444             case Channel.TYPE_FILE:
445                 return String.format("{%d-%d %s} Filepath: %s, ProgramNumber %d",
446                         mProto.virtualMajor, mProto.virtualMinor, mProto.shortName,
447                         mProto.filepath, mProto.programNumber);
448             //case Channel.TYPE_TUNER:
449             default:
450                 return String.format("{%d-%d %s} Frequency: %d, ProgramNumber %d",
451                         mProto.virtualMajor, mProto.virtualMinor, mProto.shortName,
452                         mProto.frequency, mProto.programNumber);
453         }
454     }
455 
456     @Override
compareTo(@onNull TunerChannel channel)457     public int compareTo(@NonNull TunerChannel channel) {
458         // In the same frequency, the program number acts as the sub-channel number.
459         int ret = getFrequency() - channel.getFrequency();
460         if (ret != 0) {
461             return ret;
462         }
463         ret = getProgramNumber() - channel.getProgramNumber();
464         if (ret != 0) {
465             return ret;
466         }
467         ret = StringUtils.compare(getName(), channel.getName());
468         if (ret != 0) {
469             return ret;
470         }
471         // For FileTsStreamer, file paths should be compared.
472         return StringUtils.compare(getFilepath(), channel.getFilepath());
473     }
474 
475     @Override
equals(Object o)476     public boolean equals(Object o) {
477         if (!(o instanceof TunerChannel)) {
478             return false;
479         }
480         return compareTo((TunerChannel) o) == 0;
481     }
482 
483     @Override
hashCode()484     public int hashCode() {
485         return Objects.hash(getFrequency(), getProgramNumber(), getName(), getFilepath());
486     }
487 
488     // Serialization
toByteArray()489     synchronized public byte[] toByteArray() {
490         try {
491             return MessageNano.toByteArray(mProto);
492         } catch (Exception e) {
493             // Retry toByteArray. b/34197766
494             Log.w(TAG, "TunerChannel or its variables are modified in multiple thread without lock",
495                     e);
496             return MessageNano.toByteArray(mProto);
497         }
498     }
499 
parseFrom(byte[] data)500     public static TunerChannel parseFrom(byte[] data) {
501         if (data == null) {
502             return null;
503         }
504         try {
505             return new TunerChannel(TunerChannelProto.parseFrom(data));
506         } catch (IOException e) {
507             Log.e(TAG, "Could not parse from byte array", e);
508             return null;
509         }
510     }
511 }
512