OSDN Git Service

Implement support for buffer producer profile
authorWonsik Kim <wonsik@google.com>
Fri, 23 May 2014 07:07:55 +0000 (16:07 +0900)
committerWonsik Kim <wonsik@google.com>
Tue, 8 Jul 2014 06:52:00 +0000 (15:52 +0900)
Bug: 15177117
Change-Id: I7776a8b2959281f10cba742ab75f15f80f6feecf

services/core/jni/com_android_server_tv_TvInputHal.cpp

index 64d418a..a9d5c72 100644 (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) {