Home | History | Annotate | Download | only in data
      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 
     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 
    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 
    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      */
    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      */
    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      */
    176     public TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) {
    177         this(0, Channel.TYPE_TUNER, channel, pmtItems);
    178     }
    179 
    180     private TunerChannel(TunerChannelProto tunerChannelProto) {
    181         mProto = tunerChannelProto;
    182     }
    183 
    184     public static TunerChannel forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) {
    185         return new TunerChannel(channel, 0, pmtItems, Channel.TYPE_FILE);
    186     }
    187 
    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      */
    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 
    232     public String getName() {
    233         return (!mProto.shortName.isEmpty()) ? mProto.shortName : mProto.longName;
    234     }
    235 
    236     public String getShortName() {
    237         return mProto.shortName;
    238     }
    239 
    240     public int getProgramNumber() {
    241         return mProto.programNumber;
    242     }
    243 
    244     public int getServiceType() {
    245         return mProto.serviceType;
    246     }
    247 
    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 
    256     public int getVirtualMajor() {
    257         return mProto.virtualMajor;
    258     }
    259 
    260     public int getVirtualMinor() {
    261         return mProto.virtualMinor;
    262     }
    263 
    264     public int getFrequency() {
    265         return mProto.frequency;
    266     }
    267 
    268     public String getModulation() {
    269         return mProto.modulation;
    270     }
    271 
    272     public int getTsid() {
    273         return mProto.tsid;
    274     }
    275 
    276     public int getVideoPid() {
    277         return mProto.videoPid;
    278     }
    279 
    280     synchronized public void setVideoPid(int videoPid) {
    281         mProto.videoPid = videoPid;
    282     }
    283 
    284     public int getVideoStreamType() {
    285         return mProto.videoStreamType;
    286     }
    287 
    288     public int getAudioPid() {
    289         if (mProto.audioTrackIndex == -1) {
    290             return INVALID_PID;
    291         }
    292         return mProto.audioPids[mProto.audioTrackIndex];
    293     }
    294 
    295     public int getAudioStreamType() {
    296         if (mProto.audioTrackIndex == -1) {
    297             return INVALID_STREAMTYPE;
    298         }
    299         return mProto.audioStreamTypes[mProto.audioTrackIndex];
    300     }
    301 
    302     public List<Integer> getAudioPids() {
    303         return Ints.asList(mProto.audioPids);
    304     }
    305 
    306     synchronized public void setAudioPids(List<Integer> audioPids) {
    307         mProto.audioPids = Ints.toArray(audioPids);
    308     }
    309 
    310     public List<Integer> getAudioStreamTypes() {
    311         return Ints.asList(mProto.audioStreamTypes);
    312     }
    313 
    314     synchronized public void setAudioStreamTypes(List<Integer> audioStreamTypes) {
    315         mProto.audioStreamTypes = Ints.toArray(audioStreamTypes);
    316     }
    317 
    318     public int getPcrPid() {
    319         return mProto.pcrPid;
    320     }
    321 
    322     public int getType() {
    323         return mProto.type;
    324     }
    325 
    326     synchronized public void setFilepath(String filepath) {
    327         mProto.filepath = filepath == null ? "" : filepath;
    328     }
    329 
    330     public String getFilepath() {
    331         return mProto.filepath;
    332     }
    333 
    334     synchronized public void setVirtualMajor(int virtualMajor) {
    335         mProto.virtualMajor = virtualMajor;
    336     }
    337 
    338     synchronized public void setVirtualMinor(int virtualMinor) {
    339         mProto.virtualMinor = virtualMinor;
    340     }
    341 
    342     synchronized public void setShortName(String shortName) {
    343         mProto.shortName = shortName == null ? "" : shortName;
    344     }
    345 
    346     synchronized public void setFrequency(int frequency) {
    347         mProto.frequency = frequency;
    348     }
    349 
    350     synchronized public void setModulation(String modulation) {
    351         mProto.modulation = modulation == null ? "" : modulation;
    352     }
    353 
    354     public boolean hasVideo() {
    355         return mProto.videoPid != INVALID_PID;
    356     }
    357 
    358     public boolean hasAudio() {
    359         return getAudioPid() != INVALID_PID;
    360     }
    361 
    362     public long getChannelId() {
    363         return mProto.channelId;
    364     }
    365 
    366     synchronized public void setChannelId(long channelId) {
    367         mProto.channelId = channelId;
    368     }
    369 
    370     public String getDisplayNumber() {
    371         return getDisplayNumber(true);
    372     }
    373 
    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 
    385     public String getDescription() {
    386         return mProto.description;
    387     }
    388 
    389     @Override
    390     synchronized public void setHasCaptionTrack() {
    391         mProto.hasCaptionTrack = true;
    392     }
    393 
    394     @Override
    395     public boolean hasCaptionTrack() {
    396         return mProto.hasCaptionTrack;
    397     }
    398 
    399     @Override
    400     public List<AtscAudioTrack> getAudioTracks() {
    401         return Collections.unmodifiableList(Arrays.asList(mProto.audioTracks));
    402     }
    403 
    404     synchronized public void setAudioTracks(List<AtscAudioTrack> audioTracks) {
    405         mProto.audioTracks = audioTracks.toArray(new AtscAudioTrack[audioTracks.size()]);
    406     }
    407 
    408     @Override
    409     public List<AtscCaptionTrack> getCaptionTracks() {
    410         return Collections.unmodifiableList(Arrays.asList(mProto.captionTracks));
    411     }
    412 
    413     synchronized public void setCaptionTracks(List<AtscCaptionTrack> captionTracks) {
    414         mProto.captionTracks = captionTracks.toArray(new AtscCaptionTrack[captionTracks.size()]);
    415     }
    416 
    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 
    425     synchronized public void setRecordingProhibited(boolean recordingProhibited) {
    426         mProto.recordingProhibited = recordingProhibited;
    427     }
    428 
    429     public boolean isRecordingProhibited() {
    430         return mProto.recordingProhibited;
    431     }
    432 
    433     synchronized public void setVideoFormat(String videoFormat) {
    434         mProto.videoFormat = videoFormat == null ? "" : videoFormat;
    435     }
    436 
    437     public String getVideoFormat() {
    438         return mProto.videoFormat;
    439     }
    440 
    441     @Override
    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
    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
    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
    484     public int hashCode() {
    485         return Objects.hash(getFrequency(), getProgramNumber(), getName(), getFilepath());
    486     }
    487 
    488     // Serialization
    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 
    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