summaryrefslogtreecommitdiffstats
path: root/services/core/jni
diff options
context:
space:
mode:
authorWonsik Kim <wonsik@google.com>2014-07-08 08:02:26 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-07-02 20:35:42 +0000
commit1e47ff9f8faa2927084b3da1e2d7d16ef0477637 (patch)
tree1240de521d9d4c30529129c6badf6a0e530a2a1f /services/core/jni
parent98d29489081350d64ee397df83a552aa85651d8e (diff)
parent0b2691b6873f9d328884c5a9e5ab1fe308f3ee36 (diff)
downloadframeworks_base-1e47ff9f8faa2927084b3da1e2d7d16ef0477637.zip
frameworks_base-1e47ff9f8faa2927084b3da1e2d7d16ef0477637.tar.gz
frameworks_base-1e47ff9f8faa2927084b3da1e2d7d16ef0477637.tar.bz2
Merge "Implement support for buffer producer profile"
Diffstat (limited to 'services/core/jni')
-rw-r--r--services/core/jni/com_android_server_tv_TvInputHal.cpp275
1 files changed, 250 insertions, 25 deletions
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 64d418a..a9d5c72 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -68,6 +68,166 @@ static struct {
////////////////////////////////////////////////////////////////////////////////
+class BufferProducerThread : public Thread {
+public:
+ BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
+
+ virtual status_t readyToRun();
+
+ void setSurface(const sp<Surface>& surface);
+ void onCaptured(uint32_t seq, bool succeeded);
+ void shutdown();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+ sp<Surface> mSurface;
+ tv_input_device_t* mDevice;
+ int mDeviceId;
+ tv_stream_t mStream;
+ sp<ANativeWindowBuffer_t> mBuffer;
+ enum {
+ CAPTURING,
+ CAPTURED,
+ RELEASED,
+ } mBufferState;
+ uint32_t mSeq;
+ bool mShutdown;
+
+ virtual bool threadLoop();
+
+ void setSurfaceLocked(const sp<Surface>& surface);
+};
+
+BufferProducerThread::BufferProducerThread(
+ tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
+ : Thread(false),
+ mDevice(device),
+ mDeviceId(deviceId),
+ mBuffer(NULL),
+ mBufferState(RELEASED),
+ mSeq(0u),
+ mShutdown(false) {
+ memcpy(&mStream, stream, sizeof(mStream));
+}
+
+status_t BufferProducerThread::readyToRun() {
+ sp<ANativeWindow> anw(mSurface);
+ status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = native_window_set_buffers_dimensions(
+ anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ return NO_ERROR;
+}
+
+void BufferProducerThread::setSurface(const sp<Surface>& surface) {
+ Mutex::Autolock autoLock(&mLock);
+ setSurfaceLocked(surface);
+}
+
+void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
+ if (surface == mSurface) {
+ return;
+ }
+
+ if (mBufferState == CAPTURING) {
+ mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
+ }
+ while (mBufferState == CAPTURING) {
+ status_t err = mCondition.waitRelative(mLock, s2ns(1));
+ if (err != NO_ERROR) {
+ ALOGE("error %d while wating for buffer state to change.", err);
+ break;
+ }
+ }
+ mBuffer.clear();
+ mBufferState = RELEASED;
+
+ mSurface = surface;
+ mCondition.broadcast();
+}
+
+void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
+ Mutex::Autolock autoLock(&mLock);
+ if (seq != mSeq) {
+ ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
+ }
+ if (mBufferState != CAPTURING) {
+ ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
+ }
+ if (succeeded) {
+ mBufferState = CAPTURED;
+ } else {
+ mBuffer.clear();
+ mBufferState = RELEASED;
+ }
+ mCondition.broadcast();
+}
+
+void BufferProducerThread::shutdown() {
+ Mutex::Autolock autoLock(&mLock);
+ mShutdown = true;
+ setSurfaceLocked(NULL);
+ requestExitAndWait();
+}
+
+bool BufferProducerThread::threadLoop() {
+ Mutex::Autolock autoLock(&mLock);
+
+ status_t err = NO_ERROR;
+ if (mSurface == NULL) {
+ err = mCondition.waitRelative(mLock, s2ns(1));
+ // It's OK to time out here.
+ if (err != NO_ERROR && err != TIMED_OUT) {
+ ALOGE("error %d while wating for non-null surface to be set", err);
+ return false;
+ }
+ return true;
+ }
+ sp<ANativeWindow> anw(mSurface);
+ while (mBufferState == CAPTURING) {
+ err = mCondition.waitRelative(mLock, s2ns(1));
+ if (err != NO_ERROR) {
+ ALOGE("error %d while wating for buffer state to change.", err);
+ return false;
+ }
+ }
+ if (mBufferState == CAPTURED && anw != NULL) {
+ err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
+ if (err != NO_ERROR) {
+ ALOGE("error %d while queueing buffer to surface", err);
+ return false;
+ }
+ mBuffer.clear();
+ mBufferState = RELEASED;
+ }
+ if (mBuffer == NULL && !mShutdown && anw != NULL) {
+ ANativeWindowBuffer_t* buffer = NULL;
+ err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
+ if (err != NO_ERROR) {
+ ALOGE("error %d while dequeueing buffer to surface", err);
+ return false;
+ }
+ mBuffer = buffer;
+ mBufferState = CAPTURING;
+ mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
+ buffer->handle, ++mSeq);
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
class JTvInputHal {
public:
~JTvInputHal();
@@ -79,23 +239,31 @@ public:
const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
private:
+ // Connection between a surface and a stream.
class Connection {
public:
Connection() {}
sp<Surface> mSurface;
+ tv_stream_type_t mStreamType;
+
+ // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
sp<NativeHandle> mSourceHandle;
+ // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
+ sp<BufferProducerThread> mThread;
};
JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
static void notify(
- tv_input_device_t* dev,tv_input_event_t* event, void* data);
+ tv_input_device_t* dev, tv_input_event_t* event, void* data);
void onDeviceAvailable(const tv_input_device_info_t& info);
void onDeviceUnavailable(int deviceId);
void onStreamConfigurationsChanged(int deviceId);
+ void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
+ Mutex mLock;
jweak mThiz;
tv_input_device_t* mDevice;
tv_input_callback_ops_t mCallback;
@@ -153,11 +321,16 @@ int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surfac
// Nothing to do
return NO_ERROR;
}
- if (Surface::isValid(connection.mSurface)) {
+ // Clear the surface in the connection.
+ if (connection.mSurface != NULL) {
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ if (Surface::isValid(connection.mSurface)) {
+ connection.mSurface->setSidebandStream(NULL);
+ }
+ }
connection.mSurface.clear();
}
- connection.mSurface = surface;
- if (connection.mSourceHandle == NULL) {
+ if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
// Need to configure stream
int numConfigs = 0;
const tv_stream_config_t* configs = NULL;
@@ -177,22 +350,32 @@ int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surfac
ALOGE("Cannot find a config with given stream ID: %d", streamId);
return BAD_VALUE;
}
- // TODO: handle buffer producer profile.
- if (configs[configIndex].type !=
- TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
- ALOGE("Profiles other than independent video source is not yet "
- "supported : type = %d", configs[configIndex].type);
- return INVALID_OPERATION;
- }
tv_stream_t stream;
stream.stream_id = configs[configIndex].stream_id;
+ if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ stream.buffer_producer.width = configs[configIndex].max_video_width;
+ stream.buffer_producer.height = configs[configIndex].max_video_height;
+ }
if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
ALOGE("Couldn't add stream");
return UNKNOWN_ERROR;
}
- connection.mSourceHandle = NativeHandle::create(
- stream.sideband_stream_source_handle, false);
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ connection.mSourceHandle = NativeHandle::create(
+ stream.sideband_stream_source_handle, false);
+ } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ if (connection.mThread != NULL) {
+ connection.mThread->shutdown();
+ }
+ connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
+ connection.mThread->run();
+ }
+ }
+ connection.mSurface = surface;
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
connection.mSurface->setSidebandStream(connection.mSourceHandle);
+ } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ connection.mThread->setSurface(surface);
}
return NO_ERROR;
}
@@ -220,6 +403,12 @@ int JTvInputHal::removeStream(int deviceId, int streamId) {
ALOGE("Couldn't remove stream");
return BAD_VALUE;
}
+
+ // Clear everything
+ if (connection.mThread != NULL) {
+ connection.mThread->shutdown();
+ connection.mThread.clear();
+ }
connection.mSourceHandle.clear();
}
return NO_ERROR;
@@ -249,14 +438,29 @@ void JTvInputHal::notify(
case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
thiz->onStreamConfigurationsChanged(event->device_info.device_id);
} break;
+ case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
+ thiz->onCaptured(event->capture_result.device_id,
+ event->capture_result.stream_id,
+ event->capture_result.seq,
+ true /* succeeded */);
+ } break;
+ case TV_INPUT_EVENT_CAPTURE_FAILED: {
+ thiz->onCaptured(event->capture_result.device_id,
+ event->capture_result.stream_id,
+ event->capture_result.seq,
+ false /* succeeded */);
+ } break;
default:
ALOGE("Unrecognizable event");
}
}
void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+ {
+ Mutex::Autolock autoLock(&mLock);
+ mConnections.add(info.device_id, KeyedVector<int, Connection>());
+ }
JNIEnv* env = AndroidRuntime::getJNIEnv();
- mConnections.add(info.device_id, KeyedVector<int, Connection>());
jobject builder = env->NewObject(
gTvInputHardwareInfoBuilderClassInfo.clazz,
@@ -290,13 +494,16 @@ void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
}
void JTvInputHal::onDeviceUnavailable(int deviceId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
- for (size_t i = 0; i < connections.size(); ++i) {
- removeStream(deviceId, connections.keyAt(i));
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ for (size_t i = 0; i < connections.size(); ++i) {
+ removeStream(deviceId, connections.keyAt(i));
+ }
+ connections.clear();
+ mConnections.removeItem(deviceId);
}
- connections.clear();
- mConnections.removeItem(deviceId);
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mThiz,
gTvInputHalClassInfo.deviceUnavailable,
@@ -304,18 +511,36 @@ void JTvInputHal::onDeviceUnavailable(int deviceId) {
}
void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
- for (size_t i = 0; i < connections.size(); ++i) {
- removeStream(deviceId, connections.keyAt(i));
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ for (size_t i = 0; i < connections.size(); ++i) {
+ removeStream(deviceId, connections.keyAt(i));
+ }
+ connections.clear();
}
- connections.clear();
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mThiz,
gTvInputHalClassInfo.streamConfigsChanged,
deviceId);
}
+void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
+ sp<BufferProducerThread> thread;
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ Connection& connection = connections.editValueFor(streamId);
+ if (connection.mThread == NULL) {
+ ALOGE("capture thread not existing.");
+ return;
+ }
+ thread = connection.mThread;
+ }
+ thread->onCaptured(seq, succeeded);
+}
+
////////////////////////////////////////////////////////////////////////////////
static jlong nativeOpen(JNIEnv* env, jobject thiz) {