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

Commit cf66e47d authored by Nipun Kwatra's avatar Nipun Kwatra Committed by Android (Google) Code Review
Browse files

Merge "Added MediaSourceSplitter to split single source to multiple ones."

parents 3fec2bf9 f83cba77
Loading
Loading
Loading
Loading
+193 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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.
 */

// This class provides a way to split a single media source into multiple sources.
// The constructor takes in the real mediaSource and createClient() can then be
// used to create multiple sources served from this real mediaSource.
//
// Usage:
// - Create MediaSourceSplitter by passing in a real mediaSource from which
// multiple duplicate channels are needed.
// - Create a client using createClient() and use it as any other mediaSource.
//
// Note that multiple clients can be created using createClient() and
// started/stopped in any order. MediaSourceSplitter stops the real source only
// when all clients have been stopped.
//
// If a new client is created/started after some existing clients have already
// started, the new client will start getting its read frames from the current
// time.

#ifndef MEDIA_SOURCE_SPLITTER_H_

#define MEDIA_SOURCE_SPLITTER_H_

#include <media/stagefright/MediaSource.h>
#include <utils/threads.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>

namespace android {

class MediaBuffer;
class MetaData;

class MediaSourceSplitter : public RefBase {
public:
    // Constructor
    // mediaSource: The real mediaSource. The class keeps a reference to it to
    // implement the various clients.
    MediaSourceSplitter(sp<MediaSource> mediaSource);

    ~MediaSourceSplitter();

    // Creates a new client of base type MediaSource. Multiple clients can be
    // created which get their data through the same real mediaSource. These
    // clients can then be used like any other MediaSource, all of which provide
    // data from the same real source.
    sp<MediaSource> createClient();

private:
    // Total number of clients created through createClient().
    int32_t mNumberOfClients;

    // reference to the real MediaSource passed to the constructor.
    sp<MediaSource> mSource;

    // Stores pointer to the MediaBuffer read from the real MediaSource.
    // All clients use this to implement the read() call.
    MediaBuffer *mLastReadMediaBuffer;

    // Status code for read from the real MediaSource. All clients return
    // this for their read().
    status_t mLastReadStatus;

    // Boolean telling whether the real MediaSource has started.
    bool mSourceStarted;

    // List of booleans, one for each client, storing whether the corresponding
    // client's start() has been called.
    Vector<bool> mClientsStarted;

    // Stores the number of clients which are currently started.
    int32_t mNumberOfClientsStarted;

    // Since different clients call read() asynchronously, we need to keep track
    // of what data is currently read into the mLastReadMediaBuffer.
    // mCurrentReadBit stores the bit for the current read buffer. This bit
    // flips each time a new buffer is read from the source.
    // mClientsDesiredReadBit stores the bit for the next desired read buffer
    // for each client. This bit flips each time read() is completed for this
    // client.
    bool mCurrentReadBit;
    Vector<bool> mClientsDesiredReadBit;

    // Number of clients whose current read has been completed.
    int32_t mNumberOfCurrentReads;

    // Boolean telling whether the last read has been completed for all clients.
    // The variable is reset to false each time buffer is read from the real
    // source.
    bool mLastReadCompleted;

    // A global mutex for access to critical sections.
    Mutex mLock;

    // Condition variable for waiting on read from source to complete.
    Condition mReadFromSourceCondition;

    // Condition variable for waiting on all client's last read to complete.
    Condition mAllReadsCompleteCondition;

    // Functions used by Client to implement the MediaSource interface.

    // If the real source has not been started yet by any client, starts it.
    status_t start(int client_id, MetaData *params);

    // Stops the real source after all clients have called stop().
    status_t stop(int client_id);

    // returns the real source's getFormat().
    sp<MetaData> getFormat(int client_id);

    // If the client's desired buffer has already been read into
    // mLastReadMediaBuffer, points the buffer to that. Otherwise if it is the
    // master client, reads the buffer from source or else waits for the master
    // client to read the buffer and uses that.
    status_t read(int client_id,
            MediaBuffer **buffer, const MediaSource::ReadOptions *options = NULL);

    // Not implemented right now.
    status_t pause(int client_id);

    // Function which reads a buffer from the real source into
    // mLastReadMediaBuffer
    void readFromSource_lock(const MediaSource::ReadOptions *options);

    // Waits until read from the real source has been completed.
    // _lock means that the function should be called when the thread has already
    // obtained the lock for the mutex mLock.
    void waitForReadFromSource_lock(int32_t client_id);

    // Waits until all clients have read the current buffer in
    // mLastReadCompleted.
    void waitForAllClientsLastRead_lock(int32_t client_id);

    // Each client calls this after it completes its read(). Once all clients
    // have called this for the current buffer, the function calls
    // mAllReadsCompleteCondition.broadcast() to signal the waiting clients.
    void signalReadComplete_lock(bool readAborted);

    // Make these constructors private.
    MediaSourceSplitter();
    MediaSourceSplitter(const MediaSourceSplitter &);
    MediaSourceSplitter &operator=(const MediaSourceSplitter &);

    // This class implements the MediaSource interface. Each client stores a
    // reference to the parent MediaSourceSplitter and uses it to complete the
    // various calls.
    class Client : public MediaSource {
    public:
        // Constructor stores reference to the parent MediaSourceSplitter and it
        // client id.
        Client(sp<MediaSourceSplitter> splitter, int32_t client_id);

        // MediaSource interface
        virtual status_t start(MetaData *params = NULL);

        virtual status_t stop();

        virtual sp<MetaData> getFormat();

        virtual status_t read(
                MediaBuffer **buffer, const ReadOptions *options = NULL);

        virtual status_t pause();

    private:
        // Refernce to the parent MediaSourceSplitter
        sp<MediaSourceSplitter> mSplitter;

        // Id of this client.
        int32_t mClient_id;
    };

    friend class Client;
};

}  // namespace android

#endif  // MEDIA_SOURCE_SPLITTER_H_
+229 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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_NDEBUG 0
#define LOG_TAG "MediaSourceSplitter"
#include <utils/Log.h>

#include <media/stagefright/MediaSourceSplitter.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>

namespace android {

MediaSourceSplitter::MediaSourceSplitter(sp<MediaSource> mediaSource) {
    mNumberOfClients = 0;
    mSource = mediaSource;
    mSourceStarted = false;

    mNumberOfClientsStarted = 0;
    mNumberOfCurrentReads = 0;
    mCurrentReadBit = 0;
    mLastReadCompleted = true;
}

MediaSourceSplitter::~MediaSourceSplitter() {
}

sp<MediaSource> MediaSourceSplitter::createClient() {
    Mutex::Autolock autoLock(mLock);

    sp<MediaSource> client = new Client(this, mNumberOfClients++);
    mClientsStarted.push(false);
    mClientsDesiredReadBit.push(0);
    return client;
}

status_t MediaSourceSplitter::start(int client_id, MetaData *params) {
    Mutex::Autolock autoLock(mLock);

    LOGV("start client (%d)", client_id);
    if (mClientsStarted[client_id]) {
        return OK;
    }

    mNumberOfClientsStarted++;

    if (!mSourceStarted) {
        LOGV("Starting real source from client (%d)", client_id);
        status_t err = mSource->start(params);

        if (err == OK) {
            mSourceStarted = true;
            mClientsStarted.editItemAt(client_id) = true;
            mClientsDesiredReadBit.editItemAt(client_id) = !mCurrentReadBit;
        }

        return err;
    } else {
        mClientsStarted.editItemAt(client_id) = true;
        if (mLastReadCompleted) {
            // Last read was completed. So join in the threads for the next read.
            mClientsDesiredReadBit.editItemAt(client_id) = !mCurrentReadBit;
        } else {
            // Last read is ongoing. So join in the threads for the current read.
            mClientsDesiredReadBit.editItemAt(client_id) = mCurrentReadBit;
        }
        return OK;
    }
}

status_t MediaSourceSplitter::stop(int client_id) {
    Mutex::Autolock autoLock(mLock);

    LOGV("stop client (%d)", client_id);
    CHECK(client_id >= 0 && client_id < mNumberOfClients);
    CHECK(mClientsStarted[client_id]);

    if (--mNumberOfClientsStarted == 0) {
        LOGV("Stopping real source from client (%d)", client_id);
        status_t err = mSource->stop();
        mSourceStarted = false;
        mClientsStarted.editItemAt(client_id) = false;
        return err;
    } else {
        mClientsStarted.editItemAt(client_id) = false;
        if (!mLastReadCompleted) {
            // Other threads may be waiting for all the reads to complete.
            // Signal that the read has been aborted.
            signalReadComplete_lock(true);
        }
        return OK;
    }
}

sp<MetaData> MediaSourceSplitter::getFormat(int client_id) {
    Mutex::Autolock autoLock(mLock);

    LOGV("getFormat client (%d)", client_id);
    return mSource->getFormat();
}

status_t MediaSourceSplitter::read(int client_id,
        MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
    Mutex::Autolock autoLock(mLock);

    CHECK(client_id >= 0 && client_id < mNumberOfClients);

    LOGV("read client (%d)", client_id);
    *buffer = NULL;

    if (!mClientsStarted[client_id]) {
        return OK;
    }

    if (mCurrentReadBit != mClientsDesiredReadBit[client_id]) {
        // Desired buffer has not been read from source yet.

        // If the current client is the special client with client_id = 0
        // then read from source, else wait until the client 0 has finished
        // reading from source.
        if (client_id == 0) {
            // Wait for all client's last read to complete first so as to not
            // corrupt the buffer at mLastReadMediaBuffer.
            waitForAllClientsLastRead_lock(client_id);

            readFromSource_lock(options);
            *buffer = mLastReadMediaBuffer;
        } else {
            waitForReadFromSource_lock(client_id);

            *buffer = mLastReadMediaBuffer;
            (*buffer)->add_ref();
        }
        CHECK(mCurrentReadBit == mClientsDesiredReadBit[client_id]);
    } else {
        // Desired buffer has already been read from source. Use the cached data.
        CHECK(client_id != 0);

        *buffer = mLastReadMediaBuffer;
        (*buffer)->add_ref();
    }

    mClientsDesiredReadBit.editItemAt(client_id) = !mClientsDesiredReadBit[client_id];
    signalReadComplete_lock(false);

    return mLastReadStatus;
}

void MediaSourceSplitter::readFromSource_lock(const MediaSource::ReadOptions *options) {
    mLastReadStatus = mSource->read(&mLastReadMediaBuffer , options);

    mCurrentReadBit = !mCurrentReadBit;
    mLastReadCompleted = false;
    mReadFromSourceCondition.broadcast();
}

void MediaSourceSplitter::waitForReadFromSource_lock(int32_t client_id) {
    mReadFromSourceCondition.wait(mLock);
}

void MediaSourceSplitter::waitForAllClientsLastRead_lock(int32_t client_id) {
    if (mLastReadCompleted) {
        return;
    }
    mAllReadsCompleteCondition.wait(mLock);
    CHECK(mLastReadCompleted);
}

void MediaSourceSplitter::signalReadComplete_lock(bool readAborted) {
    if (!readAborted) {
        mNumberOfCurrentReads++;
    }

    if (mNumberOfCurrentReads == mNumberOfClientsStarted) {
        mLastReadCompleted = true;
        mNumberOfCurrentReads = 0;
        mAllReadsCompleteCondition.broadcast();
    }
}

status_t MediaSourceSplitter::pause(int client_id) {
    return ERROR_UNSUPPORTED;
}

// Client

MediaSourceSplitter::Client::Client(
        sp<MediaSourceSplitter> splitter,
        int32_t client_id) {
    mSplitter = splitter;
    mClient_id = client_id;
}

status_t MediaSourceSplitter::Client::start(MetaData *params) {
    return mSplitter->start(mClient_id, params);
}

status_t MediaSourceSplitter::Client::stop() {
    return mSplitter->stop(mClient_id);
}

sp<MetaData> MediaSourceSplitter::Client::getFormat() {
    return mSplitter->getFormat(mClient_id);
}

status_t MediaSourceSplitter::Client::read(
        MediaBuffer **buffer, const ReadOptions *options) {
    return mSplitter->read(mClient_id, buffer, options);
}

status_t MediaSourceSplitter::Client::pause() {
    return mSplitter->pause(mClient_id);
}

}  // namespace android