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
--- 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;
}