Loading services/core/jni/com_android_server_tv_TvInputHal.cpp +250 −25 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -79,12 +239,18 @@ 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); Loading @@ -95,7 +261,9 @@ private: 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; Loading Loading @@ -153,11 +321,16 @@ int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surfac // Nothing to do return NO_ERROR; } // 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; Loading @@ -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; } 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; } Loading Loading @@ -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; Loading Loading @@ -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) { JNIEnv* env = AndroidRuntime::getJNIEnv(); { Mutex::Autolock autoLock(&mLock); mConnections.add(info.device_id, KeyedVector<int, Connection>()); } JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject builder = env->NewObject( gTvInputHardwareInfoBuilderClassInfo.clazz, Loading Loading @@ -290,13 +494,16 @@ void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { } void JTvInputHal::onDeviceUnavailable(int deviceId) { JNIEnv* env = AndroidRuntime::getJNIEnv(); { 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); } JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mThiz, gTvInputHalClassInfo.deviceUnavailable, Loading @@ -304,18 +511,36 @@ void JTvInputHal::onDeviceUnavailable(int deviceId) { } void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { JNIEnv* env = AndroidRuntime::getJNIEnv(); { 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(); } 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) { Loading Loading
services/core/jni/com_android_server_tv_TvInputHal.cpp +250 −25 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -79,12 +239,18 @@ 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); Loading @@ -95,7 +261,9 @@ private: 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; Loading Loading @@ -153,11 +321,16 @@ int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surfac // Nothing to do return NO_ERROR; } // 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; Loading @@ -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; } 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; } Loading Loading @@ -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; Loading Loading @@ -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) { JNIEnv* env = AndroidRuntime::getJNIEnv(); { Mutex::Autolock autoLock(&mLock); mConnections.add(info.device_id, KeyedVector<int, Connection>()); } JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject builder = env->NewObject( gTvInputHardwareInfoBuilderClassInfo.clazz, Loading Loading @@ -290,13 +494,16 @@ void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { } void JTvInputHal::onDeviceUnavailable(int deviceId) { JNIEnv* env = AndroidRuntime::getJNIEnv(); { 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); } JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mThiz, gTvInputHalClassInfo.deviceUnavailable, Loading @@ -304,18 +511,36 @@ void JTvInputHal::onDeviceUnavailable(int deviceId) { } void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { JNIEnv* env = AndroidRuntime::getJNIEnv(); { 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(); } 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) { Loading