diff options
author | Wonsik Kim <wonsik@google.com> | 2014-07-08 08:02:26 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-07-02 20:35:42 +0000 |
commit | 1e47ff9f8faa2927084b3da1e2d7d16ef0477637 (patch) | |
tree | 1240de521d9d4c30529129c6badf6a0e530a2a1f /services/core/jni | |
parent | 98d29489081350d64ee397df83a552aa85651d8e (diff) | |
parent | 0b2691b6873f9d328884c5a9e5ab1fe308f3ee36 (diff) | |
download | frameworks_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.cpp | 275 |
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) { |