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