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