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/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/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/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..847492b8911dc 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()
{
@@ -185,9 +189,17 @@ 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 {
+ if (isHolePunchRenderingEnabled())
+ return adoptRef(*new TextureMapperPlatformLayerProxyGL);
+
#if USE(TEXTURE_MAPPER_DMABUF)
if (webKitDMABufVideoSinkIsEnabled() && webKitDMABufVideoSinkProbePlatform())
return adoptRef(*new TextureMapperPlatformLayerProxyDMABuf);
@@ -211,10 +223,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 +1018,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;
@@ -1028,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
@@ -1302,11 +1312,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.
@@ -1658,12 +1669,11 @@ FloatSize MediaPlayerPrivateGStreamer::naturalSize() const
if (!m_videoSize.isEmpty())
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
+ if (isHolePunchRenderingEnabled())
+ return s_holePunchDefaultFrameSize;
return m_videoSize;
}
@@ -1892,8 +1902,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 +1920,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 +1932,6 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
m_audioSink = element;
}
}
-#endif
if (!messageSourceIsPlaybin || m_isDelayingLoad)
break;
@@ -2284,9 +2291,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 +2327,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)
{
@@ -2925,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),
@@ -3037,19 +2998,14 @@ 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
}
void MediaPlayerPrivateGStreamer::configureVideoDecoder(GstElement* decoder)
@@ -3184,9 +3140,8 @@ PlatformLayer* MediaPlayerPrivateGStreamer::platformLayer() const
#if USE(NICOSIA)
void MediaPlayerPrivateGStreamer::swapBuffersIfNeeded()
{
-#if USE(GSTREAMER_HOLEPUNCH)
- pushNextHolePunchBuffer();
-#endif
+ if (isHolePunchRenderingEnabled())
+ pushNextHolePunchBuffer();
}
#else
RefPtr MediaPlayerPrivateGStreamer::proxy() const
@@ -3196,9 +3151,8 @@ RefPtr MediaPlayerPrivateGStreamer::proxy() con
void MediaPlayerPrivateGStreamer::swapBuffersIfNeeded()
{
-#if USE(GSTREAMER_HOLEPUNCH)
- pushNextHolePunchBuffer();
-#endif
+ if (isHolePunchRenderingEnabled())
+ pushNextHolePunchBuffer();
}
#endif
@@ -4059,23 +4013,21 @@ GstElement* MediaPlayerPrivateGStreamer::createVideoSinkGL()
}
#endif // USE(GSTREAMER_GL)
-#if USE(GSTREAMER_HOLEPUNCH)
-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. Mark them unused as default.
+ // and position of the video rendering window.
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
+ if (quirksManagerForTesting) {
+ quirksManagerForTesting->setHolePunchVideoRectangle(videoSink, rect);
+ return;
+ }
- UNUSED_PARAM(rect);
+ auto& quirksManager = GStreamerQuirksManager::singleton();
+ quirksManager.setHolePunchVideoRectangle(videoSink, rect);
}
class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePunchClient {
@@ -4086,31 +4038,29 @@ class GStreamerHolePunchClient : public TextureMapperPlatformLayerBuffer::HolePu
RefPtr m_host;
};
-GstElement* MediaPlayerPrivateGStreamer::createHolePunchVideoSink()
+bool MediaPlayerPrivateGStreamer::isHolePunchRenderingEnabled() const
{
- // 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 (m_quirksManagerForTesting)
+ return m_quirksManagerForTesting->supportsVideoHolePunchRendering();
+ auto& quirksManager = GStreamerQuirksManager::singleton();
+ return quirksManager.supportsVideoHolePunchRendering();
+}
-#if USE(WESTEROS_SINK)
- AtomString val;
- bool isPIPRequested =
- m_player->doesHaveAttribute("pip"_s, &val) && equalLettersIgnoringASCIICase(val, "true"_s);
- if (m_isLegacyPlaybin && !isPIPRequested)
+GstElement* MediaPlayerPrivateGStreamer::createHolePunchVideoSink()
+{
+ if (!isHolePunchRenderingEnabled())
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
+ GstElement* sink = nullptr;
+ if (m_quirksManagerForTesting)
+ sink = m_quirksManagerForTesting->createHolePunchVideoSink(m_isLegacyPlaybin, m_player);
+ else
+ sink = GStreamerQuirksManager::singleton().createHolePunchVideoSink(m_isLegacyPlaybin, m_player);
- return makeGStreamerElement("fakevideosink", nullptr);
+ // Configure sink before it allocates resources.
+ if (sink)
+ configureElement(sink);
+ return sink;
}
void MediaPlayerPrivateGStreamer::pushNextHolePunchBuffer()
@@ -4128,6 +4078,7 @@ void MediaPlayerPrivateGStreamer::pushNextHolePunchBuffer()
proxy.pushNextBuffer(WTFMove(layerBuffer));
};
+ ASSERT(isHolePunchRenderingEnabled());
#if USE(NICOSIA)
auto& proxy = downcast(m_nicosiaLayer->impl()).proxy();
ASSERT(is(proxy));
@@ -4142,9 +4093,13 @@ 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()
+{
+ return isHolePunchRenderingEnabled();
}
-#endif
GstElement* MediaPlayerPrivateGStreamer::createVideoSink()
{
@@ -4180,11 +4135,12 @@ 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();
+ // 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)
if (!m_videoSink && m_canRenderingBeAccelerated)
@@ -4508,32 +4464,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
+ setRectangleToVideoSink(m_quirksManagerForTesting.get(), m_videoSink.get(), IntRect());
}
}
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
+ 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 ac361a379b610..c741675edef59 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"
@@ -196,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)
@@ -254,7 +257,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 +279,6 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface
MediaPlayerPrivateGStreamer* m_playerPrivate;
};
void setVideoRectangle(const IntRect& rect);
-#endif
protected:
enum MainThreadNotification {
@@ -313,11 +314,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();
@@ -344,8 +344,6 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface
void setStreamVolumeElement(GstStreamVolume*);
- GstElement* pipeline() const { return m_pipeline.get(); }
-
void repaint();
void cancelRepaint(bool destroying = false);
@@ -543,9 +541,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,16 +666,16 @@ class MediaPlayerPrivateGStreamer : public MediaPlayerPrivateInterface
AbortableTaskQueue m_sinkTaskQueue;
-#if USE(GSTREAMER_HOLEPUNCH)
RefPtr m_gstreamerHolePunchHost;
Lock m_holePunchLock;
-#endif
bool m_didTryToRecoverPlayingState { false };
// Specific to MediaStream playback.
MediaTime m_startTime;
MediaTime m_pausedTime;
+
+ RefPtr m_quirksManagerForTesting;
};
}
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/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/GStreamerHolePunchQuirkWesteros.cpp b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp
new file mode 100644
index 0000000000000..0fd94427142e3
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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"
+#include "MediaPlayerPrivateGStreamer.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);
+ // 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;
+}
+
+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..11f0ed9731a93
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerHolePunchQuirkWesteros.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 GStreamerHolePunchQuirkWesteros final : public GStreamerHolePunchQuirk {
+public:
+ const char* identifier() final { return "WesterosHolePunch"; }
+
+ GstElement* createHolePunchVideoSink(bool, const MediaPlayer*) final;
+ bool setHolePunchVideoRectangle(GstElement*, const IntRect&) final;
+ bool requiresClockSynchronization() const final { return false; }
+};
+
+} // 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..610f026e434d1
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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);
+ 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;
+}
+
+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);
+ }
+
+ 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);
+ }
+}
+
+#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..ee3662eb20586
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkAmLogic.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 GStreamerQuirkAmLogic final : public GStreamerQuirk {
+public:
+ GStreamerQuirkAmLogic();
+ const char* identifier() final { return "AmLogic"; }
+
+ GstElement* createWebAudioSink() final;
+ void 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..01738d0e220e1
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 };
+}
+
+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);
+ 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;
+
+ 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);
+ }
+}
+
+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..308a9d3a87961
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkBroadcom.h
@@ -0,0 +1,48 @@
+/*
+ * 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 GStreamerQuirkBroadcom final : public GStreamerQuirk {
+public:
+ GStreamerQuirkBroadcom();
+ const char* identifier() final { return "Broadcom"; }
+
+ 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; }
+ unsigned getAdditionalPlaybinFlags() const final { return getGstPlayFlag("text") | getGstPlayFlag("native-audio"); }
+ bool shouldParseIncomingLibWebRTCBitStream() const final { return false; }
+
+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..390df682013c8
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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);
+ 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;
+}
+
+void GStreamerQuirkRealtek::configureElement(GstElement* element, const OptionSet& characteristics)
+{
+ if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream))
+ return;
+
+ 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);
+ }
+}
+
+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..c46020f0ac5e4
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkRealtek.h
@@ -0,0 +1,46 @@
+/*
+ * 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;
+ 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;
+};
+
+} // 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..0ef739ee4c562
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirkWesteros.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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));
+ }
+}
+
+void GStreamerQuirkWesteros::configureElement(GstElement* element, const OptionSet& characteristics)
+{
+ if (!characteristics.contains(ElementRuntimeCharacteristics::IsMediaStream))
+ 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);
+ }
+}
+
+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..518e512ecafec
--- /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"; }
+
+ void 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..1b0c1c6bb750d
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.cpp
@@ -0,0 +1,297 @@
+/*
+ * 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 "GStreamerHolePunchQuirkFake.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(false, true);
+ return sharedInstance;
+}
+
+GStreamerQuirksManager::GStreamerQuirksManager(bool isForTesting, bool loadQuirksFromEnvironment)
+ : m_isForTesting(isForTesting)
+{
+ 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.
+#if !PLATFORM(WPE)
+ 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)
+ 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;
+ }
+
+ 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.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* 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;
+
+ if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "help"_s)) {
+ WTFLogAlways("Supported quirks for WEBKIT_GST_HOLE_PUNCH_QUIRK are: fake, westeros, bcmnexus");
+ return;
+ }
+
+ // TODO: Maybe check this is coherent (somehow) with the quirk(s) selected above.
+ if (WTF::equalLettersIgnoringASCIICase(holePunchQuirk, "bcmnexus"_s))
+ 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());
+}
+
+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)
+{
+ if (!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)
+{
+ if (!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.");
+}
+
+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);
+ for (const auto& quirk : m_quirks)
+ quirk->configureElement(element, characteristics);
+}
+
+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;
+}
+
+void GStreamerQuirksManager::setHolePunchEnabledForTesting(bool enabled)
+{
+ if (enabled)
+ m_holePunchQuirk = WTF::makeUnique();
+ else
+ 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;
+}
+
+bool GStreamerQuirksManager::shouldParseIncomingLibWebRTCBitStream() const
+{
+ for (auto& quirk : m_quirks) {
+ if (!quirk->shouldParseIncomingLibWebRTCBitStream())
+ return false;
+ }
+ return true;
+}
+
+#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..273ce14f6cfad
--- /dev/null
+++ b/Source/WebCore/platform/gstreamer/GStreamerQuirks.h
@@ -0,0 +1,118 @@
+/*
+ * 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 "MediaPlayer.h"
+#include
+#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 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 { }; }
+ virtual unsigned getAdditionalPlaybinFlags() const { return getGstPlayFlag("text") | getGstPlayFlag("soft-colorbalance"); }
+ virtual bool shouldParseIncomingLibWebRTCBitStream() const { return true; }
+};
+
+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; }
+ virtual bool requiresClockSynchronization() const { return true; }
+};
+
+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();
+ 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&);
+ bool sinksRequireClockSynchronization() const;
+
+ void setHolePunchEnabledForTesting(bool);
+
+ unsigned getAdditionalPlaybinFlags() const;
+
+ bool shouldParseIncomingLibWebRTCBitStream() const;
+
+private:
+ GStreamerQuirksManager(bool, bool);
+
+ Vector> m_quirks;
+ std::unique_ptr m_holePunchQuirk;
+ bool m_isForTesting { false };
+};
+
+} // namespace WebCore
+
+#endif // USE(GSTREAMER)
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
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();