• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
createInstance(Context context)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
getTunerTypeAndCount(Context context)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      */
isDvbDeliverySystem(@eliverySystemType int deliverySystemType)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      */
useBuiltInTuner(Context context)147     static boolean useBuiltInTuner(Context context) {
148         return getBuiltInTunerType(context) != 0;
149     }
150 
getBuiltInTunerType(Context context)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 
TunerHal(Context context)162     protected TunerHal(Context context) {
163         mIsStreaming = false;
164         mFrequency = -1;
165         mModulation = null;
166     }
167 
isStreaming()168     protected boolean isStreaming() {
169         return mIsStreaming;
170     }
171 
getDeliverySystemTypeFromDevice()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      */
isReusable()182     public boolean isReusable() {
183         return true;
184     }
185 
186     @Override
finalize()187     protected void finalize() throws Throwable {
188         super.finalize();
189         close();
190     }
191 
nativeFinalize(long deviceId)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      */
openFirstAvailable()200     protected abstract boolean openFirstAvailable();
201 
isDeviceOpen()202     protected abstract boolean isDeviceOpen();
203 
getDeviceId()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      */
tune(int frequency, @ModulationType String modulation, String channelNumber)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 
nativeTune(long deviceId, int frequency, @ModulationType String modulation, int timeout_ms)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      */
addPidFilter(int pid, @FilterType int filterType)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 
nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType)277     protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType);
nativeCloseAllPidFilters(long deviceId)278     protected native void nativeCloseAllPidFilters(long deviceId);
nativeSetHasPendingTune(long deviceId, boolean hasPendingTune)279     protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune);
nativeGetDeliverySystemType(long deviceId)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      */
stopTune()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 
setHasPendingTune(boolean hasPendingTune)298     public void setHasPendingTune(boolean hasPendingTune) {
299         nativeSetHasPendingTune(getDeviceId(), hasPendingTune);
300     }
301 
getDeliverySystemType()302     public int getDeliverySystemType() {
303         return mDeliverySystemType;
304     }
305 
nativeStopTune(long deviceId)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      */
readTsStream(byte[] javaBuffer, int javaBufferSize)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 
nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize)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      */
openDvbFrontEndFd()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      */
openDvbDemuxFd()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      */
openDvbDvrFd()349     protected int openDvbDvrFd() {
350         return -1;
351     }
352 }
353