--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -57,19 +57,19 @@ TrackTypeToStr(TrackInfo::TrackType aTra
}
}
MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
MediaDataDemuxer* aDemuxer,
VideoFrameContainer* aVideoFrameContainer,
layers::LayersBackend aLayersBackend)
: MediaDecoderReader(aDecoder)
- , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2),
+ , mAudio(this, MediaData::AUDIO_DATA,
Preferences::GetUint("media.audio-max-decode-error", 3))
- , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2),
+ , mVideo(this, MediaData::VIDEO_DATA,
Preferences::GetUint("media.video-max-decode-error", 2))
, mDemuxer(aDemuxer)
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
, mLayersBackendType(aLayersBackend)
, mInitDone(false)
, mIsEncrypted(false)
@@ -557,17 +557,17 @@ MediaFormatReader::RequestVideoData(bool
if (!mVideo.HasInternalSeekPending() &&
ShouldSkip(aSkipToNextKeyframe, timeThreshold)) {
RefPtr<MediaDataPromise> p = mVideo.EnsurePromise(__func__);
SkipVideoDemuxToNextKeyFrame(timeThreshold);
return p;
}
RefPtr<MediaDataPromise> p = mVideo.EnsurePromise(__func__);
- NotifyDecodingRequested(TrackInfo::kVideoTrack);
+ ScheduleUpdate(TrackInfo::kVideoTrack);
return p;
}
void
MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure)
{
MOZ_ASSERT(OnTaskQueue());
@@ -601,17 +601,16 @@ MediaFormatReader::OnDemuxFailed(TrackTy
MOZ_ASSERT(false);
break;
}
}
void
MediaFormatReader::DoDemuxVideo()
{
- // TODO Use DecodeAhead value rather than 1.
mVideo.mDemuxRequest.Begin(mVideo.mTrackDemuxer->GetSamples(1)
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::OnVideoDemuxCompleted,
&MediaFormatReader::OnVideoDemuxFailed));
}
void
MediaFormatReader::OnVideoDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples)
@@ -652,25 +651,24 @@ MediaFormatReader::RequestAudioData()
}
if (mShutdown) {
NS_WARNING("RequestAudioData on shutdown MediaFormatReader!");
return MediaDataPromise::CreateAndReject(CANCELED, __func__);
}
RefPtr<MediaDataPromise> p = mAudio.EnsurePromise(__func__);
- NotifyDecodingRequested(TrackInfo::kAudioTrack);
+ ScheduleUpdate(TrackInfo::kAudioTrack);
return p;
}
void
MediaFormatReader::DoDemuxAudio()
{
- // TODO Use DecodeAhead value rather than 1.
mAudio.mDemuxRequest.Begin(mAudio.mTrackDemuxer->GetSamples(1)
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::OnAudioDemuxCompleted,
&MediaFormatReader::OnAudioDemuxFailed));
}
void
MediaFormatReader::OnAudioDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples)
@@ -692,26 +690,27 @@ MediaFormatReader::NotifyNewOutput(Track
auto& decoder = GetDecoderData(aTrack);
if (!decoder.mOutputRequested) {
LOG("MediaFormatReader produced output while flushing, discarding.");
return;
}
decoder.mOutput.AppendElement(aSample);
decoder.mNumSamplesOutput++;
decoder.mNumOfConsecutiveError = 0;
+ decoder.mDecodePending = false;
ScheduleUpdate(aTrack);
}
void
MediaFormatReader::NotifyInputExhausted(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
LOGV("Decoder has requested more %s data", TrackTypeToStr(aTrack));
auto& decoder = GetDecoderData(aTrack);
- decoder.mInputExhausted = true;
+ decoder.mDecodePending = false;
ScheduleUpdate(aTrack);
}
void
MediaFormatReader::NotifyDrainComplete(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
@@ -750,43 +749,31 @@ void
MediaFormatReader::NotifyEndOfStream(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mDemuxEOS = true;
ScheduleUpdate(aTrack);
}
-void
-MediaFormatReader::NotifyDecodingRequested(TrackType aTrack)
-{
- MOZ_ASSERT(OnTaskQueue());
- auto& decoder = GetDecoderData(aTrack);
- decoder.mDecodingRequested = true;
- ScheduleUpdate(aTrack);
-}
-
bool
MediaFormatReader::NeedInput(DecoderData& aDecoder)
{
- // We try to keep a few more compressed samples input than decoded samples
- // have been output, provided the state machine has requested we send it a
- // decoded sample. To account for H.264 streams which may require a longer
- // run of input than we input, decoders fire an "input exhausted" callback,
- // which overrides our "few more samples" threshold.
+ // To account for H.264 streams which may require a longer
+ // run of input than we input, decoders fire an "input exhausted" callback.
+ // The decoder will not be fed a new raw sample until either Output callback
+ // has been called, or InputExhausted was called.
return
+ (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) &&
!aDecoder.HasPendingDrain() &&
!aDecoder.HasFatalError() &&
- aDecoder.mDecodingRequested &&
!aDecoder.mDemuxRequest.Exists() &&
+ !aDecoder.mOutput.Length() &&
!aDecoder.HasInternalSeekPending() &&
- aDecoder.mOutput.Length() <= aDecoder.mDecodeAhead &&
- (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() ||
- aDecoder.mTimeThreshold.isSome() ||
- aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput <= aDecoder.mDecodeAhead);
+ !aDecoder.mDecodePending;
}
void
MediaFormatReader::ScheduleUpdate(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
if (mShutdown) {
return;
@@ -927,16 +914,17 @@ MediaFormatReader::DecodeDemuxedSamples(
MediaRawData* aSample)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
if (NS_FAILED(decoder.mDecoder->Input(aSample))) {
LOG("Unable to pass frame to decoder");
return false;
}
+ decoder.mDecodePending = true;
return true;
}
void
MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
AbstractMediaDecoder::AutoNotifyDecoded& aA)
{
MOZ_ASSERT(OnTaskQueue());
@@ -1002,17 +990,17 @@ MediaFormatReader::HandleDemuxedSamples(
decoder.mLastStreamSourceID = info->GetID();
decoder.mNextStreamSourceID.reset();
// Reset will clear our array of queued samples. So make a copy now.
nsTArray<RefPtr<MediaRawData>> samples{decoder.mQueuedSamples};
Reset(aTrack);
decoder.ShutdownDecoder();
if (sample->mKeyframe) {
decoder.mQueuedSamples.AppendElements(Move(samples));
- NotifyDecodingRequested(aTrack);
+ ScheduleUpdate(aTrack);
} else {
TimeInterval time =
TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
TimeUnit::FromMicroseconds(sample->GetEndTime()));
InternalSeekTarget seekTarget =
decoder.mTimeThreshold.refOr(InternalSeekTarget(time, false));
LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
sample->mTime);
@@ -1040,19 +1028,16 @@ MediaFormatReader::HandleDemuxedSamples(
decoder.mQueuedSamples.RemoveElementAt(0);
if (mDemuxOnly) {
// If demuxed-only case, ReturnOutput will resolve with one demuxed data.
// Then we should stop doing the iteration.
return;
}
samplesPending = true;
}
-
- // We have serviced the decoder's request for more data.
- decoder.mInputExhausted = false;
}
void
MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTarget)
{
MOZ_ASSERT(OnTaskQueue());
LOG("%s internal seek to %f",
TrackTypeToStr(aTrack), aTarget.Time().ToSeconds());
@@ -1066,17 +1051,17 @@ MediaFormatReader::InternalSeek(TrackTyp
->Then(OwnerThread(), __func__,
[self, aTrack] (media::TimeUnit aTime) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
MOZ_ASSERT(decoder.mTimeThreshold,
"Seek promise must be disconnected when timethreshold is reset");
decoder.mTimeThreshold.ref().mHasSeeked = true;
self->SetVideoDecodeThreshold();
- self->NotifyDecodingRequested(aTrack);
+ self->ScheduleUpdate(aTrack);
},
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
break;
@@ -1270,16 +1255,17 @@ MediaFormatReader::Update(TrackType aTra
if (decoder.mNeedDraining) {
DrainDecoder(aTrack);
return;
}
if (decoder.mError &&
decoder.mError.ref() == MediaDataDecoderError::DECODE_ERROR) {
+ decoder.mDecodePending = false;
decoder.mError.reset();
if (++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) {
NotifyError(aTrack);
return;
}
LOG("%s decoded error count %d", TrackTypeToStr(aTrack),
decoder.mNumOfConsecutiveError);
media::TimeUnit nextKeyframe;
@@ -1287,21 +1273,21 @@ MediaFormatReader::Update(TrackType aTra
NS_SUCCEEDED(decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe))) {
SkipVideoDemuxToNextKeyFrame(decoder.mLastSampleTime.refOr(TimeInterval()).Length());
return;
}
}
bool needInput = NeedInput(decoder);
- LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u",
- TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
+ LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d promise:%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.mLastStreamSourceID);
if (decoder.mWaitingForData &&
(!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) {
// Nothing more we can do at present.
LOGV("Still waiting for data.");
return;
}
@@ -1579,17 +1565,17 @@ void
MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping succeeded, skipped %u frames", aSkipped);
mSkipRequest.Complete();
VideoSkipReset(aSkipped);
- NotifyDecodingRequested(TrackInfo::kVideoTrack);
+ ScheduleUpdate(TrackInfo::kVideoTrack);
}
void
MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping failed, skipped %u frames", aFailure.mSkipped);
mSkipRequest.Complete();
@@ -1597,17 +1583,17 @@ MediaFormatReader::OnVideoSkipFailed(Med
switch (aFailure.mFailure) {
case DemuxerFailureReason::END_OF_STREAM: MOZ_FALLTHROUGH;
case DemuxerFailureReason::WAITING_FOR_DATA:
// Some frames may have been output by the decoder since we initiated the
// videoskip process and we know they would be late.
DropDecodedSamples(TrackInfo::kVideoTrack);
// We can't complete the skip operation, will just service a video frame
// normally.
- NotifyDecodingRequested(TrackInfo::kVideoTrack);
+ ScheduleUpdate(TrackInfo::kVideoTrack);
break;
case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH;
case DemuxerFailureReason::SHUTDOWN:
if (mVideo.HasPromise()) {
mVideo.RejectPromise(CANCELED, __func__);
}
break;
default:
@@ -2015,22 +2001,21 @@ MediaFormatReader::GetMozDebugReaderData
MonitorAutoLock mon(mVideo.mMonitor);
videoName = mVideo.mDescription;
}
result += nsPrintfCString("audio decoder: %s\n", audioName);
result += nsPrintfCString("audio frames decoded: %lld\n",
mAudio.mNumSamplesOutputTotal);
if (HasAudio()) {
- result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n",
+ result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n",
NeedInput(mAudio), mAudio.HasPromise(),
- mAudio.mInputExhausted,
+ mAudio.mDecodePending,
mAudio.mDemuxRequest.Exists(),
int(mAudio.mQueuedSamples.Length()),
- mAudio.mDecodingRequested,
mAudio.mTimeThreshold
? mAudio.mTimeThreshold.ref().Time().ToSeconds()
: -1.0,
mAudio.mTimeThreshold
? mAudio.mTimeThreshold.ref().mHasSeeked
: -1,
mAudio.mNumSamplesInput, mAudio.mNumSamplesOutput,
unsigned(size_t(mAudio.mSizeOfQueue)),
@@ -2039,22 +2024,21 @@ MediaFormatReader::GetMozDebugReaderData
}
result += nsPrintfCString("video decoder: %s\n", videoName);
result += nsPrintfCString("hardware video decoding: %s\n",
VideoIsHardwareAccelerated() ? "enabled" : "disabled");
result += nsPrintfCString("video frames decoded: %lld (skipped:%lld)\n",
mVideo.mNumSamplesOutputTotal,
mVideo.mNumSamplesSkippedTotal);
if (HasVideo()) {
- result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n",
+ result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n",
NeedInput(mVideo), mVideo.HasPromise(),
- mVideo.mInputExhausted,
+ mVideo.mDecodePending,
mVideo.mDemuxRequest.Exists(),
int(mVideo.mQueuedSamples.Length()),
- mVideo.mDecodingRequested,
mVideo.mTimeThreshold
? mVideo.mTimeThreshold.ref().Time().ToSeconds()
: -1.0,
mVideo.mTimeThreshold
? mVideo.mTimeThreshold.ref().mHasSeeked
: -1,
mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput,
unsigned(size_t(mVideo.mSizeOfQueue)),
@@ -2082,14 +2066,14 @@ MediaFormatReader::SetBlankDecode(TrackT
if (decoder.mIsBlankDecode == aIsBlankDecode) {
return;
}
decoder.mIsBlankDecode = aIsBlankDecode;
decoder.Flush();
decoder.ShutdownDecoder();
- NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate().
+ ScheduleUpdate(TrackInfo::kVideoTrack);
return;
}
} // namespace mozilla
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -164,17 +164,16 @@ 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, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR);
void NotifyWaitingForData(TrackType aTrack);
void NotifyEndOfStream(TrackType aTrack);
- void NotifyDecodingRequested(TrackType aTrack);
void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
// Initializes mLayersBackendType if possible.
void InitLayersBackendType();
// DecoderCallback proxies the MediaDataDecoderCallback calls to these
// functions.
@@ -226,32 +225,29 @@ private:
private:
MediaFormatReader* mReader;
TrackType mType;
};
struct DecoderData {
DecoderData(MediaFormatReader* aOwner,
MediaData::Type aType,
- uint32_t aDecodeAhead,
uint32_t aNumOfMaxError)
: mOwner(aOwner)
, mType(aType)
, mMonitor("DecoderData")
, mDescription("shutdown")
- , mDecodeAhead(aDecodeAhead)
, mUpdateScheduled(false)
, mDemuxEOS(false)
, mWaitingForData(false)
, mReceivedNewData(false)
, mDiscontinuity(true)
, mDecoderInitialized(false)
- , mDecodingRequested(false)
, mOutputRequested(false)
- , mInputExhausted(false)
+ , mDecodePending(false)
, mNeedDraining(false)
, mDraining(false)
, mDrainComplete(false)
, mNumOfConsecutiveError(0)
, mMaxConsecutiveError(aNumOfMaxError)
, mNumSamplesInput(0)
, mNumSamplesOutput(0)
, mNumSamplesOutputTotal(0)
@@ -284,17 +280,16 @@ private:
if (mDecoder) {
mDecoder->Shutdown();
}
mDescription = "shutdown";
mDecoder = nullptr;
}
// Only accessed from reader's task queue.
- uint32_t mDecodeAhead;
bool mUpdateScheduled;
bool mDemuxEOS;
bool mWaitingForData;
bool mReceivedNewData;
bool mDiscontinuity;
// Pending seek.
MozPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mSeekRequest;
@@ -309,21 +304,24 @@ private:
return !mWaitingPromise.IsEmpty();
}
// 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;
- // Set when decoding can proceed. It is reset when a decoding promise is
- // rejected or prior a seek operation.
- bool mDecodingRequested;
bool mOutputRequested;
- bool mInputExhausted;
+ // Set to true once the MediaDataDecoder has been fed a compressed sample.
+ // No more sample will be passed to the decoder while true.
+ // mDecodePending is reset when:
+ // 1- The decoder returns a sample
+ // 2- The decoder calls InputExhausted
+ // 3- The decoder is Flushed or Reset.
+ bool mDecodePending;
bool mNeedDraining;
bool mDraining;
bool mDrainComplete;
bool HasPendingDrain() const
{
return mDraining || mDrainComplete;
}
@@ -373,19 +371,18 @@ private:
// Flush the decoder if present and reset decoding related data.
// Decoding will be suspended until mInputRequested is set again.
// Following a flush, the decoder is ready to accept any new data.
void Flush()
{
if (mDecoder) {
mDecoder->Flush();
}
- mDecodingRequested = false;
mOutputRequested = false;
- mInputExhausted = false;
+ mDecodePending = false;
mOutput.Clear();
mNumSamplesInput = 0;
mNumSamplesOutput = 0;
mSizeOfQueue = 0;
mDraining = false;
mDrainComplete = false;
}
@@ -395,20 +392,19 @@ private:
// The track demuxer is *not* reset.
void ResetState()
{
MOZ_ASSERT(mOwner->OnTaskQueue());
mDemuxEOS = false;
mWaitingForData = false;
mDiscontinuity = true;
mQueuedSamples.Clear();
- mDecodingRequested = false;
mOutputRequested = false;
- mInputExhausted = false;
mNeedDraining = false;
+ mDecodePending = false;
mDraining = false;
mDrainComplete = false;
mTimeThreshold.reset();
mLastSampleTime.reset();
mOutput.Clear();
mNumSamplesInput = 0;
mNumSamplesOutput = 0;
mSizeOfQueue = 0;
@@ -439,19 +435,18 @@ private:
bool mIsBlankDecode;
};
class DecoderDataWithPromise : public DecoderData {
public:
DecoderDataWithPromise(MediaFormatReader* aOwner,
MediaData::Type aType,
- uint32_t aDecodeAhead,
uint32_t aNumOfMaxError)
- : DecoderData(aOwner, aType, aDecodeAhead, aNumOfMaxError)
+ : DecoderData(aOwner, aType, aNumOfMaxError)
, mHasPromise(false)
{}
bool HasPromise() const override
{
return mHasPromise;
}
@@ -470,17 +465,16 @@ private:
mHasPromise = false;
}
void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
const char* aMethodName) override
{
MOZ_ASSERT(mOwner->OnTaskQueue());
mPromise.Reject(aReason, aMethodName);
- mDecodingRequested = false;
mHasPromise = false;
}
private:
MozPromiseHolder<MediaDataPromise> mPromise;
Atomic<bool> mHasPromise;
};