IMediaSource: use shared memory to transfer large buffer.

Also move MediaBufferGroup to libstagefright/foundation/.

Bug: 26295488
Change-Id: I88f4e6bf83ffb2b196628a2d4d83ea7b1f6ad9c2
diff --git a/include/media/IMediaSource.h b/include/media/IMediaSource.h
index 1420120..f7586a7 100644
--- a/include/media/IMediaSource.h
+++ b/include/media/IMediaSource.h
@@ -26,6 +26,7 @@
 struct MediaSource;
 class MetaData;
 class MediaBuffer;
+class MediaBufferGroup;
 
 class IMediaSource : public IInterface {
 public:
@@ -112,6 +113,8 @@
 class BnMediaSource: public BnInterface<IMediaSource>
 {
 public:
+    BnMediaSource();
+
     virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                 uint32_t flags = 0);
 
@@ -122,6 +125,12 @@
     virtual status_t setBuffers(const Vector<MediaBuffer *> & /* buffers */) {
         return ERROR_UNSUPPORTED;
     }
+
+protected:
+    virtual ~BnMediaSource();
+
+private:
+    MediaBufferGroup *mGroup;
 };
 
 
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 1e0c7d4..18b80e3 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -48,6 +48,9 @@
 
 class MediaBuffer : public MediaBufferBase {
 public:
+    // allocations larger than or equal to this will use shared memory.
+    static const size_t kSharedMemThreshold = 64 * 1024;
+
     // The underlying data remains the responsibility of the caller!
     MediaBuffer(void *data, size_t size);
 
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
index a006f7f..7ca3fa1 100644
--- a/include/media/stagefright/MediaBufferGroup.h
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -39,7 +39,11 @@
     // The returned buffer will have a reference count of 1.
     // If nonBlocking is true and a buffer is not immediately available,
     // buffer is set to NULL and it returns WOULD_BLOCK.
-    status_t acquire_buffer(MediaBuffer **buffer, bool nonBlocking = false);
+    // If requestedSize is 0, any free MediaBuffer will be returned.
+    // If requestedSize is > 0, the returned MediaBuffer should have buffer
+    // size of at least requstedSize.
+    status_t acquire_buffer(
+            MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
 
 protected:
     virtual void signalBufferReturned(MediaBuffer *buffer);
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index fc9a123..b988c46 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -25,6 +25,7 @@
 #include <binder/Parcel.h>
 #include <media/IMediaSource.h>
 #include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 
@@ -47,8 +48,9 @@
 
 class RemoteMediaBufferReleaser : public BBinder {
 public:
-    RemoteMediaBufferReleaser(MediaBuffer *buf) {
+    RemoteMediaBufferReleaser(MediaBuffer *buf, sp<BnMediaSource> owner) {
         mBuf = buf;
+        mOwner = owner;
     }
     ~RemoteMediaBufferReleaser() {
         if (mBuf) {
@@ -70,6 +72,10 @@
     }
 private:
     MediaBuffer *mBuf;
+    // Keep a ref to ensure MediaBuffer is released before the owner, i.e., BnMediaSource,
+    // because BnMediaSource needs to delete MediaBufferGroup in its dtor and
+    // MediaBufferGroup dtor requires all MediaBuffer's have 0 ref count.
+    sp<BnMediaSource> mOwner;
 };
 
 
@@ -207,6 +213,15 @@
 #undef LOG_TAG
 #define LOG_TAG "BnMediaSource"
 
+BnMediaSource::BnMediaSource()
+    : mGroup(NULL) {
+}
+
+BnMediaSource::~BnMediaSource() {
+    delete mGroup;
+    mGroup = NULL;
+}
+
 status_t BnMediaSource::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -263,17 +278,45 @@
                 size_t usedSize = buf->range_length();
                 // even if we're using shared memory, we might not want to use it, since for small
                 // sizes it's faster to copy data through the Binder transaction
-                if (buf->mMemory != NULL && usedSize >= 64 * 1024) {
-                    ALOGV("buffer is using shared memory: %zu", usedSize);
+                // On the other hand, if the data size is large enough, it's better to use shared
+                // memory. When data is too large, binder can't handle it.
+                if (usedSize >= MediaBuffer::kSharedMemThreshold) {
+                    ALOGV("use shared memory: %zu", usedSize);
+
+                    MediaBuffer *transferBuf = buf;
+                    size_t offset = buf->range_offset();
+                    if (transferBuf->mMemory == NULL) {
+                        if (mGroup == NULL) {
+                            mGroup = new MediaBufferGroup;
+                            size_t allocateSize = usedSize;
+                            if (usedSize < SIZE_MAX / 3) {
+                                allocateSize = usedSize * 3 / 2;
+                            }
+                            mGroup->add_buffer(new MediaBuffer(allocateSize));
+                        }
+
+                        ret = mGroup->acquire_buffer(
+                                &transferBuf, false /* nonBlocking */, usedSize);
+                        if (ret != OK || transferBuf == NULL || transferBuf->mMemory == NULL) {
+                            ALOGW("failed to acquire shared memory, ret %d", ret);
+                            reply->writeInt32(NULL_BUFFER);
+                            return NO_ERROR;
+                        }
+                        memcpy(transferBuf->data(), (uint8_t*)buf->data() + buf->range_offset(),
+                                buf->range_length());
+                        offset = 0;
+                    }
+
                     reply->writeInt32(SHARED_BUFFER);
-                    RemoteMediaBufferReleaser *wrapper = new RemoteMediaBufferReleaser(buf);
+                    RemoteMediaBufferReleaser *wrapper =
+                        new RemoteMediaBufferReleaser(transferBuf, this);
                     reply->writeStrongBinder(wrapper);
-                    reply->writeStrongBinder(IInterface::asBinder(buf->mMemory));
-                    reply->writeInt32(buf->range_offset());
+                    reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
+                    reply->writeInt32(offset);
                     reply->writeInt32(usedSize);
                     buf->meta_data()->writeToParcel(*reply);
                 } else {
-                    // buffer is not in shared memory, or is small: copy it
+                    // buffer is small: copy it
                     if (buf->mMemory != NULL) {
                         ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length());
                     }
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index fd4ed58..68e02e7 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -29,7 +29,6 @@
         MPEG4Extractor.cpp                \
         MPEG4Writer.cpp                   \
         MediaAdapter.cpp                  \
-        MediaBufferGroup.cpp              \
         MediaClock.cpp                    \
         MediaCodec.cpp                    \
         MediaCodecList.cpp                \
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
deleted file mode 100644
index 6ac6d4a..0000000
--- a/media/libstagefright/MediaBufferGroup.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MediaBufferGroup"
-#include <utils/Log.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaBufferGroup.h>
-
-namespace android {
-
-MediaBufferGroup::MediaBufferGroup()
-    : mFirstBuffer(NULL),
-      mLastBuffer(NULL) {
-}
-
-MediaBufferGroup::~MediaBufferGroup() {
-    MediaBuffer *next;
-    for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
-         buffer = next) {
-        next = buffer->nextBuffer();
-
-        CHECK_EQ(buffer->refcount(), 0);
-
-        buffer->setObserver(NULL);
-        buffer->release();
-    }
-}
-
-void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
-    Mutex::Autolock autoLock(mLock);
-
-    buffer->setObserver(this);
-
-    if (mLastBuffer) {
-        mLastBuffer->setNextBuffer(buffer);
-    } else {
-        mFirstBuffer = buffer;
-    }
-
-    mLastBuffer = buffer;
-}
-
-status_t MediaBufferGroup::acquire_buffer(
-        MediaBuffer **out, bool nonBlocking) {
-    Mutex::Autolock autoLock(mLock);
-
-    for (;;) {
-        for (MediaBuffer *buffer = mFirstBuffer;
-             buffer != NULL; buffer = buffer->nextBuffer()) {
-            if (buffer->refcount() == 0) {
-                buffer->add_ref();
-                buffer->reset();
-
-                *out = buffer;
-                goto exit;
-            }
-        }
-
-        if (nonBlocking) {
-            *out = NULL;
-            return WOULD_BLOCK;
-        }
-
-        // All buffers are in use. Block until one of them is returned to us.
-        mCondition.wait(mLock);
-    }
-
-exit:
-    return OK;
-}
-
-void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
-    Mutex::Autolock autoLock(mLock);
-    mCondition.signal();
-}
-
-}  // namespace android
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index e17534f..711601f 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -16,6 +16,7 @@
     AStringUtils.cpp              \
     AWakeLock.cpp                 \
     MediaBuffer.cpp               \
+    MediaBufferGroup.cpp          \
     MetaData.cpp                  \
     ParsedMessage.cpp             \
     base64.cpp                    \
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index d83a351..fa8e241 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -30,9 +30,6 @@
 
 namespace android {
 
-// allocations larger than this will use shared memory
-static const size_t kSharedMemThreshold = 64 * 1024;
-
 MediaBuffer::MediaBuffer(void *data, size_t size)
     : mObserver(NULL),
       mNextBuffer(NULL),
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
new file mode 100644
index 0000000..9022324
--- /dev/null
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaBufferGroup"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+
+namespace android {
+
+MediaBufferGroup::MediaBufferGroup()
+    : mFirstBuffer(NULL),
+      mLastBuffer(NULL) {
+}
+
+MediaBufferGroup::~MediaBufferGroup() {
+    MediaBuffer *next;
+    for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
+         buffer = next) {
+        next = buffer->nextBuffer();
+
+        CHECK_EQ(buffer->refcount(), 0);
+
+        buffer->setObserver(NULL);
+        buffer->release();
+    }
+}
+
+void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    buffer->setObserver(this);
+
+    if (mLastBuffer) {
+        mLastBuffer->setNextBuffer(buffer);
+    } else {
+        mFirstBuffer = buffer;
+    }
+
+    mLastBuffer = buffer;
+}
+
+status_t MediaBufferGroup::acquire_buffer(
+        MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
+    Mutex::Autolock autoLock(mLock);
+
+    for (;;) {
+        MediaBuffer *freeBuffer = NULL;
+        MediaBuffer *freeBufferPrevious = NULL;
+        MediaBuffer *buffer = NULL;
+        MediaBuffer *bufferPrevious = NULL;
+        size_t smallest = requestedSize;
+        for (buffer = mFirstBuffer;
+             buffer != NULL; buffer = buffer->nextBuffer()) {
+            if (buffer->refcount() == 0) {
+               if (buffer->size() >= requestedSize) {
+                   break;
+               } else if (buffer->size() < smallest) {
+                   freeBuffer = buffer;
+                   freeBufferPrevious = bufferPrevious;
+               }
+            }
+            bufferPrevious = buffer;
+        }
+
+        if (buffer == NULL && freeBuffer != NULL) {
+            ALOGV("allocate new buffer, requested size %zu vs available %zu",
+                    requestedSize, freeBuffer->size());
+            size_t allocateSize = requestedSize;
+            if (requestedSize < SIZE_MAX / 3) {
+                allocateSize = requestedSize * 3 / 2;
+            }
+            MediaBuffer *newBuffer = new MediaBuffer(allocateSize);
+            newBuffer->setObserver(this);
+            if (freeBuffer == mFirstBuffer) {
+                mFirstBuffer = newBuffer;
+            }
+            if (freeBuffer == mLastBuffer) {
+                mLastBuffer = newBuffer;
+            }
+            newBuffer->setNextBuffer(freeBuffer->nextBuffer());
+            if (freeBufferPrevious != NULL) {
+                freeBufferPrevious->setNextBuffer(newBuffer);
+            }
+            freeBuffer->setObserver(NULL);
+            freeBuffer->release();
+
+            buffer = newBuffer;
+        }
+
+        if (buffer != NULL) {
+            buffer->add_ref();
+            buffer->reset();
+
+            *out = buffer;
+            goto exit;
+        }
+
+        if (nonBlocking) {
+            *out = NULL;
+            return WOULD_BLOCK;
+        }
+
+        // All buffers are in use. Block until one of them is returned to us.
+        mCondition.wait(mLock);
+    }
+
+exit:
+    return OK;
+}
+
+void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+    Mutex::Autolock autoLock(mLock);
+    mCondition.signal();
+}
+
+}  // namespace android