/* * 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 #include #include #include #include namespace android { MediaSourceSplitter::MediaSourceSplitter(sp mediaSource) { mNumberOfClients = 0; mSource = mediaSource; mSourceStarted = false; mNumberOfClientsStarted = 0; mNumberOfCurrentReads = 0; mCurrentReadBit = 0; mLastReadCompleted = true; } MediaSourceSplitter::~MediaSourceSplitter() { } sp MediaSourceSplitter::createClient() { Mutex::Autolock autoLock(mLock); sp client = new Client(this, mNumberOfClients++); mClientsStarted.push(false); mClientsDesiredReadBit.push(0); return client; } status_t MediaSourceSplitter::start(int clientId, MetaData *params) { Mutex::Autolock autoLock(mLock); LOGV("start client (%d)", clientId); if (mClientsStarted[clientId]) { return OK; } mNumberOfClientsStarted++; if (!mSourceStarted) { LOGV("Starting real source from client (%d)", clientId); status_t err = mSource->start(params); if (err == OK) { mSourceStarted = true; mClientsStarted.editItemAt(clientId) = true; mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit; } return err; } else { mClientsStarted.editItemAt(clientId) = true; if (mLastReadCompleted) { // Last read was completed. So join in the threads for the next read. mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit; } else { // Last read is ongoing. So join in the threads for the current read. mClientsDesiredReadBit.editItemAt(clientId) = mCurrentReadBit; } return OK; } } status_t MediaSourceSplitter::stop(int clientId) { Mutex::Autolock autoLock(mLock); LOGV("stop client (%d)", clientId); CHECK(clientId >= 0 && clientId < mNumberOfClients); CHECK(mClientsStarted[clientId]); if (--mNumberOfClientsStarted == 0) { LOGV("Stopping real source from client (%d)", clientId); status_t err = mSource->stop(); mSourceStarted = false; mClientsStarted.editItemAt(clientId) = false; return err; } else { mClientsStarted.editItemAt(clientId) = false; if (!mLastReadCompleted && (mClientsDesiredReadBit[clientId] == mCurrentReadBit)) { // !mLastReadCompleted implies that buffer has been read from source, but all // clients haven't read it. // mClientsDesiredReadBit[clientId] == mCurrentReadBit implies that this // client would have wanted to read from this buffer. (i.e. it has not yet // called read() for the current read buffer.) // Since other threads may be waiting for all the clients' reads to complete, // signal that this read has been aborted. signalReadComplete_lock(true); } return OK; } } sp MediaSourceSplitter::getFormat(int clientId) { Mutex::Autolock autoLock(mLock); LOGV("getFormat client (%d)", clientId); return mSource->getFormat(); } status_t MediaSourceSplitter::read(int clientId, MediaBuffer **buffer, const MediaSource::ReadOptions *options) { Mutex::Autolock autoLock(mLock); CHECK(clientId >= 0 && clientId < mNumberOfClients); LOGV("read client (%d)", clientId); *buffer = NULL; if (!mClientsStarted[clientId]) { return OK; } if (mCurrentReadBit != mClientsDesiredReadBit[clientId]) { // Desired buffer has not been read from source yet. // If the current client is the special client with clientId = 0 // then read from source, else wait until the client 0 has finished // reading from source. if (clientId == 0) { // Wait for all client's last read to complete first so as to not // corrupt the buffer at mLastReadMediaBuffer. waitForAllClientsLastRead_lock(clientId); readFromSource_lock(options); *buffer = mLastReadMediaBuffer; } else { waitForReadFromSource_lock(clientId); *buffer = mLastReadMediaBuffer; (*buffer)->add_ref(); } CHECK(mCurrentReadBit == mClientsDesiredReadBit[clientId]); } else { // Desired buffer has already been read from source. Use the cached data. CHECK(clientId != 0); *buffer = mLastReadMediaBuffer; (*buffer)->add_ref(); } mClientsDesiredReadBit.editItemAt(clientId) = !mClientsDesiredReadBit[clientId]; 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 clientId) { mReadFromSourceCondition.wait(mLock); } void MediaSourceSplitter::waitForAllClientsLastRead_lock(int32_t clientId) { 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 clientId) { return ERROR_UNSUPPORTED; } // Client MediaSourceSplitter::Client::Client( sp splitter, int32_t clientId) { mSplitter = splitter; mClientId = clientId; } status_t MediaSourceSplitter::Client::start(MetaData *params) { return mSplitter->start(mClientId, params); } status_t MediaSourceSplitter::Client::stop() { return mSplitter->stop(mClientId); } sp MediaSourceSplitter::Client::getFormat() { return mSplitter->getFormat(mClientId); } status_t MediaSourceSplitter::Client::read( MediaBuffer **buffer, const ReadOptions *options) { return mSplitter->read(mClientId, buffer, options); } status_t MediaSourceSplitter::Client::pause() { return mSplitter->pause(mClientId); } } // namespace android