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.tuner.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     // See ATSC Code Points Registry.
44     private static final String[] ATSC_SERVICE_TYPE_NAMES = new String[] {
45             "ATSC Reserved",
46             "Analog television channels",
47             "ATSC_digital_television",
48             "ATSC_audio",
49             "ATSC_data_only_service",
50             "Software Download",
51             "Unassociated/Small Screen Service",
52             "Parameterized Service",
53             "ATSC NRT Service",
54             "Extended Parameterized Service" };
55     private static final String ATSC_SERVICE_TYPE_NAME_RESERVED =
56             ATSC_SERVICE_TYPE_NAMES[Channel.SERVICE_TYPE_ATSC_RESERVED];
57 
58     public static final int INVALID_FREQUENCY = -1;
59 
60     // According to RFC4259, The number of available PIDs ranges from 0 to 8191.
61     public static final int INVALID_PID = -1;
62 
63     // According to ISO13818-1, Mpeg2 StreamType has a range from 0x00 to 0xff.
64     public static final int INVALID_STREAMTYPE = -1;
65 
66     private final TunerChannelProto mProto;
67 
TunerChannel(PsipData.VctItem channel, int programNumber, List<PsiData.PmtItem> pmtItems, int type)68     private TunerChannel(PsipData.VctItem channel, int programNumber,
69             List<PsiData.PmtItem> pmtItems, int type) {
70         mProto = new TunerChannelProto();
71         if (channel == null) {
72             mProto.shortName = "";
73             mProto.tsid = 0;
74             mProto.programNumber = programNumber;
75             mProto.virtualMajor = 0;
76             mProto.virtualMinor = 0;
77         } else {
78             mProto.shortName = channel.getShortName();
79             if (channel.getLongName() != null) {
80                 mProto.longName = channel.getLongName();
81             }
82             mProto.tsid = channel.getChannelTsid();
83             mProto.programNumber = channel.getProgramNumber();
84             mProto.virtualMajor = channel.getMajorChannelNumber();
85             mProto.virtualMinor = channel.getMinorChannelNumber();
86             if (channel.getDescription() != null) {
87                 mProto.description = channel.getDescription();
88             }
89             mProto.serviceType = channel.getServiceType();
90         }
91         mProto.type = type;
92         mProto.channelId = -1L;
93         mProto.frequency = INVALID_FREQUENCY;
94         mProto.videoPid = INVALID_PID;
95         mProto.videoStreamType = INVALID_STREAMTYPE;
96         List<Integer> audioPids = new ArrayList<>();
97         List<Integer> audioStreamTypes = new ArrayList<>();
98         for (PsiData.PmtItem pmt : pmtItems) {
99             switch (pmt.getStreamType()) {
100                 // MPEG ES stream video types
101                 case Channel.MPEG1:
102                 case Channel.MPEG2:
103                 case Channel.H263:
104                 case Channel.H264:
105                 case Channel.H265:
106                     mProto.videoPid = pmt.getEsPid();
107                     mProto.videoStreamType = pmt.getStreamType();
108                     break;
109 
110                 // MPEG ES stream audio types
111                 case Channel.MPEG1AUDIO:
112                 case Channel.MPEG2AUDIO:
113                 case Channel.MPEG2AACAUDIO:
114                 case Channel.MPEG4LATMAACAUDIO:
115                 case Channel.A52AC3AUDIO:
116                 case Channel.EAC3AUDIO:
117                     audioPids.add(pmt.getEsPid());
118                     audioStreamTypes.add(pmt.getStreamType());
119                     break;
120 
121                 // Non MPEG ES stream types
122                 case 0x100: // PmtItem.ES_PID_PCR:
123                     mProto.pcrPid = pmt.getEsPid();
124                     break;
125             }
126         }
127         mProto.audioPids = Ints.toArray(audioPids);
128         mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
129         mProto.audioTrackIndex = (audioPids.size() > 0) ? 0 : -1;
130     }
131 
TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems)132     public TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
133         this(channel, 0, pmtItems, Channel.TYPE_TUNER);
134     }
135 
TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems)136     public TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems) {
137         this(null, programNumber, pmtItems, Channel.TYPE_TUNER);
138     }
139 
TunerChannel(TunerChannelProto tunerChannelProto)140     private TunerChannel(TunerChannelProto tunerChannelProto) {
141         mProto = tunerChannelProto;
142     }
143 
forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems)144     public static TunerChannel forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
145         return new TunerChannel(channel, 0, pmtItems, Channel.TYPE_FILE);
146     }
147 
getName()148     public String getName() {
149         return (!mProto.shortName.isEmpty()) ? mProto.shortName : mProto.longName;
150     }
151 
getShortName()152     public String getShortName() {
153         return mProto.shortName;
154     }
155 
getProgramNumber()156     public int getProgramNumber() {
157         return mProto.programNumber;
158     }
159 
getServiceType()160     public int getServiceType() {
161         return mProto.serviceType;
162     }
163 
getServiceTypeName()164     public String getServiceTypeName() {
165         int serviceType = mProto.serviceType;
166         if (serviceType >= 0 && serviceType < ATSC_SERVICE_TYPE_NAMES.length) {
167             return ATSC_SERVICE_TYPE_NAMES[serviceType];
168         }
169         return ATSC_SERVICE_TYPE_NAME_RESERVED;
170     }
171 
getVirtualMajor()172     public int getVirtualMajor() {
173         return mProto.virtualMajor;
174     }
175 
getVirtualMinor()176     public int getVirtualMinor() {
177         return mProto.virtualMinor;
178     }
179 
getFrequency()180     public int getFrequency() {
181         return mProto.frequency;
182     }
183 
getModulation()184     public String getModulation() {
185         return mProto.modulation;
186     }
187 
getTsid()188     public int getTsid() {
189         return mProto.tsid;
190     }
191 
getVideoPid()192     public int getVideoPid() {
193         return mProto.videoPid;
194     }
195 
setVideoPid(int videoPid)196     public void setVideoPid(int videoPid) {
197         mProto.videoPid = videoPid;
198     }
199 
getVideoStreamType()200     public int getVideoStreamType() {
201         return mProto.videoStreamType;
202     }
203 
getAudioPid()204     public int getAudioPid() {
205         if (mProto.audioTrackIndex == -1) {
206             return INVALID_PID;
207         }
208         return mProto.audioPids[mProto.audioTrackIndex];
209     }
210 
getAudioStreamType()211     public int getAudioStreamType() {
212         if (mProto.audioTrackIndex == -1) {
213             return INVALID_STREAMTYPE;
214         }
215         return mProto.audioStreamTypes[mProto.audioTrackIndex];
216     }
217 
getAudioPids()218     public List<Integer> getAudioPids() {
219         return Ints.asList(mProto.audioPids);
220     }
221 
setAudioPids(List<Integer> audioPids)222     public void setAudioPids(List<Integer> audioPids) {
223         mProto.audioPids = Ints.toArray(audioPids);
224     }
225 
getAudioStreamTypes()226     public List<Integer> getAudioStreamTypes() {
227         return Ints.asList(mProto.audioStreamTypes);
228     }
229 
setAudioStreamTypes(List<Integer> audioStreamTypes)230     public void setAudioStreamTypes(List<Integer> audioStreamTypes) {
231         mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
232     }
233 
getPcrPid()234     public int getPcrPid() {
235         return mProto.pcrPid;
236     }
237 
getType()238     public int getType() {
239         return mProto.type;
240     }
241 
setFilepath(String filepath)242     public void setFilepath(String filepath) {
243         mProto.filepath = filepath;
244     }
245 
getFilepath()246     public String getFilepath() {
247         return mProto.filepath;
248     }
249 
setVirtualMajor(int virtualMajor)250     public void setVirtualMajor(int virtualMajor) {
251         mProto.virtualMajor = virtualMajor;
252     }
253 
setVirtualMinor(int virtualMinor)254     public void setVirtualMinor(int virtualMinor) {
255         mProto.virtualMinor = virtualMinor;
256     }
257 
setShortName(String shortName)258     public void setShortName(String shortName) {
259         mProto.shortName = shortName;
260     }
261 
setFrequency(int frequency)262     public void setFrequency(int frequency) {
263         mProto.frequency = frequency;
264     }
265 
setModulation(String modulation)266     public void setModulation(String modulation) {
267         mProto.modulation = modulation;
268     }
269 
hasVideo()270     public boolean hasVideo() {
271         return mProto.videoPid != INVALID_PID;
272     }
273 
hasAudio()274     public boolean hasAudio() {
275         return getAudioPid() != INVALID_PID;
276     }
277 
getChannelId()278     public long getChannelId() {
279         return mProto.channelId;
280     }
281 
setChannelId(long channelId)282     public void setChannelId(long channelId) {
283         mProto.channelId = channelId;
284     }
285 
getDisplayNumber()286     public String getDisplayNumber() {
287         if (mProto.virtualMajor != 0 && mProto.virtualMinor != 0) {
288             return String.format("%d-%d", mProto.virtualMajor, mProto.virtualMinor);
289         } else if (mProto.virtualMajor != 0) {
290             return Integer.toString(mProto.virtualMajor);
291         } else {
292             return Integer.toString(mProto.programNumber);
293         }
294     }
295 
getDescription()296     public String getDescription() {
297         return mProto.description;
298     }
299 
300     @Override
setHasCaptionTrack()301     public void setHasCaptionTrack() {
302         mProto.hasCaptionTrack = true;
303     }
304 
305     @Override
hasCaptionTrack()306     public boolean hasCaptionTrack() {
307         return mProto.hasCaptionTrack;
308     }
309 
310     @Override
getAudioTracks()311     public List<AtscAudioTrack> getAudioTracks() {
312         return Collections.unmodifiableList(Arrays.asList(mProto.audioTracks));
313     }
314 
setAudioTracks(List<AtscAudioTrack> audioTracks)315     public void setAudioTracks(List<AtscAudioTrack> audioTracks) {
316         mProto.audioTracks = audioTracks.toArray(new AtscAudioTrack[audioTracks.size()]);
317     }
318 
319     @Override
getCaptionTracks()320     public List<AtscCaptionTrack> getCaptionTracks() {
321         return Collections.unmodifiableList(Arrays.asList(mProto.captionTracks));
322     }
323 
setCaptionTracks(List<AtscCaptionTrack> captionTracks)324     public void setCaptionTracks(List<AtscCaptionTrack> captionTracks) {
325         mProto.captionTracks = captionTracks.toArray(new AtscCaptionTrack[captionTracks.size()]);
326     }
327 
selectAudioTrack(int index)328     public void selectAudioTrack(int index) {
329         if (0 <= index && index < mProto.audioPids.length) {
330             mProto.audioTrackIndex = index;
331         } else {
332             mProto.audioTrackIndex = -1;
333         }
334     }
335 
336     @Override
toString()337     public String toString() {
338         switch (mProto.type) {
339             case Channel.TYPE_FILE:
340                 return String.format("{%d-%d %s} Filepath: %s, ProgramNumber %d",
341                         mProto.virtualMajor, mProto.virtualMinor, mProto.shortName,
342                         mProto.filepath, mProto.programNumber);
343             //case Channel.TYPE_TUNER:
344             default:
345                 return String.format("{%d-%d %s} Frequency: %d, ProgramNumber %d",
346                         mProto.virtualMajor, mProto.virtualMinor, mProto.shortName,
347                         mProto.frequency, mProto.programNumber);
348         }
349     }
350 
351     @Override
compareTo(@onNull TunerChannel channel)352     public int compareTo(@NonNull TunerChannel channel) {
353         // In the same frequency, the program number acts as the sub-channel number.
354         int ret = getFrequency() - channel.getFrequency();
355         if (ret != 0) {
356             return ret;
357         }
358         ret = getProgramNumber() - channel.getProgramNumber();
359         if (ret != 0) {
360             return ret;
361         }
362 
363         // For FileTsStreamer, file paths should be compared.
364         return StringUtils.compare(getFilepath(), channel.getFilepath());
365     }
366 
367     @Override
equals(Object o)368     public boolean equals(Object o) {
369         if (!(o instanceof TunerChannel)) {
370             return false;
371         }
372         return compareTo((TunerChannel) o) == 0;
373     }
374 
375     @Override
hashCode()376     public int hashCode() {
377         return Objects.hash(getFrequency(), getProgramNumber(), getFilepath());
378     }
379 
380     // Serialization
toByteArray()381     public byte[] toByteArray() {
382         return MessageNano.toByteArray(mProto);
383     }
384 
parseFrom(byte[] data)385     public static TunerChannel parseFrom(byte[] data) {
386         if (data == null) {
387             return null;
388         }
389         try {
390             return new TunerChannel(TunerChannelProto.parseFrom(data));
391         } catch (IOException e) {
392             Log.e(TAG, "Could not parse from byte array", e);
393             return null;
394         }
395     }
396 }
397