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.util.Log; 21 import com.android.tv.common.BuildConfig; 22 import com.android.tv.common.compat.TvInputConstantCompat; 23 import com.android.tv.tuner.api.Tuner; 24 import com.android.tv.common.annotation.UsedByNative; 25 import java.util.Objects; 26 27 /** A base class to handle a hardware tuner device. */ 28 public abstract class TunerHal implements Tuner { 29 private static final String TAG = "TunerHal"; 30 31 private static final int PID_PAT = 0; 32 private static final int PID_ATSC_SI_BASE = 0x1ffb; 33 private static final int PID_DVB_SDT = 0x0011; 34 private static final int PID_DVB_EIT = 0x0012; 35 private static final int DEFAULT_VSB_TUNE_TIMEOUT_MS = 2000; 36 private static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for 37 38 @DeliverySystemType private int mDeliverySystemType; 39 @DeliverySystemType private int[] mDeliverySystemTypes; 40 private boolean mIsStreaming; 41 private int mFrequency; 42 private String mModulation; 43 44 static { 45 if (!BuildConfig.NO_JNI_TEST) { 46 System.loadLibrary("tunertvinput_jni"); 47 } 48 } 49 TunerHal(Context context)50 protected TunerHal(Context context) { 51 mIsStreaming = false; 52 mFrequency = -1; 53 mModulation = null; 54 } 55 isStreaming()56 protected boolean isStreaming() { 57 return mIsStreaming; 58 } 59 getDeliverySystemTypeFromDevice()60 protected void getDeliverySystemTypeFromDevice() { 61 getDeliverySystemTypesFromDevice(); 62 } 63 getDeliverySystemTypesFromDevice()64 protected void getDeliverySystemTypesFromDevice() { 65 if (mDeliverySystemType == DELIVERY_SYSTEM_UNDEFINED) { 66 mDeliverySystemType = nativeGetDeliverySystemType(getDeviceId()); 67 } 68 if (mDeliverySystemTypes == null) { 69 mDeliverySystemTypes = nativeGetDeliverySystemTypes(getDeviceId()); 70 } 71 } 72 73 /** 74 * Returns {@code true} if this tuner HAL can be reused to save tuning time between channels of 75 * the same frequency. 76 */ 77 @Override isReusable()78 public boolean isReusable() { 79 return true; 80 } 81 82 @Override finalize()83 protected void finalize() throws Throwable { 84 super.finalize(); 85 close(); 86 } 87 nativeFinalize(long deviceId)88 protected native void nativeFinalize(long deviceId); 89 90 @Override tune( int frequency, @ModulationType String modulation, String channelNumber)91 public synchronized boolean tune( 92 int frequency, @ModulationType String modulation, 93 String channelNumber) { 94 return tuneInternal(mDeliverySystemType, frequency, modulation, channelNumber); 95 } 96 97 @Override tune( int deliverySystemType, int frequency, @ModulationType String modulation, String channelNumber)98 public synchronized boolean tune( 99 int deliverySystemType, int frequency, @ModulationType String modulation, 100 String channelNumber) { 101 return tuneInternal(deliverySystemType, frequency, modulation, channelNumber); 102 } 103 104 /** 105 * Sets the tuner channel. This should be called after acquiring a tuner device. 106 * 107 * @param deliverySystemType a system delivery type of the channel to tune to 108 * @param frequency a frequency of the channel to tune to 109 * @param modulation a modulation method of the channel to tune to 110 * @param channelNumber channel number when channel number is already known. Some tuner HAL may 111 * use channelNumber instead of frequency for tune. 112 * @return {@code true} if the operation was successful, {@code false} otherwise 113 */ tuneInternal( int deliverySystemType, int frequency, @ModulationType String modulation, String channelNumber)114 protected boolean tuneInternal( 115 int deliverySystemType, int frequency, @ModulationType String modulation, 116 String channelNumber) { 117 118 if (!isDeviceOpen()) { 119 Log.e(TAG, "There's no available device"); 120 return false; 121 } 122 if (mIsStreaming) { 123 nativeCloseAllPidFilters(getDeviceId()); 124 mIsStreaming = false; 125 } 126 if (mDeliverySystemTypes != null) { 127 int i; 128 for (i = 0; i < mDeliverySystemTypes.length; i++) { 129 if (deliverySystemType == mDeliverySystemTypes[i]) { 130 break; 131 } 132 } 133 134 if (i == mDeliverySystemTypes.length) { 135 Log.e(TAG, "Unsupported delivery system type for device"); 136 return false; 137 } 138 } 139 140 // When tuning to a new channel in the same frequency, there's no need to stop current tuner 141 // device completely and the only thing necessary for tuning is reopening pid filters. 142 if (mFrequency == frequency && Objects.equals(mModulation, modulation)) { 143 addPidFilter(PID_PAT, FILTER_TYPE_OTHER); 144 addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); 145 if (Tuner.isDvbDeliverySystem(deliverySystemType)) { 146 addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER); 147 addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER); 148 } 149 mIsStreaming = true; 150 return true; 151 } 152 153 int timeout_ms = 154 modulation.equals(MODULATION_8VSB) 155 ? DEFAULT_VSB_TUNE_TIMEOUT_MS 156 : DEFAULT_QAM_TUNE_TIMEOUT_MS; 157 158 boolean tuneStatus; 159 switch(deliverySystemType) { 160 case DELIVERY_SYSTEM_UNDEFINED: 161 case DELIVERY_SYSTEM_ATSC: 162 tuneStatus = nativeTune(getDeviceId(), frequency, modulation, timeout_ms); 163 break; 164 case DELIVERY_SYSTEM_DVBT: 165 case DELIVERY_SYSTEM_DVBT2: 166 tuneStatus = nativeTune(getDeviceId(), deliverySystemType, frequency, modulation, 167 timeout_ms); 168 break; 169 default: 170 Log.e(TAG, "Unsupported delivery system type for device"); 171 return false; 172 } 173 174 if (tuneStatus == true) { 175 addPidFilter(PID_PAT, FILTER_TYPE_OTHER); 176 addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); 177 if (Tuner.isDvbDeliverySystem(deliverySystemType)) { 178 addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER); 179 addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER); 180 } 181 mFrequency = frequency; 182 mModulation = modulation; 183 mIsStreaming = true; 184 } 185 186 return tuneStatus; 187 } 188 nativeTune( long deviceId, int frequency, @ModulationType String modulation, int timeout_ms)189 protected native boolean nativeTune( 190 long deviceId, int frequency, 191 @ModulationType String modulation, int timeout_ms); 192 nativeTune( long deviceId, int deliverySystemType, int frequency, @ModulationType String modulation, int timeout_ms)193 protected native boolean nativeTune( 194 long deviceId, int deliverySystemType, int frequency, 195 @ModulationType String modulation, int timeout_ms); 196 197 /** 198 * Sets a pid filter. This should be set after setting a channel. 199 * 200 * @param pid a pid number to be added to filter list 201 * @param filterType a type of pid. Must be one of (FILTER_TYPE_XXX) 202 * @return {@code true} if the operation was successful, {@code false} otherwise 203 */ 204 @Override addPidFilter(int pid, @FilterType int filterType)205 public synchronized boolean addPidFilter(int pid, @FilterType int filterType) { 206 if (!isDeviceOpen()) { 207 Log.e(TAG, "There's no available device"); 208 return false; 209 } 210 if (pid >= 0 && pid <= 0x1fff) { 211 nativeAddPidFilter(getDeviceId(), pid, filterType); 212 return true; 213 } 214 return false; 215 } 216 nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType)217 protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType); 218 nativeCloseAllPidFilters(long deviceId)219 protected native void nativeCloseAllPidFilters(long deviceId); 220 nativeSetHasPendingTune(long deviceId, boolean hasPendingTune)221 protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune); 222 nativeGetDeliverySystemType(long deviceId)223 protected native int nativeGetDeliverySystemType(long deviceId); 224 nativeGetDeliverySystemTypes(long deviceId)225 protected native int[] nativeGetDeliverySystemTypes(long deviceId); 226 nativeGetSignalStrength(long deviceId)227 protected native int nativeGetSignalStrength(long deviceId); 228 229 /** 230 * Stops current tuning. The tuner device and pid filters will be reset by this call and make 231 * the tuner ready to accept another tune request. 232 */ 233 @Override stopTune()234 public synchronized void stopTune() { 235 if (isDeviceOpen()) { 236 if (mIsStreaming) { 237 nativeCloseAllPidFilters(getDeviceId()); 238 } 239 nativeStopTune(getDeviceId()); 240 } 241 mIsStreaming = false; 242 mFrequency = -1; 243 mModulation = null; 244 } 245 246 @Override setHasPendingTune(boolean hasPendingTune)247 public void setHasPendingTune(boolean hasPendingTune) { 248 nativeSetHasPendingTune(getDeviceId(), hasPendingTune); 249 } 250 251 @Override getDeliverySystemType()252 public int getDeliverySystemType() { 253 return mDeliverySystemType; 254 } 255 256 @Override getDeliverySystemTypes()257 public int[] getDeliverySystemTypes() { 258 return mDeliverySystemTypes; 259 } 260 nativeStopTune(long deviceId)261 protected native void nativeStopTune(long deviceId); 262 263 /** 264 * This method must be called after {@link #tune(int, String, String)} and before {@link 265 * #stopTune()}. Writes at most maxSize TS frames in a buffer provided by the user. The frames 266 * employ MPEG encoding. 267 * 268 * @param javaBuffer a buffer to write the video data in 269 * @param javaBufferSize the max amount of bytes to write in this buffer. Usually this number 270 * should be equal to the length of the buffer. 271 * @return the amount of bytes written in the buffer. Note that this value could be 0 if no new 272 * frames have been obtained since the last call. 273 */ 274 @Override readTsStream(byte[] javaBuffer, int javaBufferSize)275 public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) { 276 if (isDeviceOpen()) { 277 return nativeWriteInBuffer(getDeviceId(), javaBuffer, javaBufferSize); 278 } else { 279 return 0; 280 } 281 } 282 283 /** 284 * This method gets signal strength for currently tuned channel. 285 * Each specific tuner should implement its own method. 286 * 287 * @return {@link TvInputConstantCompat#SIGNAL_STRENGTH_NOT_USED 288 * when signal check is not supported from tuner. 289 * {@link TvInputConstantCompat#SIGNAL_STRENGTH_ERROR} 290 * when signal returned is not valid. 291 * 0 - 100 representing strength from low to high. Curve raw data if necessary. 292 */ 293 @Override getSignalStrength()294 public int getSignalStrength() { 295 return TvInputConstantCompat.SIGNAL_STRENGTH_NOT_USED; 296 } 297 nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize)298 protected native int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize); 299 300 /** 301 * Opens Linux DVB frontend device. This method is called from native JNI and used only for 302 * DvbTunerHal. 303 */ 304 @UsedByNative("DvbManager.cpp") openDvbFrontEndFd()305 protected int openDvbFrontEndFd() { 306 return -1; 307 } 308 309 /** 310 * Opens Linux DVB demux device. This method is called from native JNI and used only for 311 * DvbTunerHal. 312 */ 313 @UsedByNative("DvbManager.cpp") openDvbDemuxFd()314 protected int openDvbDemuxFd() { 315 return -1; 316 } 317 318 /** 319 * Opens Linux DVB dvr device. This method is called from native JNI and used only for 320 * DvbTunerHal. 321 */ 322 @UsedByNative("DvbManager.cpp") openDvbDvrFd()323 protected int openDvbDvrFd() { 324 return -1; 325 } 326 } 327