Bug 1123498 - Make MP4Reader skip-to-next-keyframe less aggressively. r=mattwoodrow, a=sledru
authorChris Pearce <cpearce@mozilla.com>
Tue, 20 Jan 2015 15:20:43 +1300
changeset 242954 cee6bfbbecd7
parent 242953 94e7cb795a05
child 242955 8691f7169392
push id4344
push userryanvm@gmail.com
push date2015-01-20 17:02 +0000
treeherdermozilla-beta@ea7deca21c27 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, sledru
bugs1123498
milestone36.0
Bug 1123498 - Make MP4Reader skip-to-next-keyframe less aggressively. r=mattwoodrow, a=sledru
dom/media/fmp4/MP4Reader.cpp
dom/media/fmp4/MP4Reader.h
media/libstagefright/binding/Index.cpp
media/libstagefright/binding/include/mp4_demuxer/Index.h
media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
media/libstagefright/binding/mp4_demuxer.cpp
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -102,17 +102,16 @@ InvokeAndRetry(ThisType* aThisVal, Retur
 
     prevFailure = failure;
     if (NS_WARN_IF(!stream->BlockingReadIntoCache(failure.mOffset, failure.mCount, aMonitor))) {
       return result;
     }
   }
 }
 
-
 MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
   , mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
   , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mDemuxerInitialized(false)
   , mIsEncrypted(false)
@@ -191,16 +190,17 @@ MP4Reader::InitLayersBackendType()
   nsRefPtr<LayerManager> layerManager =
     nsContentUtils::LayerManagerForDocument(element->OwnerDoc());
   NS_ENSURE_TRUE_VOID(layerManager);
 
   mLayersBackendType = layerManager->GetCompositorBackendType();
 }
 
 static bool sIsEMEEnabled = false;
+static bool sDemuxSkipToNextKeyframe = true;
 
 nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   PlatformDecoderModule::Init();
   mStream = new MP4Stream(mDecoder->GetResource());
   mTimestampOffset = GetDecoder()->GetTimestampOffset();
@@ -212,16 +212,17 @@ MP4Reader::Init(MediaDecoderReader* aClo
 
   mVideo.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
 
   static bool sSetupPrefCache = false;
   if (!sSetupPrefCache) {
     sSetupPrefCache = true;
     Preferences::AddBoolVarCache(&sIsEMEEnabled, "media.eme.enabled", false);
+    Preferences::AddBoolVarCache(&sDemuxSkipToNextKeyframe, "media.fmp4.demux-skip", true);
   }
 
   return NS_OK;
 }
 
 #ifdef MOZ_EME
 class DispatchKeyNeededEvent : public nsRunnable {
 public:
@@ -514,16 +515,39 @@ MP4Reader::GetDecoderData(TrackType aTra
 {
   MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
   if (aTrack == kAudio) {
     return mAudio;
   }
   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<MediaDecoderReader::VideoDataPromise>
 MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
                             int64_t aTimeThreshold)
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
 
   if (mShutdown) {
@@ -532,17 +556,17 @@ MP4Reader::RequestVideoData(bool aSkipTo
     nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
     p->Reject(CANCELED, __func__);
     return p;
   }
 
   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())) {
       NS_WARNING("Failed to skip/flush video when skipping-to-next-keyframe.");
     }
     mDecoder->NotifyDecodedFrames(parsed, 0);
   }
 
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -116,16 +116,19 @@ private:
   void UpdateIndex();
   bool IsSupportedAudioMimeType(const char* aMimeType);
   bool IsSupportedVideoMimeType(const char* aMimeType);
   void NotifyResourcesStatusChanged();
   void RequestCodecResource();
   bool IsWaitingOnCodecResource();
   virtual bool IsWaitingOnCDMResource() MOZ_OVERRIDE;
 
+  Microseconds GetNextKeyframeTime();
+  bool ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
+
   size_t SizeOfQueue(TrackType aTrack);
 
   nsRefPtr<MP4Stream> mStream;
   int64_t mTimestampOffset;
   nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
   nsRefPtr<PlatformDecoderModule> mPlatform;
 
   class DecoderCallback : public MediaDataDecoderCallback {
--- a/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -181,16 +181,41 @@ void SampleIterator::Seek(Microseconds a
       break;
     }
     Next();
   }
   mCurrentMoof = syncMoof;
   mCurrentSample = syncSample;
 }
 
+Microseconds
+SampleIterator::GetNextKeyframeTime()
+{
+  nsTArray<Moof>& 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<MediaSource::Indice>& aIndex,
              Stream* aSource, uint32_t aTrackId, Microseconds aTimestampOffset,
              Monitor* aMonitor)
   : mSource(aSource)
   , mMonitor(aMonitor)
 {
   if (aIndex.isEmpty()) {
     mMoofParser = new MoofParser(aSource, aTrackId, aTimestampOffset, aMonitor);
--- a/media/libstagefright/binding/include/mp4_demuxer/Index.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -17,16 +17,17 @@ class Sample;
 class Index;
 
 class SampleIterator
 {
 public:
   explicit SampleIterator(Index* aIndex);
   MP4Sample* GetNext();
   void Seek(Microseconds aTime);
+  Microseconds GetNextKeyframeTime();
 
 private:
   Sample* Get();
   void Next();
   nsRefPtr<Index> mIndex;
   size_t mCurrentMoof;
   size_t mCurrentSample;
 };
--- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
@@ -68,24 +68,29 @@ public:
   void UpdateIndex(const nsTArray<mozilla::MediaByteRange>& aByteRanges);
 
   void ConvertByteRangesToTime(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges,
     nsTArray<Interval<Microseconds>>* aIntervals);
 
   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;
   CryptoFile mCrypto;
 
   nsAutoPtr<StageFrightPrivate> mPrivate;
   nsRefPtr<Stream> mSource;
   nsTArray<mozilla::MediaByteRange> mCachedByteRanges;
   nsTArray<Interval<Microseconds>> mCachedTimeRanges;
   Microseconds mTimestampOffset;
   Monitor* mMonitor;
+  Microseconds mNextKeyframeTime;
 };
 
 } // namespace mozilla
 
 #endif // MP4_DEMUXER_H_
--- a/media/libstagefright/binding/mp4_demuxer.cpp
+++ b/media/libstagefright/binding/mp4_demuxer.cpp
@@ -70,17 +70,18 @@ public:
   virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; }
 
 private:
   nsRefPtr<Stream> mSource;
 };
 
 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));
 }
 
 MP4Demuxer::~MP4Demuxer()
 {
   if (mPrivate->mAudio.get()) {
     mPrivate->mAudio->stop();
@@ -243,16 +244,19 @@ MP4Demuxer::DemuxVideoSample()
   if (mPrivate->mVideoIterator) {
     nsAutoPtr<MP4Sample> sample(mPrivate->mVideoIterator->GetNext());
     if (sample) {
       sample->extra_data = mVideoConfig.extra_data;
       if (sample->crypto.valid) {
         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();
   }
 
   nsAutoPtr<MP4Sample> sample(new MP4Sample());
   status_t status =
     mPrivate->mVideo->read(&sample->mMediaBuffer, &mPrivate->mVideoOptions);
   mPrivate->mVideoOptions.clearSeekTo();
@@ -328,9 +332,19 @@ MP4Demuxer::GetEvictionOffset(Microsecon
 
   uint64_t offset = std::numeric_limits<uint64_t>::max();
   for (int i = 0; i < mPrivate->mIndexes.Length(); i++) {
     offset = std::min(offset, mPrivate->mIndexes[i]->GetEvictionOffset(aTime));
   }
   return offset == std::numeric_limits<uint64_t>::max() ? -1 : offset;
 }
 
+Microseconds
+MP4Demuxer::GetNextKeyframeTime()
+{
+  mMonitor->AssertCurrentThreadOwns();
+  if (!mPrivate->mVideoIterator) {
+    return -1;
+  }
+  return mNextKeyframeTime;
+}
+
 } // namespace mp4_demuxer