Merge "Implement support for buffer producer profile"

This commit is contained in:
Wonsik Kim
2014-07-08 08:02:26 +00:00
committed by Android (Google) Code Review

View File

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