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:
Andreas Huber
2010-02-12 10:42:02 -08:00
parent 965e37ec88
commit ba7ec917ea
4 changed files with 190 additions and 27 deletions

View File

@@ -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

View File

@@ -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(

View File

@@ -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

View File

@@ -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);