commit f0c2b93a23455f346905af822d0131949e831a92
parent 7248bea6c4fc35ae1d847479ca5d8d3a1ee468e6
Author: John Lin <jolin@mozilla.com>
Date: Mon, 27 Oct 2025 18:05:37 +0000
Bug 1905878 - p3: Use decode property when available. r=media-playback-reviewers,alwu
Differential Revision: https://phabricator.services.mozilla.com/D268728
Diffstat:
7 files changed, 101 insertions(+), 24 deletions(-)
diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp
@@ -137,11 +137,7 @@ static constexpr auto EXHAUSTED_DATA_MARGIN =
static const uint32_t MIN_VIDEO_QUEUE_SIZE = 3;
static const uint32_t MAX_VIDEO_QUEUE_SIZE = 10;
-#ifdef MOZ_APPLEMEDIA
-static const uint32_t HW_VIDEO_QUEUE_SIZE = 10;
-#else
static const uint32_t HW_VIDEO_QUEUE_SIZE = 3;
-#endif
static const uint32_t VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE = 9999;
static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
@@ -752,10 +748,12 @@ class MediaDecoderStateMachine::DecodingState
}
uint32_t VideoPrerollFrames() const {
- return std::min(
- static_cast<uint32_t>(
- mMaster->GetAmpleVideoFrames() / 2. * mMaster->mPlaybackRate + 1),
- sVideoQueueDefaultSize);
+ uint32_t preroll = static_cast<uint32_t>(
+ mMaster->GetAmpleVideoFrames() / 2. * mMaster->mPlaybackRate + 1);
+ // Keep it under maximal queue size.
+ mMaster->mReader->GetMaxVideoQueueSize().apply(
+ [&preroll](const uint32_t& x) { preroll = std::min(preroll, x); });
+ return preroll;
}
bool DonePrerollingAudio() const {
@@ -4241,6 +4239,9 @@ void MediaDecoderStateMachine::FinishDecodeFirstFrame() {
LOG("FinishDecodeFirstFrame");
mMediaSink->Redraw(Info().mVideo);
+ mReader->GetSendToCompositorSize().apply([self = RefPtr{this}](uint32_t x) {
+ self->mMediaSink->SetVideoQueueSendToCompositorSize(x);
+ });
LOG("Media duration %" PRId64 ", mediaSeekable=%d",
Duration().ToMicroseconds(), mMediaSeekable);
@@ -4657,9 +4658,23 @@ void MediaDecoderStateMachine::OnMediaSinkAudioError(nsresult aResult) {
uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const {
MOZ_ASSERT(OnTaskQueue());
- return mReader->VideoIsHardwareAccelerated()
- ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
- : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
+ if (mReader->VideoIsHardwareAccelerated()) {
+ // HW decoding should be fast so queue size can be as small as possible
+ // to lower frame latency.
+ uint32_t hw =
+ std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE);
+ mReader->GetMinVideoQueueSize().apply(
+ [&hw](const uint32_t& x) { hw = std::max(hw, x); });
+ return hw;
+ } else {
+ // SW decoding is slower and queuing more frames in advance reduces the
+ // chances of dropping late frames.
+ uint32_t sw =
+ std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
+ mReader->GetMaxVideoQueueSize().apply(
+ [&sw](const uint32_t& x) { sw = std::min(sw, x); });
+ return sw;
+ }
}
void MediaDecoderStateMachine::GetDebugInfo(
diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp
@@ -1834,6 +1834,7 @@ void MediaFormatReader::NotifyNewOutput(
// update the decoder name again, instead of using the wrong name.
if (decoder.mNumSamplesOutput == 1) {
decoder.mDescription = mVideo.mDecoder->GetDescriptionName();
+ decoder.LoadDecodeProperties();
}
}
decoder.mDecodePerfRecorder->Record(
@@ -3535,6 +3536,22 @@ void MediaFormatReader::SetEncryptedCustomIdent() {
mEncryptedCustomIdent = true;
}
+void MediaFormatReader::VideoDecodeProperties::Load(
+ RefPtr<MediaDataDecoder>& aDecoder) {
+ using V = MediaDataDecoder::PropertyValue;
+ aDecoder
+ ->GetDecodeProperty(MediaDataDecoder::PropertyName::MaxNumVideoBuffers)
+ .apply([this](const V& v) { mMaxQueueSize = Some(v.as<uint32_t>()); });
+ aDecoder
+ ->GetDecodeProperty(MediaDataDecoder::PropertyName::MinNumVideoBuffers)
+ .apply([this](const V& v) { mMinQueueSize = Some(v.as<uint32_t>()); });
+ aDecoder
+ ->GetDecodeProperty(MediaDataDecoder::PropertyName::MaxNumCurrentImages)
+ .apply([this](const V& v) {
+ mSendToCompositorSize = Some(v.as<uint32_t>());
+ });
+}
+
} // namespace mozilla
#undef NS_DispatchToMainThread
diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h
@@ -257,6 +257,30 @@ class MediaFormatReader final
template <typename T>
friend struct DDLoggedTypeTraits; // For DecoderData
+ class VideoDecodeProperties final {
+ public:
+ void Load(RefPtr<MediaDataDecoder>& aDecoder);
+ void Clear() {
+ mMaxQueueSize.reset();
+ mMinQueueSize.reset();
+ mSendToCompositorSize.reset();
+ }
+
+ Maybe<uint32_t> MaxQueueSize() { return mMaxQueueSize; }
+ Maybe<uint32_t> MinQueueSize() { return mMinQueueSize; }
+ Maybe<uint32_t> SendToCompositorSize() { return mSendToCompositorSize; }
+
+ private:
+ Maybe<uint32_t> mMaxQueueSize;
+ Maybe<uint32_t> mMinQueueSize;
+ Maybe<uint32_t> mSendToCompositorSize;
+ };
+
+ VideoDecodeProperties& GetVideoDecodeProperties() {
+ MutexAutoLock lock(mVideo.mMutex);
+ return mVideo.mVideoDecodeProperties;
+ }
+
private:
bool HasVideo() const { return mVideo.mTrackDemuxer; }
bool HasAudio() const { return mAudio.mTrackDemuxer; }
@@ -415,15 +439,25 @@ class MediaFormatReader final
RefPtr<TaskQueue> mTaskQueue;
// Mutex protecting mDescription, mDecoder, mTrackDemuxer, mWorkingInfo,
- // mProcessName and mCodecName as those can be read outside the TaskQueue.
- // They are only written on the TaskQueue however, as such mMutex doesn't
- // need to be held when those members are read on the TaskQueue.
+ // mProcessName, mCodecName and mDecodeProperties as those can be read
+ // outside the TaskQueue. They are only written on the TaskQueue however, as
+ // such mMutex doesn't need to be held when those members are read on the
+ // TaskQueue.
Mutex mMutex MOZ_UNANNOTATED;
// The platform decoder.
RefPtr<MediaDataDecoder> mDecoder;
nsCString mDescription;
nsCString mProcessName;
nsCString mCodecName;
+ VideoDecodeProperties mVideoDecodeProperties;
+
+ void LoadDecodeProperties() {
+ MOZ_ASSERT(mOwner->OnTaskQueue());
+ if (mType == MediaData::Type::VIDEO_DATA) {
+ mVideoDecodeProperties.Load(mDecoder);
+ }
+ }
+
void ShutdownDecoder();
// Only accessed from reader's task queue.
@@ -600,6 +634,9 @@ class MediaFormatReader final
if (!HasFatalError()) {
mError.reset();
}
+ if (mType == MediaData::Type::VIDEO_DATA) {
+ mVideoDecodeProperties.Clear();
+ }
}
// Return whether an InternalSeek() has been requested but has not yet
diff --git a/dom/media/ReaderProxy.h b/dom/media/ReaderProxy.h
@@ -92,6 +92,16 @@ class ReaderProxy {
bool IsEncryptedCustomIdent() const;
+ Maybe<uint32_t> GetMaxVideoQueueSize() {
+ return mReader->GetVideoDecodeProperties().MaxQueueSize();
+ }
+ Maybe<uint32_t> GetMinVideoQueueSize() {
+ return mReader->GetVideoDecodeProperties().MinQueueSize();
+ }
+ Maybe<uint32_t> GetSendToCompositorSize() {
+ return mReader->GetVideoDecodeProperties().SendToCompositorSize();
+ }
+
private:
~ReaderProxy();
RefPtr<MetadataPromise> OnMetadataRead(MetadataHolder&& aMetadata);
diff --git a/dom/media/mediasink/MediaSink.h b/dom/media/mediasink/MediaSink.h
@@ -141,6 +141,8 @@ class MediaSink {
virtual void GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) {}
+ virtual void SetVideoQueueSendToCompositorSize(const uint32_t aSize) {}
+
protected:
virtual ~MediaSink() = default;
};
diff --git a/dom/media/mediasink/VideoSink.h b/dom/media/mediasink/VideoSink.h
@@ -73,6 +73,10 @@ class VideoSink : public MediaSink {
void GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) override;
+ void SetVideoQueueSendToCompositorSize(const uint32_t aSize) override {
+ mVideoQueueSendToCompositorSize = aSize;
+ }
+
private:
virtual ~VideoSink();
@@ -149,8 +153,8 @@ class VideoSink : public MediaSink {
DelayedScheduler<TimeStamp> mUpdateScheduler;
// Max frame number sent to compositor at a time.
- // Based on the pref value obtained in MDSM.
- const uint32_t mVideoQueueSendToCompositorSize;
+ // Based on the value obtained in MDSM.
+ uint32_t mVideoQueueSendToCompositorSize;
#ifdef XP_WIN
// Whether we've called timeBeginPeriod(1) to request high resolution
diff --git a/mobile/android/app/geckoview-prefs.js b/mobile/android/app/geckoview-prefs.js
@@ -323,14 +323,6 @@ pref("media.navigator.permission.device", true);
// this is to preserve battery and data (bug 1540573)
pref("media.throttle-cellular-regardless-of-download-rate", true);
-// Number of video frames we buffer while decoding video.
-// On Android this is decided by a similar value which varies for
-// each OMX decoder |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This
-// number must be less than the OMX equivalent or gecko will think it is
-// chronically starved of video frames. All decoders seen so far have a value
-// of at least 4. (bug 973408)
-pref("media.video-queue.default-size", 3);
-
// The maximum number of queued frames to send to the compositor.
// On Android, it needs to be throttled because SurfaceTexture contains only one
// (the most recent) image data. (bug 1299068)