Squashed commit of the following:
commit 427e927298449826bb5b98327b0c05957aa051e6 Author: Andreas Huber <andih@google.com> Date: Fri Feb 12 10:39:07 2010 -0800 Fixing a race condition in AwesomePlayer and support for suspend/resume. commit 96201a04b6657b6bd69ec6100f4de66aebcaa0b4 Author: Andreas Huber <andih@google.com> Date: Fri Feb 12 10:36:15 2010 -0800 Protect MPEG4Source's sanity by properly locking. related-to-bug: 2231576
This commit is contained in:
@@ -169,7 +169,8 @@ AwesomePlayer::AwesomePlayer()
|
||||
mAudioPlayer(NULL),
|
||||
mFlags(0),
|
||||
mLastVideoBuffer(NULL),
|
||||
mVideoBuffer(NULL) {
|
||||
mVideoBuffer(NULL),
|
||||
mSuspensionState(NULL) {
|
||||
CHECK_EQ(mClient.connect(), OK);
|
||||
|
||||
DataSource::RegisterDefaultSniffers();
|
||||
@@ -221,7 +222,11 @@ void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) {
|
||||
status_t AwesomePlayer::setDataSource(
|
||||
const char *uri, const KeyedVector<String8, String8> *headers) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
return setDataSource_l(uri, headers);
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::setDataSource_l(
|
||||
const char *uri, const KeyedVector<String8, String8> *headers) {
|
||||
reset_l();
|
||||
|
||||
mUri = uri;
|
||||
@@ -243,15 +248,22 @@ status_t AwesomePlayer::setDataSource(
|
||||
|
||||
reset_l();
|
||||
|
||||
sp<DataSource> source = new FileSource(fd, offset, length);
|
||||
sp<DataSource> dataSource = new FileSource(fd, offset, length);
|
||||
|
||||
status_t err = source->initCheck();
|
||||
status_t err = dataSource->initCheck();
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
sp<MediaExtractor> extractor = MediaExtractor::Create(source);
|
||||
mFileSource = dataSource;
|
||||
|
||||
return setDataSource_l(dataSource);
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::setDataSource_l(
|
||||
const sp<DataSource> &dataSource) {
|
||||
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
|
||||
|
||||
if (extractor == NULL) {
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -299,6 +311,26 @@ void AwesomePlayer::reset_l() {
|
||||
|
||||
cancelPlayerEvents();
|
||||
|
||||
if (mPrefetcher != NULL) {
|
||||
CHECK_EQ(mPrefetcher->getStrongCount(), 1);
|
||||
}
|
||||
mPrefetcher.clear();
|
||||
|
||||
// Shutdown audio first, so that the respone to the reset request
|
||||
// appears to happen instantaneously as far as the user is concerned
|
||||
// If we did this later, audio would continue playing while we
|
||||
// shutdown the video-related resources and the player appear to
|
||||
// not be as responsive to a reset request.
|
||||
mAudioSource.clear();
|
||||
|
||||
if (mTimeSource != mAudioPlayer) {
|
||||
delete mTimeSource;
|
||||
}
|
||||
mTimeSource = NULL;
|
||||
|
||||
delete mAudioPlayer;
|
||||
mAudioPlayer = NULL;
|
||||
|
||||
mVideoRenderer.clear();
|
||||
|
||||
if (mLastVideoBuffer) {
|
||||
@@ -325,16 +357,6 @@ void AwesomePlayer::reset_l() {
|
||||
IPCThreadState::self()->flushCommands();
|
||||
}
|
||||
|
||||
mAudioSource.clear();
|
||||
|
||||
if (mTimeSource != mAudioPlayer) {
|
||||
delete mTimeSource;
|
||||
}
|
||||
mTimeSource = NULL;
|
||||
|
||||
delete mAudioPlayer;
|
||||
mAudioPlayer = NULL;
|
||||
|
||||
mDurationUs = -1;
|
||||
mFlags = 0;
|
||||
mVideoWidth = mVideoHeight = -1;
|
||||
@@ -344,10 +366,13 @@ void AwesomePlayer::reset_l() {
|
||||
mSeeking = false;
|
||||
mSeekTimeUs = 0;
|
||||
|
||||
mPrefetcher.clear();
|
||||
|
||||
mUri.setTo("");
|
||||
mUriHeaders.clear();
|
||||
|
||||
mFileSource.clear();
|
||||
|
||||
delete mSuspensionState;
|
||||
mSuspensionState = NULL;
|
||||
}
|
||||
|
||||
void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
|
||||
@@ -403,7 +428,10 @@ void AwesomePlayer::onStreamDone() {
|
||||
|
||||
status_t AwesomePlayer::play() {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
return play_l();
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::play_l() {
|
||||
if (mFlags & PLAYING) {
|
||||
return OK;
|
||||
}
|
||||
@@ -579,7 +607,10 @@ status_t AwesomePlayer::getDuration(int64_t *durationUs) {
|
||||
|
||||
status_t AwesomePlayer::getPosition(int64_t *positionUs) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
return getPosition_l(positionUs);
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::getPosition_l(int64_t *positionUs) {
|
||||
if (mVideoSource != NULL) {
|
||||
*positionUs = mVideoTimeUs;
|
||||
} else if (mAudioPlayer != NULL) {
|
||||
@@ -697,7 +728,11 @@ status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {
|
||||
|
||||
void AwesomePlayer::onVideoEvent() {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
if (!mVideoEventPending) {
|
||||
// The event has been cancelled in reset_l() but had already
|
||||
// been scheduled for execution at that time.
|
||||
return;
|
||||
}
|
||||
mVideoEventPending = false;
|
||||
|
||||
if (mSeeking) {
|
||||
@@ -985,6 +1020,7 @@ void AwesomePlayer::onPrepareAsyncEvent() {
|
||||
|
||||
if (prefetcher != NULL) {
|
||||
prefetcher->prepare();
|
||||
prefetcher.clear();
|
||||
}
|
||||
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
@@ -1006,5 +1042,75 @@ void AwesomePlayer::onPrepareAsyncEvent() {
|
||||
mPreparedCondition.broadcast();
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::suspend() {
|
||||
LOGI("suspend");
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
if (mSuspensionState != NULL) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
while (mFlags & PREPARING) {
|
||||
mPreparedCondition.wait(mLock);
|
||||
}
|
||||
|
||||
SuspensionState *state = new SuspensionState;
|
||||
state->mUri = mUri;
|
||||
state->mUriHeaders = mUriHeaders;
|
||||
state->mFileSource = mFileSource;
|
||||
|
||||
state->mFlags = mFlags & (PLAYING | LOOPING);
|
||||
getPosition_l(&state->mPositionUs);
|
||||
|
||||
reset_l();
|
||||
|
||||
mSuspensionState = state;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::resume() {
|
||||
LOGI("resume");
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
if (mSuspensionState == NULL) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
SuspensionState *state = mSuspensionState;
|
||||
mSuspensionState = NULL;
|
||||
|
||||
status_t err;
|
||||
if (state->mFileSource != NULL) {
|
||||
err = setDataSource_l(state->mFileSource);
|
||||
|
||||
if (err == OK) {
|
||||
mFileSource = state->mFileSource;
|
||||
}
|
||||
} else {
|
||||
err = setDataSource_l(state->mUri, &state->mUriHeaders);
|
||||
}
|
||||
|
||||
if (err != OK) {
|
||||
delete state;
|
||||
state = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
seekTo_l(state->mPositionUs);
|
||||
|
||||
mFlags = state->mFlags & LOOPING;
|
||||
|
||||
if (state->mFlags & PLAYING) {
|
||||
play_l();
|
||||
}
|
||||
|
||||
delete state;
|
||||
state = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ protected:
|
||||
virtual ~MPEG4Source();
|
||||
|
||||
private:
|
||||
Mutex mLock;
|
||||
|
||||
sp<MetaData> mFormat;
|
||||
sp<DataSource> mDataSource;
|
||||
int32_t mTimescale;
|
||||
@@ -1300,6 +1302,8 @@ MPEG4Source::~MPEG4Source() {
|
||||
}
|
||||
|
||||
status_t MPEG4Source::start(MetaData *params) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
CHECK(!mStarted);
|
||||
|
||||
int32_t val;
|
||||
@@ -1325,6 +1329,8 @@ status_t MPEG4Source::start(MetaData *params) {
|
||||
}
|
||||
|
||||
status_t MPEG4Source::stop() {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
CHECK(mStarted);
|
||||
|
||||
if (mBuffer != NULL) {
|
||||
@@ -1345,6 +1351,8 @@ status_t MPEG4Source::stop() {
|
||||
}
|
||||
|
||||
sp<MetaData> MPEG4Source::getFormat() {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
@@ -1369,6 +1377,8 @@ size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
|
||||
|
||||
status_t MPEG4Source::read(
|
||||
MediaBuffer **out, const ReadOptions *options) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
CHECK(mStarted);
|
||||
|
||||
*out = NULL;
|
||||
@@ -1428,6 +1438,7 @@ status_t MPEG4Source::read(
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
CHECK(mBuffer != NULL);
|
||||
mBuffer->set_range(0, size);
|
||||
mBuffer->meta_data()->clear();
|
||||
mBuffer->meta_data()->setInt64(
|
||||
@@ -1461,8 +1472,10 @@ status_t MPEG4Source::read(
|
||||
}
|
||||
|
||||
MediaBuffer *clone = mBuffer->clone();
|
||||
CHECK(clone != NULL);
|
||||
clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);
|
||||
|
||||
CHECK(mBuffer != NULL);
|
||||
mBuffer->set_range(
|
||||
mBuffer->range_offset() + mNALLengthSize + nal_size,
|
||||
mBuffer->range_length() - mNALLengthSize - nal_size);
|
||||
@@ -1521,6 +1534,7 @@ status_t MPEG4Source::read(
|
||||
}
|
||||
CHECK_EQ(srcOffset, size);
|
||||
|
||||
CHECK(mBuffer != NULL);
|
||||
mBuffer->set_range(0, dstOffset);
|
||||
mBuffer->meta_data()->clear();
|
||||
mBuffer->meta_data()->setInt64(
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace android {
|
||||
|
||||
struct PrefetchedSource : public MediaSource {
|
||||
PrefetchedSource(
|
||||
const sp<Prefetcher> &prefetcher,
|
||||
size_t index,
|
||||
const sp<MediaSource> &source);
|
||||
|
||||
@@ -52,13 +51,13 @@ private:
|
||||
Mutex mLock;
|
||||
Condition mCondition;
|
||||
|
||||
sp<Prefetcher> mPrefetcher;
|
||||
sp<MediaSource> mSource;
|
||||
size_t mIndex;
|
||||
bool mStarted;
|
||||
bool mReachedEOS;
|
||||
int64_t mSeekTimeUs;
|
||||
int64_t mCacheDurationUs;
|
||||
bool mPrefetcherStopped;
|
||||
|
||||
List<MediaBuffer *> mCachedBuffers;
|
||||
|
||||
@@ -69,6 +68,7 @@ private:
|
||||
void clearCache_l();
|
||||
|
||||
void cacheMore();
|
||||
void onPrefetcherStopped();
|
||||
|
||||
PrefetchedSource(const PrefetchedSource &);
|
||||
PrefetchedSource &operator=(const PrefetchedSource &);
|
||||
@@ -88,7 +88,7 @@ sp<MediaSource> Prefetcher::addSource(const sp<MediaSource> &source) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
sp<PrefetchedSource> psource =
|
||||
new PrefetchedSource(this, mSources.size(), source);
|
||||
new PrefetchedSource(mSources.size(), source);
|
||||
|
||||
mSources.add(psource);
|
||||
|
||||
@@ -130,8 +130,6 @@ void Prefetcher::threadFunc() {
|
||||
for (;;) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
if (mDone) {
|
||||
mThreadExited = true;
|
||||
mCondition.signal();
|
||||
break;
|
||||
}
|
||||
mCondition.waitRelative(mLock, 10000000ll);
|
||||
@@ -169,6 +167,19 @@ void Prefetcher::threadFunc() {
|
||||
source->cacheMore();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mSources.size(); ++i) {
|
||||
sp<PrefetchedSource> source = mSources[i].promote();
|
||||
|
||||
if (source == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
source->onPrefetcherStopped();
|
||||
}
|
||||
|
||||
mThreadExited = true;
|
||||
mCondition.signal();
|
||||
}
|
||||
|
||||
int64_t Prefetcher::getCachedDurationUs(bool *noMoreData) {
|
||||
@@ -219,16 +230,15 @@ status_t Prefetcher::prepare() {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PrefetchedSource::PrefetchedSource(
|
||||
const sp<Prefetcher> &prefetcher,
|
||||
size_t index,
|
||||
const sp<MediaSource> &source)
|
||||
: mPrefetcher(prefetcher),
|
||||
mSource(source),
|
||||
: mSource(source),
|
||||
mIndex(index),
|
||||
mStarted(false),
|
||||
mReachedEOS(false),
|
||||
mSeekTimeUs(0),
|
||||
mCacheDurationUs(0) {
|
||||
mCacheDurationUs(0),
|
||||
mPrefetcherStopped(false) {
|
||||
}
|
||||
|
||||
PrefetchedSource::~PrefetchedSource() {
|
||||
@@ -238,6 +248,8 @@ PrefetchedSource::~PrefetchedSource() {
|
||||
}
|
||||
|
||||
status_t PrefetchedSource::start(MetaData *params) {
|
||||
CHECK(!mStarted);
|
||||
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
status_t err = mSource->start(params);
|
||||
@@ -252,6 +264,8 @@ status_t PrefetchedSource::start(MetaData *params) {
|
||||
}
|
||||
|
||||
status_t PrefetchedSource::stop() {
|
||||
CHECK(mStarted);
|
||||
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
clearCache_l();
|
||||
@@ -281,7 +295,7 @@ status_t PrefetchedSource::read(
|
||||
mSeekTimeUs = seekTimeUs;
|
||||
}
|
||||
|
||||
while (!mReachedEOS && mCachedBuffers.empty()) {
|
||||
while (!mPrefetcherStopped && !mReachedEOS && mCachedBuffers.empty()) {
|
||||
mCondition.wait(mLock);
|
||||
}
|
||||
|
||||
@@ -390,4 +404,10 @@ void PrefetchedSource::clearCache_l() {
|
||||
updateCacheDuration_l();
|
||||
}
|
||||
|
||||
void PrefetchedSource::onPrefetcherStopped() {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
mPrefetcherStopped = true;
|
||||
mCondition.signal();
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
namespace android {
|
||||
|
||||
struct AudioPlayer;
|
||||
struct DataSource;
|
||||
struct MediaBuffer;
|
||||
struct MediaExtractor;
|
||||
struct MediaSource;
|
||||
@@ -78,6 +79,9 @@ struct AwesomePlayer {
|
||||
|
||||
status_t getVideoDimensions(int32_t *width, int32_t *height) const;
|
||||
|
||||
status_t suspend();
|
||||
status_t resume();
|
||||
|
||||
private:
|
||||
friend struct AwesomeEvent;
|
||||
|
||||
@@ -103,6 +107,8 @@ private:
|
||||
String8 mUri;
|
||||
KeyedVector<String8, String8> mUriHeaders;
|
||||
|
||||
sp<DataSource> mFileSource;
|
||||
|
||||
sp<MediaSource> mVideoSource;
|
||||
sp<AwesomeRenderer> mVideoRenderer;
|
||||
|
||||
@@ -140,12 +146,29 @@ private:
|
||||
void postBufferingEvent_l();
|
||||
void postStreamDoneEvent_l();
|
||||
void postCheckAudioStatusEvent_l();
|
||||
status_t getPosition_l(int64_t *positionUs);
|
||||
status_t play_l();
|
||||
|
||||
MediaBuffer *mLastVideoBuffer;
|
||||
MediaBuffer *mVideoBuffer;
|
||||
|
||||
sp<Prefetcher> mPrefetcher;
|
||||
|
||||
struct SuspensionState {
|
||||
String8 mUri;
|
||||
KeyedVector<String8, String8> mUriHeaders;
|
||||
sp<DataSource> mFileSource;
|
||||
|
||||
uint32_t mFlags;
|
||||
int64_t mPositionUs;
|
||||
|
||||
} *mSuspensionState;
|
||||
|
||||
status_t setDataSource_l(
|
||||
const char *uri,
|
||||
const KeyedVector<String8, String8> *headers = NULL);
|
||||
|
||||
status_t setDataSource_l(const sp<DataSource> &dataSource);
|
||||
status_t setDataSource_l(const sp<MediaExtractor> &extractor);
|
||||
void reset_l();
|
||||
status_t seekTo_l(int64_t timeUs);
|
||||
|
||||
Reference in New Issue
Block a user