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.util.Log; 23 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.util.Objects; 27 28 /** 29 * A base class to handle a hardware tuner device. 30 */ 31 public abstract class TunerHal implements AutoCloseable { 32 protected static final String TAG = "TunerHal"; 33 protected static final boolean DEBUG = false; 34 35 @IntDef({ FILTER_TYPE_OTHER, FILTER_TYPE_AUDIO, FILTER_TYPE_VIDEO, FILTER_TYPE_PCR }) 36 @Retention(RetentionPolicy.SOURCE) 37 public @interface FilterType {} 38 public static final int FILTER_TYPE_OTHER = 0; 39 public static final int FILTER_TYPE_AUDIO = 1; 40 public static final int FILTER_TYPE_VIDEO = 2; 41 public static final int FILTER_TYPE_PCR = 3; 42 43 @StringDef({ MODULATION_8VSB, MODULATION_QAM256 }) 44 @Retention(RetentionPolicy.SOURCE) 45 public @interface ModulationType {} 46 public static final String MODULATION_8VSB = "8VSB"; 47 public static final String MODULATION_QAM256 = "QAM256"; 48 49 public static final int TUNER_TYPE_BUILT_IN = 1; 50 public static final int TUNER_TYPE_USB = 2; 51 52 protected static final int PID_PAT = 0; 53 protected static final int PID_ATSC_SI_BASE = 0x1ffb; 54 protected static final int DEFAULT_VSB_TUNE_TIMEOUT_MS = 2000; 55 protected static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for 56 // QAM256 tuning. 57 private boolean mIsStreaming; 58 private int mFrequency; 59 private String mModulation; 60 61 static { 62 System.loadLibrary("tunertvinput_jni"); 63 } 64 65 /** 66 * Creates a TunerHal instance. 67 * @param context context for creating the TunerHal instance 68 * @return the TunerHal instance 69 */ createInstance(Context context)70 public synchronized static TunerHal createInstance(Context context) { 71 TunerHal tunerHal = null; 72 if (getTunerType(context) == TUNER_TYPE_BUILT_IN) { 73 } 74 if (tunerHal == null) { 75 tunerHal = new UsbTunerHal(context); 76 } 77 if (tunerHal.openFirstAvailable()) { 78 return tunerHal; 79 } 80 return null; 81 } 82 83 /** 84 * Gets the number of tuner devices currently present. 85 */ getTunerCount(Context context)86 public static int getTunerCount(Context context) { 87 if (getTunerType(context) == TUNER_TYPE_BUILT_IN) { 88 } 89 return UsbTunerHal.getNumberOfDevices(context); 90 } 91 92 /** 93 * Gets the type of tuner devices currently used. 94 */ getTunerType(Context context)95 public static int getTunerType(Context context) { 96 return TUNER_TYPE_USB; 97 } 98 TunerHal(Context context)99 protected TunerHal(Context context) { 100 mIsStreaming = false; 101 mFrequency = -1; 102 mModulation = null; 103 } 104 isStreaming()105 protected boolean isStreaming() { 106 return mIsStreaming; 107 } 108 109 @Override finalize()110 protected void finalize() throws Throwable { 111 super.finalize(); 112 close(); 113 } 114 nativeFinalize(long deviceId)115 protected native void nativeFinalize(long deviceId); 116 117 /** 118 * Acquires the first available tuner device. If there is a tuner device that is available, the 119 * tuner device will be locked to the current instance. 120 * 121 * @return {@code true} if the operation was successful, {@code false} otherwise 122 */ openFirstAvailable()123 protected abstract boolean openFirstAvailable(); 124 isDeviceOpen()125 protected abstract boolean isDeviceOpen(); 126 getDeviceId()127 protected abstract long getDeviceId(); 128 129 /** 130 * Sets the tuner channel. This should be called after acquiring a tuner device. 131 * 132 * @param frequency a frequency of the channel to tune to 133 * @param modulation a modulation method of the channel to tune to 134 * @return {@code true} if the operation was successful, {@code false} otherwise 135 */ tune(int frequency, @ModulationType String modulation)136 public synchronized boolean tune(int frequency, @ModulationType String modulation) { 137 if (!isDeviceOpen()) { 138 Log.e(TAG, "There's no available device"); 139 return false; 140 } 141 if (mIsStreaming) { 142 nativeCloseAllPidFilters(getDeviceId()); 143 mIsStreaming = false; 144 } 145 146 // When tuning to a new channel in the same frequency, there's no need to stop current tuner 147 // device completely and the only thing necessary for tuning is reopening pid filters. 148 if (mFrequency == frequency && Objects.equals(mModulation, modulation)) { 149 addPidFilter(PID_PAT, FILTER_TYPE_OTHER); 150 addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); 151 mIsStreaming = true; 152 return true; 153 } 154 int timeout_ms = modulation.equals(MODULATION_8VSB) ? DEFAULT_VSB_TUNE_TIMEOUT_MS 155 : DEFAULT_QAM_TUNE_TIMEOUT_MS; 156 if (nativeTune(getDeviceId(), frequency, modulation, timeout_ms)) { 157 addPidFilter(PID_PAT, FILTER_TYPE_OTHER); 158 addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); 159 mFrequency = frequency; 160 mModulation = modulation; 161 mIsStreaming = true; 162 return true; 163 } 164 return false; 165 } 166 nativeTune(long deviceId, int frequency, @ModulationType String modulation, int timeout_ms)167 protected native boolean nativeTune(long deviceId, int frequency, 168 @ModulationType String modulation, int timeout_ms); 169 170 /** 171 * Sets a pid filter. This should be set after setting a channel. 172 * 173 * @param pid a pid number to be added to filter list 174 * @param filterType a type of pid. Must be one of (FILTER_TYPE_XXX) 175 * @return {@code true} if the operation was successful, {@code false} otherwise 176 */ addPidFilter(int pid, @FilterType int filterType)177 public synchronized boolean addPidFilter(int pid, @FilterType int filterType) { 178 if (!isDeviceOpen()) { 179 Log.e(TAG, "There's no available device"); 180 return false; 181 } 182 if (pid >= 0 && pid <= 0x1fff) { 183 nativeAddPidFilter(getDeviceId(), pid, filterType); 184 return true; 185 } 186 return false; 187 } 188 nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType)189 protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType); nativeCloseAllPidFilters(long deviceId)190 protected native void nativeCloseAllPidFilters(long deviceId); nativeSetHasPendingTune(long deviceId, boolean hasPendingTune)191 protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune); 192 193 /** 194 * Stops current tuning. The tuner device and pid filters will be reset by this call and make 195 * the tuner ready to accept another tune request. 196 */ stopTune()197 public synchronized void stopTune() { 198 if (isDeviceOpen()) { 199 if (mIsStreaming) { 200 nativeCloseAllPidFilters(getDeviceId()); 201 } 202 nativeStopTune(getDeviceId()); 203 } 204 mIsStreaming = false; 205 mFrequency = -1; 206 mModulation = null; 207 } 208 setHasPendingTune(boolean hasPendingTune)209 public void setHasPendingTune(boolean hasPendingTune) { 210 nativeSetHasPendingTune(getDeviceId(), hasPendingTune); 211 } 212 nativeStopTune(long deviceId)213 protected native void nativeStopTune(long deviceId); 214 215 /** 216 * This method must be called after {@link TunerHal#tune} and before 217 * {@link TunerHal#stopTune}. Writes at most maxSize TS frames in a buffer 218 * provided by the user. The frames employ MPEG encoding. 219 * 220 * @param javaBuffer a buffer to write the video data in 221 * @param javaBufferSize the max amount of bytes to write in this buffer. Usually this number 222 * should be equal to the length of the buffer. 223 * @return the amount of bytes written in the buffer. Note that this value could be 0 if no new 224 * frames have been obtained since the last call. 225 */ readTsStream(byte[] javaBuffer, int javaBufferSize)226 public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) { 227 if (isDeviceOpen()) { 228 return nativeWriteInBuffer(getDeviceId(), javaBuffer, javaBufferSize); 229 } else { 230 return 0; 231 } 232 } 233 nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize)234 protected native int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize); 235 236 /** 237 * Opens Linux DVB frontend device. This method is called from native JNI and used only for 238 * UsbTunerHal. 239 */ openDvbFrontEndFd()240 protected int openDvbFrontEndFd() { 241 return -1; 242 } 243 244 /** 245 * Opens Linux DVB demux device. This method is called from native JNI and used only for 246 * UsbTunerHal. 247 */ openDvbDemuxFd()248 protected int openDvbDemuxFd() { 249 return -1; 250 } 251 252 /** 253 * Opens Linux DVB dvr device. This method is called from native JNI and used only for 254 * UsbTunerHal. 255 */ openDvbDvrFd()256 protected int openDvbDvrFd() { 257 return -1; 258 } 259 } 260