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