Merge "Added MediaSourceSplitter to split single source to multiple ones."
This commit is contained in:
193
include/media/stagefright/MediaSourceSplitter.h
Normal file
193
include/media/stagefright/MediaSourceSplitter.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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
media/libstagefright/MediaSourceSplitter.cpp
Normal file
229
media/libstagefright/MediaSourceSplitter.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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
|
||||
Reference in New Issue
Block a user