Bug 1397307 - P6. Calculate average video frame rate as video is playing. r=gerald
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 12 Sep 2017 21:20:09 +0200
changeset 665554 3fbbe2c4ec7be1057bbca4eab35bfbb4bad8de9c
parent 665553 768bf2920dab786813f9a698bd5c5b1778f09508
child 665555 1df89fb6ff5d24981157557a82de345b6da3f100
push id80115
push userbmo:eoger@fastmail.com
push dateFri, 15 Sep 2017 18:29:01 +0000
reviewersgerald
bugs1397307
milestone57.0a1
Bug 1397307 - P6. Calculate average video frame rate as video is playing. r=gerald We unfortunately can't store this information in the VideoInfo as typically the framerate isn't found in the container's metadata. Additionally, the VideoInfo object is readable-only as it is shared across threads. As such, we can only estimate it as we demux samples. MozReview-Commit-ID: 5HB33ubfGAs
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/platforms/PlatformDecoderModule.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -742,29 +742,28 @@ MediaFormatReader::DecoderFactory::DoCre
         &mOwner->OnTrackWaitingForKeyProducer()
       });
       break;
     }
 
     case TrackType::kVideoTrack: {
       // Decoders use the layers backend to decide if they can use hardware decoding,
       // so specify LAYERS_NONE if we want to forcibly disable it.
-      aData.mDecoder = mOwner->mPlatform->CreateDecoder({
-        ownerData.mInfo
-        ? *ownerData.mInfo->GetAsVideoInfo()
-        : *ownerData.mOriginalInfo->GetAsVideoInfo(),
-        ownerData.mTaskQueue,
-        mOwner->mKnowsCompositor,
-        mOwner->GetImageContainer(),
-        mOwner->mCrashHelper,
-        CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode),
-        &result,
-        TrackType::kVideoTrack,
-        &mOwner->OnTrackWaitingForKeyProducer()
-      });
+      aData.mDecoder = mOwner->mPlatform->CreateDecoder(
+        { ownerData.mInfo ? *ownerData.mInfo->GetAsVideoInfo()
+                          : *ownerData.mOriginalInfo->GetAsVideoInfo(),
+          ownerData.mTaskQueue,
+          mOwner->mKnowsCompositor,
+          mOwner->GetImageContainer(),
+          mOwner->mCrashHelper,
+          CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode),
+          &result,
+          TrackType::kVideoTrack,
+          &mOwner->OnTrackWaitingForKeyProducer(),
+          CreateDecoderParams::VideoFrameRate(ownerData.mMeanRate.Mean()) });
       break;
     }
 
     default:
       break;
   }
 
   if (aData.mDecoder) {
@@ -2086,31 +2085,37 @@ MediaFormatReader::HandleDemuxedSamples(
           TrackTypeToStr(aTrack),
           decoder.mLastStreamSourceID,
           info->GetID());
 
       decoder.mNextStreamSourceID.reset();
       decoder.mLastStreamSourceID = info->GetID();
       decoder.mInfo = info;
 
+      decoder.mMeanRate.Reset();
+
       if (sample->mKeyframe) {
         if (samples.Length()) {
           decoder.mQueuedSamples = Move(samples);
         }
       } else {
         auto time = TimeInterval(sample->mTime, sample->GetEndTime());
         InternalSeekTarget seekTarget =
           decoder.mTimeThreshold.refOr(InternalSeekTarget(time, false));
         LOG("Stream change occurred on a non-keyframe. Seeking to:%" PRId64,
             sample->mTime.ToMicroseconds());
         InternalSeek(aTrack, seekTarget);
         return;
       }
     }
 
+    // Calculate the average frame rate. The first frame will be accounted
+    // for twice.
+    decoder.mMeanRate.Update(sample->mDuration);
+
     if (!decoder.mDecoder) {
       mDecoderFactory->CreateDecoder(aTrack);
       return;
     }
 
     LOGV("Input:%" PRId64 " (dts:%" PRId64 " kf:%d)",
          sample->mTime.ToMicroseconds(), sample->mTimecode.ToMicroseconds(),
          sample->mKeyframe);
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -570,16 +570,39 @@ private:
     Maybe<media::TimeUnit> mLastTimeRangesEnd;
     // TrackInfo as first discovered during ReadMetadata.
     UniquePtr<TrackInfo> mOriginalInfo;
     RefPtr<TrackInfoSharedPtr> mInfo;
     Maybe<media::TimeUnit> mFirstDemuxedSampleTime;
     // Use NullDecoderModule or not.
     bool mIsNullDecode;
 
+    class
+    {
+    public:
+      float Mean() const { return mMean; }
+
+      void Update(const media::TimeUnit& aValue)
+      {
+        if (aValue == media::TimeUnit::Zero()) {
+          return;
+        }
+        mMean += (1.0f / aValue.ToSeconds() - mMean) / ++mCount;
+      }
+
+      void Reset()
+      {
+        mMean = 0;
+        mCount = 0;
+      }
+
+    private:
+      float mMean = 0;
+      uint64_t mCount = 0;
+    } mMeanRate;
   };
 
   template <typename Type>
   class DecoderDataWithPromise : public DecoderData
   {
   public:
     DecoderDataWithPromise(MediaFormatReader* aOwner,
                            MediaData::Type aType,
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -54,16 +54,23 @@ struct MOZ_STACK_CLASS CreateDecoderPara
 
   struct UseNullDecoder
   {
     UseNullDecoder() = default;
     explicit UseNullDecoder(bool aUseNullDecoder) : mUse(aUseNullDecoder) { }
     bool mUse = false;
   };
 
+  struct VideoFrameRate
+  {
+    VideoFrameRate() = default;
+    explicit VideoFrameRate(float aFramerate) : mValue(aFramerate) { }
+    float mValue = 0.0f;
+  };
+
   template <typename T1, typename... Ts>
   CreateDecoderParams(const TrackInfo& aConfig, T1&& a1, Ts&&... args)
     : mConfig(aConfig)
   {
     Set(mozilla::Forward<T1>(a1), mozilla::Forward<Ts>(args)...);
   }
 
   const VideoInfo& VideoConfig() const
@@ -92,31 +99,33 @@ struct MOZ_STACK_CLASS CreateDecoderPara
   layers::ImageContainer* mImageContainer = nullptr;
   MediaResult* mError = nullptr;
   RefPtr<layers::KnowsCompositor> mKnowsCompositor;
   RefPtr<GMPCrashHelper> mCrashHelper;
   UseNullDecoder mUseNullDecoder;
   TrackInfo::TrackType mType = TrackInfo::kUndefinedTrack;
   MediaEventProducer<TrackInfo::TrackType>* mOnWaitingForKeyEvent = nullptr;
   OptionSet mOptions = OptionSet(Option::Default);
+  VideoFrameRate mRate;
 
 private:
   void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
   void Set(DecoderDoctorDiagnostics* aDiagnostics)
   {
     mDiagnostics = aDiagnostics;
   }
   void Set(layers::ImageContainer* aImageContainer)
   {
     mImageContainer = aImageContainer;
   }
   void Set(MediaResult* aError) { mError = aError; }
   void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
   void Set(UseNullDecoder aUseNullDecoder) { mUseNullDecoder = aUseNullDecoder; }
   void Set(OptionSet aOptions) { mOptions = aOptions; }
+  void Set(VideoFrameRate aRate) { mRate = aRate; }
   void Set(layers::KnowsCompositor* aKnowsCompositor)
   {
     mKnowsCompositor = aKnowsCompositor;
   }
   void Set(TrackInfo::TrackType aType)
   {
     mType = aType;
   }