diff options
Diffstat (limited to 'tuner/src/com/android/tv/tuner/TunerHal.java')
-rw-r--r-- | tuner/src/com/android/tv/tuner/TunerHal.java | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/tuner/src/com/android/tv/tuner/TunerHal.java b/tuner/src/com/android/tv/tuner/TunerHal.java new file mode 100644 index 00000000..5801406b --- /dev/null +++ b/tuner/src/com/android/tv/tuner/TunerHal.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tv.tuner; + +import android.content.Context; +import android.support.annotation.IntDef; +import android.support.annotation.StringDef; +import android.support.annotation.WorkerThread; +import android.util.Log; +import android.util.Pair; +import com.android.tv.common.BuildConfig; +import com.android.tv.common.customization.CustomizationManager; + + +import com.android.tv.common.annotation.UsedByNative; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** A base class to handle a hardware tuner device. */ +public abstract class TunerHal implements AutoCloseable { + protected static final String TAG = "TunerHal"; + protected static final boolean DEBUG = false; + + @IntDef({FILTER_TYPE_OTHER, FILTER_TYPE_AUDIO, FILTER_TYPE_VIDEO, FILTER_TYPE_PCR}) + @Retention(RetentionPolicy.SOURCE) + public @interface FilterType {} + + public static final int FILTER_TYPE_OTHER = 0; + public static final int FILTER_TYPE_AUDIO = 1; + public static final int FILTER_TYPE_VIDEO = 2; + public static final int FILTER_TYPE_PCR = 3; + + @StringDef({MODULATION_8VSB, MODULATION_QAM256}) + @Retention(RetentionPolicy.SOURCE) + public @interface ModulationType {} + + public static final String MODULATION_8VSB = "8VSB"; + public static final String MODULATION_QAM256 = "QAM256"; + + @IntDef({ + DELIVERY_SYSTEM_UNDEFINED, + DELIVERY_SYSTEM_ATSC, + DELIVERY_SYSTEM_DVBC, + DELIVERY_SYSTEM_DVBS, + DELIVERY_SYSTEM_DVBS2, + DELIVERY_SYSTEM_DVBT, + DELIVERY_SYSTEM_DVBT2 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeliverySystemType {} + + public static final int DELIVERY_SYSTEM_UNDEFINED = 0; + public static final int DELIVERY_SYSTEM_ATSC = 1; + public static final int DELIVERY_SYSTEM_DVBC = 2; + public static final int DELIVERY_SYSTEM_DVBS = 3; + public static final int DELIVERY_SYSTEM_DVBS2 = 4; + public static final int DELIVERY_SYSTEM_DVBT = 5; + public static final int DELIVERY_SYSTEM_DVBT2 = 6; + + @IntDef({TUNER_TYPE_BUILT_IN, TUNER_TYPE_USB, TUNER_TYPE_NETWORK}) + @Retention(RetentionPolicy.SOURCE) + public @interface TunerType {} + + public static final int TUNER_TYPE_BUILT_IN = 1; + public static final int TUNER_TYPE_USB = 2; + public static final int TUNER_TYPE_NETWORK = 3; + + protected static final int PID_PAT = 0; + protected static final int PID_ATSC_SI_BASE = 0x1ffb; + protected static final int PID_DVB_SDT = 0x0011; + protected static final int PID_DVB_EIT = 0x0012; + protected static final int DEFAULT_VSB_TUNE_TIMEOUT_MS = 2000; + protected static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for + // QAM256 tuning. + @IntDef({ + BUILT_IN_TUNER_TYPE_LINUX_DVB + }) + @Retention(RetentionPolicy.SOURCE) + private @interface BuiltInTunerType {} + + private static final int BUILT_IN_TUNER_TYPE_LINUX_DVB = 1; + + private static Integer sBuiltInTunerType; + + protected @DeliverySystemType int mDeliverySystemType; + private boolean mIsStreaming; + private int mFrequency; + private String mModulation; + + static { + if (!BuildConfig.NO_JNI_TEST) { + System.loadLibrary("tunertvinput_jni"); + } + } + + /** + * Creates a TunerHal instance. + * + * @param context context for creating the TunerHal instance + * @return the TunerHal instance + */ + @WorkerThread + public static synchronized TunerHal createInstance(Context context) { + TunerHal tunerHal = null; + if (DvbTunerHal.getNumberOfDevices(context) > 0) { + if (DEBUG) Log.d(TAG, "Use DvbTunerHal"); + tunerHal = new DvbTunerHal(context); + } + return tunerHal != null && tunerHal.openFirstAvailable() ? tunerHal : null; + } + + /** Gets the number of tuner devices currently present. */ + @WorkerThread + public static Pair<Integer, Integer> getTunerTypeAndCount(Context context) { + if (useBuiltInTuner(context)) { + if (getBuiltInTunerType(context) == BUILT_IN_TUNER_TYPE_LINUX_DVB) { + return new Pair<>(TUNER_TYPE_BUILT_IN, DvbTunerHal.getNumberOfDevices(context)); + } + } else { + int usbTunerCount = DvbTunerHal.getNumberOfDevices(context); + if (usbTunerCount > 0) { + return new Pair<>(TUNER_TYPE_USB, usbTunerCount); + } + } + return new Pair<>(null, 0); + } + + /** Check a delivery system is for DVB or not. */ + public static boolean isDvbDeliverySystem(@DeliverySystemType int deliverySystemType) { + return deliverySystemType == DELIVERY_SYSTEM_DVBC + || deliverySystemType == DELIVERY_SYSTEM_DVBS + || deliverySystemType == DELIVERY_SYSTEM_DVBS2 + || deliverySystemType == DELIVERY_SYSTEM_DVBT + || deliverySystemType == DELIVERY_SYSTEM_DVBT2; + } + + /** + * Returns if tuner input service would use built-in tuners instead of USB tuners or network + * tuners. + */ + public static boolean useBuiltInTuner(Context context) { + return getBuiltInTunerType(context) != 0; + } + + private static @BuiltInTunerType int getBuiltInTunerType(Context context) { + if (sBuiltInTunerType == null) { + sBuiltInTunerType = 0; + if (CustomizationManager.hasLinuxDvbBuiltInTuner(context) + && DvbTunerHal.getNumberOfDevices(context) > 0) { + sBuiltInTunerType = BUILT_IN_TUNER_TYPE_LINUX_DVB; + } + } + return sBuiltInTunerType; + } + + protected TunerHal(Context context) { + mIsStreaming = false; + mFrequency = -1; + mModulation = null; + } + + protected boolean isStreaming() { + return mIsStreaming; + } + + protected void getDeliverySystemTypeFromDevice() { + if (mDeliverySystemType == DELIVERY_SYSTEM_UNDEFINED) { + mDeliverySystemType = nativeGetDeliverySystemType(getDeviceId()); + } + } + + /** + * Returns {@code true} if this tuner HAL can be reused to save tuning time between channels of + * the same frequency. + */ + public boolean isReusable() { + return true; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + close(); + } + + protected native void nativeFinalize(long deviceId); + + /** + * Acquires the first available tuner device. If there is a tuner device that is available, the + * tuner device will be locked to the current instance. + * + * @return {@code true} if the operation was successful, {@code false} otherwise + */ + protected abstract boolean openFirstAvailable(); + + protected abstract boolean isDeviceOpen(); + + protected abstract long getDeviceId(); + + /** + * Sets the tuner channel. This should be called after acquiring a tuner device. + * + * @param frequency a frequency of the channel to tune to + * @param modulation a modulation method of the channel to tune to + * @param channelNumber channel number when channel number is already known. Some tuner HAL may + * use channelNumber instead of frequency for tune. + * @return {@code true} if the operation was successful, {@code false} otherwise + */ + public synchronized boolean tune( + int frequency, @ModulationType String modulation, String channelNumber) { + if (!isDeviceOpen()) { + Log.e(TAG, "There's no available device"); + return false; + } + if (mIsStreaming) { + nativeCloseAllPidFilters(getDeviceId()); + mIsStreaming = false; + } + + // When tuning to a new channel in the same frequency, there's no need to stop current tuner + // device completely and the only thing necessary for tuning is reopening pid filters. + if (mFrequency == frequency && Objects.equals(mModulation, modulation)) { + addPidFilter(PID_PAT, FILTER_TYPE_OTHER); + addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); + if (isDvbDeliverySystem(mDeliverySystemType)) { + addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER); + addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER); + } + mIsStreaming = true; + return true; + } + int timeout_ms = + modulation.equals(MODULATION_8VSB) + ? DEFAULT_VSB_TUNE_TIMEOUT_MS + : DEFAULT_QAM_TUNE_TIMEOUT_MS; + if (nativeTune(getDeviceId(), frequency, modulation, timeout_ms)) { + addPidFilter(PID_PAT, FILTER_TYPE_OTHER); + addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); + if (isDvbDeliverySystem(mDeliverySystemType)) { + addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER); + addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER); + } + mFrequency = frequency; + mModulation = modulation; + mIsStreaming = true; + return true; + } + return false; + } + + protected native boolean nativeTune( + long deviceId, int frequency, @ModulationType String modulation, int timeout_ms); + + /** + * Sets a pid filter. This should be set after setting a channel. + * + * @param pid a pid number to be added to filter list + * @param filterType a type of pid. Must be one of (FILTER_TYPE_XXX) + * @return {@code true} if the operation was successful, {@code false} otherwise + */ + public synchronized boolean addPidFilter(int pid, @FilterType int filterType) { + if (!isDeviceOpen()) { + Log.e(TAG, "There's no available device"); + return false; + } + if (pid >= 0 && pid <= 0x1fff) { + nativeAddPidFilter(getDeviceId(), pid, filterType); + return true; + } + return false; + } + + protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType); + + protected native void nativeCloseAllPidFilters(long deviceId); + + protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune); + + protected native int nativeGetDeliverySystemType(long deviceId); + + /** + * Stops current tuning. The tuner device and pid filters will be reset by this call and make + * the tuner ready to accept another tune request. + */ + public synchronized void stopTune() { + if (isDeviceOpen()) { + if (mIsStreaming) { + nativeCloseAllPidFilters(getDeviceId()); + } + nativeStopTune(getDeviceId()); + } + mIsStreaming = false; + mFrequency = -1; + mModulation = null; + } + + public void setHasPendingTune(boolean hasPendingTune) { + nativeSetHasPendingTune(getDeviceId(), hasPendingTune); + } + + public int getDeliverySystemType() { + return mDeliverySystemType; + } + + protected native void nativeStopTune(long deviceId); + + /** + * This method must be called after {@link TunerHal#tune} and before {@link TunerHal#stopTune}. + * Writes at most maxSize TS frames in a buffer provided by the user. The frames employ MPEG + * encoding. + * + * @param javaBuffer a buffer to write the video data in + * @param javaBufferSize the max amount of bytes to write in this buffer. Usually this number + * should be equal to the length of the buffer. + * @return the amount of bytes written in the buffer. Note that this value could be 0 if no new + * frames have been obtained since the last call. + */ + public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) { + if (isDeviceOpen()) { + return nativeWriteInBuffer(getDeviceId(), javaBuffer, javaBufferSize); + } else { + return 0; + } + } + + protected native int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize); + + /** + * Opens Linux DVB frontend device. This method is called from native JNI and used only for + * DvbTunerHal. + */ + @UsedByNative("DvbManager.cpp") + protected int openDvbFrontEndFd() { + return -1; + } + + /** + * Opens Linux DVB demux device. This method is called from native JNI and used only for + * DvbTunerHal. + */ + @UsedByNative("DvbManager.cpp") + protected int openDvbDemuxFd() { + return -1; + } + + /** + * Opens Linux DVB dvr device. This method is called from native JNI and used only for + * DvbTunerHal. + */ + @UsedByNative("DvbManager.cpp") + protected int openDvbDvrFd() { + return -1; + } +} |