Bug 1289668 - Record inter-keyframe statistics - r=kamidphish
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 26 Jul 2016 09:36:55 +1000
changeset 307118 a3bd0fb7c2691c1b22b7d359c230643221a2fb13
parent 307117 fff6018d7bba1434d86fd42265569118e19387f7
child 307119 e39471da882b0edec6f36e0d0ff7c21c3fcce35b
push id20159
push usercbook@mozilla.com
push dateFri, 29 Jul 2016 10:35:36 +0000
treeherderfx-team@0d59012b60b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish
bugs1289668
milestone50.0a1
Bug 1289668 - Record inter-keyframe statistics - r=kamidphish FrameStatisticsData can now store inter-keyframe information, which is provided by the MediaFormatReader (based on live decoding). MozReview-Commit-ID: HhBy6pgT6ZX
dom/media/FrameStatistics.h
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/FrameStatistics.h
+++ b/dom/media/FrameStatistics.h
@@ -24,30 +24,45 @@ struct FrameStatisticsData
   // Number of decoded frames which were actually sent down the rendering
   // pipeline to be painted ("presented"). Access protected by mReentrantMonitor.
   uint64_t mPresentedFrames = 0;
 
   // Number of frames that have been skipped because they have missed their
   // composition deadline.
   uint64_t mDroppedFrames = 0;
 
+  // Sum of all inter-keyframe segment durations, in microseconds.
+  // Dividing by count will give the average inter-keyframe time.
+  uint64_t mInterKeyframeSum_us = 0;
+  // Number of inter-keyframe segments summed so far.
+  size_t mInterKeyframeCount = 0;
+
+  // Maximum inter-keyframe segment duration, in microseconds.
+  uint64_t mInterKeyFrameMax_us = 0;
+
   FrameStatisticsData() = default;
   FrameStatisticsData(uint64_t aParsed, uint64_t aDecoded, uint64_t aDropped)
     : mParsedFrames(aParsed)
     , mDecodedFrames(aDecoded)
     , mDroppedFrames(aDropped)
   {}
 
   void
   Accumulate(const FrameStatisticsData& aStats)
   {
     mParsedFrames += aStats.mParsedFrames;
     mDecodedFrames += aStats.mDecodedFrames;
     mPresentedFrames += aStats.mPresentedFrames;
     mDroppedFrames += aStats.mDroppedFrames;
+    mInterKeyframeSum_us += aStats.mInterKeyframeSum_us;
+    mInterKeyframeCount += aStats.mInterKeyframeCount;
+    // It doesn't make sense to add max numbers, instead keep the bigger one.
+    if (mInterKeyFrameMax_us < aStats.mInterKeyFrameMax_us) {
+      mInterKeyFrameMax_us = aStats.mInterKeyFrameMax_us;
+    }
   }
 };
 
 // Frame decoding/painting related performance counters.
 // Threadsafe.
 class FrameStatistics
 {
 public:
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -62,16 +62,17 @@ MediaFormatReader::MediaFormatReader(Abs
   : MediaDecoderReader(aDecoder)
   , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2),
            Preferences::GetUint("media.audio-max-decode-error", 3))
   , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2),
            Preferences::GetUint("media.video-max-decode-error", 2))
   , mDemuxer(aDemuxer)
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
+  , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
   , mLayersBackendType(aLayersBackend)
   , mInitDone(false)
   , mIsEncrypted(false)
   , mTrackDemuxersMayBlock(false)
   , mDemuxOnly(false)
   , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
 {
@@ -1154,16 +1155,18 @@ MediaFormatReader::Update(TrackType aTra
   // Drop any frames found prior our internal seek target.
   while (decoder.mTimeThreshold && decoder.mOutput.Length()) {
     RefPtr<MediaData>& output = decoder.mOutput[0];
     InternalSeekTarget target = decoder.mTimeThreshold.ref();
     media::TimeUnit time = media::TimeUnit::FromMicroseconds(output->mTime);
     if (time >= target.Time()) {
       // We have reached our internal seek target.
       decoder.mTimeThreshold.reset();
+      // We might have dropped some keyframes.
+      mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe;
     }
     if (time < target.Time() || (target.mDropTarget && target.Contains(time))) {
       LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)",
            TrackTypeToStr(aTrack),
            media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
            target.Time().ToSeconds(),
            output->mKeyframe);
       decoder.mOutput.RemoveElementAt(0);
@@ -1189,16 +1192,28 @@ MediaFormatReader::Update(TrackType aTra
       decoder.mNumSamplesOutputTotal++;
       ReturnOutput(output, aTrack);
       // We have a decoded sample ready to be returned.
       if (aTrack == TrackType::kVideoTrack) {
         uint64_t delta =
           decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
         a.mStats.mDecodedFrames = static_cast<uint32_t>(delta);
         mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
+        if (output->mKeyframe) {
+          if (mPreviousDecodedKeyframeTime_us < output->mTime) {
+            // There is a previous keyframe -> Record inter-keyframe stats.
+            uint64_t segment_us = output->mTime - mPreviousDecodedKeyframeTime_us;
+            a.mStats.mInterKeyframeSum_us += segment_us;
+            a.mStats.mInterKeyframeCount += 1;
+            if (a.mStats.mInterKeyFrameMax_us < segment_us) {
+              a.mStats.mInterKeyFrameMax_us = segment_us;
+            }
+          }
+          mPreviousDecodedKeyframeTime_us = output->mTime;
+        }
         nsCString error;
         mVideo.mIsHardwareAccelerated =
           mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
       }
     } else if (decoder.HasFatalError()) {
       LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
       decoder.RejectPromise(DECODE_ERROR, __func__);
       return;
@@ -1752,16 +1767,18 @@ MediaFormatReader::DoVideoSeek()
 
 void
 MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("Video seeked to %lld", aTime.ToMicroseconds());
   mVideo.mSeekRequest.Complete();
 
+  mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe;
+
   SetVideoDecodeThreshold();
 
   if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) {
     MOZ_ASSERT(mPendingSeekTime.isSome());
     if (mOriginalSeekTarget.IsFast()) {
       // We are performing a fast seek. We need to seek audio to where the
       // video seeked to, to ensure proper A/V sync once playback resume.
       mPendingSeekTime = Some(aTime);
@@ -1769,16 +1786,23 @@ MediaFormatReader::OnVideoSeekCompleted(
     DoAudioSeek();
   } else {
     mPendingSeekTime.reset();
     mSeekPromise.Resolve(aTime, __func__);
   }
 }
 
 void
+MediaFormatReader::OnVideoSeekFailed(DemuxerFailureReason aFailure)
+{
+  mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe;
+  OnSeekFailed(TrackType::kVideoTrack, aFailure);
+}
+
+void
 MediaFormatReader::SetVideoDecodeThreshold()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (!HasVideo() || !mVideo.mDecoder) {
     return;
   }
 
@@ -1828,16 +1852,22 @@ MediaFormatReader::OnAudioSeekCompleted(
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("Audio seeked to %lld", aTime.ToMicroseconds());
   mAudio.mSeekRequest.Complete();
   mPendingSeekTime.reset();
   mSeekPromise.Resolve(aTime, __func__);
 }
 
+void
+MediaFormatReader::OnAudioSeekFailed(DemuxerFailureReason aFailure)
+{
+  OnSeekFailed(TrackType::kAudioTrack, aFailure);
+}
+
 media::TimeIntervals
 MediaFormatReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
   media::TimeIntervals videoti;
   media::TimeIntervals audioti;
   media::TimeIntervals intervals;
 
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -510,16 +510,21 @@ private:
   void OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure);
 
   // The last number of decoded output frames that we've reported to
   // MediaDecoder::NotifyDecoded(). We diff the number of output video
   // frames every time that DecodeVideoData() is called, and report the
   // delta there.
   uint64_t mLastReportedNumDecodedFrames;
 
+  // Timestamp of the previous decoded keyframe, in microseconds.
+  int64_t mPreviousDecodedKeyframeTime_us;
+  // Default mLastDecodedKeyframeTime_us value, must be bigger than anything.
+  static const int64_t sNoPreviousDecodedKeyframe = INT64_MAX;
+
   layers::LayersBackend mLayersBackendType;
 
   // Metadata objects
   // True if we've read the streams' metadata.
   bool mInitDone;
   MozPromiseHolder<MetadataPromise> mMetadataPromise;
   bool IsEncrypted()
   {
@@ -541,28 +546,22 @@ private:
   {
     return IsSeeking() && mOriginalSeekTarget.IsVideoOnly();
   }
   void ScheduleSeek();
   void AttemptSeek();
   void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
   void DoVideoSeek();
   void OnVideoSeekCompleted(media::TimeUnit aTime);
-  void OnVideoSeekFailed(DemuxerFailureReason aFailure)
-  {
-    OnSeekFailed(TrackType::kVideoTrack, aFailure);
-  }
+  void OnVideoSeekFailed(DemuxerFailureReason aFailure);
   bool mSeekScheduled;
 
   void DoAudioSeek();
   void OnAudioSeekCompleted(media::TimeUnit aTime);
-  void OnAudioSeekFailed(DemuxerFailureReason aFailure)
-  {
-    OnSeekFailed(TrackType::kAudioTrack, aFailure);
-  }
+  void OnAudioSeekFailed(DemuxerFailureReason aFailure);
   // The SeekTarget that was last given to Seek()
   SeekTarget mOriginalSeekTarget;
   // Temporary seek information while we wait for the data
   Maybe<media::TimeUnit> mFallbackSeekTime;
   Maybe<media::TimeUnit> mPendingSeekTime;
   MozPromiseHolder<SeekPromise> mSeekPromise;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;