[GStreamer] Move GL video sink to its own GstBin sub-class
https://bugs.webkit.org/show_bug.cgi?id=204624

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

This reduces the MediaPlayerPrivate code-base and adds a good
separation of responsibility regarding GL video rendering. The
TextureCopier remains in the player because it's too specific.

* platform/GStreamer.cmake:
* platform/graphics/gstreamer/GLVideoSinkGStreamer.cpp: Added.
(webkit_gl_video_sink_init):
(webKitGLVideoSinkFinalize):
(ensureGstGLContext):
(requestGLContext):
(webKitGLVideoSinkChangeState):
(webkit_gl_video_sink_class_init):
(webKitGLVideoSinkSetMediaPlayerPrivate):
(webKitGLVideoSinkProbePlatform):
* platform/graphics/gstreamer/GLVideoSinkGStreamer.h: Added.
* platform/graphics/gstreamer/GStreamerCommon.cpp:
(WebCore::initializeGStreamerAndRegisterWebKitElements):
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
(WebCore::MediaPlayerPrivateGStreamer::load):
(WebCore::MediaPlayerPrivateGStreamer::changePipelineState):
(WebCore::MediaPlayerPrivateGStreamer::handleSyncMessage):
(WebCore::MediaPlayerPrivateGStreamer::createVideoSinkGL):
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:

Tools:

* Scripts/webkitpy/style/checker.py: White-list the new GLVideoSinkGStreamer GObject implementation.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@252917 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 08ae81c..3cfcaeb 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,34 @@
+2019-11-28  Philippe Normand  <pnormand@igalia.com>
+
+        [GStreamer] Move GL video sink to its own GstBin sub-class
+        https://bugs.webkit.org/show_bug.cgi?id=204624
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        This reduces the MediaPlayerPrivate code-base and adds a good
+        separation of responsibility regarding GL video rendering. The
+        TextureCopier remains in the player because it's too specific.
+
+        * platform/GStreamer.cmake:
+        * platform/graphics/gstreamer/GLVideoSinkGStreamer.cpp: Added.
+        (webkit_gl_video_sink_init):
+        (webKitGLVideoSinkFinalize):
+        (ensureGstGLContext):
+        (requestGLContext):
+        (webKitGLVideoSinkChangeState):
+        (webkit_gl_video_sink_class_init):
+        (webKitGLVideoSinkSetMediaPlayerPrivate):
+        (webKitGLVideoSinkProbePlatform):
+        * platform/graphics/gstreamer/GLVideoSinkGStreamer.h: Added.
+        * platform/graphics/gstreamer/GStreamerCommon.cpp:
+        (WebCore::initializeGStreamerAndRegisterWebKitElements):
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
+        (WebCore::MediaPlayerPrivateGStreamer::load):
+        (WebCore::MediaPlayerPrivateGStreamer::changePipelineState):
+        (WebCore::MediaPlayerPrivateGStreamer::handleSyncMessage):
+        (WebCore::MediaPlayerPrivateGStreamer::createVideoSinkGL):
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
+
 2019-11-27  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Generate fewer InlineItems when new lines are not preserved
diff --git a/Source/WebCore/platform/GStreamer.cmake b/Source/WebCore/platform/GStreamer.cmake
index 2c941f0..874d49a 100644
--- a/Source/WebCore/platform/GStreamer.cmake
+++ b/Source/WebCore/platform/GStreamer.cmake
@@ -7,6 +7,7 @@
 
     list(APPEND WebCore_SOURCES
         platform/graphics/gstreamer/AudioTrackPrivateGStreamer.cpp
+        platform/graphics/gstreamer/GLVideoSinkGStreamer.cpp
         platform/graphics/gstreamer/GRefPtrGStreamer.cpp
         platform/graphics/gstreamer/GStreamerCommon.cpp
         platform/graphics/gstreamer/GstAllocatorFastMalloc.cpp
diff --git a/Source/WebCore/platform/graphics/gstreamer/GLVideoSinkGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/GLVideoSinkGStreamer.cpp
new file mode 100644
index 0000000..8b00162
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/GLVideoSinkGStreamer.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2019 Igalia S.L
+ *
+ * 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 "GLVideoSinkGStreamer.h"
+
+#if ENABLE(VIDEO) && USE(GSTREAMER_GL)
+
+#include "GLContext.h"
+#include "GStreamerCommon.h"
+#include "MediaPlayerPrivateGStreamer.h"
+
+#if USE(GLX)
+#include "GLContextGLX.h"
+#include <gst/gl/x11/gstgldisplay_x11.h>
+#endif
+
+#if USE(EGL)
+#include "GLContextEGL.h"
+#include <gst/gl/egl/gstgldisplay_egl.h>
+#endif
+
+#if PLATFORM(X11)
+#include "PlatformDisplayX11.h"
+#endif
+
+#if PLATFORM(WAYLAND)
+#include "PlatformDisplayWayland.h"
+#endif
+
+#if USE(WPE_RENDERER)
+#include "PlatformDisplayLibWPE.h"
+#endif
+
+// gstglapi.h may include eglplatform.h and it includes X.h, which
+// defines None, breaking MediaPlayer::None enum
+#if PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL
+#undef None
+#endif // PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL
+
+#include <gst/app/gstappsink.h>
+
+using namespace WebCore;
+
+struct _WebKitGLVideoSinkPrivate {
+    GRefPtr<GstElement> appSink;
+    GRefPtr<GstGLContext> glContext;
+    GRefPtr<GstGLDisplay> glDisplay;
+    GRefPtr<GstContext> glDisplayElementContext;
+    GRefPtr<GstContext> glAppElementContext;
+    MediaPlayerPrivateGStreamer* mediaPlayerPrivate;
+};
+
+GST_DEBUG_CATEGORY_STATIC(webkit_gl_video_sink_debug);
+#define GST_CAT_DEFAULT webkit_gl_video_sink_debug
+
+#define GST_GL_CAPS_FORMAT "{ RGBx, RGBA, I420, Y444, YV12, Y41B, Y42B, NV12, NV21, VUYA }"
+static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
+
+#define WEBKIT_GL_VIDEO_SINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_GL_VIDEO_SINK, WebKitGLVideoSinkPrivate))
+
+#define webkit_gl_video_sink_parent_class parent_class
+#define WEBKIT_GL_VIDEO_SINK_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_gl_video_sink_debug, "webkitglvideosink", 0, "GL video sink element");
+G_DEFINE_TYPE_WITH_CODE(WebKitGLVideoSink, webkit_gl_video_sink, GST_TYPE_BIN, WEBKIT_GL_VIDEO_SINK_CATEGORY_INIT);
+
+static void webkit_gl_video_sink_init(WebKitGLVideoSink* sink)
+{
+    sink->priv = WEBKIT_GL_VIDEO_SINK_GET_PRIVATE(sink);
+    new (sink->priv) WebKitGLVideoSinkPrivate();
+
+    sink->priv->appSink = gst_element_factory_make("appsink", "webkit-gl-video-appsink");
+    ASSERT(sink->priv->appSink);
+    g_object_set(sink->priv->appSink.get(), "enable-last-sample", FALSE, "emit-signals", TRUE, "max-buffers", 1, nullptr);
+
+    GstElement* upload = gst_element_factory_make("glupload", nullptr);
+    GstElement* colorconvert = gst_element_factory_make("glcolorconvert", nullptr);
+    ASSERT(upload);
+    ASSERT(colorconvert);
+    gst_bin_add_many(GST_BIN_CAST(sink), upload, colorconvert, sink->priv->appSink.get(), nullptr);
+
+    // Workaround until we can depend on GStreamer 1.16.2.
+    // https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/commit/8d32de090554cf29fe359f83aa46000ba658a693
+    // Forcing a color conversion to RGBA here allows glupload to internally use
+    // an uploader that adds a VideoMeta, through the TextureUploadMeta caps
+    // feature, without needing the patch above. However this specific caps
+    // feature is going to be removed from GStreamer so it is considered a
+    // short-term workaround. This code path most likely will have a negative
+    // performance impact on embedded platforms as well. Downstream embedders
+    // are highly encouraged to cherry-pick the patch linked above in their BSP
+    // and set the WEBKIT_GST_NO_RGBA_CONVERSION environment variable until
+    // GStreamer 1.16.2 is released.
+    // See also https://bugs.webkit.org/show_bug.cgi?id=201422
+    GRefPtr<GstCaps> caps;
+    if (webkitGstCheckVersion(1, 16, 2) || getenv("WEBKIT_GST_NO_RGBA_CONVERSION"))
+        caps = adoptGRef(gst_caps_from_string("video/x-raw, format = (string) " GST_GL_CAPS_FORMAT));
+    else {
+        GST_INFO_OBJECT(sink, "Forcing RGBA as GStreamer is not new enough.");
+        caps = adoptGRef(gst_caps_from_string("video/x-raw, format = (string) RGBA"));
+    }
+    gst_caps_set_features(caps.get(), 0, gst_caps_features_new(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, nullptr));
+    g_object_set(sink->priv->appSink.get(), "caps", caps.get(), nullptr);
+
+    gst_element_link_many(upload, colorconvert, sink->priv->appSink.get(), nullptr);
+
+    GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(upload, "sink"));
+    gst_element_add_pad(GST_ELEMENT_CAST(sink), gst_ghost_pad_new("sink", pad.get()));
+}
+
+void webKitGLVideoSinkFinalize(GObject* object)
+{
+    ASSERT(WTF::isMainThread());
+
+    WebKitGLVideoSink* sink = WEBKIT_GL_VIDEO_SINK(object);
+    WebKitGLVideoSinkPrivate* priv = sink->priv;
+
+    if (priv->mediaPlayerPrivate)
+        g_signal_handlers_disconnect_by_data(priv->appSink.get(), priv->mediaPlayerPrivate);
+
+    // We used a placement new for construction, the destructor won't be called automatically.
+    priv->~_WebKitGLVideoSinkPrivate();
+
+    GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static bool ensureGstGLContext(WebKitGLVideoSink* sink)
+{
+    WebKitGLVideoSinkPrivate* priv = sink->priv;
+
+    if (priv->glContext)
+        return true;
+
+    auto& sharedDisplay = PlatformDisplay::sharedDisplayForCompositing();
+
+    // The floating ref removal support was added in https://bugzilla.gnome.org/show_bug.cgi?id=743062.
+    bool shouldAdoptRef = webkitGstCheckVersion(1, 14, 0);
+    if (!priv->glDisplay) {
+#if PLATFORM(X11)
+#if USE(GLX)
+        if (is<PlatformDisplayX11>(sharedDisplay)) {
+            GST_DEBUG_OBJECT(sink, "Creating X11 shared GL display");
+            if (shouldAdoptRef)
+                priv->glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_x11_new_with_display(downcast<PlatformDisplayX11>(sharedDisplay).native())));
+            else
+                priv->glDisplay = GST_GL_DISPLAY(gst_gl_display_x11_new_with_display(downcast<PlatformDisplayX11>(sharedDisplay).native()));
+        }
+#elif USE(EGL)
+        if (is<PlatformDisplayX11>(sharedDisplay)) {
+            GST_DEBUG_OBJECT(sink, "Creating X11 shared EGL display");
+            if (shouldAdoptRef)
+                priv->glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayX11>(sharedDisplay).eglDisplay())));
+            else
+                priv->glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayX11>(sharedDisplay).eglDisplay()));
+        }
+#endif
+#endif
+
+#if PLATFORM(WAYLAND)
+        if (is<PlatformDisplayWayland>(sharedDisplay)) {
+            GST_DEBUG_OBJECT(sink, "Creating Wayland shared display");
+            if (shouldAdoptRef)
+                priv->glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayWayland>(sharedDisplay).eglDisplay())));
+            else
+                priv->glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayWayland>(sharedDisplay).eglDisplay()));
+        }
+#endif
+
+#if USE(WPE_RENDERER)
+        if (is<PlatformDisplayLibWPE>(sharedDisplay)) {
+            GST_DEBUG_OBJECT(sink, "Creating WPE shared EGL display");
+            if (shouldAdoptRef)
+                priv->glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayLibWPE>(sharedDisplay).eglDisplay())));
+            else
+                priv->glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayLibWPE>(sharedDisplay).eglDisplay()));
+        }
+#endif
+
+        ASSERT(priv->glDisplay);
+    }
+
+    GLContext* sharedContext = sharedDisplay.sharingGLContext();
+    // EGL and GLX are mutually exclusive, no need for ifdefs here.
+    GstGLPlatform glPlatform = sharedContext->isEGLContext() ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_GLX;
+
+#if USE(OPENGL_ES)
+    GstGLAPI glAPI = GST_GL_API_GLES2;
+#elif USE(OPENGL)
+    GstGLAPI glAPI = GST_GL_API_OPENGL;
+#else
+    ASSERT_NOT_REACHED();
+#endif
+
+    PlatformGraphicsContext3D contextHandle = sharedContext->platformContext();
+    if (!contextHandle)
+        return false;
+
+    if (shouldAdoptRef)
+        priv->glContext = adoptGRef(gst_gl_context_new_wrapped(priv->glDisplay.get(), reinterpret_cast<guintptr>(contextHandle), glPlatform, glAPI));
+    else
+        priv->glContext = gst_gl_context_new_wrapped(priv->glDisplay.get(), reinterpret_cast<guintptr>(contextHandle), glPlatform, glAPI);
+
+    // Activate and fill the GStreamer wrapped context with the Webkit's shared one.
+    auto* previousActiveContext = GLContext::current();
+    sharedContext->makeContextCurrent();
+    if (gst_gl_context_activate(priv->glContext.get(), TRUE)) {
+        GUniqueOutPtr<GError> error;
+        if (!gst_gl_context_fill_info(priv->glContext.get(), &error.outPtr()))
+            GST_WARNING("Failed to fill in GStreamer context: %s", error->message);
+        gst_gl_context_activate(priv->glContext.get(), FALSE);
+    } else
+        GST_WARNING("Failed to activate GStreamer context %" GST_PTR_FORMAT, priv->glContext.get());
+    if (previousActiveContext)
+        previousActiveContext->makeContextCurrent();
+
+    return true;
+}
+
+GRefPtr<GstContext> requestGLContext(WebKitGLVideoSink* sink, const char* contextType)
+{
+    WebKitGLVideoSinkPrivate* priv = sink->priv;
+    if (!ensureGstGLContext(sink))
+        return nullptr;
+
+    if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) {
+        GstContext* displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
+        gst_context_set_gl_display(displayContext, priv->glDisplay.get());
+        return adoptGRef(displayContext);
+    }
+
+    if (!g_strcmp0(contextType, "gst.gl.app_context")) {
+        GstContext* appContext = gst_context_new("gst.gl.app_context", TRUE);
+        GstStructure* structure = gst_context_writable_structure(appContext);
+#if GST_CHECK_VERSION(1, 12, 0)
+        gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, priv->glContext.get(), nullptr);
+#else
+        gst_structure_set(structure, "context", GST_GL_TYPE_CONTEXT, priv->glContext.get(), nullptr);
+#endif
+        return adoptGRef(appContext);
+    }
+
+    return nullptr;
+}
+
+static GstStateChangeReturn webKitGLVideoSinkChangeState(GstElement* element, GstStateChange transition)
+{
+    WebKitGLVideoSink* sink = WEBKIT_GL_VIDEO_SINK(element);
+    WebKitGLVideoSinkPrivate* priv = sink->priv;
+
+#if GST_CHECK_VERSION(1, 14, 0)
+    GST_DEBUG_OBJECT(element, "%s", gst_state_change_get_name(transition));
+#endif
+
+    switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+    case GST_STATE_CHANGE_READY_TO_READY:
+    case GST_STATE_CHANGE_READY_TO_PAUSED: {
+        if (!priv->glDisplayElementContext)
+            priv->glDisplayElementContext = requestGLContext(sink, GST_GL_DISPLAY_CONTEXT_TYPE);
+
+        if (priv->glDisplayElementContext)
+            gst_element_set_context(GST_ELEMENT_CAST(sink), priv->glDisplayElementContext.get());
+
+        if (!priv->glAppElementContext)
+            priv->glAppElementContext = requestGLContext(sink, "gst.gl.app_context");
+
+        if (priv->glAppElementContext)
+            gst_element_set_context(GST_ELEMENT_CAST(sink), priv->glAppElementContext.get());
+        break;
+    }
+    default:
+        break;
+    }
+
+    return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
+}
+
+static void webkit_gl_video_sink_class_init(WebKitGLVideoSinkClass* klass)
+{
+    GObjectClass* objectClass = G_OBJECT_CLASS(klass);
+    GstElementClass* elementClass = GST_ELEMENT_CLASS(klass);
+
+    objectClass->finalize = webKitGLVideoSinkFinalize;
+
+    gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate));
+
+    gst_element_class_set_static_metadata(elementClass, "WebKit GL video sink", "Sink/Video", "Renders video", "Philippe Normand <philn@igalia.com>");
+
+    elementClass->change_state = GST_DEBUG_FUNCPTR(webKitGLVideoSinkChangeState);
+    g_type_class_add_private(klass, sizeof(WebKitGLVideoSinkPrivate));
+}
+
+void webKitGLVideoSinkSetMediaPlayerPrivate(WebKitGLVideoSink* sink, MediaPlayerPrivateGStreamer* player)
+{
+    WebKitGLVideoSinkPrivate* priv = sink->priv;
+
+    priv->mediaPlayerPrivate = player;
+    g_signal_connect(priv->appSink.get(), "new-sample", G_CALLBACK(+[](GstElement* sink, MediaPlayerPrivateGStreamer* player) -> GstFlowReturn {
+        GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink)));
+        player->triggerRepaint(sample.get());
+        return GST_FLOW_OK;
+    }), player);
+    g_signal_connect(priv->appSink.get(), "new-preroll", G_CALLBACK(+[](GstElement* sink, MediaPlayerPrivateGStreamer* player) -> GstFlowReturn {
+        GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_preroll(GST_APP_SINK(sink)));
+        player->triggerRepaint(sample.get());
+        return GST_FLOW_OK;
+    }), player);
+
+    GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(priv->appSink.get(), "sink"));
+    gst_pad_add_probe(pad.get(), static_cast<GstPadProbeType>(GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH), [] (GstPad*, GstPadProbeInfo* info,  gpointer userData) -> GstPadProbeReturn {
+        // In some platforms (e.g. OpenMAX on the Raspberry Pi) when a resolution change occurs the
+        // pipeline has to be drained before a frame with the new resolution can be decoded.
+        // In this context, it's important that we don't hold references to any previous frame
+        // (e.g. m_sample) so that decoding can continue.
+        // We are also not supposed to keep the original frame after a flush.
+        if (info->type & GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM) {
+            if (GST_QUERY_TYPE(GST_PAD_PROBE_INFO_QUERY(info)) != GST_QUERY_DRAIN)
+                return GST_PAD_PROBE_OK;
+            GST_DEBUG("Acting upon DRAIN query");
+        }
+        if (info->type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) {
+            if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_EVENT(info)) != GST_EVENT_FLUSH_START)
+                return GST_PAD_PROBE_OK;
+            GST_DEBUG("Acting upon flush-start event");
+        }
+
+        auto* player = static_cast<MediaPlayerPrivateGStreamer*>(userData);
+        player->flushCurrentBuffer();
+        return GST_PAD_PROBE_OK;
+    }, player, nullptr);
+}
+
+bool webKitGLVideoSinkProbePlatform()
+{
+    return isGStreamerPluginAvailable("app") && isGStreamerPluginAvailable("opengl");
+}
+
+#endif
diff --git a/Source/WebCore/platform/graphics/gstreamer/GLVideoSinkGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/GLVideoSinkGStreamer.h
new file mode 100644
index 0000000..456bcca
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/GLVideoSinkGStreamer.h
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (C) 2019 Igalia, S.L
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#pragma once
+
+#if ENABLE(VIDEO) && USE(GSTREAMER_GL)
+
+#include <gst/gst.h>
+
+namespace WebCore {
+class MediaPlayerPrivateGStreamer;
+}
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_GL_VIDEO_SINK            (webkit_gl_video_sink_get_type ())
+#define WEBKIT_GL_VIDEO_SINK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_GL_VIDEO_SINK, WebKitGLVideoSink))
+#define WEBKIT_GL_VIDEO_SINK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_GL_VIDEO_SINK, WebKitGLVideoSinkClass))
+#define WEBKIT_IS_GL_VIDEO_SINK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_GL_VIDEO_SINK))
+#define WEBKIT_IS_GL_VIDEO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_GL_VIDEO_SINK))
+
+typedef struct _WebKitGLVideoSink        WebKitGLVideoSink;
+typedef struct _WebKitGLVideoSinkClass   WebKitGLVideoSinkClass;
+typedef struct _WebKitGLVideoSinkPrivate WebKitGLVideoSinkPrivate;
+
+struct _WebKitGLVideoSink {
+    GstBin parent;
+
+    WebKitGLVideoSinkPrivate *priv;
+};
+
+struct _WebKitGLVideoSinkClass {
+    GstBinClass parentClass;
+};
+
+GType webkit_gl_video_sink_get_type(void);
+
+bool webKitGLVideoSinkProbePlatform();
+void webKitGLVideoSinkSetMediaPlayerPrivate(WebKitGLVideoSink*, WebCore::MediaPlayerPrivateGStreamer*);
+
+G_END_DECLS
+
+#endif // ENABLE(VIDEO) && USE(GSTREAMER_GL)
diff --git a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp
index b15f7f3..da50262 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.cpp
@@ -45,6 +45,27 @@
         gst_object_unref(ptr);
 }
 
+
+template <> GRefPtr<GstPlugin> adoptGRef(GstPlugin* ptr)
+{
+    ASSERT(!ptr || !g_object_is_floating(ptr));
+    return GRefPtr<GstPlugin>(ptr, GRefPtrAdopt);
+}
+
+template <> GstPlugin* refGPtr<GstPlugin>(GstPlugin* ptr)
+{
+    if (ptr)
+        gst_object_ref_sink(GST_OBJECT(ptr));
+
+    return ptr;
+}
+
+template <> void derefGPtr<GstPlugin>(GstPlugin* ptr)
+{
+    if (ptr)
+        gst_object_unref(ptr);
+}
+
 template <> GRefPtr<GstPad> adoptGRef(GstPad* ptr)
 {
     ASSERT(!ptr || !g_object_is_floating(ptr));
diff --git a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h
index a1965d4..1dc0529 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/GRefPtrGStreamer.h
@@ -34,6 +34,10 @@
 
 namespace WTF {
 
+template<> GRefPtr<GstPlugin> adoptGRef(GstPlugin* ptr);
+template<> GstPlugin* refGPtr<GstPlugin>(GstPlugin* ptr);
+template<> void derefGPtr<GstPlugin>(GstPlugin* ptr);
+
 template<> GRefPtr<GstElement> adoptGRef(GstElement* ptr);
 template<> GstElement* refGPtr<GstElement>(GstElement* ptr);
 template<> void derefGPtr<GstElement>(GstElement* ptr);
diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp
index 2091776..089bf74 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp
@@ -23,6 +23,7 @@
 
 #if USE(GSTREAMER)
 
+#include "GLVideoSinkGStreamer.h"
 #include "GstAllocatorFastMalloc.h"
 #include "IntSize.h"
 #include "SharedBuffer.h"
@@ -296,6 +297,9 @@
 
 #if ENABLE(VIDEO)
         gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
+#if USE(GSTREAMER_GL)
+        gst_element_register(0, "webkitglvideosink", GST_RANK_PRIMARY, WEBKIT_TYPE_GL_VIDEO_SINK);
+#endif
 #endif
     });
     return true;
@@ -379,6 +383,14 @@
     return SharedBuffer::create(*this);
 }
 
+bool isGStreamerPluginAvailable(const char* name)
+{
+    GRefPtr<GstPlugin> plugin = adoptGRef(gst_registry_find_plugin(gst_registry_get(), name));
+    if (!plugin)
+        GST_WARNING("Plugin %s not found. Please check your GStreamer installation", name);
+    return plugin;
+}
+
 }
 
 #endif // USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h
index c813b78..969c7c3 100644
--- a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h
+++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h
@@ -221,6 +221,8 @@
 
 enum class GstVideoDecoderPlatform { ImxVPU, Video4Linux };
 
+bool isGStreamerPluginAvailable(const char* name);
+
 }
 
 #ifndef GST_BUFFER_DTS_OR_PTS
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index e2249cd..579ed99 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -123,40 +123,10 @@
 #endif // ENABLE(VIDEO_TRACK)
 
 #if USE(GSTREAMER_GL)
-#define TEXTURE_COPIER_COLOR_CONVERT_FLAG VideoTextureCopierGStreamer::ColorConversion::NoConvert
-#define GST_GL_CAPS_FORMAT "{ RGBx, RGBA, I420, Y444, YV12, Y41B, Y42B, NV12, NV21, VUYA }"
-
-#include <gst/app/gstappsink.h>
-
-#include "GLContext.h"
-#if USE(GLX)
-#include "GLContextGLX.h"
-#include <gst/gl/x11/gstgldisplay_x11.h>
-#endif
-
-#if USE(EGL)
-#include "GLContextEGL.h"
-#include <gst/gl/egl/gstgldisplay_egl.h>
-#endif
-
-#if PLATFORM(X11)
-#include "PlatformDisplayX11.h"
-#endif
-
-#if PLATFORM(WAYLAND)
-#include "PlatformDisplayWayland.h"
-#endif
-
-#if USE(WPE_RENDERER)
-#include "PlatformDisplayLibWPE.h"
-#endif
-
-// gstglapi.h may include eglplatform.h and it includes X.h, which
-// defines None, breaking MediaPlayer::None enum
-#if PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL
-#undef None
-#endif // PLATFORM(X11) && GST_GL_HAVE_PLATFORM_EGL
+#include "GLVideoSinkGStreamer.h"
 #include "VideoTextureCopierGStreamer.h"
+
+#define TEXTURE_COPIER_COLOR_CONVERT_FLAG VideoTextureCopierGStreamer::ColorConversion::NoConvert
 #endif // USE(GSTREAMER_GL)
 
 #if USE(TEXTURE_MAPPER_GL)
@@ -459,15 +429,8 @@
 
     m_notifier->invalidate();
 
-    if (m_videoSink) {
+    if (m_videoSink)
         g_signal_handlers_disconnect_matched(m_videoSink.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
-#if USE(GSTREAMER_GL)
-        if (GST_IS_BIN(m_videoSink.get())) {
-            GRefPtr<GstElement> appsink = adoptGRef(gst_bin_get_by_name(GST_BIN_CAST(m_videoSink.get()), "webkit-gl-video-sink"));
-            g_signal_handlers_disconnect_by_data(appsink.get(), this);
-        }
-#endif
-    }
 
     if (m_volumeElement)
         g_signal_handlers_disconnect_matched(m_volumeElement.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
@@ -574,9 +537,6 @@
     loadFull(String("mediastream://") + stream.id(), pipelineName);
     syncOnClock(false);
 
-#if USE(GSTREAMER_GL)
-    ensureGLVideoSinkContext();
-#endif
     m_player->play();
 }
 #endif
@@ -1182,11 +1142,6 @@
     GST_DEBUG_OBJECT(pipeline(), "Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
         gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
 
-#if USE(GSTREAMER_GL)
-    if (currentState <= GST_STATE_READY && newState >= GST_STATE_PAUSED)
-        ensureGLVideoSinkContext();
-#endif
-
     GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), newState);
     GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
     if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE)
@@ -1778,14 +1733,6 @@
         return true;
     }
 
-#if USE(GSTREAMER_GL)
-    GRefPtr<GstContext> elementContext = adoptGRef(requestGLContext(contextType));
-    if (elementContext) {
-        gst_element_set_context(GST_ELEMENT(message->src), elementContext.get());
-        return true;
-    }
-#endif // USE(GSTREAMER_GL)
-
 #if ENABLE(ENCRYPTED_MEDIA)
     if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id")) {
         if (isMainThread()) {
@@ -1829,126 +1776,11 @@
         return true;
     }
 #endif // ENABLE(ENCRYPTED_MEDIA)
+
+    GST_DEBUG_OBJECT(pipeline(), "Unhandled %s need-context message for %s", contextType, GST_MESSAGE_SRC_NAME(message));
     return false;
 }
 
-#if USE(GSTREAMER_GL)
-GstContext* MediaPlayerPrivateGStreamer::requestGLContext(const char* contextType)
-{
-    if (!ensureGstGLContext())
-        return nullptr;
-
-    if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) {
-        GstContext* displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
-        gst_context_set_gl_display(displayContext, gstGLDisplay());
-        return displayContext;
-    }
-
-    if (!g_strcmp0(contextType, "gst.gl.app_context")) {
-        GstContext* appContext = gst_context_new("gst.gl.app_context", TRUE);
-        GstStructure* structure = gst_context_writable_structure(appContext);
-#if GST_CHECK_VERSION(1, 12, 0)
-        gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, gstGLContext(), nullptr);
-#else
-        gst_structure_set(structure, "context", GST_GL_TYPE_CONTEXT, gstGLContext(), nullptr);
-#endif
-        return appContext;
-    }
-
-    return nullptr;
-}
-
-bool MediaPlayerPrivateGStreamer::ensureGstGLContext()
-{
-    if (m_glContext)
-        return true;
-
-    auto& sharedDisplay = PlatformDisplay::sharedDisplayForCompositing();
-
-    // The floating ref removal support was added in https://bugzilla.gnome.org/show_bug.cgi?id=743062.
-    bool shouldAdoptRef = webkitGstCheckVersion(1, 14, 0);
-    if (!m_glDisplay) {
-#if PLATFORM(X11)
-#if USE(GLX)
-        if (is<PlatformDisplayX11>(sharedDisplay)) {
-            GST_DEBUG_OBJECT(pipeline(), "Creating X11 shared GL display");
-            if (shouldAdoptRef)
-                m_glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_x11_new_with_display(downcast<PlatformDisplayX11>(sharedDisplay).native())));
-            else
-                m_glDisplay = GST_GL_DISPLAY(gst_gl_display_x11_new_with_display(downcast<PlatformDisplayX11>(sharedDisplay).native()));
-        }
-#elif USE(EGL)
-        if (is<PlatformDisplayX11>(sharedDisplay)) {
-            GST_DEBUG_OBJECT(pipeline(), "Creating X11 shared EGL display");
-            if (shouldAdoptRef)
-                m_glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayX11>(sharedDisplay).eglDisplay())));
-            else
-                m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayX11>(sharedDisplay).eglDisplay()));
-        }
-#endif
-#endif
-
-#if PLATFORM(WAYLAND)
-        if (is<PlatformDisplayWayland>(sharedDisplay)) {
-            GST_DEBUG_OBJECT(pipeline(), "Creating Wayland shared display");
-            if (shouldAdoptRef)
-                m_glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayWayland>(sharedDisplay).eglDisplay())));
-            else
-                m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayWayland>(sharedDisplay).eglDisplay()));
-        }
-#endif
-
-#if USE(WPE_RENDERER)
-        if (is<PlatformDisplayLibWPE>(sharedDisplay)) {
-            GST_DEBUG_OBJECT(pipeline(), "Creating WPE shared EGL display");
-            if (shouldAdoptRef)
-                m_glDisplay = adoptGRef(GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayLibWPE>(sharedDisplay).eglDisplay())));
-            else
-                m_glDisplay = GST_GL_DISPLAY(gst_gl_display_egl_new_with_egl_display(downcast<PlatformDisplayLibWPE>(sharedDisplay).eglDisplay()));
-        }
-#endif
-
-        ASSERT(m_glDisplay);
-    }
-
-    GLContext* webkitContext = sharedDisplay.sharingGLContext();
-    // EGL and GLX are mutually exclusive, no need for ifdefs here.
-    GstGLPlatform glPlatform = webkitContext->isEGLContext() ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_GLX;
-
-#if USE(OPENGL_ES)
-    GstGLAPI glAPI = GST_GL_API_GLES2;
-#elif USE(OPENGL)
-    GstGLAPI glAPI = GST_GL_API_OPENGL;
-#else
-    ASSERT_NOT_REACHED();
-#endif
-
-    PlatformGraphicsContext3D contextHandle = webkitContext->platformContext();
-    if (!contextHandle)
-        return false;
-
-    if (shouldAdoptRef)
-        m_glContext = adoptGRef(gst_gl_context_new_wrapped(m_glDisplay.get(), reinterpret_cast<guintptr>(contextHandle), glPlatform, glAPI));
-    else
-        m_glContext = gst_gl_context_new_wrapped(m_glDisplay.get(), reinterpret_cast<guintptr>(contextHandle), glPlatform, glAPI);
-
-    // Activate and fill the GStreamer wrapped context with the Webkit's shared one.
-    auto previousActiveContext = GLContext::current();
-    webkitContext->makeContextCurrent();
-    if (gst_gl_context_activate(m_glContext.get(), TRUE)) {
-        GUniqueOutPtr<GError> error;
-        if (!gst_gl_context_fill_info(m_glContext.get(), &error.outPtr()))
-            GST_WARNING("Failed to fill in GStreamer context: %s", error->message);
-        gst_gl_context_activate(m_glContext.get(), FALSE);
-    } else
-        GST_WARNING("Failed to activate GStreamer context %" GST_PTR_FORMAT, m_glContext.get());
-    if (previousActiveContext)
-        previousActiveContext->makeContextCurrent();
-
-    return true;
-}
-#endif // USE(GSTREAMER_GL)
-
 // Returns the size of the video
 FloatSize MediaPlayerPrivateGStreamer::naturalSize() const
 {
@@ -3359,20 +3191,6 @@
 }
 
 #if USE(GSTREAMER_GL)
-GstFlowReturn MediaPlayerPrivateGStreamer::newSampleCallback(GstElement* sink, MediaPlayerPrivateGStreamer* player)
-{
-    GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink)));
-    player->triggerRepaint(sample.get());
-    return GST_FLOW_OK;
-}
-
-GstFlowReturn MediaPlayerPrivateGStreamer::newPrerollCallback(GstElement* sink, MediaPlayerPrivateGStreamer* player)
-{
-    GRefPtr<GstSample> sample = adoptGRef(gst_app_sink_pull_preroll(GST_APP_SINK(sink)));
-    player->triggerRepaint(sample.get());
-    return GST_FLOW_OK;
-}
-
 void MediaPlayerPrivateGStreamer::flushCurrentBuffer()
 {
     auto sampleLocker = holdLock(m_sampleMutex);
@@ -3595,118 +3413,17 @@
 }
 
 #if USE(GSTREAMER_GL)
-GstElement* MediaPlayerPrivateGStreamer::createGLAppSink()
-{
-    GstElement* appsink = gst_element_factory_make("appsink", "webkit-gl-video-sink");
-    if (!appsink)
-        return nullptr;
-
-    g_object_set(appsink, "enable-last-sample", FALSE, "emit-signals", TRUE, "max-buffers", 1, nullptr);
-    g_signal_connect(appsink, "new-sample", G_CALLBACK(newSampleCallback), this);
-    g_signal_connect(appsink, "new-preroll", G_CALLBACK(newPrerollCallback), this);
-
-    GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(appsink, "sink"));
-    gst_pad_add_probe(pad.get(), static_cast<GstPadProbeType>(GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH), [] (GstPad*, GstPadProbeInfo* info,  gpointer userData) -> GstPadProbeReturn {
-        // In some platforms (e.g. OpenMAX on the Raspberry Pi) when a resolution change occurs the
-        // pipeline has to be drained before a frame with the new resolution can be decoded.
-        // In this context, it's important that we don't hold references to any previous frame
-        // (e.g. m_sample) so that decoding can continue.
-        // We are also not supposed to keep the original frame after a flush.
-        if (info->type & GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM) {
-            if (GST_QUERY_TYPE(GST_PAD_PROBE_INFO_QUERY(info)) != GST_QUERY_DRAIN)
-                return GST_PAD_PROBE_OK;
-            GST_DEBUG("Acting upon DRAIN query");
-        }
-        if (info->type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) {
-            if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_EVENT(info)) != GST_EVENT_FLUSH_START)
-                return GST_PAD_PROBE_OK;
-            GST_DEBUG("Acting upon flush-start event");
-        }
-
-        auto* player = static_cast<MediaPlayerPrivateGStreamer*>(userData);
-        player->flushCurrentBuffer();
-        return GST_PAD_PROBE_OK;
-    }, this, nullptr);
-
-    return appsink;
-}
-
 GstElement* MediaPlayerPrivateGStreamer::createVideoSinkGL()
 {
-    gboolean result = TRUE;
-    GstElement* videoSink = gst_bin_new(nullptr);
-    GstElement* upload = gst_element_factory_make("glupload", nullptr);
-    GstElement* colorconvert = gst_element_factory_make("glcolorconvert", nullptr);
-    GstElement* appsink = createGLAppSink();
-
-    // glsinkbin is not used because it includes glcolorconvert which only process RGBA,
-    // but we can display YUV formats too.
-
-    if (!appsink || !upload || !colorconvert) {
-        GST_WARNING("Failed to create GstGL elements");
-        gst_object_unref(videoSink);
-
-        if (upload)
-            gst_object_unref(upload);
-        if (colorconvert)
-            gst_object_unref(colorconvert);
-        if (appsink)
-            gst_object_unref(appsink);
-
-        g_warning("WebKit wasn't able to find the GStreamer opengl plugin. Hardware-accelerated zero-copy video rendering can't be enabled without this plugin.");
+    if (!webKitGLVideoSinkProbePlatform()) {
+        g_warning("WebKit wasn't able to find the GL video sink dependencies. Hardware-accelerated zero-copy video rendering can't be enabled without this plugin.");
         return nullptr;
     }
 
-    gst_bin_add_many(GST_BIN(videoSink), upload, colorconvert, appsink, nullptr);
-
-    // Workaround until we can depend on GStreamer 1.16.2.
-    // https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/commit/8d32de090554cf29fe359f83aa46000ba658a693
-    // Forcing a color conversion to RGBA here allows glupload to internally use
-    // an uploader that adds a VideoMeta, through the TextureUploadMeta caps
-    // feature, without needing the patch above. However this specific caps
-    // feature is going to be removed from GStreamer so it is considered a
-    // short-term workaround. This code path most likely will have a negative
-    // performance impact on embedded platforms as well. Downstream embedders
-    // are highly encouraged to cherry-pick the patch linked above in their BSP
-    // and set the WEBKIT_GST_NO_RGBA_CONVERSION environment variable until
-    // GStreamer 1.16.2 is released.
-    // See also https://bugs.webkit.org/show_bug.cgi?id=201422
-    GRefPtr<GstCaps> caps;
-    if (webkitGstCheckVersion(1, 16, 2) || getenv("WEBKIT_GST_NO_RGBA_CONVERSION"))
-        caps = adoptGRef(gst_caps_from_string("video/x-raw, format = (string) " GST_GL_CAPS_FORMAT));
-    else {
-        GST_INFO_OBJECT(pipeline(), "Forcing RGBA as GStreamer is not new enough.");
-        caps = adoptGRef(gst_caps_from_string("video/x-raw, format = (string) RGBA"));
-    }
-    gst_caps_set_features(caps.get(), 0, gst_caps_features_new(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, nullptr));
-    g_object_set(appsink, "caps", caps.get(), nullptr);
-
-    result &= gst_element_link_many(upload, colorconvert, appsink, nullptr);
-
-    GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(upload, "sink"));
-    gst_element_add_pad(videoSink, gst_ghost_pad_new("sink", pad.get()));
-
-    if (!result) {
-        GST_WARNING("Failed to link GstGL elements");
-        gst_object_unref(videoSink);
-        videoSink = nullptr;
-    }
-    return videoSink;
-}
-
-void MediaPlayerPrivateGStreamer::ensureGLVideoSinkContext()
-{
-    if (!m_glDisplayElementContext)
-        m_glDisplayElementContext = adoptGRef(requestGLContext(GST_GL_DISPLAY_CONTEXT_TYPE));
-
-    if (m_glDisplayElementContext)
-        gst_element_set_context(m_videoSink.get(), m_glDisplayElementContext.get());
-
-    if (!m_glAppElementContext)
-        m_glAppElementContext = adoptGRef(requestGLContext("gst.gl.app_context"));
-
-    if (m_glAppElementContext)
-        gst_element_set_context(m_videoSink.get(), m_glAppElementContext.get());
+    GstElement* sink = gst_element_factory_make("webkitglvideosink", nullptr);
+    ASSERT(sink);
+    webKitGLVideoSinkSetMediaPlayerPrivate(WEBKIT_GL_VIDEO_SINK(sink), this);
+    return sink;
 }
 #endif // USE(GSTREAMER_GL)
 
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
index 2adb0ff..2790ec3 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
@@ -160,10 +160,6 @@
     void setVolume(float) override;
     float volume() const override;
 
-#if USE(GSTREAMER_GL)
-    bool ensureGstGLContext();
-    GstContext* requestGLContext(const char* contextType);
-#endif
     void setMuted(bool) override;
     bool muted() const;
 
@@ -255,6 +251,11 @@
     bool handleSyncMessage(GstMessage*);
     void handleMessage(GstMessage*);
 
+    void triggerRepaint(GstSample*);
+#if USE(GSTREAMER_GL)
+    void flushCurrentBuffer();
+#endif
+
 protected:
     enum MainThreadNotification {
         VideoChanged = 1 << 0,
@@ -285,14 +286,7 @@
 #endif
 
 #if USE(GSTREAMER_GL)
-    static GstFlowReturn newSampleCallback(GstElement*, MediaPlayerPrivateGStreamer*);
-    static GstFlowReturn newPrerollCallback(GstElement*, MediaPlayerPrivateGStreamer*);
-    void flushCurrentBuffer();
-    GstElement* createGLAppSink();
     GstElement* createVideoSinkGL();
-    GstGLContext* gstGLContext() const { return m_glContext.get(); }
-    GstGLDisplay* gstGLDisplay() const { return m_glDisplay.get(); }
-    void ensureGLVideoSinkContext();
 #endif
 
 #if USE(TEXTURE_MAPPER_GL)
@@ -311,7 +305,6 @@
 
     void setPipeline(GstElement*);
 
-    void triggerRepaint(GstSample*);
     void repaint();
     void cancelRepaint(bool destroying = false);
 
@@ -404,12 +397,7 @@
     bool m_destroying { false };
 
 #if USE(GSTREAMER_GL)
-    GRefPtr<GstGLContext> m_glContext;
-    GRefPtr<GstGLDisplay> m_glDisplay;
-    GRefPtr<GstContext> m_glDisplayElementContext;
-    GRefPtr<GstContext> m_glAppElementContext;
     std::unique_ptr<VideoTextureCopierGStreamer> m_videoTextureCopier;
-
     GRefPtr<GstGLColorConvert> m_colorConvert;
     GRefPtr<GstCaps> m_colorConvertInputCaps;
     GRefPtr<GstCaps> m_colorConvertOutputCaps;
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index e2323ad..8b3f536 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,12 @@
+2019-11-28  Philippe Normand  <pnormand@igalia.com>
+
+        [GStreamer] Move GL video sink to its own GstBin sub-class
+        https://bugs.webkit.org/show_bug.cgi?id=204624
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        * Scripts/webkitpy/style/checker.py: White-list the new GLVideoSinkGStreamer GObject implementation.
+
 2019-11-27  Zalan Bujtas  <zalan@apple.com>
 
         [LFC] Unreviewed test gardening.
diff --git a/Tools/Scripts/webkitpy/style/checker.py b/Tools/Scripts/webkitpy/style/checker.py
index 434a61e..13d1d48 100644
--- a/Tools/Scripts/webkitpy/style/checker.py
+++ b/Tools/Scripts/webkitpy/style/checker.py
@@ -217,6 +217,8 @@
     ([
       # These files define GObjects, which implies some definitions of
       # variables and functions containing underscores.
+      os.path.join('Source', 'WebCore', 'platform', 'graphics', 'gstreamer', 'GLVideoSinkGStreamer.cpp'),
+      os.path.join('Source', 'WebCore', 'platform', 'graphics', 'gstreamer', 'GLVideoSinkGStreamer.h'),
       os.path.join('Source', 'WebCore', 'platform', 'graphics', 'gstreamer', 'VideoSinkGStreamer.cpp'),
       os.path.join('Source', 'WebCore', 'platform', 'graphics', 'gstreamer', 'WebKitWebSourceGStreamer.cpp'),
       os.path.join('Source', 'WebCore', 'platform', 'audio', 'gstreamer', 'WebKitWebAudioSourceGStreamer.cpp'),