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

Commit 83efe020 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "IMediaSource: Improve shared memory buffer transfer" into nyc-mr1-dev

parents 87cbfa8c f59c0baf
Loading
Loading
Loading
Loading
+96 −10
Original line number Diff line number Diff line
@@ -18,14 +18,17 @@

#define IMEDIA_SOURCE_BASE_H_

#include <map>

#include <binder/IInterface.h>
#include <binder/IMemory.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaErrors.h>

namespace android {

struct MediaSource;
class MetaData;
class MediaBuffer;
class MediaBufferGroup;

class IMediaSource : public IInterface {
@@ -56,7 +59,7 @@ public:
    // a) not request a seek
    // b) not be late, i.e. lateness_us = 0
    struct ReadOptions {
        enum SeekMode {
        enum SeekMode : int32_t {
            SEEK_PREVIOUS_SYNC,
            SEEK_NEXT_SYNC,
            SEEK_CLOSEST_SYNC,
@@ -72,6 +75,7 @@ public:
        void clearSeekTo();
        bool getSeekTo(int64_t *time_us, SeekMode *mode) const;

        // TODO: remove this if unused.
        void setLateBy(int64_t lateness_us);
        int64_t getLateBy() const;

@@ -79,6 +83,11 @@ public:
        void clearNonBlocking();
        bool getNonBlocking() const;

        // Used to clear all non-persistent options for multiple buffer reads.
        void clearNonPersistent() {
            clearSeekTo();
        }

    private:
        enum Options {
            kSeekTo_Option      = 1,
@@ -98,21 +107,26 @@ public:
    // A result of INFO_FORMAT_CHANGED indicates that the format of this
    // MediaSource has changed mid-stream, the client can continue reading
    // but should be prepared for buffers of the new configuration.
    //
    // TODO: consider removing read() in favor of readMultiple().
    virtual status_t read(
            MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;

    // Returns a vector of new buffers of data. The vector size could be
    // <= |maxNumBuffers|. Used for buffers with small size
    // since all buffer data are passed back by binder, not shared memory.
    // Returns a vector of new buffers of data, where the new buffers are added
    // to the end of the vector.
    // Call blocks until an error is encountered, or the end of the stream is
    // reached, or format change is hit, or |kMaxNumReadMultiple| buffers have
    // been read.
    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
    // End of stream is signaled by a result of ERROR_END_OF_STREAM.
    // A result of INFO_FORMAT_CHANGED indicates that the format of this
    // MediaSource has changed mid-stream, the client can continue reading
    // but should be prepared for buffers of the new configuration.
    //
    // ReadOptions may be specified. Persistent options apply to all reads;
    // non-persistent options (e.g. seek) apply only to the first read.
    virtual status_t readMultiple(
            Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1) = 0;
            Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1,
            const ReadOptions *options = nullptr) = 0;

    // Returns true if |readMultiple| is supported, otherwise false.
    virtual bool supportReadMultiple() = 0;
@@ -148,20 +162,92 @@ public:
    }

    virtual status_t readMultiple(
            Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */) {
            Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */,
            const ReadOptions * /* options = nullptr */) {
        return ERROR_UNSUPPORTED;
    }

    virtual bool supportReadMultiple() {
        return false;
    }

    static const size_t kBinderMediaBuffers = 4; // buffers managed by BnMediaSource

protected:
    virtual ~BnMediaSource();

private:
    MediaBufferGroup *mGroup;
};
    uint32_t mBuffersSinceStop; // Buffer tracking variable

    std::unique_ptr<MediaBufferGroup> mGroup;

    // To prevent marshalling IMemory with each read transaction, we cache the IMemory pointer
    // into a map.
    //
    // This is converted into an index, which is used to identify the associated memory
    // on the receiving side.  We hold a reference to the IMemory here to ensure it doesn't
    // change underneath us.

    struct IndexCache {
        IndexCache() : mIndex(0) { }

        // Returns the index of the IMemory stored in cache or 0 if not found.
        uint64_t lookup(const sp<IMemory> &mem) {
            auto p = mMemoryToIndex.find(mem.get());
            if (p == mMemoryToIndex.end()) {
                return 0;
            }
            if (MediaBuffer::isDeadObject(p->second.first)) {
                // this object's dead
                ALOGW("Attempting to lookup a dead IMemory");
                (void)mMemoryToIndex.erase(p);
                return 0;
            }
            ALOGW_IF(p->second.first.get() != mem.get(), "Mismatched buffers without reset");
            return p->second.second;
        }

        // Returns the index of the IMemory stored in the index cache.
        uint64_t insert(const sp<IMemory> &mem) {
            auto p = mMemoryToIndex.find(mem.get());
            if (p == mMemoryToIndex.end()) {
                if (mIndex == UINT64_MAX) {
                    ALOGE("Index overflow");
                    mIndex = 1; // skip overflow condition and hope for the best
                } else {
                    ++mIndex;
                }
                (void)mMemoryToIndex.emplace(// C++11 mem.get(), std::make_pair(mem, mIndex))
                        std::piecewise_construct,
                        std::forward_as_tuple(mem.get()), std::forward_as_tuple(mem, mIndex));
                return mIndex;
            }
            ALOGW("IMemory already inserted into cache");
            return p->second.second;
        }

        void reset() {
            mMemoryToIndex.clear();
            mIndex = 0;
        }

        void gc() {
            for (auto it = mMemoryToIndex.begin(); it != mMemoryToIndex.end(); ) {
                if (MediaBuffer::isDeadObject(it->second.first)) {
                    it = mMemoryToIndex.erase(it);
                } else {
                    ++it;
                }
            }
        }

    private:
        uint64_t mIndex;
        // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
        // Could key on uintptr_t instead of IMemory *
        std::map<IMemory *, std::pair<sp<IMemory>, uint64_t>> mMemoryToIndex;
    } mIndexCache;
};

}  // namespace android

+92 −5
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@

#define MEDIA_BUFFER_H_

#include <atomic>
#include <list>
#include <media/stagefright/foundation/MediaBufferBase.h>

#include <pthread.h>
@@ -60,6 +62,12 @@ public:

    MediaBuffer(const sp<ABuffer> &buffer);

    MediaBuffer(const sp<IMemory> &mem) :
        MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) {
        // delegate and override mMemory
        mMemory = mem;
    }

    // Decrements the reference count and returns the buffer to its
    // associated MediaBufferGroup if the reference count drops to 0.
    virtual void release();
@@ -91,9 +99,44 @@ public:

    int refcount() const;

    bool isDeadObject() const {
        return isDeadObject(mMemory);
    }

    static bool isDeadObject(const sp<IMemory> &memory) {
        if (memory.get() == nullptr || memory->pointer() == nullptr) return false;
        return reinterpret_cast<SharedControl *>(memory->pointer())->isDeadObject();
    }

protected:
    // MediaBuffer remote releases are handled through a
    // pending release count variable stored in a SharedControl block
    // at the start of the IMemory.

    // Returns old value of pending release count.
    inline int32_t addPendingRelease(int32_t value) {
        return getSharedControl()->addPendingRelease(value);
    }

    // Issues all pending releases (works in parallel).
    // Assumes there is a MediaBufferObserver.
    inline void resolvePendingRelease() {
        if (mMemory.get() == nullptr) return;
        while (addPendingRelease(-1) > 0) {
            release();
        }
        addPendingRelease(1);
    }

    // true if MediaBuffer is observed (part of a MediaBufferGroup).
    inline bool isObserved() const {
        return mObserver != nullptr;
    }

    virtual ~MediaBuffer();

    sp<IMemory> mMemory;

private:
    friend class MediaBufferGroup;
    friend class OMXDecoder;
@@ -105,7 +148,6 @@ private:
    void claim();

    MediaBufferObserver *mObserver;
    MediaBuffer *mNextBuffer;
    int mRefCount;

    void *mData;
@@ -119,12 +161,57 @@ private:

    MediaBuffer *mOriginal;

    void setNextBuffer(MediaBuffer *buffer);
    MediaBuffer *nextBuffer();

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

    // SharedControl block at the start of IMemory.
    struct SharedControl {
        enum {
            FLAG_DEAD_OBJECT = (1 << 0),
        };

        // returns old value
        inline int32_t addPendingRelease(int32_t value) {
            return std::atomic_fetch_add_explicit(
                    &mPendingRelease, (int_least32_t)value, std::memory_order_seq_cst);
        }

        inline int32_t getPendingRelease() const {
            return std::atomic_load_explicit(&mPendingRelease, std::memory_order_seq_cst);
        }

        inline void setPendingRelease(int32_t value) {
            std::atomic_store_explicit(
                    &mPendingRelease, (int_least32_t)value, std::memory_order_seq_cst);
        }

        inline bool isDeadObject() const {
            return (std::atomic_load_explicit(
                    &mFlags, std::memory_order_seq_cst) & FLAG_DEAD_OBJECT) != 0;
        }

        inline void setDeadObject() {
            (void)std::atomic_fetch_or_explicit(
                    &mFlags, (int_least32_t)FLAG_DEAD_OBJECT, std::memory_order_seq_cst);
        }

        inline void clear() {
            std::atomic_store_explicit(
                    &mFlags, (int_least32_t)0, std::memory_order_seq_cst);
            std::atomic_store_explicit(
                    &mPendingRelease, (int_least32_t)0, std::memory_order_seq_cst);
        }

    private:
        // Caution: atomic_int_fast32_t is 64 bits on LP64.
        std::atomic_int_least32_t mFlags;
        std::atomic_int_least32_t mPendingRelease;
        int32_t unused[6]; // additional buffer space
    };

    inline SharedControl *getSharedControl() const {
         return reinterpret_cast<SharedControl *>(mMemory->pointer());
     }
};

}  // namespace android
+8 −3
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ class MetaData;

class MediaBufferGroup : public MediaBufferObserver {
public:
    MediaBufferGroup();
    MediaBufferGroup(size_t growthLimit = 0);
    ~MediaBufferGroup();

    void add_buffer(MediaBuffer *buffer);
@@ -45,6 +45,11 @@ public:
    status_t acquire_buffer(
            MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);

    size_t buffers() const { return mBuffers.size(); }

    // freeBuffers is the number of free buffers allowed to remain.
    void gc(size_t freeBuffers = 0);

protected:
    virtual void signalBufferReturned(MediaBuffer *buffer);

@@ -53,8 +58,8 @@ private:

    Mutex mLock;
    Condition mCondition;

    MediaBuffer *mFirstBuffer, *mLastBuffer;
    size_t mGrowthLimit;  // Do not automatically grow group larger than this.
    std::list<MediaBuffer *> mBuffers;

    MediaBufferGroup(const MediaBufferGroup &);
    MediaBufferGroup &operator=(const MediaBufferGroup &);
+201 −196

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -1444,7 +1444,7 @@ void NuPlayer::GenericSource::readBuffer(
            }
        }

        options.clearSeekTo();
        options.clearNonPersistent();

        size_t id = 0;
        size_t count = mediaBuffers.size();
Loading