From f0876e7f3ccb500ad0a829e9b090895885dc8b65 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Mon, 26 Feb 2024 09:23:10 +0000 Subject: [PATCH 01/17] [GStreamer][WPE] Replace platform ifdefs with runtime quirks https://bugs.webkit.org/show_bug.cgi?id=269287 Reviewed by NOBODY (OOPS!). Compile-time quirks prevent re-usability of the same binaries across different platforms (using containers). The proposed solution is to always compile those, they don't depend on any specific APIs so that shouldn't be an issue. Then at runtime quirks can be tweaked using the WEBKIT_GST_QUIRKS and WEBKIT_GST_HOLE_PUNCH environment variables. * Source/WebCore/platform/SourcesGStreamer.txt: * Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.cpp: (WebCore::AudioDestinationGStreamer::AudioDestinationGStreamer): * Source/WebCore/platform/audio/gstreamer/AudioFileReaderGStreamer.cpp: (WebCore::decodebinAutoplugSelectCallback): (WebCore::AudioFileReader::~AudioFileReader): (WebCore::AudioFileReader::decodeAudioForBusCreation): * Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp: (WebCore::ensureGStreamerInitialized): (WebCore::registerWebKitGStreamerElements): * Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp: (WebCore::GStreamerRegistryScanner::ElementFactories::ElementFactories): (WebCore::GStreamerRegistryScanner::ElementFactories::hasElementForCaps const): * Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp: (WebCore::MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer): (WebCore::MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer): (WebCore::setSyncOnClock): (WebCore::MediaPlayerPrivateGStreamer::createAudioSink): (WebCore::MediaPlayerPrivateGStreamer::gstreamerPositionFromSinks const): (WebCore::MediaPlayerPrivateGStreamer::naturalSize const): (WebCore::MediaPlayerPrivateGStreamer::handleMessage): (WebCore::MediaPlayerPrivateGStreamer::configureElement): (WebCore::MediaPlayerPrivateGStreamer::configureElementPlatformQuirks): (WebCore::MediaPlayerPrivateGStreamer::createGSTPlayBin): (WebCore::MediaPlayerPrivateGStreamer::swapBuffersIfNeeded): (WebCore::setRectangleToVideoSink): (WebCore::MediaPlayerPrivateGStreamer::isHolePunchRenderingEnabled const): (WebCore::MediaPlayerPrivateGStreamer::createHolePunchVideoSink): (WebCore::MediaPlayerPrivateGStreamer::shouldIgnoreIntrinsicSize): (WebCore::MediaPlayerPrivateGStreamer::createVideoSink): * Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h: * Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.cpp: Added. (WebCore::GStreamerHolePunchQuirkBcmNexus::setHolePunchVideoRectangle): * Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.h: Added. * Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp: Added. (WebCore::GStreamerHolePunchQuirkWesteros::createHolePunchVideoSink): (WebCore::GStreamerHolePunchQuirkWesteros::setHolePunchVideoRectangle): * Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h: Added. * Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp: Added. (WebCore::GStreamerQuirkAmLogic::GStreamerQuirkAmLogic): (WebCore::GStreamerQuirkAmLogic::createWebAudioSink): (WebCore::GStreamerQuirkAmLogic::configureElement): * Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h: Added. * Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.cpp: Added. (WebCore::GStreamerQuirkBcmNexus::GStreamerQuirkBcmNexus): (WebCore::GStreamerQuirkBcmNexus::isHardwareAccelerated): * Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.h: Added. * Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp: Added. (WebCore::GStreamerQuirkBroadcom::GStreamerQuirkBroadcom): (WebCore::GStreamerQuirkBroadcom::configureElement): (WebCore::GStreamerQuirkBroadcom::isHardwareAccelerated): * Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h: Added. * Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp: Added. (WebCore::GStreamerQuirkRealtek::GStreamerQuirkRealtek): (WebCore::GStreamerQuirkRealtek::createWebAudioSink): (WebCore::GStreamerQuirkRealtek::configureElement): (WebCore::GStreamerQuirkRealtek::isHardwareAccelerated): * Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h: Added. * Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp: Added. (WebCore::GStreamerQuirkWesteros::GStreamerQuirkWesteros): (WebCore::GStreamerQuirkWesteros::configureElement): (WebCore::GStreamerQuirkWesteros::isHardwareAccelerated): * Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h: Added. * Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp: Added. (WebCore::GStreamerQuirksManager::singleton): (WebCore::GStreamerQuirksManager::GStreamerQuirksManager): (WebCore::GStreamerQuirksManager::isEnabled const): (WebCore::GStreamerQuirksManager::createWebAudioSink): (WebCore::GStreamerQuirksManager::createHolePunchVideoSink): (WebCore::GStreamerQuirksManager::setHolePunchVideoRectangle): (WebCore::GStreamerQuirksManager::configureElement): (WebCore::GStreamerQuirksManager::isHardwareAccelerated const): (WebCore::GStreamerQuirksManager::supportsVideoHolePunchRendering const): (WebCore::GStreamerQuirksManager::audioVideoDecoderFactoryListType const): (WebCore::GStreamerQuirksManager::disallowedWebAudioDecoders const): * Source/WebCore/platform/gstreamer/GStreamerQuirks.h: Added. (WebCore::GStreamerQuirk::isPlatformSupported const): (WebCore::GStreamerQuirk::createWebAudioSink): (WebCore::GStreamerQuirk::configureElement): (WebCore::GStreamerQuirk::isHardwareAccelerated): (WebCore::GStreamerQuirk::audioVideoDecoderFactoryListType const): (WebCore::GStreamerQuirk::disallowedWebAudioDecoders const): (WebCore::GStreamerHolePunchQuirk::createHolePunchVideoSink): (WebCore::GStreamerHolePunchQuirk::setHolePunchVideoRectangle): * Source/cmake/OptionsWPE.cmake: --- Source/WebCore/platform/GStreamer.cmake | 8 + .../gstreamer/AudioDestinationGStreamer.cpp | 24 +- .../gstreamer/AudioFileReaderGStreamer.cpp | 26 +-- .../graphics/gstreamer/GStreamerCommon.cpp | 18 +- .../gstreamer/GStreamerRegistryScanner.cpp | 33 ++- .../gstreamer/MediaPlayerPrivateGStreamer.cpp | 215 +++++++----------- .../gstreamer/MediaPlayerPrivateGStreamer.h | 12 +- .../GStreamerHolePunchQuirkBcmNexus.cpp | 42 ++++ .../GStreamerHolePunchQuirkBcmNexus.h | 40 ++++ .../GStreamerHolePunchQuirkWesteros.cpp | 59 +++++ .../GStreamerHolePunchQuirkWesteros.h | 39 ++++ .../gstreamer/GStreamerQuirkAmLogic.cpp | 65 ++++++ .../gstreamer/GStreamerQuirkAmLogic.h | 41 ++++ .../gstreamer/GStreamerQuirkBcmNexus.cpp | 61 +++++ .../gstreamer/GStreamerQuirkBcmNexus.h | 44 ++++ .../gstreamer/GStreamerQuirkBroadcom.cpp | 73 ++++++ .../gstreamer/GStreamerQuirkBroadcom.h | 45 ++++ .../gstreamer/GStreamerQuirkRealtek.cpp | 88 +++++++ .../gstreamer/GStreamerQuirkRealtek.h | 45 ++++ .../gstreamer/GStreamerQuirkWesteros.cpp | 87 +++++++ .../gstreamer/GStreamerQuirkWesteros.h | 43 ++++ .../platform/gstreamer/GStreamerQuirks.cpp | 212 +++++++++++++++++ .../platform/gstreamer/GStreamerQuirks.h | 101 ++++++++ 23 files changed, 1202 insertions(+), 219 deletions(-) create mode 100644 Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp create mode 100644 Source/WebCore/platform/gstreamer/GStreamerQuirks.h diff --git a/Source/WebCore/platform/GStreamer.cmake b/Source/WebCore/platform/GStreamer.cmake index 066e594942bc6..eaea38f4bb005 100644 --- a/Source/WebCore/platform/GStreamer.cmake +++ b/Source/WebCore/platform/GStreamer.cmake @@ -67,6 +67,14 @@ if (ENABLE_VIDEO OR ENABLE_WEB_AUDIO) platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp platform/gstreamer/GStreamerCodecUtilities.cpp + platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.cpp + platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp + platform/gstreamer/GStreamerQuirkAmLogic.cpp + platform/gstreamer/GStreamerQuirkBcmNexus.cpp + platform/gstreamer/GStreamerQuirkBroadcom.cpp + platform/gstreamer/GStreamerQuirkRealtek.cpp + platform/gstreamer/GStreamerQuirkWesteros.cpp + platform/gstreamer/GStreamerQuirks.cpp platform/gstreamer/VideoEncoderPrivateGStreamer.cpp platform/mediarecorder/MediaRecorderPrivateGStreamer.cpp diff --git a/Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.cpp b/Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.cpp index 947aad84e8c61..84cd5c14999e6 100644 --- a/Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.cpp +++ b/Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.cpp @@ -27,6 +27,7 @@ #include "AudioSourceProvider.h" #include "AudioUtilities.h" #include "GStreamerCommon.h" +#include "GStreamerQuirks.h" #include "Logging.h" #include "WebKitAudioSinkGStreamer.h" #include "WebKitWebAudioSourceGStreamer.h" @@ -124,34 +125,19 @@ AudioDestinationGStreamer::AudioDestinationGStreamer(AudioIOCallback& callback, m_src = GST_ELEMENT_CAST(g_object_new(WEBKIT_TYPE_WEB_AUDIO_SRC, "rate", sampleRate, "bus", m_renderBus.get(), "destination", this, "frames", AudioUtilities::renderQuantumSize, nullptr)); -#if PLATFORM(AMLOGIC) - // autoaudiosink changes child element state to READY internally in auto detection phase - // that causes resource acquisition in some cases interrupting any playback already running. - // On Amlogic we need to set direct-mode=false prop before changing state to READY - // but this is not possible with autoaudiosink. - GRefPtr audioSink = makeGStreamerElement("amlhalasink", nullptr); - ASSERT_WITH_MESSAGE(audioSink, "amlhalasink should be available in the system but it is not"); - g_object_set(audioSink.get(), "direct-mode", FALSE, nullptr); -#else - GRefPtr audioSink = createPlatformAudioSink("music"_s); -#endif + auto& quirksManager = GStreamerQuirksManager::singleton(); + GRefPtr audioSink = quirksManager.createWebAudioSink(); m_audioSinkAvailable = audioSink; if (!audioSink) { GST_ERROR("Failed to create GStreamer audio sink element"); return; } - // Probe platform early on for a working audio output device. This is not needed for the WebKit - // custom audio sink because it doesn't rely on autoaudiosink. - if (!WEBKIT_IS_AUDIO_SINK(audioSink.get())) { + // Probe platform early on for a working audio output device in autoaudiosink. + if (g_str_has_prefix(GST_OBJECT_NAME(audioSink.get()), "autoaudiosink")) { g_signal_connect(audioSink.get(), "child-added", G_CALLBACK(+[](GstChildProxy*, GObject* object, gchar*, gpointer) { if (GST_IS_AUDIO_BASE_SINK(object)) g_object_set(GST_AUDIO_BASE_SINK(object), "buffer-time", static_cast(100000), nullptr); - -#if PLATFORM(REALTEK) - if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstRTKAudioSink")) - g_object_set(object, "media-tunnel", FALSE, "audio-service", TRUE, nullptr); -#endif }), nullptr); // Autoaudiosink does the real sink detection in the GST_STATE_NULL->READY transition diff --git a/Source/WebCore/platform/audio/gstreamer/AudioFileReaderGStreamer.cpp b/Source/WebCore/platform/audio/gstreamer/AudioFileReaderGStreamer.cpp index 0fc782889662f..55ec4bccbb2da 100644 --- a/Source/WebCore/platform/audio/gstreamer/AudioFileReaderGStreamer.cpp +++ b/Source/WebCore/platform/audio/gstreamer/AudioFileReaderGStreamer.cpp @@ -24,6 +24,7 @@ #include "AudioBus.h" #include "GStreamerCommon.h" +#include "GStreamerQuirks.h" #include #include #include @@ -85,11 +86,11 @@ class AudioFileReader : public CanMakeWeakPtr { bool m_errorOccurred { false }; }; -#if PLATFORM(BCM_NEXUS) || PLATFORM(BROADCOM) || PLATFORM(REALTEK) int decodebinAutoplugSelectCallback(GstElement*, GstPad*, GstCaps*, GstElementFactory* factory, gpointer) { static int GST_AUTOPLUG_SELECT_SKIP; static int GST_AUTOPLUG_SELECT_TRY; + static Vector pluginsToSkip; static std::once_flag onceFlag; std::call_once(onceFlag, [] { GEnumClass* enumClass = G_ENUM_CLASS(g_type_class_ref(g_type_from_name("GstAutoplugSelectResult"))); @@ -98,24 +99,10 @@ int decodebinAutoplugSelectCallback(GstElement*, GstPad*, GstCaps*, GstElementFa value = g_enum_get_value_by_name(enumClass, "GST_AUTOPLUG_SELECT_TRY"); GST_AUTOPLUG_SELECT_TRY = value->value; g_type_class_unref(enumClass); + + pluginsToSkip = GStreamerQuirksManager::singleton().disallowedWebAudioDecoders(); }); - const Vector pluginsToSkip = { -#if PLATFORM(BCM_NEXUS) || PLATFORM(BROADCOM) - "brcmaudfilter"_s, -#endif -#if PLATFORM(REALTEK) - "omxaacdec"_s, - "omxac3dec"_s, - "omxac4dec"_s, - "omxeac3dec"_s, - "omxflacdec"_s, - "omxlpcmdec"_s, - "omxmp3dec"_s, - "omxopusdec"_s, - "omxvorbisdec"_s, -#endif - }; auto factoryName = StringView::fromLatin1(gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory))); for (const auto& pluginToSkip : pluginsToSkip) { if (pluginToSkip == factoryName) @@ -123,7 +110,6 @@ int decodebinAutoplugSelectCallback(GstElement*, GstPad*, GstCaps*, GstElementFa } return GST_AUTOPLUG_SELECT_TRY; } -#endif static void copyGstreamerBuffersToAudioChannel(const GRefPtr& buffers, AudioChannel* audioChannel) { @@ -173,9 +159,7 @@ AudioFileReader::~AudioFileReader() if (m_decodebin) { g_signal_handlers_disconnect_matched(m_decodebin.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); -#if PLATFORM(BCM_NEXUS) || PLATFORM(BROADCOM) || PLATFORM(REALTEK) g_signal_handlers_disconnect_matched(m_decodebin.get(), G_SIGNAL_MATCH_FUNC, 0, 0, nullptr, reinterpret_cast(decodebinAutoplugSelectCallback), nullptr); -#endif m_decodebin = nullptr; } @@ -426,9 +410,7 @@ void AudioFileReader::decodeAudioForBusCreation() g_object_set(source, "stream", memoryStream.get(), nullptr); m_decodebin = makeGStreamerElement("decodebin", "decodebin"); -#if PLATFORM(BCM_NEXUS) || PLATFORM(BROADCOM) || PLATFORM(REALTEK) g_signal_connect(m_decodebin.get(), "autoplug-select", G_CALLBACK(decodebinAutoplugSelectCallback), nullptr); -#endif g_signal_connect_swapped(m_decodebin.get(), "pad-added", G_CALLBACK(decodebinPadAddedCallback), this); gst_bin_add_many(GST_BIN(m_pipeline.get()), source, m_decodebin.get(), nullptr); diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp index 6a10233f154cb..1e2c6138b1e18 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp @@ -27,6 +27,7 @@ #include "DMABufVideoSinkGStreamer.h" #include "GLVideoSinkGStreamer.h" #include "GStreamerAudioMixer.h" +#include "GStreamerQuirks.h" #include "GStreamerRegistryScanner.h" #include "GStreamerSinksWorkarounds.h" #include "GUniquePtrGStreamer.h" @@ -307,19 +308,6 @@ bool ensureGStreamerInitialized() gst_mpegts_initialize(); #endif -#if PLATFORM(BCM_NEXUS) - { - auto registry = gst_registry_get(); - GRefPtr brcmaudfilter = adoptGRef(gst_registry_lookup_feature(registry, "brcmaudfilter")); - GRefPtr mpegaudioparse = adoptGRef(gst_registry_lookup_feature(registry, "mpegaudioparse")); - - if (brcmaudfilter && mpegaudioparse) { - GST_INFO("overriding mpegaudioparse rank with brcmaudfilter rank + 1"); - gst_plugin_feature_set_rank(mpegaudioparse.get(), gst_plugin_feature_get_rank(brcmaudfilter.get()) + 1); - } - } -#endif - registerAppsinkWithWorkaroundsIfNeeded(); #endif }); @@ -421,6 +409,10 @@ void registerWebKitGStreamerElements() if (auto vaapiPlugin = adoptGRef(gst_registry_find_plugin(registry, "vaapi"))) gst_registry_remove_plugin(registry, vaapiPlugin.get()); } + + // Make sure the quirks are created as early as possible. + [[maybe_unused]] auto& quirksManager = GStreamerQuirksManager::singleton(); + registryWasUpdated = true; }); diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp index 55cb338448d91..dbea71ce146a2 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp @@ -19,6 +19,7 @@ #include "config.h" #include "GStreamerRegistryScanner.h" +#include "GStreamerQuirks.h" #if USE(GSTREAMER) #include "ContentType.h" @@ -77,17 +78,12 @@ void GStreamerRegistryScanner::getSupportedDecodingTypes(HashSet types) { -#if PLATFORM(BCM_NEXUS) || PLATFORM(BROADCOM) + auto& quirksManager = GStreamerQuirksManager::singleton(); + auto audioVideoDecoderFactory = quirksManager.audioVideoDecoderFactoryListType(); if (types.contains(Type::AudioDecoder)) - audioDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL); + audioDecoderFactories = gst_element_factory_list_get_elements(audioVideoDecoderFactory | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL); if (types.contains(Type::VideoDecoder)) - videoDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL); -#else - if (types.contains(Type::AudioDecoder)) - audioDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL); - if (types.contains(Type::VideoDecoder)) - videoDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL); -#endif + videoDecoderFactories = gst_element_factory_list_get_elements(audioVideoDecoderFactory | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL); if (types.contains(Type::AudioParser)) audioParserFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_NONE); if (types.contains(Type::VideoParser)) @@ -236,15 +232,16 @@ GStreamerRegistryScanner::RegistryLookupResult GStreamerRegistryScanner::Element auto* factory = reinterpret_cast(factories->data); auto metadata = String::fromLatin1(gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS)); auto components = metadata.split('/'); - if (components.contains("Hardware"_s) -#if PLATFORM(BCM_NEXUS) || PLATFORM(BROADCOM) - || g_str_has_prefix(GST_OBJECT_NAME(factory), "brcm") -#elif PLATFORM(REALTEK) - || g_str_has_prefix(GST_OBJECT_NAME(factory), "omx") -#elif USE(WESTEROS_SINK) - || g_str_has_prefix(GST_OBJECT_NAME(factory), "westeros") -#endif - ) { + auto& quirksManager = GStreamerQuirksManager::singleton(); + if (quirksManager.isEnabled()) { + auto isAccelerated = quirksManager.isHardwareAccelerated(factory); + if (isAccelerated && *isAccelerated) { + isUsingHardware = true; + selectedFactory = factory; + break; + } + } + if (components.contains("Hardware"_s)) { isUsingHardware = true; selectedFactory = factory; break; diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 9764a80f45cec..63a8dcdd8f140 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -28,31 +28,33 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) -#include "GraphicsContext.h" +#include "AudioTrackPrivateGStreamer.h" #include "GStreamerAudioMixer.h" #include "GStreamerCommon.h" +#include "GStreamerQuirks.h" #include "GStreamerRegistryScanner.h" +#include "GraphicsContext.h" #include "HTTPHeaderNames.h" #include "ImageGStreamer.h" #include "ImageOrientation.h" +#include "InbandMetadataTextTrackPrivateGStreamer.h" +#include "InbandTextTrackPrivateGStreamer.h" #include "IntRect.h" #include "Logging.h" #include "MediaPlayer.h" #include "MediaPlayerRequestInstallMissingPluginsCallback.h" #include "MIMETypeRegistry.h" +#include "MediaPlayer.h" #include "NotImplemented.h" #include "SecurityOrigin.h" -#include "TimeRanges.h" -#include "VideoSinkGStreamer.h" -#include "WebKitAudioSinkGStreamer.h" -#include "WebKitWebSourceGStreamer.h" -#include "AudioTrackPrivateGStreamer.h" -#include "InbandMetadataTextTrackPrivateGStreamer.h" -#include "InbandTextTrackPrivateGStreamer.h" #include "TextCombinerGStreamer.h" #include "TextSinkGStreamer.h" +#include "TimeRanges.h" #include "VideoFrameMetadataGStreamer.h" +#include "VideoSinkGStreamer.h" #include "VideoTrackPrivateGStreamer.h" +#include "WebKitAudioSinkGStreamer.h" +#include "WebKitWebSourceGStreamer.h" #if ENABLE(MEDIA_STREAM) #include "GStreamerMediaStreamSource.h" @@ -83,20 +85,24 @@ #include #include #include -#include -#include #include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include #include +#include #if USE(GSTREAMER_MPEGTS) #define GST_USE_UNSTABLE_API @@ -144,9 +150,7 @@ GST_DEBUG_CATEGORY(webkit_media_player_debug); namespace WebCore { using namespace std; -#if USE(GSTREAMER_HOLEPUNCH) static const FloatSize s_holePunchDefaultFrameSize(1280, 720); -#endif static void initializeDebugCategory() { @@ -211,10 +215,8 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer() GST_DEBUG_OBJECT(pipeline(), "Disposing player"); m_isPlayerShuttingDown.store(true); -#if USE(GSTREAMER_HOLEPUNCH) if (m_gstreamerHolePunchHost) m_gstreamerHolePunchHost->playerPrivateWillBeDestroyed(); -#endif m_sinkTaskQueue.startAborting(); @@ -1008,7 +1010,7 @@ void MediaPlayerPrivateGStreamer::setPlaybinURL(const URL& url) g_object_set(m_pipeline.get(), "uri", m_url.string().utf8().data(), nullptr); } -static void setSyncOnClock(GstElement *element, bool sync) +static void setSyncOnClock(GstElement* element, bool sync) { if (!element) return; @@ -1302,11 +1304,12 @@ void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState networ GstElement* MediaPlayerPrivateGStreamer::createAudioSink() { -#if PLATFORM(BROADCOM) || USE(WESTEROS_SINK) || PLATFORM(AMLOGIC) || PLATFORM(REALTEK) + auto& quirksManager = GStreamerQuirksManager::singleton(); + // If audio is being controlled by an another pipeline, creating sink here may interfere with // audio playback. Instead, check if an audio sink was setup in handleMessage and use it. - return nullptr; -#endif + if (quirksManager.isEnabled()) + return nullptr; // For platform specific audio sinks, they need to be properly upranked so that they get properly autoplugged. @@ -1364,9 +1367,7 @@ GstClockTime MediaPlayerPrivateGStreamer::gstreamerPositionFromSinks() const gint64 videoPosition = GST_CLOCK_TIME_NONE; gst_query_parse_position(query.get(), 0, &videoPosition); GST_TRACE_OBJECT(pipeline(), "Video position %" GST_TIME_FORMAT, GST_TIME_ARGS(videoPosition)); - if (GST_CLOCK_TIME_IS_VALID(videoPosition) && (!GST_CLOCK_TIME_IS_VALID(gstreamerPosition) - || (m_playbackRate >= 0 && videoPosition > gstreamerPosition) - || (m_playbackRate < 0 && videoPosition < gstreamerPosition))) + if (GST_CLOCK_TIME_IS_VALID(videoPosition) && (!GST_CLOCK_TIME_IS_VALID(gstreamerPosition) || (m_playbackRate >= 0 && videoPosition > gstreamerPosition) || (m_playbackRate < 0 && videoPosition < gstreamerPosition))) gstreamerPosition = videoPosition; } return static_cast(gstreamerPosition); @@ -1655,17 +1656,13 @@ FloatSize MediaPlayerPrivateGStreamer::naturalSize() const if (!hasVideo()) return FloatSize(); - if (!m_videoSize.isEmpty()) + if (!m_videoSize.isEmpty() && !isHolePunchRenderingEnabled()) return m_videoSize; -#if USE(GSTREAMER_HOLEPUNCH) - // When using the holepuch we may not be able to get the video frames size, so we can't use + // When using the holepunch we may not be able to get the video frames size, so we can't use // it. But we need to report some non empty naturalSize for the player's GraphicsLayer // to be properly created. return s_holePunchDefaultFrameSize; -#endif - - return m_videoSize; } void MediaPlayerPrivateGStreamer::configureMediaStreamAudioTracks() @@ -1892,8 +1889,7 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) GstState newState; gst_message_parse_state_changed(message, ¤tState, &newState, nullptr); -#if USE(GSTREAMER_HOLEPUNCH) && (USE(WPEWEBKIT_PLATFORM_BCM_NEXUS) || USE(WESTEROS_SINK)) - if (currentState <= GST_STATE_READY && newState >= GST_STATE_READY) { + if (isHolePunchRenderingEnabled() && currentState <= GST_STATE_READY && newState >= GST_STATE_READY) { // If we didn't create a video sink, store a reference to the created one. if (!m_videoSink) { // Detect the videoSink element. Getting the video-sink property of the pipeline requires @@ -1911,10 +1907,9 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) } } } -#endif -#if PLATFORM(BROADCOM) || USE(WESTEROS_SINK) || PLATFORM(AMLOGIC) || PLATFORM(REALTEK) - if (currentState <= GST_STATE_READY && newState >= GST_STATE_READY) { + auto& quirksManager = GStreamerQuirksManager::singleton(); + if (quirksManager.isEnabled() && currentState <= GST_STATE_READY && newState >= GST_STATE_READY) { // Detect an audio sink element and store reference to it if it supersedes what we currently have. GstElement* element = GST_ELEMENT(GST_MESSAGE_SRC(message)); if (GST_OBJECT_FLAG_IS_SET(element, GST_ELEMENT_FLAG_SINK)) { @@ -1924,7 +1919,6 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) m_audioSink = element; } } -#endif if (!messageSourceIsPlaybin || m_isDelayingLoad) break; @@ -2284,9 +2278,7 @@ void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry void MediaPlayerPrivateGStreamer::configureElement(GstElement* element) { -#if PLATFORM(BROADCOM) || USE(WESTEROS_SINK) || PLATFORM(AMLOGIC) || PLATFORM(REALTEK) configureElementPlatformQuirks(element); -#endif GUniquePtr elementName(gst_element_get_name(element)); auto elementClass = makeString(gst_element_get_metadata(element, GST_ELEMENT_METADATA_KLASS)); @@ -2322,58 +2314,22 @@ void MediaPlayerPrivateGStreamer::configureElement(GstElement* element) g_object_set(G_OBJECT(element), "high-watermark", 0.10, nullptr); } -#if PLATFORM(BROADCOM) || USE(WESTEROS_SINK) || PLATFORM(AMLOGIC) || PLATFORM(REALTEK) void MediaPlayerPrivateGStreamer::configureElementPlatformQuirks(GstElement* element) { GST_DEBUG_OBJECT(pipeline(), "Element set-up for %s", GST_ELEMENT_NAME(element)); -#if PLATFORM(AMLOGIC) - if (!g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstAmlHalAsink")) { - GST_INFO_OBJECT(pipeline(), "Set property disable-xrun to TRUE"); - g_object_set(element, "disable-xrun", TRUE, nullptr); - if (hasVideo()) - g_object_set(G_OBJECT(element), "wait-video", TRUE, nullptr); - } -#endif - -#if PLATFORM(BROADCOM) - if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiosink")) - g_object_set(G_OBJECT(element), "async", TRUE, nullptr); - else if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiodecoder")) { - if (m_isLiveStream.value_or(false)) { - // Limit BCM audio decoder buffering to 1sec so live progressive playback can start faster. - g_object_set(G_OBJECT(element), "limit_buffering_ms", 1000, nullptr); - } - } -#if ENABLE(MEDIA_STREAM) - if (m_streamPrivate && !g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstBrcmPCMSink") && gstObjectHasProperty(element, "low_latency")) { - GST_DEBUG_OBJECT(pipeline(), "Set 'low_latency' in brcmpcmsink"); - g_object_set(element, "low_latency", TRUE, "low_latency_max_queued_ms", 60, nullptr); - } -#endif -#endif - -#if ENABLE(MEDIA_STREAM) - if (m_streamPrivate && !g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstWesterosSink") && gstObjectHasProperty(element, "immediate-output")) { - GST_DEBUG_OBJECT(pipeline(), "Enable 'immediate-output' in WesterosSink"); - g_object_set(element, "immediate-output", TRUE, nullptr); - } -#endif + OptionSet characteristics; + if (isMediaStreamPlayer()) + characteristics.add({ ElementRuntimeCharacteristics::IsMediaStream }); + if (hasVideo()) + characteristics.add({ ElementRuntimeCharacteristics::HasVideo }); + if (hasAudio()) + characteristics.add({ ElementRuntimeCharacteristics::HasAudio }); + if (m_isLiveStream.value_or(false)) + characteristics.add({ ElementRuntimeCharacteristics::IsLiveStream }); -#if ENABLE(MEDIA_STREAM) && PLATFORM(REALTEK) - if (m_streamPrivate) { - if (gstObjectHasProperty(element, "media-tunnel")) { - GST_INFO_OBJECT(pipeline(), "Enable 'immediate-output' in rtkaudiosink"); - g_object_set(element, "media-tunnel", FALSE, "audio-service", TRUE, "lowdelay-sync-mode", TRUE, nullptr); - } - if (gstObjectHasProperty(element, "lowdelay-mode")) { - GST_INFO_OBJECT(pipeline(), "Enable 'lowdelay-mode' in rtk omx decoder"); - g_object_set(element, "lowdelay-mode", TRUE, nullptr); - } - } -#endif + GStreamerQuirksManager::singleton().configureElement(element, WTFMove(characteristics)); } -#endif void MediaPlayerPrivateGStreamer::configureDownloadBuffer(GstElement* element) { @@ -3037,19 +2993,15 @@ void MediaPlayerPrivateGStreamer::createGSTPlayBin(const URL& url) if (!m_player->isVideoPlayer()) return; -#if !USE(GSTREAMER_HOLEPUNCH) + if (isHolePunchRenderingEnabled()) + return; + GRefPtr videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink")); if (videoSinkPad) g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(+[](GstPad* videoSinkPad, GParamSpec*, MediaPlayerPrivateGStreamer* player) { player->videoSinkCapsChanged(videoSinkPad); - }), this); -#endif - -#if USE(WESTEROS_SINK) - // Configure Westeros sink before it allocates resources. - if (m_videoSink) - configureElementPlatformQuirks(m_videoSink.get()); -#endif + }), + this); } void MediaPlayerPrivateGStreamer::configureVideoDecoder(GstElement* decoder) @@ -3184,9 +3136,7 @@ PlatformLayer* MediaPlayerPrivateGStreamer::platformLayer() const #if USE(NICOSIA) void MediaPlayerPrivateGStreamer::swapBuffersIfNeeded() { -#if USE(GSTREAMER_HOLEPUNCH) pushNextHolePunchBuffer(); -#endif } #else RefPtr MediaPlayerPrivateGStreamer::proxy() const @@ -3196,9 +3146,8 @@ RefPtr MediaPlayerPrivateGStreamer::proxy() con void MediaPlayerPrivateGStreamer::swapBuffersIfNeeded() { -#if USE(GSTREAMER_HOLEPUNCH) - pushNextHolePunchBuffer(); -#endif + if (isHolePunchRenderingEnabled()) + pushNextHolePunchBuffer(); } #endif @@ -4059,7 +4008,6 @@ GstElement* MediaPlayerPrivateGStreamer::createVideoSinkGL() } #endif // USE(GSTREAMER_GL) -#if USE(GSTREAMER_HOLEPUNCH) static void setRectangleToVideoSink(GstElement* videoSink, const IntRect& rect) { // Here goes the platform-dependant code to set to the videoSink the size @@ -4068,14 +4016,9 @@ static void setRectangleToVideoSink(GstElement* videoSink, const IntRect& rect) if (!videoSink) return; -#if USE(WESTEROS_SINK) || USE(WPEWEBKIT_PLATFORM_BCM_NEXUS) - // Valid for brcmvideosink and westerossink. - GUniquePtr rectString(g_strdup_printf("%d,%d,%d,%d", rect.x(), rect.y(), rect.width(), rect.height())); - g_object_set(videoSink, "rectangle", rectString.get(), nullptr); - return; -#endif - - UNUSED_PARAM(rect); + auto& quirksManager = GStreamerQuirksManager::singleton(); + if (quirksManager.isEnabled()) + quirksManager.setHolePunchVideoRectangle(videoSink, rect); } class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePunchClient { @@ -4086,31 +4029,28 @@ class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePu RefPtr m_host; }; +bool MediaPlayerPrivateGStreamer::isHolePunchRenderingEnabled() const +{ + auto& quirksManager = GStreamerQuirksManager::singleton(); + return quirksManager.isEnabled() && quirksManager.supportsVideoHolePunchRendering(); +} + GstElement* MediaPlayerPrivateGStreamer::createHolePunchVideoSink() { - // Here goes the platform-dependant code to create the videoSink. As a default - // we use a fakeVideoSink so nothing is drawn to the page. + if (!isHolePunchRenderingEnabled()) + return nullptr; -#if USE(WESTEROS_SINK) - AtomString val; - bool isPIPRequested = - m_player->doesHaveAttribute("pip"_s, &val) && equalLettersIgnoringASCIICase(val, "true"_s); - if (m_isLegacyPlaybin && !isPIPRequested) + auto player = m_player.get(); + if (!player) return nullptr; - // Westeros using holepunch. - GstElement* videoSink = makeGStreamerElement("westerossink", "WesterosVideoSink"); - g_object_set(G_OBJECT(videoSink), "zorder", 0.0f, nullptr); - if (isPIPRequested) - g_object_set(G_OBJECT(videoSink), "res-usage", 0u, nullptr); - return videoSink; -#endif -#if USE(WPEWEBKIT_PLATFORM_BCM_NEXUS) - // Nexus boxes use autovideosink. - return nullptr; -#endif + auto& quirksManager = GStreamerQuirksManager::singleton(); + auto sink = quirksManager.createHolePunchVideoSink(m_isLegacyPlaybin, player.get()); - return makeGStreamerElement("fakevideosink", nullptr); + // Configure sink before it allocates resources. + if (sink) + configureElement(sink); + return sink; } void MediaPlayerPrivateGStreamer::pushNextHolePunchBuffer() @@ -4144,7 +4084,11 @@ void MediaPlayerPrivateGStreamer::setVideoRectangle(const IntRect& rect) if (m_visible && !m_suspended) setRectangleToVideoSink(m_videoSink.get(), rect); } -#endif + +bool MediaPlayerPrivateGStreamer::shouldIgnoreIntrinsicSize() +{ + return isHolePunchRenderingEnabled(); +} GstElement* MediaPlayerPrivateGStreamer::createVideoSink() { @@ -4180,11 +4124,13 @@ GstElement* MediaPlayerPrivateGStreamer::createVideoSink() return m_videoSink.get(); } -#if USE(GSTREAMER_HOLEPUNCH) - m_videoSink = createHolePunchVideoSink(); - pushNextHolePunchBuffer(); - return m_videoSink.get(); -#endif + if (isHolePunchRenderingEnabled()) { + m_videoSink = createHolePunchVideoSink(); + if (m_videoSink) { + pushNextHolePunchBuffer(); + return m_videoSink.get(); + } + } #if USE(TEXTURE_MAPPER_DMABUF) if (!m_videoSink && m_canRenderingBeAccelerated) @@ -4508,32 +4454,25 @@ std::optional MediaPlayerPrivateGStreamer::videoFrameMetadat void MediaPlayerPrivateGStreamer::setPageIsVisible(bool visible) { - if (m_visible != visible) { -#if USE(GSTREAMER_HOLEPUNCH) Locker locker { m_holePunchLock }; -#endif + m_visible = visible; -#if USE(GSTREAMER_HOLEPUNCH) if (!m_visible) setRectangleToVideoSink(m_videoSink.get(), IntRect()); -#endif } } void MediaPlayerPrivateGStreamer::setPageIsSuspended(bool suspended) { if (m_suspended != suspended) { -#if USE(GSTREAMER_HOLEPUNCH) Locker locker { m_holePunchLock }; -#endif + m_suspended = suspended; -#if USE(GSTREAMER_HOLEPUNCH) if (m_suspended) setRectangleToVideoSink(m_videoSink.get(), IntRect()); -#endif } } diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h index ac361a379b610..0489f30d7afe9 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h @@ -254,7 +254,6 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface // to avoid deadlocks from threads in the playback pipeline waiting for the main thread. AbortableTaskQueue& sinkTaskQueue() { return m_sinkTaskQueue; } -#if USE(GSTREAMER_HOLEPUNCH) class GStreamerHolePunchHost : public ThreadSafeRefCounted { public: static Ref create(MediaPlayerPrivateGStreamer& playerPrivate) @@ -277,7 +276,6 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface MediaPlayerPrivateGStreamer* m_playerPrivate; }; void setVideoRectangle(const IntRect& rect); -#endif protected: enum MainThreadNotification { @@ -313,11 +311,10 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface virtual void sourceSetup(GstElement*); virtual void updatePlaybackRate(); -#if USE(GSTREAMER_HOLEPUNCH) + bool isHolePunchRenderingEnabled() const; GstElement* createHolePunchVideoSink(); void pushNextHolePunchBuffer(); - bool shouldIgnoreIntrinsicSize() final { return true; } -#endif + bool shouldIgnoreIntrinsicSize() final; #if USE(TEXTURE_MAPPER_DMABUF) GstElement* createVideoSinkDMABuf(); @@ -543,9 +540,8 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface void configureVideoDecoder(GstElement*); void configureElement(GstElement*); -#if PLATFORM(BROADCOM) || USE(WESTEROS_SINK) || PLATFORM(AMLOGIC) || PLATFORM(REALTEK) + void configureElementPlatformQuirks(GstElement*); -#endif void setPlaybinURL(const URL& urlString); @@ -669,10 +665,8 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface AbortableTaskQueue m_sinkTaskQueue; -#if USE(GSTREAMER_HOLEPUNCH) RefPtr m_gstreamerHolePunchHost; Lock m_holePunchLock; -#endif bool m_didTryToRecoverPlayingState { false }; diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.cpp b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.cpp new file mode 100644 index 0000000000000..37e3a70c4621c --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerHolePunchQuirkBcmNexus.h" + +#include "GStreamerCommon.h" + +#if USE(GSTREAMER) + +namespace WebCore { + +bool GStreamerHolePunchQuirkBcmNexus::setHolePunchVideoRectangle(GstElement* videoSink, const IntRect& rect) +{ + if (UNLIKELY(!gstObjectHasProperty(videoSink, "rectangle"))) + return false; + + auto rectString = makeString(rect.x(), ',', rect.y(), ',', rect.width(), ',', rect.height()); + g_object_set(videoSink, "rectangle", rectString.ascii().data(), nullptr); + return true; +} + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.h b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.h new file mode 100644 index 0000000000000..71e6e5e042178 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkBcmNexus.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerHolePunchQuirkBcmNexus final : public GStreamerHolePunchQuirk { +public: + const char* identifier() final { return "BcmNexusHolePunch"; } + + // NOTE: We don't override createHolePunchVideoSink here because autovideosink takes care of + // auto-plugging the right sink. + bool setHolePunchVideoRectangle(GstElement*, const IntRect&) final; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp new file mode 100644 index 0000000000000..11ac3a07d9e97 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerHolePunchQuirkWesteros.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" + +namespace WebCore { + +GstElement* GStreamerHolePunchQuirkWesteros::createHolePunchVideoSink(bool isLegacyPlaybin, const MediaPlayer* player) +{ + AtomString val; + bool isPIPRequested = player && player->doesHaveAttribute("pip"_s, &val) && equalLettersIgnoringASCIICase(val, "true"_s); + if (isLegacyPlaybin && !isPIPRequested) + return nullptr; + + // Westeros using holepunch. + GstElement* videoSink = makeGStreamerElement("westerossink", "WesterosVideoSink"); + g_object_set(videoSink, "zorder", 0.0f, nullptr); + if (isPIPRequested) + g_object_set(videoSink, "res-usage", 0u, nullptr); + return videoSink; +} + +bool GStreamerHolePunchQuirkWesteros::setHolePunchVideoRectangle(GstElement* videoSink, const IntRect& rect) +{ + if (UNLIKELY(!gstObjectHasProperty(videoSink, "rectangle"))) + return false; + + auto rectString = makeString(rect.x(), ',', rect.y(), ',', rect.width(), ',', rect.height()); + g_object_set(videoSink, "rectangle", rectString.ascii().data(), nullptr); + return true; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h new file mode 100644 index 0000000000000..5d2d2c434261c --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerHolePunchQuirkWesteros final : public GStreamerHolePunchQuirk { +public: + const char* identifier() final { return "WesterosHolePunch"; } + + GstElement* createHolePunchVideoSink(bool, const MediaPlayer*) final; + bool setHolePunchVideoRectangle(GstElement*, const IntRect&) final; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp new file mode 100644 index 0000000000000..28c39d5c711c1 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerQuirkAmLogic.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include + +namespace WebCore { + +GST_DEBUG_CATEGORY_STATIC(webkit_amlogic_quirks_debug); +#define GST_CAT_DEFAULT webkit_amlogic_quirks_debug + +GStreamerQuirkAmLogic::GStreamerQuirkAmLogic() +{ + GST_DEBUG_CATEGORY_INIT(webkit_amlogic_quirks_debug, "webkitquirksamlogic", 0, "WebKit AmLogic Quirks"); +} + +GstElement* GStreamerQuirkAmLogic::createWebAudioSink() +{ + // autoaudiosink changes child element state to READY internally in auto detection phase + // that causes resource acquisition in some cases interrupting any playback already running. + // On Amlogic we need to set direct-mode=false prop before changing state to READY + // but this is not possible with autoaudiosink. + auto sink = makeGStreamerElement("amlhalasink", nullptr); + ASSERT_WITH_MESSAGE(sink, "amlhalasink should be available in the system but it is not"); + g_object_set(sink, "direct-mode", FALSE, nullptr); + return sink; +} + +bool GStreamerQuirkAmLogic::configureElement(GstElement* element, const OptionSet& characteristics) +{ + GST_INFO("Set property disable-xrun to TRUE"); + g_object_set(element, "disable-xrun", TRUE, nullptr); + if (characteristics.contains(ElementRuntimeCharacteristics::HasVideo)) + g_object_set(element, "wait-video", TRUE, nullptr); + + return true; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h new file mode 100644 index 0000000000000..53980761601a4 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerQuirkAmLogic final : public GStreamerQuirk { +public: + GStreamerQuirkAmLogic(); + const char* identifier() final { return "AmLogic"; } + + GstElement* createWebAudioSink() final; + bool configureElement(GstElement*, const OptionSet&) final; + +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.cpp new file mode 100644 index 0000000000000..b0f3e2a57bf1e --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerQuirkBcmNexus.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include + +namespace WebCore { + +GST_DEBUG_CATEGORY_STATIC(webkit_bcmnexus_quirks_debug); +#define GST_CAT_DEFAULT webkit_bcmnexus_quirks_debug + +GStreamerQuirkBcmNexus::GStreamerQuirkBcmNexus() +{ + GST_DEBUG_CATEGORY_INIT(webkit_bcmnexus_quirks_debug, "webkitquirksbcmnexus", 0, "WebKit BcmNexus Quirks"); + m_disallowedWebAudioDecoders = { "brcmaudfilter"_s }; + + auto registry = gst_registry_get(); + auto brcmaudfilter = adoptGRef(gst_registry_lookup_feature(registry, "brcmaudfilter")); + auto mpegaudioparse = adoptGRef(gst_registry_lookup_feature(registry, "mpegaudioparse")); + + if (brcmaudfilter && mpegaudioparse) { + GST_INFO("Overriding mpegaudioparse rank with brcmaudfilter rank + 1"); + gst_plugin_feature_set_rank(mpegaudioparse.get(), gst_plugin_feature_get_rank(brcmaudfilter.get()) + 1); + } +} + +std::optional GStreamerQuirkBcmNexus::isHardwareAccelerated(GstElementFactory* factory) +{ + if (g_str_has_prefix(GST_OBJECT_NAME(factory), "brcm")) + return true; + + return std::nullopt; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.h new file mode 100644 index 0000000000000..e3987e80f4db1 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBcmNexus.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerQuirkBcmNexus final : public GStreamerQuirk { +public: + GStreamerQuirkBcmNexus(); + const char* identifier() final { return "BcmNexus"; } + + std::optional isHardwareAccelerated(GstElementFactory*) final; + std::optional audioVideoDecoderFactoryListType() const final { return GST_ELEMENT_FACTORY_TYPE_PARSER; } + Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; } + +private: + Vector m_disallowedWebAudioDecoders; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp new file mode 100644 index 0000000000000..64d7cdec346a7 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerQuirkBroadcom.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include + +namespace WebCore { + +GST_DEBUG_CATEGORY_STATIC(webkit_broadcom_quirks_debug); +#define GST_CAT_DEFAULT webkit_broadcom_quirks_debug + +GStreamerQuirkBroadcom::GStreamerQuirkBroadcom() +{ + GST_DEBUG_CATEGORY_INIT(webkit_broadcom_quirks_debug, "webkitquirksbroadcom", 0, "WebKit Broadcom Quirks"); + m_disallowedWebAudioDecoders = { "brcmaudfilter"_s }; +} + +bool GStreamerQuirkBroadcom::configureElement(GstElement* element, const OptionSet& characteristics) +{ + if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiosink")) + g_object_set(G_OBJECT(element), "async", TRUE, nullptr); + else if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiodecoder")) { + // Limit BCM audio decoder buffering to 1sec so live progressive playback can start faster. + if (characteristics.contains(ElementRuntimeCharacteristics::IsLiveStream)) + g_object_set(G_OBJECT(element), "limit_buffering_ms", 1000, nullptr); + } + + if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream)) + return true; + + if (!g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstBrcmPCMSink") && gstObjectHasProperty(element, "low_latency")) { + GST_DEBUG("Set 'low_latency' in brcmpcmsink"); + g_object_set(element, "low_latency", TRUE, "low_latency_max_queued_ms", 60, nullptr); + } + + return true; +} + +std::optional GStreamerQuirkBroadcom::isHardwareAccelerated(GstElementFactory* factory) +{ + if (g_str_has_prefix(GST_OBJECT_NAME(factory), "brcm")) + return true; + + return std::nullopt; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h new file mode 100644 index 0000000000000..32c3329471794 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerQuirkBroadcom final : public GStreamerQuirk { +public: + GStreamerQuirkBroadcom(); + const char* identifier() final { return "Broadcom"; } + + bool configureElement(GstElement*, const OptionSet&) final; + std::optional isHardwareAccelerated(GstElementFactory*) final; + std::optional audioVideoDecoderFactoryListType() const final { return GST_ELEMENT_FACTORY_TYPE_PARSER; } + Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; } + +private: + Vector m_disallowedWebAudioDecoders; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp new file mode 100644 index 0000000000000..9da23740b936f --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerQuirkRealtek.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include + +namespace WebCore { + +GST_DEBUG_CATEGORY_STATIC(webkit_realtek_quirks_debug); +#define GST_CAT_DEFAULT webkit_realtek_quirks_debug + +GStreamerQuirkRealtek::GStreamerQuirkRealtek() +{ + GST_DEBUG_CATEGORY_INIT(webkit_realtek_quirks_debug, "webkitquirksrealtek", 0, "WebKit Realtek Quirks"); + m_disallowedWebAudioDecoders = { + "omxaacdec"_s, + "omxac3dec"_s, + "omxac4dec"_s, + "omxeac3dec"_s, + "omxflacdec"_s, + "omxlpcmdec"_s, + "omxmp3dec"_s, + "omxopusdec"_s, + "omxvorbisdec"_s, + }; +} + +GstElement* GStreamerQuirkRealtek::createWebAudioSink() +{ + auto sink = makeGStreamerElement("rtkaudiosink", nullptr); + ASSERT_WITH_MESSAGE(sink, "rtkaudiosink should be available in the system but it is not"); + g_object_set(sink, "media-tunnel", FALSE, "audio-service", TRUE, nullptr); + return sink; +} + +bool GStreamerQuirkRealtek::configureElement(GstElement* element, const OptionSet& characteristics) +{ + if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream)) + return false; + + if (gstObjectHasProperty(element, "media-tunnel")) { + GST_INFO("Enable 'immediate-output' in rtkaudiosink"); + g_object_set(element, "media-tunnel", FALSE, "audio-service", TRUE, "lowdelay-sync-mode", TRUE, nullptr); + } + + if (gstObjectHasProperty(element, "lowdelay-mode")) { + GST_INFO("Enable 'lowdelay-mode' in rtk omx decoder"); + g_object_set(element, "lowdelay-mode", TRUE, nullptr); + } + + return true; +} + +std::optional GStreamerQuirkRealtek::isHardwareAccelerated(GstElementFactory* factory) +{ + if (g_str_has_prefix(GST_OBJECT_NAME(factory), "omx")) + return true; + + return std::nullopt; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h new file mode 100644 index 0000000000000..a0fc595e6c70a --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerQuirkRealtek final : public GStreamerQuirk { +public: + GStreamerQuirkRealtek(); + const char* identifier() final { return "Realtek"; } + + GstElement* createWebAudioSink() final; + bool configureElement(GstElement*, const OptionSet&) final; + std::optional isHardwareAccelerated(GstElementFactory*) final; + Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; } + +private: + Vector m_disallowedWebAudioDecoders; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp new file mode 100644 index 0000000000000..5a6c232ea45af --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerQuirkWesteros.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include + +namespace WebCore { + +GST_DEBUG_CATEGORY_STATIC(webkit_westeros_quirks_debug); +#define GST_CAT_DEFAULT webkit_westeros_quirks_debug + +GStreamerQuirkWesteros::GStreamerQuirkWesteros() +{ + GST_DEBUG_CATEGORY_INIT(webkit_westeros_quirks_debug, "webkitquirkswesteros", 0, "WebKit Westeros Quirks"); + + auto westerosFactory = adoptGRef(gst_element_factory_find("westerossink")); + if (UNLIKELY(!westerosFactory)) + return; + + gst_object_unref(gst_plugin_feature_load(GST_PLUGIN_FEATURE(westerosFactory.get()))); + for (auto* t = gst_element_factory_get_static_pad_templates(westerosFactory.get()); t; t = g_list_next(t)) { + auto* padtemplate = static_cast(t->data); + if (padtemplate->direction != GST_PAD_SINK) + continue; + if (m_sinkCaps) + m_sinkCaps = adoptGRef(gst_caps_merge(m_sinkCaps.leakRef(), gst_static_caps_get(&padtemplate->static_caps))); + else + m_sinkCaps = adoptGRef(gst_static_caps_get(&padtemplate->static_caps)); + } +} + +bool GStreamerQuirkWesteros::configureElement(GstElement* element, const OptionSet& characteristics) +{ + if (g_str_has_prefix(GST_ELEMENT_NAME(element), "uridecodebin3")) { + GRefPtr defaultCaps; + g_object_get(element, "caps", &defaultCaps.outPtr(), nullptr); + defaultCaps = adoptGRef(gst_caps_merge(gst_caps_ref(m_sinkCaps.get()), defaultCaps.leakRef())); + GST_INFO("Setting stop caps to %" GST_PTR_FORMAT, defaultCaps.get()); + g_object_set(element, "caps", defaultCaps.get(), nullptr); + return true; + } + + if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream)) + return false; + + if (!g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstWesterosSink") && gstObjectHasProperty(element, "immediate-output")) { + GST_INFO("Enable 'immediate-output' in WesterosSink"); + g_object_set(element, "immediate-output", TRUE, nullptr); + } + return true; +} + +std::optional GStreamerQuirkWesteros::isHardwareAccelerated(GstElementFactory* factory) +{ + if (g_str_has_prefix(GST_OBJECT_NAME(factory), "westeros")) + return true; + + return std::nullopt; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h new file mode 100644 index 0000000000000..810d727290914 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerQuirkWesteros final : public GStreamerQuirk { +public: + GStreamerQuirkWesteros(); + const char* identifier() final { return "Westeros"; } + + bool configureElement(GstElement*, const OptionSet&) final; + std::optional isHardwareAccelerated(GstElementFactory*) final; + +private: + GRefPtr m_sinkCaps; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp new file mode 100644 index 0000000000000..a31ac9aeba915 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "GStreamerQuirks.h" + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include "GStreamerHolePunchQuirkBcmNexus.h" +#include "GStreamerHolePunchQuirkWesteros.h" +#include "GStreamerQuirkAmLogic.h" +#include "GStreamerQuirkBcmNexus.h" +#include "GStreamerQuirkBroadcom.h" +#include "GStreamerQuirkRealtek.h" +#include "GStreamerQuirkWesteros.h" +#include +#include +#include + +namespace WebCore { + +GST_DEBUG_CATEGORY_STATIC(webkit_quirks_debug); +#define GST_CAT_DEFAULT webkit_quirks_debug + +GStreamerQuirksManager& GStreamerQuirksManager::singleton() +{ + static NeverDestroyed sharedInstance; + return sharedInstance; +} + +GStreamerQuirksManager::GStreamerQuirksManager() +{ + GST_DEBUG_CATEGORY_INIT(webkit_quirks_debug, "webkitquirks", 0, "WebKit Quirks"); + + // For the time being keep this disabled on non-WPE platforms. GTK on desktop shouldn't require + // quirks, for instance. +#if !PLATFORM(WPE) + return; +#endif + + const char* quirksList = g_getenv("WEBKIT_GST_QUIRKS"); + GST_DEBUG("Attempting to parse requested quirks: %s", GST_STR_NULL(quirksList)); + if (quirksList) { + StringView quirks { quirksList, static_cast(strlen(quirksList)) }; + if (WTF::equalLettersIgnoringASCIICase(quirks, "help"_s)) { + WTFLogAlways("Supported quirks for WEBKIT_GST_QUIRKS are: amlogic, broadcom, bcmnexus, realtek, westeros"); + return; + } + + for (const auto& identifier : quirks.split(',')) { + std::unique_ptr quirk; + if (WTF::equalLettersIgnoringASCIICase(identifier, "amlogic"_s)) + quirk = WTF::makeUnique(); + else if (WTF::equalLettersIgnoringASCIICase(identifier, "broadcom"_s)) + quirk = WTF::makeUnique(); + else if (WTF::equalLettersIgnoringASCIICase(identifier, "bcmnexus"_s)) + quirk = WTF::makeUnique(); + else if (WTF::equalLettersIgnoringASCIICase(identifier, "realtek"_s)) + quirk = WTF::makeUnique(); + else if (WTF::equalLettersIgnoringASCIICase(identifier, "westeros"_s)) + quirk = WTF::makeUnique(); + else { + GST_WARNING("Unknown quirk requested: %s. Skipping", identifier.toStringWithoutCopying().ascii().data()); + continue; + } + + if (!quirk->isPlatformSupported()) { + GST_WARNING("Quirk %s was requested but is not supported on this platform. Skipping", quirk->identifier()); + continue; + } + m_quirks.append(WTFMove(quirk)); + } + } + + const char* holePunchQuirk = g_getenv("WEBKIT_GST_HOLE_PUNCH_QUIRK"); + GST_DEBUG("Attempting to parse requested hole-punch quirk: %s", GST_STR_NULL(holePunchQuirk)); + if (!holePunchQuirk) + return; + + StringView identifier { holePunchQuirk, static_cast(strlen(holePunchQuirk)) }; + if (WTF::equalLettersIgnoringASCIICase(identifier, "help"_s)) { + WTFLogAlways("Supported quirks for WEBKIT_GST_HOLE_PUNCH_QUIRK are: westeros, bcmnexus"); + return; + } + + // TODO: Maybe check this is coherent (somehow) with the quirk(s) selected above. + if (WTF::equalLettersIgnoringASCIICase(identifier, "bcmnexus"_s)) + m_holePunchQuirk = WTF::makeUnique(); + else if (WTF::equalLettersIgnoringASCIICase(identifier, "westeros"_s)) + m_holePunchQuirk = WTF::makeUnique(); + else + GST_WARNING("HolePunch quirk %s un-supported.", identifier.toStringWithoutCopying().ascii().data()); +} + +bool GStreamerQuirksManager::isEnabled() const +{ + return !m_quirks.isEmpty(); +} + +GstElement* GStreamerQuirksManager::createWebAudioSink() +{ + for (const auto& quirk : m_quirks) { + auto* sink = quirk->createWebAudioSink(); + if (!sink) + continue; + + GST_DEBUG("Using WebAudioSink from quirk %s : %" GST_PTR_FORMAT, quirk->identifier(), sink); + return sink; + } + + GST_DEBUG("Quirks didn't specify a WebAudioSink, falling back to default sink"); + return createPlatformAudioSink("music"_s); +} + +GstElement* GStreamerQuirksManager::createHolePunchVideoSink(bool isLegacyPlaybin, const MediaPlayer* player) +{ + RELEASE_ASSERT_WITH_MESSAGE(isEnabled() && m_holePunchQuirk, "createHolePunchVideoSink() should be called only if one hole-punch quirk was requested"); + if (!isEnabled() || !m_holePunchQuirk) { + GST_DEBUG("None of the quirks requested a HolePunchSink"); + return nullptr; + } + auto sink = m_holePunchQuirk->createHolePunchVideoSink(isLegacyPlaybin, player); + GST_DEBUG("Using HolePunchSink from quirk %s : %" GST_PTR_FORMAT, m_holePunchQuirk->identifier(), sink); + return sink; +} + +void GStreamerQuirksManager::setHolePunchVideoRectangle(GstElement* videoSink, const IntRect& rect) +{ + RELEASE_ASSERT_WITH_MESSAGE(isEnabled() && m_holePunchQuirk, "setHolePunchVideoRectangle() should be called only if one hole-punch quirk was requested"); + if (!isEnabled() || !m_holePunchQuirk) { + GST_DEBUG("None of the quirks requested a HolePunchSink"); + return; + } + + if (!m_holePunchQuirk->setHolePunchVideoRectangle(videoSink, rect)) + GST_WARNING("Hole punch video rectangle configuration failed."); +} + +void GStreamerQuirksManager::configureElement(GstElement* element, OptionSet&& characteristics) +{ + GST_DEBUG("Configuring element %" GST_PTR_FORMAT, element); + for (const auto& quirk : m_quirks) { + if (quirk->configureElement(element, characteristics)) + return; + } +} + +std::optional GStreamerQuirksManager::isHardwareAccelerated(GstElementFactory* factory) const +{ + for (const auto& quirk : m_quirks) { + auto result = quirk->isHardwareAccelerated(factory); + if (!result) + continue; + + GST_DEBUG("Setting %" GST_PTR_FORMAT " as %s accelerated from quirk %s", factory, quirk->identifier(), *result ? "hardware" : "software"); + return *result; + } + + return std::nullopt; +} + +bool GStreamerQuirksManager::supportsVideoHolePunchRendering() const +{ + return m_holePunchQuirk.get(); +} + +GstElementFactoryListType GStreamerQuirksManager::audioVideoDecoderFactoryListType() const +{ + for (const auto& quirk : m_quirks) { + auto result = quirk->audioVideoDecoderFactoryListType(); + if (!result) + continue; + + GST_DEBUG("Quirk %s requests audio/video decoder factory list override to %" G_GUINT32_FORMAT, quirk->identifier(), static_cast(*result)); + return *result; + } + + return GST_ELEMENT_FACTORY_TYPE_DECODER; +} + +Vector GStreamerQuirksManager::disallowedWebAudioDecoders() const +{ + Vector result; + for (const auto& quirk : m_quirks) + result.appendVector(quirk->disallowedWebAudioDecoders()); + + return result; +} + +#undef GST_CAT_DEFAULT + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h new file mode 100644 index 0000000000000..e56bef9251f71 --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GRefPtrGStreamer.h" +#include "MediaPlayer.h" +#include +#include + +namespace WebCore { + +enum class ElementRuntimeCharacteristics : uint8_t { + IsMediaStream = 1 << 0, + HasVideo = 1 << 1, + HasAudio = 1 << 2, + IsLiveStream = 1 << 3, +}; + +class GStreamerQuirkBase { + WTF_MAKE_FAST_ALLOCATED; + +public: + GStreamerQuirkBase() = default; + virtual ~GStreamerQuirkBase() = default; + + virtual const char* identifier() = 0; +}; + +class GStreamerQuirk : public GStreamerQuirkBase { + WTF_MAKE_FAST_ALLOCATED; +public: + GStreamerQuirk() = default; + virtual ~GStreamerQuirk() = default; + + virtual bool isPlatformSupported() const { return true; } + virtual GstElement* createWebAudioSink() { return nullptr; } + virtual bool configureElement(GstElement*, const OptionSet&) { return false; } + virtual std::optional isHardwareAccelerated(GstElementFactory*) { return std::nullopt; } + virtual std::optional audioVideoDecoderFactoryListType() const { return std::nullopt; } + virtual Vector disallowedWebAudioDecoders() const { return { }; } +}; + +class GStreamerHolePunchQuirk : public GStreamerQuirkBase { + WTF_MAKE_FAST_ALLOCATED; +public: + GStreamerHolePunchQuirk() = default; + virtual ~GStreamerHolePunchQuirk() = default; + + virtual GstElement* createHolePunchVideoSink(bool, const MediaPlayer*) { return nullptr; } + virtual bool setHolePunchVideoRectangle(GstElement*, const IntRect&) { return false; } +}; + +class GStreamerQuirksManager { + friend NeverDestroyed; + WTF_MAKE_FAST_ALLOCATED; + +public: + static GStreamerQuirksManager& singleton(); + + bool isEnabled() const; + + GstElement* createWebAudioSink(); + void configureElement(GstElement*, OptionSet&&); + std::optional isHardwareAccelerated(GstElementFactory*) const; + GstElementFactoryListType audioVideoDecoderFactoryListType() const; + Vector disallowedWebAudioDecoders() const; + + bool supportsVideoHolePunchRendering() const; + GstElement* createHolePunchVideoSink(bool isLegacyPlaybin, const MediaPlayer*); + void setHolePunchVideoRectangle(GstElement*, const IntRect&); + +private: + GStreamerQuirksManager(); + + Vector> m_quirks; + std::unique_ptr m_holePunchQuirk; +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) From 43daf5daf19e12ad86a2ef6f4956268cbb923834 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Wed, 6 Mar 2024 10:03:08 +0000 Subject: [PATCH 02/17] Quirks: Fix build after backporting --- .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 63a8dcdd8f140..934b5427f25fd 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -4040,12 +4040,8 @@ GstElement* MediaPlayerPrivateGStreamer::createHolePunchVideoSink() if (!isHolePunchRenderingEnabled()) return nullptr; - auto player = m_player.get(); - if (!player) - return nullptr; - auto& quirksManager = GStreamerQuirksManager::singleton(); - auto sink = quirksManager.createHolePunchVideoSink(m_isLegacyPlaybin, player.get()); + auto sink = quirksManager.createHolePunchVideoSink(m_isLegacyPlaybin, m_player); // Configure sink before it allocates resources. if (sink) From 6417045013d0f99e35c7ce0e5e971544a2467164 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Wed, 6 Mar 2024 10:03:56 +0000 Subject: [PATCH 03/17] Quirks: Allow legacy platform ifdefs --- .../platform/gstreamer/GStreamerQuirks.cpp | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp index a31ac9aeba915..c459849cb8998 100644 --- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp @@ -56,10 +56,30 @@ GStreamerQuirksManager::GStreamerQuirksManager() return; #endif - const char* quirksList = g_getenv("WEBKIT_GST_QUIRKS"); - GST_DEBUG("Attempting to parse requested quirks: %s", GST_STR_NULL(quirksList)); - if (quirksList) { - StringView quirks { quirksList, static_cast(strlen(quirksList)) }; + const char* quirksListFromEnvironment = g_getenv("WEBKIT_GST_QUIRKS"); + StringBuilder quirksListBuilder; + if (quirksListFromEnvironment) + quirksListBuilder.append(quirksListFromEnvironment); + else { +#if PLATFORM(AMLOGIC) + quirksListBuilder.append("amlogic,"); +#endif +#if PLATFORM(BROADCOM) + quirksListBuilder.append("broadcom,"); +#endif +#if PLATFORM(BCM_NEXUS) + quirksListBuilder.append("bcmnexus,"); +#endif +#if PLATFORM(REALTEK) + quirksListBuilder.append("realtek,"); +#endif +#if PLATFORM(WESTEROS) + quirksListBuilder.append("westeros"); +#endif + } + auto quirks = quirksListBuilder.toString(); + GST_DEBUG("Attempting to parse requested quirks: %s", quirks.ascii().data()); + if (!quirks.isEmpty()) { if (WTF::equalLettersIgnoringASCIICase(quirks, "help"_s)) { WTFLogAlways("Supported quirks for WEBKIT_GST_QUIRKS are: amlogic, broadcom, bcmnexus, realtek, westeros"); return; @@ -78,7 +98,7 @@ GStreamerQuirksManager::GStreamerQuirksManager() else if (WTF::equalLettersIgnoringASCIICase(identifier, "westeros"_s)) quirk = WTF::makeUnique(); else { - GST_WARNING("Unknown quirk requested: %s. Skipping", identifier.toStringWithoutCopying().ascii().data()); + GST_WARNING("Unknown quirk requested: %s. Skipping", identifier.ascii().data()); continue; } @@ -90,24 +110,33 @@ GStreamerQuirksManager::GStreamerQuirksManager() } } - const char* holePunchQuirk = g_getenv("WEBKIT_GST_HOLE_PUNCH_QUIRK"); - GST_DEBUG("Attempting to parse requested hole-punch quirk: %s", GST_STR_NULL(holePunchQuirk)); - if (!holePunchQuirk) + const char* holePunchQuirkFromEnvironment = g_getenv("WEBKIT_GST_HOLE_PUNCH_QUIRK"); + String holePunchQuirk; + if (holePunchQuirkFromEnvironment) + holePunchQuirk = String::fromUTF8(holePunchQuirkFromEnvironment); + else { +#if USE(WESTEROS_SINK) + holePunchQuirk = "westeros"_s; +#elif PLATFORM(BCM_NEXUS) + holePunchQuirk = "bcmnexus"_s; +#endif + } + GST_DEBUG("Attempting to parse requested hole-punch quirk: %s", holePunchQuirk.ascii().data()); + if (holePunchQuirk.isEmpty()) return; - StringView identifier { holePunchQuirk, static_cast(strlen(holePunchQuirk)) }; - if (WTF::equalLettersIgnoringASCIICase(identifier, "help"_s)) { + if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "help"_s)) { WTFLogAlways("Supported quirks for WEBKIT_GST_HOLE_PUNCH_QUIRK are: westeros, bcmnexus"); return; } // TODO: Maybe check this is coherent (somehow) with the quirk(s) selected above. - if (WTF::equalLettersIgnoringASCIICase(identifier, "bcmnexus"_s)) + if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "bcmnexus"_s)) m_holePunchQuirk = WTF::makeUnique(); - else if (WTF::equalLettersIgnoringASCIICase(identifier, "westeros"_s)) + else if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "westeros"_s)) m_holePunchQuirk = WTF::makeUnique(); else - GST_WARNING("HolePunch quirk %s un-supported.", identifier.toStringWithoutCopying().ascii().data()); + GST_WARNING("HolePunch quirk %s un-supported.", holePunchQuirk.ascii().data()); } bool GStreamerQuirksManager::isEnabled() const From e5cace2d85ae053cab190e462b205d7d0514ed6d Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Thu, 7 Mar 2024 09:32:07 +0000 Subject: [PATCH 04/17] Quirks: Fix hole-punch rendering --- .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 934b5427f25fd..a77248303f8e2 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -1656,13 +1656,16 @@ FloatSize MediaPlayerPrivateGStreamer::naturalSize() const if (!hasVideo()) return FloatSize(); - if (!m_videoSize.isEmpty() && !isHolePunchRenderingEnabled()) + if (!m_videoSize.isEmpty()) return m_videoSize; // When using the holepunch we may not be able to get the video frames size, so we can't use // it. But we need to report some non empty naturalSize for the player's GraphicsLayer // to be properly created. - return s_holePunchDefaultFrameSize; + if (isHolePunchRenderingEnabled()) + return s_holePunchDefaultFrameSize; + + return m_videoSize; } void MediaPlayerPrivateGStreamer::configureMediaStreamAudioTracks() From 3c9f4f1a1d21e1f3ef7215d41db3a1b8e4d0145f Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Mon, 11 Mar 2024 14:12:06 +0000 Subject: [PATCH 05/17] Quirks: Fix non-hole punch rendering --- .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index a77248303f8e2..69b6134de8188 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -3139,7 +3139,8 @@ PlatformLayer* MediaPlayerPrivateGStreamer::platformLayer() const #if USE(NICOSIA) void MediaPlayerPrivateGStreamer::swapBuffersIfNeeded() { - pushNextHolePunchBuffer(); + if (isHolePunchRenderingEnabled()) + pushNextHolePunchBuffer(); } #else RefPtr MediaPlayerPrivateGStreamer::proxy() const From ee595791f61023407e1ed0c45514bd8bb3daef2a Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Tue, 12 Mar 2024 10:07:31 +0000 Subject: [PATCH 06/17] Quirks: Add fakevideosink hole-punch quirk --- .../gstreamer/MediaPlayerPrivateGStreamer.cpp | 2 +- .../gstreamer/GStreamerHolePunchQuirkFake.h | 39 +++++++++++++++++++ .../platform/gstreamer/GStreamerQuirks.cpp | 8 ++-- 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkFake.h diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 69b6134de8188..ffe1bdf848c9f 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -4036,7 +4036,7 @@ class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePu bool MediaPlayerPrivateGStreamer::isHolePunchRenderingEnabled() const { auto& quirksManager = GStreamerQuirksManager::singleton(); - return quirksManager.isEnabled() && quirksManager.supportsVideoHolePunchRendering(); + return quirksManager.supportsVideoHolePunchRendering(); } GstElement* MediaPlayerPrivateGStreamer::createHolePunchVideoSink() diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkFake.h b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkFake.h new file mode 100644 index 0000000000000..3790f477151db --- /dev/null +++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkFake.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Igalia S.L + * Copyright (C) 2024 Metrological Group B.V. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * aint with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#if USE(GSTREAMER) + +#include "GStreamerCommon.h" +#include "GStreamerQuirks.h" + +namespace WebCore { + +class GStreamerHolePunchQuirkFake final : public GStreamerHolePunchQuirk { +public: + const char* identifier() final { return "FakeHolePunch"; } + GstElement* createHolePunchVideoSink(bool, const MediaPlayer*) final { return makeGStreamerElement("fakevideosink", nullptr); } + bool setHolePunchVideoRectangle(GstElement*, const IntRect&) final { return true; } +}; + +} // namespace WebCore + +#endif // USE(GSTREAMER) diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp index c459849cb8998..07fe8c2b8373e 100644 --- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp @@ -25,6 +25,7 @@ #include "GStreamerCommon.h" #include "GStreamerHolePunchQuirkBcmNexus.h" +#include "GStreamerHolePunchQuirkFake.h" #include "GStreamerHolePunchQuirkWesteros.h" #include "GStreamerQuirkAmLogic.h" #include "GStreamerQuirkBcmNexus.h" @@ -126,7 +127,7 @@ GStreamerQuirksManager::GStreamerQuirksManager() return; if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "help"_s)) { - WTFLogAlways("Supported quirks for WEBKIT_GST_HOLE_PUNCH_QUIRK are: westeros, bcmnexus"); + WTFLogAlways("Supported quirks for WEBKIT_GST_HOLE_PUNCH_QUIRK are: fake, westeros, bcmnexus"); return; } @@ -135,6 +136,8 @@ GStreamerQuirksManager::GStreamerQuirksManager() m_holePunchQuirk = WTF::makeUnique(); else if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "westeros"_s)) m_holePunchQuirk = WTF::makeUnique(); + else if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "fake"_s)) + m_holePunchQuirk = WTF::makeUnique(); else GST_WARNING("HolePunch quirk %s un-supported.", holePunchQuirk.ascii().data()); } @@ -161,8 +164,7 @@ GstElement* GStreamerQuirksManager::createWebAudioSink() GstElement* GStreamerQuirksManager::createHolePunchVideoSink(bool isLegacyPlaybin, const MediaPlayer* player) { - RELEASE_ASSERT_WITH_MESSAGE(isEnabled() && m_holePunchQuirk, "createHolePunchVideoSink() should be called only if one hole-punch quirk was requested"); - if (!isEnabled() || !m_holePunchQuirk) { + if (!m_holePunchQuirk) { GST_DEBUG("None of the quirks requested a HolePunchSink"); return nullptr; } From 473c324fb4950da6d0c35ed851b8b566e9013f6b Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Tue, 12 Mar 2024 10:29:30 +0000 Subject: [PATCH 07/17] Quirks: set video rectangle even if no platform quirk is requested --- .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index ffe1bdf848c9f..52feb48782efb 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -4015,14 +4015,14 @@ GstElement* MediaPlayerPrivateGStreamer::createVideoSinkGL() static void setRectangleToVideoSink(GstElement* videoSink, const IntRect& rect) { // Here goes the platform-dependant code to set to the videoSink the size - // and position of the video rendering window. Mark them unused as default. + // and position of the video rendering window. if (!videoSink) return; + ASSERT(isHolePunchRenderingEnabled()); auto& quirksManager = GStreamerQuirksManager::singleton(); - if (quirksManager.isEnabled()) - quirksManager.setHolePunchVideoRectangle(videoSink, rect); + quirksManager.setHolePunchVideoRectangle(videoSink, rect); } class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePunchClient { From c18176569ea38fd9dd619b637771d286adbe7d60 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Tue, 12 Mar 2024 10:54:57 +0000 Subject: [PATCH 08/17] Quirks: Fix debug build --- .../platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 52feb48782efb..06b8a9dedbd98 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -4020,7 +4020,6 @@ static void setRectangleToVideoSink(GstElement* videoSink, const IntRect& rect) if (!videoSink) return; - ASSERT(isHolePunchRenderingEnabled()); auto& quirksManager = GStreamerQuirksManager::singleton(); quirksManager.setHolePunchVideoRectangle(videoSink, rect); } @@ -4068,6 +4067,7 @@ void MediaPlayerPrivateGStreamer::pushNextHolePunchBuffer() proxy.pushNextBuffer(WTFMove(layerBuffer)); }; + ASSERT(isHolePunchRenderingEnabled()); #if USE(NICOSIA) auto& proxy = downcast(m_nicosiaLayer->impl()).proxy(); ASSERT(is(proxy)); From 4f2974ac2ae95d19c9e3ca14e9e2992990c89c3f Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Tue, 12 Mar 2024 11:44:02 +0000 Subject: [PATCH 09/17] Quirks: Fix fake hole-punching --- .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 3 +++ Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index 06b8a9dedbd98..bb4cce4f8fa6b 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -192,6 +192,9 @@ MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player) #if USE(TEXTURE_MAPPER_GL) && USE(NICOSIA) m_nicosiaLayer = Nicosia::ContentLayer::create(Nicosia::ContentLayerTextureMapperImpl::createFactory(*this, [&]() -> Ref { + if (isHolePunchRenderingEnabled()) + return adoptRef(*new TextureMapperPlatformLayerProxyGL(true)); + #if USE(TEXTURE_MAPPER_DMABUF) if (webKitDMABufVideoSinkIsEnabled() && webKitDMABufVideoSinkProbePlatform()) return adoptRef(*new TextureMapperPlatformLayerProxyDMABuf); diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp index 07fe8c2b8373e..656db593b9a7a 100644 --- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp +++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp @@ -175,8 +175,7 @@ GstElement* GStreamerQuirksManager::createHolePunchVideoSink(bool isLegacyPlaybi void GStreamerQuirksManager::setHolePunchVideoRectangle(GstElement* videoSink, const IntRect& rect) { - RELEASE_ASSERT_WITH_MESSAGE(isEnabled() && m_holePunchQuirk, "setHolePunchVideoRectangle() should be called only if one hole-punch quirk was requested"); - if (!isEnabled() || !m_holePunchQuirk) { + if (!m_holePunchQuirk) { GST_DEBUG("None of the quirks requested a HolePunchSink"); return; } From 754f3b0dc04ec7c158ea9eae122c504ad7eb6110 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Tue, 12 Mar 2024 12:06:32 +0000 Subject: [PATCH 10/17] Quirks: Fix build --- .../platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index bb4cce4f8fa6b..f34293ff13147 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -193,7 +193,7 @@ MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player) m_nicosiaLayer = Nicosia::ContentLayer::create(Nicosia::ContentLayerTextureMapperImpl::createFactory(*this, [&]() -> Ref { if (isHolePunchRenderingEnabled()) - return adoptRef(*new TextureMapperPlatformLayerProxyGL(true)); + return adoptRef(*new TextureMapperPlatformLayerProxyGL); #if USE(TEXTURE_MAPPER_DMABUF) if (webKitDMABufVideoSinkIsEnabled() && webKitDMABufVideoSinkProbePlatform()) From 8dbe2a32667c26ea9d2e5d151b20068dab177aca Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Wed, 13 Mar 2024 09:27:03 +0000 Subject: [PATCH 11/17] Quirks: Add plumbing for hole-punch testing --- .../wpe/media/video-punch-hole-expected.txt | 21 +++++++++++++++ .../platform/wpe/media/video-punch-hole.html | 27 +++++++++++++++++++ Source/WebCore/html/HTMLVideoElement.h | 9 +++++++ .../WebCore/platform/graphics/MediaPlayer.cpp | 5 ++++ .../WebCore/platform/graphics/MediaPlayer.h | 3 +++ .../gstreamer/MediaPlayerPrivateGStreamer.cpp | 27 ++++++++++++++----- .../gstreamer/MediaPlayerPrivateGStreamer.h | 3 +++ .../platform/gstreamer/GStreamerQuirks.cpp | 22 ++++++++++++--- .../platform/gstreamer/GStreamerQuirks.h | 13 +++++++-- Source/WebCore/testing/Internals.cpp | 9 +++++++ Source/WebCore/testing/Internals.h | 2 ++ Source/WebCore/testing/Internals.idl | 2 ++ 12 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 LayoutTests/platform/wpe/media/video-punch-hole-expected.txt create mode 100644 LayoutTests/platform/wpe/media/video-punch-hole.html diff --git a/LayoutTests/platform/wpe/media/video-punch-hole-expected.txt b/LayoutTests/platform/wpe/media/video-punch-hole-expected.txt new file mode 100644 index 0000000000000..6713965d3ae78 --- /dev/null +++ b/LayoutTests/platform/wpe/media/video-punch-hole-expected.txt @@ -0,0 +1,21 @@ + +(GraphicsLayer + (anchor 0.00 0.00) + (bounds 1288.00 807.00) + (children 1 + (GraphicsLayer + (bounds 1288.00 807.00) + (contentsOpaque 1) + (children 1 + (GraphicsLayer + (position 8.00 8.00) + (bounds 1280.00 720.00) + ) + ) + ) + ) +) +RUN(internals.enableGStreamerHolePunching(video)) +RUN(video.src = findMediaFile('video', '../../../media/content/test')) +RUN(video.play()) + diff --git a/LayoutTests/platform/wpe/media/video-punch-hole.html b/LayoutTests/platform/wpe/media/video-punch-hole.html new file mode 100644 index 0000000000000..532e9f35d7974 --- /dev/null +++ b/LayoutTests/platform/wpe/media/video-punch-hole.html @@ -0,0 +1,27 @@ + + + + + + + + + +

+
+
diff --git a/Source/WebCore/html/HTMLVideoElement.h b/Source/WebCore/html/HTMLVideoElement.h
index 08cb2b00d6499..d76e1ee010f03 100644
--- a/Source/WebCore/html/HTMLVideoElement.h
+++ b/Source/WebCore/html/HTMLVideoElement.h
@@ -119,6 +119,11 @@ class HTMLVideoElement final : public HTMLMediaElement, public Supplementable&&);
     void cancelVideoFrameCallback(unsigned);
 
+#if USE(GSTREAMER)
+    void enableGStreamerHolePunching() { m_enableGStreamerHolePunching = true; }
+    bool isGStreamerHolePunchingEnabled() const final { return m_enableGStreamerHolePunching; }
+#endif
+
 private:
     HTMLVideoElement(const QualifiedName&, Document&, bool createdByParser);
 
@@ -175,6 +180,10 @@ class HTMLVideoElement final : public HTMLMediaElement, public Supplementable> m_videoFrameRequests;
     Vector> m_servicedVideoFrameRequests;
     unsigned m_nextVideoFrameRequestIndex { 0 };
+
+#if USE(GSTREAMER)
+    bool m_enableGStreamerHolePunching { false };
+#endif
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/MediaPlayer.cpp b/Source/WebCore/platform/graphics/MediaPlayer.cpp
index 76832faee329e..a6445f5ce22b2 100644
--- a/Source/WebCore/platform/graphics/MediaPlayer.cpp
+++ b/Source/WebCore/platform/graphics/MediaPlayer.cpp
@@ -1625,6 +1625,11 @@ void MediaPlayer::simulateAudioInterruption()
 
     m_private->simulateAudioInterruption();
 }
+
+bool MediaPlayer::isGStreamerHolePunchingEnabled()
+{
+    return client().isGStreamerHolePunchingEnabled();
+}
 #endif
 
 void MediaPlayer::beginSimulatedHDCPError()
diff --git a/Source/WebCore/platform/graphics/MediaPlayer.h b/Source/WebCore/platform/graphics/MediaPlayer.h
index a103b671c33c2..9ff5a4597feea 100644
--- a/Source/WebCore/platform/graphics/MediaPlayer.h
+++ b/Source/WebCore/platform/graphics/MediaPlayer.h
@@ -295,6 +295,8 @@ class MediaPlayerClient {
 
     virtual bool mediaPlayerShouldDisableHDR() const { return false; }
 
+    virtual bool isGStreamerHolePunchingEnabled() const { return false; }
+
 #if !RELEASE_LOG_DISABLED
     virtual const void* mediaPlayerLogIdentifier() { return nullptr; }
     virtual const Logger& mediaPlayerLogger() = 0;
@@ -620,6 +622,7 @@ class WEBCORE_EXPORT MediaPlayer : public MediaPlayerEnums, public ThreadSafeRef
 
 #if USE(GSTREAMER)
     void simulateAudioInterruption();
+    bool isGStreamerHolePunchingEnabled();
 #endif
 
     void beginSimulatedHDCPError();
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index f34293ff13147..959200630aeee 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -189,6 +189,11 @@ MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
 #endif
     m_isPlayerShuttingDown.store(false);
 
+    if (player->isGStreamerHolePunchingEnabled()) {
+        m_quirksManagerForTesting = GStreamerQuirksManager::createForTesting();
+        m_quirksManagerForTesting->setHolePunchEnabledForTesting(true);
+    }
+
 #if USE(TEXTURE_MAPPER_GL) && USE(NICOSIA)
     m_nicosiaLayer = Nicosia::ContentLayer::create(Nicosia::ContentLayerTextureMapperImpl::createFactory(*this,
         [&]() -> Ref {
@@ -4015,7 +4020,7 @@ GstElement* MediaPlayerPrivateGStreamer::createVideoSinkGL()
 }
 #endif // USE(GSTREAMER_GL)
 
-static void setRectangleToVideoSink(GstElement* videoSink, const IntRect& rect)
+static void setRectangleToVideoSink(GStreamerQuirksManager* quirksManagerForTesting, GstElement* videoSink, const IntRect& rect)
 {
     // Here goes the platform-dependant code to set to the videoSink the size
     // and position of the video rendering window.
@@ -4023,6 +4028,11 @@ static void setRectangleToVideoSink(GstElement* videoSink, const IntRect& rect)
     if (!videoSink)
         return;
 
+    if (quirksManagerForTesting) {
+        quirksManagerForTesting->setHolePunchVideoRectangle(videoSink, rect);
+        return;
+    }
+
     auto& quirksManager = GStreamerQuirksManager::singleton();
     quirksManager.setHolePunchVideoRectangle(videoSink, rect);
 }
@@ -4037,6 +4047,8 @@ class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePu
 
 bool MediaPlayerPrivateGStreamer::isHolePunchRenderingEnabled() const
 {
+    if (m_quirksManagerForTesting)
+        return m_quirksManagerForTesting->supportsVideoHolePunchRendering();
     auto& quirksManager = GStreamerQuirksManager::singleton();
     return quirksManager.supportsVideoHolePunchRendering();
 }
@@ -4046,8 +4058,11 @@ GstElement* MediaPlayerPrivateGStreamer::createHolePunchVideoSink()
     if (!isHolePunchRenderingEnabled())
         return nullptr;
 
-    auto& quirksManager = GStreamerQuirksManager::singleton();
-    auto sink = quirksManager.createHolePunchVideoSink(m_isLegacyPlaybin, m_player);
+    GstElement* sink = nullptr;
+    if (m_quirksManagerForTesting)
+        sink = m_quirksManagerForTesting->createHolePunchVideoSink(m_isLegacyPlaybin, m_player);
+    else
+        sink = GStreamerQuirksManager::singleton().createHolePunchVideoSink(m_isLegacyPlaybin, m_player);
 
     // Configure sink before it allocates resources.
     if (sink)
@@ -4085,7 +4100,7 @@ void MediaPlayerPrivateGStreamer::setVideoRectangle(const IntRect& rect)
     Locker locker { m_holePunchLock };
 
     if (m_visible && !m_suspended)
-        setRectangleToVideoSink(m_videoSink.get(), rect);
+        setRectangleToVideoSink(m_quirksManagerForTesting.get(), m_videoSink.get(), rect);
 }
 
 bool MediaPlayerPrivateGStreamer::shouldIgnoreIntrinsicSize()
@@ -4463,7 +4478,7 @@ void MediaPlayerPrivateGStreamer::setPageIsVisible(bool visible)
         m_visible = visible;
 
         if (!m_visible)
-            setRectangleToVideoSink(m_videoSink.get(), IntRect());
+            setRectangleToVideoSink(m_quirksManagerForTesting.get(), m_videoSink.get(), IntRect());
     }
 }
 
@@ -4475,7 +4490,7 @@ void MediaPlayerPrivateGStreamer::setPageIsSuspended(bool suspended)
         m_suspended = suspended;
 
         if (m_suspended)
-            setRectangleToVideoSink(m_videoSink.get(), IntRect());
+            setRectangleToVideoSink(m_quirksManagerForTesting.get(), m_videoSink.get(), IntRect());
     }
 }
 
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
index 0489f30d7afe9..a8066d9751078 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
@@ -29,6 +29,7 @@
 #include "AbortableTaskQueue.h"
 #include "GStreamerCommon.h"
 #include "GStreamerEMEUtilities.h"
+#include "GStreamerQuirks.h"
 #include "ImageOrientation.h"
 #include "Logging.h"
 #include "MainThreadNotifier.h"
@@ -673,6 +674,8 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface
     // Specific to MediaStream playback.
     MediaTime m_startTime;
     MediaTime m_pausedTime;
+
+    RefPtr m_quirksManagerForTesting;
 };
 
 }
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
index 656db593b9a7a..1951d094b5199 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
@@ -43,13 +43,17 @@ GST_DEBUG_CATEGORY_STATIC(webkit_quirks_debug);
 
 GStreamerQuirksManager& GStreamerQuirksManager::singleton()
 {
-    static NeverDestroyed sharedInstance;
+    static NeverDestroyed sharedInstance(false, true);
     return sharedInstance;
 }
 
-GStreamerQuirksManager::GStreamerQuirksManager()
+GStreamerQuirksManager::GStreamerQuirksManager(bool isForTesting, bool loadQuirksFromEnvironment)
+    : m_isForTesting(isForTesting)
 {
-    GST_DEBUG_CATEGORY_INIT(webkit_quirks_debug, "webkitquirks", 0, "WebKit Quirks");
+    static std::once_flag debugRegisteredFlag;
+    std::call_once(debugRegisteredFlag, [] {
+        GST_DEBUG_CATEGORY_INIT(webkit_quirks_debug, "webkitquirks", 0, "WebKit Quirks");
+    });
 
     // For the time being keep this disabled on non-WPE platforms. GTK on desktop shouldn't require
     // quirks, for instance.
@@ -57,6 +61,10 @@ GStreamerQuirksManager::GStreamerQuirksManager()
     return;
 #endif
 
+    GST_DEBUG("Quirk manager created%s", m_isForTesting ? " for testing." : ".");
+    if (!loadQuirksFromEnvironment)
+        return;
+
     const char* quirksListFromEnvironment = g_getenv("WEBKIT_GST_QUIRKS");
     StringBuilder quirksListBuilder;
     if (quirksListFromEnvironment)
@@ -235,6 +243,14 @@ Vector GStreamerQuirksManager::disallowedWebAudioDecoders() const
     return result;
 }
 
+void GStreamerQuirksManager::setHolePunchEnabledForTesting(bool enabled)
+{
+    if (enabled)
+        m_holePunchQuirk = WTF::makeUnique();
+    else
+        m_holePunchQuirk = nullptr;
+}
+
 #undef GST_CAT_DEFAULT
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
index e56bef9251f71..5dd0901717311 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
@@ -25,6 +25,7 @@
 #include "GRefPtrGStreamer.h"
 #include "MediaPlayer.h"
 #include 
+#include 
 #include 
 
 namespace WebCore {
@@ -70,13 +71,18 @@ class GStreamerHolePunchQuirk : public GStreamerQuirkBase {
     virtual bool setHolePunchVideoRectangle(GstElement*, const IntRect&) { return false; }
 };
 
-class GStreamerQuirksManager {
+class GStreamerQuirksManager : public RefCounted {
     friend NeverDestroyed;
     WTF_MAKE_FAST_ALLOCATED;
 
 public:
     static GStreamerQuirksManager& singleton();
 
+    static RefPtr createForTesting()
+    {
+        return adoptRef(*new GStreamerQuirksManager(true, false));
+    }
+
     bool isEnabled() const;
 
     GstElement* createWebAudioSink();
@@ -89,11 +95,14 @@ class GStreamerQuirksManager {
     GstElement* createHolePunchVideoSink(bool isLegacyPlaybin, const MediaPlayer*);
     void setHolePunchVideoRectangle(GstElement*, const IntRect&);
 
+    void setHolePunchEnabledForTesting(bool);
+
 private:
-    GStreamerQuirksManager();
+    GStreamerQuirksManager(bool, bool);
 
     Vector> m_quirks;
     std::unique_ptr m_holePunchQuirk;
+    bool m_isForTesting { false };
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/testing/Internals.cpp b/Source/WebCore/testing/Internals.cpp
index cbeb123c32e89..cad9428421394 100644
--- a/Source/WebCore/testing/Internals.cpp
+++ b/Source/WebCore/testing/Internals.cpp
@@ -4116,6 +4116,15 @@ ExceptionOr Internals::setOverridePreferredDynamicRangeMode(HTMLMediaEleme
     return { };
 }
 
+void Internals::enableGStreamerHolePunching(HTMLVideoElement& element)
+{
+#if USE(GSTREAMER)
+    element.enableGStreamerHolePunching();
+#else
+    UNUSED_PARAM(element);
+#endif
+}
+
 #endif
 
 bool Internals::isSelectPopupVisible(HTMLSelectElement& element)
diff --git a/Source/WebCore/testing/Internals.h b/Source/WebCore/testing/Internals.h
index 8b6f9c21e35c5..e702cd4f2c34a 100644
--- a/Source/WebCore/testing/Internals.h
+++ b/Source/WebCore/testing/Internals.h
@@ -705,6 +705,8 @@ class Internals final : public RefCounted, private ContextDestruction
     double elementEffectivePlaybackRate(const HTMLMediaElement&);
 
     ExceptionOr setOverridePreferredDynamicRangeMode(HTMLMediaElement&, const String&);
+
+    void enableGStreamerHolePunching(HTMLVideoElement&);
 #endif
 
     ExceptionOr setIsPlayingToBluetoothOverride(std::optional);
diff --git a/Source/WebCore/testing/Internals.idl b/Source/WebCore/testing/Internals.idl
index a3684eaeb36ed..82a8e99d7bc37 100644
--- a/Source/WebCore/testing/Internals.idl
+++ b/Source/WebCore/testing/Internals.idl
@@ -776,6 +776,8 @@ typedef (FetchRequest or FetchResponse) FetchObject;
     [Conditional=VIDEO] undefined setOverridePreferredDynamicRangeMode(HTMLMediaElement media, DOMString mode);
     [Conditional=VIDEO] double elementEffectivePlaybackRate(HTMLMediaElement media);
 
+    [Conditional=VIDEO] undefined enableGStreamerHolePunching(HTMLVideoElement element);
+
     undefined setIsPlayingToBluetoothOverride(optional boolean? isPlaying = null);
 
     [Conditional=LEGACY_ENCRYPTED_MEDIA] undefined initializeMockCDM();

From 0141bf9d644b1a47fa5f0da0b2b014ba024586c5 Mon Sep 17 00:00:00 2001
From: Philippe Normand 
Date: Wed, 13 Mar 2024 13:03:11 +0000
Subject: [PATCH 12/17] Quirks: Add quirk for sink clock sync'ing

---
 .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp    | 8 ++++----
 .../platform/gstreamer/GStreamerHolePunchQuirkWesteros.h  | 1 +
 Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp     | 8 ++++++++
 Source/WebCore/platform/gstreamer/GStreamerQuirks.h       | 2 ++
 4 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index 959200630aeee..cb6d93fdc3f5a 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -1038,12 +1038,12 @@ static void setSyncOnClock(GstElement* element, bool sync)
 
 void MediaPlayerPrivateGStreamer::syncOnClock(bool sync)
 {
-#if !USE(WESTEROS_SINK)
+    auto& quirksManager = GStreamerQuirksManager::singleton();
+    if (quirksManager.supportsVideoHolePunchRendering() && !quirksManager.sinksRequireClockSynchronization())
+        return;
+
     setSyncOnClock(videoSink(), sync);
     setSyncOnClock(audioSink(), sync);
-#else
-    UNUSED_PARAM(sync);
-#endif
 }
 
 template 
diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h
index 5d2d2c434261c..11f0ed9731a93 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.h
@@ -32,6 +32,7 @@ class GStreamerHolePunchQuirkWesteros final : public GStreamerHolePunchQuirk {
 
     GstElement* createHolePunchVideoSink(bool, const MediaPlayer*) final;
     bool setHolePunchVideoRectangle(GstElement*, const IntRect&) final;
+    bool requiresClockSynchronization() const final { return false; }
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
index 1951d094b5199..18eacbe3fcfe9 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
@@ -192,6 +192,14 @@ void GStreamerQuirksManager::setHolePunchVideoRectangle(GstElement* videoSink, c
         GST_WARNING("Hole punch video rectangle configuration failed.");
 }
 
+bool GStreamerQuirksManager::sinksRequireClockSynchronization() const
+{
+    if (!m_holePunchQuirk)
+        return true;
+
+    return m_holePunchQuirk->requiresClockSynchronization();
+}
+
 void GStreamerQuirksManager::configureElement(GstElement* element, OptionSet&& characteristics)
 {
     GST_DEBUG("Configuring element %" GST_PTR_FORMAT, element);
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
index 5dd0901717311..190d57c6a00c6 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
@@ -69,6 +69,7 @@ class GStreamerHolePunchQuirk : public GStreamerQuirkBase {
 
     virtual GstElement* createHolePunchVideoSink(bool, const MediaPlayer*) { return nullptr; }
     virtual bool setHolePunchVideoRectangle(GstElement*, const IntRect&) { return false; }
+    virtual bool requiresClockSynchronization() const { return true; }
 };
 
 class GStreamerQuirksManager : public RefCounted {
@@ -94,6 +95,7 @@ class GStreamerQuirksManager : public RefCounted {
     bool supportsVideoHolePunchRendering() const;
     GstElement* createHolePunchVideoSink(bool isLegacyPlaybin, const MediaPlayer*);
     void setHolePunchVideoRectangle(GstElement*, const IntRect&);
+    bool sinksRequireClockSynchronization() const;
 
     void setHolePunchEnabledForTesting(bool);
 

From dd24e194aeba860975e85d68cc75f83e0d9aa2ec Mon Sep 17 00:00:00 2001
From: Philippe Normand 
Date: Wed, 13 Mar 2024 13:29:23 +0000
Subject: [PATCH 13/17] Quirks: Review feedback changes

---
 .../graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp     | 7 ++++---
 .../WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp   | 2 +-
 .../WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp   | 2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index cb6d93fdc3f5a..4b96bc9fa4d43 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -1375,7 +1375,9 @@ GstClockTime MediaPlayerPrivateGStreamer::gstreamerPositionFromSinks() const
         gint64 videoPosition = GST_CLOCK_TIME_NONE;
         gst_query_parse_position(query.get(), 0, &videoPosition);
         GST_TRACE_OBJECT(pipeline(), "Video position %" GST_TIME_FORMAT, GST_TIME_ARGS(videoPosition));
-        if (GST_CLOCK_TIME_IS_VALID(videoPosition) && (!GST_CLOCK_TIME_IS_VALID(gstreamerPosition) || (m_playbackRate >= 0 && videoPosition > gstreamerPosition) || (m_playbackRate < 0 && videoPosition < gstreamerPosition)))
+        if (GST_CLOCK_TIME_IS_VALID(videoPosition) && (!GST_CLOCK_TIME_IS_VALID(gstreamerPosition)
+            || (m_playbackRate >= 0 && videoPosition > gstreamerPosition)
+            || (m_playbackRate < 0 && videoPosition < gstreamerPosition)))
             gstreamerPosition = videoPosition;
     }
     return static_cast(gstreamerPosition);
@@ -3011,8 +3013,7 @@ void MediaPlayerPrivateGStreamer::createGSTPlayBin(const URL& url)
     if (videoSinkPad)
         g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(+[](GstPad* videoSinkPad, GParamSpec*, MediaPlayerPrivateGStreamer* player) {
             player->videoSinkCapsChanged(videoSinkPad);
-        }),
-            this);
+        }), this);
 }
 
 void MediaPlayerPrivateGStreamer::configureVideoDecoder(GstElement* decoder)
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
index 28c39d5c711c1..ad4aa19bc2c05 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
@@ -43,7 +43,7 @@ GstElement* GStreamerQuirkAmLogic::createWebAudioSink()
     // On Amlogic we need to set direct-mode=false prop before changing state to READY
     // but this is not possible with autoaudiosink.
     auto sink = makeGStreamerElement("amlhalasink", nullptr);
-    ASSERT_WITH_MESSAGE(sink, "amlhalasink should be available in the system but it is not");
+    RELEASE_ASSERT_WITH_MESSAGE(sink, "amlhalasink should be available in the system but it is not");
     g_object_set(sink, "direct-mode", FALSE, nullptr);
     return sink;
 }
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
index 9da23740b936f..c5c0c75160fd0 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
@@ -50,7 +50,7 @@ GStreamerQuirkRealtek::GStreamerQuirkRealtek()
 GstElement* GStreamerQuirkRealtek::createWebAudioSink()
 {
     auto sink = makeGStreamerElement("rtkaudiosink", nullptr);
-    ASSERT_WITH_MESSAGE(sink, "rtkaudiosink should be available in the system but it is not");
+    RELEASE_ASSERT_WITH_MESSAGE(sink, "rtkaudiosink should be available in the system but it is not");
     g_object_set(sink, "media-tunnel", FALSE, "audio-service", TRUE, nullptr);
     return sink;
 }

From 971037d2db3fcd6c58cee4d7567fca23442dcf7e Mon Sep 17 00:00:00 2001
From: Philippe Normand 
Date: Thu, 21 Mar 2024 10:07:40 +0000
Subject: [PATCH 14/17] Quirks: Address review feedback

Backport of https://commits.webkit.org/276450@main keeping old ifdefs
compatibility, so the GStreamerQuirksManager::getAdditionalPlaybinFlags()
implementation diverges from upstream version.
---
 .../gstreamer/MediaPlayerPrivateGStreamer.cpp | 25 ++++++-------------
 .../gstreamer/MediaPlayerPrivateGStreamer.h   |  4 +--
 .../GStreamerHolePunchQuirkWesteros.cpp       | 10 +++++++-
 .../gstreamer/GStreamerQuirkAmLogic.cpp       | 13 +++++++---
 .../gstreamer/GStreamerQuirkBroadcom.h        |  2 ++
 .../platform/gstreamer/GStreamerQuirks.cpp    | 24 ++++++++++++++++++
 .../platform/gstreamer/GStreamerQuirks.h      |  5 +++-
 7 files changed, 59 insertions(+), 24 deletions(-)

diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index 4b96bc9fa4d43..847492b8911dc 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -2894,19 +2894,11 @@ void MediaPlayerPrivateGStreamer::setPlaybackFlags(bool isMediaStream)
     if (isMediaStream)
         flags = flags & ~getGstPlayFlag("buffering");
 
-#if !USE(GSTREAMER_TEXT_SINK)
-    hasText = 0x0;
-#endif
-
-#if USE(GSTREAMER_NATIVE_VIDEO)
-    hasSoftwareColorBalance = 0x0;
-#else
-    hasNativeVideo = 0x0;
-#endif
-
-#if !USE(GSTREAMER_NATIVE_AUDIO)
-    hasNativeAudio = 0x0;
-#endif
+    unsigned additionalFlags = GStreamerQuirksManager::singleton().getAdditionalPlaybinFlags();
+    hasText &= additionalFlags;
+    hasSoftwareColorBalance &= additionalFlags;
+    hasNativeVideo &= additionalFlags;
+    hasNativeAudio &= additionalFlags;
 
     GST_INFO_OBJECT(pipeline(), "text %s, audio %s (native %s), video %s (native %s, software color balance %s)", boolForPrinting(hasText),
         boolForPrinting(hasAudio), boolForPrinting(hasNativeAudio), boolForPrinting(hasVideo), boolForPrinting(hasNativeVideo),
@@ -4145,10 +4137,9 @@ GstElement* MediaPlayerPrivateGStreamer::createVideoSink()
 
     if (isHolePunchRenderingEnabled()) {
         m_videoSink = createHolePunchVideoSink();
-        if (m_videoSink) {
-            pushNextHolePunchBuffer();
-            return m_videoSink.get();
-        }
+        // Do not check the m_videoSink value. The nullptr case will trigger auto-plugging in playbin.
+        pushNextHolePunchBuffer();
+        return m_videoSink.get();
     }
 
 #if USE(TEXTURE_MAPPER_DMABUF)
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
index a8066d9751078..c741675edef59 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
@@ -197,6 +197,8 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface
     void acceleratedRenderingStateChanged() final;
     bool performTaskAtMediaTime(Function&&, const MediaTime&) override;
 
+    GstElement* pipeline() const { return m_pipeline.get(); }
+
 #if USE(TEXTURE_MAPPER_GL)
     PlatformLayer* platformLayer() const override;
 #if PLATFORM(WIN_CAIRO)
@@ -342,8 +344,6 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface
 
     void setStreamVolumeElement(GstStreamVolume*);
 
-    GstElement* pipeline() const { return m_pipeline.get(); }
-
     void repaint();
     void cancelRepaint(bool destroying = false);
 
diff --git a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp
index 11ac3a07d9e97..0fd94427142e3 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp
@@ -20,6 +20,7 @@
 
 #include "config.h"
 #include "GStreamerHolePunchQuirkWesteros.h"
+#include "MediaPlayerPrivateGStreamer.h"
 
 #if USE(GSTREAMER)
 
@@ -37,8 +38,15 @@ GstElement* GStreamerHolePunchQuirkWesteros::createHolePunchVideoSink(bool isLeg
     // Westeros using holepunch.
     GstElement* videoSink = makeGStreamerElement("westerossink", "WesterosVideoSink");
     g_object_set(videoSink, "zorder", 0.0f, nullptr);
-    if (isPIPRequested)
+    if (isPIPRequested) {
         g_object_set(videoSink, "res-usage", 0u, nullptr);
+        // Set context for pipelines that use ERM in decoder elements.
+        auto context = adoptGRef(gst_context_new("erm", FALSE));
+        auto contextStructure = gst_context_writable_structure(context.get());
+        gst_structure_set(contextStructure, "res-usage", G_TYPE_UINT, 0x0u, nullptr);
+        auto playerPrivate = reinterpret_cast(player->playerPrivate());
+        gst_element_set_context(playerPrivate->pipeline(), context.get());
+    }
     return videoSink;
 }
 
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
index ad4aa19bc2c05..008673a036314 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
@@ -50,10 +50,17 @@ GstElement* GStreamerQuirkAmLogic::createWebAudioSink()
 
 bool GStreamerQuirkAmLogic::configureElement(GstElement* element, const OptionSet& characteristics)
 {
-    GST_INFO("Set property disable-xrun to TRUE");
-    g_object_set(element, "disable-xrun", TRUE, nullptr);
-    if (characteristics.contains(ElementRuntimeCharacteristics::HasVideo))
+    if (gstObjectHasProperty(element, "disable-xrun")) {
+        GST_INFO("Set property disable-xrun to TRUE");
+        g_object_set(element, "disable-xrun", TRUE, nullptr);
+    } else
+        return false;
+
+    if (characteristics.contains(ElementRuntimeCharacteristics::HasVideo) && gstObjectHasProperty(element, "wait-video")) {
+        GST_INFO("Set property wait-video to TRUE");
         g_object_set(element, "wait-video", TRUE, nullptr);
+    } else
+        return false;
 
     return true;
 }
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
index 32c3329471794..bff50784b843a 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
@@ -22,6 +22,7 @@
 
 #if USE(GSTREAMER)
 
+#include "GStreamerCommon.h"
 #include "GStreamerQuirks.h"
 
 namespace WebCore {
@@ -35,6 +36,7 @@ class GStreamerQuirkBroadcom final : public GStreamerQuirk {
     std::optional isHardwareAccelerated(GstElementFactory*) final;
     std::optional audioVideoDecoderFactoryListType() const final { return GST_ELEMENT_FACTORY_TYPE_PARSER; }
     Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; }
+    unsigned getAdditionalPlaybinFlags() const final { return getGstPlayFlag("text") | getGstPlayFlag("native-audio"); }
 
 private:
     Vector m_disallowedWebAudioDecoders;
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
index 18eacbe3fcfe9..1bb59a7ee7b77 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
@@ -259,6 +259,30 @@ void GStreamerQuirksManager::setHolePunchEnabledForTesting(bool enabled)
         m_holePunchQuirk = nullptr;
 }
 
+unsigned GStreamerQuirksManager::getAdditionalPlaybinFlags() const
+{
+    unsigned flags = 0;
+#if USE(GSTREAMER_NATIVE_VIDEO)
+    flags |= getGstPlayFlag("native-video");
+#else
+    flags |= getGstPlayFlag("soft-colorbalance");
+#endif
+#if USE(GSTREAMER_NATIVE_AUDIO)
+    flags |= getGstPlayFlag("native-audio");
+#endif
+#if USE(GSTREAMER_TEXT_SINK)
+    flags |= getGstPlayFlag("text");
+#endif
+    for (const auto& quirk : m_quirks) {
+        if (auto additionalFlags = quirk->getAdditionalPlaybinFlags()) {
+            GST_DEBUG("Quirk %s requests these playbin flags: %u", quirk->identifier(), additionalFlags);
+            flags |= additionalFlags;
+        }
+    }
+
+    return flags;
+}
+
 #undef GST_CAT_DEFAULT
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
index 190d57c6a00c6..527b9c0677840 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
@@ -22,7 +22,7 @@
 
 #if USE(GSTREAMER)
 
-#include "GRefPtrGStreamer.h"
+#include "GStreamerCommon.h"
 #include "MediaPlayer.h"
 #include 
 #include 
@@ -59,6 +59,7 @@ class GStreamerQuirk : public GStreamerQuirkBase {
     virtual std::optional isHardwareAccelerated(GstElementFactory*) { return std::nullopt; }
     virtual std::optional audioVideoDecoderFactoryListType() const { return std::nullopt; }
     virtual Vector disallowedWebAudioDecoders() const { return { }; }
+    virtual unsigned getAdditionalPlaybinFlags() const { return 0; }
 };
 
 class GStreamerHolePunchQuirk : public GStreamerQuirkBase {
@@ -99,6 +100,8 @@ class GStreamerQuirksManager : public RefCounted {
 
     void setHolePunchEnabledForTesting(bool);
 
+    unsigned getAdditionalPlaybinFlags() const;
+
 private:
     GStreamerQuirksManager(bool, bool);
 

From 6bac194ec0def69f90dcb94868bf54fba34d9313 Mon Sep 17 00:00:00 2001
From: Philippe Normand 
Date: Thu, 21 Mar 2024 13:31:31 +0000
Subject: [PATCH 15/17] Quirks: Westeros: Remove uridecodebin3 configuration

Downstream GStreamer is already patched for playbin3 decoder/sink handling.
---
 .../platform/gstreamer/GStreamerQuirkWesteros.cpp        | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
index 5a6c232ea45af..6c577dedc1f3e 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
@@ -53,15 +53,6 @@ GStreamerQuirkWesteros::GStreamerQuirkWesteros()
 
 bool GStreamerQuirkWesteros::configureElement(GstElement* element, const OptionSet& characteristics)
 {
-    if (g_str_has_prefix(GST_ELEMENT_NAME(element), "uridecodebin3")) {
-        GRefPtr defaultCaps;
-        g_object_get(element, "caps", &defaultCaps.outPtr(), nullptr);
-        defaultCaps = adoptGRef(gst_caps_merge(gst_caps_ref(m_sinkCaps.get()), defaultCaps.leakRef()));
-        GST_INFO("Setting stop caps to %" GST_PTR_FORMAT, defaultCaps.get());
-        g_object_set(element, "caps", defaultCaps.get(), nullptr);
-        return true;
-    }
-
     if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream))
         return false;
 

From a4ef11bbeed4d6d9ab30273eeb63e42ff0ec89d2 Mon Sep 17 00:00:00 2001
From: Philippe Normand 
Date: Tue, 26 Mar 2024 08:46:03 +0000
Subject: [PATCH 16/17] [GStreamer][Quirks] Apply configureElement to every
 requested quirk https://bugs.webkit.org/show_bug.cgi?id=271697

Reviewed by NOBODY (OOPS!).

On some platforms it is desirable to apply that quirk unconditionally, because multiple
platform-specific quirks are requested.

* Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp:
(WebCore::GStreamerQuirkAmLogic::configureElement):
* Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h:
* Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp:
(WebCore::GStreamerQuirkBroadcom::configureElement):
* Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h:
* Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp:
(WebCore::GStreamerQuirkRealtek::configureElement):
* Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h:
* Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp:
(WebCore::GStreamerQuirkWesteros::configureElement):
* Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h:
* Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp:
(WebCore::GStreamerQuirksManager::configureElement):
* Source/WebCore/platform/gstreamer/GStreamerQuirks.h:
(WebCore::GStreamerQuirk::configureElement):
---
 .../platform/gstreamer/GStreamerQuirkAmLogic.cpp       | 10 +++-------
 .../WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h |  3 +--
 .../platform/gstreamer/GStreamerQuirkBroadcom.cpp      |  6 ++----
 .../platform/gstreamer/GStreamerQuirkBroadcom.h        |  2 +-
 .../platform/gstreamer/GStreamerQuirkRealtek.cpp       |  6 ++----
 .../WebCore/platform/gstreamer/GStreamerQuirkRealtek.h |  2 +-
 .../platform/gstreamer/GStreamerQuirkWesteros.cpp      |  5 ++---
 .../platform/gstreamer/GStreamerQuirkWesteros.h        |  2 +-
 Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp  |  6 ++----
 Source/WebCore/platform/gstreamer/GStreamerQuirks.h    |  2 +-
 10 files changed, 16 insertions(+), 28 deletions(-)

diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
index 008673a036314..610f026e434d1 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
@@ -48,21 +48,17 @@ GstElement* GStreamerQuirkAmLogic::createWebAudioSink()
     return sink;
 }
 
-bool GStreamerQuirkAmLogic::configureElement(GstElement* element, const OptionSet& characteristics)
+void GStreamerQuirkAmLogic::configureElement(GstElement* element, const OptionSet& characteristics)
 {
     if (gstObjectHasProperty(element, "disable-xrun")) {
         GST_INFO("Set property disable-xrun to TRUE");
         g_object_set(element, "disable-xrun", TRUE, nullptr);
-    } else
-        return false;
+    }
 
     if (characteristics.contains(ElementRuntimeCharacteristics::HasVideo) && gstObjectHasProperty(element, "wait-video")) {
         GST_INFO("Set property wait-video to TRUE");
         g_object_set(element, "wait-video", TRUE, nullptr);
-    } else
-        return false;
-
-    return true;
+    }
 }
 
 #undef GST_CAT_DEFAULT
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h
index 53980761601a4..ee3662eb20586 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.h
@@ -32,8 +32,7 @@ class GStreamerQuirkAmLogic final : public GStreamerQuirk {
     const char* identifier() final { return "AmLogic"; }
 
     GstElement* createWebAudioSink() final;
-    bool configureElement(GstElement*, const OptionSet&) final;
-
+    void configureElement(GstElement*, const OptionSet&) final;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp
index 64d7cdec346a7..01738d0e220e1 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp
@@ -37,7 +37,7 @@ GStreamerQuirkBroadcom::GStreamerQuirkBroadcom()
     m_disallowedWebAudioDecoders = { "brcmaudfilter"_s };
 }
 
-bool GStreamerQuirkBroadcom::configureElement(GstElement* element, const OptionSet& characteristics)
+void GStreamerQuirkBroadcom::configureElement(GstElement* element, const OptionSet& characteristics)
 {
     if (g_str_has_prefix(GST_ELEMENT_NAME(element), "brcmaudiosink"))
         g_object_set(G_OBJECT(element), "async", TRUE, nullptr);
@@ -48,14 +48,12 @@ bool GStreamerQuirkBroadcom::configureElement(GstElement* element, const OptionS
     }
 
     if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream))
-        return true;
+        return;
 
     if (!g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstBrcmPCMSink") && gstObjectHasProperty(element, "low_latency")) {
         GST_DEBUG("Set 'low_latency' in brcmpcmsink");
         g_object_set(element, "low_latency", TRUE, "low_latency_max_queued_ms", 60, nullptr);
     }
-
-    return true;
 }
 
 std::optional GStreamerQuirkBroadcom::isHardwareAccelerated(GstElementFactory* factory)
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
index bff50784b843a..91526079b67f2 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
@@ -32,7 +32,7 @@ class GStreamerQuirkBroadcom final : public GStreamerQuirk {
     GStreamerQuirkBroadcom();
     const char* identifier() final { return "Broadcom"; }
 
-    bool configureElement(GstElement*, const OptionSet&) final;
+    void configureElement(GstElement*, const OptionSet&) final;
     std::optional isHardwareAccelerated(GstElementFactory*) final;
     std::optional audioVideoDecoderFactoryListType() const final { return GST_ELEMENT_FACTORY_TYPE_PARSER; }
     Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; }
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
index c5c0c75160fd0..390df682013c8 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
@@ -55,10 +55,10 @@ GstElement* GStreamerQuirkRealtek::createWebAudioSink()
     return sink;
 }
 
-bool GStreamerQuirkRealtek::configureElement(GstElement* element, const OptionSet& characteristics)
+void GStreamerQuirkRealtek::configureElement(GstElement* element, const OptionSet& characteristics)
 {
     if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream))
-        return false;
+        return;
 
     if (gstObjectHasProperty(element, "media-tunnel")) {
         GST_INFO("Enable 'immediate-output' in rtkaudiosink");
@@ -69,8 +69,6 @@ bool GStreamerQuirkRealtek::configureElement(GstElement* element, const OptionSe
         GST_INFO("Enable 'lowdelay-mode' in rtk omx decoder");
         g_object_set(element, "lowdelay-mode", TRUE, nullptr);
     }
-
-    return true;
 }
 
 std::optional GStreamerQuirkRealtek::isHardwareAccelerated(GstElementFactory* factory)
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
index a0fc595e6c70a..8e095aead08c6 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
@@ -32,7 +32,7 @@ class GStreamerQuirkRealtek final : public GStreamerQuirk {
     const char* identifier() final { return "Realtek"; }
 
     GstElement* createWebAudioSink() final;
-    bool configureElement(GstElement*, const OptionSet&) final;
+    void configureElement(GstElement*, const OptionSet&) final;
     std::optional isHardwareAccelerated(GstElementFactory*) final;
     Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; }
 
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
index 6c577dedc1f3e..0ef739ee4c562 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
@@ -51,16 +51,15 @@ GStreamerQuirkWesteros::GStreamerQuirkWesteros()
     }
 }
 
-bool GStreamerQuirkWesteros::configureElement(GstElement* element, const OptionSet& characteristics)
+void GStreamerQuirkWesteros::configureElement(GstElement* element, const OptionSet& characteristics)
 {
     if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream))
-        return false;
+        return;
 
     if (!g_strcmp0(G_OBJECT_TYPE_NAME(G_OBJECT(element)), "GstWesterosSink") && gstObjectHasProperty(element, "immediate-output")) {
         GST_INFO("Enable 'immediate-output' in WesterosSink");
         g_object_set(element, "immediate-output", TRUE, nullptr);
     }
-    return true;
 }
 
 std::optional GStreamerQuirkWesteros::isHardwareAccelerated(GstElementFactory* factory)
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h
index 810d727290914..518e512ecafec 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.h
@@ -31,7 +31,7 @@ class GStreamerQuirkWesteros final : public GStreamerQuirk {
     GStreamerQuirkWesteros();
     const char* identifier() final { return "Westeros"; }
 
-    bool configureElement(GstElement*, const OptionSet&) final;
+    void configureElement(GstElement*, const OptionSet&) final;
     std::optional isHardwareAccelerated(GstElementFactory*) final;
 
 private:
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
index 1bb59a7ee7b77..35cd7deb1e397 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
@@ -203,10 +203,8 @@ bool GStreamerQuirksManager::sinksRequireClockSynchronization() const
 void GStreamerQuirksManager::configureElement(GstElement* element, OptionSet&& characteristics)
 {
     GST_DEBUG("Configuring element %" GST_PTR_FORMAT, element);
-    for (const auto& quirk : m_quirks) {
-        if (quirk->configureElement(element, characteristics))
-            return;
-    }
+    for (const auto& quirk : m_quirks)
+        quirk->configureElement(element, characteristics);
 }
 
 std::optional GStreamerQuirksManager::isHardwareAccelerated(GstElementFactory* factory) const
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
index 527b9c0677840..8d38eda018945 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
@@ -55,7 +55,7 @@ class GStreamerQuirk : public GStreamerQuirkBase {
 
     virtual bool isPlatformSupported() const { return true; }
     virtual GstElement* createWebAudioSink() { return nullptr; }
-    virtual bool configureElement(GstElement*, const OptionSet&) { return false; }
+    virtual void configureElement(GstElement*, const OptionSet&) { }
     virtual std::optional isHardwareAccelerated(GstElementFactory*) { return std::nullopt; }
     virtual std::optional audioVideoDecoderFactoryListType() const { return std::nullopt; }
     virtual Vector disallowedWebAudioDecoders() const { return { }; }

From 93d4c8284d6aea8f09229ffcc361853a63a33687 Mon Sep 17 00:00:00 2001
From: Philippe Normand 
Date: Wed, 27 Mar 2024 10:07:12 +0000
Subject: [PATCH 17/17] [GStreamer][LibWebRTC] Add quirks in decoder factory
 https://bugs.webkit.org/show_bug.cgi?id=271751

Reviewed by NOBODY (OOPS!).

Allow the decoder pipeline to skip auto-plugging of hardware-accelerated decoders and also to
disable parsing, both for Broadcom and Realtek quirks.

* Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h:
* Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h:
* Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp:
(WebCore::GStreamerQuirksManager::shouldParseIncomingLibWebRTCBitStream const):
* Source/WebCore/platform/gstreamer/GStreamerQuirks.h:
(WebCore::GStreamerQuirk::shouldParseIncomingLibWebRTCBitStream const):
* Source/WebCore/platform/mediastream/libwebrtc/gstreamer/GStreamerVideoDecoderFactory.cpp:
(WebCore::GStreamerWebRTCVideoDecoder::getGstAutoplugSelectResult):
(WebCore::H264Decoder::H264Decoder):
---
 .../gstreamer/GStreamerQuirkBroadcom.h        |  1 +
 .../gstreamer/GStreamerQuirkRealtek.h         |  1 +
 .../platform/gstreamer/GStreamerQuirks.cpp    |  9 +++++
 .../platform/gstreamer/GStreamerQuirks.h      |  5 ++-
 .../GStreamerVideoDecoderFactory.cpp          | 34 +++++++++++++++----
 5 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
index 91526079b67f2..308a9d3a87961 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
@@ -37,6 +37,7 @@ class GStreamerQuirkBroadcom final : public GStreamerQuirk {
     std::optional audioVideoDecoderFactoryListType() const final { return GST_ELEMENT_FACTORY_TYPE_PARSER; }
     Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; }
     unsigned getAdditionalPlaybinFlags() const final { return getGstPlayFlag("text") | getGstPlayFlag("native-audio"); }
+    bool shouldParseIncomingLibWebRTCBitStream() const final { return false; }
 
 private:
     Vector m_disallowedWebAudioDecoders;
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
index 8e095aead08c6..c46020f0ac5e4 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
@@ -35,6 +35,7 @@ class GStreamerQuirkRealtek final : public GStreamerQuirk {
     void configureElement(GstElement*, const OptionSet&) final;
     std::optional isHardwareAccelerated(GstElementFactory*) final;
     Vector disallowedWebAudioDecoders() const final { return m_disallowedWebAudioDecoders; }
+    bool shouldParseIncomingLibWebRTCBitStream() const final { return false; }
 
 private:
     Vector m_disallowedWebAudioDecoders;
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
index 35cd7deb1e397..1b0c1c6bb750d 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
@@ -281,6 +281,15 @@ unsigned GStreamerQuirksManager::getAdditionalPlaybinFlags() const
     return flags;
 }
 
+bool GStreamerQuirksManager::shouldParseIncomingLibWebRTCBitStream() const
+{
+    for (auto& quirk : m_quirks) {
+        if (!quirk->shouldParseIncomingLibWebRTCBitStream())
+            return false;
+    }
+    return true;
+}
+
 #undef GST_CAT_DEFAULT
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
index 8d38eda018945..273ce14f6cfad 100644
--- a/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
@@ -59,7 +59,8 @@ class GStreamerQuirk : public GStreamerQuirkBase {
     virtual std::optional isHardwareAccelerated(GstElementFactory*) { return std::nullopt; }
     virtual std::optional audioVideoDecoderFactoryListType() const { return std::nullopt; }
     virtual Vector disallowedWebAudioDecoders() const { return { }; }
-    virtual unsigned getAdditionalPlaybinFlags() const { return 0; }
+    virtual unsigned getAdditionalPlaybinFlags() const { return getGstPlayFlag("text") | getGstPlayFlag("soft-colorbalance"); }
+    virtual bool shouldParseIncomingLibWebRTCBitStream() const { return true; }
 };
 
 class GStreamerHolePunchQuirk : public GStreamerQuirkBase {
@@ -102,6 +103,8 @@ class GStreamerQuirksManager : public RefCounted {
 
     unsigned getAdditionalPlaybinFlags() const;
 
+    bool shouldParseIncomingLibWebRTCBitStream() const;
+
 private:
     GStreamerQuirksManager(bool, bool);
 
diff --git a/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/GStreamerVideoDecoderFactory.cpp b/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/GStreamerVideoDecoderFactory.cpp
index e892a0fd89ce7..17d9a3cf3c916 100644
--- a/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/GStreamerVideoDecoderFactory.cpp
+++ b/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/GStreamerVideoDecoderFactory.cpp
@@ -23,6 +23,7 @@
 #if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
 #include "GStreamerVideoDecoderFactory.h"
 
+#include "GStreamerQuirks.h"
 #include "GStreamerVideoCommon.h"
 #include "GStreamerRegistryScanner.h"
 #include "GStreamerVideoFrameLibWebRTC.h"
@@ -112,6 +113,16 @@ class GStreamerWebRTCVideoDecoder : public webrtc::VideoDecoder {
         m_needsKeyframe = true;
     }
 
+    static unsigned getGstAutoplugSelectResult(const char* nick)
+    {
+        static GEnumClass* enumClass = static_cast(g_type_class_ref(g_type_from_name("GstAutoplugSelectResult")));
+        ASSERT(enumClass);
+        GEnumValue* ev = g_enum_get_value_by_nick(enumClass, nick);
+        if (!ev)
+            return 0;
+        return ev->value;
+    }
+
     bool Configure(const webrtc::VideoDecoder::Settings& codecSettings) override
     {
         m_src = makeElement("appsrc");
@@ -128,9 +139,17 @@ class GStreamerWebRTCVideoDecoder : public webrtc::VideoDecoder {
 
         auto sinkpad = adoptGRef(gst_element_get_static_pad(capsfilter, "sink"));
         g_signal_connect(decoder, "pad-added", G_CALLBACK(decodebinPadAddedCb), sinkpad.get());
-#if PLATFORM(BROADCOM) || PLATFORM(REALTEK)
-        g_signal_connect(decoder, "autoplug-select", G_CALLBACK(decodebinAutoplugSelect), nullptr);
-#endif
+
+        auto& quirksManager = GStreamerQuirksManager::singleton();
+        if (quirksManager.isEnabled()) {
+            g_signal_connect(decoder, "autoplug-select", G_CALLBACK(+[](GstElement*, GstPad*, GstCaps*, GstElementFactory* factory, gpointer) -> unsigned {
+                auto& quirksManager = GStreamerQuirksManager::singleton();
+                auto isHardwareAccelerated = quirksManager.isHardwareAccelerated(factory).value_or(false);
+                if (isHardwareAccelerated)
+                    return getGstAutoplugSelectResult("skip");
+                return getGstAutoplugSelectResult("try");
+            }), nullptr);
+        }
 
         // Make the decoder output "parsed" frames only and let the main decodebin
         // do the real decoding. This allows us to have optimized decoding/rendering
@@ -350,10 +369,13 @@ class GStreamerWebRTCVideoDecoder : public webrtc::VideoDecoder {
 
 class H264Decoder : public GStreamerWebRTCVideoDecoder {
 public:
-    H264Decoder() {
-#if !PLATFORM(REALTEK) && !PLATFORM(BROADCOM)
+    H264Decoder()
+    {
         m_requireParse = true;
-#endif
+
+        auto& quirksManager = GStreamerQuirksManager::singleton();
+        if (quirksManager.isEnabled())
+            m_requireParse = quirksManager.shouldParseIncomingLibWebRTCBitStream();
     }
 
     bool Configure(const webrtc::VideoDecoder::Settings& codecSettings) final