Implement support for buffer producer profile
Bug: 15177117 Change-Id: I7776a8b2959281f10cba742ab75f15f80f6feecf
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user