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