Bug 1395922 - [P4] Make MDSM enter buffering state when MediaKeys is removed and resume the playback if setting same MediaKeys back.
MozReview-Commit-ID: KdmeGqoVgak
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1317,31 +1317,63 @@ MediaFormatReader::Init()
mVideo.mTaskQueue = new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"MFR::mVideo::mTaskQueue");
return NS_OK;
}
-void
+bool
MediaFormatReader::ResolveSetCDMPromiseIfDone(TrackType aTrack)
{
// When a CDM proxy is set, MFR would shutdown the existing MediaDataDecoder
// and would create new one for specific track in the next Update.
MOZ_ASSERT(OnTaskQueue());
+ if (mSetCDMPromise.IsEmpty()) {
+ return true;
+ }
+
+ MOZ_ASSERT(mCDMProxy);
if (mSetCDMForTracks.contains(aTrack)) {
- MOZ_ASSERT(!mSetCDMPromise.IsEmpty());
-
mSetCDMForTracks -= aTrack;
- if (mSetCDMForTracks.isEmpty()) {
- mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__);
- }
+ }
+
+ if (mSetCDMForTracks.isEmpty()) {
+ LOGV("%s : Done ", __func__);
+ mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__);
+ ScheduleUpdate(TrackInfo::kAudioTrack);
+ ScheduleUpdate(TrackInfo::kVideoTrack);
+ return true;
}
+ LOGV("%s : %s track is ready.", __func__, TrackTypeToStr(aTrack));
+ return false;
+}
+
+void
+MediaFormatReader::PrepareToSetCDMForTrack(TrackType aTrack)
+{
+ MOZ_ASSERT(OnTaskQueue());
+ LOGV("%s : %s", __func__, TrackTypeToStr(aTrack));
+
+ mSetCDMForTracks += aTrack;
+ if (mCDMProxy) {
+ // An old cdm proxy exists, so detaching old cdm proxy by shutting down
+ // MediaDataDecoder.
+ ShutdownDecoder(aTrack);
+ }
+ ScheduleUpdate(aTrack);
+}
+
+bool
+MediaFormatReader::IsDecoderWaitingForCDM(TrackType aTrack)
+{
+ MOZ_ASSERT(OnTaskQueue());
+ return IsEncrypted() && mSetCDMForTracks.contains(aTrack) && !mCDMProxy;
}
RefPtr<SetCDMPromise>
MediaFormatReader::SetCDMProxy(CDMProxy* aProxy)
{
MOZ_ASSERT(OnTaskQueue());
LOGV("SetCDMProxy (%p)", aProxy);
@@ -1352,37 +1384,39 @@ MediaFormatReader::SetCDMProxy(CDMProxy*
__func__);
}
mSetCDMPromise.RejectIfExists(
MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
"Another new CDM proxy is being set."),
__func__);
- if (HasAudio()) {
- mSetCDMForTracks += TrackInfo::kAudioTrack;
- ScheduleUpdate(TrackInfo::kAudioTrack);
- }
- if (HasVideo()) {
- mSetCDMForTracks += TrackInfo::kVideoTrack;
- ScheduleUpdate(TrackInfo::kVideoTrack);
- }
-
// Shutdown all decoders as switching CDM proxy indicates that it's
// inappropriate for the existing decoders to continue decoding via the old
// CDM proxy.
- if (!mSetCDMForTracks.isEmpty()) {
- ReleaseResources();
+ if (HasAudio()) {
+ PrepareToSetCDMForTrack(TrackInfo::kAudioTrack);
+ }
+ if (HasVideo()) {
+ PrepareToSetCDMForTrack(TrackInfo::kVideoTrack);
}
mCDMProxy = aProxy;
- if (!mInitDone || mSetCDMForTracks.isEmpty()) {
- // MFR is not initialized yet or demuxer is initialized without active
- // audio and video, the promise can be resolved directly.
+ if (IsEncrypted() && !mCDMProxy) {
+ // Release old PDMFactory which contains an EMEDecoderModule.
+ mPlatform = nullptr;
+ }
+
+ if (!mInitDone || mSetCDMForTracks.isEmpty() || !mCDMProxy) {
+ // 1) MFR is not initialized yet or
+ // 2) Demuxer is initialized without active audio and video or
+ // 3) A null cdm proxy is set
+ // the promise can be resolved directly.
+ mSetCDMForTracks.clear();
return SetCDMPromise::CreateAndResolve(/* aIgnored = */ true, __func__);
}
RefPtr<SetCDMPromise> p = mSetCDMPromise.Ensure(__func__);
return p;
}
bool
@@ -2286,18 +2320,16 @@ MediaFormatReader::Update(TrackType aTra
}
LOGV("Processing update for %s", TrackTypeToStr(aTrack));
bool needOutput = false;
auto& decoder = GetDecoderData(aTrack);
decoder.mUpdateScheduled = false;
- ResolveSetCDMPromiseIfDone(aTrack);
-
if (!mInitDone) {
return;
}
if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) {
LOGV("Skipping in progress, nothing more to do");
return;
}
@@ -2436,16 +2468,23 @@ MediaFormatReader::Update(TrackType aTra
// There is no more samples left to be decoded and we are already in
// EOS state. We can immediately reject the data promise.
LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
} else if (decoder.mWaitingForKey) {
LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for key",
TrackTypeToStr(aTrack));
decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
+ } else if (IsDecoderWaitingForCDM(aTrack)) {
+ // Rejecting the promise could lead to entering buffering state for MDSM,
+ // once a qualified(with the same key system and sessions created by the
+ // same InitData) new cdm proxy is set, decoding can be resumed.
+ LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for CDM",
+ TrackTypeToStr(aTrack));
+ decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
}
}
if (decoder.mDrainState == DrainState::DrainRequested ||
decoder.mDrainState == DrainState::PartialDrainPending) {
if (decoder.mOutput.IsEmpty()) {
DrainDecoder(aTrack);
}
@@ -2493,43 +2532,46 @@ MediaFormatReader::Update(TrackType aTra
}
return;
}
bool needInput = NeedInput(decoder);
LOGV("Update(%s) ni=%d no=%d in:%" PRIu64 " out:%" PRIu64
" qs=%u decoding:%d flushing:%d desc:%s pending:%u waiting:%d eos:%d "
- "ds:%d sid:%u",
+ "ds:%d sid:%u waitcdm:%d",
TrackTypeToStr(aTrack),
needInput,
needOutput,
decoder.mNumSamplesInput,
decoder.mNumSamplesOutput,
uint32_t(size_t(decoder.mSizeOfQueue)),
decoder.mDecodeRequest.Exists(),
decoder.mFlushing,
decoder.mDescription.get(),
uint32_t(decoder.mOutput.Length()),
decoder.mWaitingForData,
decoder.mDemuxEOS,
int32_t(decoder.mDrainState),
- decoder.mLastStreamSourceID);
-
- if (IsWaitingOnCDMResource()) {
+ decoder.mLastStreamSourceID,
+ IsDecoderWaitingForCDM(aTrack));
+
+ if (IsWaitingOnCDMResource() || !ResolveSetCDMPromiseIfDone(aTrack)) {
// If the content is encrypted, MFR won't start to create decoder until
// CDMProxy is set.
return;
}
if ((decoder.mWaitingForData &&
(!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) ||
(decoder.mWaitingForKey && decoder.mDecodeRequest.Exists())) {
// Nothing more we can do at present.
- LOGV("Still waiting for data or key.");
+ LOGV("Still waiting for data or key. data(%d)/key(%d)",
+ decoder.mWaitingForData,
+ decoder.mWaitingForKey);
return;
}
if (decoder.CancelWaitingForKey()) {
LOGV("No longer waiting for key. Resolving waiting promise");
return;
}
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -189,17 +189,16 @@ public:
RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);
// The MediaDecoderStateMachine uses various heuristics that assume that
// raw media data is arriving sequentially from a network channel. This
// makes sense in the <video src="foo"> case, but not for more advanced use
// cases like MSE.
bool UseBufferingHeuristics() const { return mTrackDemuxersMayBlock; }
- void ResolveSetCDMPromiseIfDone(TrackType aTrack);
RefPtr<SetCDMPromise> SetCDMProxy(CDMProxy* aProxy);
// Returns a string describing the state of the decoder data.
// Used for debugging purposes.
void GetMozDebugReaderData(nsACString& aString);
// Switch the video decoder to NullDecoderModule. It might takes effective
// since a few samples later depends on how much demuxed samples are already
@@ -789,15 +788,18 @@ private:
MediaEventProducer<MediaResult> mOnDecodeWarning;
RefPtr<FrameStatistics> mFrameStats;
// Used in bug 1393399 for telemetry.
const MediaDecoderOwnerID mMediaDecoderOwnerID;
+ bool ResolveSetCDMPromiseIfDone(TrackType aTrack);
+ void PrepareToSetCDMForTrack(TrackType aTrack);
MozPromiseHolder<SetCDMPromise> mSetCDMPromise;
TrackSet mSetCDMForTracks{};
+ bool IsDecoderWaitingForCDM(TrackType aTrack);
};
} // namespace mozilla
#endif