From: Po Lu <luangruo@yahoo.com>
To: emacs-devel@gnu.org
Subject: GStreamer xwidget
Date: Fri, 19 Nov 2021 10:51:21 +0800 [thread overview]
Message-ID: <87ee7cq2mu.fsf@yahoo.com> (raw)
In-Reply-To: 87ee7cq2mu.fsf.ref@yahoo.com
[-- Attachment #1: Type: text/plain, Size: 506 bytes --]
I would like to install the following change that introduces a
GStreamer-based media widget.
It depends on GStreamer, gst-plugins-base, and gst-plugins-good and is
thus a compile-time option.
Right now, it can only display a test pattern, but getting that to work
and perform decently was a challenge (for instance, offscreen rendering
is not an option, instead the pipeline is dynamically built based on
available xwidget views, utilizing hardware acceleration and video
decoding if available).
WDYT?
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Implement-a-media-xwidget-based-on-GStreamer.patch --]
[-- Type: text/x-patch, Size: 31895 bytes --]
From 39a034aff24b2b64b028345aeae66c7aae6f8820 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Thu, 18 Nov 2021 18:10:45 +0800
Subject: [PATCH] Implement a media xwidget based on GStreamer
* configure.ac: Add option to use GStreamer for media playback.
* doc/lispref/display.texi (Xwidgets): Document new functions
and xwidget type.
* src/Makefile.in (EMACS_CFLAGS): Add GStreamer cflags.
(LIBES): Add GStreamer linker options.
* etc/NEWS: Announce `media' xwidget.
* etc/xterm.c (x_scroll_run): Don't resize cairo surface
if not a GTK xwidget.
(x_term_init): Initialize GStreamer.
* src/xwidget.c (gst_create_window)
(gst_message_cb, unlink_gst_xwidget_view)
(check_gstreamer_dependencies): New functions.
(Fmake_xwidget): Add support for media xwidgets.
(Fxwidget_perform_lispy_event): Return nil if it doesn't make
sense to perform an event.
(Fxwidget_resize): Add support for media widgets.
(xwidget_button, xwidget_motion_or_crossing): Handle non-GTK
xwidgets.
(xwidget_expose): Handle media xwidgets.
(x_draw_xwidget_glyph_string): Set up GStreamer widgets if
appropriate.
(Fxwidget_size_request): Return nil on widgets that can't
have a size request.
(Fdelete_xwidget_view): Clean up after GStreamer.
(Fxwidget_media_pause, Fxwidget_media_play): New functions.
(syms_of_xwidget): New symbols and subrs.
(kill_xwidget): Clean up after GStreamer.
* src/xwidget.h (struct xwidget, struct xwidget_view): Add
GStreamer specific fields for media xwidgets.
(XWIDGET_VIEW_GTK_P): New macro.
---
configure.ac | 13 +
doc/lispref/display.texi | 12 +
etc/NEWS | 7 +
src/Makefile.in | 7 +-
src/xterm.c | 16 +-
src/xwidget.c | 536 +++++++++++++++++++++++++++++++--------
src/xwidget.h | 25 ++
7 files changed, 508 insertions(+), 108 deletions(-)
diff --git a/configure.ac b/configure.ac
index c231c2ceae..ae3f28f8db 100644
--- a/configure.ac
+++ b/configure.ac
@@ -485,6 +485,7 @@ AC_DEFUN
OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support])
OPTION_DEFAULT_ON([modules],[don't compile with dynamic modules support])
OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support])
+OPTION_DEFAULT_OFF([gstreamer],[compile with xwidget video playback support using GStreamer])
OPTION_DEFAULT_OFF([native-compilation],[compile with Emacs Lisp native compiler support])
OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 32-bit Cygwin])
@@ -2834,6 +2835,18 @@ AC_DEFUN
fi
AC_SUBST(XWIDGETS_OBJ)
+HAVE_GSTREAMER=no
+if test "$with_gstreamer" != "no" && test "${HAVE_X_WINDOWS}" = "yes" \
+ && test "${HAVE_XWIDGETS}" = "yes"; then
+ EMACS_CHECK_MODULES([GSTREAMER], "gstreamer-1.0 gstreamer-video-1.0")
+ if test "$HAVE_GSTREAMER" = "yes"; then
+ AC_DEFINE(HAVE_GSTREAMER, 1, [Define to 1 if using GStreamer for video support in xwidgets.])
+ fi
+fi
+
+AC_SUBST(GSTREAMER_CFLAGS)
+AC_SUBST(GSTREAMER_LIBS)
+
CFLAGS=$OLD_CFLAGS
LIBS=$OLD_LIBS
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index dd2c6e003f..1b4a3d9a22 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -6797,6 +6797,8 @@ Xwidgets
@table @code
@item webkit
The WebKit component.
+@item media
+A media widget that allows to display videos inside Emacs buffers.
@end table
The @var{width} and @var{height} arguments specify the widget size in
@@ -7004,6 +7006,16 @@ Xwidgets
be passed as an index to @code{xwidget-webkit-goto-history}.
@end defun
+@defun xwidget-media-pause xwidget
+Suspend playback of @var{xwidget}, a media xwidget. You can later
+resume playback using @code{xwidget-media-play}.
+@end defun
+
+@defun xwidget-media-pause xwidget
+Start or resume playback of @var{xwidget}, a media xwidget. You can
+later pause playback using @code{xwidget-media-pause}.
+@end defun
+
@node Buttons
@section Buttons
@cindex buttons in buffers
diff --git a/etc/NEWS b/etc/NEWS
index cee2844be3..8cb5495b4e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -488,6 +488,13 @@ This is a convenience function to extract the field data from
** Xwidgets
++++
+*** New xwidget type 'media'.
+This xwidget type allows to display video content inside Emacs buffers.
+You must have the GStreamer library installed, along with the plugins
+"xvimagesink", "queue", "videotestsrc" and "tee", and Emacs must be
+built with '--with-gstreamer'.
+
---
*** New user option 'xwidget-webkit-buffer-name-format'.
Using this option you can control how the xwidget-webkit buffers are
diff --git a/src/Makefile.in b/src/Makefile.in
index 4c5535f8ad..646392c051 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -333,6 +333,9 @@ LIBGMP =
LIBGCCJIT_LIBS = @LIBGCCJIT_LIBS@
LIBGCCJIT_CFLAGS = @LIBGCCJIT_CFLAGS@
+GSTREAMER_LIBS = @GSTREAMER_LIBS@
+GSTREAMER_CFLAGS = @GSTREAMER_CFLAGS@
+
## dynlib.o if necessary, else empty
DYNLIB_OBJ = @DYNLIB_OBJ@
@@ -377,7 +380,7 @@ EMACS_CFLAGS=
$(WEBKIT_CFLAGS) $(WEBP_CFLAGS) $(LCMS2_CFLAGS) \
$(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
$(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
- $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \
+ $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(GSTREAMER_CFLAGS) \
$(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
$(WERROR_CFLAGS)
ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
@@ -524,7 +527,7 @@ LIBES =
$(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
- $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS)
+ $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(GSTREAMER_LIBS)
## FORCE it so that admin/unidata can decide whether this file is
## up-to-date. Although since charprop depends on bootstrap-emacs,
diff --git a/src/xterm.c b/src/xterm.c
index 816b6dc5a8..a7116d9d95 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -4471,9 +4471,11 @@ x_scroll_run (struct window *w, struct run *run)
view->y + view->clip_top,
view->clip_right - view->clip_left,
view->clip_bottom - view->clip_top);
- cairo_xlib_surface_set_size (view->cr_surface,
- view->clip_right - view->clip_left,
- view->clip_bottom - view->clip_top);
+
+ if (XWIDGET_VIEW_GTK_P (view))
+ cairo_xlib_surface_set_size (view->cr_surface,
+ view->clip_right - view->clip_left,
+ view->clip_bottom - view->clip_top);
}
xwidget_expose (view);
XFlush (dpy);
@@ -12940,6 +12942,14 @@ #define NUM_ARGV 10
gtk_init (&argc, &argv2);
request_sigio ();
+#ifdef HAVE_GSTREAMER
+ unrequest_sigio ();
+ /* gst_init might call XOpenDisplay, which fails if a signal
+ is received. */
+ gst_init (&argc, &argv2);
+ request_sigio ();
+#endif
+
g_log_remove_handler ("GLib", id);
xg_initialize ();
diff --git a/src/xwidget.c b/src/xwidget.c
index e1bf40ea43..ffed743412 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -40,6 +40,10 @@ Copyright (C) 2011-2021 Free Software Foundation, Inc.
#include <JavaScriptCore/JavaScript.h>
#include <cairo.h>
#include <X11/Xlib.h>
+#ifdef HAVE_GSTREAMER
+#include <gst/video/videooverlay.h>
+#define XG_WINDOW_HANDLE "xg-window-handle"
+#endif
#elif defined NS_IMPL_COCOA
#include "nsxwidget.h"
#endif
@@ -111,6 +115,12 @@ webkit_decide_policy_cb (WebKitWebView *,
static void find_widget (GtkWidget *t, struct widget_search_data *);
static void mouse_target_changed (WebKitWebView *, WebKitHitTestResult *, guint,
gpointer);
+#ifdef HAVE_GSTREAMER
+static GstBusSyncReply gst_create_window (GstBus *, GstMessage *, gpointer);
+static gboolean gst_message_cb (GstBus *, GstMessage *, gpointer);
+static bool check_gstreamer_dependencies (void);
+static void unlink_gst_xwidget_view (struct xwidget_view *);
+#endif
#endif
@@ -123,6 +133,7 @@ DEFUN ("make-xwidget",
TYPE is a symbol which can take one of the following values:
- webkit
+- media
RELATED is nil, or an xwidget. When constructing a WebKit widget, it
will share the same settings and internal subprocess as RELATED.
@@ -140,9 +151,18 @@ DEFUN ("make-xwidget",
CHECK_FIXNAT (width);
CHECK_FIXNAT (height);
- if (!EQ (type, Qwebkit))
+ if (!EQ (type, Qwebkit) && !EQ (type, Qmedia))
error ("Bad xwidget type");
+#ifdef HAVE_GSTREAMER
+ if (EQ (type, Qmedia)
+ && !check_gstreamer_dependencies ())
+ error ("Your Emacs is missing run-time dependencies required for GStreamer support");
+#else
+ if (EQ (type, Qmedia))
+ error ("Your Emacs was not built with GStreamer support");
+#endif
+
struct xwidget *xw = allocate_xwidget ();
Lisp_Object val;
xw->type = type;
@@ -180,103 +200,142 @@ DEFUN ("make-xwidget",
gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
xw->height);
- if (EQ (xw->type, Qwebkit))
- {
- WebKitWebView *related_view;
+ xw->xg_p = true;
+ WebKitWebView *related_view;
+
+ if (NILP (related)
+ || !XWIDGETP (related)
+ || !EQ (XXWIDGET (related)->type, Qwebkit))
+ {
+ xw->widget_osr = webkit_web_view_new ();
+
+ webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr),
+ "about:blank");
+ /* webkitgtk uses GSubprocess which sets sigaction causing
+ Emacs to not catch SIGCHLD with its usual handle setup in
+ 'catch_child_signal'. This resets the SIGCHLD sigaction. */
+ catch_child_signal ();
+ }
+ else
+ {
+ related_view = WEBKIT_WEB_VIEW (XXWIDGET (related)->widget_osr);
+ xw->widget_osr = webkit_web_view_new_with_related_view (related_view);
+ }
+
+ /* Enable the developer extras. */
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (xw->widget_osr));
+ g_object_set (G_OBJECT (settings), "enable-developer-extras", TRUE, NULL);
+
+ if (xw->xg_p)
+ {
+ gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
+ xw->height);
- if (NILP (related)
- || !XWIDGETP (related)
- || !EQ (XXWIDGET (related)->type, Qwebkit))
+ if (EQ (xw->type, Qwebkit))
{
- xw->widget_osr = webkit_web_view_new ();
-
- webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr),
- "about:blank");
- /* webkitgtk uses GSubprocess which sets sigaction causing
- Emacs to not catch SIGCHLD with its usual handle setup in
- 'catch_child_signal'. This resets the SIGCHLD sigaction. */
- catch_child_signal ();
+ gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
+ GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr)));
}
else
{
- related_view = WEBKIT_WEB_VIEW (XXWIDGET (related)->widget_osr);
- xw->widget_osr = webkit_web_view_new_with_related_view (related_view);
+ gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
+ xw->widget_osr);
}
- /* Enable the developer extras. */
- settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (xw->widget_osr));
- g_object_set (G_OBJECT (settings), "enable-developer-extras", TRUE, NULL);
+ gtk_widget_show (xw->widget_osr);
+ gtk_widget_show (xw->widgetwindow_osr);
+ synthesize_focus_in_event (xw->widgetwindow_osr);
+
+
+ g_signal_connect (G_OBJECT (gtk_widget_get_window (xw->widgetwindow_osr)),
+ "from-embedder", G_CALLBACK (from_embedder), NULL);
+ g_signal_connect (G_OBJECT (gtk_widget_get_window (xw->widgetwindow_osr)),
+ "to-embedder", G_CALLBACK (to_embedder), NULL);
+
+ /* Store some xwidget data in the gtk widgets for convenient
+ retrieval in the event handlers. */
+ g_object_set_data (G_OBJECT (xw->widget_osr), XG_XWIDGET, xw);
+ g_object_set_data (G_OBJECT (xw->widgetwindow_osr), XG_XWIDGET, xw);
+
+ /* signals */
+ if (EQ (xw->type, Qwebkit))
+ {
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "load-changed",
+ G_CALLBACK (webkit_view_load_changed_cb), xw);
+
+ g_signal_connect (G_OBJECT (webkit_context),
+ "download-started",
+ G_CALLBACK (webkit_download_cb), xw);
+
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "decide-policy",
+ G_CALLBACK
+ (webkit_decide_policy_cb),
+ xw);
+
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "mouse-target-changed",
+ G_CALLBACK (mouse_target_changed),
+ xw);
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "create",
+ G_CALLBACK (webkit_create_cb),
+ xw);
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "script-dialog",
+ G_CALLBACK (webkit_script_dialog_cb),
+ NULL);
+ g_signal_connect (G_OBJECT (xw->widget_osr),
+ "run-file-chooser",
+ G_CALLBACK (run_file_chooser_cb),
+ NULL);
+ }
+
+ g_signal_connect (G_OBJECT (xw->widgetwindow_osr), "damage-event",
+ G_CALLBACK (offscreen_damage_event), xw);
}
- gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
- xw->height);
+ unblock_input ();
+ }
+#ifdef HAVE_GSTREAMER
+ else
+ {
+ xw->xg_p = false;
+ xw->gst_pipeline = gst_pipeline_new (NULL);
+ xw->gst_source = gst_element_factory_make ("videotestsrc", NULL);
+ xw->gst_tee = gst_element_factory_make ("tee", NULL);
- if (EQ (xw->type, Qwebkit))
- {
- gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
- GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr)));
- }
- else
- {
- gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
- xw->widget_osr);
- }
+ GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (xw->gst_pipeline));
+ GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
+ GstElement *queue = gst_element_factory_make ("queue", NULL);
- gtk_widget_show (xw->widget_osr);
- gtk_widget_show (xw->widgetwindow_osr);
- synthesize_focus_in_event (xw->widgetwindow_osr);
+ g_object_set (G_OBJECT (fakesink), "sync", TRUE, NULL);
+ gst_bus_set_sync_handler (bus, gst_create_window, xw->gst_pipeline, NULL);
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (G_OBJECT (bus), "message",
+ G_CALLBACK (gst_message_cb), xw->gst_pipeline);
- g_signal_connect (G_OBJECT (gtk_widget_get_window (xw->widgetwindow_osr)),
- "from-embedder", G_CALLBACK (from_embedder), NULL);
- g_signal_connect (G_OBJECT (gtk_widget_get_window (xw->widgetwindow_osr)),
- "to-embedder", G_CALLBACK (to_embedder), NULL);
+ xw->gst_fakesink = fakesink;
- /* Store some xwidget data in the gtk widgets for convenient
- retrieval in the event handlers. */
- g_object_set_data (G_OBJECT (xw->widget_osr), XG_XWIDGET, xw);
- g_object_set_data (G_OBJECT (xw->widgetwindow_osr), XG_XWIDGET, xw);
+ gst_object_ref (xw->gst_pipeline);
+ gst_object_ref (xw->gst_source);
+ gst_object_ref (xw->gst_tee);
+ gst_object_ref (xw->gst_fakesink);
- /* signals */
- if (EQ (xw->type, Qwebkit))
- {
- g_signal_connect (G_OBJECT (xw->widget_osr),
- "load-changed",
- G_CALLBACK (webkit_view_load_changed_cb), xw);
-
- g_signal_connect (G_OBJECT (webkit_context),
- "download-started",
- G_CALLBACK (webkit_download_cb), xw);
-
- g_signal_connect (G_OBJECT (xw->widget_osr),
- "decide-policy",
- G_CALLBACK
- (webkit_decide_policy_cb),
- xw);
-
- g_signal_connect (G_OBJECT (xw->widget_osr),
- "mouse-target-changed",
- G_CALLBACK (mouse_target_changed),
- xw);
- g_signal_connect (G_OBJECT (xw->widget_osr),
- "create",
- G_CALLBACK (webkit_create_cb),
- xw);
- g_signal_connect (G_OBJECT (xw->widget_osr),
- "script-dialog",
- G_CALLBACK (webkit_script_dialog_cb),
- NULL);
- g_signal_connect (G_OBJECT (xw->widget_osr),
- "run-file-chooser",
- G_CALLBACK (run_file_chooser_cb),
- NULL);
- }
+ gst_bin_add_many (GST_BIN (xw->gst_pipeline), xw->gst_source,
+ xw->gst_tee, queue, fakesink, NULL);
- g_signal_connect (G_OBJECT (xw->widgetwindow_osr), "damage-event",
- G_CALLBACK (offscreen_damage_event), xw);
+ gst_element_link_many (xw->gst_tee, queue, fakesink, NULL);
- unblock_input ();
+ if (!gst_element_link (xw->gst_source, xw->gst_tee))
+ emacs_abort ();
+
+ if (!gst_element_set_state (xw->gst_pipeline, GST_STATE_PAUSED))
+ emacs_abort ();
}
+#endif
#elif defined NS_IMPL_COCOA
nsxwidget_init (xw);
#endif
@@ -335,6 +394,9 @@ DEFUN ("xwidget-perform-lispy-event",
f = SELECTED_FRAME ();
#ifdef USE_GTK
+ if (!xw->xg_p)
+ return Qnil;
+
widget = gtk_window_get_focus (GTK_WINDOW (xw->widgetwindow_osr));
if (!widget)
@@ -888,7 +950,8 @@ xwidget_button (struct xwidget_view *view,
bool down_p, int x, int y, int button,
int modifier_state, Time time)
{
- if (NILP (XXWIDGET (view->model)->buffer))
+ if (NILP (XXWIDGET (view->model)->buffer)
+ || !XWIDGET_VIEW_GTK_P (view))
return;
record_osr_embedder (view);
@@ -947,7 +1010,7 @@ xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
int y;
GtkWidget *target;
- if (NILP (model->buffer))
+ if (NILP (model->buffer) || !model->xg_p)
return;
xg_event = gdk_event_new (event->type == MotionNotify
@@ -1117,7 +1180,17 @@ xwidget_expose (struct xwidget_view *xv)
{
struct xwidget *xw = XXWIDGET (xv->model);
- xv_do_draw (xv, xw);
+ if (xw->xg_p)
+ xv_do_draw (xv, xw);
+#ifdef HAVE_GSTREAMER
+ else if (EQ (xw->type, Qmedia) && XWIDGET_LIVE_P (xw))
+ {
+ if (xv->wdesc != None)
+ XMoveWindow (xv->dpy, xv->internal_window,
+ -xv->clip_left, -xv->clip_top);
+ gst_video_overlay_expose (GST_VIDEO_OVERLAY (xv->video_sink));
+ }
+#endif
}
#endif /* USE_GTK */
@@ -1690,12 +1763,81 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
CopyFromParent, CWEventMask, &a);
XLowerWindow (xv->dpy, xv->wdesc);
XDefineCursor (xv->dpy, xv->wdesc, xv->cursor);
- xv->cr_surface = cairo_xlib_surface_create (xv->dpy,
- xv->wdesc,
- FRAME_DISPLAY_INFO (s->f)->visual,
- clip_right - clip_left,
- clip_bottom - clip_top);
- xv->cr_context = cairo_create (xv->cr_surface);
+
+ if (XWIDGET_VIEW_GTK_P (xv))
+ {
+ xv->cr_surface = cairo_xlib_surface_create (xv->dpy,
+ xv->wdesc,
+ FRAME_DISPLAY_INFO (s->f)->visual,
+ clip_right - clip_left,
+ clip_bottom - clip_top);
+ xv->cr_context = cairo_create (xv->cr_surface);
+ }
+#ifdef HAVE_GSTREAMER
+ else
+ {
+ XFlush (xv->dpy);
+
+ if (EQ (xww->type, Qmedia) && XWIDGET_LIVE_P (xww))
+ {
+ GstPadTemplate *templ;
+ GstPad *sinkpad;
+
+ a.event_mask = ExposureMask;
+ a.background_pixel = FRAME_BACKGROUND_PIXEL (xv->frame);
+
+ xv->internal_window = XCreateWindow (xv->dpy, xv->wdesc,
+ -clip_left, -clip_top,
+ xww->width, xww->height,
+ 0, CopyFromParent, CopyFromParent,
+ CopyFromParent,
+ (CWEventMask | CWBackPixel), &a);
+
+ XMapWindow (xv->dpy, xv->internal_window);
+ XFlush (xv->dpy);
+
+ Fputhash (make_fixnum (xv->internal_window), xvw, x_window_to_xwv_map);
+
+ templ = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (xww->gst_tee),
+ "src_%u");
+
+ xv->teepad = gst_element_request_pad (xww->gst_tee,
+ templ, NULL, NULL);
+ xv->video_sink = gst_element_factory_make ("xvimagesink", NULL);
+ xv->video_queue = gst_element_factory_make ("queue", NULL);
+
+ gst_object_ref (xv->video_queue);
+ gst_object_ref (xv->video_sink);
+
+ g_object_set_data (G_OBJECT (xv->video_sink), XG_WINDOW_HANDLE,
+ (gpointer) xv->internal_window);
+ g_object_set (G_OBJECT (xv->video_sink), "display",
+ FRAME_TERMINAL (xv->frame)->name, NULL);
+
+ gst_video_overlay_handle_events (GST_VIDEO_OVERLAY (xv->video_sink),
+ FALSE);
+
+ gst_bin_add_many (GST_BIN (xww->gst_pipeline), xv->video_queue,
+ xv->video_sink, NULL);
+
+ if (!gst_element_link (xv->video_queue, xv->video_sink))
+ emacs_abort ();
+
+ gst_element_sync_state_with_parent (xv->video_queue);
+ gst_element_sync_state_with_parent (xv->video_sink);
+
+ sinkpad = gst_element_get_static_pad (xv->video_queue, "sink");
+ gst_pad_link (xv->teepad, sinkpad);
+ gst_object_unref (sinkpad);
+
+ xv->sinkpad = sinkpad;
+
+ gst_element_sync_state_with_parent (xww->gst_tee);
+
+ xwidget_expose (xv);
+ }
+ }
+#endif
Fputhash (make_fixnum (xv->wdesc), xvw, x_window_to_xwv_map);
moved = false;
@@ -1709,8 +1851,9 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
XMoveResizeWindow (xv->dpy, xv->wdesc, x + clip_left, y + clip_top,
clip_right - clip_left, clip_bottom - clip_top);
XFlush (xv->dpy);
- cairo_xlib_surface_set_size (xv->cr_surface, clip_right - clip_left,
- clip_bottom - clip_top);
+ if (XWIDGET_VIEW_GTK_P (xv))
+ cairo_xlib_surface_set_size (xv->cr_surface, clip_right - clip_left,
+ clip_bottom - clip_top);
#elif defined NS_IMPL_COCOA
nsxwidget_move_view (xv, x + clip_left, y + clip_top);
#endif
@@ -1740,8 +1883,10 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
clip_bottom - clip_top);
}
XFlush (xv->dpy);
- cairo_xlib_surface_set_size (xv->cr_surface, clip_right - clip_left,
- clip_bottom - clip_top);
+
+ if (xww->xg_p)
+ cairo_xlib_surface_set_size (xv->cr_surface, clip_right - clip_left,
+ clip_bottom - clip_top);
}
#elif defined NS_IMPL_COCOA
nsxwidget_resize_view (xv, clip_right - clip_left,
@@ -1765,7 +1910,10 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
if (!xwidget_hidden (xv))
{
#ifdef USE_GTK
- gtk_widget_queue_draw (xww->widget_osr);
+ if (xww->xg_p)
+ gtk_widget_queue_draw (xww->widget_osr);
+ else
+ xwidget_expose (xv);
#elif defined NS_IMPL_COCOA
nsxwidget_set_needsdisplay (xv);
#endif
@@ -1788,6 +1936,12 @@ #define CHECK_WEBKIT_WIDGET(xw) \
if (NILP (xw->buffer) || !EQ (xw->type, Qwebkit)) \
error ("Not a WebKit widget")
+#ifdef HAVE_GSTREAMER
+#define CHECK_MEDIA_WIDGET(xw) \
+ if (NILP (xw->buffer) || !EQ (xw->type, Qmedia)) \
+ error ("Not a WebKit widget")
+#endif
+
/* Macro that checks xwidget hold webkit web view first. */
#define WEBKIT_FN_INIT() \
CHECK_LIVE_XWIDGET (xwidget); \
@@ -1989,6 +2143,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
#ifdef USE_GTK
xv->just_resized = true;
SET_FRAME_GARBAGED (xv->frame);
+#ifdef HAVE_GSTREAMER
+ if (EQ (xw->type, Qmedia))
+ {
+ XResizeWindow (xv->dpy, xv->internal_window,
+ xw->width, xw->height);
+ }
+#endif
+
#else
wset_redisplay (XWINDOW (xv->w));
#endif
@@ -2000,7 +2162,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
/* If there is an offscreen widget resize it first. */
#ifdef USE_GTK
- if (xw->widget_osr)
+ if (xw->xg_p && xw->widget_osr)
{
gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
xw->height);
@@ -2030,9 +2192,14 @@ DEFUN ("xwidget-size-request",
{
CHECK_LIVE_XWIDGET (xwidget);
#ifdef USE_GTK
- GtkRequisition requisition;
- gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
- return list2i (requisition.width, requisition.height);
+ if (XXWIDGET (xwidget)->xg_p)
+ {
+ GtkRequisition requisition;
+ gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
+ return list2i (requisition.width, requisition.height);
+ }
+
+ return Qnil;
#elif defined NS_IMPL_COCOA
return nsxwidget_get_size (XXWIDGET (xwidget));
#endif
@@ -2115,18 +2282,29 @@ DEFUN ("delete-xwidget-view",
#ifdef USE_GTK
struct xwidget *xw = XXWIDGET (xv->model);
GdkWindow *w;
-
+#ifdef HAVE_GSTREAMER
+ block_input ();
+ if (XWIDGET_LIVE_P (xw) && EQ (xw->type, Qmedia))
+ unlink_gst_xwidget_view (xv);
+ unblock_input ();
+#endif
if (xv->wdesc != None)
{
block_input ();
- cairo_destroy (xv->cr_context);
- cairo_surface_destroy (xv->cr_surface);
+ if (XWIDGET_VIEW_GTK_P (xv))
+ {
+ cairo_destroy (xv->cr_context);
+ cairo_surface_destroy (xv->cr_surface);
+ }
XDestroyWindow (xv->dpy, xv->wdesc);
Fremhash (make_fixnum (xv->wdesc), x_window_to_xwv_map);
+
+ if (XWIDGET_LIVE_P (xw) && EQ (xw->type, Qmedia))
+ Fremhash (make_fixnum (xv->internal_window), x_window_to_xwv_map);
unblock_input ();
}
- if (xw->embedder_view == xv && !NILP (xw->buffer))
+ if (xw->xg_p && xw->embedder_view == xv && !NILP (xw->buffer))
{
w = gtk_widget_get_window (xw->widgetwindow_osr);
@@ -2555,6 +2733,77 @@ DEFUN ("xwidget-webkit-back-forward-list", Fxwidget_webkit_back_forward_list,
return list3 (back, here, forward);
}
+
+#ifdef HAVE_GSTREAMER
+DEFUN ("xwidget-media-pause", Fxwidget_media_pause, Sxwidget_media_pause,
+ 1, 1, 0, doc: /* Pause specified media XWIDGET.
+This causes playback to stop until you resume it using
+`xwidget-media-play'. */)
+ (Lisp_Object xwidget)
+{
+ struct xwidget *xw;
+ struct xwidget_view *xv;
+ Lisp_Object tem;
+
+ CHECK_LIVE_XWIDGET (xwidget);
+ xw = XXWIDGET (xwidget);
+ CHECK_MEDIA_WIDGET (xw);
+
+ block_input ();
+ gst_element_set_state (xw->gst_pipeline, GST_STATE_PAUSED);
+ gst_element_sync_state_with_parent (xw->gst_tee);
+ gst_element_sync_state_with_parent (xw->gst_fakesink);
+
+ for (tem = internal_xwidget_view_list; CONSP (tem); tem = XCDR (tem))
+ {
+ xv = XXWIDGET_VIEW (XCAR (tem));
+
+ if (EQ (xv->model, xwidget))
+ {
+ gst_element_sync_state_with_parent (xv->video_queue);
+ gst_element_sync_state_with_parent (xv->video_sink);
+ }
+ }
+
+ unblock_input ();
+
+ return Qnil;
+}
+
+DEFUN ("xwidget-media-play", Fxwidget_media_play, Sxwidget_media_play,
+ 1, 1, 0, doc: /* Resume playback of specified media XWIDGET. */)
+ (Lisp_Object xwidget)
+{
+ struct xwidget *xw;
+ struct xwidget_view *xv;
+ Lisp_Object tem;
+
+ CHECK_LIVE_XWIDGET (xwidget);
+ xw = XXWIDGET (xwidget);
+ CHECK_MEDIA_WIDGET (xw);
+
+ block_input ();
+ gst_element_set_state (xw->gst_pipeline, GST_STATE_PLAYING);
+ gst_element_sync_state_with_parent (xw->gst_tee);
+ gst_element_sync_state_with_parent (xw->gst_fakesink);
+
+ for (tem = internal_xwidget_view_list; CONSP (tem); tem = XCDR (tem))
+ {
+ xv = XXWIDGET_VIEW (XCAR (tem));
+
+ if (EQ (xv->model, xwidget))
+ {
+ gst_element_sync_state_with_parent (xv->video_queue);
+ gst_element_sync_state_with_parent (xv->video_sink);
+ }
+ }
+
+ gst_element_sync_state_with_parent (xw->gst_tee);
+ unblock_input ();
+
+ return Qnil;
+}
+#endif
#endif
void
@@ -2584,6 +2833,7 @@ syms_of_xwidget (void)
defsubr (&Sxwidget_webkit_zoom);
defsubr (&Sxwidget_webkit_execute_script);
DEFSYM (Qwebkit, "webkit");
+ DEFSYM (Qmedia, "media");
defsubr (&Sxwidget_size_request);
defsubr (&Sdelete_xwidget_view);
@@ -2600,6 +2850,10 @@ syms_of_xwidget (void)
#ifdef USE_GTK
defsubr (&Sxwidget_webkit_load_html);
defsubr (&Sxwidget_webkit_back_forward_list);
+#ifdef HAVE_GSTREAMER
+ defsubr (&Sxwidget_media_pause);
+ defsubr (&Sxwidget_media_play);
+#endif
#endif
defsubr (&Skill_xwidget);
@@ -2862,7 +3116,7 @@ kill_xwidget (struct xwidget *xw)
#ifdef USE_GTK
xw->buffer = Qnil;
- if (xw->widget_osr && xw->widgetwindow_osr)
+ if (xw->xg_p && xw->widget_osr && xw->widgetwindow_osr)
{
gtk_widget_destroy (xw->widget_osr);
gtk_widget_destroy (xw->widgetwindow_osr);
@@ -2882,6 +3136,26 @@ kill_xwidget (struct xwidget *xw)
}
}
+#ifdef HAVE_GSTREAMER
+ Lisp_Object tem;
+
+ if (EQ (xw->type, Qmedia))
+ {
+ for (tem = internal_xwidget_view_list; CONSP (tem);
+ tem = XCDR (tem))
+ {
+ struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tem));
+
+ if (XXWIDGET (xv->model) == xw)
+ unlink_gst_xwidget_view (xv);
+ }
+ gst_object_unref (xw->gst_tee);
+ gst_object_unref (xw->gst_pipeline);
+ gst_object_unref (xw->gst_source);
+ gst_object_unref (xw->gst_fakesink);
+ }
+#endif
+
xw->widget_osr = NULL;
xw->widgetwindow_osr = NULL;
xw->find_text = NULL;
@@ -2908,3 +3182,59 @@ kill_buffer_xwidgets (Lisp_Object buffer)
}
}
}
+
+#ifdef HAVE_GSTREAMER
+static GstBusSyncReply
+gst_create_window (GstBus *bus, GstMessage *msg, gpointer user_data)
+{
+ GstObject *elm;
+ Window wdesc;
+
+ if (!gst_is_video_overlay_prepare_window_handle_message (msg))
+ return GST_BUS_PASS;
+
+ elm = GST_MESSAGE_SRC (msg);
+ wdesc = (Window) g_object_get_data (G_OBJECT (elm), XG_WINDOW_HANDLE);
+
+ gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (elm), wdesc);
+
+ return GST_BUS_DROP;
+}
+
+static gboolean
+gst_message_cb (GstBus *bus, GstMessage *message, gpointer user_data)
+{
+ return TRUE;
+}
+
+static bool
+check_gstreamer_dependencies (void)
+{
+ GstRegistry *registry = gst_registry_get ();
+
+ return (gst_registry_find_feature (registry, "xvimagesink",
+ GST_TYPE_ELEMENT_FACTORY)
+ && gst_registry_find_feature (registry, "queue",
+ GST_TYPE_ELEMENT_FACTORY)
+ && gst_registry_find_feature (registry, "videotestsrc",
+ GST_TYPE_ELEMENT_FACTORY)
+ && gst_registry_find_feature (registry, "tee",
+ GST_TYPE_ELEMENT_FACTORY));
+}
+
+static void
+unlink_gst_xwidget_view (struct xwidget_view *xv)
+{
+ struct xwidget *xw = XXWIDGET (xv->model);
+
+ gst_pad_unlink (xv->teepad, xv->sinkpad);
+ gst_element_unlink (xv->video_queue, xv->video_sink);
+
+ gst_element_set_state (xv->video_queue, GST_STATE_NULL);
+ gst_element_set_state (xv->video_sink, GST_STATE_NULL);
+ gst_bin_remove_many (GST_BIN (xw->gst_pipeline), xv->video_sink,
+ xv->video_queue, NULL);
+ gst_object_unref (xv->video_sink);
+ gst_object_unref (xv->video_queue);
+}
+#endif
diff --git a/src/xwidget.h b/src/xwidget.h
index 78fe865dd8..66f7fb2c4c 100644
--- a/src/xwidget.h
+++ b/src/xwidget.h
@@ -39,6 +39,10 @@ #define XWIDGET_H_INCLUDED
#import "nsxwidget.h"
#endif
+#ifdef HAVE_GSTREAMER
+#include <gst/gst.h>
+#endif
+
struct xwidget
{
union vectorlike_header header;
@@ -65,6 +69,16 @@ #define XWIDGET_H_INCLUDED
char *find_text;
#if defined (USE_GTK)
+ /* Whether or not the widget is used to display GTK widgets. */
+ bool xg_p;
+
+#ifdef HAVE_GSTREAMER
+ GstElement *gst_tee;
+ GstElement *gst_pipeline;
+ GstElement *gst_source;
+ GstElement *gst_fakesink;
+#endif
+
/* For offscreen widgets, unused if not osr. */
GtkWidget *widget_osr;
GtkWidget *widgetwindow_osr;
@@ -112,6 +126,14 @@ #define XWIDGET_H_INCLUDED
Emacs_Cursor cursor;
struct frame *frame;
+#ifdef HAVE_GSTREAMER
+ Window internal_window;
+ GstPad *teepad;
+ GstPad *sinkpad;
+ GstElement *video_sink;
+ GstElement *video_queue;
+#endif
+
cairo_surface_t *cr_surface;
cairo_t *cr_context;
int just_resized;
@@ -159,6 +181,9 @@ #define XXWIDGET_VIEW(a) (eassert (XWIDGET_VIEW_P (a)), \
#define CHECK_XWIDGET_VIEW(x) \
CHECK_TYPE (XWIDGET_VIEW_P (x), Qxwidget_view_p, x)
+#define XWIDGET_VIEW_GTK_P(x) \
+ XXWIDGET ((x)->model)->xg_p
+
#define XG_XWIDGET "emacs_xwidget"
#define XG_XWIDGET_VIEW "emacs_xwidget_view"
--
2.31.1
next parent reply other threads:[~2021-11-19 2:51 UTC|newest]
Thread overview: 70+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <87ee7cq2mu.fsf.ref@yahoo.com>
2021-11-19 2:51 ` Po Lu [this message]
2021-11-19 4:01 ` GStreamer xwidget T.V Raman
2021-11-19 4:21 ` Po Lu
2021-11-19 5:38 ` Lars Ingebrigtsen
2021-11-19 5:49 ` Po Lu
2021-11-19 6:19 ` Lars Ingebrigtsen
2021-11-19 6:37 ` Po Lu
2021-11-19 6:53 ` Lars Ingebrigtsen
2021-11-19 13:03 ` Eli Zaretskii
2021-11-19 13:07 ` Po Lu
2021-11-19 13:22 ` Eli Zaretskii
2021-11-19 13:33 ` Po Lu
2021-11-19 13:45 ` Eli Zaretskii
2021-11-20 5:07 ` Po Lu
2021-11-20 7:23 ` Eli Zaretskii
2021-11-20 7:27 ` Po Lu
2021-11-21 5:19 ` Richard Stallman
2021-11-21 6:53 ` Lars Ingebrigtsen
2021-11-22 4:31 ` Richard Stallman
2021-11-20 7:42 ` Richard Stallman
2021-11-20 8:05 ` Po Lu
2021-11-20 8:16 ` Lars Ingebrigtsen
2021-11-21 5:18 ` Richard Stallman
2021-11-21 5:27 ` Po Lu
2021-11-22 4:31 ` Richard Stallman
2021-11-22 4:41 ` Po Lu
2021-11-23 6:11 ` Richard Stallman
2021-11-23 7:07 ` Po Lu
2021-11-23 20:54 ` Richard Stallman
2021-11-24 0:32 ` Po Lu
2021-11-25 5:32 ` Richard Stallman
2021-11-25 8:13 ` Po Lu
2021-11-25 11:34 ` Alexandre Garreau
2021-11-27 4:09 ` Richard Stallman
2021-12-01 12:30 ` Dmitry Gutov
2021-12-01 17:53 ` Arthur Miller
2021-12-02 0:51 ` Po Lu
2021-12-02 2:47 ` chad
2021-11-27 4:08 ` Richard Stallman
2021-11-27 4:38 ` Po Lu
2021-11-28 4:24 ` Richard Stallman
2021-11-28 4:42 ` Po Lu
2021-11-28 8:04 ` Yuri Khan
2021-11-28 8:16 ` Po Lu
2021-11-29 3:02 ` Richard Stallman
2021-11-29 7:31 ` Yuri Khan
2021-11-29 7:44 ` Po Lu
2021-11-29 21:12 ` Richard Stallman
2021-11-30 1:38 ` Po Lu
2021-11-30 8:30 ` Yasushi SHOJI
2021-11-30 9:29 ` Po Lu
2021-11-30 10:30 ` Yasushi SHOJI
2021-12-01 7:04 ` Richard Stallman
2021-12-01 7:09 ` Po Lu
2021-11-30 4:09 ` Richard Stallman
2021-11-29 3:01 ` Richard Stallman
2021-11-29 3:12 ` Po Lu
2021-11-30 4:09 ` Richard Stallman
2021-11-30 4:36 ` Po Lu
2021-12-01 7:04 ` Richard Stallman
2021-11-23 6:11 ` Richard Stallman
2021-11-23 6:55 ` Po Lu
2021-11-24 4:28 ` Richard Stallman
2021-11-21 6:52 ` Lars Ingebrigtsen
2021-11-21 14:45 ` Arthur Miller
2021-11-23 6:09 ` Richard Stallman
2021-11-22 4:31 ` Richard Stallman
2021-12-01 7:07 ` Richard Stallman
2021-12-01 7:31 ` Po Lu
2021-12-01 8:30 ` Alexandre Garreau
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87ee7cq2mu.fsf@yahoo.com \
--to=luangruo@yahoo.com \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).