tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 392a6f0444ce5ba72c030c181171533bf0b46597
parent 735d657d3b96db93f955231037b31c0cdd024212
Author: John Lin <jolin@mozilla.com>
Date:   Tue, 28 Oct 2025 22:20:27 +0000

Bug 1905878 - p3: Use decode property when available. r=media-playback-reviewers,alwu

Differential Revision: https://phabricator.services.mozilla.com/D268728

Diffstat:
Mdom/media/MediaDecoderStateMachine.cpp | 37++++++++++++++++++++++++++-----------
Mdom/media/MediaFormatReader.cpp | 17+++++++++++++++++
Mdom/media/MediaFormatReader.h | 43++++++++++++++++++++++++++++++++++++++++---
Mdom/media/ReaderProxy.h | 10++++++++++
Mdom/media/mediasink/MediaSink.h | 2++
Mdom/media/mediasink/VideoSink.h | 8++++++--
Mmobile/android/app/geckoview-prefs.js | 8--------
7 files changed, 101 insertions(+), 24 deletions(-)

diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp @@ -136,11 +136,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; @@ -751,10 +747,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 { @@ -4240,6 +4238,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); @@ -4656,9 +4657,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)