stagefright: add indexed buffer and format getters to MediaCodec

These are designed to be called from the same thread as the one
calling dequeue?Buffer, and use a mutex to avoid switching
context.  All other calls of MediaCodec are designed to be blocking
and synchronous.

Bug: 14297827
Change-Id: If341c6e4407ca6f10f5e0d47008dddc0e20b0a50
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 3e6eefb..3fa7b48 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -112,6 +112,10 @@
     status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
     status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
 
+    status_t getOutputBuffer(size_t index, sp<ABuffer> *buffer);
+    status_t getOutputFormat(size_t index, sp<AMessage> *format);
+    status_t getInputBuffer(size_t index, sp<ABuffer> *buffer);
+
     status_t requestIDRFrame();
 
     // Notification will be posted once there "is something to do", i.e.
@@ -189,6 +193,7 @@
         sp<ABuffer> mData;
         sp<ABuffer> mEncryptedData;
         sp<AMessage> mNotify;
+        sp<AMessage> mFormat;
         bool mOwnedByClient;
     };
 
@@ -204,6 +209,12 @@
     sp<AMessage> mOutputFormat;
     sp<AMessage> mInputFormat;
 
+    // Used only to synchronize asynchronous getBufferAndFormat
+    // across all the other (synchronous) buffer state change
+    // operations, such as de/queueIn/OutputBuffer, start and
+    // stop/flush/reset/release.
+    Mutex mBufferLock;
+
     List<size_t> mAvailPortBuffers[2];
     Vector<BufferInfo> mPortBuffers[2];
 
@@ -236,6 +247,10 @@
     status_t onReleaseOutputBuffer(const sp<AMessage> &msg);
     ssize_t dequeuePortBuffer(int32_t portIndex);
 
+    status_t getBufferAndFormat(
+            size_t portIndex, size_t index,
+            sp<ABuffer> *buffer, sp<AMessage> *format);
+
     bool handleDequeueInputBuffer(uint32_t replyID, bool newRequest = false);
     bool handleDequeueOutputBuffer(uint32_t replyID, bool newRequest = false);
     void cancelPendingDequeueOperations();
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6e3d6f8..1e6ac45 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -410,6 +410,46 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::getOutputBuffer(size_t index, sp<ABuffer> *buffer) {
+    sp<AMessage> format;
+    return getBufferAndFormat(kPortIndexOutput, index, buffer, &format);
+}
+
+status_t MediaCodec::getOutputFormat(size_t index, sp<AMessage> *format) {
+    sp<ABuffer> buffer;
+    return getBufferAndFormat(kPortIndexOutput, index, &buffer, format);
+}
+
+status_t MediaCodec::getInputBuffer(size_t index, sp<ABuffer> *buffer) {
+    sp<AMessage> format;
+    return getBufferAndFormat(kPortIndexInput, index, buffer, &format);
+}
+
+status_t MediaCodec::getBufferAndFormat(
+        size_t portIndex, size_t index,
+        sp<ABuffer> *buffer, sp<AMessage> *format) {
+    // use mutex instead of a context switch
+
+    buffer->clear();
+    format->clear();
+    if (mState != STARTED) {
+        return INVALID_OPERATION;
+    }
+
+    // we do not want mPortBuffers to change during this section
+    // we also don't want mOwnedByClient to change during this
+    Mutex::Autolock al(mBufferLock);
+    Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+    if (index < buffers->size()) {
+        const BufferInfo &info = buffers->itemAt(index);
+        if (info.mOwnedByClient) {
+            *buffer = info.mData;
+            *format = info.mFormat;
+        }
+    }
+    return OK;
+}
+
 status_t MediaCodec::flush() {
     sp<AMessage> msg = new AMessage(kWhatFlush, id());
 
@@ -710,6 +750,7 @@
 
                 case CodecBase::kWhatBuffersAllocated:
                 {
+                    Mutex::Autolock al(mBufferLock);
                     int32_t portIndex;
                     CHECK(msg->findInt32("portIndex", &portIndex));
 
@@ -1463,8 +1504,8 @@
 status_t MediaCodec::queueCSDInputBuffer(size_t bufferIndex) {
     CHECK(!mCSD.empty());
 
-    BufferInfo *info =
-        &mPortBuffers[kPortIndexInput].editItemAt(bufferIndex);
+    const BufferInfo *info =
+        &mPortBuffers[kPortIndexInput].itemAt(bufferIndex);
 
     sp<ABuffer> csd = *mCSD.begin();
     mCSD.erase(mCSD.begin());
@@ -1530,6 +1571,7 @@
 
 void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) {
     CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+    Mutex::Autolock al(mBufferLock);
 
     Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
 
@@ -1684,11 +1726,15 @@
         info->mData->setRange(0, result);
     }
 
+    // synchronization boundary for getBufferAndFormat
+    {
+        Mutex::Autolock al(mBufferLock);
+        info->mOwnedByClient = false;
+    }
     reply->setBuffer("buffer", info->mData);
     reply->post();
 
     info->mNotify = NULL;
-    info->mOwnedByClient = false;
 
     return OK;
 }
@@ -1716,6 +1762,12 @@
         return -EACCES;
     }
 
+    // synchronization boundary for getBufferAndFormat
+    {
+        Mutex::Autolock al(mBufferLock);
+        info->mOwnedByClient = false;
+    }
+
     if (render && info->mData != NULL && info->mData->size() != 0) {
         info->mNotify->setInt32("render", true);
 
@@ -1743,7 +1795,6 @@
 
     info->mNotify->post();
     info->mNotify = NULL;
-    info->mOwnedByClient = false;
 
     return OK;
 }
@@ -1762,7 +1813,11 @@
 
     BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index);
     CHECK(!info->mOwnedByClient);
-    info->mOwnedByClient = true;
+    {
+        Mutex::Autolock al(mBufferLock);
+        info->mFormat = portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
+        info->mOwnedByClient = true;
+    }
 
     return index;
 }