Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b65990f4 authored by Marco Nelissen's avatar Marco Nelissen
Browse files

MediaSource: use shared memory for transferring larger buffers

For small buffers we still copy the data as part of the binder transaction,
since that saves one extra binder call, but for buffers > 64KB we now use
shared memory.

Change-Id: I19aad7ea7f6104991b9b6b4f24ea880b54f739be
parent c95f046e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#include <pthread.h>

#include <binder/MemoryDealer.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>

@@ -93,6 +94,8 @@ protected:
private:
    friend class MediaBufferGroup;
    friend class OMXDecoder;
    friend class BnMediaSource;
    friend class BpMediaSource;

    // For use by OMXDecoder, reference count must be 1, drop reference
    // count to 0 without signalling the observer.
@@ -118,6 +121,7 @@ private:

    MediaBuffer(const MediaBuffer &);
    MediaBuffer &operator=(const MediaBuffer &);
    sp<IMemory> mMemory;
};

}  // namespace android
+1 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

#define LOG_NDEBUG 0
//#define LOG_NDEBUG 0
#define LOG_TAG "BpMediaExtractor"
#include <utils/Log.h>

+103 −12
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define LOG_TAG "BpMediaSource"
#include <utils/Log.h>

#include <inttypes.h>
#include <stdint.h>
#include <sys/types.h>

@@ -34,9 +35,69 @@ enum {
    STOP,
    PAUSE,
    GETFORMAT,
    READ
    READ,
    RELEASE_BUFFER
};

enum {
    NULL_BUFFER,
    SHARED_BUFFER,
    INLINE_BUFFER
};

class RemoteMediaBufferReleaser : public BBinder {
public:
    RemoteMediaBufferReleaser(MediaBuffer *buf) {
        mBuf = buf;
    }
    ~RemoteMediaBufferReleaser() {
        if (mBuf) {
            ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer");
            mBuf->release();
        }
    }
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0) {
        if (code == RELEASE_BUFFER) {
            mBuf->release();
            mBuf = NULL;
            return OK;
        } else {
            return BBinder::onTransact(code, data, reply, flags);
        }
    }
private:
    MediaBuffer *mBuf;
};


class RemoteMediaBufferWrapper : public MediaBuffer {
public:
    RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source);
protected:
    virtual ~RemoteMediaBufferWrapper();
private:
    sp<IMemory> mMemory;
    sp<IBinder> mRemoteSource;
};

RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source)
: MediaBuffer(mem->pointer(), mem->size()) {
    mMemory = mem;
    mRemoteSource = source;
}

RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() {
    mMemory.clear();
    // Explicitly ask the remote side to release the buffer. We could also just clear
    // mRemoteSource, but that doesn't immediately release the reference on the remote side.
    Parcel data, reply;
    mRemoteSource->transact(RELEASE_BUFFER, data, &reply);
    mRemoteSource.clear();
}

class BpMediaSource : public BpInterface<IMediaSource> {
public:
    BpMediaSource(const sp<IBinder>& impl)
@@ -94,13 +155,26 @@ public:
            return ret;
        }
        // wrap the returned data in a MediaBuffer
        // XXX use a group, and use shared memory for transfer
        ret = reply.readInt32();
        int32_t len = reply.readInt32();
        if (len < 0) {
            ALOGV("got status %d and len %d, returning NULL buffer", ret, len);
        int32_t buftype = reply.readInt32();
        if (buftype == SHARED_BUFFER) {
            sp<IBinder> remote = reply.readStrongBinder();
            sp<IBinder> binder = reply.readStrongBinder();
            sp<IMemory> mem = interface_cast<IMemory>(binder);
            if (mem == NULL) {
                ALOGE("received NULL IMemory for shared buffer");
            }
            size_t offset = reply.readInt32();
            size_t length = reply.readInt32();
            MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote);
            buf->set_range(offset, length);
            buf->meta_data()->updateFromParcel(reply);
            *buffer = buf;
        } else if (buftype == NULL_BUFFER) {
            ALOGV("got status %d and NULL buffer", ret);
            *buffer = NULL;
        } else {
            int32_t len = reply.readInt32();
            ALOGV("got status %d and len %d", ret, len);
            *buffer = new MediaBuffer(len);
            reply.read((*buffer)->data(), len);
@@ -183,17 +257,34 @@ status_t BnMediaSource::onTransact(
            } else {
                ret = read(&buf, NULL);
            }
            // return data inside binder for now
            // XXX return data using shared memory

            reply->writeInt32(ret);
            if (buf != NULL) {
                ALOGV("ret %d, buflen %zu", ret, buf->range_length());
                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);
                    reply->writeInt32(SHARED_BUFFER);
                    RemoteMediaBufferReleaser *wrapper = new RemoteMediaBufferReleaser(buf);
                    reply->writeStrongBinder(wrapper);
                    reply->writeStrongBinder(IInterface::asBinder(buf->mMemory));
                    reply->writeInt32(buf->range_offset());
                    reply->writeInt32(usedSize);
                    buf->meta_data()->writeToParcel(*reply);
                } else {
                    // buffer is not in shared memory, or is small: copy it
                    if (buf->mMemory != NULL) {
                        ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length());
                    }
                    reply->writeInt32(INLINE_BUFFER);
                    reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
                    buf->meta_data()->writeToParcel(*reply);
                    buf->release();
                }
            } else {
                ALOGV("ret %d, buf %p", ret, buf);
                reply->writeInt32(-1);
                reply->writeInt32(NULL_BUFFER);
            }
            return NO_ERROR;
        }
+21 −2
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@

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),
@@ -47,13 +50,29 @@ MediaBuffer::MediaBuffer(size_t size)
    : mObserver(NULL),
      mNextBuffer(NULL),
      mRefCount(0),
      mData(malloc(size)),
      mData(NULL),
      mSize(size),
      mRangeOffset(0),
      mRangeLength(size),
      mOwnsData(true),
      mMetaData(new MetaData),
      mOriginal(NULL) {
    if (size < kSharedMemThreshold) {
        mData = malloc(size);
    } else {
        sp<MemoryDealer> memoryDealer = new MemoryDealer(size, "MediaBuffer");
        mMemory = memoryDealer->allocate(size);
        if (mMemory == NULL) {
            ALOGW("Failed to allocate shared memory, trying regular allocation!");
            mData = malloc(size);
            if (mData == NULL) {
                ALOGE("Out of memory");
            }
        } else {
            mData = mMemory->pointer();
            ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData);
        }
    }
}

MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
@@ -158,7 +177,7 @@ void MediaBuffer::reset() {
MediaBuffer::~MediaBuffer() {
    CHECK(mObserver == NULL);

    if (mOwnsData && mData != NULL) {
    if (mOwnsData && mData != NULL && mMemory == NULL) {
        free(mData);
        mData = NULL;
    }