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