Merge "Add a frame capture API of TV input" into lmp-dev

This commit is contained in:
Terry Heo
2014-07-24 01:28:13 +00:00
committed by Android (Google) Code Review
7 changed files with 248 additions and 1 deletions

View File

@@ -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"

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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));