mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
gtk: remove, plugin has moved to -good
This commit is contained in:
parent
ec963e688f
commit
90a836bb89
31 changed files with 5 additions and 3822 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -61,9 +61,6 @@ gst*orc.h
|
||||||
/tests/examples/ipcpipeline/ipcpipeline1
|
/tests/examples/ipcpipeline/ipcpipeline1
|
||||||
/tests/examples/codecparsers/parse-jpeg
|
/tests/examples/codecparsers/parse-jpeg
|
||||||
/tests/examples/codecparsers/parse-vp8
|
/tests/examples/codecparsers/parse-vp8
|
||||||
/tests/examples/gtk/gtkglsink
|
|
||||||
/tests/examples/gtk/gtksink
|
|
||||||
/tests/examples/gtk/glliveshader
|
|
||||||
/tests/examples/shapewipe/shapewipe-example
|
/tests/examples/shapewipe/shapewipe-example
|
||||||
/tests/examples/jack/jack_client
|
/tests/examples/jack/jack_client
|
||||||
/tests/examples/opencv/gstmotioncells_dynamic_test
|
/tests/examples/opencv/gstmotioncells_dynamic_test
|
||||||
|
|
|
@ -52,6 +52,7 @@ CRUFT_FILES = \
|
||||||
$(top_builddir)/docs/plugins/xml/plugin-sdp.xml \
|
$(top_builddir)/docs/plugins/xml/plugin-sdp.xml \
|
||||||
$(top_builddir)/ext/alsaspdif/.libs/*.{so,dll,DLL,dylib} \
|
$(top_builddir)/ext/alsaspdif/.libs/*.{so,dll,DLL,dylib} \
|
||||||
$(top_builddir)/ext/gtk/.libs/libgstgtksink.so \
|
$(top_builddir)/ext/gtk/.libs/libgstgtksink.so \
|
||||||
|
$(top_builddir)/ext/gtk/.libs/libgstgtk.so \
|
||||||
$(top_builddir)/ext/hls/.libs/libgstfragmented* \
|
$(top_builddir)/ext/hls/.libs/libgstfragmented* \
|
||||||
$(top_builddir)/ext/ivorbis/.libs/*.{so,dll,DLL,dylib} \
|
$(top_builddir)/ext/ivorbis/.libs/*.{so,dll,DLL,dylib} \
|
||||||
$(top_builddir)/ext/jack/.libs/*.{so,dll,DLL,dylib} \
|
$(top_builddir)/ext/jack/.libs/*.{so,dll,DLL,dylib} \
|
||||||
|
@ -126,6 +127,7 @@ CRUFT_DIRS = \
|
||||||
$(top_srcdir)/gst-libs/gst/signalprocessor \
|
$(top_srcdir)/gst-libs/gst/signalprocessor \
|
||||||
$(top_srcdir)/tests/examples/gl \
|
$(top_srcdir)/tests/examples/gl \
|
||||||
$(top_srcdir)/tests/examples/scaletempo \
|
$(top_srcdir)/tests/examples/scaletempo \
|
||||||
|
$(top_srcdir)/tests/examples/gtk \
|
||||||
$(top_srcdir)/tests/examples/shapewipe \
|
$(top_srcdir)/tests/examples/shapewipe \
|
||||||
$(top_srcdir)/tests/examples/switch \
|
$(top_srcdir)/tests/examples/switch \
|
||||||
$(top_srcdir)/tests/examples/jack \
|
$(top_srcdir)/tests/examples/jack \
|
||||||
|
@ -134,6 +136,7 @@ CRUFT_DIRS = \
|
||||||
$(top_srcdir)/ext/cog \
|
$(top_srcdir)/ext/cog \
|
||||||
$(top_srcdir)/ext/eglgles \
|
$(top_srcdir)/ext/eglgles \
|
||||||
$(top_srcdir)/ext/gsettings \
|
$(top_srcdir)/ext/gsettings \
|
||||||
|
$(top_srcdir)/ext/gtk \
|
||||||
$(top_srcdir)/ext/ivorbis \
|
$(top_srcdir)/ext/ivorbis \
|
||||||
$(top_srcdir)/ext/jack \
|
$(top_srcdir)/ext/jack \
|
||||||
$(top_srcdir)/ext/metadata \
|
$(top_srcdir)/ext/metadata \
|
||||||
|
|
38
configure.ac
38
configure.ac
|
@ -1943,42 +1943,6 @@ AG_GST_CHECK_FEATURE(GL, [gl elements], gl, [
|
||||||
AM_CONDITIONAL(USE_GL, test "x$HAVE_GL" = "xyes")
|
AM_CONDITIONAL(USE_GL, test "x$HAVE_GL" = "xyes")
|
||||||
AM_CONDITIONAL(USE_OPENGL, test "x$GST_GL_HAVE_API_GL" = "x1")
|
AM_CONDITIONAL(USE_OPENGL, test "x$GST_GL_HAVE_API_GL" = "x1")
|
||||||
|
|
||||||
dnl *** gtk+ ***
|
|
||||||
HAVE_GTK3_GL="no"
|
|
||||||
translit(dnm, m, l) AM_CONDITIONAL(USE_GTK3, true)
|
|
||||||
AG_GST_CHECK_FEATURE(GTK3, [Gtk+ elements], gtk, [
|
|
||||||
PKG_CHECK_MODULES(GTK3, gtk+-3.0, [
|
|
||||||
AC_DEFINE([HAVE_GTK3], 1, [Define if Gtk+ 3.0 is installed])
|
|
||||||
HAVE_GTK3="yes"
|
|
||||||
], [
|
|
||||||
HAVE_GTK3="no"
|
|
||||||
])
|
|
||||||
PKG_CHECK_MODULES(GTK3_GL, gtk+-3.0 >= 3.15.0, [
|
|
||||||
GDK_WINDOWING="no"
|
|
||||||
if test "x$GST_GL_HAVE_WINDOW_X11" = "x1" -a "x$GST_GL_HAVE_PLATFORM_GLX" = "x1"; then
|
|
||||||
PKG_CHECK_MODULES(GTK3_X11, gtk+-x11-3.0, [
|
|
||||||
GTK3_CFLAGS="$GTK3_CFLAGS $GTK3_X11_CFLAGS"
|
|
||||||
GTK3_LIBS="$GTK3_LIBS $GTK3_X11_LIBS"
|
|
||||||
GDK_WINDOWING="yes"
|
|
||||||
], [AC_MSG_NOTICE([Could not find Gtk X11 integration])])
|
|
||||||
fi
|
|
||||||
if test "x$GST_GL_HAVE_WINDOW_WAYLAND" = "x1" -a "x$GST_GL_HAVE_PLATFORM_EGL" = "x1"; then
|
|
||||||
PKG_CHECK_MODULES(GTK3_WAYLAND, gtk+-wayland-3.0, [
|
|
||||||
GTK3_CFLAGS="$GTK3_CFLAGS $GTK3_WAYLAND_CFLAGS"
|
|
||||||
GTK3_LIBS="$GTK3_LIBS $GTK3_WAYLAND_LIBS"
|
|
||||||
GDK_WINDOWING="yes"
|
|
||||||
], [AC_MSG_NOTICE([Could not find Gtk Wayland integration])])
|
|
||||||
fi
|
|
||||||
if test "x$GDK_WINDOWING" = "xyes"; then
|
|
||||||
AC_DEFINE([HAVE_GTK3_GL], 1, [Define if Gtk+ 3.0 GL is installed])
|
|
||||||
HAVE_GTK3_GL="yes"
|
|
||||||
fi
|
|
||||||
], [
|
|
||||||
HAVE_GTK3_GL="no"
|
|
||||||
])
|
|
||||||
])
|
|
||||||
AM_CONDITIONAL(USE_GTK3_GL, test "x$HAVE_GTK3_GL" = "xyes")
|
|
||||||
|
|
||||||
dnl *** Qt ***
|
dnl *** Qt ***
|
||||||
translit(dnm, m, l) AM_CONDITIONAL(USE_QT, true)
|
translit(dnm, m, l) AM_CONDITIONAL(USE_QT, true)
|
||||||
AG_GST_CHECK_FEATURE(QT, [Qt elements], qt, [
|
AG_GST_CHECK_FEATURE(QT, [Qt elements], qt, [
|
||||||
|
@ -2735,7 +2699,6 @@ tests/examples/camerabin2/Makefile
|
||||||
tests/examples/codecparsers/Makefile
|
tests/examples/codecparsers/Makefile
|
||||||
tests/examples/directfb/Makefile
|
tests/examples/directfb/Makefile
|
||||||
tests/examples/audiomixmatrix/Makefile
|
tests/examples/audiomixmatrix/Makefile
|
||||||
tests/examples/gtk/Makefile
|
|
||||||
tests/examples/ipcpipeline/Makefile
|
tests/examples/ipcpipeline/Makefile
|
||||||
tests/examples/mpegts/Makefile
|
tests/examples/mpegts/Makefile
|
||||||
tests/examples/mxf/Makefile
|
tests/examples/mxf/Makefile
|
||||||
|
@ -2759,7 +2722,6 @@ ext/directfb/Makefile
|
||||||
ext/wayland/Makefile
|
ext/wayland/Makefile
|
||||||
ext/daala/Makefile
|
ext/daala/Makefile
|
||||||
ext/dts/Makefile
|
ext/dts/Makefile
|
||||||
ext/gtk/Makefile
|
|
||||||
ext/faac/Makefile
|
ext/faac/Makefile
|
||||||
ext/faad/Makefile
|
ext/faad/Makefile
|
||||||
ext/fdkaac/Makefile
|
ext/fdkaac/Makefile
|
||||||
|
|
|
@ -1601,38 +1601,6 @@ GST_TYPE_GRABCUT
|
||||||
gst_grabcut_get_type
|
gst_grabcut_get_type
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
<SECTION>
|
|
||||||
<FILE>element-gtkglsink</FILE>
|
|
||||||
<TITLE>gtkglsink</TITLE>
|
|
||||||
GstGtkGLSink
|
|
||||||
<SUBSECTION Standard>
|
|
||||||
GstGtkGLSinkClass
|
|
||||||
GST_GTK_GL_SINK
|
|
||||||
GST_GTK_GL_SINK_CAST
|
|
||||||
GST_IS_GTK_GL_SINK
|
|
||||||
GST_GTK_GL_SINK_CLASS
|
|
||||||
GST_IS_GTK_GL_SINK_CLASS
|
|
||||||
GST_TYPE_GTK_GL_SINK
|
|
||||||
<SUBSECTION Private>
|
|
||||||
gst_gtk_gl_sink_get_type
|
|
||||||
</SECTION>
|
|
||||||
|
|
||||||
<SECTION>
|
|
||||||
<FILE>element-gtksink</FILE>
|
|
||||||
<TITLE>gtksink</TITLE>
|
|
||||||
GstGtkSink
|
|
||||||
<SUBSECTION Standard>
|
|
||||||
GstGtkSinkClass
|
|
||||||
GST_GTK_SINK
|
|
||||||
GST_GTK_SINK_CAST
|
|
||||||
GST_IS_GTK_SINK
|
|
||||||
GST_GTK_SINK_CLASS
|
|
||||||
GST_IS_GTK_SINK_CLASS
|
|
||||||
GST_TYPE_GTK_SINK
|
|
||||||
<SUBSECTION Private>
|
|
||||||
gst_gtk_sink_get_type
|
|
||||||
</SECTION>
|
|
||||||
|
|
||||||
<SECTION>
|
<SECTION>
|
||||||
<FILE>element-h263parse</FILE>
|
<FILE>element-h263parse</FILE>
|
||||||
<TITLE>h263parse</TITLE>
|
<TITLE>h263parse</TITLE>
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
<plugin>
|
|
||||||
<name>gstgtk</name>
|
|
||||||
<description>Gtk+ sink</description>
|
|
||||||
<filename>../../ext/gtk/.libs/libgstgtksink.so</filename>
|
|
||||||
<basename>libgstgtksink.so</basename>
|
|
||||||
<version>1.11.2</version>
|
|
||||||
<license>LGPL</license>
|
|
||||||
<source>gst-plugins-bad</source>
|
|
||||||
<package>GStreamer Bad Plug-ins source release</package>
|
|
||||||
<origin>Unknown package origin</origin>
|
|
||||||
<elements>
|
|
||||||
<element>
|
|
||||||
<name>gtkglsink</name>
|
|
||||||
<longname>Gtk GL Video Sink</longname>
|
|
||||||
<class>Sink/Video</class>
|
|
||||||
<description>A video sink that renders to a GtkWidget using OpenGL</description>
|
|
||||||
<author>Matthew Waters <matthew@centricular.com></author>
|
|
||||||
<pads>
|
|
||||||
<caps>
|
|
||||||
<name>sink</name>
|
|
||||||
<direction>sink</direction>
|
|
||||||
<presence>always</presence>
|
|
||||||
<details>video/x-raw(memory:GLMemory), format=(string)RGBA, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(memory:GLMemory, meta:GstVideoOverlayComposition), format=(string)RGBA, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
|
|
||||||
</caps>
|
|
||||||
</pads>
|
|
||||||
</element>
|
|
||||||
<element>
|
|
||||||
<name>gtksink</name>
|
|
||||||
<longname>Gtk Video Sink</longname>
|
|
||||||
<class>Sink/Video</class>
|
|
||||||
<description>A video sink that renders to a GtkWidget</description>
|
|
||||||
<author>Matthew Waters <matthew@centricular.com></author>
|
|
||||||
<pads>
|
|
||||||
<caps>
|
|
||||||
<name>sink</name>
|
|
||||||
<direction>sink</direction>
|
|
||||||
<presence>always</presence>
|
|
||||||
<details>video/x-raw, format=(string){ BGRx, BGRA }, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
|
|
||||||
</caps>
|
|
||||||
</pads>
|
|
||||||
</element>
|
|
||||||
</elements>
|
|
||||||
</plugin>
|
|
|
@ -1,43 +0,0 @@
|
||||||
<plugin>
|
|
||||||
<name>gtk</name>
|
|
||||||
<description>Gtk+ sink</description>
|
|
||||||
<filename>../../ext/gtk/.libs/libgstgtk.so</filename>
|
|
||||||
<basename>libgstgtk.so</basename>
|
|
||||||
<version>1.13.0.1</version>
|
|
||||||
<license>LGPL</license>
|
|
||||||
<source>gst-plugins-bad</source>
|
|
||||||
<package>GStreamer Bad Plug-ins git</package>
|
|
||||||
<origin>Unknown package origin</origin>
|
|
||||||
<elements>
|
|
||||||
<element>
|
|
||||||
<name>gtkglsink</name>
|
|
||||||
<longname>Gtk GL Video Sink</longname>
|
|
||||||
<class>Sink/Video</class>
|
|
||||||
<description>A video sink that renders to a GtkWidget using OpenGL</description>
|
|
||||||
<author>Matthew Waters <matthew@centricular.com></author>
|
|
||||||
<pads>
|
|
||||||
<caps>
|
|
||||||
<name>sink</name>
|
|
||||||
<direction>sink</direction>
|
|
||||||
<presence>always</presence>
|
|
||||||
<details>video/x-raw(memory:GLMemory), format=(string)RGBA, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(memory:GLMemory, meta:GstVideoOverlayComposition), format=(string)RGBA, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
|
|
||||||
</caps>
|
|
||||||
</pads>
|
|
||||||
</element>
|
|
||||||
<element>
|
|
||||||
<name>gtksink</name>
|
|
||||||
<longname>Gtk Video Sink</longname>
|
|
||||||
<class>Sink/Video</class>
|
|
||||||
<description>A video sink that renders to a GtkWidget</description>
|
|
||||||
<author>Matthew Waters <matthew@centricular.com></author>
|
|
||||||
<pads>
|
|
||||||
<caps>
|
|
||||||
<name>sink</name>
|
|
||||||
<direction>sink</direction>
|
|
||||||
<presence>always</presence>
|
|
||||||
<details>video/x-raw, format=(string){ BGRx, BGRA }, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
|
|
||||||
</caps>
|
|
||||||
</pads>
|
|
||||||
</element>
|
|
||||||
</elements>
|
|
||||||
</plugin>
|
|
|
@ -88,12 +88,6 @@ else
|
||||||
GL_DIR=
|
GL_DIR=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if USE_GTK3
|
|
||||||
GTK3_DIR=gtk
|
|
||||||
else
|
|
||||||
GTK3_DIR=
|
|
||||||
endif
|
|
||||||
|
|
||||||
if USE_QT
|
if USE_QT
|
||||||
QT_DIR=qt
|
QT_DIR=qt
|
||||||
else
|
else
|
||||||
|
@ -437,7 +431,6 @@ SUBDIRS=\
|
||||||
$(DTS_DIR) \
|
$(DTS_DIR) \
|
||||||
$(RESINDVD_DIR) \
|
$(RESINDVD_DIR) \
|
||||||
$(GL_DIR) \
|
$(GL_DIR) \
|
||||||
$(GTK3_DIR) \
|
|
||||||
$(QT_DIR) \
|
$(QT_DIR) \
|
||||||
$(FAAC_DIR) \
|
$(FAAC_DIR) \
|
||||||
$(FAAD_DIR) \
|
$(FAAD_DIR) \
|
||||||
|
@ -519,7 +512,6 @@ DIST_SUBDIRS = \
|
||||||
daala \
|
daala \
|
||||||
dts \
|
dts \
|
||||||
gl \
|
gl \
|
||||||
gtk \
|
|
||||||
qt \
|
qt \
|
||||||
modplug \
|
modplug \
|
||||||
mpeg2enc \
|
mpeg2enc \
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
# preamble
|
|
||||||
NULL =
|
|
||||||
BUILT_SOURCES =
|
|
||||||
CLEANFILES =
|
|
||||||
EXTRA_DIST =
|
|
||||||
DISTCLEANFILES =
|
|
||||||
lib_LTLIBRARIES =
|
|
||||||
|
|
||||||
# source
|
|
||||||
sources = \
|
|
||||||
gtkgstbasewidget.c \
|
|
||||||
gtkgstbasewidget.h \
|
|
||||||
gtkgstwidget.c \
|
|
||||||
gtkgstwidget.h \
|
|
||||||
gstgtkbasesink.c \
|
|
||||||
gstgtkbasesink.h \
|
|
||||||
gstgtksink.c \
|
|
||||||
gstgtksink.h \
|
|
||||||
gstgtkutils.c \
|
|
||||||
gstgtkutils.h \
|
|
||||||
gstplugin.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
libgstgtk_la_CFLAGS = \
|
|
||||||
$(GST_PLUGINS_BAD_CFLAGS) \
|
|
||||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
|
||||||
$(GST_GL_CFLAGS) \
|
|
||||||
$(GST_CFLAGS) \
|
|
||||||
$(GTK3_CFLAGS)
|
|
||||||
libgstgtk_la_LIBADD = \
|
|
||||||
$(GST_PLUGINS_BASE_LIBS) \
|
|
||||||
-lgstvideo-$(GST_API_VERSION) \
|
|
||||||
$(GST_BASE_LIBS) \
|
|
||||||
$(GTK3_LIBS)
|
|
||||||
|
|
||||||
libgstgtk_la_SOURCES = $(sources)
|
|
||||||
libgstgtk_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
|
||||||
|
|
||||||
if USE_GTK3_GL
|
|
||||||
if USE_GL
|
|
||||||
libgstgtk_la_SOURCES += gstgtkglsink.c gstgtkglsink.h gtkgstglwidget.c gtkgstglwidget.h
|
|
||||||
libgstgtk_la_LIBADD += $(GST_GL_LIBS)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
plugin_LTLIBRARIES = libgstgtk.la
|
|
|
@ -1,495 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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:gtkgstsink
|
|
||||||
* @title: GstGtkBaseSink
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gstgtkbasesink.h"
|
|
||||||
#include "gstgtkutils.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (gst_debug_gtk_base_sink);
|
|
||||||
#define GST_CAT_DEFAULT gst_debug_gtk_base_sink
|
|
||||||
|
|
||||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
|
||||||
#define DEFAULT_PAR_N 0
|
|
||||||
#define DEFAULT_PAR_D 1
|
|
||||||
#define DEFAULT_IGNORE_ALPHA TRUE
|
|
||||||
|
|
||||||
static void gst_gtk_base_sink_finalize (GObject * object);
|
|
||||||
static void gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * param_spec);
|
|
||||||
static void gst_gtk_base_sink_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * param_spec);
|
|
||||||
|
|
||||||
static gboolean gst_gtk_base_sink_start (GstBaseSink * bsink);
|
|
||||||
static gboolean gst_gtk_base_sink_stop (GstBaseSink * bsink);
|
|
||||||
|
|
||||||
static GstStateChangeReturn
|
|
||||||
gst_gtk_base_sink_change_state (GstElement * element,
|
|
||||||
GstStateChange transition);
|
|
||||||
|
|
||||||
static void gst_gtk_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
|
||||||
GstClockTime * start, GstClockTime * end);
|
|
||||||
static gboolean gst_gtk_base_sink_set_caps (GstBaseSink * bsink,
|
|
||||||
GstCaps * caps);
|
|
||||||
static GstFlowReturn gst_gtk_base_sink_show_frame (GstVideoSink * bsink,
|
|
||||||
GstBuffer * buf);
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface);
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PROP_0,
|
|
||||||
PROP_WIDGET,
|
|
||||||
PROP_FORCE_ASPECT_RATIO,
|
|
||||||
PROP_PIXEL_ASPECT_RATIO,
|
|
||||||
PROP_IGNORE_ALPHA,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define gst_gtk_base_sink_parent_class parent_class
|
|
||||||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGtkBaseSink, gst_gtk_base_sink,
|
|
||||||
GST_TYPE_VIDEO_SINK,
|
|
||||||
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
|
||||||
gst_gtk_base_sink_navigation_interface_init);
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink,
|
|
||||||
"gtkbasesink", 0, "Gtk Video Sink base class"));
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_class_init (GstGtkBaseSinkClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class;
|
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstBaseSinkClass *gstbasesink_class;
|
|
||||||
GstVideoSinkClass *gstvideosink_class;
|
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
|
||||||
gstelement_class = (GstElementClass *) klass;
|
|
||||||
gstbasesink_class = (GstBaseSinkClass *) klass;
|
|
||||||
gstvideosink_class = (GstVideoSinkClass *) klass;
|
|
||||||
|
|
||||||
gobject_class->set_property = gst_gtk_base_sink_set_property;
|
|
||||||
gobject_class->get_property = gst_gtk_base_sink_get_property;
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_WIDGET,
|
|
||||||
g_param_spec_object ("widget", "Gtk Widget",
|
|
||||||
"The GtkWidget to place in the widget hierarchy "
|
|
||||||
"(must only be get from the GTK main thread)",
|
|
||||||
GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
|
|
||||||
g_param_spec_boolean ("force-aspect-ratio",
|
|
||||||
"Force aspect ratio",
|
|
||||||
"When enabled, scaling will respect original aspect ratio",
|
|
||||||
DEFAULT_FORCE_ASPECT_RATIO,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
|
|
||||||
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
|
||||||
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
|
|
||||||
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_IGNORE_ALPHA,
|
|
||||||
g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
|
|
||||||
"When enabled, alpha will be ignored and converted to black",
|
|
||||||
DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
gobject_class->finalize = gst_gtk_base_sink_finalize;
|
|
||||||
|
|
||||||
gstelement_class->change_state = gst_gtk_base_sink_change_state;
|
|
||||||
gstbasesink_class->set_caps = gst_gtk_base_sink_set_caps;
|
|
||||||
gstbasesink_class->get_times = gst_gtk_base_sink_get_times;
|
|
||||||
gstbasesink_class->start = gst_gtk_base_sink_start;
|
|
||||||
gstbasesink_class->stop = gst_gtk_base_sink_stop;
|
|
||||||
|
|
||||||
gstvideosink_class->show_frame = gst_gtk_base_sink_show_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_init (GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
gtk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
|
||||||
gtk_sink->par_n = DEFAULT_PAR_N;
|
|
||||||
gtk_sink->par_d = DEFAULT_PAR_D;
|
|
||||||
gtk_sink->ignore_alpha = DEFAULT_IGNORE_ALPHA;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->window && gtk_sink->window_destroy_id)
|
|
||||||
g_signal_handler_disconnect (gtk_sink->window, gtk_sink->window_destroy_id);
|
|
||||||
if (gtk_sink->widget && gtk_sink->widget_destroy_id)
|
|
||||||
g_signal_handler_disconnect (gtk_sink->widget, gtk_sink->widget_destroy_id);
|
|
||||||
|
|
||||||
g_clear_object (>k_sink->widget);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
widget_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
g_clear_object (>k_sink->widget);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
window_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
gtk_sink->window = NULL;
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GtkGstBaseWidget *
|
|
||||||
gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
if (gtk_sink->widget != NULL)
|
|
||||||
return gtk_sink->widget;
|
|
||||||
|
|
||||||
/* Ensure GTK is initialized, this has no side effect if it was already
|
|
||||||
* initialized. Also, we do that lazily, so the application can be first */
|
|
||||||
if (!gtk_init_check (NULL, NULL)) {
|
|
||||||
GST_ERROR_OBJECT (gtk_sink, "Could not ensure GTK initialization.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_assert (GST_GTK_BASE_SINK_GET_CLASS (gtk_sink)->create_widget);
|
|
||||||
gtk_sink->widget = (GtkGstBaseWidget *)
|
|
||||||
GST_GTK_BASE_SINK_GET_CLASS (gtk_sink)->create_widget ();
|
|
||||||
|
|
||||||
gtk_sink->bind_aspect_ratio =
|
|
||||||
g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget,
|
|
||||||
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
gtk_sink->bind_pixel_aspect_ratio =
|
|
||||||
g_object_bind_property (gtk_sink, "pixel-aspect-ratio", gtk_sink->widget,
|
|
||||||
"pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
gtk_sink->bind_ignore_alpha =
|
|
||||||
g_object_bind_property (gtk_sink, "ignore-alpha", gtk_sink->widget,
|
|
||||||
"ignore-alpha", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
|
|
||||||
/* Take the floating ref, other wise the destruction of the container will
|
|
||||||
* make this widget disapear possibly before we are done. */
|
|
||||||
gst_object_ref_sink (gtk_sink->widget);
|
|
||||||
gtk_sink->widget_destroy_id = g_signal_connect (gtk_sink->widget, "destroy",
|
|
||||||
G_CALLBACK (widget_destroy_cb), gtk_sink);
|
|
||||||
|
|
||||||
/* back pointer */
|
|
||||||
gtk_gst_base_widget_set_element (GTK_GST_BASE_WIDGET (gtk_sink->widget),
|
|
||||||
GST_ELEMENT (gtk_sink));
|
|
||||||
|
|
||||||
return gtk_sink->widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_WIDGET:
|
|
||||||
{
|
|
||||||
GObject *widget = NULL;
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->widget != NULL)
|
|
||||||
widget = G_OBJECT (gtk_sink->widget);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (!widget)
|
|
||||||
widget =
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_get_widget,
|
|
||||||
gtk_sink);
|
|
||||||
|
|
||||||
g_value_set_object (value, widget);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PROP_FORCE_ASPECT_RATIO:
|
|
||||||
g_value_set_boolean (value, gtk_sink->force_aspect_ratio);
|
|
||||||
break;
|
|
||||||
case PROP_PIXEL_ASPECT_RATIO:
|
|
||||||
gst_value_set_fraction (value, gtk_sink->par_n, gtk_sink->par_d);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_ALPHA:
|
|
||||||
g_value_set_boolean (value, gtk_sink->ignore_alpha);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_FORCE_ASPECT_RATIO:
|
|
||||||
gtk_sink->force_aspect_ratio = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
case PROP_PIXEL_ASPECT_RATIO:
|
|
||||||
gtk_sink->par_n = gst_value_get_fraction_numerator (value);
|
|
||||||
gtk_sink->par_d = gst_value_get_fraction_denominator (value);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_ALPHA:
|
|
||||||
gtk_sink->ignore_alpha = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_navigation_send_event (GstNavigation * navigation,
|
|
||||||
GstStructure * structure)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *sink = GST_GTK_BASE_SINK (navigation);
|
|
||||||
GstEvent *event;
|
|
||||||
GstPad *pad;
|
|
||||||
|
|
||||||
event = gst_event_new_navigation (structure);
|
|
||||||
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
|
||||||
|
|
||||||
GST_TRACE_OBJECT (sink, "navigation event %" GST_PTR_FORMAT, structure);
|
|
||||||
|
|
||||||
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
|
|
||||||
if (!gst_pad_send_event (pad, gst_event_ref (event))) {
|
|
||||||
/* If upstream didn't handle the event we'll post a message with it
|
|
||||||
* for the application in case it wants to do something with it */
|
|
||||||
gst_element_post_message (GST_ELEMENT_CAST (sink),
|
|
||||||
gst_navigation_message_new_event (GST_OBJECT_CAST (sink), event));
|
|
||||||
}
|
|
||||||
gst_event_unref (event);
|
|
||||||
gst_object_unref (pad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface)
|
|
||||||
{
|
|
||||||
iface->send_event = gst_gtk_base_sink_navigation_send_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_start_on_main (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
GstGtkBaseSinkClass *klass = GST_GTK_BASE_SINK_GET_CLASS (bsink);
|
|
||||||
GtkWidget *toplevel;
|
|
||||||
|
|
||||||
if (gst_gtk_base_sink_get_widget (gst_sink) == NULL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* After this point, gtk_sink->widget will always be set */
|
|
||||||
|
|
||||||
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (gst_sink->widget));
|
|
||||||
if (!gtk_widget_is_toplevel (toplevel)) {
|
|
||||||
/* sanity check */
|
|
||||||
g_assert (klass->window_title);
|
|
||||||
|
|
||||||
/* User did not add widget its own UI, let's popup a new GtkWindow to
|
|
||||||
* make gst-launch-1.0 work. */
|
|
||||||
gst_sink->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_window_set_default_size (GTK_WINDOW (gst_sink->window), 640, 480);
|
|
||||||
gtk_window_set_title (GTK_WINDOW (gst_sink->window), klass->window_title);
|
|
||||||
gtk_container_add (GTK_CONTAINER (gst_sink->window), toplevel);
|
|
||||||
gst_sink->window_destroy_id = g_signal_connect (gst_sink->window, "destroy",
|
|
||||||
G_CALLBACK (window_destroy_cb), gst_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_start (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
return ! !gst_gtk_invoke_on_main ((GThreadFunc)
|
|
||||||
gst_gtk_base_sink_start_on_main, bsink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_stop_on_main (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (gst_sink->window) {
|
|
||||||
gtk_widget_destroy (gst_sink->window);
|
|
||||||
gst_sink->window = NULL;
|
|
||||||
gst_sink->widget = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_stop (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (gst_sink->window)
|
|
||||||
return ! !gst_gtk_invoke_on_main ((GThreadFunc)
|
|
||||||
gst_gtk_base_sink_stop_on_main, bsink);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_widget_show_all_and_unref (GtkWidget * widget)
|
|
||||||
{
|
|
||||||
gtk_widget_show_all (widget);
|
|
||||||
g_object_unref (widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstStateChangeReturn
|
|
||||||
gst_gtk_base_sink_change_state (GstElement * element, GstStateChange transition)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (element);
|
|
||||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
|
|
||||||
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
|
||||||
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
|
||||||
|
|
||||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
switch (transition) {
|
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
||||||
{
|
|
||||||
GtkWindow *window = NULL;
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->window)
|
|
||||||
window = g_object_ref (gtk_sink->window);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (window)
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_widget_show_all_and_unref,
|
|
||||||
window);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->widget)
|
|
||||||
gtk_gst_base_widget_set_buffer (gtk_sink->widget, NULL);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
|
||||||
GstClockTime * start, GstClockTime * end)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink;
|
|
||||||
|
|
||||||
gtk_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
|
|
||||||
*start = GST_BUFFER_TIMESTAMP (buf);
|
|
||||||
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
|
||||||
*end = *start + GST_BUFFER_DURATION (buf);
|
|
||||||
else {
|
|
||||||
if (GST_VIDEO_INFO_FPS_N (>k_sink->v_info) > 0) {
|
|
||||||
*end = *start +
|
|
||||||
gst_util_uint64_scale_int (GST_SECOND,
|
|
||||||
GST_VIDEO_INFO_FPS_D (>k_sink->v_info),
|
|
||||||
GST_VIDEO_INFO_FPS_N (>k_sink->v_info));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
gst_gtk_base_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
|
|
||||||
|
|
||||||
if (!gst_video_info_from_caps (>k_sink->v_info, caps))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (gtk_sink->widget == NULL) {
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
GST_ELEMENT_ERROR (gtk_sink, RESOURCE, NOT_FOUND,
|
|
||||||
("%s", "Output widget was destroyed"), (NULL));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gtk_gst_base_widget_set_format (gtk_sink->widget, >k_sink->v_info)) {
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_gtk_base_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink;
|
|
||||||
|
|
||||||
GST_TRACE ("rendering buffer:%p", buf);
|
|
||||||
|
|
||||||
gtk_sink = GST_GTK_BASE_SINK (vsink);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (vsink);
|
|
||||||
|
|
||||||
if (gtk_sink->widget == NULL) {
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
GST_ELEMENT_ERROR (gtk_sink, RESOURCE, NOT_FOUND,
|
|
||||||
("%s", "Output widget was destroyed"), (NULL));
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_gst_base_widget_set_buffer (gtk_sink->widget, buf);
|
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GST_GTK_BASE_SINK_H__
|
|
||||||
#define __GST_GTK_BASE_SINK_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/video/gstvideosink.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
#include "gtkgstbasewidget.h"
|
|
||||||
|
|
||||||
#define GST_TYPE_GTK_BASE_SINK (gst_gtk_base_sink_get_type())
|
|
||||||
#define GST_GTK_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GTK_BASE_SINK,GstGtkBaseSink))
|
|
||||||
#define GST_GTK_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GTK_BASE_SINK,GstGtkBaseSinkClass))
|
|
||||||
#define GST_GTK_BASE_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GTK_BASE_SINK, GstGtkBaseSinkClass))
|
|
||||||
#define GST_IS_GTK_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GTK_BASE_SINK))
|
|
||||||
#define GST_IS_GTK_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GTK_BASE_SINK))
|
|
||||||
#define GST_GTK_BASE_SINK_CAST(obj) ((GstGtkBaseSink*)(obj))
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _GstGtkBaseSink GstGtkBaseSink;
|
|
||||||
typedef struct _GstGtkBaseSinkClass GstGtkBaseSinkClass;
|
|
||||||
|
|
||||||
GType gst_gtk_base_sink_get_type (void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkBaseSink:
|
|
||||||
*
|
|
||||||
* Opaque #GstGtkBaseSink object
|
|
||||||
*/
|
|
||||||
struct _GstGtkBaseSink
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstVideoSink parent;
|
|
||||||
|
|
||||||
GstVideoInfo v_info;
|
|
||||||
|
|
||||||
GtkGstBaseWidget *widget;
|
|
||||||
|
|
||||||
/* properties */
|
|
||||||
gboolean force_aspect_ratio;
|
|
||||||
GBinding *bind_aspect_ratio;
|
|
||||||
|
|
||||||
gint par_n;
|
|
||||||
gint par_d;
|
|
||||||
GBinding *bind_pixel_aspect_ratio;
|
|
||||||
|
|
||||||
gboolean ignore_alpha;
|
|
||||||
GBinding *bind_ignore_alpha;
|
|
||||||
|
|
||||||
GtkWidget *window;
|
|
||||||
gulong widget_destroy_id;
|
|
||||||
gulong window_destroy_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkBaseSinkClass:
|
|
||||||
*
|
|
||||||
* The #GstGtkBaseSinkClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GstGtkBaseSinkClass
|
|
||||||
{
|
|
||||||
GstVideoSinkClass object_class;
|
|
||||||
|
|
||||||
/* metadata */
|
|
||||||
const gchar *window_title;
|
|
||||||
|
|
||||||
/* virtuals */
|
|
||||||
GtkWidget* (*create_widget) (void);
|
|
||||||
};
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GST_GTK_BASE_SINK_H__ */
|
|
|
@ -1,311 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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-gtkglsink
|
|
||||||
* @title: gtkglsink
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <gst/gl/gstglfuncs.h>
|
|
||||||
|
|
||||||
#include "gstgtkglsink.h"
|
|
||||||
#include "gtkgstglwidget.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
|
|
||||||
#define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
|
|
||||||
|
|
||||||
static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink);
|
|
||||||
static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink);
|
|
||||||
static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
|
|
||||||
static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink,
|
|
||||||
GstQuery * query);
|
|
||||||
static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
|
|
||||||
GstCaps * filter);
|
|
||||||
|
|
||||||
static GstStaticPadTemplate gst_gtk_gl_sink_template =
|
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
||||||
GST_PAD_SINK,
|
|
||||||
GST_PAD_ALWAYS,
|
|
||||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
||||||
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
|
|
||||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
||||||
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
|
|
||||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
|
|
||||||
|
|
||||||
#define gst_gtk_gl_sink_parent_class parent_class
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
|
|
||||||
GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
|
|
||||||
"gtkglsink", 0, "Gtk GL Video Sink"));
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
|
|
||||||
{
|
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstBaseSinkClass *gstbasesink_class;
|
|
||||||
GstGtkBaseSinkClass *gstgtkbasesink_class;
|
|
||||||
|
|
||||||
gstelement_class = (GstElementClass *) klass;
|
|
||||||
gstbasesink_class = (GstBaseSinkClass *) klass;
|
|
||||||
gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
|
|
||||||
|
|
||||||
gstbasesink_class->query = gst_gtk_gl_sink_query;
|
|
||||||
gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
|
|
||||||
gstbasesink_class->start = gst_gtk_gl_sink_start;
|
|
||||||
gstbasesink_class->stop = gst_gtk_gl_sink_stop;
|
|
||||||
gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
|
|
||||||
|
|
||||||
gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
|
|
||||||
gstgtkbasesink_class->window_title = "Gtk+ GL renderer";
|
|
||||||
|
|
||||||
gst_element_class_set_metadata (gstelement_class, "Gtk GL Video Sink",
|
|
||||||
"Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
|
|
||||||
"Matthew Waters <matthew@centricular.com>");
|
|
||||||
|
|
||||||
gst_element_class_add_static_pad_template (gstelement_class,
|
|
||||||
&gst_gtk_gl_sink_template);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
|
||||||
case GST_QUERY_CONTEXT:
|
|
||||||
{
|
|
||||||
if (gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
|
|
||||||
gtk_sink->display, gtk_sink->context, gtk_sink->gtk_context))
|
|
||||||
return TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
|
|
||||||
GstGtkGLSink * gtk_sink)
|
|
||||||
{
|
|
||||||
gint scale_factor, width, height;
|
|
||||||
gboolean reconfigure;
|
|
||||||
|
|
||||||
scale_factor = gtk_widget_get_scale_factor (widget);
|
|
||||||
width = scale_factor * gtk_widget_get_allocated_width (widget);
|
|
||||||
height = scale_factor * gtk_widget_get_allocated_height (widget);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
reconfigure =
|
|
||||||
(width != gtk_sink->display_width || height != gtk_sink->display_height);
|
|
||||||
gtk_sink->display_width = width;
|
|
||||||
gtk_sink->display_height = height;
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (reconfigure) {
|
|
||||||
GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad.");
|
|
||||||
gst_pad_push_event (GST_BASE_SINK (gtk_sink)->sinkpad,
|
|
||||||
gst_event_new_reconfigure ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_start (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
GtkGstGLWidget *gst_widget;
|
|
||||||
|
|
||||||
if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* After this point, gtk_sink->widget will always be set */
|
|
||||||
gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
|
|
||||||
|
|
||||||
/* Track the allocation size */
|
|
||||||
g_signal_connect (gst_widget, "size-allocate", G_CALLBACK (_size_changed_cb),
|
|
||||||
gtk_sink);
|
|
||||||
_size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink);
|
|
||||||
|
|
||||||
if (!gtk_gst_gl_widget_init_winsys (gst_widget))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
|
|
||||||
gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
|
|
||||||
gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
|
|
||||||
|
|
||||||
if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
|
|
||||||
gtk_sink->display);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_stop (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
|
|
||||||
if (gtk_sink->display) {
|
|
||||||
gst_object_unref (gtk_sink->display);
|
|
||||||
gtk_sink->display = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gtk_sink->context) {
|
|
||||||
gst_object_unref (gtk_sink->context);
|
|
||||||
gtk_sink->context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gtk_sink->gtk_context) {
|
|
||||||
gst_object_unref (gtk_sink->gtk_context);
|
|
||||||
gtk_sink->gtk_context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
GstBufferPool *pool = NULL;
|
|
||||||
GstStructure *config;
|
|
||||||
GstCaps *caps;
|
|
||||||
GstVideoInfo info;
|
|
||||||
guint size;
|
|
||||||
gboolean need_pool;
|
|
||||||
GstStructure *allocation_meta = NULL;
|
|
||||||
gint display_width, display_height;
|
|
||||||
|
|
||||||
if (!gtk_sink->display || !gtk_sink->context)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gst_query_parse_allocation (query, &caps, &need_pool);
|
|
||||||
|
|
||||||
if (caps == NULL)
|
|
||||||
goto no_caps;
|
|
||||||
|
|
||||||
if (!gst_video_info_from_caps (&info, caps))
|
|
||||||
goto invalid_caps;
|
|
||||||
|
|
||||||
/* the normal size of a frame */
|
|
||||||
size = info.size;
|
|
||||||
|
|
||||||
if (need_pool) {
|
|
||||||
GST_DEBUG_OBJECT (gtk_sink, "create new pool");
|
|
||||||
pool = gst_gl_buffer_pool_new (gtk_sink->context);
|
|
||||||
|
|
||||||
config = gst_buffer_pool_get_config (pool);
|
|
||||||
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
|
|
||||||
gst_buffer_pool_config_add_option (config,
|
|
||||||
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
|
||||||
|
|
||||||
if (!gst_buffer_pool_set_config (pool, config))
|
|
||||||
goto config_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we need at least 2 buffer because we hold on to the last one */
|
|
||||||
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
|
||||||
if (pool)
|
|
||||||
gst_object_unref (pool);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
display_width = gtk_sink->display_width;
|
|
||||||
display_height = gtk_sink->display_height;
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (display_width != 0 && display_height != 0) {
|
|
||||||
GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
|
|
||||||
display_width, display_height);
|
|
||||||
allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
|
|
||||||
"width", G_TYPE_UINT, display_width,
|
|
||||||
"height", G_TYPE_UINT, display_height, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_query_add_allocation_meta (query,
|
|
||||||
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
|
|
||||||
|
|
||||||
if (allocation_meta)
|
|
||||||
gst_structure_free (allocation_meta);
|
|
||||||
|
|
||||||
/* we also support various metadata */
|
|
||||||
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
|
|
||||||
|
|
||||||
if (gtk_sink->context->gl_vtable->FenceSync)
|
|
||||||
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
no_caps:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "no caps specified");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
invalid_caps:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "invalid caps specified");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
config_failed:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "failed setting config");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstCaps *
|
|
||||||
gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
|
|
||||||
{
|
|
||||||
GstCaps *tmp = NULL;
|
|
||||||
GstCaps *result = NULL;
|
|
||||||
|
|
||||||
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
|
|
||||||
filter);
|
|
||||||
|
|
||||||
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
|
||||||
gst_caps_unref (tmp);
|
|
||||||
} else {
|
|
||||||
result = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = gst_gl_overlay_compositor_add_caps (result);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GST_GTK_GL_SINK_H__
|
|
||||||
#define __GST_GTK_GL_SINK_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/video/gstvideosink.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
#include <gst/gl/gl.h>
|
|
||||||
|
|
||||||
#include "gstgtkbasesink.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define GST_TYPE_GTK_GL_SINK (gst_gtk_gl_sink_get_type())
|
|
||||||
#define GST_GTK_GL_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GTK_GL_SINK,GstGtkGLSink))
|
|
||||||
#define GST_GTK_GL_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GTK_GL_SINK,GstGtkGLSinkClass))
|
|
||||||
#define GST_IS_GTK_GL_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GTK_GL_SINK))
|
|
||||||
#define GST_IS_GTK_GL_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GTK_GL_SINK))
|
|
||||||
#define GST_GTK_GL_SINK_CAST(obj) ((GstGtkGLSink*)(obj))
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _GstGtkGLSink GstGtkGLSink;
|
|
||||||
typedef struct _GstGtkGLSinkClass GstGtkGLSinkClass;
|
|
||||||
|
|
||||||
GType gst_gtk_gl_sink_get_type (void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkGLSink:
|
|
||||||
*
|
|
||||||
* Opaque #GstGtkGLSink object
|
|
||||||
*/
|
|
||||||
struct _GstGtkGLSink
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstGtkBaseSink parent;
|
|
||||||
|
|
||||||
GstGLDisplay *display;
|
|
||||||
GstGLContext *context;
|
|
||||||
GstGLContext *gtk_context;
|
|
||||||
|
|
||||||
GstGLUpload *upload;
|
|
||||||
GstBuffer *uploaded_buffer;
|
|
||||||
|
|
||||||
/* read/write with object lock */
|
|
||||||
gint display_width;
|
|
||||||
gint display_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkGLSinkClass:
|
|
||||||
*
|
|
||||||
* The #GstGtkGLSinkClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GstGtkGLSinkClass
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstGtkBaseSinkClass object_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GST_GTK_GL_SINK_H__ */
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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-gtkgstsink
|
|
||||||
* @title: gtkgstsink
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gtkgstwidget.h"
|
|
||||||
#include "gstgtksink.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (gst_debug_gtk_sink);
|
|
||||||
#define GST_CAT_DEFAULT gst_debug_gtk_sink
|
|
||||||
|
|
||||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
||||||
#define FORMATS "{ BGRx, BGRA }"
|
|
||||||
#else
|
|
||||||
#define FORMATS "{ xRGB, ARGB }"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static GstStaticPadTemplate gst_gtk_sink_template =
|
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
||||||
GST_PAD_SINK,
|
|
||||||
GST_PAD_ALWAYS,
|
|
||||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS))
|
|
||||||
);
|
|
||||||
|
|
||||||
#define gst_gtk_sink_parent_class parent_class
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GstGtkSink, gst_gtk_sink, GST_TYPE_GTK_BASE_SINK,
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_sink, "gtksink", 0,
|
|
||||||
"Gtk Video Sink"));
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_sink_class_init (GstGtkSinkClass * klass)
|
|
||||||
{
|
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstGtkBaseSinkClass *base_class;
|
|
||||||
|
|
||||||
gstelement_class = (GstElementClass *) klass;
|
|
||||||
base_class = (GstGtkBaseSinkClass *) klass;
|
|
||||||
|
|
||||||
base_class->create_widget = gtk_gst_widget_new;
|
|
||||||
base_class->window_title = "Gtk+ Cairo renderer";
|
|
||||||
|
|
||||||
gst_element_class_set_metadata (gstelement_class, "Gtk Video Sink",
|
|
||||||
"Sink/Video", "A video sink that renders to a GtkWidget",
|
|
||||||
"Matthew Waters <matthew@centricular.com>");
|
|
||||||
|
|
||||||
gst_element_class_add_static_pad_template (gstelement_class,
|
|
||||||
&gst_gtk_sink_template);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_sink_init (GstGtkSink * gtk_sink)
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GST_GTK_SINK_H__
|
|
||||||
#define __GST_GTK_SINK_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/video/gstvideosink.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
#include "gstgtkbasesink.h"
|
|
||||||
|
|
||||||
#define GST_TYPE_GTK_SINK (gst_gtk_sink_get_type())
|
|
||||||
#define GST_GTK_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GTK_SINK,GstGtkSink))
|
|
||||||
#define GST_GTK_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GTK_SINK,GstGtkSinkClass))
|
|
||||||
#define GST_IS_GTK_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GTK_SINK))
|
|
||||||
#define GST_IS_GTK_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GTK_SINK))
|
|
||||||
#define GST_GTK_SINK_CAST(obj) ((GstGtkSink*)(obj))
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _GstGtkSink GstGtkSink;
|
|
||||||
typedef struct _GstGtkSinkClass GstGtkSinkClass;
|
|
||||||
|
|
||||||
GType gst_gtk_sink_get_type (void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkSink:
|
|
||||||
*
|
|
||||||
* Opaque #GstGtkSink object
|
|
||||||
*/
|
|
||||||
struct _GstGtkSink
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstGtkBaseSink parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkSinkClass:
|
|
||||||
*
|
|
||||||
* The #GstGtkSinkClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GstGtkSinkClass
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstGtkBaseSinkClass object_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GST_GTK_SINK_H__ */
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "gstgtkutils.h"
|
|
||||||
|
|
||||||
struct invoke_context
|
|
||||||
{
|
|
||||||
GThreadFunc func;
|
|
||||||
gpointer data;
|
|
||||||
GMutex lock;
|
|
||||||
GCond cond;
|
|
||||||
gboolean fired;
|
|
||||||
|
|
||||||
gpointer res;
|
|
||||||
};
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_invoke_func (struct invoke_context *info)
|
|
||||||
{
|
|
||||||
g_mutex_lock (&info->lock);
|
|
||||||
info->res = info->func (info->data);
|
|
||||||
info->fired = TRUE;
|
|
||||||
g_cond_signal (&info->cond);
|
|
||||||
g_mutex_unlock (&info->lock);
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpointer
|
|
||||||
gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
|
|
||||||
{
|
|
||||||
GMainContext *main_context = g_main_context_default ();
|
|
||||||
struct invoke_context info;
|
|
||||||
|
|
||||||
g_mutex_init (&info.lock);
|
|
||||||
g_cond_init (&info.cond);
|
|
||||||
info.fired = FALSE;
|
|
||||||
info.func = func;
|
|
||||||
info.data = data;
|
|
||||||
|
|
||||||
g_main_context_invoke (main_context, (GSourceFunc) gst_gtk_invoke_func,
|
|
||||||
&info);
|
|
||||||
|
|
||||||
g_mutex_lock (&info.lock);
|
|
||||||
while (!info.fired)
|
|
||||||
g_cond_wait (&info.cond, &info.lock);
|
|
||||||
g_mutex_unlock (&info.lock);
|
|
||||||
|
|
||||||
g_mutex_clear (&info.lock);
|
|
||||||
g_cond_clear (&info.cond);
|
|
||||||
|
|
||||||
return info.res;
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GST_GTK_UTILS_H__
|
|
||||||
#define __GST_GTK_UTILS_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
|
|
||||||
|
|
||||||
#endif /* __GST_GTK_UTILS_H__ */
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gstgtksink.h"
|
|
||||||
#if defined(HAVE_GTK3_GL)
|
|
||||||
#include "gstgtkglsink.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
plugin_init (GstPlugin * plugin)
|
|
||||||
{
|
|
||||||
if (!gst_element_register (plugin, "gtksink",
|
|
||||||
GST_RANK_NONE, GST_TYPE_GTK_SINK)) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
#if defined(HAVE_GTK3_GL)
|
|
||||||
if (!gst_element_register (plugin, "gtkglsink",
|
|
||||||
GST_RANK_NONE, GST_TYPE_GTK_GL_SINK)) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
||||||
GST_VERSION_MINOR,
|
|
||||||
gtk,
|
|
||||||
"Gtk+ sink",
|
|
||||||
plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME,
|
|
||||||
GST_PACKAGE_ORIGIN)
|
|
|
@ -1,497 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "gtkgstbasewidget.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (gst_debug_gtk_base_widget);
|
|
||||||
#define GST_CAT_DEFAULT gst_debug_gtk_base_widget
|
|
||||||
|
|
||||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
|
||||||
#define DEFAULT_PAR_N 0
|
|
||||||
#define DEFAULT_PAR_D 1
|
|
||||||
#define DEFAULT_IGNORE_ALPHA TRUE
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PROP_0,
|
|
||||||
PROP_FORCE_ASPECT_RATIO,
|
|
||||||
PROP_PIXEL_ASPECT_RATIO,
|
|
||||||
PROP_IGNORE_ALPHA,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_base_widget_get_preferred_width (GtkWidget * widget, gint * min,
|
|
||||||
gint * natural)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
|
|
||||||
gint video_width = gst_widget->display_width;
|
|
||||||
|
|
||||||
if (!gst_widget->negotiated)
|
|
||||||
video_width = 10;
|
|
||||||
|
|
||||||
if (min)
|
|
||||||
*min = 1;
|
|
||||||
if (natural)
|
|
||||||
*natural = video_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_base_widget_get_preferred_height (GtkWidget * widget, gint * min,
|
|
||||||
gint * natural)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
|
|
||||||
gint video_height = gst_widget->display_height;
|
|
||||||
|
|
||||||
if (!gst_widget->negotiated)
|
|
||||||
video_height = 10;
|
|
||||||
|
|
||||||
if (min)
|
|
||||||
*min = 1;
|
|
||||||
if (natural)
|
|
||||||
*natural = video_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_base_widget_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_FORCE_ASPECT_RATIO:
|
|
||||||
gtk_widget->force_aspect_ratio = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
case PROP_PIXEL_ASPECT_RATIO:
|
|
||||||
gtk_widget->par_n = gst_value_get_fraction_numerator (value);
|
|
||||||
gtk_widget->par_d = gst_value_get_fraction_denominator (value);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_ALPHA:
|
|
||||||
gtk_widget->ignore_alpha = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_base_widget_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_FORCE_ASPECT_RATIO:
|
|
||||||
g_value_set_boolean (value, gtk_widget->force_aspect_ratio);
|
|
||||||
break;
|
|
||||||
case PROP_PIXEL_ASPECT_RATIO:
|
|
||||||
gst_value_set_fraction (value, gtk_widget->par_n, gtk_widget->par_d);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_ALPHA:
|
|
||||||
g_value_set_boolean (value, gtk_widget->ignore_alpha);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_calculate_par (GtkGstBaseWidget * widget, GstVideoInfo * info)
|
|
||||||
{
|
|
||||||
gboolean ok;
|
|
||||||
gint width, height;
|
|
||||||
gint par_n, par_d;
|
|
||||||
gint display_par_n, display_par_d;
|
|
||||||
|
|
||||||
width = GST_VIDEO_INFO_WIDTH (info);
|
|
||||||
height = GST_VIDEO_INFO_HEIGHT (info);
|
|
||||||
|
|
||||||
par_n = GST_VIDEO_INFO_PAR_N (info);
|
|
||||||
par_d = GST_VIDEO_INFO_PAR_D (info);
|
|
||||||
|
|
||||||
if (!par_n)
|
|
||||||
par_n = 1;
|
|
||||||
|
|
||||||
/* get display's PAR */
|
|
||||||
if (widget->par_n != 0 && widget->par_d != 0) {
|
|
||||||
display_par_n = widget->par_n;
|
|
||||||
display_par_d = widget->par_d;
|
|
||||||
} else {
|
|
||||||
display_par_n = 1;
|
|
||||||
display_par_d = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ok = gst_video_calculate_display_ratio (&widget->display_ratio_num,
|
|
||||||
&widget->display_ratio_den, width, height, par_n, par_d, display_par_n,
|
|
||||||
display_par_d);
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
|
|
||||||
display_par_d);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_apply_par (GtkGstBaseWidget * widget)
|
|
||||||
{
|
|
||||||
guint display_ratio_num, display_ratio_den;
|
|
||||||
gint width, height;
|
|
||||||
|
|
||||||
width = GST_VIDEO_INFO_WIDTH (&widget->v_info);
|
|
||||||
height = GST_VIDEO_INFO_HEIGHT (&widget->v_info);
|
|
||||||
|
|
||||||
display_ratio_num = widget->display_ratio_num;
|
|
||||||
display_ratio_den = widget->display_ratio_den;
|
|
||||||
|
|
||||||
if (height % display_ratio_den == 0) {
|
|
||||||
GST_DEBUG ("keeping video height");
|
|
||||||
widget->display_width = (guint)
|
|
||||||
gst_util_uint64_scale_int (height, display_ratio_num,
|
|
||||||
display_ratio_den);
|
|
||||||
widget->display_height = height;
|
|
||||||
} else if (width % display_ratio_num == 0) {
|
|
||||||
GST_DEBUG ("keeping video width");
|
|
||||||
widget->display_width = width;
|
|
||||||
widget->display_height = (guint)
|
|
||||||
gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG ("approximating while keeping video height");
|
|
||||||
widget->display_width = (guint)
|
|
||||||
gst_util_uint64_scale_int (height, display_ratio_num,
|
|
||||||
display_ratio_den);
|
|
||||||
widget->display_height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG ("scaling to %dx%d", widget->display_width, widget->display_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_queue_draw (GtkGstBaseWidget * widget)
|
|
||||||
{
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
||||||
widget->draw_id = 0;
|
|
||||||
|
|
||||||
if (widget->pending_resize) {
|
|
||||||
widget->pending_resize = FALSE;
|
|
||||||
|
|
||||||
widget->v_info = widget->pending_v_info;
|
|
||||||
widget->negotiated = TRUE;
|
|
||||||
|
|
||||||
_apply_par (widget);
|
|
||||||
|
|
||||||
gtk_widget_queue_resize (GTK_WIDGET (widget));
|
|
||||||
} else {
|
|
||||||
gtk_widget_queue_draw (GTK_WIDGET (widget));
|
|
||||||
}
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const gchar *
|
|
||||||
_gdk_key_to_navigation_string (guint keyval)
|
|
||||||
{
|
|
||||||
/* TODO: expand */
|
|
||||||
switch (keyval) {
|
|
||||||
#define KEY(key) case GDK_KEY_ ## key: return G_STRINGIFY(key)
|
|
||||||
KEY (Up);
|
|
||||||
KEY (Down);
|
|
||||||
KEY (Left);
|
|
||||||
KEY (Right);
|
|
||||||
KEY (Home);
|
|
||||||
KEY (End);
|
|
||||||
#undef KEY
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_base_widget_key_event (GtkWidget * widget, GdkEventKey * event)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
GstElement *element;
|
|
||||||
|
|
||||||
if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
||||||
if (GST_IS_NAVIGATION (element)) {
|
|
||||||
const gchar *str = _gdk_key_to_navigation_string (event->keyval);
|
|
||||||
const gchar *key_type =
|
|
||||||
event->type == GDK_KEY_PRESS ? "key-press" : "key-release";
|
|
||||||
|
|
||||||
if (!str)
|
|
||||||
str = event->string;
|
|
||||||
|
|
||||||
gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
|
|
||||||
}
|
|
||||||
g_object_unref (element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_fit_stream_to_allocated_size (GtkGstBaseWidget * base_widget,
|
|
||||||
GtkAllocation * allocation, GstVideoRectangle * result)
|
|
||||||
{
|
|
||||||
if (base_widget->force_aspect_ratio) {
|
|
||||||
GstVideoRectangle src, dst;
|
|
||||||
|
|
||||||
src.x = 0;
|
|
||||||
src.y = 0;
|
|
||||||
src.w = base_widget->display_width;
|
|
||||||
src.h = base_widget->display_height;
|
|
||||||
|
|
||||||
dst.x = 0;
|
|
||||||
dst.y = 0;
|
|
||||||
dst.w = allocation->width;
|
|
||||||
dst.h = allocation->height;
|
|
||||||
|
|
||||||
gst_video_sink_center_rect (src, dst, result, TRUE);
|
|
||||||
} else {
|
|
||||||
result->x = 0;
|
|
||||||
result->y = 0;
|
|
||||||
result->w = allocation->width;
|
|
||||||
result->h = allocation->height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_display_size_to_stream_size (GtkGstBaseWidget * base_widget, gdouble x,
|
|
||||||
gdouble y, gdouble * stream_x, gdouble * stream_y)
|
|
||||||
{
|
|
||||||
gdouble stream_width, stream_height;
|
|
||||||
GtkAllocation allocation;
|
|
||||||
GstVideoRectangle result;
|
|
||||||
|
|
||||||
gtk_widget_get_allocation (GTK_WIDGET (base_widget), &allocation);
|
|
||||||
_fit_stream_to_allocated_size (base_widget, &allocation, &result);
|
|
||||||
|
|
||||||
stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
|
|
||||||
stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
|
|
||||||
|
|
||||||
/* from display coordinates to stream coordinates */
|
|
||||||
if (result.w > 0)
|
|
||||||
*stream_x = (x - result.x) / result.w * stream_width;
|
|
||||||
else
|
|
||||||
*stream_x = 0.;
|
|
||||||
|
|
||||||
/* clip to stream size */
|
|
||||||
if (*stream_x < 0.)
|
|
||||||
*stream_x = 0.;
|
|
||||||
if (*stream_x > GST_VIDEO_INFO_WIDTH (&base_widget->v_info))
|
|
||||||
*stream_x = GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
|
|
||||||
|
|
||||||
/* same for y-axis */
|
|
||||||
if (result.h > 0)
|
|
||||||
*stream_y = (y - result.y) / result.h * stream_height;
|
|
||||||
else
|
|
||||||
*stream_y = 0.;
|
|
||||||
|
|
||||||
if (*stream_y < 0.)
|
|
||||||
*stream_y = 0.;
|
|
||||||
if (*stream_y > GST_VIDEO_INFO_HEIGHT (&base_widget->v_info))
|
|
||||||
*stream_y = GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
|
|
||||||
|
|
||||||
GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
GstElement *element;
|
|
||||||
|
|
||||||
if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
||||||
if (GST_IS_NAVIGATION (element)) {
|
|
||||||
const gchar *key_type =
|
|
||||||
event->type ==
|
|
||||||
GDK_BUTTON_PRESS ? "mouse-button-press" : "mouse-button-release";
|
|
||||||
gdouble x, y;
|
|
||||||
|
|
||||||
_display_size_to_stream_size (base_widget, event->x, event->y, &x, &y);
|
|
||||||
|
|
||||||
gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type,
|
|
||||||
event->button, x, y);
|
|
||||||
}
|
|
||||||
g_object_unref (element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
GstElement *element;
|
|
||||||
|
|
||||||
if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
||||||
if (GST_IS_NAVIGATION (element)) {
|
|
||||||
gdouble x, y;
|
|
||||||
|
|
||||||
_display_size_to_stream_size (base_widget, event->x, event->y, &x, &y);
|
|
||||||
|
|
||||||
gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move",
|
|
||||||
0, x, y);
|
|
||||||
}
|
|
||||||
g_object_unref (element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_klass = (GObjectClass *) klass;
|
|
||||||
GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
|
|
||||||
|
|
||||||
gobject_klass->set_property = gtk_gst_base_widget_set_property;
|
|
||||||
gobject_klass->get_property = gtk_gst_base_widget_get_property;
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
|
|
||||||
g_param_spec_boolean ("force-aspect-ratio",
|
|
||||||
"Force aspect ratio",
|
|
||||||
"When enabled, scaling will respect original aspect ratio",
|
|
||||||
DEFAULT_FORCE_ASPECT_RATIO,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO,
|
|
||||||
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
|
||||||
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
|
|
||||||
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_klass, PROP_IGNORE_ALPHA,
|
|
||||||
g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
|
|
||||||
"When enabled, alpha will be ignored and converted to black",
|
|
||||||
DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width;
|
|
||||||
widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height;
|
|
||||||
widget_klass->key_press_event = gtk_gst_base_widget_key_event;
|
|
||||||
widget_klass->key_release_event = gtk_gst_base_widget_key_event;
|
|
||||||
widget_klass->button_press_event = gtk_gst_base_widget_button_event;
|
|
||||||
widget_klass->button_release_event = gtk_gst_base_widget_button_event;
|
|
||||||
widget_klass->motion_notify_event = gtk_gst_base_widget_motion_event;
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0,
|
|
||||||
"Gtk Video Base Widget");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
|
|
||||||
{
|
|
||||||
int event_mask;
|
|
||||||
|
|
||||||
widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
|
||||||
widget->par_n = DEFAULT_PAR_N;
|
|
||||||
widget->par_d = DEFAULT_PAR_D;
|
|
||||||
widget->ignore_alpha = DEFAULT_IGNORE_ALPHA;
|
|
||||||
|
|
||||||
gst_video_info_init (&widget->v_info);
|
|
||||||
gst_video_info_init (&widget->pending_v_info);
|
|
||||||
|
|
||||||
g_weak_ref_init (&widget->element, NULL);
|
|
||||||
g_mutex_init (&widget->lock);
|
|
||||||
|
|
||||||
gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
|
|
||||||
event_mask = gtk_widget_get_events (GTK_WIDGET (widget));
|
|
||||||
event_mask |= GDK_KEY_PRESS_MASK
|
|
||||||
| GDK_KEY_RELEASE_MASK
|
|
||||||
| GDK_BUTTON_PRESS_MASK
|
|
||||||
| GDK_BUTTON_RELEASE_MASK
|
|
||||||
| GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK;
|
|
||||||
gtk_widget_set_events (GTK_WIDGET (widget), event_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object);
|
|
||||||
|
|
||||||
gst_buffer_replace (&widget->pending_buffer, NULL);
|
|
||||||
gst_buffer_replace (&widget->buffer, NULL);
|
|
||||||
g_mutex_clear (&widget->lock);
|
|
||||||
g_weak_ref_clear (&widget->element);
|
|
||||||
|
|
||||||
if (widget->draw_id)
|
|
||||||
g_source_remove (widget->draw_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget,
|
|
||||||
GstElement * element)
|
|
||||||
{
|
|
||||||
g_weak_ref_set (&widget->element, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget,
|
|
||||||
GstVideoInfo * v_info)
|
|
||||||
{
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
||||||
|
|
||||||
if (gst_video_info_is_equal (&widget->pending_v_info, v_info)) {
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_calculate_par (widget, v_info)) {
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
widget->pending_resize = TRUE;
|
|
||||||
widget->pending_v_info = *v_info;
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer)
|
|
||||||
{
|
|
||||||
/* As we have no type, this is better then no check */
|
|
||||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
||||||
|
|
||||||
gst_buffer_replace (&widget->pending_buffer, buffer);
|
|
||||||
|
|
||||||
if (!widget->draw_id) {
|
|
||||||
widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
|
||||||
(GSourceFunc) _queue_draw, widget, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GTK_GST_BASE_WIDGET_H__
|
|
||||||
#define __GTK_GST_BASE_WIDGET_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
#define GTK_GST_BASE_WIDGET(w) ((GtkGstBaseWidget *)(w))
|
|
||||||
#define GTK_GST_BASE_WIDGET_CLASS(k) ((GtkGstBaseWidgetClass *)(k))
|
|
||||||
#define GTK_GST_BASE_WIDGET_LOCK(w) g_mutex_lock(&((GtkGstBaseWidget*)(w))->lock)
|
|
||||||
#define GTK_GST_BASE_WIDGET_UNLOCK(w) g_mutex_unlock(&((GtkGstBaseWidget*)(w))->lock)
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _GtkGstBaseWidget GtkGstBaseWidget;
|
|
||||||
typedef struct _GtkGstBaseWidgetClass GtkGstBaseWidgetClass;
|
|
||||||
|
|
||||||
struct _GtkGstBaseWidget
|
|
||||||
{
|
|
||||||
union {
|
|
||||||
GtkDrawingArea drawing_area;
|
|
||||||
#if GTK_CHECK_VERSION(3, 15, 0)
|
|
||||||
GtkGLArea gl_area;
|
|
||||||
#endif
|
|
||||||
} parent;
|
|
||||||
|
|
||||||
/* properties */
|
|
||||||
gboolean force_aspect_ratio;
|
|
||||||
gint par_n, par_d;
|
|
||||||
gboolean ignore_alpha;
|
|
||||||
|
|
||||||
gint display_width;
|
|
||||||
gint display_height;
|
|
||||||
|
|
||||||
gboolean negotiated;
|
|
||||||
GstBuffer *pending_buffer;
|
|
||||||
GstBuffer *buffer;
|
|
||||||
GstVideoInfo v_info;
|
|
||||||
|
|
||||||
/* resize */
|
|
||||||
gboolean pending_resize;
|
|
||||||
GstVideoInfo pending_v_info;
|
|
||||||
guint display_ratio_num;
|
|
||||||
guint display_ratio_den;
|
|
||||||
|
|
||||||
/*< private >*/
|
|
||||||
GMutex lock;
|
|
||||||
GWeakRef element;
|
|
||||||
|
|
||||||
/* Pending draw idles callback */
|
|
||||||
guint draw_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _GtkGstBaseWidgetClass
|
|
||||||
{
|
|
||||||
union {
|
|
||||||
GtkDrawingAreaClass drawing_area_class;
|
|
||||||
#if GTK_CHECK_VERSION(3, 15, 0)
|
|
||||||
GtkGLAreaClass gl_area_class;
|
|
||||||
#endif
|
|
||||||
} parent_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* For implementer */
|
|
||||||
void gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass);
|
|
||||||
void gtk_gst_base_widget_init (GtkGstBaseWidget * widget);
|
|
||||||
|
|
||||||
void gtk_gst_base_widget_finalize (GObject * object);
|
|
||||||
|
|
||||||
/* API */
|
|
||||||
gboolean gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget, GstVideoInfo * v_info);
|
|
||||||
void gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer);
|
|
||||||
void gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget, GstElement * element);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GTK_GST_BASE_WIDGET_H__ */
|
|
|
@ -1,558 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "gtkgstglwidget.h"
|
|
||||||
#include "gstgtkutils.h"
|
|
||||||
#include <gst/gl/gstglfuncs.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (GDK_WINDOWING_X11)
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#include <gst/gl/x11/gstgldisplay_x11.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
|
|
||||||
#include <gdk/gdkwayland.h>
|
|
||||||
#include <gst/gl/wayland/gstgldisplay_wayland.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:gtkgstglwidget
|
|
||||||
* @title: GtkGstGlWidget
|
|
||||||
* @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers
|
|
||||||
* @see_also: #GtkGLArea, #GstBuffer
|
|
||||||
*
|
|
||||||
* #GtkGstGLWidget is an #GtkWidget that renders GStreamer video buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define GST_CAT_DEFAULT gtk_gst_gl_widget_debug
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GtkGstGLWidget, gtk_gst_gl_widget, GTK_TYPE_GL_AREA,
|
|
||||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkgstglwidget", 0,
|
|
||||||
"Gtk Gst GL Widget"););
|
|
||||||
|
|
||||||
#define GTK_GST_GL_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
|
|
||||||
GTK_TYPE_GST_GL_WIDGET, GtkGstGLWidgetPrivate))
|
|
||||||
|
|
||||||
struct _GtkGstGLWidgetPrivate
|
|
||||||
{
|
|
||||||
gboolean initted;
|
|
||||||
GstGLDisplay *display;
|
|
||||||
GdkGLContext *gdk_context;
|
|
||||||
GstGLContext *other_context;
|
|
||||||
GstGLContext *context;
|
|
||||||
GstGLUpload *upload;
|
|
||||||
GstGLShader *shader;
|
|
||||||
GLuint vao;
|
|
||||||
GLuint vertex_buffer;
|
|
||||||
GLint attr_position;
|
|
||||||
GLint attr_texture;
|
|
||||||
GLuint current_tex;
|
|
||||||
GstGLOverlayCompositor *overlay_compositor;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const GLfloat vertices[] = {
|
|
||||||
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
|
|
||||||
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
|
|
||||||
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
|
|
||||||
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_bind_buffer (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, priv->vertex_buffer);
|
|
||||||
|
|
||||||
/* Load the vertex position */
|
|
||||||
gl->VertexAttribPointer (priv->attr_position, 3, GL_FLOAT, GL_FALSE,
|
|
||||||
5 * sizeof (GLfloat), (void *) 0);
|
|
||||||
|
|
||||||
/* Load the texture coordinate */
|
|
||||||
gl->VertexAttribPointer (priv->attr_texture, 2, GL_FLOAT, GL_FALSE,
|
|
||||||
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
|
|
||||||
|
|
||||||
gl->EnableVertexAttribArray (priv->attr_position);
|
|
||||||
gl->EnableVertexAttribArray (priv->attr_texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_unbind_buffer (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
gl->DisableVertexAttribArray (priv->attr_position);
|
|
||||||
gl->DisableVertexAttribArray (priv->attr_texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_init_redisplay (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
gst_gl_insert_debug_marker (priv->other_context, "initializing redisplay");
|
|
||||||
if (!(priv->shader = gst_gl_shader_new_default (priv->context, &error))) {
|
|
||||||
GST_ERROR ("Failed to initialize shader: %s", error->message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->attr_position =
|
|
||||||
gst_gl_shader_get_attribute_location (priv->shader, "a_position");
|
|
||||||
priv->attr_texture =
|
|
||||||
gst_gl_shader_get_attribute_location (priv->shader, "a_texcoord");
|
|
||||||
|
|
||||||
if (gl->GenVertexArrays) {
|
|
||||||
gl->GenVertexArrays (1, &priv->vao);
|
|
||||||
gl->BindVertexArray (priv->vao);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl->GenBuffers (1, &priv->vertex_buffer);
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, priv->vertex_buffer);
|
|
||||||
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
|
|
||||||
GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
if (gl->GenVertexArrays) {
|
|
||||||
gtk_gst_gl_widget_bind_buffer (gst_widget);
|
|
||||||
gl->BindVertexArray (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
priv->overlay_compositor =
|
|
||||||
gst_gl_overlay_compositor_new (priv->other_context);
|
|
||||||
|
|
||||||
priv->initted = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_redraw_texture (GtkGstGLWidget * gst_widget, guint tex)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
|
|
||||||
|
|
||||||
if (gst_widget->base.force_aspect_ratio) {
|
|
||||||
GstVideoRectangle src, dst, result;
|
|
||||||
gint widget_width, widget_height, widget_scale;
|
|
||||||
|
|
||||||
gl->ClearColor (0.0, 0.0, 0.0, 0.0);
|
|
||||||
gl->Clear (GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
widget_scale = gtk_widget_get_scale_factor ((GtkWidget *) gst_widget);
|
|
||||||
widget_width = gtk_widget_get_allocated_width ((GtkWidget *) gst_widget);
|
|
||||||
widget_height = gtk_widget_get_allocated_height ((GtkWidget *) gst_widget);
|
|
||||||
|
|
||||||
src.x = 0;
|
|
||||||
src.y = 0;
|
|
||||||
src.w = gst_widget->base.display_width;
|
|
||||||
src.h = gst_widget->base.display_height;
|
|
||||||
|
|
||||||
dst.x = 0;
|
|
||||||
dst.y = 0;
|
|
||||||
dst.w = widget_width * widget_scale;
|
|
||||||
dst.h = widget_height * widget_scale;
|
|
||||||
|
|
||||||
gst_video_sink_center_rect (src, dst, &result, TRUE);
|
|
||||||
|
|
||||||
gl->Viewport (result.x, result.y, result.w, result.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_gl_shader_use (priv->shader);
|
|
||||||
|
|
||||||
if (gl->BindVertexArray)
|
|
||||||
gl->BindVertexArray (priv->vao);
|
|
||||||
gtk_gst_gl_widget_bind_buffer (gst_widget);
|
|
||||||
|
|
||||||
gl->ActiveTexture (GL_TEXTURE0);
|
|
||||||
gl->BindTexture (GL_TEXTURE_2D, tex);
|
|
||||||
gst_gl_shader_set_uniform_1i (priv->shader, "tex", 0);
|
|
||||||
|
|
||||||
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
|
||||||
|
|
||||||
if (gl->BindVertexArray)
|
|
||||||
gl->BindVertexArray (0);
|
|
||||||
gtk_gst_gl_widget_unbind_buffer (gst_widget);
|
|
||||||
|
|
||||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
_draw_black (GstGLContext * context)
|
|
||||||
{
|
|
||||||
const GstGLFuncs *gl = context->gl_vtable;
|
|
||||||
|
|
||||||
gst_gl_insert_debug_marker (context, "no buffer. rendering black");
|
|
||||||
gl->ClearColor (0.0, 0.0, 0.0, 0.0);
|
|
||||||
gl->Clear (GL_COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_gl_widget_render (GtkGLArea * widget, GdkGLContext * context)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = GTK_GST_GL_WIDGET (widget)->priv;
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
||||||
|
|
||||||
if (!priv->context || !priv->other_context)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
gst_gl_context_activate (priv->other_context, TRUE);
|
|
||||||
|
|
||||||
if (!priv->initted)
|
|
||||||
gtk_gst_gl_widget_init_redisplay (GTK_GST_GL_WIDGET (widget));
|
|
||||||
|
|
||||||
if (!priv->initted || !base_widget->negotiated) {
|
|
||||||
_draw_black (priv->other_context);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Upload latest buffer */
|
|
||||||
if (base_widget->pending_buffer) {
|
|
||||||
GstBuffer *buffer = base_widget->pending_buffer;
|
|
||||||
GstVideoFrame gl_frame;
|
|
||||||
GstGLSyncMeta *sync_meta;
|
|
||||||
|
|
||||||
if (!gst_video_frame_map (&gl_frame, &base_widget->v_info, buffer,
|
|
||||||
GST_MAP_READ | GST_MAP_GL)) {
|
|
||||||
_draw_black (priv->other_context);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->current_tex = *(guint *) gl_frame.data[0];
|
|
||||||
gst_gl_insert_debug_marker (priv->other_context, "redrawing texture %u",
|
|
||||||
priv->current_tex);
|
|
||||||
|
|
||||||
gst_gl_overlay_compositor_upload_overlays (priv->overlay_compositor,
|
|
||||||
buffer);
|
|
||||||
|
|
||||||
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
|
|
||||||
if (sync_meta) {
|
|
||||||
/* XXX: the set_sync() seems to be needed for resizing */
|
|
||||||
gst_gl_sync_meta_set_sync_point (sync_meta, priv->context);
|
|
||||||
gst_gl_sync_meta_wait (sync_meta, priv->other_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_video_frame_unmap (&gl_frame);
|
|
||||||
|
|
||||||
if (base_widget->buffer)
|
|
||||||
gst_buffer_unref (base_widget->buffer);
|
|
||||||
|
|
||||||
/* Keep the buffer to ensure current_tex stay valid */
|
|
||||||
base_widget->buffer = buffer;
|
|
||||||
base_widget->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG ("rendering buffer %p with gdk context %p",
|
|
||||||
base_widget->buffer, context);
|
|
||||||
|
|
||||||
_redraw_texture (GTK_GST_GL_WIDGET (widget), priv->current_tex);
|
|
||||||
gst_gl_overlay_compositor_draw_overlays (priv->overlay_compositor);
|
|
||||||
|
|
||||||
gst_gl_insert_debug_marker (priv->other_context, "texture %u redrawn",
|
|
||||||
priv->current_tex);
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (priv->other_context)
|
|
||||||
gst_gl_context_activate (priv->other_context, FALSE);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_reset_gl (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->other_context->gl_vtable;
|
|
||||||
|
|
||||||
if (!priv->gdk_context)
|
|
||||||
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (gst_widget));
|
|
||||||
|
|
||||||
if (priv->gdk_context == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gdk_gl_context_make_current (priv->gdk_context);
|
|
||||||
gst_gl_context_activate (priv->other_context, TRUE);
|
|
||||||
|
|
||||||
if (priv->vao) {
|
|
||||||
gl->DeleteVertexArrays (1, &priv->vao);
|
|
||||||
priv->vao = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->vertex_buffer) {
|
|
||||||
gl->DeleteBuffers (1, &priv->vertex_buffer);
|
|
||||||
priv->vertex_buffer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->upload) {
|
|
||||||
gst_object_unref (priv->upload);
|
|
||||||
priv->upload = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->shader) {
|
|
||||||
gst_object_unref (priv->shader);
|
|
||||||
priv->shader = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->overlay_compositor)
|
|
||||||
gst_object_unref (priv->overlay_compositor);
|
|
||||||
|
|
||||||
gst_gl_context_activate (priv->other_context, FALSE);
|
|
||||||
|
|
||||||
gst_object_unref (priv->other_context);
|
|
||||||
priv->other_context = NULL;
|
|
||||||
|
|
||||||
gdk_gl_context_clear_current ();
|
|
||||||
|
|
||||||
g_object_unref (priv->gdk_context);
|
|
||||||
priv->gdk_context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = GTK_GST_GL_WIDGET (object)->priv;
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (object);
|
|
||||||
|
|
||||||
if (priv->other_context)
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) _reset_gl, base_widget);
|
|
||||||
|
|
||||||
if (priv->context)
|
|
||||||
gst_object_unref (priv->context);
|
|
||||||
|
|
||||||
if (priv->display)
|
|
||||||
gst_object_unref (priv->display);
|
|
||||||
|
|
||||||
gtk_gst_base_widget_finalize (object);
|
|
||||||
G_OBJECT_CLASS (gtk_gst_gl_widget_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_class_init (GtkGstGLWidgetClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_klass = (GObjectClass *) klass;
|
|
||||||
GtkGLAreaClass *gl_widget_klass = (GtkGLAreaClass *) klass;
|
|
||||||
|
|
||||||
g_type_class_add_private (klass, sizeof (GtkGstGLWidgetPrivate));
|
|
||||||
gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass));
|
|
||||||
|
|
||||||
gobject_klass->finalize = gtk_gst_gl_widget_finalize;
|
|
||||||
gl_widget_klass->render = gtk_gst_gl_widget_render;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_init (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (gst_widget);
|
|
||||||
GdkDisplay *display;
|
|
||||||
GtkGstGLWidgetPrivate *priv;
|
|
||||||
|
|
||||||
gtk_gst_base_widget_init (base_widget);
|
|
||||||
|
|
||||||
gst_widget->priv = priv = GTK_GST_GL_WIDGET_GET_PRIVATE (gst_widget);
|
|
||||||
|
|
||||||
display = gdk_display_get_default ();
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (GDK_WINDOWING_X11)
|
|
||||||
if (GDK_IS_X11_DISPLAY (display))
|
|
||||||
priv->display = (GstGLDisplay *)
|
|
||||||
gst_gl_display_x11_new_with_display (gdk_x11_display_get_xdisplay
|
|
||||||
(display));
|
|
||||||
#endif
|
|
||||||
#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
|
|
||||||
if (GDK_IS_WAYLAND_DISPLAY (display)) {
|
|
||||||
struct wl_display *wayland_display =
|
|
||||||
gdk_wayland_display_get_wl_display (display);
|
|
||||||
priv->display = (GstGLDisplay *)
|
|
||||||
gst_gl_display_wayland_new_with_display (wayland_display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
(void) display;
|
|
||||||
|
|
||||||
if (!priv->display)
|
|
||||||
priv->display = gst_gl_display_new ();
|
|
||||||
|
|
||||||
gtk_gl_area_set_has_alpha (GTK_GL_AREA (gst_widget),
|
|
||||||
!base_widget->ignore_alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_get_gl_context (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
GstGLPlatform platform;
|
|
||||||
GstGLAPI gl_api;
|
|
||||||
guintptr gl_handle;
|
|
||||||
|
|
||||||
gtk_widget_realize (GTK_WIDGET (gst_widget));
|
|
||||||
|
|
||||||
if (priv->other_context)
|
|
||||||
gst_object_unref (priv->other_context);
|
|
||||||
priv->other_context = NULL;
|
|
||||||
|
|
||||||
if (priv->gdk_context)
|
|
||||||
g_object_unref (priv->gdk_context);
|
|
||||||
|
|
||||||
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (gst_widget));
|
|
||||||
if (priv->gdk_context == NULL) {
|
|
||||||
GError *error = gtk_gl_area_get_error (GTK_GL_AREA (gst_widget));
|
|
||||||
|
|
||||||
GST_ERROR_OBJECT (gst_widget, "Error creating GdkGLContext : %s",
|
|
||||||
error ? error->message : "No error set by Gdk");
|
|
||||||
g_clear_error (&error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_ref (priv->gdk_context);
|
|
||||||
|
|
||||||
gdk_gl_context_make_current (priv->gdk_context);
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && GST_GL_HAVE_PLATFORM_GLX && defined (GDK_WINDOWING_X11)
|
|
||||||
if (GST_IS_GL_DISPLAY_X11 (priv->display)) {
|
|
||||||
platform = GST_GL_PLATFORM_GLX;
|
|
||||||
gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
|
|
||||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
|
||||||
if (gl_handle)
|
|
||||||
priv->other_context =
|
|
||||||
gst_gl_context_new_wrapped (priv->display, gl_handle,
|
|
||||||
platform, gl_api);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
|
|
||||||
if (GST_IS_GL_DISPLAY_WAYLAND (priv->display)) {
|
|
||||||
platform = GST_GL_PLATFORM_EGL;
|
|
||||||
gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
|
|
||||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
|
||||||
if (gl_handle)
|
|
||||||
priv->other_context =
|
|
||||||
gst_gl_context_new_wrapped (priv->display, gl_handle,
|
|
||||||
platform, gl_api);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
(void) platform;
|
|
||||||
(void) gl_api;
|
|
||||||
(void) gl_handle;
|
|
||||||
|
|
||||||
if (priv->other_context) {
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
gst_gl_context_activate (priv->other_context, TRUE);
|
|
||||||
if (!gst_gl_context_fill_info (priv->other_context, &error)) {
|
|
||||||
GST_ERROR ("failed to retrieve gdk context info: %s", error->message);
|
|
||||||
g_clear_error (&error);
|
|
||||||
g_object_unref (priv->other_context);
|
|
||||||
priv->other_context = NULL;
|
|
||||||
} else {
|
|
||||||
gst_gl_context_activate (priv->other_context, FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
gtk_gst_gl_widget_new (void)
|
|
||||||
{
|
|
||||||
return (GtkWidget *) g_object_new (GTK_TYPE_GST_GL_WIDGET, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
g_return_val_if_fail (GTK_IS_GST_GL_WIDGET (gst_widget), FALSE);
|
|
||||||
g_return_val_if_fail (priv->display != NULL, FALSE);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
|
|
||||||
|
|
||||||
if (priv->display && priv->gdk_context && priv->other_context) {
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!priv->other_context) {
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) _get_gl_context, gst_widget);
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GST_IS_GL_CONTEXT (priv->other_context)) {
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (priv->display);
|
|
||||||
if (!gst_gl_display_create_context (priv->display, priv->other_context,
|
|
||||||
&priv->context, &error)) {
|
|
||||||
g_clear_error (&error);
|
|
||||||
GST_OBJECT_UNLOCK (priv->display);
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
gst_gl_display_add_context (priv->display, priv->context);
|
|
||||||
GST_OBJECT_UNLOCK (priv->display);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GstGLContext *
|
|
||||||
gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
if (!gst_widget->priv->other_context)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return gst_object_ref (gst_widget->priv->other_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
GstGLContext *
|
|
||||||
gtk_gst_gl_widget_get_context (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
if (!gst_widget->priv->context)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return gst_object_ref (gst_widget->priv->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
GstGLDisplay *
|
|
||||||
gtk_gst_gl_widget_get_display (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
if (!gst_widget->priv->display)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return gst_object_ref (gst_widget->priv->display);
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GTK_GST_GL_WIDGET_H__
|
|
||||||
#define __GTK_GST_GL_WIDGET_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/gl/gl.h>
|
|
||||||
|
|
||||||
#include "gtkgstbasewidget.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
GType gtk_gst_gl_widget_get_type (void);
|
|
||||||
#define GTK_TYPE_GST_GL_WIDGET (gtk_gst_gl_widget_get_type())
|
|
||||||
#define GTK_GST_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_GST_GL_WIDGET,GtkGstGLWidget))
|
|
||||||
#define GTK_GST_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_GST_GL_WIDGET,GtkGstGLWidgetClass))
|
|
||||||
#define GTK_IS_GST_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_GST_GL_WIDGET))
|
|
||||||
#define GTK_IS_GST_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_GST_GL_WIDGET))
|
|
||||||
#define GTK_GST_GL_WIDGET_CAST(obj) ((GtkGstGLWidget*)(obj))
|
|
||||||
|
|
||||||
typedef struct _GtkGstGLWidget GtkGstGLWidget;
|
|
||||||
typedef struct _GtkGstGLWidgetClass GtkGstGLWidgetClass;
|
|
||||||
typedef struct _GtkGstGLWidgetPrivate GtkGstGLWidgetPrivate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GtkGstGLWidget:
|
|
||||||
*
|
|
||||||
* Opaque #GtkGstGLWidget object
|
|
||||||
*/
|
|
||||||
struct _GtkGstGLWidget
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GtkGstBaseWidget base;
|
|
||||||
|
|
||||||
GtkGstGLWidgetPrivate *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GtkGstGLWidgetClass:
|
|
||||||
*
|
|
||||||
* The #GtkGstGLWidgetClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GtkGstGLWidgetClass
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GtkGstBaseWidgetClass base_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
GtkWidget * gtk_gst_gl_widget_new (void);
|
|
||||||
|
|
||||||
gboolean gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * widget);
|
|
||||||
GstGLDisplay * gtk_gst_gl_widget_get_display (GtkGstGLWidget * widget);
|
|
||||||
GstGLContext * gtk_gst_gl_widget_get_context (GtkGstGLWidget * widget);
|
|
||||||
GstGLContext * gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * widget);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GTK_GST_GL_WIDGET_H__ */
|
|
|
@ -1,191 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "gtkgstwidget.h"
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:gtkgstwidget
|
|
||||||
* @title: GtkGstWidget
|
|
||||||
* @short_description: a #GtkWidget that renders GStreamer video #GstBuffers
|
|
||||||
* @see_also: #GtkDrawingArea, #GstBuffer
|
|
||||||
*
|
|
||||||
* #GtkGstWidget is an #GtkWidget that renders GStreamer video buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (GtkGstWidget, gtk_gst_widget, GTK_TYPE_DRAWING_AREA);
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_widget_draw (GtkWidget * widget, cairo_t * cr)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
|
|
||||||
guint widget_width, widget_height;
|
|
||||||
cairo_surface_t *surface;
|
|
||||||
GstVideoFrame frame;
|
|
||||||
|
|
||||||
widget_width = gtk_widget_get_allocated_width (widget);
|
|
||||||
widget_height = gtk_widget_get_allocated_height (widget);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
|
|
||||||
|
|
||||||
/* There is not much to optimize in term of redisplay, so simply swap the
|
|
||||||
* pending_buffer with the active buffer */
|
|
||||||
if (gst_widget->pending_buffer) {
|
|
||||||
if (gst_widget->buffer)
|
|
||||||
gst_buffer_unref (gst_widget->buffer);
|
|
||||||
gst_widget->buffer = gst_widget->pending_buffer;
|
|
||||||
gst_widget->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* failed to map the video frame */
|
|
||||||
if (gst_widget->negotiated && gst_widget->buffer
|
|
||||||
&& gst_video_frame_map (&frame, &gst_widget->v_info,
|
|
||||||
gst_widget->buffer, GST_MAP_READ)) {
|
|
||||||
gdouble scale_x = (gdouble) widget_width / gst_widget->display_width;
|
|
||||||
gdouble scale_y = (gdouble) widget_height / gst_widget->display_height;
|
|
||||||
GstVideoRectangle result;
|
|
||||||
cairo_format_t format;
|
|
||||||
|
|
||||||
gst_widget->v_info = frame.info;
|
|
||||||
if (frame.info.finfo->format == GST_VIDEO_FORMAT_ARGB ||
|
|
||||||
frame.info.finfo->format == GST_VIDEO_FORMAT_BGRA) {
|
|
||||||
format = CAIRO_FORMAT_ARGB32;
|
|
||||||
} else {
|
|
||||||
format = CAIRO_FORMAT_RGB24;
|
|
||||||
}
|
|
||||||
|
|
||||||
surface = cairo_image_surface_create_for_data (frame.data[0],
|
|
||||||
format, frame.info.width, frame.info.height, frame.info.stride[0]);
|
|
||||||
|
|
||||||
if (gst_widget->force_aspect_ratio) {
|
|
||||||
GstVideoRectangle src, dst;
|
|
||||||
|
|
||||||
src.x = 0;
|
|
||||||
src.y = 0;
|
|
||||||
src.w = gst_widget->display_width;
|
|
||||||
src.h = gst_widget->display_height;
|
|
||||||
|
|
||||||
dst.x = 0;
|
|
||||||
dst.y = 0;
|
|
||||||
dst.w = widget_width;
|
|
||||||
dst.h = widget_height;
|
|
||||||
|
|
||||||
gst_video_sink_center_rect (src, dst, &result, TRUE);
|
|
||||||
|
|
||||||
scale_x = scale_y = MIN (scale_x, scale_y);
|
|
||||||
} else {
|
|
||||||
result.x = 0;
|
|
||||||
result.y = 0;
|
|
||||||
result.w = widget_width;
|
|
||||||
result.h = widget_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gst_widget->ignore_alpha) {
|
|
||||||
GdkRGBA color = { 0.0, 0.0, 0.0, 1.0 };
|
|
||||||
|
|
||||||
gdk_cairo_set_source_rgba (cr, &color);
|
|
||||||
if (result.x > 0) {
|
|
||||||
cairo_rectangle (cr, 0, 0, result.x, widget_height);
|
|
||||||
cairo_fill (cr);
|
|
||||||
}
|
|
||||||
if (result.y > 0) {
|
|
||||||
cairo_rectangle (cr, 0, 0, widget_width, result.y);
|
|
||||||
cairo_fill (cr);
|
|
||||||
}
|
|
||||||
if (result.w < widget_width) {
|
|
||||||
cairo_rectangle (cr, result.x + result.w, 0, widget_width - result.w,
|
|
||||||
widget_height);
|
|
||||||
cairo_fill (cr);
|
|
||||||
}
|
|
||||||
if (result.h < widget_height) {
|
|
||||||
cairo_rectangle (cr, 0, result.y + result.h, widget_width,
|
|
||||||
widget_height - result.h);
|
|
||||||
cairo_fill (cr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scale_x *= (gdouble) gst_widget->display_width / (gdouble) frame.info.width;
|
|
||||||
scale_y *=
|
|
||||||
(gdouble) gst_widget->display_height / (gdouble) frame.info.height;
|
|
||||||
|
|
||||||
cairo_translate (cr, result.x, result.y);
|
|
||||||
cairo_scale (cr, scale_x, scale_y);
|
|
||||||
cairo_rectangle (cr, 0, 0, result.w, result.h);
|
|
||||||
cairo_set_source_surface (cr, surface, 0, 0);
|
|
||||||
cairo_paint (cr);
|
|
||||||
|
|
||||||
cairo_surface_destroy (surface);
|
|
||||||
|
|
||||||
gst_video_frame_unmap (&frame);
|
|
||||||
} else {
|
|
||||||
GdkRGBA color;
|
|
||||||
|
|
||||||
if (gst_widget->ignore_alpha) {
|
|
||||||
color.red = color.blue = color.green = 0.0;
|
|
||||||
color.alpha = 1.0;
|
|
||||||
} else {
|
|
||||||
gtk_style_context_get_color (gtk_widget_get_style_context (widget),
|
|
||||||
GTK_STATE_FLAG_NORMAL, &color);
|
|
||||||
}
|
|
||||||
gdk_cairo_set_source_rgba (cr, &color);
|
|
||||||
cairo_rectangle (cr, 0, 0, widget_width, widget_height);
|
|
||||||
cairo_fill (cr);
|
|
||||||
}
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_widget_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
gtk_gst_base_widget_finalize (object);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (gtk_gst_widget_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_widget_class_init (GtkGstWidgetClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_klass = (GObjectClass *) klass;
|
|
||||||
GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
|
|
||||||
|
|
||||||
gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass));
|
|
||||||
gobject_klass->finalize = gtk_gst_widget_finalize;
|
|
||||||
widget_klass->draw = gtk_gst_widget_draw;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_widget_init (GtkGstWidget * widget)
|
|
||||||
{
|
|
||||||
gtk_gst_base_widget_init (GTK_GST_BASE_WIDGET (widget));
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
gtk_gst_widget_new (void)
|
|
||||||
{
|
|
||||||
return (GtkWidget *) g_object_new (GTK_TYPE_GST_WIDGET, NULL);
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GTK_GST_WIDGET_H__
|
|
||||||
#define __GTK_GST_WIDGET_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
|
|
||||||
#include "gtkgstbasewidget.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
GType gtk_gst_widget_get_type (void);
|
|
||||||
#define GTK_TYPE_GST_WIDGET (gtk_gst_widget_get_type())
|
|
||||||
#define GTK_GST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_GST_WIDGET,GtkGstWidget))
|
|
||||||
#define GTK_GST_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_GST_WIDGET,GtkGstWidgetClass))
|
|
||||||
#define GTK_IS_GST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_GST_WIDGET))
|
|
||||||
#define GST_IS_GST_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_GST_WIDGET))
|
|
||||||
#define GTK_GST_WIDGET_CAST(obj) ((GtkGstWidget*)(obj))
|
|
||||||
|
|
||||||
typedef struct _GtkGstWidget GtkGstWidget;
|
|
||||||
typedef struct _GtkGstWidgetClass GtkGstWidgetClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GtkGstWidget:
|
|
||||||
*
|
|
||||||
* Opaque #GtkGstWidget object
|
|
||||||
*/
|
|
||||||
struct _GtkGstWidget
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GtkGstBaseWidget base;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GtkGstWidgetClass:
|
|
||||||
*
|
|
||||||
* The #GtkGstWidgetClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GtkGstWidgetClass
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GtkGstBaseWidgetClass base_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
GtkWidget * gtk_gst_widget_new (void);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GTK_GST_WIDGET_H__ */
|
|
|
@ -1,54 +0,0 @@
|
||||||
gtk_sources = [
|
|
||||||
'gstgtkbasesink.c',
|
|
||||||
'gstgtksink.c',
|
|
||||||
'gstgtkutils.c',
|
|
||||||
'gstplugin.c',
|
|
||||||
'gtkgstbasewidget.c',
|
|
||||||
'gtkgstwidget.c',
|
|
||||||
]
|
|
||||||
|
|
||||||
gtk_defines = []
|
|
||||||
optional_deps = []
|
|
||||||
|
|
||||||
gtk_dep = dependency('gtk+-3.0', required : false)
|
|
||||||
if gtk_dep.found()
|
|
||||||
if build_gstgl and gstgl_dep.found() and gtk_dep.version().version_compare('>=3.15.0')
|
|
||||||
have_gtk3_gl_windowing = false
|
|
||||||
|
|
||||||
if gst_gl_have_window_x11 and gst_gl_have_platform_glx
|
|
||||||
gtk_x11_dep = dependency('gtk+-x11-3.0', required : false)
|
|
||||||
if gtk_x11_dep.found()
|
|
||||||
optional_deps += gtk_x11_dep
|
|
||||||
have_gtk3_gl_windowing = true
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if gst_gl_have_window_wayland and gst_gl_have_platform_egl
|
|
||||||
gtk_wayland_dep = dependency('gtk+-wayland-3.0', required : false)
|
|
||||||
if gtk_wayland_dep.found()
|
|
||||||
optional_deps += gtk_wayland_dep
|
|
||||||
have_gtk3_gl_windowing = true
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if have_gtk3_gl_windowing
|
|
||||||
gtk_sources += [
|
|
||||||
'gstgtkglsink.c',
|
|
||||||
'gtkgstglwidget.c',
|
|
||||||
]
|
|
||||||
optional_deps += gstgl_dep
|
|
||||||
gtk_defines += ['-DGST_USE_UNSTABLE_API', '-DHAVE_GTK3_GL']
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
gstgtk = library('gstgtk',
|
|
||||||
gtk_sources,
|
|
||||||
c_args : gst_plugins_bad_args + gtk_defines,
|
|
||||||
link_args : noseh_link_args,
|
|
||||||
include_directories : [configinc],
|
|
||||||
dependencies : [gtk_dep, gstvideo_dep, gstbase_dep, libm] + optional_deps,
|
|
||||||
install : true,
|
|
||||||
install_dir : plugins_install_dir,
|
|
||||||
)
|
|
||||||
endif
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ subdir('fluidsynth')
|
||||||
subdir('gl')
|
subdir('gl')
|
||||||
#subdir('gme')
|
#subdir('gme')
|
||||||
subdir('gsm')
|
subdir('gsm')
|
||||||
subdir('gtk')
|
|
||||||
subdir('hls')
|
subdir('hls')
|
||||||
subdir('iqa')
|
subdir('iqa')
|
||||||
subdir('kate')
|
subdir('kate')
|
||||||
|
|
|
@ -30,12 +30,6 @@ else
|
||||||
AVSAMPLE_DIR=
|
AVSAMPLE_DIR=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if USE_GTK3
|
|
||||||
GTK3_DIR=gtk
|
|
||||||
else
|
|
||||||
GTK3_DIR=
|
|
||||||
endif
|
|
||||||
|
|
||||||
if USE_WAYLAND
|
if USE_WAYLAND
|
||||||
if HAVE_GTK3
|
if HAVE_GTK3
|
||||||
WAYLAND_DIR=waylandsink
|
WAYLAND_DIR=waylandsink
|
||||||
|
@ -65,9 +59,9 @@ playout_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||||
playout_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_LIBS)
|
playout_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_LIBS)
|
||||||
|
|
||||||
SUBDIRS= codecparsers mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) \
|
SUBDIRS= codecparsers mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) \
|
||||||
$(GTK3_DIR) $(AVSAMPLE_DIR) $(WAYLAND_DIR) $(MATRIXMIX_DIR) \
|
$(AVSAMPLE_DIR) $(WAYLAND_DIR) $(MATRIXMIX_DIR) \
|
||||||
$(IPCPIPELINE_DIR) $(WEBRTC_DIR)
|
$(IPCPIPELINE_DIR) $(WEBRTC_DIR)
|
||||||
DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 gtk \
|
DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 \
|
||||||
avsamplesink waylandsink audiomixmatrix ipcpipeline webrtc
|
avsamplesink waylandsink audiomixmatrix ipcpipeline webrtc
|
||||||
|
|
||||||
include $(top_srcdir)/common/parallel-subdirs.mak
|
include $(top_srcdir)/common/parallel-subdirs.mak
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
|
|
||||||
noinst_PROGRAMS = gtksink
|
|
||||||
|
|
||||||
gtksink_SOURCES = gtksink.c
|
|
||||||
gtksink_CFLAGS = $(GTK3_CFLAGS) \
|
|
||||||
$(GST_CFLAGS)
|
|
||||||
gtksink_LDADD = $(GTK3_LIBS) \
|
|
||||||
$(GST_LIBS)
|
|
||||||
|
|
||||||
if USE_GTK3_GL
|
|
||||||
if USE_GL
|
|
||||||
noinst_PROGRAMS += gtkglsink glliveshader
|
|
||||||
|
|
||||||
gtkglsink_SOURCES = gtkglsink.c
|
|
||||||
gtkglsink_CFLAGS = \
|
|
||||||
$(GST_PLUGINS_BAD_CFLAGS) \
|
|
||||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
|
||||||
$(GST_GL_CFLAGS) \
|
|
||||||
$(GST_CFLAGS) \
|
|
||||||
$(GL_CFLAGS) \
|
|
||||||
$(GTK3_CFLAGS)
|
|
||||||
gtkglsink_LDADD = \
|
|
||||||
$(GST_GL_LIBS) \
|
|
||||||
$(GST_LIBS) \
|
|
||||||
$(GTK3_LIBS) \
|
|
||||||
$(X11_LIBS)
|
|
||||||
|
|
||||||
glliveshader_SOURCES = glliveshader.c
|
|
||||||
glliveshader_CFLAGS = \
|
|
||||||
$(GST_PLUGINS_BAD_CFLAGS) \
|
|
||||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
|
||||||
$(GST_GL_CFLAGS) \
|
|
||||||
$(GST_CFLAGS) \
|
|
||||||
$(GTK3_CFLAGS)
|
|
||||||
glliveshader_LDADD = \
|
|
||||||
$(GST_GL_LIBS) \
|
|
||||||
$(GST_LIBS) \
|
|
||||||
$(GTK3_LIBS) \
|
|
||||||
$(X11_LIBS)
|
|
||||||
endif
|
|
||||||
endif
|
|
|
@ -1,350 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/gl/gl.h>
|
|
||||||
#include <gst/gl/gstglfuncs.h>
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef GL_GEOMETRY_SHADER
|
|
||||||
#define GL_GEOMETRY_SHADER 0x8DD9
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static GMainLoop *loop;
|
|
||||||
|
|
||||||
static const gchar *vert = "#version 330\n\
|
|
||||||
in vec4 a_position;\n\
|
|
||||||
in vec2 a_texcoord;\n\
|
|
||||||
out vec2 v_texcoord;\n\
|
|
||||||
uniform float time;\n\
|
|
||||||
uniform float width;\n\
|
|
||||||
uniform float height;\n\
|
|
||||||
void main()\n\
|
|
||||||
{\n\
|
|
||||||
gl_Position = a_position;\n\
|
|
||||||
v_texcoord = a_texcoord;\n\
|
|
||||||
}\n";
|
|
||||||
|
|
||||||
static const gchar *geom = "#version 330\n\
|
|
||||||
\n\
|
|
||||||
layout(triangles) in;\n\
|
|
||||||
layout(triangle_strip, max_vertices = 3) out;\n\
|
|
||||||
in vec2 v_texcoord[];\n\
|
|
||||||
out vec2 g_texcoord;\n\
|
|
||||||
\n\
|
|
||||||
void main() {\n\
|
|
||||||
for(int i = 0; i < 3; i++) {\n\
|
|
||||||
gl_Position = gl_in[i].gl_Position;\n\
|
|
||||||
g_texcoord = v_texcoord[i];\n\
|
|
||||||
EmitVertex();\n\
|
|
||||||
}\n\
|
|
||||||
EndPrimitive();\n\
|
|
||||||
}\n";
|
|
||||||
|
|
||||||
static const gchar *frag = "#version 330\n\
|
|
||||||
in vec2 g_texcoord;\n\
|
|
||||||
uniform sampler2D tex;\n\
|
|
||||||
uniform float time;\n\
|
|
||||||
uniform float width;\n\
|
|
||||||
uniform float height;\n\
|
|
||||||
void main()\n\
|
|
||||||
{\n\
|
|
||||||
gl_FragColor = texture2D(tex, g_texcoord);\n\
|
|
||||||
}\n";
|
|
||||||
|
|
||||||
#define MAX_SHADER_STAGES 8
|
|
||||||
struct shader_state;
|
|
||||||
|
|
||||||
struct text_view_state
|
|
||||||
{
|
|
||||||
struct shader_state *state;
|
|
||||||
|
|
||||||
GLenum type;
|
|
||||||
gchar *str;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct shader_state
|
|
||||||
{
|
|
||||||
GstGLContext *context;
|
|
||||||
GstElement *shader;
|
|
||||||
gboolean shader_linked;
|
|
||||||
GtkWidget *label;
|
|
||||||
struct text_view_state text_states[MAX_SHADER_STAGES];
|
|
||||||
gint n_stages;
|
|
||||||
};
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
bus_call (GstBus * bus, GstMessage * msg, gpointer data)
|
|
||||||
{
|
|
||||||
switch (GST_MESSAGE_TYPE (msg)) {
|
|
||||||
case GST_MESSAGE_EOS:
|
|
||||||
g_print ("End of stream\n");
|
|
||||||
g_main_loop_quit (loop);
|
|
||||||
break;
|
|
||||||
case GST_MESSAGE_ERROR:{
|
|
||||||
gchar *debug;
|
|
||||||
GError *error;
|
|
||||||
|
|
||||||
gst_message_parse_error (msg, &error, &debug);
|
|
||||||
g_free (debug);
|
|
||||||
|
|
||||||
g_printerr ("Error: %s\n", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
|
|
||||||
g_main_loop_quit (loop);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gchar *
|
|
||||||
_find_source_for_shader_type (struct shader_state *state, GLenum type)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < state->n_stages; i++) {
|
|
||||||
if (state->text_states[i].type == type)
|
|
||||||
return state->text_states[i].str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_add_stage_to_shader (GstGLShader * shader, struct shader_state *state,
|
|
||||||
GLenum type, const gchar * default_src)
|
|
||||||
{
|
|
||||||
GError *error = NULL;
|
|
||||||
GstGLSLVersion version;
|
|
||||||
GstGLSLProfile profile;
|
|
||||||
GstGLSLStage *stage;
|
|
||||||
const gchar *src;
|
|
||||||
|
|
||||||
src = _find_source_for_shader_type (state, type);
|
|
||||||
if (!src)
|
|
||||||
src = default_src;
|
|
||||||
if (!src)
|
|
||||||
/* FIXME: assume this stage is not needed */
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (!gst_glsl_string_get_version_profile (src, &version, &profile)) {
|
|
||||||
g_print ("Warning: failed to retreive GLSL version and profile for "
|
|
||||||
"shader type 0x%x\nsrc:\n%s\n", type, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(stage = gst_glsl_stage_new_with_string (shader->context, type,
|
|
||||||
version, profile, src))) {
|
|
||||||
g_print ("Error: Failed to create GLSL Stage from src:\n%s\n", src);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gst_gl_shader_compile_attach_stage (shader, stage, &error)) {
|
|
||||||
/* ignore failed shader compilations */
|
|
||||||
g_print ("%s", error->message);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstGLShader *
|
|
||||||
_new_shader (GstGLContext * context, struct shader_state *state)
|
|
||||||
{
|
|
||||||
GstGLShader *shader = gst_gl_shader_new (context);
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
if (!_add_stage_to_shader (shader, state, GL_VERTEX_SHADER, vert)) {
|
|
||||||
gst_object_unref (shader);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!_add_stage_to_shader (shader, state, GL_GEOMETRY_SHADER, geom)) {
|
|
||||||
gst_object_unref (shader);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!_add_stage_to_shader (shader, state, GL_FRAGMENT_SHADER, frag)) {
|
|
||||||
gst_object_unref (shader);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gst_gl_shader_link (shader, &error)) {
|
|
||||||
/* ignore failed shader compilations */
|
|
||||||
g_print ("%s", error->message);
|
|
||||||
gst_object_unref (shader);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_set_compilation_state (struct shader_state *state)
|
|
||||||
{
|
|
||||||
gtk_label_set_text (GTK_LABEL (state->label),
|
|
||||||
state->shader_linked ? "Success" : "Failure");
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstGLShader *
|
|
||||||
_create_shader (GstElement * element, struct shader_state *state)
|
|
||||||
{
|
|
||||||
GstGLContext *context;
|
|
||||||
GstGLShader *shader, *new_shader;
|
|
||||||
|
|
||||||
g_object_get (G_OBJECT (element), "context", &context, "shader", &shader,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
new_shader = _new_shader (context, state);
|
|
||||||
if (!shader && !new_shader)
|
|
||||||
g_warning ("Failed to create a shader!");
|
|
||||||
state->shader_linked = new_shader != NULL;
|
|
||||||
|
|
||||||
if (shader)
|
|
||||||
gst_object_unref (shader);
|
|
||||||
gst_object_unref (context);
|
|
||||||
|
|
||||||
g_main_context_invoke (NULL, (GSourceFunc) _set_compilation_state, state);
|
|
||||||
|
|
||||||
return new_shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_on_text_changed (GtkTextBuffer * text, struct text_view_state *state)
|
|
||||||
{
|
|
||||||
GtkTextIter start, end;
|
|
||||||
|
|
||||||
gtk_text_buffer_get_bounds (text, &start, &end);
|
|
||||||
g_free (state->str);
|
|
||||||
state->str = gtk_text_buffer_get_text (text, &start, &end, FALSE);
|
|
||||||
g_object_set (state->state->shader, "update-shader", TRUE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GtkWidget *
|
|
||||||
_new_source_view (struct shader_state *state, GLenum type, const gchar * templ)
|
|
||||||
{
|
|
||||||
static int i = 0;
|
|
||||||
GtkWidget *scroll, *text_view;
|
|
||||||
GtkTextBuffer *text;
|
|
||||||
|
|
||||||
g_return_val_if_fail (i < MAX_SHADER_STAGES, NULL);
|
|
||||||
|
|
||||||
state->text_states[i].state = state;
|
|
||||||
state->text_states[i].type = type;
|
|
||||||
state->text_states[i].str = g_strdup (templ);
|
|
||||||
|
|
||||||
scroll = gtk_scrolled_window_new (NULL, NULL);
|
|
||||||
gtk_widget_set_size_request (scroll, 20, 20);
|
|
||||||
text_view = gtk_text_view_new ();
|
|
||||||
gtk_container_add (GTK_CONTAINER (scroll), text_view);
|
|
||||||
text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
|
|
||||||
if (state->text_states[i].str)
|
|
||||||
gtk_text_buffer_set_text (text, state->text_states[i].str, -1);
|
|
||||||
g_signal_connect (text, "changed", G_CALLBACK (_on_text_changed),
|
|
||||||
&state->text_states[i]);
|
|
||||||
state->n_stages++;
|
|
||||||
i++;
|
|
||||||
|
|
||||||
return scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char *argv[])
|
|
||||||
{
|
|
||||||
GstElement *pipeline, *src, *upload, *shader, *sink;
|
|
||||||
GtkWidget *window, *paned, *video, *right_box, *book;
|
|
||||||
struct shader_state state = { 0, };
|
|
||||||
GstBus *bus;
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11
|
|
||||||
XInitThreads ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gst_init (&argc, &argv);
|
|
||||||
gtk_init (&argc, &argv);
|
|
||||||
|
|
||||||
loop = g_main_loop_new (NULL, FALSE);
|
|
||||||
|
|
||||||
pipeline = gst_pipeline_new (NULL);
|
|
||||||
src = gst_element_factory_make ("videotestsrc", NULL);
|
|
||||||
upload = gst_element_factory_make ("glupload", NULL);
|
|
||||||
shader = gst_element_factory_make ("glshader", NULL);
|
|
||||||
sink = gst_element_factory_make ("gtkglsink", NULL);
|
|
||||||
g_object_get (sink, "widget", &video, NULL);
|
|
||||||
|
|
||||||
g_assert (src && shader && sink);
|
|
||||||
gst_bin_add_many (GST_BIN (pipeline), src, upload, shader, sink, NULL);
|
|
||||||
g_assert (gst_element_link_many (src, upload, shader, sink, NULL));
|
|
||||||
|
|
||||||
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
|
||||||
gst_bus_add_watch (bus, bus_call, loop);
|
|
||||||
gst_object_unref (bus);
|
|
||||||
|
|
||||||
state.shader = gst_object_ref (shader);
|
|
||||||
g_signal_connect (shader, "create-shader", G_CALLBACK (_create_shader),
|
|
||||||
&state);
|
|
||||||
|
|
||||||
book = gtk_notebook_new ();
|
|
||||||
/* text view inside a scroll view */
|
|
||||||
gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state,
|
|
||||||
GL_VERTEX_SHADER, vert), gtk_label_new ("Vertex"));
|
|
||||||
gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state,
|
|
||||||
GL_GEOMETRY_SHADER, geom), gtk_label_new ("Geometry"));
|
|
||||||
gtk_notebook_append_page (GTK_NOTEBOOK (book), _new_source_view (&state,
|
|
||||||
GL_FRAGMENT_SHADER, frag), gtk_label_new ("Fragment"));
|
|
||||||
/* status label */
|
|
||||||
state.label = gtk_label_new ("Success");
|
|
||||||
|
|
||||||
/* right side source code editor */
|
|
||||||
right_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
||||||
gtk_box_pack_start (GTK_BOX (right_box), book, TRUE, TRUE, 0);
|
|
||||||
gtk_box_pack_start (GTK_BOX (right_box), state.label, FALSE, TRUE, 0);
|
|
||||||
|
|
||||||
paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
|
|
||||||
gtk_paned_pack1 (GTK_PANED (paned), video, TRUE, FALSE);
|
|
||||||
gtk_widget_set_size_request (video, 20, 20);
|
|
||||||
gtk_paned_pack2 (GTK_PANED (paned), right_box, TRUE, FALSE);
|
|
||||||
|
|
||||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
|
|
||||||
gtk_container_add (GTK_CONTAINER (window), paned);
|
|
||||||
|
|
||||||
gtk_widget_show_all (window);
|
|
||||||
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
||||||
|
|
||||||
g_main_loop_run (loop);
|
|
||||||
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
|
|
||||||
/*shader strings leaked here */
|
|
||||||
/*g_free (state.str); */
|
|
||||||
gst_object_unref (state.shader);
|
|
||||||
|
|
||||||
gst_object_unref (pipeline);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,200 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2014 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
|
|
||||||
#include <gst/gl/gl.h>
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
|
||||||
#include <gst/gl/x11/gstgldisplay_x11.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_null_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
g_print ("GST_STATE_NULL\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_ready_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
|
||||||
g_print ("GST_STATE_READY\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_paused_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
|
||||||
g_print ("GST_STATE_PAUSED\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_playing_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
||||||
g_print ("GST_STATE_PLAYING\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
end_stream_cb (GstBus * bus, GstMessage * message, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
g_print ("End of stream\n");
|
|
||||||
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
gst_object_unref (pipeline);
|
|
||||||
|
|
||||||
gtk_main_quit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
g_print ("Close\n");
|
|
||||||
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
gst_object_unref (pipeline);
|
|
||||||
|
|
||||||
gtk_main_quit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char *argv[])
|
|
||||||
{
|
|
||||||
GtkWidget *window, *window_control;
|
|
||||||
GtkWidget *button_state_null, *button_state_ready;
|
|
||||||
GtkWidget *button_state_paused, *button_state_playing;
|
|
||||||
GtkWidget *grid, *area;
|
|
||||||
GstElement *pipeline;
|
|
||||||
GstElement *videosrc, *upload, *effect, *videosink;
|
|
||||||
GstStateChangeReturn ret;
|
|
||||||
GstCaps *caps;
|
|
||||||
GstBus *bus;
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && defined(GDK_WINDOWING_X11)
|
|
||||||
XInitThreads ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gst_init (&argc, &argv);
|
|
||||||
gtk_init (&argc, &argv);
|
|
||||||
|
|
||||||
pipeline = gst_pipeline_new ("pipeline");
|
|
||||||
|
|
||||||
//window that contains an area where the video is drawn
|
|
||||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
|
|
||||||
gtk_window_move (GTK_WINDOW (window), 300, 10);
|
|
||||||
gtk_window_set_title (GTK_WINDOW (window), "gtkgstwidget");
|
|
||||||
|
|
||||||
//window to control the states
|
|
||||||
window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE);
|
|
||||||
gtk_window_move (GTK_WINDOW (window_control), 10, 10);
|
|
||||||
grid = gtk_grid_new ();
|
|
||||||
gtk_container_add (GTK_CONTAINER (window_control), grid);
|
|
||||||
|
|
||||||
//control state null
|
|
||||||
button_state_null = gtk_button_new_with_label ("GST_STATE_NULL");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_null), "clicked",
|
|
||||||
G_CALLBACK (button_state_null_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_null, 0, 1, 1, 1);
|
|
||||||
gtk_widget_show (button_state_null);
|
|
||||||
|
|
||||||
//control state ready
|
|
||||||
button_state_ready = gtk_button_new_with_label ("GST_STATE_READY");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_ready), "clicked",
|
|
||||||
G_CALLBACK (button_state_ready_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_ready, 0, 2, 1, 1);
|
|
||||||
gtk_widget_show (button_state_ready);
|
|
||||||
|
|
||||||
//control state paused
|
|
||||||
button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_paused), "clicked",
|
|
||||||
G_CALLBACK (button_state_paused_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_paused, 0, 3, 1, 1);
|
|
||||||
gtk_widget_show (button_state_paused);
|
|
||||||
|
|
||||||
//control state playing
|
|
||||||
button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_playing), "clicked",
|
|
||||||
G_CALLBACK (button_state_playing_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_playing, 0, 4, 1, 1);
|
|
||||||
gtk_widget_show (button_state_playing);
|
|
||||||
|
|
||||||
gtk_widget_show (grid);
|
|
||||||
gtk_widget_show (window_control);
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (destroy_cb),
|
|
||||||
pipeline);
|
|
||||||
|
|
||||||
//configure the pipeline
|
|
||||||
videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc");
|
|
||||||
upload = gst_element_factory_make ("glupload", "glupload");
|
|
||||||
effect = gst_element_factory_make ("glfiltercube", "glfiltercube");
|
|
||||||
videosink = gst_element_factory_make ("gtkglsink", "gtksink");
|
|
||||||
|
|
||||||
g_object_get (videosink, "widget", &area, NULL);
|
|
||||||
gtk_container_add (GTK_CONTAINER (window), area);
|
|
||||||
g_object_unref (area);
|
|
||||||
|
|
||||||
caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, 640,
|
|
||||||
"height", G_TYPE_INT, 480, "format", G_TYPE_STRING, "RGBA",
|
|
||||||
"framerate", GST_TYPE_FRACTION, 30, 1, NULL);
|
|
||||||
|
|
||||||
gst_bin_add_many (GST_BIN (pipeline), videosrc, upload, effect, videosink,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (!gst_element_link_filtered (videosrc, upload, caps)) {
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
g_warning ("Failed to link videosrc to glfiltercube!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
|
|
||||||
if (!gst_element_link_many (upload, effect, videosink, NULL)) {
|
|
||||||
g_warning ("Failed to link videosrc to glfiltercube!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
//set window id on this event
|
|
||||||
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
|
||||||
g_signal_connect (bus, "message::error", G_CALLBACK (end_stream_cb),
|
|
||||||
pipeline);
|
|
||||||
g_signal_connect (bus, "message::warning", G_CALLBACK (end_stream_cb),
|
|
||||||
pipeline);
|
|
||||||
g_signal_connect (bus, "message::eos", G_CALLBACK (end_stream_cb), pipeline);
|
|
||||||
gst_object_unref (bus);
|
|
||||||
|
|
||||||
//start
|
|
||||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
|
||||||
g_print ("Failed to start up pipeline!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_widget_show_all (window);
|
|
||||||
|
|
||||||
gtk_main ();
|
|
||||||
|
|
||||||
gst_deinit ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2014 Matthew Waters <matthew@centricular.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_null_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
g_print ("GST_STATE_NULL\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_ready_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
|
||||||
g_print ("GST_STATE_READY\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_paused_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
|
||||||
g_print ("GST_STATE_PAUSED\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
button_state_playing_cb (GtkWidget * widget, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
||||||
g_print ("GST_STATE_PLAYING\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
end_stream_cb (GstBus * bus, GstMessage * message, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
g_print ("End of stream\n");
|
|
||||||
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
gst_object_unref (pipeline);
|
|
||||||
|
|
||||||
gtk_main_quit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline)
|
|
||||||
{
|
|
||||||
g_print ("Close\n");
|
|
||||||
|
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
||||||
gst_object_unref (pipeline);
|
|
||||||
|
|
||||||
gtk_main_quit ();
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char *argv[])
|
|
||||||
{
|
|
||||||
GtkWidget *window, *window_control;
|
|
||||||
GtkWidget *button_state_null, *button_state_ready;
|
|
||||||
GtkWidget *button_state_paused, *button_state_playing;
|
|
||||||
GtkWidget *grid, *area;
|
|
||||||
GstElement *pipeline;
|
|
||||||
GstElement *videosrc, *videosink;
|
|
||||||
GstStateChangeReturn ret;
|
|
||||||
GstCaps *caps;
|
|
||||||
GstBus *bus;
|
|
||||||
|
|
||||||
gst_init (&argc, &argv);
|
|
||||||
gtk_init (&argc, &argv);
|
|
||||||
|
|
||||||
pipeline = gst_pipeline_new ("pipeline");
|
|
||||||
|
|
||||||
//window that contains an area where the video is drawn
|
|
||||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
|
|
||||||
gtk_window_move (GTK_WINDOW (window), 300, 10);
|
|
||||||
gtk_window_set_title (GTK_WINDOW (window), "gtkgstwidget");
|
|
||||||
|
|
||||||
//window to control the states
|
|
||||||
window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE);
|
|
||||||
gtk_window_move (GTK_WINDOW (window_control), 10, 10);
|
|
||||||
grid = gtk_grid_new ();
|
|
||||||
gtk_container_add (GTK_CONTAINER (window_control), grid);
|
|
||||||
|
|
||||||
//control state null
|
|
||||||
button_state_null = gtk_button_new_with_label ("GST_STATE_NULL");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_null), "clicked",
|
|
||||||
G_CALLBACK (button_state_null_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_null, 0, 1, 1, 1);
|
|
||||||
gtk_widget_show (button_state_null);
|
|
||||||
|
|
||||||
//control state ready
|
|
||||||
button_state_ready = gtk_button_new_with_label ("GST_STATE_READY");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_ready), "clicked",
|
|
||||||
G_CALLBACK (button_state_ready_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_ready, 0, 2, 1, 1);
|
|
||||||
gtk_widget_show (button_state_ready);
|
|
||||||
|
|
||||||
//control state paused
|
|
||||||
button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_paused), "clicked",
|
|
||||||
G_CALLBACK (button_state_paused_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_paused, 0, 3, 1, 1);
|
|
||||||
gtk_widget_show (button_state_paused);
|
|
||||||
|
|
||||||
//control state playing
|
|
||||||
button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING");
|
|
||||||
g_signal_connect (G_OBJECT (button_state_playing), "clicked",
|
|
||||||
G_CALLBACK (button_state_playing_cb), pipeline);
|
|
||||||
gtk_grid_attach (GTK_GRID (grid), button_state_playing, 0, 4, 1, 1);
|
|
||||||
gtk_widget_show (button_state_playing);
|
|
||||||
|
|
||||||
gtk_widget_show (grid);
|
|
||||||
gtk_widget_show (window_control);
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (destroy_cb),
|
|
||||||
pipeline);
|
|
||||||
|
|
||||||
//configure the pipeline
|
|
||||||
videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc");
|
|
||||||
videosink = gst_element_factory_make ("gtksink", "gtksink");
|
|
||||||
|
|
||||||
g_object_get (videosink, "widget", &area, NULL);
|
|
||||||
gtk_container_add (GTK_CONTAINER (window), area);
|
|
||||||
g_object_unref (area);
|
|
||||||
|
|
||||||
gtk_widget_realize (area);
|
|
||||||
|
|
||||||
caps = gst_caps_new_simple ("video/x-raw",
|
|
||||||
"width", G_TYPE_INT, 640,
|
|
||||||
"height", G_TYPE_INT, 480, "format", G_TYPE_STRING, "BGRA", NULL);
|
|
||||||
|
|
||||||
gst_bin_add_many (GST_BIN (pipeline), videosrc, videosink, NULL);
|
|
||||||
|
|
||||||
if (!gst_element_link_filtered (videosrc, videosink, caps)) {
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
g_warning ("Failed to link videosrc to glfiltercube!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
|
|
||||||
//set window id on this event
|
|
||||||
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
|
||||||
g_signal_connect (bus, "message::error", G_CALLBACK (end_stream_cb),
|
|
||||||
pipeline);
|
|
||||||
g_signal_connect (bus, "message::warning", G_CALLBACK (end_stream_cb),
|
|
||||||
pipeline);
|
|
||||||
g_signal_connect (bus, "message::eos", G_CALLBACK (end_stream_cb), pipeline);
|
|
||||||
gst_object_unref (bus);
|
|
||||||
|
|
||||||
//start
|
|
||||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
|
||||||
g_print ("Failed to start up pipeline!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_widget_show_all (window);
|
|
||||||
|
|
||||||
gtk_main ();
|
|
||||||
|
|
||||||
gst_deinit ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@
|
||||||
#subdir('codecparsers')
|
#subdir('codecparsers')
|
||||||
subdir('compositor')
|
subdir('compositor')
|
||||||
#subdir('directfb')
|
#subdir('directfb')
|
||||||
#subdir('gtk')
|
|
||||||
#subdir('ipcpipeline')
|
#subdir('ipcpipeline')
|
||||||
subdir('mpegts')
|
subdir('mpegts')
|
||||||
#subdir('mxf')
|
#subdir('mxf')
|
||||||
|
|
Loading…
Reference in a new issue