tor-browser

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

commit dbecb0c4758dd36fe8caffa0fee1a4e9ec5e1153
parent 4ee3b55e4f10bd33589106d7d7eb014b203b2049
Author: John Lin <jolin@mozilla.com>
Date:   Mon, 27 Oct 2025 18:05:36 +0000

Bug 1905878 - p1: let MediaDataDecoder define video decoding related properties. r=media-playback-reviewers,chunmin

MediaDecoderStateMachine uses global/static values for video queue sizes
(https://searchfox.org/firefox-main/rev/4e8871487a9f97992e00d5c8105ef1d8cf3c88f3/dom/media/MediaDecoderStateMachine.cpp#152-165),
which are actually constraints of platform decoders. In some cases the same content
process could have more than one MDSMs that uses different platform decoders (e.g.
on Android one video can be decoded by MediaCodec but another by libvpx) and causes
suboptimal results. Moving the definition to PlatformDecoderModule for each
MDSM to get its own value ensures playback smoothness.

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

Diffstat:
Mdom/media/ipc/MediaIPCUtils.h | 7+++++++
Mdom/media/ipc/PRemoteDecoder.ipdl | 8++++++++
Mdom/media/ipc/RemoteDecoderChild.cpp | 3+++
Mdom/media/ipc/RemoteDecoderChild.h | 10++++++++++
Mdom/media/ipc/RemoteDecoderParent.cpp | 11++++++++++-
Mdom/media/ipc/RemoteMediaDataDecoder.cpp | 8++++++++
Mdom/media/ipc/RemoteMediaDataDecoder.h | 4++++
Mdom/media/platforms/AllocationPolicy.h | 4++++
Mdom/media/platforms/PlatformDecoderModule.h | 25+++++++++++++++++++++++++
Mdom/media/platforms/android/RemoteDataDecoder.cpp | 19+++++++++++++++++++
Mdom/media/platforms/apple/AppleVTDecoder.h | 12++++++++++++
Mdom/media/platforms/wrappers/MediaChangeMonitor.h | 7+++++++
Mdom/media/platforms/wrappers/MediaDataDecoderProxy.cpp | 7+++++++
Mdom/media/platforms/wrappers/MediaDataDecoderProxy.h | 1+
14 files changed, 125 insertions(+), 1 deletion(-)

diff --git a/dom/media/ipc/MediaIPCUtils.h b/dom/media/ipc/MediaIPCUtils.h @@ -223,6 +223,13 @@ struct ParamTraits<mozilla::MediaDataDecoder::ConversionRequired> mozilla::MediaDataDecoder::ConversionRequired::kNeedHVCC)> {}; template <> +struct ParamTraits<mozilla::MediaDataDecoder::PropertyName> + : public ContiguousEnumSerializerInclusive< + mozilla::MediaDataDecoder::PropertyName, + mozilla::MediaDataDecoder::PropertyName(0), + mozilla::MediaDataDecoder::sHighestPropertyName> {}; + +template <> struct ParamTraits<mozilla::media::TimeUnit> { using paramType = mozilla::media::TimeUnit; diff --git a/dom/media/ipc/PRemoteDecoder.ipdl b/dom/media/ipc/PRemoteDecoder.ipdl @@ -8,6 +8,8 @@ include "mozilla/dom/MediaIPCUtils.h"; include protocol PRemoteMediaManager; using mozilla::MediaDataDecoder::ConversionRequired from "PlatformDecoderModule.h"; +using mozilla::MediaDataDecoder::PropertyName from "PlatformDecoderModule.h"; +using mozilla::MediaDataDecoder::PropertyValue from "PlatformDecoderModule.h"; using mozilla::TrackInfo::TrackType from "MediaInfo.h"; using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; using mozilla::MediaResult from "MediaResult.h"; @@ -25,6 +27,11 @@ union DecodedOutputIPDL nullable ArrayOfRemoteVideoData; }; +struct DecodePropertyIPDL { + PropertyName name; + PropertyValue value; +}; + struct InitCompletionIPDL { TrackType type; @@ -35,6 +42,7 @@ struct InitCompletionIPDL nsCString hardwareReason; ConversionRequired conversion; bool shouldDecoderAlwaysBeRecycled; + DecodePropertyIPDL[] decodeProperties; }; union InitResultIPDL diff --git a/dom/media/ipc/RemoteDecoderChild.cpp b/dom/media/ipc/RemoteDecoderChild.cpp @@ -75,6 +75,9 @@ RefPtr<MediaDataDecoder::InitPromise> RemoteDecoderChild::Init() { mConversion = initResponse.conversion(); mShouldDecoderAlwaysBeRecycled = initResponse.shouldDecoderAlwaysBeRecycled(); + for (auto p : initResponse.decodeProperties()) { + mDecodeProperties[p.name()] = Some(p.value()); + } // Either the promise has not yet been resolved or the handler has // been disconnected and we can't get here. mInitPromise.Resolve(initResponse.type(), __func__); diff --git a/dom/media/ipc/RemoteDecoderChild.h b/dom/media/ipc/RemoteDecoderChild.h @@ -6,6 +6,7 @@ #ifndef include_dom_media_ipc_RemoteDecoderChild_h #define include_dom_media_ipc_RemoteDecoderChild_h +#include "mozilla/EnumeratedArray.h" #include "mozilla/PRemoteDecoderChild.h" #include "mozilla/RemoteMediaManagerChild.h" #include "mozilla/ShmemRecycleAllocator.h" @@ -42,6 +43,14 @@ class RemoteDecoderChild : public ShmemRecycleAllocator<RemoteDecoderChild>, void SetSeekThreshold(const media::TimeUnit& aTime); MediaDataDecoder::ConversionRequired NeedsConversion() const; bool ShouldDecoderAlwaysBeRecycled() const; + using DecodeProperties = + EnumeratedArray<MediaDataDecoder::PropertyName, + Maybe<MediaDataDecoder::PropertyValue>, + MediaDataDecoder::sPropertyNameCount>; + const DecodeProperties& GetDecodeProperties() const { + return mDecodeProperties; + } + void DestroyIPDL(); // Called from IPDL when our actor has been destroyed @@ -79,6 +88,7 @@ class RemoteDecoderChild : public ShmemRecycleAllocator<RemoteDecoderChild>, MediaDataDecoder::ConversionRequired mConversion = MediaDataDecoder::ConversionRequired::kNeedNone; bool mShouldDecoderAlwaysBeRecycled = false; + DecodeProperties mDecodeProperties; }; } // namespace mozilla diff --git a/dom/media/ipc/RemoteDecoderParent.cpp b/dom/media/ipc/RemoteDecoderParent.cpp @@ -65,12 +65,21 @@ mozilla::ipc::IPCResult RemoteDecoderParent::RecvInit( nsCString hardwareReason; bool hardwareAccelerated = self->mDecoder->IsHardwareAccelerated(hardwareReason); + nsTArray<DecodePropertyIPDL> properties; + for (size_t i = 0; i < MediaDataDecoder::sPropertyNameCount; i++) { + MediaDataDecoder::PropertyName name = + static_cast<MediaDataDecoder::PropertyName>(i); + if (auto v = self->mDecoder->GetDecodeProperty(name)) { + properties.AppendElement( + DecodePropertyIPDL(name, std::move(v.ref()))); + } + } resolver(InitCompletionIPDL{ track, self->mDecoder->GetDescriptionName(), self->mDecoder->GetProcessName(), self->mDecoder->GetCodecName(), hardwareAccelerated, hardwareReason, self->mDecoder->NeedsConversion(), - self->mDecoder->ShouldDecoderAlwaysBeRecycled()}); + self->mDecoder->ShouldDecoderAlwaysBeRecycled(), properties}); } }); return IPC_OK(); diff --git a/dom/media/ipc/RemoteMediaDataDecoder.cpp b/dom/media/ipc/RemoteMediaDataDecoder.cpp @@ -68,6 +68,7 @@ RefPtr<MediaDataDecoder::InitPromise> RemoteMediaDataDecoder::Init() { mIsHardwareAccelerated = mChild->IsHardwareAccelerated(mHardwareAcceleratedReason); mConversion = mChild->NeedsConversion(); + mDecodeProperties = mChild->GetDecodeProperties(); mShouldDecoderAlwaysBeRecycled = mChild->ShouldDecoderAlwaysBeRecycled(); LOG("%p RemoteDecoderChild has been initialized - description: %s, " @@ -158,6 +159,13 @@ MediaDataDecoder::ConversionRequired RemoteMediaDataDecoder::NeedsConversion() return mConversion; } +Maybe<MediaDataDecoder::PropertyValue> +RemoteMediaDataDecoder::GetDecodeProperty( + MediaDataDecoder::PropertyName aName) const { + MutexAutoLock lock(mMutex); + return mDecodeProperties[aName]; +} + nsCString RemoteMediaDataDecoder::GetDescriptionName() const { MutexAutoLock lock(mMutex); return mDescription; diff --git a/dom/media/ipc/RemoteMediaDataDecoder.h b/dom/media/ipc/RemoteMediaDataDecoder.h @@ -7,6 +7,7 @@ #define include_dom_media_ipc_RemoteMediaDataDecoder_h #include "MediaData.h" #include "PlatformDecoderModule.h" +#include "mozilla/EnumeratedArray.h" namespace mozilla { @@ -44,6 +45,7 @@ class RemoteMediaDataDecoder final nsCString GetProcessName() const override; nsCString GetCodecName() const override; ConversionRequired NeedsConversion() const override; + Maybe<PropertyValue> GetDecodeProperty(PropertyName aName) const override; bool ShouldDecoderAlwaysBeRecycled() const override; private: @@ -64,6 +66,8 @@ class RemoteMediaDataDecoder final nsCString mHardwareAcceleratedReason MOZ_GUARDED_BY(mMutex); ConversionRequired mConversion MOZ_GUARDED_BY(mMutex); bool mShouldDecoderAlwaysBeRecycled MOZ_GUARDED_BY(mMutex); + EnumeratedArray<PropertyName, Maybe<PropertyValue>, sPropertyNameCount> + mDecodeProperties MOZ_GUARDED_BY(mMutex); }; } // namespace mozilla diff --git a/dom/media/platforms/AllocationPolicy.h b/dom/media/platforms/AllocationPolicy.h @@ -164,6 +164,10 @@ class AllocationWrapper final : public MediaDataDecoder { return mDecoder->NeedsConversion(); } + Maybe<PropertyValue> GetDecodeProperty(PropertyName aName) const override { + return mDecoder->GetDecodeProperty(aName); + } + typedef MozPromise<RefPtr<MediaDataDecoder>, MediaResult, /* IsExclusive = */ true> AllocateDecoderPromise; diff --git a/dom/media/platforms/PlatformDecoderModule.h b/dom/media/platforms/PlatformDecoderModule.h @@ -645,6 +645,31 @@ class MediaDataDecoder : public DecoderDoctorLifeLogger<MediaDataDecoder> { virtual ConversionRequired NeedsConversion() const { return ConversionRequired::kNeedNone; } + + // Properties specific to platform decoders. They can be consulted by the + // client to improve playback smoothness. When not defined by the decoder, the + // client can choose its own. + // MaxNumVideoBuffers: some platform decoders have limited output buffers. + // This specifies how many (output) video buffers can be held at most by the + // client. Holding more will exhaust the deocder buffer pool and block further + // decoding. + // MinNumVideoBuffers: some platform decoders have decoding time longer than + // frame duration. To avoid frame dropping in this case, the client queues + // frames before starting to play. This value tells the client the least + // amount of frames it should queue. + // MaxNumCurrentImages: specifies how many (output) video buffers are current + // (renderable) at a time. + // The result is not accurated until Init() is resolved. + MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(PropertyName, + (MaxNumVideoBuffers, + MinNumVideoBuffers, + MaxNumCurrentImages)); + // Generic type for property values to avoid breaking existing client code in + // the future. + using PropertyValue = Variant<uint32_t>; + virtual Maybe<PropertyValue> GetDecodeProperty(PropertyName aName) const { + return Nothing(); + } }; } // namespace mozilla diff --git a/dom/media/platforms/android/RemoteDataDecoder.cpp b/dom/media/platforms/android/RemoteDataDecoder.cpp @@ -348,6 +348,25 @@ class RemoteVideoDecoder final : public RemoteDataDecoder { return ConversionRequired::kNeedAnnexB; } + Maybe<MediaDataDecoder::PropertyValue> GetDecodeProperty( + MediaDataDecoder::PropertyName aName) const override { + // Android has limited amount of output buffers. See Bug 794747. + static constexpr uint32_t kNumOutputBuffers = 3; + // SurfaceTexture can have only one current/renderable image at a time. + // See Bug 1299068 + static constexpr uint32_t kNumCurrentImages = 1; + switch (aName) { + case PropertyName::MaxNumVideoBuffers: + [[fallthrough]]; + case PropertyName::MinNumVideoBuffers: + return Some(PropertyValue(kNumOutputBuffers)); + case PropertyName::MaxNumCurrentImages: + return Some(PropertyValue(kNumCurrentImages)); + default: + return MediaDataDecoder::GetDecodeProperty(aName); + } + } + private: // Param and LocalRef are only valid for the duration of a JNI method call. // Use GlobalRef as the parameter type to keep the Java object referenced diff --git a/dom/media/platforms/apple/AppleVTDecoder.h b/dom/media/platforms/apple/AppleVTDecoder.h @@ -81,6 +81,18 @@ class AppleVTDecoder final : public MediaDataDecoder, return ConversionRequired::kNeedNone; } + Maybe<PropertyValue> GetDecodeProperty(PropertyName aName) const override { + // Some Intel GPU has long decoding time and needs more frames queued to + // play smoothly. See bug 1230641. + static constexpr uint32_t kMinNumOutputBuffers = 10; + switch (aName) { + case PropertyName::MinNumVideoBuffers: + return Some(PropertyValue(kMinNumOutputBuffers)); + default: + return MediaDataDecoder::GetDecodeProperty(aName); + } + } + // Access from the taskqueue and the decoder's thread. // OutputFrame is thread-safe. void OutputFrame(CVPixelBufferRef aImage, AppleFrameRef aFrameRef); diff --git a/dom/media/platforms/wrappers/MediaChangeMonitor.h b/dom/media/platforms/wrappers/MediaChangeMonitor.h @@ -79,6 +79,13 @@ class MediaChangeMonitor final return ConversionRequired::kNeedNone; } + Maybe<PropertyValue> GetDecodeProperty(PropertyName aName) const override { + if (RefPtr<MediaDataDecoder> decoder = GetDecoderOnNonOwnerThread()) { + return decoder->GetDecodeProperty(aName); + } + return MediaDataDecoder::GetDecodeProperty(aName); + } + class CodecChangeMonitor { public: virtual bool CanBeInstantiated() const = 0; diff --git a/dom/media/platforms/wrappers/MediaDataDecoderProxy.cpp b/dom/media/platforms/wrappers/MediaDataDecoderProxy.cpp @@ -152,4 +152,11 @@ MediaDataDecoder::ConversionRequired MediaDataDecoderProxy::NeedsConversion() return mProxyDecoder->NeedsConversion(); } +Maybe<MediaDataDecoder::PropertyValue> MediaDataDecoderProxy::GetDecodeProperty( + MediaDataDecoder::PropertyName aName) const { + MOZ_ASSERT(!mIsShutdown); + + return mProxyDecoder->GetDecodeProperty(aName); +} + } // namespace mozilla diff --git a/dom/media/platforms/wrappers/MediaDataDecoderProxy.h b/dom/media/platforms/wrappers/MediaDataDecoderProxy.h @@ -46,6 +46,7 @@ class MediaDataDecoderProxy bool SupportDecoderRecycling() const override; bool ShouldDecoderAlwaysBeRecycled() const override; ConversionRequired NeedsConversion() const override; + Maybe<PropertyValue> GetDecodeProperty(PropertyName aName) const override; protected: ~MediaDataDecoderProxy() = default;