From cb4f6c877e593d0e2aeea16f5c80b84e31c5927a Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 10 Mar 2021 17:27:52 -0300 Subject: wpe: Move wpesrc to wpevideosrc and add a wrapper bin `wpesrc` Currently the bin contains a single element but we are going to implement audio support and expose extra pads for audio Part-of: --- ext/wpe/WPEThreadedView.cpp | 12 +- ext/wpe/WPEThreadedView.h | 8 +- ext/wpe/gstwpe.cpp | 86 +++++ ext/wpe/gstwpesrc.cpp | 805 -------------------------------------------- ext/wpe/gstwpesrc.h | 48 --- ext/wpe/gstwpesrcbin.cpp | 215 ++++++++++++ ext/wpe/gstwpesrcbin.h | 43 +++ ext/wpe/gstwpevideosrc.cpp | 738 ++++++++++++++++++++++++++++++++++++++++ ext/wpe/gstwpevideosrc.h | 12 + ext/wpe/meson.build | 2 +- 10 files changed, 1105 insertions(+), 864 deletions(-) create mode 100644 ext/wpe/gstwpe.cpp delete mode 100644 ext/wpe/gstwpesrc.cpp delete mode 100644 ext/wpe/gstwpesrc.h create mode 100644 ext/wpe/gstwpesrcbin.cpp create mode 100644 ext/wpe/gstwpesrcbin.h create mode 100644 ext/wpe/gstwpevideosrc.cpp create mode 100644 ext/wpe/gstwpevideosrc.h (limited to 'ext') diff --git a/ext/wpe/WPEThreadedView.cpp b/ext/wpe/WPEThreadedView.cpp index f4588e5b3..e95b2bb5d 100644 --- a/ext/wpe/WPEThreadedView.cpp +++ b/ext/wpe/WPEThreadedView.cpp @@ -35,8 +35,8 @@ #include #endif -GST_DEBUG_CATEGORY_EXTERN (wpe_src_debug); -#define GST_CAT_DEFAULT wpe_src_debug +GST_DEBUG_CATEGORY_EXTERN (wpe_view_debug); +#define GST_CAT_DEFAULT wpe_view_debug #if defined(WPE_FDO_CHECK_VERSION) && WPE_FDO_CHECK_VERSION(1, 3, 0) #define USE_DEPRECATED_FDO_EGL_IMAGE 0 @@ -165,7 +165,7 @@ gpointer WPEContextThread::s_viewThread(gpointer data) return nullptr; } -WPEView* WPEContextThread::createWPEView(GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) +WPEView* WPEContextThread::createWPEView(GstWpeVideoSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) { GST_DEBUG("context %p display %p, size (%d,%d)", context, display, width, height); @@ -199,7 +199,7 @@ WPEView* WPEContextThread::createWPEView(GstWpeSrc* src, GstGLContext* context, static gboolean s_loadFailed(WebKitWebView*, WebKitLoadEvent, gchar* failing_uri, GError* error, gpointer data) { - GstWpeSrc* src = GST_WPE_SRC(data); + GstWpeVideoSrc* src = GST_WPE_VIDEO_SRC(data); if (g_error_matches(error, WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED)) { GST_INFO_OBJECT (src, "Loading cancelled."); @@ -231,7 +231,7 @@ static void s_loadProgressChaned(GObject* object, GParamSpec*, gpointer data) gst_object_unref (bus); } -WPEView::WPEView(WebKitWebContext* web_context, GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) +WPEView::WPEView(WebKitWebContext* web_context, GstWpeVideoSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height) { g_mutex_init(&threading.ready_mutex); g_cond_init(&threading.ready_cond); @@ -292,7 +292,7 @@ WPEView::WPEView(WebKitWebContext* web_context, GstWpeSrc* src, GstGLContext* co g_signal_connect(webkit.view, "load-failed-with-tls-errors", G_CALLBACK(s_loadFailedWithTLSErrors), src); g_signal_connect(webkit.view, "notify::estimated-load-progress", G_CALLBACK(s_loadProgressChaned), src); - gst_wpe_src_configure_web_view(src, webkit.view); + gst_wpe_video_src_configure_web_view(src, webkit.view); gchar* location; gboolean drawBackground = TRUE; diff --git a/ext/wpe/WPEThreadedView.h b/ext/wpe/WPEThreadedView.h index a4b5ae22d..414d9b209 100644 --- a/ext/wpe/WPEThreadedView.h +++ b/ext/wpe/WPEThreadedView.h @@ -26,7 +26,7 @@ #include #include #include -#include "gstwpesrc.h" +#include "gstwpevideosrc.h" typedef struct _GstGLContext GstGLContext; typedef struct _GstGLDisplay GstGLDisplay; @@ -40,12 +40,12 @@ typedef struct _GstEGLImage GstEGLImage; class WPEView { public: - WPEView(WebKitWebContext*, GstWpeSrc*, GstGLContext*, GstGLDisplay*, int width, int height); + WPEView(WebKitWebContext*, GstWpeVideoSrc*, GstGLContext*, GstGLDisplay*, int width, int height); ~WPEView(); bool operator!() const { return m_isValid; } - /* Used by wpesrc */ + /* Used by wpevideosrc */ void resize(int width, int height); void loadUri(const gchar*); void loadData(GBytes*); @@ -134,7 +134,7 @@ public: WPEContextThread(); ~WPEContextThread(); - WPEView* createWPEView(GstWpeSrc*, GstGLContext*, GstGLDisplay*, int width, int height); + WPEView* createWPEView(GstWpeVideoSrc*, GstGLContext*, GstGLDisplay*, int width, int height); template void dispatch(Function); diff --git a/ext/wpe/gstwpe.cpp b/ext/wpe/gstwpe.cpp new file mode 100644 index 000000000..51dba610d --- /dev/null +++ b/ext/wpe/gstwpe.cpp @@ -0,0 +1,86 @@ +/* Copyright (C) <2018> Philippe Normand + * Copyright (C) <2018> Žan Doberšek + * + * 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 along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-wpesrc + * @title: wpesrc + * + * The wpesrc element is used to produce a video texture representing a web page + * rendered off-screen by WPE. + * + * Starting from WPEBackend-FDO 1.6.x, software rendering support is available. This + * features allows wpesrc to be used on machines without GPU, and/or for testing + * purpose. To enable it, set the `LIBGL_ALWAYS_SOFTWARE=true` environment + * variable and make sure `video/x-raw, format=BGRA` caps are negotiated by the + * wpesrc element. + * + * ## Example launch lines + * + * |[ + * gst-launch-1.0 -v wpesrc location="https://gstreamer.freedesktop.org" ! queue ! glimagesink + * ]| + * Shows the GStreamer website homepage + * + * |[ + * LIBGL_ALWAYS_SOFTWARE=true gst-launch-1.0 -v wpesrc num-buffers=50 location="https://gstreamer.freedesktop.org" ! videoconvert ! pngenc ! multifilesink location=/tmp/snapshot-%05d.png + * ]| + * Saves the first 50 video frames generated for the GStreamer website as PNG files in /tmp. + * + * |[ + * gst-play-1.0 --videosink gtkglsink wpe://https://gstreamer.freedesktop.org + * ]| + * Shows the GStreamer website homepage as played with GstPlayer in a GTK+ window. + * + * |[ + * gst-launch-1.0 glvideomixer name=m sink_1::zorder=0 ! glimagesink wpesrc location="file:///home/phil/Downloads/plunk/index.html" draw-background=0 ! m. videotestsrc ! queue ! glupload ! glcolorconvert ! m. + * ]| + * Composite WPE with a video stream in a single OpenGL scene. + * + * |[ + * gst-launch-1.0 glvideomixer name=m sink_1::zorder=0 sink_0::height=818 sink_0::width=1920 ! gtkglsink wpesrc location="file:///home/phil/Downloads/plunk/index.html" draw-background=0 ! m. uridecodebin uri="http://192.168.1.44/Sintel.2010.1080p.mkv" name=d d. ! queue ! glupload ! glcolorconvert ! m. + * ]| + * Composite WPE with a video stream, sink_0 pad properties have to match the video dimensions. + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstwpevideosrc.h" +#include "gstwpesrcbin.h" + +GST_DEBUG_CATEGORY (wpe_video_src_debug); +GST_DEBUG_CATEGORY (wpe_view_debug); + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (wpe_video_src_debug, "wpesrc", 0, "WPE Source"); + GST_DEBUG_CATEGORY_INIT (wpe_view_debug, "wpeview", 0, "WPE Threaded View"); + + gboolean result = gst_element_register (plugin, "wpevideosrc", GST_RANK_NONE, + GST_TYPE_WPE_VIDEO_SRC); + result &= gst_element_register(plugin, "wpesrc", GST_RANK_NONE, GST_TYPE_WPE_SRC); + return result; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, + wpe, "WPE src plugin", plugin_init, VERSION, GST_LICENSE, PACKAGE, + GST_PACKAGE_ORIGIN) diff --git a/ext/wpe/gstwpesrc.cpp b/ext/wpe/gstwpesrc.cpp deleted file mode 100644 index 750e354a0..000000000 --- a/ext/wpe/gstwpesrc.cpp +++ /dev/null @@ -1,805 +0,0 @@ -/* Copyright (C) <2018> Philippe Normand - * Copyright (C) <2018> Žan Doberšek - * - * 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 along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:element-wpesrc - * @title: wpesrc - * - * The wpesrc element is used to produce a video texture representing a web page - * rendered off-screen by WPE. - * - * Starting from WPEBackend-FDO 1.6.x, software rendering support is available. This - * features allows wpesrc to be used on machines without GPU, and/or for testing - * purpose. To enable it, set the `LIBGL_ALWAYS_SOFTWARE=true` environment - * variable and make sure `video/x-raw, format=BGRA` caps are negotiated by the - * wpesrc element. - * - * As the webview loading is usually not instantaneous, the wpesrc element emits - * messages indicating the load progress, in percent. The value is an estimate - * based on the total number of bytes expected to be received for a document, - * including all its possible subresources and child documents. The application - * can handle these `element` messages synchronously for instance, in order to - * display a progress bar or other visual load indicator. The load percent value - * is stored in the message structure as a double value named - * `estimated-load-progress` and the structure name is `wpe-stats`. - * - * ## Example launch lines - * - * ```shell - * gst-launch-1.0 -v wpesrc location="https://gstreamer.freedesktop.org" ! queue ! glimagesink - * ``` - * Shows the GStreamer website homepage - * - * ```shell - * LIBGL_ALWAYS_SOFTWARE=true gst-launch-1.0 -v wpesrc num-buffers=50 location="https://gstreamer.freedesktop.org" \ - * videoconvert ! pngenc ! multifilesink location=/tmp/snapshot-%05d.png - * ``` - * Saves the first 50 video frames generated for the GStreamer website as PNG files in /tmp. - * - * ```shell - * gst-play-1.0 --videosink gtkglsink wpe://https://gstreamer.freedesktop.org - * ``` - * Shows the GStreamer website homepage as played with GstPlayer in a GTK+ window. - * - * ```shell - * gst-launch-1.0 glvideomixer name=m sink_1::zorder=0 ! glimagesink wpesrc location="file:///tmp/asset.html" draw-background=0 \ - * ! m. videotestsrc ! queue ! glupload ! glcolorconvert ! m. - * ``` - * Composite WPE with a video stream in a single OpenGL scene. - * - * ```shell - * gst-launch-1.0 glvideomixer name=m sink_1::zorder=0 sink_0::height=818 sink_0::width=1920 ! gtkglsink \ - * wpesrc location="file:///tmp/asset.html" draw-background=0 ! m. - * uridecodebin uri="http://example.com/Sintel.2010.1080p.mkv" name=d d. ! queue ! glupload ! glcolorconvert ! m. - * ``` - * Composite WPE with a video stream, sink_0 pad properties have to match the video dimensions. - * - * Since: 1.16 - */ - -/* - * TODO: - * - Audio support (requires an AudioSession implementation in WebKit and a WPEBackend-fdo API for it) - * - DMABuf support (requires changes in WPEBackend-fdo to expose DMABuf planes and fds) - * - Custom EGLMemory allocator - * - Better navigation events handling (would require a new GstNavigation API) - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "gstwpesrc.h" -#include -#include -#include -#include -#include - -#include "WPEThreadedView.h" - -GST_DEBUG_CATEGORY (wpe_src_debug); -#define GST_CAT_DEFAULT wpe_src_debug - -#define DEFAULT_WIDTH 1920 -#define DEFAULT_HEIGHT 1080 -#define DEFAULT_FPS_N 30 -#define DEFAULT_FPS_D 1 - -enum -{ - PROP_0, - PROP_LOCATION, - PROP_DRAW_BACKGROUND -}; - -enum -{ - SIGNAL_CONFIGURE_WEB_VIEW, - SIGNAL_LOAD_BYTES, - LAST_SIGNAL -}; -static guint gst_wpe_src_signals[LAST_SIGNAL] = { 0 }; - -struct _GstWpeSrc -{ - GstGLBaseSrc parent; - - /* properties */ - gchar *location; - gboolean draw_background; - - GBytes *bytes; - gboolean gl_enabled; - - gint64 n_frames; /* total frames sent */ - - WPEView *view; - - GMutex lock; -}; - -#define WPE_LOCK(o) g_mutex_lock(&(o)->lock) -#define WPE_UNLOCK(o) g_mutex_unlock(&(o)->lock) - -static void gst_wpe_src_uri_handler_init (gpointer iface, gpointer data); - -#define gst_wpe_src_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstWpeSrc, gst_wpe_src, GST_TYPE_GL_BASE_SRC, - G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_wpe_src_uri_handler_init); - GST_DEBUG_CATEGORY_INIT (wpe_src_debug, "wpesrc", 0, "WPE Source");); -GST_ELEMENT_REGISTER_DEFINE (wpesrc, "wpesrc", GST_RANK_NONE, - GST_TYPE_WPE_SRC); - -#if ENABLE_SHM_BUFFER_SUPPORT -#define WPE_RAW_CAPS "; video/x-raw, " \ - "format = (string) BGRA, " \ - "width = " GST_VIDEO_SIZE_RANGE ", " \ - "height = " GST_VIDEO_SIZE_RANGE ", " \ - "framerate = " GST_VIDEO_FPS_RANGE ", " \ - "pixel-aspect-ratio = (fraction)1/1" -#else -#define WPE_RAW_CAPS "" -#endif - -#define WPE_BASIC_CAPS "video/x-raw(memory:GLMemory), " \ - "format = (string) RGBA, " \ - "width = " GST_VIDEO_SIZE_RANGE ", " \ - "height = " GST_VIDEO_SIZE_RANGE ", " \ - "framerate = " GST_VIDEO_FPS_RANGE ", " \ - "pixel-aspect-ratio = (fraction)1/1, texture-target = (string)2D" - -#define WPE_SRC_CAPS WPE_BASIC_CAPS WPE_RAW_CAPS -#define WPE_SRC_DOC_CAPS WPE_BASIC_CAPS "; video/x-raw, format = (string) BGRA" - -static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (WPE_SRC_CAPS)); - -static GstFlowReturn -gst_wpe_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, GstBuffer ** buf) -{ - GstGLBaseSrc *gl_src = GST_GL_BASE_SRC (bsrc); - GstWpeSrc *src = GST_WPE_SRC (bsrc); - GstFlowReturn ret = GST_FLOW_ERROR; - GstBuffer *locked_buffer; - GstClockTime next_time; - gint64 ts_offset = 0; - - WPE_LOCK (src); - if (src->gl_enabled) { - WPE_UNLOCK (src); - return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, create, (bsrc, offset, length, buf), ret); - } - - locked_buffer = src->view->buffer (); - if (locked_buffer == NULL) { - WPE_UNLOCK (src); - GST_ELEMENT_ERROR (src, RESOURCE, FAILED, - ("WPE View did not render a buffer"), (NULL)); - return ret; - } - *buf = gst_buffer_copy_deep (locked_buffer); - - g_object_get(gl_src, "timestamp-offset", &ts_offset, NULL); - - /* The following code mimics the behaviour of GLBaseSrc::fill */ - GST_BUFFER_TIMESTAMP (*buf) = ts_offset + gl_src->running_time; - GST_BUFFER_OFFSET (*buf) = src->n_frames; - src->n_frames++; - GST_BUFFER_OFFSET_END (*buf) = src->n_frames; - if (gl_src->out_info.fps_n) { - next_time = gst_util_uint64_scale_int (src->n_frames * GST_SECOND, - gl_src->out_info.fps_d, gl_src->out_info.fps_n); - GST_BUFFER_DURATION (*buf) = next_time - gl_src->running_time; - } else { - next_time = ts_offset; - GST_BUFFER_DURATION (*buf) = GST_CLOCK_TIME_NONE; - } - - GST_LOG_OBJECT (src, "Created buffer from SHM %" GST_PTR_FORMAT, *buf); - - gl_src->running_time = next_time; - - ret = GST_FLOW_OK; - WPE_UNLOCK (src); - return ret; -} - -static gboolean -gst_wpe_src_fill_memory (GstGLBaseSrc * bsrc, GstGLMemory * memory) -{ - GstWpeSrc *src = GST_WPE_SRC (bsrc); - const GstGLFuncs *gl; - guint tex_id; - GstEGLImage *locked_image; - - if (!gst_gl_context_check_feature (GST_GL_CONTEXT (bsrc->context), - "EGL_KHR_image_base")) { - GST_ERROR_OBJECT (src, "EGL_KHR_image_base is not supported"); - return FALSE; - } - - WPE_LOCK (src); - - gl = bsrc->context->gl_vtable; - tex_id = gst_gl_memory_get_texture_id (memory); - locked_image = src->view->image (); - - if (!locked_image) { - WPE_UNLOCK (src); - return TRUE; - } - - gl->ActiveTexture (GL_TEXTURE0 + memory->plane); - gl->BindTexture (GL_TEXTURE_2D, tex_id); - gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, - gst_egl_image_get_image (locked_image)); - gl->Flush (); - WPE_UNLOCK (src); - return TRUE; -} - -static gboolean -gst_wpe_src_start (GstWpeSrc * src) -{ - GstGLContext *context = NULL; - GstGLDisplay *display = NULL; - GstGLBaseSrc *base_src = GST_GL_BASE_SRC (src); - gboolean created_view = FALSE; - GBytes *bytes; - - GST_INFO_OBJECT (src, "Starting up"); - WPE_LOCK (src); - - if (src->gl_enabled) { - context = base_src->context; - display = base_src->display; - } - - GST_DEBUG_OBJECT (src, "Will %sfill GLMemories", src->gl_enabled ? "" : "NOT "); - - auto & thread = WPEContextThread::singleton (); - - if (!src->view) { - src->view = thread.createWPEView (src, context, display, - GST_VIDEO_INFO_WIDTH (&base_src->out_info), - GST_VIDEO_INFO_HEIGHT (&base_src->out_info)); - created_view = TRUE; - GST_DEBUG_OBJECT (src, "created view %p", src->view); - } - - if (!src->view) { - WPE_UNLOCK (src); - GST_ELEMENT_ERROR (src, RESOURCE, FAILED, - ("WPEBackend-FDO EGL display initialisation failed"), (NULL)); - return FALSE; - } - - GST_OBJECT_LOCK (src); - bytes = src->bytes; - src->bytes = NULL; - GST_OBJECT_UNLOCK (src); - - if (bytes != NULL) { - src->view->loadData (bytes); - g_bytes_unref (bytes); - } - - if (created_view) { - src->n_frames = 0; - } - WPE_UNLOCK (src); - return TRUE; -} - -static gboolean -gst_wpe_src_decide_allocation (GstBaseSrc * base_src, GstQuery * query) -{ - GstGLBaseSrc *gl_src = GST_GL_BASE_SRC (base_src); - GstWpeSrc *src = GST_WPE_SRC (base_src); - GstCapsFeatures *caps_features; - - WPE_LOCK (src); - caps_features = gst_caps_get_features (gl_src->out_caps, 0); - if (caps_features != NULL && gst_caps_features_contains (caps_features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) { - src->gl_enabled = TRUE; - } else { - src->gl_enabled = FALSE; - } - - if (src->gl_enabled) { - WPE_UNLOCK (src); - return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SRC_CLASS, decide_allocation, (base_src, query), FALSE); - } - WPE_UNLOCK (src); - return gst_wpe_src_start (src); -} - -static gboolean -gst_wpe_src_gl_start (GstGLBaseSrc * base_src) -{ - GstWpeSrc *src = GST_WPE_SRC (base_src); - return gst_wpe_src_start (src); -} - -static void -gst_wpe_src_stop_unlocked (GstWpeSrc * src) -{ - if (src->view) { - GST_DEBUG_OBJECT (src, "deleting view %p", src->view); - delete src->view; - src->view = NULL; - } -} - -static void -gst_wpe_src_gl_stop (GstGLBaseSrc * base_src) -{ - GstWpeSrc *src = GST_WPE_SRC (base_src); - - WPE_LOCK (src); - gst_wpe_src_stop_unlocked (src); - WPE_UNLOCK (src); -} - -static gboolean -gst_wpe_src_stop (GstBaseSrc * base_src) -{ - GstWpeSrc *src = GST_WPE_SRC (base_src); - - /* we can call this always, GstGLBaseSrc is smart enough to not crash if - * gst_gl_base_src_gl_start() has not been called from chaining up - * gst_wpe_src_decide_allocation() */ - if (!GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SRC_CLASS, stop, (base_src), FALSE)) - return FALSE; - - WPE_LOCK (src); - - /* if gl-enabled, gst_wpe_src_stop_unlocked() would have already been called - * inside gst_wpe_src_gl_stop() from the base class stopping the OpenGL - * context */ - if (!src->gl_enabled) - gst_wpe_src_stop_unlocked (src); - - WPE_UNLOCK (src); - return TRUE; -} - -static GstCaps * -gst_wpe_src_fixate (GstBaseSrc * base_src, GstCaps * caps) -{ - GstWpeSrc *src = GST_WPE_SRC (base_src); - GstStructure *structure; - gint width, height; - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH); - gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT); - - if (gst_structure_has_field (structure, "framerate")) - gst_structure_fixate_field_nearest_fraction (structure, "framerate", - DEFAULT_FPS_N, DEFAULT_FPS_D); - else - gst_structure_set (structure, "framerate", GST_TYPE_FRACTION, DEFAULT_FPS_N, - DEFAULT_FPS_D, NULL); - - caps = GST_BASE_SRC_CLASS (parent_class)->fixate (base_src, caps); - GST_INFO_OBJECT (base_src, "Fixated caps to %" GST_PTR_FORMAT, caps); - - if (src->view) { - gst_structure_get (structure, "width", G_TYPE_INT, &width, "height", G_TYPE_INT, &height, NULL); - src->view->resize (width, height); - } - return caps; -} - -void -gst_wpe_src_configure_web_view (GstWpeSrc * src, WebKitWebView * webview) -{ - GValue args[2] = { {0}, {0} }; - - g_value_init (&args[0], GST_TYPE_ELEMENT); - g_value_set_object (&args[0], src); - g_value_init (&args[1], G_TYPE_OBJECT); - g_value_set_object (&args[1], webview); - - g_signal_emitv (args, gst_wpe_src_signals[SIGNAL_CONFIGURE_WEB_VIEW], 0, - NULL); - - g_value_unset (&args[0]); - g_value_unset (&args[1]); -} - -static void -gst_wpe_src_load_bytes (GstWpeSrc * src, GBytes * bytes) -{ - if (src->view && GST_STATE (GST_ELEMENT_CAST (src)) > GST_STATE_NULL) { - src->view->loadData (bytes); - } else { - GST_OBJECT_LOCK (src); - if (src->bytes) - g_bytes_unref (src->bytes); - src->bytes = g_bytes_ref (bytes); - GST_OBJECT_UNLOCK (src); - } -} - -static gboolean -gst_wpe_src_set_location (GstWpeSrc * src, const gchar * location, - GError ** error) -{ - GST_OBJECT_LOCK (src); - g_free (src->location); - src->location = g_strdup (location); - GST_OBJECT_UNLOCK (src); - - if (src->view) - src->view->loadUri (location); - - return TRUE; -} - -static void -gst_wpe_src_set_draw_background (GstWpeSrc * src, gboolean draw_background) -{ - GST_OBJECT_LOCK (src); - src->draw_background = draw_background; - GST_OBJECT_UNLOCK (src); - - if (src->view) - src->view->setDrawBackground (draw_background); -} - -static void -gst_wpe_src_set_property (GObject * object, guint prop_id, const GValue * value, - GParamSpec * pspec) -{ - GstWpeSrc *src = GST_WPE_SRC (object); - - switch (prop_id) { - case PROP_LOCATION: - { - const gchar *location; - - location = g_value_get_string (value); - if (location == NULL) { - GST_WARNING_OBJECT (src, "location property cannot be NULL"); - return; - } - - if (!gst_wpe_src_set_location (src, location, NULL)) { - GST_WARNING_OBJECT (src, "badly formatted location"); - return; - } - break; - } - case PROP_DRAW_BACKGROUND: - gst_wpe_src_set_draw_background (src, g_value_get_boolean (value)); - break; - default: - break; - } -} - -static void -gst_wpe_src_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstWpeSrc *src = GST_WPE_SRC (object); - - switch (prop_id) { - case PROP_LOCATION: - GST_OBJECT_LOCK (src); - g_value_set_string (value, src->location); - GST_OBJECT_UNLOCK (src); - break; - case PROP_DRAW_BACKGROUND: - GST_OBJECT_LOCK (src); - g_value_set_boolean (value, src->draw_background); - GST_OBJECT_UNLOCK (src); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -gst_wpe_src_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - gboolean ret = FALSE; - GstWpeSrc *src = GST_WPE_SRC (parent); - - if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION) { - const gchar *key; - gint button; - gdouble x, y, delta_x, delta_y; - - GST_DEBUG_OBJECT (src, "Processing event %" GST_PTR_FORMAT, event); - if (!src->view) { - return FALSE; - } - switch (gst_navigation_event_get_type (event)) { - case GST_NAVIGATION_EVENT_KEY_PRESS: - case GST_NAVIGATION_EVENT_KEY_RELEASE: - if (gst_navigation_event_parse_key_event (event, &key)) { - /* FIXME: This is wrong... The GstNavigation API should pass - hardware-level information, not high-level keysym strings */ - uint32_t keysym = - (uint32_t) xkb_keysym_from_name (key, XKB_KEYSYM_NO_FLAGS); - struct wpe_input_keyboard_event wpe_event; - wpe_event.key_code = keysym; - wpe_event.pressed = - gst_navigation_event_get_type (event) == - GST_NAVIGATION_EVENT_KEY_PRESS; - src->view->dispatchKeyboardEvent (wpe_event); - ret = TRUE; - } - break; - case GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS: - case GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE: - if (gst_navigation_event_parse_mouse_button_event (event, &button, &x, - &y)) { - struct wpe_input_pointer_event wpe_event; - wpe_event.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); - wpe_event.type = wpe_input_pointer_event_type_button; - wpe_event.x = (int) x; - wpe_event.y = (int) y; - if (button == 1) { - wpe_event.modifiers = wpe_input_pointer_modifier_button1; - } else if (button == 2) { - wpe_event.modifiers = wpe_input_pointer_modifier_button2; - } else if (button == 3) { - wpe_event.modifiers = wpe_input_pointer_modifier_button3; - } else if (button == 4) { - wpe_event.modifiers = wpe_input_pointer_modifier_button4; - } else if (button == 5) { - wpe_event.modifiers = wpe_input_pointer_modifier_button5; - } - wpe_event.button = button; - wpe_event.state = - gst_navigation_event_get_type (event) == - GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS; - src->view->dispatchPointerEvent (wpe_event); - ret = TRUE; - } - break; - case GST_NAVIGATION_EVENT_MOUSE_MOVE: - if (gst_navigation_event_parse_mouse_move_event (event, &x, &y)) { - struct wpe_input_pointer_event wpe_event; - wpe_event.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); - wpe_event.type = wpe_input_pointer_event_type_motion; - wpe_event.x = (int) x; - wpe_event.y = (int) y; - src->view->dispatchPointerEvent (wpe_event); - ret = TRUE; - } - break; - case GST_NAVIGATION_EVENT_MOUSE_SCROLL: - if (gst_navigation_event_parse_mouse_scroll_event (event, &x, &y, - &delta_x, &delta_y)) { -#if WPE_CHECK_VERSION(1, 6, 0) - struct wpe_input_axis_2d_event wpe_event; - if (delta_x) { - wpe_event.x_axis = delta_x; - } else { - wpe_event.y_axis = delta_y; - } - wpe_event.base.time = - GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); - wpe_event.base.type = - static_cast < wpe_input_axis_event_type > - (wpe_input_axis_event_type_mask_2d | - wpe_input_axis_event_type_motion_smooth); - wpe_event.base.x = (int) x; - wpe_event.base.y = (int) y; - src->view->dispatchAxisEvent (wpe_event.base); -#else - struct wpe_input_axis_event wpe_event; - if (delta_x) { - wpe_event.axis = 1; - wpe_event.value = delta_x; - } else { - wpe_event.axis = 0; - wpe_event.value = delta_y; - } - wpe_event.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); - wpe_event.type = wpe_input_axis_event_type_motion; - wpe_event.x = (int) x; - wpe_event.y = (int) y; - src->view->dispatchAxisEvent (wpe_event); -#endif - ret = TRUE; - } - break; - default: - break; - } - /* FIXME: No touch events handling support in GstNavigation */ - } - - if (!ret) { - ret = gst_pad_event_default (pad, parent, event); - } else { - gst_event_unref (event); - } - return ret; -} - -static void -gst_wpe_src_init (GstWpeSrc * src) -{ - GstPad *pad = gst_element_get_static_pad (GST_ELEMENT_CAST (src), "src"); - - gst_pad_set_event_function (pad, gst_wpe_src_event); - gst_object_unref (pad); - - src->draw_background = TRUE; - - gst_base_src_set_live (GST_BASE_SRC_CAST (src), TRUE); - - g_mutex_init (&src->lock); -} - -static void -gst_wpe_src_finalize (GObject * object) -{ - GstWpeSrc *src = GST_WPE_SRC (object); - - g_free (src->location); - g_clear_pointer (&src->bytes, g_bytes_unref); - g_mutex_clear (&src->lock); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static GstURIType -gst_wpe_src_uri_get_type (GType) -{ - return GST_URI_SRC; -} - -static const gchar *const * -gst_wpe_src_get_protocols (GType) -{ - static const char *protocols[] = { "wpe", NULL }; - return protocols; -} - -static gchar * -gst_wpe_src_get_uri (GstURIHandler * handler) -{ - GstWpeSrc *src = GST_WPE_SRC (handler); - gchar *uri; - - GST_OBJECT_LOCK (src); - uri = g_strdup_printf ("wpe://%s", src->location); - GST_OBJECT_UNLOCK (src); - - return uri; -} - -static gboolean -gst_wpe_src_set_uri (GstURIHandler * handler, const gchar * uri, - GError ** error) -{ - GstWpeSrc *src = GST_WPE_SRC (handler); - - return gst_wpe_src_set_location (src, uri + 6, error); -} - -static void -gst_wpe_src_uri_handler_init (gpointer iface_ptr, gpointer data) -{ - GstURIHandlerInterface *iface = (GstURIHandlerInterface *) iface_ptr; - - iface->get_type = gst_wpe_src_uri_get_type; - iface->get_protocols = gst_wpe_src_get_protocols; - iface->get_uri = gst_wpe_src_get_uri; - iface->set_uri = gst_wpe_src_set_uri; -} - -static void -gst_wpe_src_class_init (GstWpeSrcClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GstGLBaseSrcClass *gl_base_src_class = GST_GL_BASE_SRC_CLASS (klass); - GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass); - GstPadTemplate *tmpl; - GstCaps *doc_caps; - - gobject_class->set_property = gst_wpe_src_set_property; - gobject_class->get_property = gst_wpe_src_get_property; - gobject_class->finalize = gst_wpe_src_finalize; - - g_object_class_install_property (gobject_class, PROP_LOCATION, - g_param_spec_string ("location", "location", - "The URL to display", - "", (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - g_object_class_install_property (gobject_class, PROP_DRAW_BACKGROUND, - g_param_spec_boolean ("draw-background", "Draws the background", - "Whether to draw the WebView background", TRUE, - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - gst_element_class_set_static_metadata (gstelement_class, - "WPE source", "Source/Video", - "Creates a video stream from a WPE browser", - "Philippe Normand , Žan Doberšek "); - - tmpl = gst_static_pad_template_get (&src_factory); - gst_element_class_add_pad_template (gstelement_class, tmpl); - - base_src_class->fixate = GST_DEBUG_FUNCPTR (gst_wpe_src_fixate); - base_src_class->create = GST_DEBUG_FUNCPTR (gst_wpe_src_create); - base_src_class->decide_allocation = GST_DEBUG_FUNCPTR (gst_wpe_src_decide_allocation); - base_src_class->stop = GST_DEBUG_FUNCPTR (gst_wpe_src_stop); - - gl_base_src_class->supported_gl_api = - static_cast < GstGLAPI > - (GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2); - gl_base_src_class->gl_start = GST_DEBUG_FUNCPTR (gst_wpe_src_gl_start); - gl_base_src_class->gl_stop = GST_DEBUG_FUNCPTR (gst_wpe_src_gl_stop); - gl_base_src_class->fill_gl_memory = - GST_DEBUG_FUNCPTR (gst_wpe_src_fill_memory); - - doc_caps = gst_caps_from_string (WPE_SRC_DOC_CAPS); - gst_pad_template_set_documentation_caps (tmpl, doc_caps); - gst_clear_caps (&doc_caps); - - /** - * GstWpeSrc::configure-web-view: - * @src: the object which received the signal - * @webview: the webView - * - * Allow application to configure the webView settings. - */ - gst_wpe_src_signals[SIGNAL_CONFIGURE_WEB_VIEW] = - g_signal_new ("configure-web-view", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); - - /** - * GstWpeSrc::load-bytes: - * @src: the object which received the signal - * @bytes: the GBytes data to load - * - * Load the specified bytes into the internal webView. - */ - gst_wpe_src_signals[SIGNAL_LOAD_BYTES] = - g_signal_new_class_handler ("load-bytes", G_TYPE_FROM_CLASS (klass), - static_cast < GSignalFlags > (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), - G_CALLBACK (gst_wpe_src_load_bytes), NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_BYTES); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - - - return GST_ELEMENT_REGISTER (wpesrc, plugin); -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - wpe, "WPE src plugin", plugin_init, VERSION, GST_LICENSE, PACKAGE, - GST_PACKAGE_ORIGIN) diff --git a/ext/wpe/gstwpesrc.h b/ext/wpe/gstwpesrc.h deleted file mode 100644 index 1368077eb..000000000 --- a/ext/wpe/gstwpesrc.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) <2018> Philippe Normand - * Copyright (C) <2018> Žan Doberšek - * - * 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 along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#pragma once - -#include -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_WPE_SRC (gst_wpe_src_get_type()) -#define GST_WPE_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WPE_SRC,GstWpeSrc)) -#define GST_WPE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_WPE_SRC,GstWpeSrcClass)) -#define GST_IS_WPE_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WPE_SRC)) -#define GST_IS_WPE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_WPE_SRC)) - -typedef struct _GstWpeSrc GstWpeSrc; -typedef struct _GstWpeSrcClass GstWpeSrcClass; - -struct _GstWpeSrcClass -{ - GstGLBaseSrcClass parent_class; -}; - -GType gst_wpe_src_get_type (void); - -GST_ELEMENT_REGISTER_DECLARE (wpesrc); - -void gst_wpe_src_configure_web_view(GstWpeSrc * src, WebKitWebView * webview); - -G_END_DECLS diff --git a/ext/wpe/gstwpesrcbin.cpp b/ext/wpe/gstwpesrcbin.cpp new file mode 100644 index 000000000..58349eeef --- /dev/null +++ b/ext/wpe/gstwpesrcbin.cpp @@ -0,0 +1,215 @@ +/* Copyright (C) <2018> Philippe Normand + * Copyright (C) <2018> Žan Doberšek + * + * 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 along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-wpesrc + * @title: wpesrc + * + * FIXME The wpesrc element is used to produce a video texture representing a + * web page rendered off-screen by WPE. + * + */ + +#include "gstwpesrcbin.h" +#include "gstwpevideosrc.h" +#include "WPEThreadedView.h" + +struct _GstWpeSrc +{ + GstBin parent; + + GstElement *video_src; +}; + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_DRAW_BACKGROUND +}; + +enum +{ + SIGNAL_LOAD_BYTES, + LAST_SIGNAL +}; + +static guint gst_wpe_video_src_signals[LAST_SIGNAL] = { 0 }; + +static void gst_wpe_src_uri_handler_init (gpointer iface, gpointer data); + +#define gst_wpe_src_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstWpeSrc, gst_wpe_src, GST_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_wpe_src_uri_handler_init)); + +static GstStaticPadTemplate video_src_factory = +GST_STATIC_PAD_TEMPLATE ("video_src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw(memory:GLMemory), " + "format = (string) RGBA, " + "width = " GST_VIDEO_SIZE_RANGE ", " + "height = " GST_VIDEO_SIZE_RANGE ", " + "framerate = " GST_VIDEO_FPS_RANGE ", " + "pixel-aspect-ratio = (fraction)1/1," + "texture-target = (string)2D" +#if ENABLE_SHM_BUFFER_SUPPORT + "; video/x-raw, " + "format = (string) BGRA, " + "width = " GST_VIDEO_SIZE_RANGE ", " + "height = " GST_VIDEO_SIZE_RANGE ", " + "framerate = " GST_VIDEO_FPS_RANGE ", " + "pixel-aspect-ratio = (fraction)1/1" +#endif + )); + +static void +gst_wpe_src_load_bytes (GstWpeVideoSrc * src, GBytes * bytes) +{ + GstWpeSrc *self = GST_WPE_SRC (src); + + if (self->video_src) + g_signal_emit_by_name (self->video_src, "load-bytes", bytes, NULL); +} + +static void +gst_wpe_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWpeSrc *self = GST_WPE_SRC (object); + + if (self->video_src) + g_object_get_property (G_OBJECT (self->video_src), pspec->name, value); +} + +static void +gst_wpe_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstWpeSrc *self = GST_WPE_SRC (object); + + if (self->video_src) + g_object_set_property (G_OBJECT (self->video_src), pspec->name, value); +} + +static GstURIType +gst_wpe_src_uri_get_type (GType) +{ + return GST_URI_SRC; +} + +static const gchar *const * +gst_wpe_src_get_protocols (GType) +{ + static const char *protocols[] = { "wpe", NULL }; + return protocols; +} + +static gchar * +gst_wpe_src_get_uri (GstURIHandler * handler) +{ + GstWpeSrc *src = GST_WPE_SRC (handler); + const gchar *location; + g_object_get (src->video_src, "location", &location, NULL); + return g_strdup_printf ("wpe://%s", location); +} + +static gboolean +gst_wpe_src_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** error) +{ + GstWpeSrc *src = GST_WPE_SRC (handler); + + g_object_set (src->video_src, "location", uri + 6, NULL); + return TRUE; +} + +static void +gst_wpe_src_uri_handler_init (gpointer iface_ptr, gpointer data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) iface_ptr; + + iface->get_type = gst_wpe_src_uri_get_type; + iface->get_protocols = gst_wpe_src_get_protocols; + iface->get_uri = gst_wpe_src_get_uri; + iface->set_uri = gst_wpe_src_set_uri; +} + +static void +gst_wpe_src_init (GstWpeSrc * src) +{ + src->video_src = gst_element_factory_make ("wpevideosrc", NULL); + + gst_bin_add (GST_BIN_CAST (src), src->video_src); + + GstPad *pad = + gst_element_get_static_pad (GST_ELEMENT_CAST (src->video_src), "src"); + + GstPad *ghost_pad = gst_ghost_pad_new_from_template ("video_src", pad, + gst_static_pad_template_get (&video_src_factory)); + GstProxyPad *proxy_pad = + gst_proxy_pad_get_internal (GST_PROXY_PAD (ghost_pad)); + gst_pad_set_active (GST_PAD_CAST (proxy_pad), TRUE); + gst_object_unref (proxy_pad); + + gst_element_add_pad (GST_ELEMENT_CAST (src), GST_PAD_CAST (ghost_pad)); + + gst_object_unref (pad); +} + +static void +gst_wpe_src_class_init (GstWpeSrcClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gst_wpe_src_set_property; + gobject_class->get_property = gst_wpe_src_get_property; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "location", "The URL to display", "", + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_DRAW_BACKGROUND, + g_param_spec_boolean ("draw-background", "Draws the background", + "Whether to draw the WebView background", TRUE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_set_static_metadata (element_class, "WPE source", + "Source/Video", "Creates a video stream from a WPE browser", + "Philippe Normand , Žan Doberšek " + ""); + + /** + * GstWpeSrc::load-bytes: + * @src: the object which received the signal + * @bytes: the GBytes data to load + * + * Load the specified bytes into the internal webView. + */ + gst_wpe_video_src_signals[SIGNAL_LOAD_BYTES] = + g_signal_new_class_handler ("load-bytes", G_TYPE_FROM_CLASS (klass), + static_cast < GSignalFlags > (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), + G_CALLBACK (gst_wpe_src_load_bytes), NULL, NULL, NULL, G_TYPE_NONE, 1, + G_TYPE_BYTES); + + gst_element_class_set_static_metadata (element_class, "WPE source", + "Source/Video", "Creates a video stream from a WPE browser", + "Philippe Normand , Žan Doberšek " + ""); + + gst_element_class_add_static_pad_template (element_class, &video_src_factory); +} diff --git a/ext/wpe/gstwpesrcbin.h b/ext/wpe/gstwpesrcbin.h new file mode 100644 index 000000000..87b0aea21 --- /dev/null +++ b/ext/wpe/gstwpesrcbin.h @@ -0,0 +1,43 @@ +/* Copyright (C) <2018> Philippe Normand + * Copyright (C) <2018> Žan Doberšek + * + * 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 along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#pragma once + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_WPE_SRC (gst_wpe_src_get_type()) +#define GST_WPE_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WPE_SRC,GstWpeSrc)) +#define GST_WPE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_WPE_SRC,GstWpeSrcClass)) +#define GST_IS_WPE_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WPE_SRC)) +#define GST_IS_WPE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_WPE_SRC)) + +typedef struct _GstWpeSrc GstWpeSrc; +typedef struct _GstWpeSrcClass GstWpeSrcClass; + +struct _GstWpeSrcClass +{ + GstBinClass parent_class; +}; + +GType gst_wpe_src_get_type (void); + +G_END_DECLS diff --git a/ext/wpe/gstwpevideosrc.cpp b/ext/wpe/gstwpevideosrc.cpp new file mode 100644 index 000000000..7d7556fb5 --- /dev/null +++ b/ext/wpe/gstwpevideosrc.cpp @@ -0,0 +1,738 @@ +/* Copyright (C) <2018> Philippe Normand + * Copyright (C) <2018> Žan Doberšek + * + * 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 along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-wpevideosrc + * @title: wpevideosrc + * + * The wpevideosrc element is used to produce a video texture representing a web page + * rendered off-screen by WPE. + * + * Starting from WPEBackend-FDO 1.6.x, software rendering support is available. This + * features allows wpevideosrc to be used on machines without GPU, and/or for testing + * purpose. To enable it, set the `LIBGL_ALWAYS_SOFTWARE=true` environment + * variable and make sure `video/x-raw, format=BGRA` caps are negotiated by the + * wpevideosrc element. + * + * As the webview loading is usually not instantaneous, the wpevideosrc element emits + * messages indicating the load progress, in percent. The value is an estimate + * based on the total number of bytes expected to be received for a document, + * including all its possible subresources and child documents. The application + * can handle these `element` messages synchronously for instance, in order to + * display a progress bar or other visual load indicator. The load percent value + * is stored in the message structure as a double value named + * `estimated-load-progress` and the structure name is `wpe-stats`. + * + * ## Example launch lines + * + * ```shell + * gst-launch-1.0 -v wpevideosrc location="https://gstreamer.freedesktop.org" ! queue ! glimagesink + * ``` + * Shows the GStreamer website homepage + * + * ```shell + * LIBGL_ALWAYS_SOFTWARE=true gst-launch-1.0 -v wpevideosrc num-buffers=50 location="https://gstreamer.freedesktop.org" \ + * videoconvert ! pngenc ! multifilesink location=/tmp/snapshot-%05d.png + * ``` + * Saves the first 50 video frames generated for the GStreamer website as PNG files in /tmp. + * + * ```shell + * gst-play-1.0 --videosink gtkglsink wpe://https://gstreamer.freedesktop.org + * ``` + * Shows the GStreamer website homepage as played with GstPlayer in a GTK+ window. + * + * ```shell + * gst-launch-1.0 glvideomixer name=m sink_1::zorder=0 ! glimagesink wpevideosrc location="file:///tmp/asset.html" draw-background=0 \ + * ! m. videotestsrc ! queue ! glupload ! glcolorconvert ! m. + * ``` + * Composite WPE with a video stream in a single OpenGL scene. + * + * ```shell + * gst-launch-1.0 glvideomixer name=m sink_1::zorder=0 sink_0::height=818 sink_0::width=1920 ! gtkglsink \ + * wpevideosrc location="file:///tmp/asset.html" draw-background=0 ! m. + * uridecodebin uri="http://example.com/Sintel.2010.1080p.mkv" name=d d. ! queue ! glupload ! glcolorconvert ! m. + * ``` + * Composite WPE with a video stream, sink_0 pad properties have to match the video dimensions. + * + * Since: 1.16 + */ + +/* + * TODO: + * - Audio support (requires an AudioSession implementation in WebKit and a WPEBackend-fdo API for it) + * - DMABuf support (requires changes in WPEBackend-fdo to expose DMABuf planes and fds) + * - Custom EGLMemory allocator + * - Better navigation events handling (would require a new GstNavigation API) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstwpevideosrc.h" +#include +#include +#include +#include +#include + +#include "WPEThreadedView.h" + +#define DEFAULT_WIDTH 1920 +#define DEFAULT_HEIGHT 1080 +#define DEFAULT_FPS_N 30 +#define DEFAULT_FPS_D 1 + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_DRAW_BACKGROUND +}; + +enum +{ + SIGNAL_CONFIGURE_WEB_VIEW, + SIGNAL_LOAD_BYTES, + LAST_SIGNAL +}; +static guint gst_wpe_video_src_signals[LAST_SIGNAL] = { 0 }; + +struct _GstWpeVideoSrc +{ + GstGLBaseSrc parent; + + /* properties */ + gchar *location; + gboolean draw_background; + + GBytes *bytes; + gboolean gl_enabled; + + gint64 n_frames; /* total frames sent */ + + WPEView *view; + + GMutex lock; +}; + +#define WPE_LOCK(o) g_mutex_lock(&(o)->lock) +#define WPE_UNLOCK(o) g_mutex_unlock(&(o)->lock) + +#define gst_wpe_video_src_parent_class parent_class +G_DEFINE_TYPE(GstWpeVideoSrc, gst_wpe_video_src, GST_TYPE_GL_BASE_SRC); + +#if ENABLE_SHM_BUFFER_SUPPORT +#define WPE_RAW_CAPS "; video/x-raw, " \ + "format = (string) BGRA, " \ + "width = " GST_VIDEO_SIZE_RANGE ", " \ + "height = " GST_VIDEO_SIZE_RANGE ", " \ + "framerate = " GST_VIDEO_FPS_RANGE ", " \ + "pixel-aspect-ratio = (fraction)1/1" +#else +#define WPE_RAW_CAPS "" +#endif + +#define WPE_BASIC_CAPS "video/x-raw(memory:GLMemory), " \ + "format = (string) RGBA, " \ + "width = " GST_VIDEO_SIZE_RANGE ", " \ + "height = " GST_VIDEO_SIZE_RANGE ", " \ + "framerate = " GST_VIDEO_FPS_RANGE ", " \ + "pixel-aspect-ratio = (fraction)1/1, texture-target = (string)2D" + +#define WPE_VIDEO_SRC_CAPS WPE_BASIC_CAPS WPE_RAW_CAPS +#define WPE_VIDEO_SRC_DOC_CAPS WPE_BASIC_CAPS "; video/x-raw, format = (string) BGRA" + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (WPE_VIDEO_SRC_CAPS)); + +static GstFlowReturn +gst_wpe_video_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, GstBuffer ** buf) +{ + GstGLBaseSrc *gl_src = GST_GL_BASE_SRC (bsrc); + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (bsrc); + GstFlowReturn ret = GST_FLOW_ERROR; + GstBuffer *locked_buffer; + GstClockTime next_time; + gint64 ts_offset = 0; + + WPE_LOCK (src); + if (src->gl_enabled) { + WPE_UNLOCK (src); + return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS, create, (bsrc, offset, length, buf), ret); + } + + locked_buffer = src->view->buffer (); + if (locked_buffer == NULL) { + WPE_UNLOCK (src); + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, + ("WPE View did not render a buffer"), (NULL)); + return ret; + } + *buf = gst_buffer_copy_deep (locked_buffer); + + g_object_get(gl_src, "timestamp-offset", &ts_offset, NULL); + + /* The following code mimics the behaviour of GLBaseSrc::fill */ + GST_BUFFER_TIMESTAMP (*buf) = ts_offset + gl_src->running_time; + GST_BUFFER_OFFSET (*buf) = src->n_frames; + src->n_frames++; + GST_BUFFER_OFFSET_END (*buf) = src->n_frames; + if (gl_src->out_info.fps_n) { + next_time = gst_util_uint64_scale_int (src->n_frames * GST_SECOND, + gl_src->out_info.fps_d, gl_src->out_info.fps_n); + GST_BUFFER_DURATION (*buf) = next_time - gl_src->running_time; + } else { + next_time = ts_offset; + GST_BUFFER_DURATION (*buf) = GST_CLOCK_TIME_NONE; + } + + GST_LOG_OBJECT (src, "Created buffer from SHM %" GST_PTR_FORMAT, *buf); + + gl_src->running_time = next_time; + + ret = GST_FLOW_OK; + WPE_UNLOCK (src); + return ret; +} + +static gboolean +gst_wpe_video_src_fill_memory (GstGLBaseSrc * bsrc, GstGLMemory * memory) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (bsrc); + const GstGLFuncs *gl; + guint tex_id; + GstEGLImage *locked_image; + + if (!gst_gl_context_check_feature (GST_GL_CONTEXT (bsrc->context), + "EGL_KHR_image_base")) { + GST_ERROR_OBJECT (src, "EGL_KHR_image_base is not supported"); + return FALSE; + } + + WPE_LOCK (src); + + gl = bsrc->context->gl_vtable; + tex_id = gst_gl_memory_get_texture_id (memory); + locked_image = src->view->image (); + + if (!locked_image) { + WPE_UNLOCK (src); + return TRUE; + } + + gl->ActiveTexture (GL_TEXTURE0 + memory->plane); + gl->BindTexture (GL_TEXTURE_2D, tex_id); + gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, + gst_egl_image_get_image (locked_image)); + gl->Flush (); + WPE_UNLOCK (src); + return TRUE; +} + +static gboolean +gst_wpe_video_src_start (GstWpeVideoSrc * src) +{ + GstGLContext *context = NULL; + GstGLDisplay *display = NULL; + GstGLBaseSrc *base_src = GST_GL_BASE_SRC (src); + gboolean created_view = FALSE; + GBytes *bytes; + + GST_INFO_OBJECT (src, "Starting up"); + WPE_LOCK (src); + + if (src->gl_enabled) { + context = base_src->context; + display = base_src->display; + } + + GST_DEBUG_OBJECT (src, "Will %sfill GLMemories", src->gl_enabled ? "" : "NOT "); + + auto & thread = WPEContextThread::singleton (); + + if (!src->view) { + src->view = thread.createWPEView (src, context, display, + GST_VIDEO_INFO_WIDTH (&base_src->out_info), + GST_VIDEO_INFO_HEIGHT (&base_src->out_info)); + created_view = TRUE; + GST_DEBUG_OBJECT (src, "created view %p", src->view); + } + + if (!src->view) { + WPE_UNLOCK (src); + GST_ELEMENT_ERROR (src, RESOURCE, FAILED, + ("WPEBackend-FDO EGL display initialisation failed"), (NULL)); + return FALSE; + } + + GST_OBJECT_LOCK (src); + bytes = src->bytes; + src->bytes = NULL; + GST_OBJECT_UNLOCK (src); + + if (bytes != NULL) { + src->view->loadData (bytes); + g_bytes_unref (bytes); + } + + if (created_view) { + src->n_frames = 0; + } + WPE_UNLOCK (src); + return TRUE; +} + +static gboolean +gst_wpe_video_src_decide_allocation (GstBaseSrc * base_src, GstQuery * query) +{ + GstGLBaseSrc *gl_src = GST_GL_BASE_SRC (base_src); + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (base_src); + GstCapsFeatures *caps_features; + + WPE_LOCK (src); + caps_features = gst_caps_get_features (gl_src->out_caps, 0); + if (caps_features != NULL && gst_caps_features_contains (caps_features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) { + src->gl_enabled = TRUE; + } else { + src->gl_enabled = FALSE; + } + + if (src->gl_enabled) { + WPE_UNLOCK (src); + return GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SRC_CLASS, decide_allocation, (base_src, query), FALSE); + } + WPE_UNLOCK (src); + return gst_wpe_video_src_start (src); +} + +static gboolean +gst_wpe_video_src_gl_start (GstGLBaseSrc * base_src) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (base_src); + return gst_wpe_video_src_start (src); +} + +static void +gst_wpe_video_src_stop_unlocked (GstWpeVideoSrc * src) +{ + if (src->view) { + GST_DEBUG_OBJECT (src, "deleting view %p", src->view); + delete src->view; + src->view = NULL; + } +} + +static void +gst_wpe_video_src_gl_stop (GstGLBaseSrc * base_src) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (base_src); + + WPE_LOCK (src); + gst_wpe_video_src_stop_unlocked (src); + WPE_UNLOCK (src); +} + +static gboolean +gst_wpe_video_src_stop (GstBaseSrc * base_src) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (base_src); + + /* we can call this always, GstGLBaseSrc is smart enough to not crash if + * gst_gl_base_src_gl_start() has not been called from chaining up + * gst_wpe_video_src_decide_allocation() */ + if (!GST_CALL_PARENT_WITH_DEFAULT(GST_BASE_SRC_CLASS, stop, (base_src), FALSE)) + return FALSE; + + WPE_LOCK (src); + + /* if gl-enabled, gst_wpe_video_src_stop_unlocked() would have already been called + * inside gst_wpe_video_src_gl_stop() from the base class stopping the OpenGL + * context */ + if (!src->gl_enabled) + gst_wpe_video_src_stop_unlocked (src); + + WPE_UNLOCK (src); + return TRUE; +} + +static GstCaps * +gst_wpe_video_src_fixate (GstBaseSrc * base_src, GstCaps * caps) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (base_src); + GstStructure *structure; + gint width, height; + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH); + gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT); + + if (gst_structure_has_field (structure, "framerate")) + gst_structure_fixate_field_nearest_fraction (structure, "framerate", + DEFAULT_FPS_N, DEFAULT_FPS_D); + else + gst_structure_set (structure, "framerate", GST_TYPE_FRACTION, DEFAULT_FPS_N, + DEFAULT_FPS_D, NULL); + + caps = GST_BASE_SRC_CLASS (parent_class)->fixate (base_src, caps); + GST_INFO_OBJECT (base_src, "Fixated caps to %" GST_PTR_FORMAT, caps); + + if (src->view) { + gst_structure_get (structure, "width", G_TYPE_INT, &width, "height", G_TYPE_INT, &height, NULL); + src->view->resize (width, height); + } + return caps; +} + +void +gst_wpe_video_src_configure_web_view (GstWpeVideoSrc * src, WebKitWebView * webview) +{ + GValue args[2] = { {0}, {0} }; + + g_value_init (&args[0], GST_TYPE_ELEMENT); + g_value_set_object (&args[0], src); + g_value_init (&args[1], G_TYPE_OBJECT); + g_value_set_object (&args[1], webview); + + g_signal_emitv (args, gst_wpe_video_src_signals[SIGNAL_CONFIGURE_WEB_VIEW], 0, + NULL); + + g_value_unset (&args[0]); + g_value_unset (&args[1]); +} + +static void +gst_wpe_video_src_load_bytes (GstWpeVideoSrc * src, GBytes * bytes) +{ + if (src->view && GST_STATE (GST_ELEMENT_CAST (src)) > GST_STATE_NULL) { + src->view->loadData (bytes); + } else { + GST_OBJECT_LOCK (src); + if (src->bytes) + g_bytes_unref (src->bytes); + src->bytes = g_bytes_ref (bytes); + GST_OBJECT_UNLOCK (src); + } +} + +static gboolean +gst_wpe_video_src_set_location (GstWpeVideoSrc * src, const gchar * location, + GError ** error) +{ + GST_OBJECT_LOCK (src); + g_free (src->location); + src->location = g_strdup (location); + GST_OBJECT_UNLOCK (src); + + if (src->view) + src->view->loadUri (location); + + return TRUE; +} + +static void +gst_wpe_video_src_set_draw_background (GstWpeVideoSrc * src, gboolean draw_background) +{ + GST_OBJECT_LOCK (src); + src->draw_background = draw_background; + GST_OBJECT_UNLOCK (src); + + if (src->view) + src->view->setDrawBackground (draw_background); +} + +static void +gst_wpe_video_src_set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (object); + + switch (prop_id) { + case PROP_LOCATION: + { + const gchar *location; + + location = g_value_get_string (value); + if (location == NULL) { + GST_WARNING_OBJECT (src, "location property cannot be NULL"); + return; + } + + if (!gst_wpe_video_src_set_location (src, location, NULL)) { + GST_WARNING_OBJECT (src, "badly formatted location"); + return; + } + break; + } + case PROP_DRAW_BACKGROUND: + gst_wpe_video_src_set_draw_background (src, g_value_get_boolean (value)); + break; + default: + break; + } +} + +static void +gst_wpe_video_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (object); + + switch (prop_id) { + case PROP_LOCATION: + GST_OBJECT_LOCK (src); + g_value_set_string (value, src->location); + GST_OBJECT_UNLOCK (src); + break; + case PROP_DRAW_BACKGROUND: + GST_OBJECT_LOCK (src); + g_value_set_boolean (value, src->draw_background); + GST_OBJECT_UNLOCK (src); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_wpe_video_src_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + gboolean ret = FALSE; + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (parent); + + if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION) { + const gchar *key; + gint button; + gdouble x, y, delta_x, delta_y; + + GST_DEBUG_OBJECT (src, "Processing event %" GST_PTR_FORMAT, event); + if (!src->view) { + return FALSE; + } + switch (gst_navigation_event_get_type (event)) { + case GST_NAVIGATION_EVENT_KEY_PRESS: + case GST_NAVIGATION_EVENT_KEY_RELEASE: + if (gst_navigation_event_parse_key_event (event, &key)) { + /* FIXME: This is wrong... The GstNavigation API should pass + hardware-level information, not high-level keysym strings */ + uint32_t keysym = + (uint32_t) xkb_keysym_from_name (key, XKB_KEYSYM_NO_FLAGS); + struct wpe_input_keyboard_event wpe_event; + wpe_event.key_code = keysym; + wpe_event.pressed = + gst_navigation_event_get_type (event) == + GST_NAVIGATION_EVENT_KEY_PRESS; + src->view->dispatchKeyboardEvent (wpe_event); + ret = TRUE; + } + break; + case GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS: + case GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE: + if (gst_navigation_event_parse_mouse_button_event (event, &button, &x, + &y)) { + struct wpe_input_pointer_event wpe_event; + wpe_event.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); + wpe_event.type = wpe_input_pointer_event_type_button; + wpe_event.x = (int) x; + wpe_event.y = (int) y; + if (button == 1) { + wpe_event.modifiers = wpe_input_pointer_modifier_button1; + } else if (button == 2) { + wpe_event.modifiers = wpe_input_pointer_modifier_button2; + } else if (button == 3) { + wpe_event.modifiers = wpe_input_pointer_modifier_button3; + } else if (button == 4) { + wpe_event.modifiers = wpe_input_pointer_modifier_button4; + } else if (button == 5) { + wpe_event.modifiers = wpe_input_pointer_modifier_button5; + } + wpe_event.button = button; + wpe_event.state = + gst_navigation_event_get_type (event) == + GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS; + src->view->dispatchPointerEvent (wpe_event); + ret = TRUE; + } + break; + case GST_NAVIGATION_EVENT_MOUSE_MOVE: + if (gst_navigation_event_parse_mouse_move_event (event, &x, &y)) { + struct wpe_input_pointer_event wpe_event; + wpe_event.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); + wpe_event.type = wpe_input_pointer_event_type_motion; + wpe_event.x = (int) x; + wpe_event.y = (int) y; + src->view->dispatchPointerEvent (wpe_event); + ret = TRUE; + } + break; + case GST_NAVIGATION_EVENT_MOUSE_SCROLL: + if (gst_navigation_event_parse_mouse_scroll_event (event, &x, &y, + &delta_x, &delta_y)) { +#if WPE_CHECK_VERSION(1, 6, 0) + struct wpe_input_axis_2d_event wpe_event; + if (delta_x) { + wpe_event.x_axis = delta_x; + } else { + wpe_event.y_axis = delta_y; + } + wpe_event.base.time = + GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); + wpe_event.base.type = + static_cast < wpe_input_axis_event_type > + (wpe_input_axis_event_type_mask_2d | + wpe_input_axis_event_type_motion_smooth); + wpe_event.base.x = (int) x; + wpe_event.base.y = (int) y; + src->view->dispatchAxisEvent (wpe_event.base); +#else + struct wpe_input_axis_event wpe_event; + if (delta_x) { + wpe_event.axis = 1; + wpe_event.value = delta_x; + } else { + wpe_event.axis = 0; + wpe_event.value = delta_y; + } + wpe_event.time = GST_TIME_AS_MSECONDS (GST_EVENT_TIMESTAMP (event)); + wpe_event.type = wpe_input_axis_event_type_motion; + wpe_event.x = (int) x; + wpe_event.y = (int) y; + src->view->dispatchAxisEvent (wpe_event); +#endif + ret = TRUE; + } + break; + default: + break; + } + /* FIXME: No touch events handling support in GstNavigation */ + } + + if (!ret) { + ret = gst_pad_event_default (pad, parent, event); + } else { + gst_event_unref (event); + } + return ret; +} + +static void +gst_wpe_video_src_init (GstWpeVideoSrc * src) +{ + GstPad *pad = gst_element_get_static_pad (GST_ELEMENT_CAST (src), "src"); + + gst_pad_set_event_function (pad, gst_wpe_video_src_event); + gst_object_unref (pad); + + src->draw_background = TRUE; + + gst_base_src_set_live (GST_BASE_SRC_CAST (src), TRUE); + + g_mutex_init (&src->lock); +} + +static void +gst_wpe_video_src_finalize (GObject * object) +{ + GstWpeVideoSrc *src = GST_WPE_VIDEO_SRC (object); + + g_free (src->location); + g_clear_pointer (&src->bytes, g_bytes_unref); + g_mutex_clear (&src->lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_wpe_video_src_class_init (GstWpeVideoSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GstGLBaseSrcClass *gl_base_src_class = GST_GL_BASE_SRC_CLASS (klass); + GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass); + GstPadTemplate *tmpl; + GstCaps *doc_caps; + + gobject_class->set_property = gst_wpe_video_src_set_property; + gobject_class->get_property = gst_wpe_video_src_get_property; + gobject_class->finalize = gst_wpe_video_src_finalize; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "location", + "The URL to display", + "", (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_DRAW_BACKGROUND, + g_param_spec_boolean ("draw-background", "Draws the background", + "Whether to draw the WebView background", TRUE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_set_static_metadata (gstelement_class, + "WPE source", "Source/Video", + "Creates a video stream from a WPE browser", + "Philippe Normand , Žan Doberšek "); + + tmpl = gst_static_pad_template_get (&src_factory); + gst_element_class_add_pad_template (gstelement_class, tmpl); + + base_src_class->fixate = GST_DEBUG_FUNCPTR (gst_wpe_video_src_fixate); + base_src_class->create = GST_DEBUG_FUNCPTR (gst_wpe_video_src_create); + base_src_class->decide_allocation = GST_DEBUG_FUNCPTR (gst_wpe_video_src_decide_allocation); + base_src_class->stop = GST_DEBUG_FUNCPTR (gst_wpe_video_src_stop); + + gl_base_src_class->supported_gl_api = + static_cast < GstGLAPI > + (GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2); + gl_base_src_class->gl_start = GST_DEBUG_FUNCPTR (gst_wpe_video_src_gl_start); + gl_base_src_class->gl_stop = GST_DEBUG_FUNCPTR (gst_wpe_video_src_gl_stop); + gl_base_src_class->fill_gl_memory = + GST_DEBUG_FUNCPTR (gst_wpe_video_src_fill_memory); + + doc_caps = gst_caps_from_string (WPE_VIDEO_SRC_DOC_CAPS); + gst_pad_template_set_documentation_caps (tmpl, doc_caps); + gst_clear_caps (&doc_caps); + + /** + * GstWpeVideoSrc::configure-web-view: + * @src: the object which received the signal + * @webview: the webView + * + * Allow application to configure the webView settings. + */ + gst_wpe_video_src_signals[SIGNAL_CONFIGURE_WEB_VIEW] = + g_signal_new ("configure-web-view", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); + + /** + * GstWpeVideoSrc::load-bytes: + * @src: the object which received the signal + * @bytes: the GBytes data to load + * + * Load the specified bytes into the internal webView. + */ + gst_wpe_video_src_signals[SIGNAL_LOAD_BYTES] = + g_signal_new_class_handler ("load-bytes", G_TYPE_FROM_CLASS (klass), + static_cast < GSignalFlags > (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), + G_CALLBACK (gst_wpe_video_src_load_bytes), NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_BYTES); +} \ No newline at end of file diff --git a/ext/wpe/gstwpevideosrc.h b/ext/wpe/gstwpevideosrc.h new file mode 100644 index 000000000..c879aa00c --- /dev/null +++ b/ext/wpe/gstwpevideosrc.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_WPE_VIDEO_SRC (gst_wpe_video_src_get_type ()) +G_DECLARE_FINAL_TYPE (GstWpeVideoSrc, gst_wpe_video_src, GST, WPE_VIDEO_SRC, GstGLBaseSrc); + +void gst_wpe_video_src_configure_web_view (GstWpeVideoSrc * src, WebKitWebView * webview); +G_END_DECLS \ No newline at end of file diff --git a/ext/wpe/meson.build b/ext/wpe/meson.build index 9381e9b08..96b30a99a 100644 --- a/ext/wpe/meson.build +++ b/ext/wpe/meson.build @@ -17,7 +17,7 @@ if not wpe_dep.found() or not wpe_fdo_dep.found() or not egl_dep.found() or not endif gstwpe = library('gstwpe', - ['WPEThreadedView.cpp', 'gstwpesrc.cpp'], + ['WPEThreadedView.cpp', 'gstwpe.cpp', 'gstwpevideosrc.cpp', 'gstwpesrcbin.cpp'], dependencies : [egl_dep, wpe_dep, wpe_fdo_dep, gstvideo_dep, gstbase_dep, gstgl_dep, xkbcommon_dep, wl_server_dep], cpp_args : gst_plugins_bad_args + ['-DHAVE_CONFIG_H=1'], include_directories : [configinc], -- cgit v1.2.1