Merge "Add a frame capture API of TV input" into lmp-dev
This commit is contained in:
@@ -1095,6 +1095,13 @@
|
||||
<permission android:name="android.permission.TV_INPUT_HARDWARE"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<!-- @SystemApi Allows to capture a frame of TV input hardware such as
|
||||
built-in tuners and HDMI-in's.
|
||||
@hide <p>Not for use by third-party applications.
|
||||
-->
|
||||
<permission android:name="android.permission.CAPTURE_TV_INPUT"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<!-- @hide Allows enabling/disabling OEM unlock
|
||||
<p>Not for use by third-party applications. -->
|
||||
<permission android:name="android.permission.OEM_UNLOCK_STATE"
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.media.tv.ITvInputHardwareCallback;
|
||||
import android.media.tv.ITvInputManagerCallback;
|
||||
import android.media.tv.TvInputHardwareInfo;
|
||||
import android.media.tv.TvInputInfo;
|
||||
import android.media.tv.TvStreamConfig;
|
||||
import android.media.tv.TvTrackInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -67,4 +68,9 @@ interface ITvInputManager {
|
||||
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
|
||||
in TvInputInfo info, int userId);
|
||||
void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
|
||||
|
||||
// For TV input capturing
|
||||
List<TvStreamConfig> getAvailableTvStreamConfigList(in String inputId, int userId);
|
||||
boolean captureFrame(in String inputId, in Surface surface, in TvStreamConfig config,
|
||||
int userId);
|
||||
}
|
||||
|
||||
@@ -682,6 +682,41 @@ public final class TvInputManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TvStreamConfig list of the given TV input.
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @return List of {@link TvStreamConfig} which is available for capturing
|
||||
* of the given TV input.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId) {
|
||||
try {
|
||||
return mService.getAvailableTvStreamConfigList(inputId, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a snapshot of the given TV input into the provided Surface.
|
||||
*
|
||||
* @param inputId the id of the TV input.
|
||||
* @param surface the {@link Surface} to which the snapshot is captured.
|
||||
* @param config the {@link TvStreamConfig} which is used for capturing.
|
||||
* @return true when the {@link Surface} is ready to be captured.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config) {
|
||||
try {
|
||||
return mService.captureFrame(inputId, surface, config, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Session provides the per-session functionality of TV inputs.
|
||||
* @hide
|
||||
|
||||
@@ -40,16 +40,17 @@ final class TvInputHal implements Handler.Callback {
|
||||
public final static int ERROR_STALE_CONFIG = -2;
|
||||
public final static int ERROR_UNKNOWN = -3;
|
||||
|
||||
// Below should be in sync with hardware/libhardware/include/hardware/tv_input.h
|
||||
public static final int EVENT_DEVICE_AVAILABLE = 1;
|
||||
public static final int EVENT_DEVICE_UNAVAILABLE = 2;
|
||||
public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
|
||||
public static final int EVENT_FIRST_FRAME_CAPTURED = 4;
|
||||
|
||||
public interface Callback {
|
||||
public void onDeviceAvailable(
|
||||
TvInputHardwareInfo info, TvStreamConfig[] configs);
|
||||
public void onDeviceUnavailable(int deviceId);
|
||||
public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
|
||||
public void onFirstFrameCaptured(int deviceId, int streamId);
|
||||
}
|
||||
|
||||
private native long nativeOpen();
|
||||
@@ -131,6 +132,11 @@ final class TvInputHal implements Handler.Callback {
|
||||
mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget();
|
||||
}
|
||||
|
||||
private void firstFrameCapturedFromNative(int deviceId, int streamId) {
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
|
||||
}
|
||||
|
||||
// Handler.Callback implementation
|
||||
|
||||
private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
|
||||
@@ -167,6 +173,13 @@ final class TvInputHal implements Handler.Callback {
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_FIRST_FRAME_CAPTURED: {
|
||||
int deviceId = msg.arg1;
|
||||
int streamId = msg.arg2;
|
||||
mCallback.onFirstFrameCaptured(deviceId, streamId);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Slog.e(TAG, "Unknown event: " + msg);
|
||||
return false;
|
||||
|
||||
@@ -172,6 +172,23 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstFrameCaptured(int deviceId, int streamId) {
|
||||
synchronized (mLock) {
|
||||
Connection connection = mConnections.get(deviceId);
|
||||
if (connection == null) {
|
||||
Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
|
||||
+ deviceId);
|
||||
return;
|
||||
}
|
||||
Runnable runnable = connection.getOnFirstFrameCapturedLocked();
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
connection.setOnFirstFrameCapturedLocked(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<TvInputHardwareInfo> getHardwareList() {
|
||||
synchronized (mLock) {
|
||||
return mInfoList;
|
||||
@@ -337,6 +354,74 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int findDeviceIdForInputIdLocked(String inputId) {
|
||||
for (int i = 0; i < mConnections.size(); ++i) {
|
||||
Connection connection = mConnections.get(i);
|
||||
if (connection.getInfoLocked().getId().equals(inputId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of TvStreamConfig which is buffered mode.
|
||||
*/
|
||||
public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
|
||||
int resolvedUserId) {
|
||||
List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
|
||||
synchronized (mLock) {
|
||||
int deviceId = findDeviceIdForInputIdLocked(inputId);
|
||||
if (deviceId < 0) {
|
||||
Slog.e(TAG, "Invalid inputId : " + inputId);
|
||||
return configsList;
|
||||
}
|
||||
Connection connection = mConnections.get(deviceId);
|
||||
for (TvStreamConfig config : connection.getConfigsLocked()) {
|
||||
if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
|
||||
configsList.add(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
return configsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a snapshot of the given TV input into the provided Surface.
|
||||
*/
|
||||
public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
|
||||
int callingUid, int resolvedUserId) {
|
||||
synchronized (mLock) {
|
||||
int deviceId = findDeviceIdForInputIdLocked(inputId);
|
||||
if (deviceId < 0) {
|
||||
Slog.e(TAG, "Invalid inputId : " + inputId);
|
||||
return false;
|
||||
}
|
||||
Connection connection = mConnections.get(deviceId);
|
||||
final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
|
||||
if (hardwareImpl != null) {
|
||||
// Stop previous capture.
|
||||
Runnable runnable = connection.getOnFirstFrameCapturedLocked();
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
connection.setOnFirstFrameCapturedLocked(null);
|
||||
}
|
||||
|
||||
boolean result = hardwareImpl.startCapture(surface, config);
|
||||
if (result) {
|
||||
connection.setOnFirstFrameCapturedLocked(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hardwareImpl.stopCapture(config);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class Connection implements IBinder.DeathRecipient {
|
||||
private final TvInputHardwareInfo mHardwareInfo;
|
||||
private TvInputInfo mInfo;
|
||||
@@ -345,6 +430,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
private TvStreamConfig[] mConfigs = null;
|
||||
private Integer mCallingUid = null;
|
||||
private Integer mResolvedUserId = null;
|
||||
private Runnable mOnFirstFrameCaptured;
|
||||
|
||||
public Connection(TvInputHardwareInfo hardwareInfo) {
|
||||
mHardwareInfo = hardwareInfo;
|
||||
@@ -367,6 +453,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
mInfo = info;
|
||||
mCallingUid = callingUid;
|
||||
mResolvedUserId = resolvedUserId;
|
||||
mOnFirstFrameCaptured = null;
|
||||
|
||||
if (mHardware != null && mCallback != null) {
|
||||
try {
|
||||
@@ -393,6 +480,10 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
return mHardware;
|
||||
}
|
||||
|
||||
public TvInputHardwareImpl getHardwareImplLocked() {
|
||||
return mHardware;
|
||||
}
|
||||
|
||||
public ITvInputHardwareCallback getCallbackLocked() {
|
||||
return mCallback;
|
||||
}
|
||||
@@ -409,6 +500,14 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
return mResolvedUserId;
|
||||
}
|
||||
|
||||
public void setOnFirstFrameCapturedLocked(Runnable runnable) {
|
||||
mOnFirstFrameCaptured = runnable;
|
||||
}
|
||||
|
||||
public Runnable getOnFirstFrameCapturedLocked() {
|
||||
return mOnFirstFrameCaptured;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
synchronized (mLock) {
|
||||
@@ -559,6 +658,37 @@ class TvInputHardwareManager implements TvInputHal.Callback {
|
||||
// TODO(hdmi): mHdmiClient.sendKeyEvent(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean startCapture(Surface surface, TvStreamConfig config) {
|
||||
synchronized (mImplLock) {
|
||||
if (mReleased) {
|
||||
return false;
|
||||
}
|
||||
if (surface == null || config == null) {
|
||||
return false;
|
||||
}
|
||||
if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
|
||||
return result == TvInputHal.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean stopCapture(TvStreamConfig config) {
|
||||
synchronized (mImplLock) {
|
||||
if (mReleased) {
|
||||
return false;
|
||||
}
|
||||
if (config == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = mHal.removeStream(mInfo.getDeviceId(), config);
|
||||
return result == TvInputHal.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.media.tv.TvContract;
|
||||
import android.media.tv.TvInputHardwareInfo;
|
||||
import android.media.tv.TvInputInfo;
|
||||
import android.media.tv.TvInputService;
|
||||
import android.media.tv.TvStreamConfig;
|
||||
import android.media.tv.TvTrackInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
@@ -1256,6 +1257,49 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
|
||||
throws RemoteException {
|
||||
if (mContext.checkCallingPermission(
|
||||
android.Manifest.permission.CAPTURE_TV_INPUT)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
|
||||
}
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
|
||||
userId, "getAvailableTvStreamConfigList");
|
||||
try {
|
||||
return mTvInputHardwareManager.getAvailableTvStreamConfigList(
|
||||
inputId, callingUid, resolvedUserId);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config,
|
||||
int userId)
|
||||
throws RemoteException {
|
||||
if (mContext.checkCallingPermission(
|
||||
android.Manifest.permission.CAPTURE_TV_INPUT)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException("Requires CAPTURE_TV_INPUT permission");
|
||||
}
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
|
||||
userId, "captureFrame");
|
||||
try {
|
||||
return mTvInputHardwareManager.captureFrame(
|
||||
inputId, surface, config, callingUid, resolvedUserId);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("resource")
|
||||
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
|
||||
|
||||
@@ -36,6 +36,7 @@ static struct {
|
||||
jmethodID deviceAvailable;
|
||||
jmethodID deviceUnavailable;
|
||||
jmethodID streamConfigsChanged;
|
||||
jmethodID firstFrameCaptured;
|
||||
} gTvInputHalClassInfo;
|
||||
|
||||
static struct {
|
||||
@@ -539,6 +540,14 @@ void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succ
|
||||
thread = connection.mThread;
|
||||
}
|
||||
thread->onCaptured(seq, succeeded);
|
||||
if (seq == 0) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->CallVoidMethod(
|
||||
mThiz,
|
||||
gTvInputHalClassInfo.firstFrameCaptured,
|
||||
deviceId,
|
||||
streamId);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -638,6 +647,9 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) {
|
||||
GET_METHOD_ID(
|
||||
gTvInputHalClassInfo.streamConfigsChanged, clazz,
|
||||
"streamConfigsChangedFromNative", "(I)V");
|
||||
GET_METHOD_ID(
|
||||
gTvInputHalClassInfo.firstFrameCaptured, clazz,
|
||||
"firstFrameCapturedFromNative", "(II)V");
|
||||
|
||||
FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
|
||||
gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
|
||||
|
||||
Reference in New Issue
Block a user