mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 1139206 - Update AudioOffloadPlayer seek r=bholley,bwu
This commit is contained in:
parent
0150518a62
commit
76f7064cc7
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
{
|
||||
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
|
||||
}
|
||||
|
||||
virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) = 0;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user