Bug 1139206 - Update AudioOffloadPlayer seek r=bholley,bwu

This commit is contained in:
Sotaro Ikeda 2015-04-15 14:00:41 -07:00
parent 0150518a62
commit 76f7064cc7
6 changed files with 116 additions and 121 deletions

View File

@ -1223,9 +1223,10 @@ void MediaDecoder::UpdateReadyStateForData()
mOwner->UpdateReadyStateForData(frameStatus);
}
void MediaDecoder::OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility)
void MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
{
MOZ_ASSERT(NS_IsMainThread());
mSeekRequest.Complete();
if (mShuttingDown)
return;
@ -1242,20 +1243,20 @@ void MediaDecoder::OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibili
seekWasAborted = true;
} else {
UnpinForSeek();
fireEnded = aAtEnd;
if (aAtEnd) {
fireEnded = aVal.mAtEnd;
if (aVal.mAtEnd) {
ChangeState(PLAY_STATE_ENDED);
} else if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
ChangeState(aAtEnd ? PLAY_STATE_ENDED : mNextState);
} else if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
ChangeState(aVal.mAtEnd ? PLAY_STATE_ENDED : mNextState);
}
}
}
PlaybackPositionChanged(aEventVisibility);
PlaybackPositionChanged(aVal.mEventVisibility);
if (mOwner) {
UpdateReadyStateForData();
if (!seekWasAborted && (aEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
if (!seekWasAborted && (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
mOwner->SeekCompleted();
if (fireEnded) {
mOwner->PlaybackEnded();

View File

@ -809,21 +809,7 @@ public:
void PlaybackEnded();
void OnSeekRejected() { mSeekRequest.Complete(); }
void OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility);
void OnSeekResolved(SeekResolveValue aVal)
{
mSeekRequest.Complete();
OnSeekResolvedInternal(aVal.mAtEnd, aVal.mEventVisibility);
}
#ifdef MOZ_AUDIO_OFFLOAD
// Temporary hack - see bug 1139206.
void SimulateSeekResolvedForAudioOffload(MediaDecoderEventVisibility aEventVisibility)
{
OnSeekResolvedInternal(false, aEventVisibility);
}
#endif
void OnSeekResolved(SeekResolveValue aVal);
// Seeking has started. Inform the element on the main
// thread.

View File

@ -57,13 +57,10 @@ static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
mStarted(false),
mPlaying(false),
mSeeking(false),
mReachedEOS(false),
mSeekDuringPause(false),
mIsElementVisible(true),
mSampleRate(0),
mStartPosUs(0),
mSeekTimeUs(0),
mPositionTimeMediaUs(-1),
mInputBuffer(nullptr),
mObserver(aObserver)
@ -199,13 +196,6 @@ status_t AudioOffloadPlayer::ChangeState(MediaDecoder::PlayState aState)
StartTimeUpdate();
} break;
case MediaDecoder::PLAY_STATE_SEEKING: {
int64_t seekTimeUs
= mObserver->GetSeekTime();
SeekTo(seekTimeUs, true);
mObserver->ResetSeekTime();
} break;
case MediaDecoder::PLAY_STATE_PAUSED:
case MediaDecoder::PLAY_STATE_SHUTDOWN:
// Just pause here during play state shutdown as well to stop playing
@ -278,8 +268,12 @@ status_t AudioOffloadPlayer::Play()
return err;
}
// Seek to last play position only when there was no seek during last pause
if (!mSeeking) {
SeekTo(mPositionTimeMediaUs);
android::Mutex::Autolock autoLock(mLock);
if (!mSeekTarget.IsValid()) {
mSeekTarget = SeekTarget(mPositionTimeMediaUs,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
DoSeek();
}
}
@ -343,28 +337,36 @@ void AudioOffloadPlayer::Reset()
WakeLockRelease();
}
status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
nsRefPtr<MediaDecoder::SeekPromise> AudioOffloadPlayer::Seek(SeekTarget aTarget)
{
MOZ_ASSERT(NS_IsMainThread());
CHECK(mAudioSink.get());
android::Mutex::Autolock autoLock(mLock);
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SeekTo ( %lld )", aTimeUs));
mSeekPromise.RejectIfExists(true, __func__);
mSeekTarget = aTarget;
nsRefPtr<MediaDecoder::SeekPromise> p = mSeekPromise.Ensure(__func__);
DoSeek();
return p;
}
status_t AudioOffloadPlayer::DoSeek()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mSeekTarget.IsValid());
CHECK(mAudioSink.get());
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("DoSeek ( %lld )", mSeekTarget.mTime));
mSeeking = true;
mReachedEOS = false;
mPositionTimeMediaUs = -1;
mSeekTimeUs = aTimeUs;
mStartPosUs = aTimeUs;
mDispatchSeekEvents = aDispatchSeekEvents;
mStartPosUs = mSeekTarget.mTime;
if (mDispatchSeekEvents) {
if (!mSeekPromise.IsEmpty()) {
nsCOMPtr<nsIRunnable> nsEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mObserver,
&MediaDecoder::SeekingStarted,
MediaDecoderEventVisibility::Observable);
mSeekTarget.mEventVisibility);
NS_DispatchToCurrentThread(nsEvent);
}
@ -374,21 +376,15 @@ status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
mAudioSink->Start();
} else {
mSeekDuringPause = true;
if (mStarted) {
mAudioSink->Flush();
}
if (mDispatchSeekEvents) {
mDispatchSeekEvents = false;
if (!mSeekPromise.IsEmpty()) {
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Fake seek complete during pause"));
nsCOMPtr<nsIRunnable> nsEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mObserver,
&MediaDecoder::SimulateSeekResolvedForAudioOffload,
MediaDecoderEventVisibility::Observable);
NS_DispatchToCurrentThread(nsEvent);
// We do not reset mSeekTarget here.
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
}
@ -407,8 +403,8 @@ int64_t AudioOffloadPlayer::GetMediaTimeUs()
android::Mutex::Autolock autoLock(mLock);
int64_t playPosition = 0;
if (mSeeking) {
return mSeekTimeUs;
if (mSeekTarget.IsValid()) {
return mSeekTarget.mTime;
}
if (!mStarted) {
return mPositionTimeMediaUs;
@ -439,6 +435,12 @@ int64_t AudioOffloadPlayer::GetOutputPlayPositionUs_l() const
void AudioOffloadPlayer::NotifyAudioEOS()
{
android::Mutex::Autolock autoLock(mLock);
// We do not reset mSeekTarget here.
if (!mSeekPromise.IsEmpty()) {
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
&MediaDecoder::PlaybackEnded);
NS_DispatchToMainThread(nsEvent);
@ -456,6 +458,15 @@ void AudioOffloadPlayer::NotifyPositionChanged()
void AudioOffloadPlayer::NotifyAudioTearDown()
{
// Fallback to state machine.
// state machine's seeks will be done with
// MediaDecoderEventVisibility::Suppressed.
android::Mutex::Autolock autoLock(mLock);
// We do not reset mSeekTarget here.
if (!mSeekPromise.IsEmpty()) {
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
&MediaOmxCommonDecoder::AudioOffloadTearDown);
NS_DispatchToMainThread(nsEvent);
@ -506,27 +517,26 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
size_t sizeDone = 0;
size_t sizeRemaining = aSize;
int64_t seekTimeUs = -1;
while (sizeRemaining > 0) {
MediaSource::ReadOptions options;
bool refreshSeekTime = false;
{
android::Mutex::Autolock autoLock(mLock);
if (mSeeking) {
options.setSeekTo(mSeekTimeUs);
if (mSeekTarget.IsValid()) {
seekTimeUs = mSeekTarget.mTime;
options.setSeekTo(seekTimeUs);
refreshSeekTime = true;
if (mInputBuffer) {
mInputBuffer->release();
mInputBuffer = nullptr;
}
mSeeking = false;
}
}
if (!mInputBuffer) {
status_t err;
err = mSource->read(&mInputBuffer, &options);
@ -535,6 +545,9 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
android::Mutex::Autolock autoLock(mLock);
if (err != OK) {
if (mSeekTarget.IsValid()) {
mSeekTarget.Reset();
}
AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Error while reading media source %d "
"Ok to receive EOS error at end", err));
if (!mReachedEOS) {
@ -564,25 +577,19 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
kKeyTime, &mPositionTimeMediaUs));
}
if (refreshSeekTime) {
if (mDispatchSeekEvents && !mSeekDuringPause) {
mDispatchSeekEvents = false;
if (mSeekTarget.IsValid() && seekTimeUs == mSeekTarget.mTime) {
MOZ_ASSERT(mSeekTarget.IsValid());
mSeekTarget.Reset();
if (!mSeekPromise.IsEmpty()) {
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("FillBuffer posting SEEK_COMPLETE"));
nsCOMPtr<nsIRunnable> nsEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mObserver,
&MediaDecoder::SimulateSeekResolvedForAudioOffload,
MediaDecoderEventVisibility::Observable);
NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
} else if (mSeekDuringPause) {
// Callback is already called for seek during pause. Just reset the
// flag
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Not posting seek complete as its"
" already faked"));
mSeekDuringPause = false;
MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
mSeekPromise.Resolve(val, __func__);
}
} else if (mSeekTarget.IsValid()) {
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("seek is updated during unlocking mLock"));
}
if (refreshSeekTime) {
NotifyPositionChanged();
// need to adjust the mStartPosUs for offload decoding since parser
@ -590,14 +597,6 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
mStartPosUs = mPositionTimeMediaUs;
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f",
mStartPosUs / 1E6));
// clear seek time with mLock locked and once we have valid
// mPositionTimeMediaUs
// before clearing mSeekTimeUs check if a new seek request has been
// received while we were reading from the source with mLock released.
if (!mSeeking) {
mSeekTimeUs = 0;
}
}
}

View File

@ -78,25 +78,27 @@ public:
~AudioOffloadPlayer();
// Caller retains ownership of "aSource".
void SetSource(const android::sp<MediaSource> &aSource);
virtual void SetSource(const android::sp<MediaSource> &aSource) override;
// Start the source if it's not already started and open the AudioSink to
// create an offloaded audio track
status_t Start(bool aSourceAlreadyStarted = false);
virtual status_t Start(bool aSourceAlreadyStarted = false) override;
double GetMediaTimeSecs();
virtual status_t ChangeState(MediaDecoder::PlayState aState) override;
virtual void SetVolume(double aVolume) override;
virtual double GetMediaTimeSecs() override;
// To update progress bar when the element is visible
void SetElementVisibility(bool aIsVisible);
status_t ChangeState(MediaDecoder::PlayState aState);
void SetVolume(double aVolume);
virtual void SetElementVisibility(bool aIsVisible) override;;
// Update ready state based on current play state. Not checking data
// availability since offloading is currently done only when whole compressed
// data is available
MediaDecoderOwner::NextFrameStatus GetNextFrameStatus();
virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus() override;
virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) override;
void TimeUpdate();
@ -112,28 +114,12 @@ private:
// Used only in main thread
bool mPlaying;
// Set when playstate is seeking and reset when FillBUffer() acknowledged
// seeking by seeking audio source. Used in main thread and offload
// callback thread, protected by Mutex mLock
bool mSeeking;
// Once playback reached end of stream (last ~100ms), position provided by DSP
// may be reset/corrupted. This bool is used to avoid that.
// Used in main thread and offload callback thread, protected by Mutex
// mLock
bool mReachedEOS;
// Set when there is a seek request during pause.
// Used in main thread and offload callback thread, protected by Mutex
// mLock
bool mSeekDuringPause;
// Seek can be triggered internally or by MediaDecoder. This bool is to
// to track seek triggered by MediaDecoder so that we can send back
// SeekingStarted and SeekingStopped events.
// Used in main thread and offload callback thread, protected by Mutex mLock
bool mDispatchSeekEvents;
// Set when the HTML Audio Element is visible to the user.
// Used only in main thread
bool mIsElementVisible;
@ -155,10 +141,15 @@ private:
// mLock
int64_t mStartPosUs;
// Given seek time when there is a request to seek
// The target of current seek when there is a request to seek
// Used in main thread and offload callback thread, protected by Mutex
// mLock
int64_t mSeekTimeUs;
SeekTarget mSeekTarget;
// MediaPromise of current seek.
// Used in main thread and offload callback thread, protected by Mutex
// mLock
MediaPromiseHolder<MediaDecoder::SeekPromise> mSeekPromise;
// Positions obtained from offlaoded tracks (DSP)
// Used in main thread and offload callback thread, protected by Mutex
@ -221,15 +212,15 @@ private:
bool IsSeeking();
// Set mSeekTime to the given position and restart the sink. Actual seek
// happens in FillBuffer(). If aDispatchSeekEvents is true, send
// Set mSeekTarget to the given position and restart the sink. Actual seek
// happens in FillBuffer(). If mSeekPromise is not empty, send
// SeekingStarted event always and SeekingStopped event when the play state is
// paused to MediaDecoder.
// When decoding and playing happens separately, if there is a seek during
// pause, we can decode and keep data ready.
// In case of offload player, no way to seek during pause. So just fake that
// seek is done.
status_t SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents = false);
status_t DoSeek();
// Start/Resume the audio sink so that callback will start being called to get
// compressed data

View File

@ -66,6 +66,8 @@ public:
{
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
}
virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) = 0;
};
} // namespace mozilla

View File

@ -126,8 +126,9 @@ MediaOmxCommonDecoder::ResumeStateMachine()
mAudioOffloadPlayer = nullptr;
int64_t timeUsecs = 0;
SecondsToUsecs(mCurrentTime, timeUsecs);
mRequestedSeekTarget = SeekTarget(timeUsecs, SeekTarget::Accurate);
mRequestedSeekTarget = SeekTarget(timeUsecs,
SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
mNextState = mPlayState;
ChangeState(PLAY_STATE_LOADING);
// exit dormant state
@ -193,10 +194,25 @@ MediaOmxCommonDecoder::ChangeState(PlayState aState)
// in between
MediaDecoder::ChangeState(aState);
if (mAudioOffloadPlayer) {
status_t err = mAudioOffloadPlayer->ChangeState(aState);
if (err != OK) {
ResumeStateMachine();
if (!mAudioOffloadPlayer) {
return;
}
status_t err = mAudioOffloadPlayer->ChangeState(aState);
if (err != OK) {
ResumeStateMachine();
return;
}
switch (mPlayState) {
case PLAY_STATE_SEEKING:
mSeekRequest.Begin(mAudioOffloadPlayer->Seek(mRequestedSeekTarget)
->RefableThen(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
&MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
mRequestedSeekTarget.Reset();
break;
default: {
break;
}
}
}