--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -128,26 +128,27 @@ size_t MediaDecoderReader::SizeOfVideoQu
return mVideoQueue.GetSize();
}
size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
{
return mAudioQueue.GetSize();
}
-nsresult MediaDecoderReader::ResetDecode()
+nsresult MediaDecoderReader::ResetDecode(TargetQueues aQueues /*= AUDIO_VIDEO*/)
{
VideoQueue().Reset();
- AudioQueue().Reset();
+ mVideoDiscontinuity = true;
+ mBaseVideoPromise.RejectIfExists(CANCELED, __func__);
- mAudioDiscontinuity = true;
- mVideoDiscontinuity = true;
-
- mBaseAudioPromise.RejectIfExists(CANCELED, __func__);
- mBaseVideoPromise.RejectIfExists(CANCELED, __func__);
+ if (aQueues == AUDIO_VIDEO) {
+ AudioQueue().Reset();
+ mAudioDiscontinuity = true;
+ mBaseAudioPromise.RejectIfExists(CANCELED, __func__);
+ }
return NS_OK;
}
RefPtr<MediaDecoderReader::MediaDataPromise>
MediaDecoderReader::DecodeToFirstVideoData()
{
MOZ_ASSERT(OnTaskQueue());
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -68,16 +68,21 @@ class MediaDecoderReader {
public:
enum NotDecodedReason {
END_OF_STREAM,
DECODE_ERROR,
WAITING_FOR_DATA,
CANCELED
};
+ enum TargetQueues {
+ VIDEO_ONLY,
+ AUDIO_VIDEO
+ };
+
using MetadataPromise =
MozPromise<RefPtr<MetadataHolder>, ReadMetadataFailureReason, IsExclusive>;
using MediaDataPromise =
MozPromise<RefPtr<MediaData>, NotDecodedReason, IsExclusive>;
using SeekPromise = MozPromise<media::TimeUnit, nsresult, IsExclusive>;
// Note that, conceptually, WaitForData makes sense in a non-exclusive sense.
// But in the current architecture it's only ever used exclusively (by MDSM),
@@ -120,17 +125,17 @@ public:
// Request*Data() calls after this is called. Calls to Request*Data()
// made after this should be processed as usual.
//
// Normally this call preceedes a Seek() call, or shutdown.
//
// The first samples of every stream produced after a ResetDecode() call
// *must* be marked as "discontinuities". If it's not, seeking work won't
// properly!
- virtual nsresult ResetDecode();
+ virtual nsresult ResetDecode(TargetQueues aQueues = AUDIO_VIDEO);
// Requests one audio sample from the reader.
//
// The decode should be performed asynchronously, and the promise should
// be resolved when it is complete. Don't hold the decoder
// monitor while calling this, as the implementation may try to wait
// on something that needs the monitor and deadlock.
virtual RefPtr<MediaDataPromise> RequestAudioData();
--- a/dom/media/MediaDecoderReaderWrapper.cpp
+++ b/dom/media/MediaDecoderReaderWrapper.cpp
@@ -325,25 +325,27 @@ MediaDecoderReaderWrapper::SetIdle()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(mReader, &MediaDecoderReader::SetIdle);
mReader->OwnerThread()->Dispatch(r.forget());
}
void
-MediaDecoderReaderWrapper::ResetDecode()
+MediaDecoderReaderWrapper::ResetDecode(TargetQueues aQueues)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mAudioDataRequest.DisconnectIfExists();
mVideoDataRequest.DisconnectIfExists();
nsCOMPtr<nsIRunnable> r =
- NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
+ NS_NewRunnableMethodWithArg<TargetQueues>(mReader,
+ &MediaDecoderReader::ResetDecode,
+ aQueues);
mReader->OwnerThread()->Dispatch(r.forget());
}
RefPtr<ShutdownPromise>
MediaDecoderReaderWrapper::Shutdown()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mRequestAudioDataCB);
--- a/dom/media/MediaDecoderReaderWrapper.h
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -27,16 +27,17 @@ typedef MozPromise<bool, bool, /* isExcl
* is passed to the underlying reader.
*/
class MediaDecoderReaderWrapper {
typedef MediaDecoderReader::MetadataPromise MetadataPromise;
typedef MediaDecoderReader::MediaDataPromise MediaDataPromise;
typedef MediaDecoderReader::SeekPromise SeekPromise;
typedef MediaDecoderReader::WaitForDataPromise WaitForDataPromise;
typedef MediaDecoderReader::BufferedUpdatePromise BufferedUpdatePromise;
+ typedef MediaDecoderReader::TargetQueues TargetQueues;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReaderWrapper);
/*
* Type 1: void(MediaData*)
* void(RefPtr<MediaData>)
*/
template <typename T>
class ArgType1CheckHelper {
@@ -250,17 +251,17 @@ public:
RefPtr<SeekPromise> Seek(SeekTarget aTarget, media::TimeUnit aEndTime);
RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);
RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise();
RefPtr<ShutdownPromise> Shutdown();
void ReleaseMediaResources();
void SetIdle();
- void ResetDecode();
+ void ResetDecode(TargetQueues aQueues);
nsresult Init() { return mReader->Init(); }
bool IsWaitForDataSupported() const { return mReader->IsWaitForDataSupported(); }
bool IsAsync() const { return mReader->IsAsync(); }
bool UseBufferingHeuristics() const { return mReader->UseBufferingHeuristics(); }
bool ForceZeroStartTime() const { return mReader->ForceZeroStartTime(); }
bool VideoIsHardwareAccelerated() const {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -477,16 +477,20 @@ bool MediaDecoderStateMachine::HaveEnoug
// at which the DecodedStream is playing.
return true;
}
bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
{
MOZ_ASSERT(OnTaskQueue());
+ if (IsVideoDecodeSuspended()) {
+ return true;
+ }
+
if (VideoQueue().GetSize() == 0) {
return false;
}
if (VideoQueue().GetSize() - 1 < GetAmpleVideoFrames() * mPlaybackRate) {
return false;
}
@@ -1342,22 +1346,87 @@ void MediaDecoderStateMachine::PlayState
StartDecoding();
}
ScheduleStateMachine();
}
void MediaDecoderStateMachine::VisibilityChanged()
{
- DECODER_LOG("VisibilityChanged: is visible = %c", mIsVisible ? 'T' : 'F');
-
+ MOZ_ASSERT(OnTaskQueue());
+ DECODER_LOG("VisibilityChanged: is visible = %d", mIsVisible.Ref());
+
+ // Not suspending background videos so there's nothing to do.
if (!sSuspendBackgroundVideos) {
- // Not suspending background videos so there's nothing to do.
+ return;
+ }
+
+ // If not transitioning to visible and not playing then there's
+ // nothing to do.
+ if (!mIsVisible || mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
+ return;
+ }
+
+ // If an existing seek is in flight don't bother creating a new one to catch
+ // up.
+ if (mSeekTask || mQueuedSeek.Exists()) {
return;
}
+
+ // Start video-only seek to the current time...
+ InitiateVideoDecodeRecoverySeek();
+}
+
+// InitiateVideoDecodeRecoverySeek is responsible for setting up a video-only
+// seek using the seek task. When suspension of decoding for videos that are in
+// background tabs (ie. invisible) is enabled, the audio keeps playing and when
+// switching back to decoding video, it is highly desirable to not cause the
+// audio to pause as the video is seeked else there be a noticeable audio glitch
+// as the tab becomes visible.
+void MediaDecoderStateMachine::InitiateVideoDecodeRecoverySeek()
+{
+ MOZ_ASSERT(OnTaskQueue());
+
+ SeekJob seekJob;
+ seekJob.mTarget = SeekTarget(GetMediaTime(),
+ SeekTarget::Type::AccurateVideoOnly,
+ MediaDecoderEventVisibility::Suppressed);
+
+ SetState(DECODER_STATE_SEEKING);
+
+ // Discard the existing seek task.
+ DiscardSeekTaskIfExist();
+
+ mSeekTaskRequest.DisconnectIfExists();
+
+ // SeekTask will register its callbacks to MediaDecoderReaderWrapper.
+ CancelMediaDecoderReaderWrapperCallback();
+
+ // Create a new SeekTask instance for the incoming seek task.
+ mSeekTask = SeekTask::CreateSeekTask(mDecoderID, OwnerThread(),
+ mReader.get(), Move(seekJob),
+ mInfo, Duration(), GetMediaTime());
+
+ mOnSeekingStart.Notify(MediaDecoderEventVisibility::Suppressed);
+
+ // Reset our state machine and decoding pipeline before seeking.
+ if (mSeekTask->NeedToResetMDSM()) {
+ Reset(MediaDecoderReader::VIDEO_ONLY);
+ }
+
+ // Do the seek.
+ mSeekTaskRequest.Begin(
+ mSeekTask->Seek(Duration())->Then(OwnerThread(), __func__, this,
+ &MediaDecoderStateMachine::OnSeekTaskResolved,
+ &MediaDecoderStateMachine::OnSeekTaskRejected));
+ // Nobody is listening to this as OnSeekTaskResolved handles what is
+ // required but the promise needs to exist or SeekJob::Exists() will
+ // assert.
+ RefPtr<MediaDecoder::SeekPromise> unused =
+ mSeekTask->GetSeekJob().mPromise.Ensure(__func__);
}
void MediaDecoderStateMachine::BufferedRangeUpdated()
{
MOZ_ASSERT(OnTaskQueue());
// While playing an unseekable stream of unknown duration, mObservedDuration
// is updated (in AdvanceFrame()) as we play. But if data is being downloaded
@@ -1697,16 +1766,23 @@ MediaDecoderStateMachine::EnsureVideoDec
SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
IsVideoDecoding(), VideoRequestStatus());
if (mState != DECODER_STATE_DECODING &&
mState != DECODER_STATE_BUFFERING) {
return NS_OK;
}
+ if (IsVideoDecodeSuspended() && !IsDecodingFirstFrame()) {
+ // The element is invisible and background videos should be suspended.
+ // If the first frame has already been decoded, don't request anymore video
+ // frames.
+ return NS_OK;
+ }
+
if (!IsVideoDecoding() || mReader->IsRequestingVidoeData() ||
mVideoWaitRequest.Exists()) {
return NS_OK;
}
RequestVideoData();
return NS_OK;
}
@@ -2277,49 +2353,52 @@ nsresult MediaDecoderStateMachine::RunSt
return NS_OK;
}
}
return NS_OK;
}
void
-MediaDecoderStateMachine::Reset()
+MediaDecoderStateMachine::Reset(MediaDecoderReader::TargetQueues aQueues /*= AUDIO_VIDEO*/)
{
MOZ_ASSERT(OnTaskQueue());
DECODER_LOG("MediaDecoderStateMachine::Reset");
// We should be resetting because we're seeking, shutting down, or entering
// dormant state. We could also be in the process of going dormant, and have
// just switched to exiting dormant before we finished entering dormant,
// hence the DECODING_NONE case below.
MOZ_ASSERT(IsShutdown() ||
mState == DECODER_STATE_SEEKING ||
mState == DECODER_STATE_DORMANT);
- // Stop the audio thread. Otherwise, MediaSink might be accessing AudioQueue
- // outside of the decoder monitor while we are clearing the queue and causes
- // crash for no samples to be popped.
- StopMediaSink();
mDecodedVideoEndTime = 0;
- mDecodedAudioEndTime = 0;
- mAudioCompleted = false;
mVideoCompleted = false;
- AudioQueue().Reset();
VideoQueue().Reset();
+ mVideoWaitRequest.DisconnectIfExists();
+
+ if (aQueues == MediaDecoderReader::AUDIO_VIDEO) {
+ // Stop the audio thread. Otherwise, MediaSink might be accessing AudioQueue
+ // outside of the decoder monitor while we are clearing the queue and causes
+ // crash for no samples to be popped.
+ StopMediaSink();
+ mDecodedAudioEndTime = 0;
+ mAudioCompleted = false;
+ AudioQueue().Reset();
+ mAudioWaitRequest.DisconnectIfExists();
+ }
mMetadataRequest.DisconnectIfExists();
- mAudioWaitRequest.DisconnectIfExists();
- mVideoWaitRequest.DisconnectIfExists();
mSeekTaskRequest.DisconnectIfExists();
mPlaybackOffset = 0;
- mReader->ResetDecode();
+ mReader->ResetDecode(aQueues);
}
int64_t
MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const
{
MOZ_ASSERT(OnTaskQueue());
int64_t clockTime = mMediaSink->GetPosition(aTimeStamp);
NS_ASSERTION(GetMediaTime() <= clockTime, "Clock should go forwards.");
@@ -2515,16 +2594,22 @@ bool MediaDecoderStateMachine::OnTaskQue
}
bool MediaDecoderStateMachine::IsStateMachineScheduled() const
{
MOZ_ASSERT(OnTaskQueue());
return mDispatchedStateMachine || mDelayedScheduler.IsScheduled();
}
+bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
+{
+ MOZ_ASSERT(OnTaskQueue());
+ return sSuspendBackgroundVideos && !mIsVisible;
+}
+
void
MediaDecoderStateMachine::LogicalPlaybackRateChanged()
{
MOZ_ASSERT(OnTaskQueue());
if (mLogicalPlaybackRate == 0) {
// This case is handled in MediaDecoder by pausing playback.
return;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -363,17 +363,17 @@ private:
void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
{
MOZ_ASSERT(OnTaskQueue());
OnNotDecoded(MediaData::VIDEO_DATA, aReason);
}
// Resets all state related to decoding and playback, emptying all buffers
// and aborting all pending operations on the decode task queue.
- void Reset();
+ void Reset(MediaDecoderReader::TargetQueues aQueues = MediaDecoderReader::AUDIO_VIDEO);
protected:
virtual ~MediaDecoderStateMachine();
void SetState(State aState);
void BufferedRangeUpdated();
@@ -504,20 +504,25 @@ protected:
// Dispatches a LoadedMetadataEvent.
// This is threadsafe and can be called on any thread.
// The decoder monitor must be held.
void EnqueueLoadedMetadataEvent();
void EnqueueFirstFrameLoadedEvent();
- // Clears any previous seeking state and initiates a new see on the decoder.
+ // Clears any previous seeking state and initiates a new seek on the decoder.
// The decoder monitor must be held.
void InitiateSeek(SeekJob aSeekJob);
+ // Clears any previous seeking state and initiates a video-only seek on the
+ // decoder to catch up the video to the current audio position, when recovering
+ // from video decoding being suspended in background.
+ void InitiateVideoDecodeRecoverySeek();
+
nsresult DispatchAudioDecodeTaskIfNeeded();
// Ensures a task to decode audio has been dispatched to the decode task queue.
// If a task to decode has already been dispatched, this does nothing,
// otherwise this dispatches a task to do the decode.
// This is called on the state machine or decode threads.
// The decoder monitor must be held.
nsresult EnsureAudioDecodeTaskQueued();
@@ -590,16 +595,20 @@ protected:
bool IsStateMachineScheduled() const;
// Returns true if we're not playing and the decode thread has filled its
// decode buffers and is waiting. We can shut the decode thread down in this
// case as it may not be needed again.
bool IsPausedAndDecoderWaiting();
+ // Returns true if the video decoding is suspended because the element is not
+ // visible
+ bool IsVideoDecodeSuspended() const;
+
// These return true if the respective stream's decode has not yet reached
// the end of stream.
bool IsAudioDecoding();
bool IsVideoDecoding();
private:
// Resolved by the MediaSink to signal that all audio/video outstanding
// work is complete and identify which part(a/v) of the sink is shutting down.
@@ -751,17 +760,17 @@ private:
// If we're quick buffering, we'll remain in buffering mode while we have less than
// QUICK_BUFFERING_LOW_DATA_USECS of decoded data available.
int64_t mQuickBufferingLowDataThresholdUsecs;
// At the start of decoding we want to "preroll" the decode until we've
// got a few frames decoded before we consider whether decode is falling
// behind. Otherwise our "we're falling behind" logic will trigger
- // unneccessarily if we start playing as soon as the first sample is
+ // unnecessarily if we start playing as soon as the first sample is
// decoded. These two fields store how many video frames and audio
// samples we must consume before are considered to be finished prerolling.
uint32_t AudioPrerollUsecs() const
{
MOZ_ASSERT(OnTaskQueue());
return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs / 2;
}
@@ -776,17 +785,18 @@ private:
MOZ_ASSERT(OnTaskQueue());
return !IsAudioDecoding() ||
GetDecodedAudioDuration() >= AudioPrerollUsecs() * mPlaybackRate;
}
bool DonePrerollingVideo()
{
MOZ_ASSERT(OnTaskQueue());
- return !IsVideoDecoding() ||
+ return !mIsVisible ||
+ !IsVideoDecoding() ||
static_cast<uint32_t>(VideoQueue().GetSize()) >=
VideoPrerollFrames() * mPlaybackRate + 1;
}
void StopPrerollingAudio()
{
MOZ_ASSERT(OnTaskQueue());
if (mIsAudioPrerolling) {
@@ -857,17 +867,17 @@ private:
// Note that the odd semantics here are designed to replicate the current
// behavior where we notify the decoder each time we come out of dormant, but
// send suppressed event visibility for those cases. This code can probably be
// simplified.
bool mNotifyMetadataBeforeFirstFrame;
// True if we've dispatched an event to the decode task queue to call
// DecodeThreadRun(). We use this flag to prevent us from dispatching
- // unneccessary runnables, since the decode thread runs in a loop.
+ // unnecessary runnables, since the decode thread runs in a loop.
bool mDispatchedEventToDecode;
// If this is true while we're in buffering mode, we can exit early,
// as it's likely we may be able to playback. This happens when we enter
// buffering mode soon after the decode starts, because the decode-ahead
// ran fast enough to exhaust all data while the download is starting up.
// Synchronised via decoder monitor.
bool mQuickBuffering;
@@ -899,17 +909,17 @@ private:
nsAutoPtr<MetadataTags> mMetadataTags;
mozilla::MediaMetadataManager mMetadataManager;
// Track our request to update the buffered ranges
MozPromiseRequestHolder<MediaDecoderReader::BufferedUpdatePromise> mBufferedUpdateRequest;
// True if we need to call FinishDecodeFirstFrame() upon frame decoding
- // successeeding.
+ // succeeding.
bool mDecodingFirstFrame;
// True if we are back from DECODER_STATE_DORMANT state and
// LoadedMetadataEvent was already sent.
bool mSentLoadedMetadataEvent;
// True if we are back from DECODER_STATE_DORMANT state and
// FirstFrameLoadedEvent was already sent, then we can skip
// SetStartTime because the mStartTime already set before. Also we don't need
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1271,17 +1271,17 @@ MediaFormatReader::WaitForData(MediaData
return WaitForDataPromise::CreateAndResolve(decoder.mType, __func__);
}
RefPtr<WaitForDataPromise> p = decoder.mWaitingPromise.Ensure(__func__);
ScheduleUpdate(trackType);
return p;
}
nsresult
-MediaFormatReader::ResetDecode()
+MediaFormatReader::ResetDecode(TargetQueues aQueues)
{
MOZ_ASSERT(OnTaskQueue());
LOGV("");
mSeekPromise.RejectIfExists(NS_OK, __func__);
mSkipRequest.DisconnectIfExists();
// Do the same for any data wait promises.
@@ -1293,24 +1293,25 @@ MediaFormatReader::ResetDecode()
if (HasVideo()) {
mVideo.ResetDemuxer();
Flush(TrackInfo::kVideoTrack);
if (mVideo.HasPromise()) {
mVideo.RejectPromise(CANCELED, __func__);
}
}
- if (HasAudio()) {
+
+ if (HasAudio() && aQueues == AUDIO_VIDEO) {
mAudio.ResetDemuxer();
Flush(TrackInfo::kAudioTrack);
if (mAudio.HasPromise()) {
mAudio.RejectPromise(CANCELED, __func__);
}
}
- return MediaDecoderReader::ResetDecode();
+ return MediaDecoderReader::ResetDecode(aQueues);
}
void
MediaFormatReader::Output(TrackType aTrack, MediaData* aSample)
{
LOGV("Decoded %s sample time=%lld timecode=%lld kf=%d dur=%lld",
TrackTypeToStr(aTrack), aSample->mTime, aSample->mTimecode,
aSample->mKeyframe, aSample->mDuration);
@@ -1478,17 +1479,17 @@ MediaFormatReader::Seek(SeekTarget aTarg
void
MediaFormatReader::AttemptSeek()
{
MOZ_ASSERT(OnTaskQueue());
if (mPendingSeekTime.isNothing()) {
return;
}
- // An internal seek may be pending due to Seek queueing multiple tasks calling
+ // An internal seek may be pending due to Seek queuing multiple tasks calling
// AttemptSeek ; we can ignore those by resetting any pending demuxer's seek.
mAudio.mSeekRequest.DisconnectIfExists();
mVideo.mSeekRequest.DisconnectIfExists();
if (HasVideo()) {
DoVideoSeek();
} else if (HasAudio()) {
DoAudioSeek();
} else {
@@ -1558,19 +1559,20 @@ MediaFormatReader::DoVideoSeek()
void
MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
{
MOZ_ASSERT(OnTaskQueue());
LOGV("Video seeked to %lld", aTime.ToMicroseconds());
mVideo.mSeekRequest.Complete();
- if (HasAudio()) {
- MOZ_ASSERT(mPendingSeekTime.isSome() && mOriginalSeekTarget.isSome());
- if (mOriginalSeekTarget.ref().IsFast()) {
+ MOZ_ASSERT(mOriginalSeekTarget.isSome());
+ if (HasAudio() && !mOriginalSeekTarget->IsVideoOnly()) {
+ MOZ_ASSERT(mPendingSeekTime.isSome());
+ if (mOriginalSeekTarget->IsFast()) {
// We are performing a fast seek. We need to seek audio to where the
// video seeked to, to ensure proper A/V sync once playback resume.
mPendingSeekTime = Some(aTime);
}
DoAudioSeek();
} else {
mPendingSeekTime.reset();
mSeekPromise.Resolve(aTime, __func__);
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -57,17 +57,17 @@ public:
RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise() override;
bool ForceZeroStartTime() const override;
// For Media Resource Management
void ReleaseMediaResources() override;
- nsresult ResetDecode() override;
+ nsresult ResetDecode(TargetQueues aQueues) override;
RefPtr<ShutdownPromise> Shutdown() override;
bool IsAsync() const override { return true; }
bool VideoIsHardwareAccelerated() const override;
bool IsWaitForDataSupported() const override { return true; }
@@ -314,17 +314,17 @@ private:
uint64_t mNumSamplesInput;
uint64_t mNumSamplesOutput;
uint64_t mNumSamplesOutputTotal;
uint64_t mNumSamplesSkippedTotal;
uint64_t mNumSamplesOutputTotalSinceTelemetry;
uint64_t mNumSamplesSkippedTotalSinceTelemetry;
- // These get overriden in the templated concrete class.
+ // These get overridden in the templated concrete class.
// Indicate if we have a pending promise for decoded frame.
// Rejecting the promise will stop the reader from decoding ahead.
virtual bool HasPromise() = 0;
virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
const char* aMethodName) = 0;
void ResetDemuxer()
{
--- a/dom/media/SeekJob.cpp
+++ b/dom/media/SeekJob.cpp
@@ -28,17 +28,17 @@ SeekJob& SeekJob::operator=(SeekJob&& aO
{
MOZ_DIAGNOSTIC_ASSERT(!Exists());
mTarget = aOther.mTarget;
aOther.mTarget.Reset();
mPromise = Move(aOther.mPromise);
return *this;
}
-bool SeekJob::Exists()
+bool SeekJob::Exists() const
{
MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
return mTarget.IsValid();
}
void SeekJob::Resolve(bool aAtEnd, const char* aCallSite)
{
MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
--- a/dom/media/SeekJob.h
+++ b/dom/media/SeekJob.h
@@ -16,17 +16,17 @@ namespace mozilla {
struct SeekJob {
SeekJob();
SeekJob(SeekJob&& aOther);
SeekJob& operator=(SeekJob&& aOther);
- bool Exists();
+ bool Exists() const;
void Resolve(bool aAtEnd, const char* aCallSite);
void RejectIfExists(const char* aCallSite);
~SeekJob();
SeekTarget mTarget;
--- a/dom/media/SeekTarget.h
+++ b/dom/media/SeekTarget.h
@@ -12,22 +12,24 @@
namespace mozilla {
enum class MediaDecoderEventVisibility : int8_t {
Observable,
Suppressed
};
// Stores the seek target; the time to seek to, and whether an Accurate,
-// or "Fast" (nearest keyframe) seek was requested.
+// "Fast" (nearest keyframe), or "Video Only" (no audio seek) seek was
+// requested.
struct SeekTarget {
enum Type {
Invalid,
PrevSyncPoint,
- Accurate
+ Accurate,
+ AccurateVideoOnly,
};
SeekTarget()
: mEventVisibility(MediaDecoderEventVisibility::Observable)
, mTime(media::TimeUnit::Invalid())
, mType(SeekTarget::Invalid)
{
}
SeekTarget(int64_t aTimeUsecs,
@@ -73,23 +75,26 @@ struct SeekTarget {
mType = aType;
}
bool IsFast() const {
return mType == SeekTarget::Type::PrevSyncPoint;
}
bool IsAccurate() const {
return mType == SeekTarget::Type::Accurate;
}
+ bool IsVideoOnly() const {
+ return mType == SeekTarget::Type::AccurateVideoOnly;
+ }
MediaDecoderEventVisibility mEventVisibility;
private:
// Seek target time.
media::TimeUnit mTime;
// Whether we should seek "Fast", or "Accurate".
- // "Fast" seeks to the seek point preceeding mTime, whereas
+ // "Fast" seeks to the seek point preceding mTime, whereas
// "Accurate" seeks as close as possible to mTime.
Type mType;
};
} // namespace mozilla
#endif /* SEEK_TARGET_H */
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/SeekTask.cpp
@@ -172,17 +172,17 @@ SeekTask::NeedToResetMDSM() const
SeekJob&
SeekTask::GetSeekJob()
{
return mSeekJob;
}
bool
-SeekTask::Exists()
+SeekTask::Exists() const
{
return mSeekJob.Exists();
}
RefPtr<SeekTask::SeekTaskPromise>
SeekTask::Seek(const media::TimeUnit& aDuration)
{
AssertOwnerThread();
@@ -417,16 +417,17 @@ bool
SeekTask::IsAudioSeekComplete()
{
AssertOwnerThread();
SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData);
return
!HasAudio() ||
+ mSeekJob.mTarget.IsVideoOnly() ||
(Exists() && !mDropAudioUntilNextDiscontinuity &&
(mIsAudioQueueFinished || mSeekedAudioData));
}
bool
SeekTask::IsVideoSeekComplete()
{
AssertOwnerThread();
@@ -473,18 +474,20 @@ SeekTask::CheckIfSeekComplete()
void
SeekTask::OnSeekResolved(media::TimeUnit)
{
AssertOwnerThread();
mSeekRequest.Complete();
// We must decode the first samples of active streams, so we can determine
// the new stream time. So dispatch tasks to do that.
- EnsureAudioDecodeTaskQueued();
EnsureVideoDecodeTaskQueued();
+ if (!mSeekJob.mTarget.IsVideoOnly()) {
+ EnsureAudioDecodeTaskQueued();
+ }
}
void
SeekTask::OnSeekRejected(nsresult aResult)
{
AssertOwnerThread();
mSeekRequest.Complete();
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -58,17 +58,17 @@ public:
virtual void Discard();
virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration);
virtual bool NeedToResetMDSM() const;
SeekJob& GetSeekJob();
- bool Exists();
+ bool Exists() const;
protected:
SeekTask(const void* aDecoderID,
AbstractThread* aThread,
MediaDecoderReaderWrapper* aReader,
SeekJob&& aSeekJob,
const MediaInfo& aInfo,
const media::TimeUnit& aDuration,
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -164,27 +164,27 @@ OggReader::~OggReader()
}
nsresult OggReader::Init() {
int ret = ogg_sync_init(&mOggState);
NS_ENSURE_TRUE(ret == 0, NS_ERROR_FAILURE);
return NS_OK;
}
-nsresult OggReader::ResetDecode()
+nsresult OggReader::ResetDecode(TargetQueues aQueues)
{
- return ResetDecode(false);
+ return ResetDecode(false, aQueues);
}
-nsresult OggReader::ResetDecode(bool start)
+nsresult OggReader::ResetDecode(bool start, TargetQueues aQueues)
{
MOZ_ASSERT(OnTaskQueue());
nsresult res = NS_OK;
- if (NS_FAILED(MediaDecoderReader::ResetDecode())) {
+ if (NS_FAILED(MediaDecoderReader::ResetDecode(aQueues))) {
res = NS_ERROR_FAILURE;
}
// Discard any previously buffered packets/pages.
ogg_sync_reset(&mOggState);
if (mVorbisState && NS_FAILED(mVorbisState->Reset())) {
res = NS_ERROR_FAILURE;
}
--- a/dom/media/ogg/OggReader.h
+++ b/dom/media/ogg/OggReader.h
@@ -46,17 +46,17 @@ class OggReader final : public MediaDeco
public:
explicit OggReader(AbstractMediaDecoder* aDecoder);
protected:
~OggReader();
public:
nsresult Init() override;
- nsresult ResetDecode() override;
+ nsresult ResetDecode(TargetQueues aQueues = AUDIO_VIDEO) override;
bool DecodeAudioData() override;
// If the Theora granulepos has not been captured, it may read several packets
// until one with a granulepos has been captured, to ensure that all packets
// read have valid time info.
bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) override;
nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
@@ -81,25 +81,25 @@ private:
RefPtr<AudioData> SyncDecodeToFirstAudioData();
RefPtr<VideoData> SyncDecodeToFirstVideoData();
// This monitor should be taken when reading or writing to mIsChained.
ReentrantMonitor mMonitor;
// Specialized Reset() method to signal if the seek is
// to the start of the stream.
- nsresult ResetDecode(bool start);
+ nsresult ResetDecode(bool start, TargetQueues aQueues = AUDIO_VIDEO);
nsresult SeekInternal(int64_t aTime, int64_t aEndTime);
bool HasSkeleton() {
return mSkeletonState != 0 && mSkeletonState->mActive;
}
- // Seeks to the keyframe preceeding the target time using available
+ // Seeks to the keyframe preceding the target time using available
// keyframe indexes.
enum IndexedSeekResult {
SEEK_OK, // Success.
SEEK_INDEX_FAIL, // Failure due to no index, or invalid index.
SEEK_FATAL_ERROR // Error returned by a stream operation.
};
IndexedSeekResult SeekToKeyframeUsingIndex(int64_t aTarget);
--- a/dom/media/raw/RawReader.cpp
+++ b/dom/media/raw/RawReader.cpp
@@ -22,20 +22,20 @@ RawReader::RawReader(AbstractMediaDecode
MOZ_COUNT_CTOR(RawReader);
}
RawReader::~RawReader()
{
MOZ_COUNT_DTOR(RawReader);
}
-nsresult RawReader::ResetDecode()
+nsresult RawReader::ResetDecode(TargetQueues aQueues)
{
mCurrentFrame = 0;
- return MediaDecoderReader::ResetDecode();
+ return MediaDecoderReader::ResetDecode(aQueues);
}
nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
MOZ_ASSERT(OnTaskQueue());
if (!ReadFromResource(reinterpret_cast<uint8_t*>(&mMetadata),
--- a/dom/media/raw/RawReader.h
+++ b/dom/media/raw/RawReader.h
@@ -15,17 +15,17 @@ class RawReader : public MediaDecoderRea
{
public:
explicit RawReader(AbstractMediaDecoder* aDecoder);
protected:
~RawReader();
public:
- nsresult ResetDecode() override;
+ nsresult ResetDecode(TargetQueues aQueues) override;
bool DecodeAudioData() override;
bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) override;
nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) override;
RefPtr<SeekPromise> Seek(SeekTarget aTarget, int64_t aEndTime) override;
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -780,17 +780,17 @@ public:
}
RefPtr<PromiseType> p = mPromise.get();
return p.forget();
}
// Provide a Monitor that should always be held when accessing this instance.
void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
- bool IsEmpty()
+ bool IsEmpty() const
{
if (mMonitor) {
mMonitor->AssertCurrentThreadOwns();
}
return !mPromise;
}
already_AddRefed<typename PromiseType::Private> Steal()