--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -212,16 +212,18 @@ public:
virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) = 0;
virtual bool HandleAudioCaptured() { return false; }
virtual RefPtr<ShutdownPromise> HandleShutdown();
virtual void HandleVideoSuspendTimeout() = 0;
+ virtual void HandleResumeVideoDecoding();
+
virtual void DumpDebugInfo() {}
protected:
using Master = MediaDecoderStateMachine;
explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
TaskQueue* OwnerThread() const { return mMaster->mTaskQueue; }
MediaResource* Resource() const { return mMaster->mResource; }
MediaDecoderReaderWrapper* Reader() const { return mMaster->mReader; }
@@ -269,16 +271,17 @@ protected:
class MediaDecoderStateMachine::DecodeMetadataState
: public MediaDecoderStateMachine::StateObject
{
public:
explicit DecodeMetadataState(Master* aPtr) : StateObject(aPtr) {}
void Enter()
{
+ MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
MOZ_ASSERT(!mMetadataRequest.Exists());
SLOG("Dispatching AsyncReadMetadata");
// Set mode to METADATA since we are about to read metadata.
Resource()->SetReadMode(MediaCacheStream::MODE_METADATA);
// We disconnect mMetadataRequest in Exit() so it is fine to capture
// a raw pointer here.
@@ -314,16 +317,22 @@ public:
return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
}
void HandleVideoSuspendTimeout() override
{
// Do nothing since no decoders are created yet.
}
+ void HandleResumeVideoDecoding() override
+ {
+ // We never suspend video decoding in this state.
+ MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+ }
+
private:
void OnMetadataRead(MetadataHolder* aMetadata);
void OnMetadataNotRead(const MediaResult& aError)
{
mMetadataRequest.Complete();
SWARN("Decode metadata failed, shutting down decoder");
mMaster->DecodeError(aError);
@@ -338,17 +347,21 @@ private:
};
class MediaDecoderStateMachine::WaitForCDMState
: public MediaDecoderStateMachine::StateObject
{
public:
explicit WaitForCDMState(Master* aPtr) : StateObject(aPtr) {}
- void Enter(bool aPendingDormant) { mPendingDormant = aPendingDormant; }
+ void Enter(bool aPendingDormant)
+ {
+ MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
+ mPendingDormant = aPendingDormant;
+ }
State GetState() const override
{
return DECODER_STATE_WAIT_FOR_CDM;
}
bool HandleDormant(bool aDormant) override;
@@ -362,16 +375,22 @@ public:
return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
}
void HandleVideoSuspendTimeout() override
{
// Do nothing since no decoders are created yet.
}
+ void HandleResumeVideoDecoding() override
+ {
+ // We never suspend video decoding in this state.
+ MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+ }
+
private:
bool mPendingDormant = false;
};
class MediaDecoderStateMachine::DormantState
: public MediaDecoderStateMachine::StateObject
{
public:
@@ -400,16 +419,21 @@ public:
mMaster->mQueuedSeek.mTarget = aTarget;
return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
}
void HandleVideoSuspendTimeout() override
{
// Do nothing since we've released decoders in Enter().
}
+
+ void HandleResumeVideoDecoding() override
+ {
+ // Do nothing since we won't resume decoding until exiting dormant.
+ }
};
class MediaDecoderStateMachine::DecodingFirstFrameState
: public MediaDecoderStateMachine::StateObject
{
public:
explicit DecodingFirstFrameState(Master* aPtr) : StateObject(aPtr) {}
@@ -442,16 +466,22 @@ public:
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleVideoSuspendTimeout() override
{
// Do nothing for we need to decode the 1st video frame to get the dimensions.
}
+ void HandleResumeVideoDecoding() override
+ {
+ // We never suspend video decoding in this state.
+ MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+ }
+
private:
// Notify FirstFrameLoaded if having decoded first frames and
// transition to SEEKING if there is any pending seek, or DECODING otherwise.
void MaybeFinishDecodeFirstFrame();
};
class MediaDecoderStateMachine::DecodingState
: public MediaDecoderStateMachine::StateObject
@@ -724,16 +754,22 @@ public:
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleVideoSuspendTimeout() override
{
// Do nothing since we want a valid video frame to show when seek is done.
}
+ void HandleResumeVideoDecoding() override
+ {
+ // We set mVideoDecodeSuspended to false in Enter().
+ MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+ }
+
private:
void OnSeekTaskResolved(const SeekTaskResolveValue& aValue)
{
mSeekTaskRequest.Complete();
if (aValue.mSeekedAudioData) {
mMaster->Push(aValue.mSeekedAudioData, MediaData::AUDIO_DATA);
mMaster->mDecodedAudioEndTime = std::max(
@@ -966,16 +1002,21 @@ public:
MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
return nullptr;
}
void HandleVideoSuspendTimeout() override
{
MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
}
+
+ void HandleResumeVideoDecoding() override
+ {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
+ }
};
bool
MediaDecoderStateMachine::
StateObject::HandleDormant(bool aDormant)
{
if (!aDormant) {
return true;
@@ -994,16 +1035,88 @@ StateObject::HandleDormant(bool aDormant
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::
StateObject::HandleShutdown()
{
return SetState<ShutdownState>();
}
+static void
+ReportRecoveryTelemetry(const TimeStamp& aRecoveryStart,
+ const MediaInfo& aMediaInfo,
+ bool aIsHardwareAccelerated)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!aMediaInfo.HasVideo()) {
+ return;
+ }
+
+ // Keyed by audio+video or video alone, hardware acceleration,
+ // and by a resolution range.
+ nsCString key(aMediaInfo.HasAudio() ? "AV" : "V");
+ key.AppendASCII(aIsHardwareAccelerated ? "(hw)," : ",");
+ static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
+ { 240, "0-240" },
+ { 480, "241-480" },
+ { 720, "481-720" },
+ { 1080, "721-1080" },
+ { 2160, "1081-2160" }
+ };
+ const char* resolution = "2161+";
+ int32_t height = aMediaInfo.mVideo.mImage.height;
+ for (const auto& res : sResolutions) {
+ if (height <= res.mH) {
+ resolution = res.mRes;
+ break;
+ }
+ }
+ key.AppendASCII(resolution);
+
+ TimeDuration duration = TimeStamp::Now() - aRecoveryStart;
+ double duration_ms = duration.ToMilliseconds();
+ Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
+ key,
+ uint32_t(duration_ms + 0.5));
+ Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
+ NS_LITERAL_CSTRING("All"),
+ uint32_t(duration_ms + 0.5));
+}
+
+void
+MediaDecoderStateMachine::
+StateObject::HandleResumeVideoDecoding()
+{
+ MOZ_ASSERT(mMaster->mVideoDecodeSuspended);
+
+ // Start counting recovery time from right now.
+ TimeStamp start = TimeStamp::Now();
+
+ // Local reference to mInfo, so that it will be copied in the lambda below.
+ auto& info = Info();
+ bool hw = Reader()->VideoIsHardwareAccelerated();
+
+ // Start video-only seek to the current time.
+ SeekJob seekJob;
+
+ const SeekTarget::Type type = mMaster->HasAudio()
+ ? SeekTarget::Type::Accurate
+ : SeekTarget::Type::PrevSyncPoint;
+
+ seekJob.mTarget = SeekTarget(mMaster->GetMediaTime(),
+ type,
+ MediaDecoderEventVisibility::Suppressed,
+ true /* aVideoOnly */);
+
+ SetState<SeekingState>(Move(seekJob))->Then(
+ AbstractThread::MainThread(), __func__,
+ [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
+ [](){});
+}
+
void
MediaDecoderStateMachine::
DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
{
mMetadataRequest.Complete();
// Set mode to PLAYBACK after reading metadata.
Resource()->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
@@ -1106,16 +1219,18 @@ DecodingFirstFrameState::Enter()
}
// Transition to DECODING if we've decoded first frames.
if (mMaster->mSentFirstFrameLoadedEvent) {
SetState<DecodingState>();
return;
}
+ MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
+
// Dispatch tasks to decode first frames.
mMaster->DispatchDecodeTasksIfNeeded();
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
DecodingFirstFrameState::HandleSeek(SeekTarget aTarget)
{
@@ -2270,57 +2385,16 @@ void MediaDecoderStateMachine::PlayState
{
DECODER_LOG("Unexpected state - Bailing out of PlayInternal()");
return;
}
ScheduleStateMachine();
}
-static void
-ReportRecoveryTelemetry(const TimeStamp& aRecoveryStart,
- const MediaInfo& aMediaInfo,
- bool aIsHardwareAccelerated)
-{
- MOZ_ASSERT(NS_IsMainThread());
- if (!aMediaInfo.HasVideo()) {
- return;
- }
-
- // Keyed by audio+video or video alone, hardware acceleration,
- // and by a resolution range.
- nsCString key(aMediaInfo.HasAudio() ? "AV" : "V");
- key.AppendASCII(aIsHardwareAccelerated ? "(hw)," : ",");
- static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
- { 240, "0-240" },
- { 480, "241-480" },
- { 720, "481-720" },
- { 1080, "721-1080" },
- { 2160, "1081-2160" }
- };
- const char* resolution = "2161+";
- int32_t height = aMediaInfo.mVideo.mImage.height;
- for (const auto& res : sResolutions) {
- if (height <= res.mH) {
- resolution = res.mRes;
- break;
- }
- }
- key.AppendASCII(resolution);
-
- TimeDuration duration = TimeStamp::Now() - aRecoveryStart;
- double duration_ms = duration.ToMilliseconds();
- Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
- key,
- uint32_t(duration_ms + 0.5));
- Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
- NS_LITERAL_CSTRING("All"),
- uint32_t(duration_ms + 0.5));
-}
-
void MediaDecoderStateMachine::VisibilityChanged()
{
MOZ_ASSERT(OnTaskQueue());
DECODER_LOG("VisibilityChanged: mIsVisible=%d, "
"mVideoDecodeSuspended=%c, mIsReaderSuspended=%d",
mIsVisible.Ref(), mVideoDecodeSuspended ? 'T' : 'F', mIsReaderSuspended.Ref());
// Start timer to trigger suspended decoding state when going invisible.
@@ -2335,49 +2409,17 @@ void MediaDecoderStateMachine::Visibilit
}
// Resuming from suspended decoding
// If suspend timer exists, destroy it.
mVideoDecodeSuspendTimer.Reset();
if (mVideoDecodeSuspended) {
-
- if (mIsReaderSuspended) {
- return;
- }
-
- // If an existing seek is in flight don't bother creating a new
- // one to catch up.
- if (mState == DECODER_STATE_SEEKING || mQueuedSeek.Exists()) {
- return;
- }
-
- // Start counting recovery time from right now.
- TimeStamp start = TimeStamp::Now();
- // Local reference to mInfo, so that it will be copied in the lambda below.
- auto& info = Info();
- bool hw = mReader->VideoIsHardwareAccelerated();
-
- // Start video-only seek to the current time.
- SeekJob seekJob;
-
- const SeekTarget::Type type = HasAudio()
- ? SeekTarget::Type::Accurate
- : SeekTarget::Type::PrevSyncPoint;
-
- seekJob.mTarget = SeekTarget(GetMediaTime(),
- type,
- MediaDecoderEventVisibility::Suppressed,
- true /* aVideoOnly */);
-
- mStateObj->SetState<SeekingState>(Move(seekJob))->Then(
- AbstractThread::MainThread(), __func__,
- [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
- [](){});
+ mStateObj->HandleResumeVideoDecoding();
}
}
void MediaDecoderStateMachine::BufferedRangeUpdated()
{
MOZ_ASSERT(OnTaskQueue());
// While playing an unseekable stream of unknown duration, mObservedDuration