Home | History | Annotate | Download | only in tuner
      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;
     18 
     19 import android.content.Context;
     20 import android.support.annotation.IntDef;
     21 import android.support.annotation.StringDef;
     22 import android.support.annotation.WorkerThread;
     23 import android.util.Log;
     24 import android.util.Pair;
     25 
     26 import com.android.tv.Features;
     27 import com.android.tv.customization.TvCustomizationManager;
     28 
     29 import java.lang.annotation.Retention;
     30 import java.lang.annotation.RetentionPolicy;
     31 import java.util.Objects;
     32 
     33 /**
     34  * A base class to handle a hardware tuner device.
     35  */
     36 public abstract class TunerHal implements AutoCloseable {
     37     protected static final String TAG = "TunerHal";
     38     protected static final boolean DEBUG = false;
     39 
     40     @IntDef({ FILTER_TYPE_OTHER, FILTER_TYPE_AUDIO, FILTER_TYPE_VIDEO, FILTER_TYPE_PCR })
     41     @Retention(RetentionPolicy.SOURCE)
     42     public @interface FilterType {}
     43     public static final int FILTER_TYPE_OTHER = 0;
     44     public static final int FILTER_TYPE_AUDIO = 1;
     45     public static final int FILTER_TYPE_VIDEO = 2;
     46     public static final int FILTER_TYPE_PCR = 3;
     47 
     48     @StringDef({ MODULATION_8VSB, MODULATION_QAM256 })
     49     @Retention(RetentionPolicy.SOURCE)
     50     public @interface ModulationType {}
     51     public static final String MODULATION_8VSB = "8VSB";
     52     public static final String MODULATION_QAM256 = "QAM256";
     53 
     54     @IntDef({ DELIVERY_SYSTEM_UNDEFINED, DELIVERY_SYSTEM_ATSC, DELIVERY_SYSTEM_DVBC,
     55             DELIVERY_SYSTEM_DVBS, DELIVERY_SYSTEM_DVBS2, DELIVERY_SYSTEM_DVBT,
     56             DELIVERY_SYSTEM_DVBT2 })
     57     @Retention(RetentionPolicy.SOURCE)
     58     public @interface DeliverySystemType {}
     59     public static final int DELIVERY_SYSTEM_UNDEFINED = 0;
     60     public static final int DELIVERY_SYSTEM_ATSC = 1;
     61     public static final int DELIVERY_SYSTEM_DVBC = 2;
     62     public static final int DELIVERY_SYSTEM_DVBS = 3;
     63     public static final int DELIVERY_SYSTEM_DVBS2 = 4;
     64     public static final int DELIVERY_SYSTEM_DVBT = 5;
     65     public static final int DELIVERY_SYSTEM_DVBT2 = 6;
     66 
     67     @IntDef({ TUNER_TYPE_BUILT_IN, TUNER_TYPE_USB, TUNER_TYPE_NETWORK })
     68     @Retention(RetentionPolicy.SOURCE)
     69     public @interface TunerType {}
     70     public static final int TUNER_TYPE_BUILT_IN = 1;
     71     public static final int TUNER_TYPE_USB = 2;
     72     public static final int TUNER_TYPE_NETWORK = 3;
     73 
     74     protected static final int PID_PAT = 0;
     75     protected static final int PID_ATSC_SI_BASE = 0x1ffb;
     76     protected static final int PID_DVB_SDT = 0x0011;
     77     protected static final int PID_DVB_EIT = 0x0012;
     78     protected static final int DEFAULT_VSB_TUNE_TIMEOUT_MS = 2000;
     79     protected static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for
     80                                                                    // QAM256 tuning.
     81     @IntDef({
     82             BUILT_IN_TUNER_TYPE_LINUX_DVB
     83     })
     84     @Retention(RetentionPolicy.SOURCE)
     85     private @interface BuiltInTunerType {}
     86     private static final int BUILT_IN_TUNER_TYPE_LINUX_DVB = 1;
     87 
     88     private static Integer sBuiltInTunerType;
     89 
     90     protected @DeliverySystemType int mDeliverySystemType;
     91     private boolean mIsStreaming;
     92     private int mFrequency;
     93     private String mModulation;
     94 
     95     static {
     96         System.loadLibrary("tunertvinput_jni");
     97     }
     98 
     99     /**
    100      * Creates a TunerHal instance.
    101      * @param context context for creating the TunerHal instance
    102      * @return the TunerHal instance
    103      */
    104     @WorkerThread
    105     public synchronized static TunerHal createInstance(Context context) {
    106         TunerHal tunerHal = null;
    107         if (DvbTunerHal.getNumberOfDevices(context) > 0) {
    108             if (DEBUG) Log.d(TAG, "Use DvbTunerHal");
    109             tunerHal = new DvbTunerHal(context);
    110         }
    111         return tunerHal != null && tunerHal.openFirstAvailable() ? tunerHal : null;
    112     }
    113 
    114     /**
    115      * Gets the number of tuner devices currently present.
    116      */
    117     @WorkerThread
    118     public static Pair<Integer, Integer> getTunerTypeAndCount(Context context) {
    119         if (useBuiltInTuner(context)) {
    120             if (getBuiltInTunerType(context) == BUILT_IN_TUNER_TYPE_LINUX_DVB) {
    121                 return new Pair<>(TUNER_TYPE_BUILT_IN, DvbTunerHal.getNumberOfDevices(context));
    122             }
    123         } else {
    124             int usbTunerCount = DvbTunerHal.getNumberOfDevices(context);
    125             if (usbTunerCount > 0) {
    126                 return new Pair<>(TUNER_TYPE_USB, usbTunerCount);
    127             }
    128         }
    129         return new Pair<>(null, 0);
    130     }
    131 
    132     /**
    133      * Check a delivery system is for DVB or not.
    134      */
    135     public static boolean isDvbDeliverySystem(@DeliverySystemType int deliverySystemType) {
    136         return deliverySystemType == DELIVERY_SYSTEM_DVBC
    137                 || deliverySystemType == DELIVERY_SYSTEM_DVBS
    138                 || deliverySystemType == DELIVERY_SYSTEM_DVBS2
    139                 || deliverySystemType == DELIVERY_SYSTEM_DVBT
    140                 || deliverySystemType == DELIVERY_SYSTEM_DVBT2;
    141     }
    142 
    143     /**
    144      * Returns if tuner input service would use built-in tuners instead of USB tuners or network
    145      * tuners.
    146      */
    147     static boolean useBuiltInTuner(Context context) {
    148         return getBuiltInTunerType(context) != 0;
    149     }
    150 
    151     private static @BuiltInTunerType int getBuiltInTunerType(Context context) {
    152         if (sBuiltInTunerType == null) {
    153             sBuiltInTunerType = 0;
    154             if (TvCustomizationManager.hasLinuxDvbBuiltInTuner(context)
    155                     && DvbTunerHal.getNumberOfDevices(context) > 0) {
    156                 sBuiltInTunerType = BUILT_IN_TUNER_TYPE_LINUX_DVB;
    157             }
    158         }
    159         return sBuiltInTunerType;
    160     }
    161 
    162     protected TunerHal(Context context) {
    163         mIsStreaming = false;
    164         mFrequency = -1;
    165         mModulation = null;
    166     }
    167 
    168     protected boolean isStreaming() {
    169         return mIsStreaming;
    170     }
    171 
    172     protected void getDeliverySystemTypeFromDevice() {
    173         if (mDeliverySystemType == DELIVERY_SYSTEM_UNDEFINED) {
    174             mDeliverySystemType = nativeGetDeliverySystemType(getDeviceId());
    175         }
    176     }
    177 
    178     /**
    179      * Returns {@code true} if this tuner HAL can be reused to save tuning time between channels
    180      * of the same frequency.
    181      */
    182     public boolean isReusable() {
    183         return true;
    184     }
    185 
    186     @Override
    187     protected void finalize() throws Throwable {
    188         super.finalize();
    189         close();
    190     }
    191 
    192     protected native void nativeFinalize(long deviceId);
    193 
    194     /**
    195      * Acquires the first available tuner device. If there is a tuner device that is available, the
    196      * tuner device will be locked to the current instance.
    197      *
    198      * @return {@code true} if the operation was successful, {@code false} otherwise
    199      */
    200     protected abstract boolean openFirstAvailable();
    201 
    202     protected abstract boolean isDeviceOpen();
    203 
    204     protected abstract long getDeviceId();
    205 
    206     /**
    207      * Sets the tuner channel. This should be called after acquiring a tuner device.
    208      *
    209      * @param frequency a frequency of the channel to tune to
    210      * @param modulation a modulation method of the channel to tune to
    211      * @param channelNumber channel number when channel number is already known. Some tuner HAL
    212      *        may use channelNumber instead of frequency for tune.
    213      * @return {@code true} if the operation was successful, {@code false} otherwise
    214      */
    215     public synchronized boolean tune(int frequency, @ModulationType String modulation,
    216             String channelNumber) {
    217         if (!isDeviceOpen()) {
    218             Log.e(TAG, "There's no available device");
    219             return false;
    220         }
    221         if (mIsStreaming) {
    222             nativeCloseAllPidFilters(getDeviceId());
    223             mIsStreaming = false;
    224         }
    225 
    226         // When tuning to a new channel in the same frequency, there's no need to stop current tuner
    227         // device completely and the only thing necessary for tuning is reopening pid filters.
    228         if (mFrequency == frequency && Objects.equals(mModulation, modulation)) {
    229             addPidFilter(PID_PAT, FILTER_TYPE_OTHER);
    230             addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER);
    231             if (isDvbDeliverySystem(mDeliverySystemType)) {
    232                 addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER);
    233                 addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER);
    234             }
    235             mIsStreaming = true;
    236             return true;
    237         }
    238         int timeout_ms = modulation.equals(MODULATION_8VSB) ? DEFAULT_VSB_TUNE_TIMEOUT_MS
    239                 : DEFAULT_QAM_TUNE_TIMEOUT_MS;
    240         if (nativeTune(getDeviceId(), frequency, modulation, timeout_ms)) {
    241             addPidFilter(PID_PAT, FILTER_TYPE_OTHER);
    242             addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER);
    243             if (isDvbDeliverySystem(mDeliverySystemType)) {
    244                 addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER);
    245                 addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER);
    246             }
    247             mFrequency = frequency;
    248             mModulation = modulation;
    249             mIsStreaming = true;
    250             return true;
    251         }
    252         return false;
    253     }
    254 
    255     protected native boolean nativeTune(long deviceId, int frequency,
    256             @ModulationType String modulation, int timeout_ms);
    257 
    258     /**
    259      * Sets a pid filter. This should be set after setting a channel.
    260      *
    261      * @param pid a pid number to be added to filter list
    262      * @param filterType a type of pid. Must be one of (FILTER_TYPE_XXX)
    263      * @return {@code true} if the operation was successful, {@code false} otherwise
    264      */
    265     public synchronized boolean addPidFilter(int pid, @FilterType int filterType) {
    266         if (!isDeviceOpen()) {
    267             Log.e(TAG, "There's no available device");
    268             return false;
    269         }
    270         if (pid >= 0 && pid <= 0x1fff) {
    271             nativeAddPidFilter(getDeviceId(), pid, filterType);
    272             return true;
    273         }
    274         return false;
    275     }
    276 
    277     protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType);
    278     protected native void nativeCloseAllPidFilters(long deviceId);
    279     protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune);
    280     protected native int nativeGetDeliverySystemType(long deviceId);
    281 
    282     /**
    283      * Stops current tuning. The tuner device and pid filters will be reset by this call and make
    284      * the tuner ready to accept another tune request.
    285      */
    286     public synchronized void stopTune() {
    287         if (isDeviceOpen()) {
    288             if (mIsStreaming) {
    289                 nativeCloseAllPidFilters(getDeviceId());
    290             }
    291             nativeStopTune(getDeviceId());
    292         }
    293         mIsStreaming = false;
    294         mFrequency = -1;
    295         mModulation = null;
    296     }
    297 
    298     public void setHasPendingTune(boolean hasPendingTune) {
    299         nativeSetHasPendingTune(getDeviceId(), hasPendingTune);
    300     }
    301 
    302     public int getDeliverySystemType() {
    303         return mDeliverySystemType;
    304     }
    305 
    306     protected native void nativeStopTune(long deviceId);
    307 
    308     /**
    309      * This method must be called after {@link TunerHal#tune} and before
    310      * {@link TunerHal#stopTune}. Writes at most maxSize TS frames in a buffer
    311      * provided by the user. The frames employ MPEG encoding.
    312      *
    313      * @param javaBuffer a buffer to write the video data in
    314      * @param javaBufferSize the max amount of bytes to write in this buffer. Usually this number
    315      *            should be equal to the length of the buffer.
    316      * @return the amount of bytes written in the buffer. Note that this value could be 0 if no new
    317      *         frames have been obtained since the last call.
    318      */
    319     public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) {
    320         if (isDeviceOpen()) {
    321             return nativeWriteInBuffer(getDeviceId(), javaBuffer, javaBufferSize);
    322         } else {
    323             return 0;
    324         }
    325     }
    326 
    327     protected native int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize);
    328 
    329     /**
    330      * Opens Linux DVB frontend device. This method is called from native JNI and used only for
    331      * DvbTunerHal.
    332      */
    333     protected int openDvbFrontEndFd() {
    334         return -1;
    335     }
    336 
    337     /**
    338      * Opens Linux DVB demux device. This method is called from native JNI and used only for
    339      * DvbTunerHal.
    340      */
    341     protected int openDvbDemuxFd() {
    342         return -1;
    343     }
    344 
    345     /**
    346      * Opens Linux DVB dvr device. This method is called from native JNI and used only for
    347      * DvbTunerHal.
    348      */
    349     protected int openDvbDvrFd() {
    350         return -1;
    351     }
    352 }
    353