author | Sotaro Ikeda <sikeda@mozilla.com> |
Tue, 31 Mar 2015 10:02:22 -0700 (2015-03-31) | |
changeset 236792 | 1f5a169f04763eb5f4cc514119d9b2d2a5e5bfe3 |
parent 236791 | 83af1139a6d335c38edb1409f30c2d5c5196ed0b |
child 236793 | b182c5d8d0dffa07d69f35b2d307ac2cf72cdeb6 |
push id | 57775 |
push user | sikeda@mozilla.com |
push date | Tue, 31 Mar 2015 17:02:34 +0000 (2015-03-31) |
treeherder | mozilla-inbound@1f5a169f0476 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bholley, bwu |
bugs | 1139206 |
milestone | 40.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -1205,49 +1205,50 @@ void MediaDecoder::UpdateReadyStateForDa if (!mOwner || mShuttingDown || !mDecoderStateMachine) { return; } MediaDecoderOwner::NextFrameStatus frameStatus = mDecoderStateMachine->GetNextFrameStatus(); mOwner->UpdateReadyStateForData(frameStatus); } -void MediaDecoder::OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility) +void MediaDecoder::OnSeekResolved(SeekResolveValue aVal) { MOZ_ASSERT(NS_IsMainThread()); + mSeekRequest.Complete(); if (mShuttingDown) return; bool fireEnded = false; bool seekWasAborted = false; { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); // An additional seek was requested while the current seek was // in operation. if (mRequestedSeekTarget.IsValid()) { ChangeState(PLAY_STATE_SEEKING); 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(); } } } }
--- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -800,31 +800,17 @@ public: // Acquires the monitor. Call from any thread. virtual bool IsExpectingMoreData(); // Called when the video has completed playing. // Call on the main thread only. 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. void SeekingStarted(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable); // Called when the backend has changed the current playback // position. It dispatches a timeupdate event and invalidates the frame. // This must be called on the main thread only.
--- a/dom/media/omx/AudioOffloadPlayer.cpp +++ b/dom/media/omx/AudioOffloadPlayer.cpp @@ -52,23 +52,20 @@ PRLogModuleInfo* gAudioOffloadPlayerLog; // maximum time in paused state when offloading audio decompression. // When elapsed, the AudioSink is destroyed to allow the audio DSP to power down. 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) { MOZ_ASSERT(NS_IsMainThread()); #ifdef PR_LOGGING if (!gAudioOffloadPlayerLog) { @@ -194,23 +191,16 @@ status_t AudioOffloadPlayer::ChangeState case MediaDecoder::PLAY_STATE_PLAYING: { status_t err = Play(); if (err != OK) { return err; } 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 // offload track immediately. Resources will be freed by // MediaOmxCommonDecoder Pause(); break; @@ -273,18 +263,22 @@ status_t AudioOffloadPlayer::Play() if (!mStarted) { // Last pause timed out and offloaded audio sink was reset. Start it again err = Start(false); if (err != OK) { 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(); } } if (!mPlaying) { CHECK(mAudioSink.get()); err = mAudioSink->Start(); if (err == OK) { mPlaying = true; @@ -338,62 +332,64 @@ void AudioOffloadPlayer::Reset() mReachedEOS = false; mStarted = false; mPlaying = false; mStartPosUs = 0; 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; +} - mSeeking = true; +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)); + 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); } if (mPlaying) { mAudioSink->Pause(); mAudioSink->Flush(); 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__); } } return OK; } double AudioOffloadPlayer::GetMediaTimeSecs() { @@ -402,18 +398,18 @@ double AudioOffloadPlayer::GetMediaTimeS static_cast<double>(USECS_PER_S)); } 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; } playPosition = GetOutputPlayPositionUs_l(); if (!mReachedEOS) { mPositionTimeMediaUs = playPosition; @@ -434,16 +430,22 @@ int64_t AudioOffloadPlayer::GetOutputPla // HAL position is relative to the first buffer we sent at mStartPosUs const int64_t renderedDuration = mStartPosUs + playedUs; return renderedDuration; } 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); } void AudioOffloadPlayer::NotifyPositionChanged() { nsCOMPtr<nsIRunnable> nsEvent = @@ -451,16 +453,25 @@ void AudioOffloadPlayer::NotifyPositionC mObserver, &MediaOmxCommonDecoder::PlaybackPositionChanged, MediaDecoderEventVisibility::Observable); NS_DispatchToMainThread(nsEvent); } 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); } // static size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink, void* aBuffer, @@ -501,45 +512,45 @@ size_t AudioOffloadPlayer::FillBuffer(vo CHECK(mAudioSink.get()); if (mReachedEOS) { return 0; } 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); - refreshSeekTime = true; + if (mSeekTarget.IsValid()) { + seekTimeUs = mSeekTarget.mTime; + options.setSeekTo(seekTimeUs); if (mInputBuffer) { mInputBuffer->release(); mInputBuffer = nullptr; } - mSeeking = false; } } if (!mInputBuffer) { - status_t err; err = mSource->read(&mInputBuffer, &options); CHECK((!err && mInputBuffer) || (err && !mInputBuffer)); 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) { // After seek there is a possible race condition if // OffloadThread is observing state_stopping_1 before // framesReady() > 0. Ensure sink stop is called // after last buffer is released. This ensures the // partial buffer is written to the driver before @@ -559,51 +570,34 @@ size_t AudioOffloadPlayer::FillBuffer(vo break; } if(mInputBuffer->range_length() != 0) { CHECK(mInputBuffer->meta_data()->findInt64( kKeyTime, &mPositionTimeMediaUs)); } - if (refreshSeekTime) { - if (mDispatchSeekEvents && !mSeekDuringPause) { - mDispatchSeekEvents = false; + if (mSeekTarget.IsValid() && seekTimeUs == mSeekTarget.mTime) { + 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__); } - - NotifyPositionChanged(); + } else if (mSeekTarget.IsValid()) { + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("seek is updated during unlocking mLock")); + } - // need to adjust the mStartPosUs for offload decoding since parser - // might not be able to get the exact seek time requested. - mStartPosUs = mPositionTimeMediaUs; - AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f", - mStartPosUs / 1E6)); + NotifyPositionChanged(); - // 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; - } - } + // need to adjust the mStartPosUs for offload decoding since parser + // might not be able to get the exact seek time requested. + mStartPosUs = mPositionTimeMediaUs; + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f", + mStartPosUs / 1E6)); } if (mInputBuffer->range_length() == 0) { mInputBuffer->release(); mInputBuffer = nullptr; continue; }
--- a/dom/media/omx/AudioOffloadPlayer.h +++ b/dom/media/omx/AudioOffloadPlayer.h @@ -73,72 +73,58 @@ public: SEEK_COMPLETE }; AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr); ~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; + + virtual status_t ChangeState(MediaDecoder::PlayState aState) override; - double GetMediaTimeSecs(); + 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(); // Close the audio sink, stop time updates, frees the input buffers void Reset(); private: // Set when audio source is started and audioSink is initialized // Used only in main thread bool mStarted; // Set when audio sink is started. i.e. playback started // 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; // Session id given by Android::AudioSystem and used while creating audio sink // Used only in main thread int mSessionId; @@ -150,20 +136,25 @@ private: // different than given mSeekTimeUs, if audio source cannot find a frame at // that position. Store seeked position in mStartPosUs and provide // mStartPosUs + GetPosition() (i.e. absolute position) to // MediaOmxCommonDecoder // Used in main thread and offload callback thread, protected by Mutex // 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 // mLock int64_t mPositionTimeMediaUs; // State obtained from MediaOmxCommonDecoder. Used only in main thread MediaDecoder::PlayState mPlayState; @@ -216,25 +207,25 @@ private: static size_t AudioSinkCallback(AudioSink *aAudioSink, void *aData, size_t aSize, void *aMe, AudioSink::cb_event_t aEvent); 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 status_t Play(); // Stop the audio sink if we need to play till we drain the current buffer. // or Pause the sink in case we should stop playing immediately void Pause(bool aPlayPendingSamples = false);
--- a/dom/media/omx/AudioOffloadPlayerBase.h +++ b/dom/media/omx/AudioOffloadPlayerBase.h @@ -61,13 +61,15 @@ public: // Update ready state based on current play state. Not checking data // availability since offloading is currently done only when whole compressed // data is available virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus() { return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; } + + virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) = 0; }; } // namespace mozilla #endif // AUDIO_OFFLOAD_PLAYER_BASE_H_
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp +++ b/dom/media/omx/MediaOmxCommonDecoder.cpp @@ -121,18 +121,19 @@ MediaOmxCommonDecoder::ResumeStateMachin if (!mDecoderStateMachine) { return; } mFallbackToStateMachine = true; 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 RefPtr<nsRunnable> event = NS_NewRunnableMethodWithArg<bool>( mDecoderStateMachine, &MediaDecoderStateMachine::SetDormant, false); @@ -188,20 +189,35 @@ void MediaOmxCommonDecoder::ChangeState(PlayState aState) { MOZ_ASSERT(NS_IsMainThread()); // Keep MediaDecoder state in sync with MediaElement irrespective of offload // playback so it will continue to work in normal mode when offloading fails // 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(NS_GetCurrentThread(), __func__, static_cast<MediaDecoder*>(this), + &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected)); + mRequestedSeekTarget.Reset(); + break; + default: { + break; } } } void MediaOmxCommonDecoder::ApplyStateToStateMachine(PlayState aState) { MOZ_ASSERT(NS_IsMainThread());