From 8a239a32e3cec4fafc88518d63c866a430b99738 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 20 Jan 2015 15:20:43 +1300 Subject: [PATCH] Bug 1123498 - Make MP4Reader skip-to-next-keyframe less aggressively. r=mattwoodrow --- dom/media/fmp4/MP4Reader.cpp | 28 +++++++++++++++++-- dom/media/fmp4/MP4Reader.h | 3 ++ media/libstagefright/binding/Index.cpp | 25 +++++++++++++++++ .../binding/include/mp4_demuxer/Index.h | 1 + .../binding/include/mp4_demuxer/mp4_demuxer.h | 5 ++++ media/libstagefright/binding/mp4_demuxer.cpp | 16 ++++++++++- 6 files changed, 75 insertions(+), 3 deletions(-) diff --git a/dom/media/fmp4/MP4Reader.cpp b/dom/media/fmp4/MP4Reader.cpp index 66ffa3e724e8..37e62641063c 100644 --- a/dom/media/fmp4/MP4Reader.cpp +++ b/dom/media/fmp4/MP4Reader.cpp @@ -107,7 +107,6 @@ InvokeAndRetry(ThisType* aThisVal, ReturnType(ThisType::*aMethod)(), MP4Stream* } } - MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder) : MediaDecoderReader(aDecoder) , mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2)) @@ -196,6 +195,7 @@ MP4Reader::InitLayersBackendType() } static bool sIsEMEEnabled = false; +static bool sDemuxSkipToNextKeyframe = true; nsresult MP4Reader::Init(MediaDecoderReader* aCloneDonor) @@ -217,6 +217,7 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor) if (!sSetupPrefCache) { sSetupPrefCache = true; Preferences::AddBoolVarCache(&sIsEMEEnabled, "media.eme.enabled", false); + Preferences::AddBoolVarCache(&sDemuxSkipToNextKeyframe, "media.fmp4.demux-skip", true); } return NS_OK; @@ -519,6 +520,29 @@ MP4Reader::GetDecoderData(TrackType aTrack) return mVideo; } +Microseconds +MP4Reader::GetNextKeyframeTime() +{ + MonitorAutoLock mon(mDemuxerMonitor); + return mDemuxer->GetNextKeyframeTime(); +} + +bool +MP4Reader::ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold) +{ + // The MP4Reader doesn't do normal skip-to-next-keyframe if the demuxer + // has exposes where the next keyframe is. We can then instead skip only + // if the time threshold (the current playback position) is after the next + // keyframe in the stream. This means we'll only skip frames that we have + // no hope of ever playing. + Microseconds nextKeyframe = -1; + if (!sDemuxSkipToNextKeyframe || + (nextKeyframe = GetNextKeyframeTime()) == -1) { + return aSkipToNextKeyframe; + } + return nextKeyframe < aTimeThreshold; +} + nsRefPtr MP4Reader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) @@ -537,7 +561,7 @@ MP4Reader::RequestVideoData(bool aSkipToNextKeyframe, MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder); bool eos = false; - if (aSkipToNextKeyframe) { + if (ShouldSkip(aSkipToNextKeyframe, aTimeThreshold)) { uint32_t parsed = 0; eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed); if (!eos && NS_FAILED(mVideo.mDecoder->Flush())) { diff --git a/dom/media/fmp4/MP4Reader.h b/dom/media/fmp4/MP4Reader.h index 67ca44c91c59..626988aaa77b 100644 --- a/dom/media/fmp4/MP4Reader.h +++ b/dom/media/fmp4/MP4Reader.h @@ -121,6 +121,9 @@ private: bool IsWaitingOnCodecResource(); virtual bool IsWaitingOnCDMResource() MOZ_OVERRIDE; + Microseconds GetNextKeyframeTime(); + bool ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold); + size_t SizeOfQueue(TrackType aTrack); nsRefPtr mStream; diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index aa57957b7f0a..12c79a78d18f 100644 --- a/media/libstagefright/binding/Index.cpp +++ b/media/libstagefright/binding/Index.cpp @@ -186,6 +186,31 @@ void SampleIterator::Seek(Microseconds aTime) mCurrentSample = syncSample; } +Microseconds +SampleIterator::GetNextKeyframeTime() +{ + nsTArray& moofs = mIndex->mMoofParser->Moofs(); + size_t sample = mCurrentSample + 1; + size_t moof = mCurrentMoof; + while (true) { + while (true) { + if (moof == moofs.Length()) { + return -1; + } + if (sample < moofs[moof].mIndex.Length()) { + break; + } + sample = 0; + ++moof; + } + if (moofs[moof].mIndex[sample].mSync) { + return moofs[moof].mIndex[sample].mDecodeTime; + } + ++sample; + } + MOZ_ASSERT(false); // should not be reached. +} + Index::Index(const stagefright::Vector& aIndex, Stream* aSource, uint32_t aTrackId, Microseconds aTimestampOffset, Monitor* aMonitor) diff --git a/media/libstagefright/binding/include/mp4_demuxer/Index.h b/media/libstagefright/binding/include/mp4_demuxer/Index.h index eb9ac609b1d1..4ac33fe342e7 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/Index.h +++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h @@ -22,6 +22,7 @@ public: explicit SampleIterator(Index* aIndex); MP4Sample* GetNext(); void Seek(Microseconds aTime); + Microseconds GetNextKeyframeTime(); private: Sample* Get(); diff --git a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h index 7d0685016530..d87d31b1d87a 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h +++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h @@ -73,6 +73,10 @@ public: int64_t GetEvictionOffset(Microseconds aTime); + // Returns timestamp of next keyframe, or -1 if demuxer can't + // report this. + Microseconds GetNextKeyframeTime(); + private: AudioDecoderConfig mAudioConfig; VideoDecoderConfig mVideoConfig; @@ -84,6 +88,7 @@ private: nsTArray> mCachedTimeRanges; Microseconds mTimestampOffset; Monitor* mMonitor; + Microseconds mNextKeyframeTime; }; } // namespace mozilla diff --git a/media/libstagefright/binding/mp4_demuxer.cpp b/media/libstagefright/binding/mp4_demuxer.cpp index e77e01c026e6..f366c8638922 100644 --- a/media/libstagefright/binding/mp4_demuxer.cpp +++ b/media/libstagefright/binding/mp4_demuxer.cpp @@ -75,7 +75,8 @@ private: MP4Demuxer::MP4Demuxer(Stream* source, Microseconds aTimestampOffset, Monitor* aMonitor) : mPrivate(new StageFrightPrivate()), mSource(source), - mTimestampOffset(aTimestampOffset), mMonitor(aMonitor) + mTimestampOffset(aTimestampOffset), mMonitor(aMonitor), + mNextKeyframeTime(-1) { mPrivate->mExtractor = new MPEG4Extractor(new DataSourceAdapter(source)); } @@ -248,6 +249,9 @@ MP4Demuxer::DemuxVideoSample() sample->crypto.mode = mVideoConfig.crypto.mode; sample->crypto.key.AppendElements(mVideoConfig.crypto.key); } + if (sample->composition_timestamp >= mNextKeyframeTime) { + mNextKeyframeTime = mPrivate->mVideoIterator->GetNextKeyframeTime(); + } } return sample.forget(); } @@ -333,4 +337,14 @@ MP4Demuxer::GetEvictionOffset(Microseconds aTime) return offset == std::numeric_limits::max() ? -1 : offset; } +Microseconds +MP4Demuxer::GetNextKeyframeTime() +{ + mMonitor->AssertCurrentThreadOwns(); + if (!mPrivate->mVideoIterator) { + return -1; + } + return mNextKeyframeTime; +} + } // namespace mp4_demuxer