Bug 1312886: P5. Enter buffering mode when waiting for a key. r?cpearce
MozReview-Commit-ID: CB3e02WEJiz
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -735,16 +735,31 @@ MediaFormatReader::NotifyWaitingForData(
decoder.mWaitingForData = true;
if (decoder.mTimeThreshold) {
decoder.mTimeThreshold.ref().mWaiting = true;
}
ScheduleUpdate(aTrack);
}
void
+MediaFormatReader::NotifyWaitingForKey(TrackType aTrack)
+{
+ MOZ_ASSERT(OnTaskQueue());
+ auto& decoder = GetDecoderData(aTrack);
+ if (mDecoder) {
+ mDecoder->NotifyWaitingForKey();
+ }
+ if (!decoder.mDecodePending) {
+ LOGV("WaitingForKey received while no pending decode. Ignoring");
+ }
+ decoder.mWaitingForKey = true;
+ ScheduleUpdate(aTrack);
+}
+
+void
MediaFormatReader::NotifyEndOfStream(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mDemuxEOS = true;
ScheduleUpdate(aTrack);
}
@@ -861,17 +876,17 @@ MediaFormatReader::UpdateReceivedNewData
ScheduleSeek();
return true;
}
if (decoder.HasInternalSeekPending() || decoder.HasWaitingPromise()) {
if (decoder.HasInternalSeekPending()) {
LOG("Attempting Internal Seek");
InternalSeek(aTrack, decoder.mTimeThreshold.ref());
}
- if (decoder.HasWaitingPromise()) {
+ if (decoder.HasWaitingPromise() && !decoder.IsWaiting()) {
MOZ_ASSERT(!decoder.HasPromise());
LOG("We have new data. Resolving WaitingPromise");
decoder.mWaitingPromise.Resolve(decoder.mType, __func__);
}
return true;
}
return false;
}
@@ -1234,16 +1249,20 @@ MediaFormatReader::Update(TrackType aTra
} else if (decoder.mDemuxEOS && !decoder.mNeedDraining &&
!decoder.HasPendingDrain() && decoder.mQueuedSamples.IsEmpty()) {
// It is possible to transition from WAITING_FOR_DATA directly to EOS
// state during the internal seek; in which case no draining would occur.
// 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__);
}
}
if (decoder.mNeedDraining) {
DrainDecoder(aTrack);
return;
}
@@ -1263,29 +1282,40 @@ MediaFormatReader::Update(TrackType aTra
return;
} else if (aTrack == TrackType::kAudioTrack) {
decoder.Flush();
}
}
bool needInput = NeedInput(decoder);
- LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d promise:%d sid:%u",
+ LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d promise:%d wfk:%d sid:%u",
TrackTypeToStr(aTrack), needInput, needOutput, decoder.mDecodePending,
decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
- decoder.mWaitingForData, decoder.HasPromise(), decoder.mLastStreamSourceID);
+ decoder.mWaitingForData, decoder.HasPromise(),
+ decoder.mWaitingForKey, decoder.mLastStreamSourceID);
- if (decoder.mWaitingForData &&
- (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) {
+ if ((decoder.mWaitingForData &&
+ (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) ||
+ (decoder.mWaitingForKey && decoder.mDecodePending)) {
// Nothing more we can do at present.
- LOGV("Still waiting for data.");
+ LOGV("Still waiting for data or key.");
return;
}
+ if (decoder.mWaitingForKey) {
+ decoder.mWaitingForKey = false;
+ if (decoder.HasWaitingPromise() && !decoder.IsWaiting()) {
+ LOGV("No longer waiting for key. Resolving waiting promise");
+ decoder.mWaitingPromise.Resolve(decoder.mType, __func__);
+ return;
+ }
+ }
+
if (!needInput) {
LOGV("No need for additional input (pending:%u)",
uint32_t(decoder.mOutput.Length()));
return;
}
// Demux samples if we don't have some.
RequestDemuxSamples(aTrack);
@@ -1351,18 +1381,18 @@ MediaFormatReader::SizeOfQueue(TrackType
RefPtr<MediaDecoderReader::WaitForDataPromise>
MediaFormatReader::WaitForData(MediaData::Type aType)
{
MOZ_ASSERT(OnTaskQueue());
TrackType trackType = aType == MediaData::VIDEO_DATA ?
TrackType::kVideoTrack : TrackType::kAudioTrack;
auto& decoder = GetDecoderData(trackType);
- if (!decoder.mWaitingForData) {
- // We aren't waiting for data any longer.
+ if (!decoder.IsWaiting()) {
+ // We aren't waiting for anything.
return WaitForDataPromise::CreateAndResolve(decoder.mType, __func__);
}
RefPtr<WaitForDataPromise> p = decoder.mWaitingPromise.Ensure(__func__);
ScheduleUpdate(trackType);
return p;
}
nsresult
@@ -1451,16 +1481,25 @@ MediaFormatReader::Error(TrackType aTrac
{
RefPtr<nsIRunnable> task =
NewRunnableMethod<TrackType, MediaResult>(
this, &MediaFormatReader::NotifyError, aTrack, aError);
OwnerThread()->Dispatch(task.forget());
}
void
+MediaFormatReader::WaitingForKey(TrackType aTrack)
+{
+ RefPtr<nsIRunnable> task =
+ NewRunnableMethod<TrackType>(
+ this, &MediaFormatReader::NotifyWaitingForKey, aTrack);
+ OwnerThread()->Dispatch(task.forget());
+}
+
+void
MediaFormatReader::Reset(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Reset(%s) BEGIN", TrackTypeToStr(aTrack));
auto& decoder = GetDecoderData(aTrack);
decoder.ResetState();
@@ -1486,24 +1525,16 @@ MediaFormatReader::DropDecodedSamples(Tr
decoder.mOutput.Clear();
decoder.mSizeOfQueue -= lengthDecodedQueue;
if (aTrack == TrackInfo::kVideoTrack && mDecoder) {
mDecoder->NotifyDecodedFrames({ 0, 0, lengthDecodedQueue });
}
}
void
-MediaFormatReader::WaitingForKey(TrackType aTrack)
-{
- if (mDecoder) {
- mDecoder->NotifyWaitingForKey();
- }
-}
-
-void
MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping up to %lld", aTimeThreshold.ToMicroseconds());
// We've reached SkipVideoDemuxToNextKeyFrame when our decoding is late.
// As such we can drop all already decoded samples and discard all pending
// samples.
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -160,16 +160,17 @@ private:
// Drain the current decoder.
void DrainDecoder(TrackType aTrack);
void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
void NotifyInputExhausted(TrackType aTrack);
void NotifyDrainComplete(TrackType aTrack);
void NotifyError(TrackType aTrack, const MediaResult& aError);
void NotifyWaitingForData(TrackType aTrack);
+ void NotifyWaitingForKey(TrackType aTrack);
void NotifyEndOfStream(TrackType aTrack);
void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
// Initializes mLayersBackendType if possible.
void InitLayersBackendType();
// DecoderCallback proxies the MediaDataDecoderCallback calls to these
@@ -230,16 +231,17 @@ private:
uint32_t aNumOfMaxError)
: mOwner(aOwner)
, mType(aType)
, mMonitor("DecoderData")
, mDescription("shutdown")
, mUpdateScheduled(false)
, mDemuxEOS(false)
, mWaitingForData(false)
+ , mWaitingForKey(false)
, mReceivedNewData(false)
, mDecoderInitialized(false)
, mOutputRequested(false)
, mDecodePending(false)
, mNeedDraining(false)
, mDraining(false)
, mDrainComplete(false)
, mNumOfConsecutiveError(0)
@@ -279,30 +281,38 @@ private:
mDescription = "shutdown";
mDecoder = nullptr;
}
// Only accessed from reader's task queue.
bool mUpdateScheduled;
bool mDemuxEOS;
bool mWaitingForData;
+ bool mWaitingForKey;
bool mReceivedNewData;
// Pending seek.
MozPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mSeekRequest;
// Queued demux samples waiting to be decoded.
nsTArray<RefPtr<MediaRawData>> mQueuedSamples;
MozPromiseRequestHolder<MediaTrackDemuxer::SamplesPromise> mDemuxRequest;
+ // A WaitingPromise is pending if the demuxer is waiting for data or
+ // if the decoder is waiting for a key.
MozPromiseHolder<WaitForDataPromise> mWaitingPromise;
- bool HasWaitingPromise()
+ bool HasWaitingPromise() const
{
MOZ_ASSERT(mOwner->OnTaskQueue());
return !mWaitingPromise.IsEmpty();
}
+ bool IsWaiting() const
+ {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ return mWaitingForData || mWaitingForKey;
+ }
// MediaDataDecoder handler's variables.
// Decoder initialization promise holder.
MozPromiseRequestHolder<MediaDataDecoder::InitPromise> mInitPromise;
// False when decoder is created. True when decoder Init() promise is resolved.
bool mDecoderInitialized;
bool mOutputRequested;
// Set to true once the MediaDataDecoder has been fed a compressed sample.
@@ -386,16 +396,17 @@ private:
// (pending demuxed and decoded).
// Decoding will be suspended until mInputRequested is set again.
// The track demuxer is *not* reset.
void ResetState()
{
MOZ_ASSERT(mOwner->OnTaskQueue());
mDemuxEOS = false;
mWaitingForData = false;
+ mWaitingForKey = false;
mQueuedSamples.Clear();
mOutputRequested = false;
mNeedDraining = false;
mDecodePending = false;
mDraining = false;
mDrainComplete = false;
mTimeThreshold.reset();
mLastSampleTime.reset();