From ff06c6352348aedd28fd9570e9bdc11cfe0a0526 Mon Sep 17 00:00:00 2001 From: Eunhae Choi Date: Thu, 26 Nov 2015 16:11:07 +0900 Subject: [PATCH] add temp waylandsink for 1.6.1 and drmdecryptor Change-Id: I5717632b04e687fe188c1aaa776dc4e0e6f292bb --- Makefile.am | 16 + configure.ac | 42 +- drmdecryptor/Makefile.am | 1 + drmdecryptor/src/Makefile.am | 16 + drmdecryptor/src/gstdrmdecryptor.c | 350 +++++++++ drmdecryptor/src/gstdrmdecryptor.h | 66 ++ packaging/gst-plugins-tizen.spec | 17 +- waylandsink/Makefile.am | 1 + waylandsink/src/Makefile.am | 46 ++ waylandsink/src/gstwaylandsink.c | 1371 +++++++++++++++++++++++++++++++++ waylandsink/src/gstwaylandsink.h | 126 +++ waylandsink/src/scaler.xml | 210 +++++ waylandsink/src/tizen-wlvideoformat.c | 113 +++ waylandsink/src/tizen-wlvideoformat.h | 41 + waylandsink/src/waylandpool.c | 769 ++++++++++++++++++ waylandsink/src/waylandpool.h | 111 +++ waylandsink/src/wldisplay.c | 358 +++++++++ waylandsink/src/wldisplay.h | 100 +++ waylandsink/src/wlvideoformat.c | 114 +++ waylandsink/src/wlvideoformat.h | 42 + waylandsink/src/wlwindow.c | 484 ++++++++++++ waylandsink/src/wlwindow.h | 98 +++ 22 files changed, 4483 insertions(+), 9 deletions(-) create mode 100644 drmdecryptor/Makefile.am create mode 100644 drmdecryptor/src/Makefile.am create mode 100644 drmdecryptor/src/gstdrmdecryptor.c create mode 100644 drmdecryptor/src/gstdrmdecryptor.h create mode 100644 waylandsink/Makefile.am create mode 100644 waylandsink/src/Makefile.am create mode 100644 waylandsink/src/gstwaylandsink.c create mode 100644 waylandsink/src/gstwaylandsink.h create mode 100644 waylandsink/src/scaler.xml create mode 100644 waylandsink/src/tizen-wlvideoformat.c create mode 100644 waylandsink/src/tizen-wlvideoformat.h create mode 100644 waylandsink/src/waylandpool.c create mode 100644 waylandsink/src/waylandpool.h create mode 100644 waylandsink/src/wldisplay.c create mode 100644 waylandsink/src/wldisplay.h create mode 100644 waylandsink/src/wlvideoformat.c create mode 100644 waylandsink/src/wlvideoformat.h create mode 100644 waylandsink/src/wlwindow.c create mode 100644 waylandsink/src/wlwindow.h diff --git a/Makefile.am b/Makefile.am index 2b9ad84..d47c423 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,14 @@ if GST_TIZEN_USE_WAYLANDSRC SUBDIRS += waylandsrc endif +if GST_TIZEN_USE_DRMDECRYPTOR +SUBDIRS += drmdecryptor +endif + +if GST_TIZEN_USE_WAYLANDSINK +SUBDIRS += waylandsink +endif + DIST_SUBDIRS = common if GST_TIZEN_USE_ENCODEBIN @@ -96,6 +104,14 @@ if GST_TIZEN_USE_WAYLANDSRC DIST_SUBDIRS += waylandsrc endif +if GST_TIZEN_USE_DRMDECRYPTOR +DIST_SUBDIRS += drmdecryptor +endif + +if GST_TIZEN_USE_WAYLANDSINK +DIST_SUBDIRS += waylandsink +endif + EXTRA_DIST = \ gstreamer.spec gstreamer.spec.in \ configure.ac autogen.sh depcomp \ diff --git a/configure.ac b/configure.ac index ade2163..1425242 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_INIT(gst-plugins-tizen, 1.0) - + dnl versions of gstreamer and plugins-base GST_MAJORMINOR=1.0 GST_REQUIRED=1.2.0 @@ -39,10 +39,10 @@ AC_PROG_LIBTOOL dnl decide on error flags AS_COMPILER_FLAG(-Wall, GST_WALL="yes", GST_WALL="no") - + if test "x$GST_WALL" = "xyes"; then GST_ERROR="$GST_ERROR -Wall" - + # if test "x$GST_PLUGIN_CVS" = "xyes"; then # AS_COMPILER_FLAG(-Werror,GST_ERROR="$GST_ERROR -Werror",GST_ERROR="$GST_ERROR") # fi @@ -466,6 +466,38 @@ AC_SUBST(WAYLAND_CLIENT_CFLAGS) AC_SUBST(WAYLAND_CLIENT_LIBS) fi +dnl use drmdecryptor -------------------------------------------------------------------------- +AC_ARG_ENABLE(drmdecryptor, AC_HELP_STRING([--enable-drmdecryptor], [using drmdecryptor]), +[ + case "${enableval}" in + yes) GST_TIZEN_USE_DRMDECRYPTOR=yes ;; + no) GST_TIZEN_USE_DRMDECRYPTOR=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-drmdecryptor) ;; + esac + ], + [GST_TIZEN_USE_DRMDECRYPTOR=yes]) +AM_CONDITIONAL(GST_TIZEN_USE_DRMDECRYPTOR, test "x$GST_TIZEN_USE_DRMDECRYPTOR" = "xyes") + +dnl use waylandsink -------------------------------------------------------------------------- +AC_ARG_ENABLE(waylandsink, AC_HELP_STRING([--enable-waylandsink], [using waylandsink]), +[ + case "${enableval}" in + yes) GST_TIZEN_USE_WAYLANDSINK=yes ;; + no) GST_TIZEN_USE_WAYLANDSINK=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-waylandsink) ;; + esac + ], + [GST_TIZEN_USE_WAYLANDSINK=yes]) +AM_CONDITIONAL([GST_TIZEN_USE_WAYLANDSINK], [test "x$GST_TIZEN_USE_WAYLANDSINK" = "xyes"]) +if test "x$GST_TIZEN_USE_WAYLANDSINK" = "xyes"; then +PKG_CHECK_MODULES(GST_WAYLAND, gstreamer-wayland-1.0 >= 1.2.0) +AC_SUBST(GST_WAYLAND_CFLAGS) +AC_SUBST(GST_WAYLAND_LIBS) + +PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.4.0 wayland-tbm-client tizen-extension-client wayland-scanner) +AC_PATH_PROG([wayland_scanner], [wayland-scanner]) +fi + AC_OUTPUT( Makefile common/Makefile @@ -494,4 +526,8 @@ wfdmanager/wfdbase/Makefile wfdtsdemux/Makefile waylandsrc/Makefile waylandsrc/src/Makefile +drmdecryptor/Makefile +drmdecryptor/src/Makefile +waylandsink/Makefile +waylandsink/src/Makefile ) diff --git a/drmdecryptor/Makefile.am b/drmdecryptor/Makefile.am new file mode 100644 index 0000000..308a09c --- /dev/null +++ b/drmdecryptor/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/drmdecryptor/src/Makefile.am b/drmdecryptor/src/Makefile.am new file mode 100644 index 0000000..24d47ec --- /dev/null +++ b/drmdecryptor/src/Makefile.am @@ -0,0 +1,16 @@ +plugin_LTLIBRARIES = libgstdrmdecryptor.la + +libgstdrmdecryptor_la_SOURCES = gstdrmdecryptor.c + +libgstdrmdecryptor_la_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstdrmdecryptor_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + $(GST_BASE_LIBS) \ + $(GST_LIBS) +libgstdrmdecryptor_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstdrmdecryptor_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstdrmdecryptor.h + diff --git a/drmdecryptor/src/gstdrmdecryptor.c b/drmdecryptor/src/gstdrmdecryptor.c new file mode 100644 index 0000000..7f0ef2f --- /dev/null +++ b/drmdecryptor/src/gstdrmdecryptor.c @@ -0,0 +1,350 @@ +/* + * drmdecryptor + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "gstdrmdecryptor.h" + +/* TODO replace with input caps */ +#define GST_STATIC_CAPS_SINK GST_STATIC_CAPS("application/encrypted-xxx") + +static GstStaticPadTemplate sinktemplate = + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS_SINK); + +static GstStaticPadTemplate srctemplate = + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (gst_drm_decryptor_debug); +#define GST_CAT_DEFAULT gst_drm_decryptor_debug +static void +_do_init (void) +{ + /* TODO change to the debug category of your element */ + GST_DEBUG_CATEGORY_INIT (gst_drm_decryptor_debug, "DrmDecryptor", 0, + "DrmDecryptor element"); +} + +static gboolean +drmdecryptor_init (GstPlugin * drm) +{ + /* TODO change to the name of your element */ + return gst_element_register (drm, "drm_decryptor", GST_RANK_PRIMARY, + GST_DRM_DECRYPTOR_TYPE); +} + +#define gst_drm_decryptor_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstDrmDecryptor, gst_drm_decryptor, GST_TYPE_ELEMENT, + _do_init ()); + + +static gboolean gst_drm_decryptor_handle_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static GstStateChangeReturn gst_drm_decryptor_change_state (GstElement * element, + GstStateChange transition); + +static void gst_drm_decryptor_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_drm_decryptor_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_drm_decryptor_finalize (GObject * object); + +static GstFlowReturn gst_drm_decryptor_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer); + +static void +gst_drm_decryptor_class_init (GstDrmDecryptorClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sinktemplate)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&srctemplate)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_drm_decryptor_change_state); + + gobject_class->finalize = gst_drm_decryptor_finalize; + gobject_class->set_property = gst_drm_decryptor_set_property; + gobject_class->get_property = gst_drm_decryptor_get_property; + + /* TODO install any needed properties */ + + gst_element_class_set_static_metadata (gstelement_class, + "drm decryptor template plugin", + "DRM/Decryptor", "Decryption Plugin", "xxx@samsung.com>"); +} + +static void +gst_drm_decryptor_init (GstDrmDecryptor * drm) +{ + drm->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); + drm->srcpad = gst_pad_new_from_static_template (&srctemplate, "src"); + gst_pad_set_chain_function (drm->sinkpad, gst_drm_decryptor_chain); + gst_pad_set_event_function (drm->sinkpad, gst_drm_decryptor_handle_sink_event); + gst_pad_set_active (drm->sinkpad, TRUE); + gst_pad_set_active (drm->srcpad, TRUE); + GST_PAD_SET_ACCEPT_TEMPLATE (drm->sinkpad); + gst_element_add_pad (GST_ELEMENT (drm), drm->sinkpad); + gst_element_add_pad (GST_ELEMENT (drm), drm->srcpad); + + /* TODO initialize your element's data */ +} + +static gboolean +gst_drm_decryptor_set_format (GstDrmDecryptor * drm, const GstCaps * caps) +{ + GstCaps *output_caps; + const GstStructure *structure; + const gchar *original_type; + gboolean ret; + + structure = gst_caps_get_structure (caps, 0); + + /* It is also possible that we store a full caps inside the caps, in this + * case, it would be better to name it 'original-media-caps' */ + original_type = gst_structure_get_string (structure, "original-media-type"); + + /* TODO get any other needed parameter from caps */ + + /* TODO If possible, set the output caps */ + output_caps = gst_caps_new_simple (original_type, NULL); + + ret = gst_pad_set_caps (drm->srcpad, output_caps); + gst_caps_unref (output_caps); + + return ret; +} + +static void +gst_drm_decryptor_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + /* TODO fill with properties */ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_drm_decryptor_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + /* TODO fill with properties */ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_drm_decryptor_open (GstDrmDecryptor * drm) +{ + /* TODO Open any resources / library that your element requires + * If none is needed, just leave this as blank or remove this function */ +} + +static void +gst_drm_decryptor_close (GstDrmDecryptor * drm) +{ + /* TODO close resources / library + * (should be symetrical to _open() */ +} + +static gboolean +gst_drm_decryptor_start (GstDrmDecryptor * drm) +{ + /* TODO Start your decryption engine, after this call it must be ready + * for keys configuration and starting to decrypt. */ +} + +static void +gst_drm_decryptor_stop (GstDrmDecryptor * drm) +{ + /* TODO Stop the decryption engine (symetrical to _start()) */ +} + +static GstStateChangeReturn +gst_drm_decryptor_change_state (GstElement * element, GstStateChange transition) +{ + GstDrmDecryptor *drm = (GstDrmDecryptor *) element; + GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_drm_decryptor_open (drm)) + result = GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (!gst_drm_decryptor_start (drm)) + result = GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + if (result == GST_STATE_CHANGE_FAILURE) + return result; + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (result == GST_STATE_CHANGE_FAILURE) + return result; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_drm_decryptor_close (drm); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_drm_decryptor_stop (drm); + break; + } + + return result; +} + +static gboolean +gst_drm_decryptor_finish (GstDrmDecryptor * drm) +{ + /* TODO finish decryption of any pending data and push it downstream */ + return TRUE; +} + +static void +gst_drm_decryptor_flush (GstDrmDecryptor * drm) +{ + /* TODO Flush any data in the decryptor */ +} + +static gboolean +gst_drm_decryptor_handle_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstDrmDecryptor *drm; + gboolean ret = TRUE; + + drm = GST_DRM_DECRYPTOR (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_PROTECTION: + { + const gchar *system_id; + GstBuffer *data; + const gchar *origin; + + gst_event_parse_protection (event, &system_id, &data, &origin); + + /* TODO the protection event contains the keys for decryption that + * upstream elements might have found + * + * The system_id contains information about the type of encryption + * data contains binary data needed for decryption (e.g. keys) + * origin is a string describing where this data was found (e.g. mpd, + * playlist, container ...) + * + * This data should be used as parameters for the decryptor + * + * In some situations multiple keys are needed, like AES128 that needs + * the IV and the key. Multiple protection events can be used to signal + * different decryption configuration parameters. It might also + * be possible to extend the protection event to hold a structure to + * contain all needed configuration in a single event but this is not + * yet implemented. + * + * For now, one could (ab)use system_id to be AES128:key and AES128:IV + * in 2 separate events to signal the parameters. + */ + break; + } + case GST_EVENT_CAPS: + { + GstCaps *caps; + gst_event_parse_caps (event, &caps); + ret = gst_drm_decryptor_set_format (drm, caps); + break; + } + case GST_EVENT_EOS: + { + gst_drm_decryptor_finish (drm); + ret = gst_pad_push_event (drm->srcpad, event); + break; + } + case GST_EVENT_FLUSH_STOP: + { + gst_drm_decryptor_flush (drm); + ret = gst_pad_push_event (drm->srcpad, event); + break; + } + default: + { + ret = gst_pad_event_default (drm->sinkpad, parent, event); + break; + } + } + + return ret; +} + +static GstFlowReturn +gst_drm_decryptor_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstDrmDecryptor *drm; + GstBuffer *out_buf = NULL; + + drm = GST_DRM_DECRYPTOR (parent); + + /* TODO do your decryption that will create output data into + * out_buf */ + + /* TODO it might be required to signal per-buffer decryption information. + * For this purpose, the protection meta can be used and attached to buffers. + * If your element supports this, it can do: + * + * protection_meta = gst_buffer_get_meta (buf, GST_PROTECTION_META_INFO); + * + * The protection meta has a structure containing cryptographic parameters + * for this buffer + */ + + return gst_pad_push (drm->srcpad, out_buf); + +} + +static void +gst_drm_decryptor_finalize (GObject * object) +{ + /* TODO final instance cleanup */ +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "drmdecryptordecrypt", + "Gstreamer DrmDecryptor plugin", drmdecryptor_init, VERSION, "Some License", + "gst-plugins-drmdecryptor", "Unknown package origin") diff --git a/drmdecryptor/src/gstdrmdecryptor.h b/drmdecryptor/src/gstdrmdecryptor.h new file mode 100644 index 0000000..4b87b8e --- /dev/null +++ b/drmdecryptor/src/gstdrmdecryptor.h @@ -0,0 +1,66 @@ +/* + * drmdecryptor + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef __GST_DRM_DECRYPTOR_H__ +#define __GST_DRM_DECRYPTOR_H__ + +#define VERSION "1.0" +#ifdef PACKAGE +#undef PACKAGE +#endif +#define PACKAGE "gstplugindrmdecryptor" +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_DRM_DECRYPTOR(obj) (GstDrmDecryptor *) (obj) +#define GST_DRM_DECRYPTOR_TYPE (gst_drm_decryptor_get_type ()) + +typedef struct _GstDrmDecryptor GstDrmDecryptor; +typedef struct _GstDrmDecryptorClass GstDrmDecryptorClass; + +enum +{ + PROP_0, +}; + +struct _GstDrmDecryptor +{ + GstElement element; + GstPad *sinkpad; + GstPad *srcpad; + + /*< private >*/ + /* TODO add your private data here */ +}; + +struct _GstDrmDecryptorClass +{ + GstElementClass parent_class; +}; + +GType gst_drm_decryptor_get_type (void); + +G_END_DECLS + +#endif /* __GST_DRM_DECRYPTOR_H__ */ + diff --git a/packaging/gst-plugins-tizen.spec b/packaging/gst-plugins-tizen.spec index fdfcabb..78c54e6 100644 --- a/packaging/gst-plugins-tizen.spec +++ b/packaging/gst-plugins-tizen.spec @@ -1,10 +1,11 @@ +%bcond_with wayland %bcond_with x %define gst_branch 1.0 Name: gst-plugins-tizen Version: 1.0.0 Summary: GStreamer tizen plugins (common) -Release: 17 +Release: 18 Group: Multimedia/Framework Url: http://gstreamer.freedesktop.org/ License: LGPL-2.1+ and Apache-2.0 @@ -27,10 +28,6 @@ BuildRequires: pkgconfig(xv) BuildRequires: pkgconfig(xdamage) BuildRequires: pkgconfig(xfixes) BuildRequires: pkgconfig(dri2proto) -%else -BuildRequires: pkgconfig(wayland-client) -BuildRequires: pkgconfig(wayland-tbm-client) -BuildRequires: pkgconfig(tizen-extension-client) %endif BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(libdrm_exynos) @@ -38,6 +35,12 @@ BuildRequires: pkgconfig(libtbm) BuildRequires: libdrm-devel BuildRequires: pkgconfig(vconf) BuildRequires: pkgconfig(mm-common) +%if %{with wayland} +BuildRequires: pkgconfig(wayland-client) >= 1.0.0 +BuildRequires: pkgconfig(wayland-tbm-client) +BuildRequires: pkgconfig(tizen-extension-client) +BuildRequires: pkgconfig(gstreamer-wayland-1.0) +%endif %description GStreamer tizen plugins (common) @@ -47,15 +50,17 @@ GStreamer tizen plugins (common) %build -export CFLAGS+=" -DGST_EXT_TIME_ANALYSIS -DGST_EXT_XV_ENHANCEMENT -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" " +export CFLAGS+=" -DGST_EXT_TIME_ANALYSIS -DGST_EXT_XV_ENHANCEMENT -DGST_WLSINK_ENHANCEMENT -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" " ./autogen.sh --disable-static %configure \ %if %{with x} --disable-waylandsrc\ + --disable-waylandsink\ %else --disable-xvimagesrc\ %endif + --disable-drmdecryptor\ --disable-static make %{?jobs:-j%jobs} diff --git a/waylandsink/Makefile.am b/waylandsink/Makefile.am new file mode 100644 index 0000000..af437a6 --- /dev/null +++ b/waylandsink/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/waylandsink/src/Makefile.am b/waylandsink/src/Makefile.am new file mode 100644 index 0000000..9099bca --- /dev/null +++ b/waylandsink/src/Makefile.am @@ -0,0 +1,46 @@ +plugin_LTLIBRARIES = libgsttizenwlsink.la + +libgsttizenwlsink_la_SOURCES = \ + gstwaylandsink.c \ + waylandpool.c \ + wldisplay.c \ + wlwindow.c \ + wlvideoformat.c \ + scaler-protocol.c \ + tizen-wlvideoformat.c + +libgsttizenwlsink_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(WAYLAND_CFLAGS) $(GST_PLUGINS_BAD_CFLAGS) $(DRM_CFLAGS) $(TBM_CFLAGS) $(MMCOMMON_CFLAGS) \ + $(GST_WAYLAND_CFLAGS) +libgsttizenwlsink_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-$(GST_MAJORMINOR) \ + $(WAYLAND_LIBS) $(DRM_LIBS) $(TBM_LIBS) $(GST_WAYLAND_LIBS) +libgsttizenwlsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgsttizenwlsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = \ + gstwaylandsink.h \ + waylandpool.h \ + wldisplay.h \ + wlwindow.h \ + wlvideoformat.h \ + scaler-client-protocol.h \ + tizen-wlvideoformat.h + +EXTRA_DIST = scaler.xml +CLEANFILES = scaler-protocol.c scaler-client-protocol.h + +%-protocol.c : %.xml + $(wayland_scanner) code < $< > $@ + +%-client-protocol.h : %.xml + $(wayland_scanner) client-header < $< > $@ + +gstwaylandsink.c: scaler-client-protocol.h + +waylandpool.c: scaler-client-protocol.h + +wldisplay.c: scaler-client-protocol.h + +wlwindow.c: scaler-client-protocol.h diff --git a/waylandsink/src/gstwaylandsink.c b/waylandsink/src/gstwaylandsink.c new file mode 100644 index 0000000..dd6788a --- /dev/null +++ b/waylandsink/src/gstwaylandsink.c @@ -0,0 +1,1371 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * Copyright (C) 2012 Wim Taymans + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +/** + * SECTION:element-waylandsink + * + * The waylandsink is creating its own window and render the decoded video frames to that. + * Setup the Wayland environment as described in + * Wayland home page. + * The current implementaion is based on weston compositor. + * + * + * Example pipelines + * |[ + * gst-launch -v videotestsrc ! waylandsink + * ]| test the video rendering in wayland + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstwaylandsink.h" +#ifdef GST_WLSINK_ENHANCEMENT +#include +#include "tizen-wlvideoformat.h" +#else +#include "wlvideoformat.h" +#endif +#include "waylandpool.h" + +#include +#include + +#ifdef GST_WLSINK_ENHANCEMENT +#define GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD (gst_waylandsink_display_geometry_method_get_type()) +#define GST_TYPE_WAYLANDSINK_ROTATE_ANGLE (gst_waylandsink_rotate_angle_get_type()) +#define GST_TYPE_WAYLANDSINK_FLIP (gst_waylandsink_flip_get_type()) + +static GType +gst_waylandsink_rotate_angle_get_type (void) +{ + static GType waylandsink_rotate_angle_type = 0; + static const GEnumValue rotate_angle_type[] = { + {0, "No rotate", "DEGREE_0"}, + {1, "Rotate 90 degree", "DEGREE_90"}, + {2, "Rotate 180 degree", "DEGREE_180"}, + {3, "Rotate 270 degree", "DEGREE_270"}, + {4, NULL, NULL}, + }; + + if (!waylandsink_rotate_angle_type) { + waylandsink_rotate_angle_type = + g_enum_register_static ("GstWaylandSinkRotateAngleType", + rotate_angle_type); + } + + return waylandsink_rotate_angle_type; +} + + +static GType +gst_waylandsink_display_geometry_method_get_type (void) +{ + static GType waylandsink_display_geometry_method_type = 0; + static const GEnumValue display_geometry_method_type[] = { + {0, "Letter box", "LETTER_BOX"}, + {1, "Origin size", "ORIGIN_SIZE"}, + {2, "Full-screen", "FULL_SCREEN"}, + {3, "Cropped full-screen", "CROPPED_FULL_SCREEN"}, + {4, "Origin size(if screen size is larger than video size(width/height)) or Letter box(if video size(width/height) is larger than screen size)", "ORIGIN_SIZE_OR_LETTER_BOX"}, + {5, NULL, NULL}, + }; + + if (!waylandsink_display_geometry_method_type) { + waylandsink_display_geometry_method_type = + g_enum_register_static ("GstWaylandSinkDisplayGeometryMethodType", + display_geometry_method_type); + } + return waylandsink_display_geometry_method_type; +} + +static GType +gst_waylandsink_flip_get_type (void) +{ + static GType waylandsink_flip_type = 0; + static const GEnumValue flip_type[] = { + {FLIP_NONE, "Flip NONE", "FLIP_NONE"}, + {FLIP_HORIZONTAL, "Flip HORIZONTAL", "FLIP_HORIZONTAL"}, + {FLIP_VERTICAL, "Flip VERTICAL", "FLIP_VERTICAL"}, + {FLIP_BOTH, "Flip BOTH", "FLIP_BOTH"}, + {FLIP_NUM, NULL, NULL}, + }; + + if (!waylandsink_flip_type) { + waylandsink_flip_type = + g_enum_register_static ("GstWaylandSinkFlipType", flip_type); + } + + return waylandsink_flip_type; +} + +#endif + + +/* signals */ +enum +{ + SIGNAL_0, + LAST_SIGNAL +}; + +/* Properties */ +enum +{ + PROP_0, + PROP_DISPLAY, +#ifdef GST_WLSINK_ENHANCEMENT + PROP_ROTATE_ANGLE, + PROP_DISPLAY_GEOMETRY_METHOD, + PROP_ORIENTATION, + PROP_FLIP +#endif +}; + +GST_DEBUG_CATEGORY (gsttizenwl_debug); +#define GST_CAT_DEFAULT gsttizenwl_debug + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE + ("{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, " + "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, " +#ifdef GST_WLSINK_ENHANCEMENT + "SN12, ST12, " +#endif + "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }")) + ); + +static void gst_wayland_sink_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static void gst_wayland_sink_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_wayland_sink_finalize (GObject * object); + +static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element, + GstStateChange transition); +static void gst_wayland_sink_set_context (GstElement * element, + GstContext * context); + +static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink, + GstCaps * filter); +static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink, + GstBuffer * buffer); +static gboolean +gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query); +static gboolean gst_wayland_sink_render (GstBaseSink * bsink, + GstBuffer * buffer); + +/* VideoOverlay interface */ +static void gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * + iface); +static void gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, + guintptr handle); +static void gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay, + gint x, gint y, gint w, gint h); +static void gst_wayland_sink_expose (GstVideoOverlay * overlay); + +/* WaylandVideo interface */ +static void gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * + iface); +static void gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video); +static void gst_wayland_sink_end_geometry_change (GstWaylandVideo * video); +#ifdef GST_WLSINK_ENHANCEMENT +static void gst_wayland_sink_update_window_geometry (GstTizenwlSink * sink); +static void render_last_buffer (GstTizenwlSink * sink); +static void gst_wayland_sink_render_last_buffer (GstTizenwlSink * sink); + +#endif + +#define gst_wayland_sink_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstTizenwlSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK, + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, + gst_wayland_sink_videooverlay_init) + G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO, + gst_wayland_sink_waylandvideo_init)); + +static void +gst_wayland_sink_class_init (GstTizenwlSinkClass * klass) +{ + FUNCTION_ENTER (); + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + gobject_class->set_property = gst_wayland_sink_set_property; + gobject_class->get_property = gst_wayland_sink_get_property; + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "wayland video sink", "Sink/Video", + "Output to wayland surface", + "Sreerenj Balachandran , " + "George Kiagiadakis "); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state); + gstelement_class->set_context = + GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps); + gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps); + gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll); + gstbasesink_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation); + gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render); + + g_object_class_install_property (gobject_class, PROP_DISPLAY, + g_param_spec_string ("display", "Wayland Display name", "Wayland " + "display name to connect to, if not supplied via the GstContext", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#ifdef GST_WLSINK_ENHANCEMENT + g_object_class_install_property (gobject_class, PROP_ROTATE_ANGLE, + g_param_spec_enum ("rotate", "Rotate angle", + "Rotate angle of display output", + GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_DISPLAY_GEOMETRY_METHOD, + g_param_spec_enum ("display-geometry-method", "Display geometry method", + "Geometrical method for display", + GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD, + DEF_DISPLAY_GEOMETRY_METHOD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", + "Orientation information used for ROI/ZOOM", + "Orientation information for display", + GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FLIP, + g_param_spec_enum ("flip", "Display flip", + "Flip for display", + GST_TYPE_WAYLANDSINK_FLIP, DEF_DISPLAY_FLIP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +#endif +} + +static void +gst_wayland_sink_init (GstTizenwlSink * sink) +{ + FUNCTION_ENTER (); + + sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD; + sink->flip = DEF_DISPLAY_FLIP; + sink->rotate_angle = DEGREE_0; + sink->orientation = DEGREE_0; + + g_mutex_init (&sink->display_lock); + g_mutex_init (&sink->render_lock); +} + +static void +gst_wayland_sink_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (object); + switch (prop_id) { + case PROP_DISPLAY: + GST_OBJECT_LOCK (sink); + g_value_set_string (value, sink->display_name); + GST_OBJECT_UNLOCK (sink); + break; +#ifdef GST_WLSINK_ENHANCEMENT + case PROP_ROTATE_ANGLE: + g_value_set_enum (value, sink->rotate_angle); + break; + case PROP_DISPLAY_GEOMETRY_METHOD: + g_value_set_enum (value, sink->display_geometry_method); + break; + case PROP_ORIENTATION: + g_value_set_enum (value, sink->orientation); + break; + case PROP_FLIP: + g_value_set_enum (value, sink->flip); + break; +#endif + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_wayland_sink_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (object); + switch (prop_id) { + case PROP_DISPLAY: + GST_OBJECT_LOCK (sink); + sink->display_name = g_value_dup_string (value); + GST_OBJECT_UNLOCK (sink); + break; +#ifdef GST_WLSINK_ENHANCEMENT + case PROP_ROTATE_ANGLE: + sink->rotate_angle = g_value_get_enum (value); + GST_INFO_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle); + if (sink->window) { + gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle); + } + sink->video_info_changed = TRUE; + if (GST_STATE (sink) == GST_STATE_PAUSED) { + /*need to render last buffer */ + gst_wayland_sink_render_last_buffer (sink); + } + break; + case PROP_DISPLAY_GEOMETRY_METHOD: + sink->display_geometry_method = g_value_get_enum (value); + GST_INFO_OBJECT (sink, "Display geometry method is set (%d)", + sink->display_geometry_method); + if (sink->window) { + gst_wl_window_set_disp_geo_method (sink->window, + sink->display_geometry_method); + } + sink->video_info_changed = TRUE; + if (GST_STATE (sink) == GST_STATE_PAUSED) { + /*need to render last buffer */ + gst_wayland_sink_render_last_buffer (sink); + } + break; + case PROP_ORIENTATION: + sink->orientation = g_value_get_enum (value); + GST_INFO_OBJECT (sink, "Orientation is set (%d)", sink->orientation); + if (sink->window) { + gst_wl_window_set_orientation (sink->window, sink->orientation); + } + sink->video_info_changed = TRUE; + if (GST_STATE (sink) == GST_STATE_PAUSED) { + /*need to render last buffer */ + gst_wayland_sink_render_last_buffer (sink); + } + break; + case PROP_FLIP: + sink->flip = g_value_get_enum (value); + GST_INFO_OBJECT (sink, "flip is set (%d)", sink->flip); + if (sink->flip) { + gst_wl_window_set_flip (sink->window, sink->flip); + } + sink->video_info_changed = TRUE; + if (GST_STATE (sink) == GST_STATE_PAUSED) { + /*need to render last buffer */ + gst_wayland_sink_render_last_buffer (sink); + } + break; +#endif + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_wayland_sink_finalize (GObject * object) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (object); + GST_DEBUG_OBJECT (sink, "Finalizing the sink.."); + + if (sink->last_buffer) + gst_buffer_unref (sink->last_buffer); + if (sink->display) { + /* see comment about this call in gst_wayland_sink_change_state() */ +#ifdef GST_WLSINK_ENHANCEMENT + if (sink->pool && !sink->display->is_native_format) +#else + if (sink->pool) +#endif + gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL + (sink->pool)); + + g_object_unref (sink->display); + sink->display = NULL; + } + if (sink->window) { + g_object_unref (sink->window); + sink->window = NULL; + } + if (sink->pool) { + gst_object_unref (sink->pool); + sink->pool = NULL; + } + + if (sink->display_name) { + g_free (sink->display_name); + sink->display_name = NULL; + } + + g_mutex_clear (&sink->display_lock); + g_mutex_clear (&sink->render_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* must be called with the display_lock */ +static void +gst_wayland_sink_set_display_from_context (GstTizenwlSink * sink, + GstContext * context) +{ + FUNCTION_ENTER (); + + struct wl_display *display; + GError *error = NULL; + + display = gst_wayland_display_handle_context_get_handle (context); + sink->display = gst_wl_display_new_existing (display, FALSE, &error); + + if (error) { + GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE, + ("Could not set display handle"), + ("Failed to use the external wayland display: '%s'", error->message)); + g_error_free (error); + } +} + +static gboolean +gst_wayland_sink_find_display (GstTizenwlSink * sink) +{ + FUNCTION_ENTER (); + + GstQuery *query; + GstMessage *msg; + GstContext *context = NULL; + GError *error = NULL; + gboolean ret = TRUE; + + g_mutex_lock (&sink->display_lock); + + if (!sink->display) { + /* first query upstream for the needed display handle */ + query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE); + if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) { + gst_query_parse_context (query, &context); + gst_wayland_sink_set_display_from_context (sink, context); + } + gst_query_unref (query); + + if (G_LIKELY (!sink->display)) { + /* now ask the application to set the display handle */ + msg = gst_message_new_need_context (GST_OBJECT_CAST (sink), + GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE); + + g_mutex_unlock (&sink->display_lock); + gst_element_post_message (GST_ELEMENT_CAST (sink), msg); + /* at this point we expect gst_wayland_sink_set_context + * to get called and fill sink->display */ + g_mutex_lock (&sink->display_lock); + + if (!sink->display) { + /* if the application didn't set a display, let's create it ourselves */ + GST_OBJECT_LOCK (sink); + sink->display = gst_wl_display_new (sink->display_name, &error); + GST_OBJECT_UNLOCK (sink); + + if (error) { + GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE, + ("Could not initialise Wayland output"), + ("Failed to create GstWlDisplay: '%s'", error->message)); + g_error_free (error); + ret = FALSE; + } else { + /* inform the world about the new display */ + context = + gst_wayland_display_handle_context_new (sink->display->display); + msg = gst_message_new_have_context (GST_OBJECT_CAST (sink), context); + gst_element_post_message (GST_ELEMENT_CAST (sink), msg); + } + } + } + } + + g_mutex_unlock (&sink->display_lock); + + return ret; +} + +static GstStateChangeReturn +gst_wayland_sink_change_state (GstElement * element, GstStateChange transition) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_wayland_sink_find_display (sink)) + return GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_buffer_replace (&sink->last_buffer, NULL); + if (sink->window) { + if (gst_wl_window_is_toplevel (sink->window)) { + g_clear_object (&sink->window); + } else { + /* remove buffer from surface, show nothing */ + wl_surface_attach (sink->window->surface, NULL, 0, 0); + wl_surface_damage (sink->window->surface, 0, 0, + sink->window->surface_width, sink->window->surface_height); + wl_surface_commit (sink->window->surface); + wl_display_flush (sink->display->display); + } + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + g_mutex_lock (&sink->display_lock); + /* If we had a toplevel window, we most likely have our own connection + * to the display too, and it is a good idea to disconnect and allow + * potentially the application to embed us with GstVideoOverlay + * (which requires to re-use the same display connection as the parent + * surface). If we didn't have a toplevel window, then the display + * connection that we have is definitely shared with the application + * and it's better to keep it around (together with the window handle) + * to avoid requesting them again from the application if/when we are + * restarted (GstVideoOverlay behaves like that in other sinks) + */ + if (sink->display && !sink->window) { /* -> the window was toplevel */ + /* Force all buffers to return to the pool, regardless of + * whether the compositor has released them or not. We are + * going to kill the display, so we need to return all buffers + * to be destroyed before this happens. + * Note that this is done here instead of the pool destructor + * because the buffers hold a reference to the pool. Also, + * the buffers can only be unref'ed from the display's event loop + * and the pool holds a reference to the display. If we drop + * our references here, when the compositor releases the buffers, + * they will be unref'ed from the event loop thread, which will + * unref the pool and therefore the display, which will try to + * stop the thread from within itself and cause a deadlock. + */ + if (sink->pool) { + gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL + (sink->pool)); + } + g_clear_object (&sink->display); + g_clear_object (&sink->pool); + } + g_mutex_unlock (&sink->display_lock); + break; + default: + break; + } + + return ret; +} + +static void +gst_wayland_sink_set_context (GstElement * element, GstContext * context) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (element); + if (gst_context_has_context_type (context, + GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) { + g_mutex_lock (&sink->display_lock); + if (G_LIKELY (!sink->display)) { + gst_wayland_sink_set_display_from_context (sink, context); + } else { + GST_WARNING_OBJECT (element, "changing display handle is not supported"); + g_mutex_unlock (&sink->display_lock); + return; + } + g_mutex_unlock (&sink->display_lock); + } + + GST_INFO ("element %p context %p", element, context); + if (GST_ELEMENT_CLASS (parent_class)->set_context) + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static GstCaps * +gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink; + GstCaps *caps; + sink = GST_WAYLAND_SINK (bsink); + + caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink)); + + g_mutex_lock (&sink->display_lock); + + if (sink->display) { + GValue list = G_VALUE_INIT; + GValue value = G_VALUE_INIT; + GArray *formats; + gint i; +#ifdef GST_WLSINK_ENHANCEMENT + uint32_t fmt; +#else + enum wl_shm_format fmt; +#endif + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&value, G_TYPE_STRING); + + formats = sink->display->formats; + for (i = 0; i < formats->len; i++) { + fmt = g_array_index (formats, uint32_t, i); + g_value_set_string (&value, gst_wayland_format_to_string (fmt)); + gst_value_list_append_value (&list, &value); +#ifdef GST_WLSINK_ENHANCEMENT + /* TBM doesn't support SN12. So we add SN12 manually as supported format. + * SN12 is exactly same with NV12. + */ + if (fmt == TBM_FORMAT_NV12) { + g_value_set_string (&value, + gst_video_format_to_string (GST_VIDEO_FORMAT_SN12)); + gst_value_list_append_value (&list, &value); + } +#endif + } + + caps = gst_caps_make_writable (caps); + gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list); + + GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps); + } + + g_mutex_unlock (&sink->display_lock); + + if (filter) { + GstCaps *intersection; + + intersection = + gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (caps); + caps = intersection; + } + + return caps; +} + +static gboolean +gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink; + GstBufferPool *newpool; + GstVideoInfo info; +#ifdef GST_WLSINK_ENHANCEMENT + uint32_t format; +#else + enum wl_shm_format format; +#endif + GArray *formats; + gint i; + GstStructure *structure; + static GstAllocationParams params = { 0, 0, 0, 15, }; + sink = GST_WAYLAND_SINK (bsink); + + GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps); + + /* extract info from caps */ + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_format; +#ifdef GST_WLSINK_ENHANCEMENT + sink->caps = gst_caps_copy (caps); +#endif + + format = gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT (&info)); + if ((gint) format == -1) + goto invalid_format; + + /* verify we support the requested format */ + formats = sink->display->formats; + for (i = 0; i < formats->len; i++) { + if (g_array_index (formats, uint32_t, i) == format) + break; + } + + if (i >= formats->len) + goto unsupported_format; + +#ifdef GST_WLSINK_ENHANCEMENT + if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 || + GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) { + sink->display->is_native_format = TRUE; + } else { + sink->display->is_native_format = FALSE; + + /* create a new pool for the new configuration */ + newpool = gst_wayland_buffer_pool_new (sink->display); + if (!newpool) + goto pool_failed; + + structure = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0); + gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms); + if (!gst_buffer_pool_set_config (newpool, structure)) + goto config_failed; + + gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool); + gst_object_unref (newpool); + + } + /* store the video info */ + sink->video_info = info; + sink->video_info_changed = TRUE; +#else + /* create a new pool for the new configuration */ + newpool = gst_wayland_buffer_pool_new (sink->display); + if (!newpool) + goto pool_failed; + + structure = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0); + gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms); + if (!gst_buffer_pool_set_config (newpool, structure)) + goto config_failed; + + /* store the video info */ + sink->video_info = info; + sink->video_info_changed = TRUE; + + gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool); + gst_object_unref (newpool); +#endif + return TRUE; + +invalid_format: + { + GST_DEBUG_OBJECT (sink, + "Could not locate image format from caps %" GST_PTR_FORMAT, caps); + return FALSE; + } +unsupported_format: + { + GST_DEBUG_OBJECT (sink, "Format %s is not available on the display", + gst_wayland_format_to_string (format)); + return FALSE; + } +pool_failed: + { + GST_DEBUG_OBJECT (sink, "Failed to create new pool"); + return FALSE; + } +config_failed: + { + GST_DEBUG_OBJECT (bsink, "failed setting config"); + gst_object_unref (newpool); + return FALSE; + } +} + +static gboolean +gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (bsink); + GstBufferPool *pool = NULL; + GstStructure *config; + GstCaps *caps; + guint size; + gboolean need_pool; + + if (sink->display->is_native_format == TRUE) + return TRUE; + + gst_query_parse_allocation (query, &caps, &need_pool); + + if (caps == NULL) + goto no_caps; + + if (sink->pool) + pool = gst_object_ref (sink->pool); + + if (pool != NULL) { + GstCaps *pcaps; + + /* we had a pool, check caps */ + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); + + if (!gst_caps_is_equal (caps, pcaps)) { + /* different caps, we can't use this pool */ + gst_object_unref (pool); + pool = NULL; + } + gst_structure_free (config); + } + + if (pool == NULL && need_pool) { + GstVideoInfo info; + + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_caps; + + GST_DEBUG_OBJECT (sink, "create new pool"); + pool = gst_wayland_buffer_pool_new (sink->display); + + /* the normal size of a frame */ + size = info.size; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, caps, size, 2, 0); + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + } + if (pool) { + gst_query_add_allocation_pool (query, pool, size, 2, 0); + gst_object_unref (pool); + } + + 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"); + gst_object_unref (pool); + return FALSE; + } +} + +static GstFlowReturn +gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer) +{ + FUNCTION_ENTER (); + + GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer); + return gst_wayland_sink_render (bsink, buffer); +} + +static void +frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = data; + + GST_LOG ("frame_redraw_cb"); + + g_atomic_int_set (&sink->redraw_pending, FALSE); + wl_callback_destroy (callback); +} + +static const struct wl_callback_listener frame_callback_listener = { + frame_redraw_callback +}; + +/* must be called with the render lock */ +static void +render_last_buffer (GstTizenwlSink * sink) +{ + FUNCTION_ENTER (); + + GstWlMeta *meta; + struct wl_surface *surface; + struct wl_callback *callback; + + meta = gst_buffer_get_wl_meta (sink->last_buffer); + surface = gst_wl_window_get_wl_surface (sink->window); + + g_atomic_int_set (&sink->redraw_pending, TRUE); + callback = wl_surface_frame (surface); + wl_callback_add_listener (callback, &frame_callback_listener, sink); + + /* Here we essentially add a reference to the buffer. This represents + * the fact that the compositor is using the buffer and it should + * not return back to the pool and be reused until the compositor + * releases it. The release is handled internally in the pool */ + gst_wayland_compositor_acquire_buffer (meta->pool, sink->last_buffer); + + GST_DEBUG ("wl_surface_attach wl_buffer %p", meta->wbuffer); + + wl_surface_attach (surface, meta->wbuffer, 0, 0); + wl_surface_damage (surface, 0, 0, sink->window->surface_width, + sink->window->surface_height); + + wl_surface_commit (surface); + wl_display_flush (sink->display->display); +} + +static GstFlowReturn +gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (bsink); + GstBuffer *to_render; + GstWlMeta *meta; + GstFlowReturn ret = GST_FLOW_OK; + +#ifdef GST_WLSINK_ENHANCEMENT + GstBufferPool *newpool; + GstStructure *structure; + static GstAllocationParams params = { 0, 0, 0, 15, }; +#endif + + g_mutex_lock (&sink->render_lock); + + GST_LOG_OBJECT (sink, "render buffer %p", buffer); + + if (G_UNLIKELY (!sink->window)) { + /* ask for window handle. Unlock render_lock while doing that because + * set_window_handle & friends will lock it in this context */ + g_mutex_unlock (&sink->render_lock); + gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink)); + g_mutex_lock (&sink->render_lock); + + if (sink->window) { + /* inform the window about our caps */ + gst_wl_window_set_video_info (sink->window, &sink->video_info); + } else { + /* if we were not provided a window, create one ourselves */ + sink->window = + gst_wl_window_new_toplevel (sink->display, &sink->video_info); + } +#ifdef GST_WLSINK_ENHANCEMENT + gst_wayland_sink_update_window_geometry (sink); + sink->video_info_changed = TRUE; +#else + sink->video_info_changed = FALSE; +#endif + } + + /* drop buffers until we get a frame callback */ + if (g_atomic_int_get (&sink->redraw_pending) == TRUE) + goto done; + + if (G_UNLIKELY (sink->video_info_changed)) { + gst_wl_window_set_video_info (sink->window, &sink->video_info); + sink->video_info_changed = FALSE; + } + GST_INFO ("window->render_rectangle(%d,%d %d x %d)", + sink->window->render_rectangle.x, + sink->window->render_rectangle.y, + sink->window->render_rectangle.w, sink->window->render_rectangle.h); + GST_INFO ("window->surface_width(%d),window->surface_height(%d)", + sink->window->surface_width, sink->window->surface_height); + + /* now that we have for sure set the video info on the window, it must have + * a valid size, otherwise this means that the application has called + * set_window_handle() without calling set_render_rectangle(), which is + * absolutely necessary for us. + */ + if (G_UNLIKELY (sink->window->surface_width == 0 || + sink->window->surface_height == 0)) + goto no_window_size; + + meta = gst_buffer_get_wl_meta (buffer); + + if (meta && meta->pool->display == sink->display) { + GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer); + to_render = buffer; + } else { + GstMapInfo src; + GST_LOG_OBJECT (sink, "buffer %p not from our pool, copying", buffer); + +#ifdef GST_WLSINK_ENHANCEMENT + if (sink->display->is_native_format == TRUE) { + /*in case of SN12 or ST12 video format */ + GstMemory *mem; + GstMapInfo mem_info = GST_MAP_INFO_INIT; + MMVideoBuffer *mm_video_buf = NULL; + int i = 0; + + mem = gst_buffer_peek_memory (buffer, 1); + gst_memory_map (mem, &mem_info, GST_MAP_READ); + mm_video_buf = (MMVideoBuffer *) mem_info.data; + gst_memory_unmap (mem, &mem_info); + + if (mm_video_buf == NULL) { + GST_WARNING_OBJECT (sink, "mm_video_buf is NULL. Skip rendering"); + return ret; + } + /* assign mm_video_buf info */ + if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) { + GST_DEBUG_OBJECT (sink, "TBM bo %p %p %p", mm_video_buf->handle.bo[0], + mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]); + + sink->display->native_video_size = 0; + + for (i = 0; i < NV_BUF_PLANE_NUM; i++) { + if (mm_video_buf->handle.bo[i] != NULL) { + sink->display->bo[i] = mm_video_buf->handle.bo[i]; + } else { + sink->display->bo[i] = 0; + } + sink->display->plane_size[i] = mm_video_buf->size[i]; + sink->display->stride_width[i] = mm_video_buf->stride_width[i]; + sink->display->stride_height[i] = mm_video_buf->stride_height[i]; + sink->display->native_video_size += sink->display->plane_size[i]; + } + } else { + GST_ERROR_OBJECT (sink, "Buffer type is not TBM"); + return ret; + } + + if (!sink->pool) { + + /* create a new pool for the new configuration */ + newpool = gst_wayland_buffer_pool_new (sink->display); + if (!newpool) { + GST_DEBUG_OBJECT (sink, "Failed to create new pool"); + return FALSE; + } + structure = gst_buffer_pool_get_config (newpool); + /*When the buffer is released, Core compare size with buffer size, + wl_buffer is not created if the size is same. It is a very critical problem + So we set 0 to size */ + gst_buffer_pool_config_set_params (structure, sink->caps, 0, 2, 0); + gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms); + if (!gst_buffer_pool_set_config (newpool, structure)) { + GST_DEBUG_OBJECT (bsink, "failed setting config"); + gst_object_unref (newpool); + return FALSE; + } + + gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool); + gst_object_unref (newpool); + + } + + if (!gst_buffer_pool_set_active (sink->pool, TRUE)) + goto activate_failed; + + ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL); + if (ret != GST_FLOW_OK) + goto no_buffer; + + + /*add displaying buffer */ + GstWlMeta *meta; + meta = gst_buffer_get_wl_meta (to_render); + gst_wayland_buffer_pool_add_displaying_buffer (sink->pool, meta, buffer); + + } else { + /*in case of normal video format and pool is not our pool */ + + if (!sink->pool) + goto no_pool; + + if (!gst_buffer_pool_set_active (sink->pool, TRUE)) + goto activate_failed; + + ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL); + if (ret != GST_FLOW_OK) + goto no_buffer; + + gst_buffer_map (buffer, &src, GST_MAP_READ); + gst_buffer_fill (to_render, 0, src.data, src.size); + gst_buffer_unmap (buffer, &src); + } +#else + if (!sink->pool) + goto no_pool; + + if (!gst_buffer_pool_set_active (sink->pool, TRUE)) + goto activate_failed; + + ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL); + if (ret != GST_FLOW_OK) + goto no_buffer; + + gst_buffer_map (buffer, &src, GST_MAP_READ); + gst_buffer_fill (to_render, 0, src.data, src.size); + gst_buffer_unmap (buffer, &src); +#endif + } + + gst_buffer_replace (&sink->last_buffer, to_render); + render_last_buffer (sink); + + if (buffer != to_render) { + GST_LOG_OBJECT (sink, "Decrease ref count of buffer"); + gst_buffer_unref (to_render); + } + goto done; + +no_window_size: + { + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, + ("Window has no size set"), + ("Make sure you set the size after calling set_window_handle")); + ret = GST_FLOW_ERROR; + goto done; + } +no_buffer: + { + GST_WARNING_OBJECT (sink, "could not create image"); + goto done; + } +no_pool: + { + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, + ("Internal error: can't allocate images"), + ("We don't have a bufferpool negotiated")); + ret = GST_FLOW_ERROR; + goto done; + } +activate_failed: + { + GST_ERROR_OBJECT (sink, "failed to activate bufferpool."); + ret = GST_FLOW_ERROR; + goto done; + } +done: + { + g_mutex_unlock (&sink->render_lock); + return ret; + } +} + +static void +gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface) +{ + FUNCTION_ENTER (); + + iface->set_window_handle = gst_wayland_sink_set_window_handle; + iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle; + iface->expose = gst_wayland_sink_expose; +} + +static void +gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (overlay); + struct wl_surface *surface = (struct wl_surface *) handle; + + g_return_if_fail (sink != NULL); + + if (sink->window != NULL) { + GST_WARNING_OBJECT (sink, "changing window handle is not supported"); + return; + } + + g_mutex_lock (&sink->render_lock); + + GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT, + (void *) handle); + + g_clear_object (&sink->window); + + if (handle) { + if (G_LIKELY (gst_wayland_sink_find_display (sink))) { + /* we cannot use our own display with an external window handle */ + if (G_UNLIKELY (sink->display->own_display)) { + GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE, + ("Application did not provide a wayland display handle"), + ("waylandsink cannot use an externally-supplied surface without " + "an externally-supplied display handle. Consider providing a " + "display handle from your application with GstContext")); + } else { + sink->window = gst_wl_window_new_in_surface (sink->display, surface); + GST_DEBUG ("sink->window %p", sink->window); + } + } else { + GST_ERROR_OBJECT (sink, "Failed to find display handle, " + "ignoring window handle"); + } + } +#ifdef GST_WLSINK_ENHANCEMENT + gst_wayland_sink_update_window_geometry (sink); +#endif + + g_mutex_unlock (&sink->render_lock); +} + +#ifdef GST_WLSINK_ENHANCEMENT +static void +gst_wayland_sink_update_window_geometry (GstTizenwlSink * sink) +{ + FUNCTION_ENTER (); + g_return_if_fail (sink != NULL); + g_return_if_fail (sink->window != NULL); + + gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle); + gst_wl_window_set_disp_geo_method (sink->window, + sink->display_geometry_method); + gst_wl_window_set_orientation (sink->window, sink->orientation); + gst_wl_window_set_flip (sink->window, sink->flip); +} + +static void +gst_wayland_sink_render_last_buffer (GstTizenwlSink * sink) +{ + FUNCTION_ENTER (); + g_return_if_fail (sink != NULL); + + g_mutex_lock (&sink->render_lock); + gst_wl_window_set_video_info (sink->window, &sink->video_info); + sink->video_info_changed = FALSE; + if (sink->last_buffer) + render_last_buffer (sink); + g_mutex_unlock (&sink->render_lock); +} +#endif +static void +gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay, + gint x, gint y, gint w, gint h) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (overlay); + + g_return_if_fail (sink != NULL); + + g_mutex_lock (&sink->render_lock); + if (!sink->window) { + g_mutex_unlock (&sink->render_lock); + GST_WARNING_OBJECT (sink, + "set_render_rectangle called without window, ignoring"); + return; + } + + GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d", + x, y, w, h); + gst_wl_window_set_render_rectangle (sink->window, x, y, w, h); + + g_mutex_unlock (&sink->render_lock); +} + +static void +gst_wayland_sink_expose (GstVideoOverlay * overlay) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (overlay); + + g_return_if_fail (sink != NULL); + + GST_DEBUG_OBJECT (sink, "expose"); + + g_mutex_lock (&sink->render_lock); + if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) { + GST_DEBUG_OBJECT (sink, "redrawing last buffer"); + render_last_buffer (sink); + } + g_mutex_unlock (&sink->render_lock); +} + +static void +gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface) +{ + FUNCTION_ENTER (); + + iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change; + iface->end_geometry_change = gst_wayland_sink_end_geometry_change; +} + +static void +gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (video); + g_return_if_fail (sink != NULL); + + g_mutex_lock (&sink->render_lock); + if (!sink->window || !sink->window->subsurface) { + g_mutex_unlock (&sink->render_lock); + GST_INFO_OBJECT (sink, + "begin_geometry_change called without window, ignoring"); + return; + } + + wl_subsurface_set_sync (sink->window->subsurface); + g_mutex_unlock (&sink->render_lock); +} + +static void +gst_wayland_sink_end_geometry_change (GstWaylandVideo * video) +{ + FUNCTION_ENTER (); + + GstTizenwlSink *sink = GST_WAYLAND_SINK (video); + g_return_if_fail (sink != NULL); + + g_mutex_lock (&sink->render_lock); + if (!sink->window || !sink->window->subsurface) { + g_mutex_unlock (&sink->render_lock); + GST_INFO_OBJECT (sink, + "end_geometry_change called without window, ignoring"); + return; + } + + wl_subsurface_set_desync (sink->window->subsurface); + g_mutex_unlock (&sink->render_lock); +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + FUNCTION_ENTER (); + + GST_DEBUG_CATEGORY_INIT (gsttizenwl_debug, "tizenwlsink", 0, + " temporary wayland video sink"); + + return gst_element_register (plugin, "tizenwlsink", GST_RANK_MARGINAL, + GST_TYPE_WAYLAND_SINK); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + tizenwlsink, + "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/waylandsink/src/gstwaylandsink.h b/waylandsink/src/gstwaylandsink.h new file mode 100644 index 0000000..92e8081 --- /dev/null +++ b/waylandsink/src/gstwaylandsink.h @@ -0,0 +1,126 @@ +/* + * GStreamer Wayland video sink + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_WAYLAND_VIDEO_SINK_H__ +#define __GST_WAYLAND_VIDEO_SINK_H__ + +#include +#include + +#include + +#include "wldisplay.h" +#include "wlwindow.h" + +G_BEGIN_DECLS +#define GST_TYPE_WAYLAND_SINK \ + (gst_wayland_sink_get_type()) +#define GST_WAYLAND_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAYLAND_SINK,GstTizenwlSink)) +#define GST_WAYLAND_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAYLAND_SINK,GstTizenwlSinkClass)) +#define GST_IS_WAYLAND_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAYLAND_SINK)) +#define GST_IS_WAYLAND_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAYLAND_SINK)) +#define GST_WAYLAND_SINK_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_WAYLAND_SINK, GstTizenwlSinkClass)) +#ifdef GST_WLSINK_ENHANCEMENT + enum +{ + DISP_GEO_METHOD_LETTER_BOX = 0, + DISP_GEO_METHOD_ORIGIN_SIZE, + DISP_GEO_METHOD_FULL_SCREEN, + DISP_GEO_METHOD_CROPPED_FULL_SCREEN, + DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX, + DISP_GEO_METHOD_NUM, +}; + +enum +{ + DEGREE_0, + DEGREE_90, + DEGREE_180, + DEGREE_270, + DEGREE_NUM, +}; + +enum +{ + FLIP_NONE = 0, + FLIP_HORIZONTAL, + FLIP_VERTICAL, + FLIP_BOTH, + FLIP_NUM, +}; + +#define DEF_DISPLAY_FLIP FLIP_NONE +#define DEF_DISPLAY_GEOMETRY_METHOD DISP_GEO_METHOD_FULL_SCREEN + +#define WL_SCREEN_SIZE_WIDTH 4096 +#define WL_SCREEN_SIZE_HEIGHT 4096 + +#endif +#if 1 +#define FUNCTION_ENTER() GST_INFO("") +#else +#define FUNCTION_ENTER() +#endif +typedef struct _GstTizenwlSink GstTizenwlSink; +typedef struct _GstTizenwlSinkClass GstTizenwlSinkClass; + +struct _GstTizenwlSink +{ + GstVideoSink parent; + + GMutex display_lock; + GstWlDisplay *display; + GstWlWindow *window; + GstBufferPool *pool; + + gboolean video_info_changed; + GstVideoInfo video_info; + + /*property */ + gchar *display_name; +#ifdef GST_WLSINK_ENHANCEMENT + guint rotate_angle; + guint display_geometry_method; + guint orientation; + guint flip; + GstCaps *caps; +#endif + gboolean redraw_pending; + GMutex render_lock; + GstBuffer *last_buffer; +}; + +struct _GstTizenwlSinkClass +{ + GstVideoSinkClass parent; +}; + +GType +gst_wayland_sink_get_type (void) + G_GNUC_CONST; + +G_END_DECLS +#endif /* __GST_WAYLAND_VIDEO_SINK_H__ */ diff --git a/waylandsink/src/scaler.xml b/waylandsink/src/scaler.xml new file mode 100644 index 0000000..e21ae5b --- /dev/null +++ b/waylandsink/src/scaler.xml @@ -0,0 +1,210 @@ + + + + + Copyright © 2013-2014 Collabora, Ltd. + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The global interface exposing surface cropping and scaling + capabilities is used to instantiate an interface extension for a + wl_surface object. This extended interface will then allow + cropping and scaling the surface contents, effectively + disconnecting the direct relationship between the buffer and the + surface size. + + + + + Informs the server that the client will not be using this + protocol object anymore. This does not affect any other objects, + wl_viewport objects included. + + + + + + + + + + Instantiate an interface extension for the given wl_surface to + crop and scale its content. If the given wl_surface already has + a wl_viewport object associated, the viewport_exists + protocol error is raised. + + + + + + + + + + An additional interface to a wl_surface object, which allows the + client to specify the cropping and scaling of the surface + contents. + + This interface allows to define the source rectangle (src_x, + src_y, src_width, src_height) from where to take the wl_buffer + contents, and scale that to destination size (dst_width, + dst_height). This state is double-buffered, and is applied on the + next wl_surface.commit. + + The two parts of crop and scale state are independent: the source + rectangle, and the destination size. Initially both are unset, that + is, no scaling is applied. The whole of the current wl_buffer is + used as the source, and the surface size is as defined in + wl_surface.attach. + + If the destination size is set, it causes the surface size to become + dst_width, dst_height. The source (rectangle) is scaled to exactly + this size. This overrides whatever the attached wl_buffer size is, + unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface + has no content and therefore no size. Otherwise, the size is always + at least 1x1 in surface coordinates. + + If the source rectangle is set, it defines what area of the + wl_buffer is taken as the source. If the source rectangle is set and + the destination size is not set, the surface size becomes the source + rectangle size rounded up to the nearest integer. If the source size + is already exactly integers, this results in cropping without scaling. + + The coordinate transformations from buffer pixel coordinates up to + the surface-local coordinates happen in the following order: + 1. buffer_transform (wl_surface.set_buffer_transform) + 2. buffer_scale (wl_surface.set_buffer_scale) + 3. crop and scale (wl_viewport.set*) + This means, that the source rectangle coordinates of crop and scale + are given in the coordinates after the buffer transform and scale, + i.e. in the coordinates that would be the surface-local coordinates + if the crop and scale was not applied. + + If the source rectangle is partially or completely outside of the + wl_buffer, then the surface contents are undefined (not void), and + the surface size is still dst_width, dst_height. + + The x, y arguments of wl_surface.attach are applied as normal to + the surface. They indicate how many pixels to remove from the + surface size from the left and the top. In other words, they are + still in the surface-local coordinate system, just like dst_width + and dst_height are. + + If the wl_surface associated with the wl_viewport is destroyed, + the wl_viewport object becomes inert. + + If the wl_viewport object is destroyed, the crop and scale + state is removed from the wl_surface. The change will be applied + on the next wl_surface.commit. + + + + + The associated wl_surface's crop and scale state is removed. + The change is applied on the next wl_surface.commit. + + + + + + + + + + Set both source rectangle and destination size of the associated + wl_surface. See wl_viewport for the description, and relation to + the wl_buffer size. + + The bad_value protocol error is raised if src_width or + src_height is negative, or if dst_width or dst_height is not + positive. + + The crop and scale state is double-buffered state, and will be + applied on the next wl_surface.commit. + + Arguments dst_x and dst_y do not exist here, use the x and y + arguments to wl_surface.attach. The x, y, dst_width, and dst_height + define the surface-local coordinate system irrespective of the + attached wl_buffer size. + + + + + + + + + + + + + Set the source rectangle of the associated wl_surface. See + wl_viewport for the description, and relation to the wl_buffer + size. + + If width is -1.0 and height is -1.0, the destination size is unset + instead. Any other pair of values for width and height that + contains zero or negative values raises the bad_value protocol + error. + + The crop and scale state is double-buffered state, and will be + applied on the next wl_surface.commit. + + + + + + + + + + + Set the destination size of the associated wl_surface. See + wl_viewport for the description, and relation to the wl_buffer + size. + + If width is -1 and height is -1, the destination size is unset + instead. Any other pair of values for width and height that + contains zero or negative values raises the bad_value protocol + error. + + The crop and scale state is double-buffered state, and will be + applied on the next wl_surface.commit. + + Arguments x and y do not exist here, use the x and y arguments to + wl_surface.attach. The x, y, width, and height define the + surface-local coordinate system irrespective of the attached + wl_buffer size. + + + + + + + diff --git a/waylandsink/src/tizen-wlvideoformat.c b/waylandsink/src/tizen-wlvideoformat.c new file mode 100644 index 0000000..4c78c14 --- /dev/null +++ b/waylandsink/src/tizen-wlvideoformat.c @@ -0,0 +1,113 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * Copyright (C) 2012 Wim Taymans + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "tizen-wlvideoformat.h" +#ifdef GST_WLSINK_ENHANCEMENT + +GST_DEBUG_CATEGORY_EXTERN (gsttizenwl_debug); +#define GST_CAT_DEFAULT gsttizenwl_debug + +typedef struct +{ + uint32_t wl_format; + GstVideoFormat gst_format; +} wl_VideoFormat; + +static const wl_VideoFormat formats[] = { +#if G_BYTE_ORDER == G_BIG_ENDIAN + {TBM_FORMAT_XRGB8888, GST_VIDEO_FORMAT_xRGB}, + {TBM_FORMAT_XBGR8888, GST_VIDEO_FORMAT_xBGR}, + {TBM_FORMAT_RGBX8888, GST_VIDEO_FORMAT_RGBx}, + {TBM_FORMAT_BGRX8888, GST_VIDEO_FORMAT_BGRx}, + {TBM_FORMAT_ARGB8888, GST_VIDEO_FORMAT_ARGB}, + {TBM_FORMAT_ABGR8888, GST_VIDEO_FORMAT_RGBA}, + {TBM_FORMAT_RGBA8888, GST_VIDEO_FORMAT_RGBA}, + {TBM_FORMAT_BGRA8888, GST_VIDEO_FORMAT_BGRA}, +#else + {TBM_FORMAT_XRGB8888, GST_VIDEO_FORMAT_BGRx}, + {TBM_FORMAT_XBGR8888, GST_VIDEO_FORMAT_RGBx}, + {TBM_FORMAT_RGBX8888, GST_VIDEO_FORMAT_xBGR}, + {TBM_FORMAT_BGRX8888, GST_VIDEO_FORMAT_xRGB}, + {TBM_FORMAT_ARGB8888, GST_VIDEO_FORMAT_BGRA}, + {TBM_FORMAT_ABGR8888, GST_VIDEO_FORMAT_RGBA}, + {TBM_FORMAT_RGBA8888, GST_VIDEO_FORMAT_ABGR}, + {TBM_FORMAT_BGRA8888, GST_VIDEO_FORMAT_ARGB}, +#endif + {TBM_FORMAT_RGB565, GST_VIDEO_FORMAT_RGB16}, + {TBM_FORMAT_BGR565, GST_VIDEO_FORMAT_BGR16}, + {TBM_FORMAT_RGB888, GST_VIDEO_FORMAT_RGB}, + {TBM_FORMAT_BGR888, GST_VIDEO_FORMAT_BGR}, + {TBM_FORMAT_YUYV, GST_VIDEO_FORMAT_YUY2}, + {TBM_FORMAT_YVYU, GST_VIDEO_FORMAT_YVYU}, + {TBM_FORMAT_UYVY, GST_VIDEO_FORMAT_UYVY}, + {TBM_FORMAT_AYUV, GST_VIDEO_FORMAT_AYUV}, + {TBM_FORMAT_NV12, GST_VIDEO_FORMAT_NV12}, + {TBM_FORMAT_NV21, GST_VIDEO_FORMAT_NV21}, + {TBM_FORMAT_NV16, GST_VIDEO_FORMAT_NV16}, + {TBM_FORMAT_YUV410, GST_VIDEO_FORMAT_YUV9}, + {TBM_FORMAT_YVU410, GST_VIDEO_FORMAT_YVU9}, + {TBM_FORMAT_YUV411, GST_VIDEO_FORMAT_Y41B}, + {TBM_FORMAT_YUV420, GST_VIDEO_FORMAT_I420}, + {TBM_FORMAT_YVU420, GST_VIDEO_FORMAT_YV12}, + {TBM_FORMAT_YUV422, GST_VIDEO_FORMAT_Y42B}, + {TBM_FORMAT_YUV444, GST_VIDEO_FORMAT_v308}, + {TBM_FORMAT_NV12MT, GST_VIDEO_FORMAT_ST12}, + {TBM_FORMAT_NV12, GST_VIDEO_FORMAT_SN12}, +}; + +uint32_t +gst_video_format_to_wayland_format (GstVideoFormat format) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (formats); i++) + if (formats[i].gst_format == format) + return formats[i].wl_format; + + GST_WARNING ("wayland video format not found"); + return -1; +} + +GstVideoFormat +gst_wayland_format_to_video_format (uint32_t wl_format) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (formats); i++) + if (formats[i].wl_format == wl_format) + return formats[i].gst_format; + + GST_WARNING ("gst video format not found"); + return GST_VIDEO_FORMAT_UNKNOWN; +} + +const gchar * +gst_wayland_format_to_string (uint32_t wl_format) +{ + return gst_video_format_to_string + (gst_wayland_format_to_video_format (wl_format)); +} +#endif diff --git a/waylandsink/src/tizen-wlvideoformat.h b/waylandsink/src/tizen-wlvideoformat.h new file mode 100644 index 0000000..a62e7d8 --- /dev/null +++ b/waylandsink/src/tizen-wlvideoformat.h @@ -0,0 +1,41 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * Copyright (C) 2012 Wim Taymans + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_TIZEN_WL_VIDEO_FORMAT_H__ +#define __GST_TIZEN_WL_VIDEO_FORMAT_H__ + +#include +#include + +#ifdef GST_WLSINK_ENHANCEMENT +#include + +G_BEGIN_DECLS + uint32_t gst_video_format_to_wayland_format (GstVideoFormat format); +GstVideoFormat gst_wayland_format_to_video_format (uint32_t wl_format); + +const gchar *gst_wayland_format_to_string (uint32_t wl_format); + +G_END_DECLS +#endif +#endif diff --git a/waylandsink/src/waylandpool.c b/waylandsink/src/waylandpool.c new file mode 100644 index 0000000..b36d2e2 --- /dev/null +++ b/waylandsink/src/waylandpool.c @@ -0,0 +1,769 @@ +/* GStreamer + * Copyright (C) 2012 Intel Corporation + * Copyright (C) 2012 Sreerenj Balachandran + * Copyright (C) 2014 Collabora Ltd. + * + * 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 "waylandpool.h" +#include "wldisplay.h" +#include "wlvideoformat.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef GST_WLSINK_ENHANCEMENT +//#define DUMP_BUFFER +#ifdef DUMP_BUFFER +int dump_cnt = 0; +int _write_rawdata (const char *file, const void *data, unsigned int size); +#endif +#endif + +GST_DEBUG_CATEGORY_EXTERN (gsttizenwl_debug); +#define GST_CAT_DEFAULT gsttizenwl_debug + +/* wl metadata */ +GType +gst_wl_meta_api_get_type (void) +{ + static volatile GType type; + static const gchar *tags[] = + { "memory", "size", "colorspace", "orientation", NULL }; + + if (g_once_init_enter (&type)) { + GType _type = gst_meta_api_type_register ("GstWlMetaAPI", tags); + g_once_init_leave (&type, _type); + } + return type; +} + +static void +gst_wl_meta_free (GstWlMeta * meta, GstBuffer * buffer) +{ +#ifdef GST_WLSINK_ENHANCEMENT + if (!meta || !meta->pool) + return; + if (meta->tsurface) + tbm_surface_destroy (meta->tsurface); + g_hash_table_remove (meta->pool->buffers_map, meta->wbuffer); +#endif + GST_DEBUG ("destroying wl_buffer %p", meta->wbuffer); + wl_buffer_destroy (meta->wbuffer); +} + +const GstMetaInfo * +gst_wl_meta_get_info (void) +{ + static const GstMetaInfo *wl_meta_info = NULL; + + if (g_once_init_enter (&wl_meta_info)) { + const GstMetaInfo *meta = + gst_meta_register (GST_WL_META_API_TYPE, "GstWlMeta", + sizeof (GstWlMeta), (GstMetaInitFunction) NULL, + (GstMetaFreeFunction) gst_wl_meta_free, + (GstMetaTransformFunction) NULL); + g_once_init_leave (&wl_meta_info, meta); + } + return wl_meta_info; +} + +/* bufferpool */ +static void gst_wayland_buffer_pool_finalize (GObject * object); +static gboolean gst_wayland_buffer_pool_set_config (GstBufferPool * pool, + GstStructure * config); +static gboolean gst_wayland_buffer_pool_start (GstBufferPool * pool); +static gboolean gst_wayland_buffer_pool_stop (GstBufferPool * pool); +static GstFlowReturn gst_wayland_buffer_pool_alloc (GstBufferPool * pool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params); + +#ifdef GST_WLSINK_ENHANCEMENT +/*tizen buffer pool*/ +static void gst_wayland_tizen_buffer_pool_finalize (GObject * object); +static gboolean gst_wayland_tizen_buffer_pool_start (GstBufferPool * pool); +static gboolean gst_wayland_tizen_buffer_pool_stop (GstBufferPool * pool); +static GstFlowReturn gst_wayland_tizen_buffer_pool_alloc (GstBufferPool * pool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params); +void gst_wayland_buffer_pool_remove_displaying_buffer (GstWaylandBufferPool * + self, struct wl_buffer *wl_buffer); +#endif + +#define gst_wayland_buffer_pool_parent_class parent_class +G_DEFINE_TYPE (GstWaylandBufferPool, gst_wayland_buffer_pool, + GST_TYPE_BUFFER_POOL); + +static void +gst_wayland_buffer_pool_class_init (GstWaylandBufferPoolClass * klass) +{ + FUNCTION_ENTER (); + + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; + + gstbufferpool_class->set_config = gst_wayland_buffer_pool_set_config; +#ifdef GST_WLSINK_ENHANCEMENT + gobject_class->finalize = gst_wayland_tizen_buffer_pool_finalize; + gstbufferpool_class->start = gst_wayland_tizen_buffer_pool_start; + gstbufferpool_class->alloc_buffer = gst_wayland_tizen_buffer_pool_alloc; +#else + gobject_class->finalize = gst_wayland_buffer_pool_finalize; + gstbufferpool_class->start = gst_wayland_buffer_pool_start; + gstbufferpool_class->stop = gst_wayland_buffer_pool_stop; + gstbufferpool_class->alloc_buffer = gst_wayland_buffer_pool_alloc; +#endif +} + +static void +gst_wayland_buffer_pool_init (GstWaylandBufferPool * self) +{ + FUNCTION_ENTER (); + + gst_video_info_init (&self->info); + g_mutex_init (&self->buffers_map_mutex); + self->buffers_map = g_hash_table_new (g_direct_hash, g_direct_equal); + + g_mutex_init (&self->displaying_buffers_map_mutex); + self->displaying_buffers_map = + g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +gst_wayland_buffer_pool_finalize (GObject * object) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *pool = GST_WAYLAND_BUFFER_POOL_CAST (object); + + if (pool->wl_pool) + gst_wayland_buffer_pool_stop (GST_BUFFER_POOL (pool)); + + g_mutex_clear (&pool->buffers_map_mutex); + g_hash_table_unref (pool->buffers_map); + + g_mutex_clear (&pool->displaying_buffers_map_mutex); + g_hash_table_unref (pool->displaying_buffers_map); + + g_object_unref (pool->display); + + G_OBJECT_CLASS (gst_wayland_buffer_pool_parent_class)->finalize (object); +} + +static void +buffer_release (void *data, struct wl_buffer *wl_buffer) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = data; + GstBuffer *buffer; + GstWlMeta *meta; + + g_mutex_lock (&self->buffers_map_mutex); + +#ifdef GST_WLSINK_ENHANCEMENT + /*remove displaying buffer */ + if (self->display->is_native_format == TRUE) + gst_wayland_buffer_pool_remove_displaying_buffer (self, wl_buffer); +#endif + buffer = g_hash_table_lookup (self->buffers_map, wl_buffer); + + GST_LOG_OBJECT (self, "wl_buffer::release (GstBuffer: %p)", buffer); + + if (buffer) { + meta = gst_buffer_get_wl_meta (buffer); + if (meta->used_by_compositor) { + meta->used_by_compositor = FALSE; + /* unlock before unref because stop() may be called from here */ + GST_LOG_OBJECT (self, "Decrease ref count of buffer"); + gst_buffer_unref (buffer); + } + } + g_mutex_unlock (&self->buffers_map_mutex); +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +void +gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self, + GstBuffer * buffer) +{ + FUNCTION_ENTER (); + + GstWlMeta *meta; + + meta = gst_buffer_get_wl_meta (buffer); + g_return_if_fail (meta != NULL); + g_return_if_fail (meta->pool == self); + g_return_if_fail (meta->used_by_compositor == FALSE); + + meta->used_by_compositor = TRUE; + GST_LOG_OBJECT (self, "Increase ref count of buffer"); + gst_buffer_ref (buffer); +} + +static void +unref_used_buffers (gpointer key, gpointer value, gpointer data) +{ + FUNCTION_ENTER (); + + GstBuffer *buffer = value; + GstWlMeta *meta = gst_buffer_get_wl_meta (buffer); + GList **to_unref = data; + + if (meta == NULL) + return; + + if (meta->used_by_compositor) { + meta->used_by_compositor = FALSE; + *to_unref = g_list_prepend (*to_unref, buffer); + } +} + +void +gst_wayland_compositor_release_all_buffers (GstWaylandBufferPool * self) +{ + FUNCTION_ENTER (); + + GList *to_unref = NULL; + + g_mutex_lock (&self->buffers_map_mutex); + g_hash_table_foreach (self->buffers_map, unref_used_buffers, &to_unref); + + if (to_unref) { + g_list_free_full (to_unref, (GDestroyNotify) gst_buffer_unref); + } + g_mutex_unlock (&self->buffers_map_mutex); +} + +static gboolean +gst_wayland_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL_CAST (pool); + GstCaps *caps; + + if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) + goto wrong_config; + + if (caps == NULL) + goto no_caps; + + /* now parse the caps from the config */ + if (!gst_video_info_from_caps (&self->info, caps)) + goto wrong_caps; + + GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, + GST_VIDEO_INFO_WIDTH (&self->info), GST_VIDEO_INFO_HEIGHT (&self->info), + caps); + + /*Fixme: Enable metadata checking handling based on the config of pool */ + + return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config); + /* ERRORS */ +wrong_config: + { + GST_WARNING_OBJECT (pool, "invalid config"); + return FALSE; + } +no_caps: + { + GST_WARNING_OBJECT (pool, "no caps in config"); + return FALSE; + } +wrong_caps: + { + GST_WARNING_OBJECT (pool, + "failed getting geometry from caps %" GST_PTR_FORMAT, caps); + return FALSE; + } +} + +static gboolean +gst_wayland_buffer_pool_start (GstBufferPool * pool) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL (pool); + + GST_DEBUG_OBJECT (self, "Initializing wayland buffer pool"); + + guint size = 0; + int fd; + char filename[1024]; + static int init = 0; + + GST_DEBUG_OBJECT (self, "Initializing wayland buffer pool"); + + /* configure */ + size = GST_VIDEO_INFO_SIZE (&self->info) * 15; + + /* allocate shm pool */ + snprintf (filename, 1024, "%s/%s-%d-%s", g_get_user_runtime_dir (), + "wayland-shm", init++, "XXXXXX"); + + fd = mkstemp (filename); + if (fd < 0) { + GST_ERROR_OBJECT (pool, "opening temp file %s failed: %s", filename, + strerror (errno)); + return FALSE; + } + if (ftruncate (fd, size) < 0) { + GST_ERROR_OBJECT (pool, "ftruncate failed: %s", strerror (errno)); + close (fd); + return FALSE; + } + + self->data = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (self->data == MAP_FAILED) { + GST_ERROR_OBJECT (pool, "mmap failed: %s", strerror (errno)); + close (fd); + return FALSE; + } + + self->wl_pool = wl_shm_create_pool (self->display->shm, fd, size); + unlink (filename); + close (fd); + + self->size = size; + self->used = 0; + + + return GST_BUFFER_POOL_CLASS (parent_class)->start (pool); +} + +static gboolean +gst_wayland_buffer_pool_stop (GstBufferPool * pool) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL (pool); + + GST_DEBUG_OBJECT (self, "Stopping wayland buffer pool"); + + munmap (self->data, self->size); + wl_shm_pool_destroy (self->wl_pool); + + self->wl_pool = NULL; + self->size = 0; + self->used = 0; + + /* all buffers are about to be destroyed; + * we should no longer do anything with them */ + g_mutex_lock (&self->buffers_map_mutex); + g_hash_table_remove_all (self->buffers_map); + g_mutex_unlock (&self->buffers_map_mutex); + + return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool); +} + +static GstFlowReturn +gst_wayland_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer, + GstBufferPoolAcquireParams * params) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL_CAST (pool); + + gint width, height, stride; + gsize size; + enum wl_shm_format format; + gint offset; + void *data; + GstWlMeta *meta; + + width = GST_VIDEO_INFO_WIDTH (&self->info); + height = GST_VIDEO_INFO_HEIGHT (&self->info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0); + size = GST_VIDEO_INFO_SIZE (&self->info); + format = + gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT (&self->info)); + + GST_DEBUG_OBJECT (self, "Allocating buffer of size %" G_GSSIZE_FORMAT + " (%d x %d, stride %d), format %s", size, width, height, stride, + gst_wayland_format_to_string (format)); + /* try to reserve another memory block from the shm pool */ + if (self->used + size > self->size) + goto no_buffer; + + offset = self->used; + self->used += size; + + data = ((gchar *) self->data) + offset; + + /* create buffer and its metadata object */ + *buffer = gst_buffer_new (); + meta = (GstWlMeta *) gst_buffer_add_meta (*buffer, GST_WL_META_INFO, NULL); + meta->pool = self; + + meta->wbuffer = wl_shm_pool_create_buffer (self->wl_pool, offset, + width, height, stride, format); + meta->used_by_compositor = FALSE; + + /* configure listening to wl_buffer.release */ + g_mutex_lock (&self->buffers_map_mutex); + g_hash_table_insert (self->buffers_map, meta->wbuffer, *buffer); + g_mutex_unlock (&self->buffers_map_mutex); + + wl_buffer_add_listener (meta->wbuffer, &buffer_listener, self); + + /* add the allocated memory on the GstBuffer */ + gst_buffer_append_memory (*buffer, + gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, data, + size, 0, size, NULL, NULL)); + + return GST_FLOW_OK; + + /* ERROR */ +no_buffer: + { + GST_WARNING_OBJECT (pool, "can't create buffer"); + return GST_FLOW_ERROR; + } +} + +GstBufferPool * +gst_wayland_buffer_pool_new (GstWlDisplay * display) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *pool; + + g_return_val_if_fail (GST_IS_WL_DISPLAY (display), NULL); + pool = g_object_new (GST_TYPE_WAYLAND_BUFFER_POOL, NULL); + pool->display = g_object_ref (display); + + return GST_BUFFER_POOL_CAST (pool); +} + +#ifdef GST_WLSINK_ENHANCEMENT + +static gboolean +gst_wayland_tizen_buffer_pool_start (GstBufferPool * pool) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL (pool); + + GST_DEBUG_OBJECT (self, "Initializing tizen buffer pool"); + + tbm_bo_handle vitual_addr; + guint size = 0; + + if (self->display->is_native_format == TRUE) { + /*in case of SN12 or ST12 video format */ + size = self->display->native_video_size * 15; + vitual_addr.ptr = NULL; + + } else { + /*in case of normal video format */ + size = GST_VIDEO_INFO_SIZE (&self->info) * 15; + + self->display->tbm_bufmgr = + wayland_tbm_client_get_bufmgr (self->display->tbm_client); + g_return_if_fail (self->display->tbm_bufmgr != NULL); + + self->display->tbm_bo = + tbm_bo_alloc (self->display->tbm_bufmgr, size, TBM_BO_DEFAULT); + if (!self->display->tbm_bo) { + GST_ERROR_OBJECT (pool, "alloc tbm bo(size:%d) failed: %s", size, + strerror (errno)); + return FALSE; + } + + vitual_addr = tbm_bo_get_handle (self->display->tbm_bo, TBM_DEVICE_CPU); + if (!vitual_addr.ptr) { + GST_ERROR_OBJECT (pool, "get tbm bo handle failed: %s", strerror (errno)); + tbm_bo_unref (self->display->tbm_bo); + self->display->tbm_bo = NULL; + return FALSE; + } + } + + self->data = vitual_addr.ptr; + self->size = size; + self->used = 0; + + return GST_BUFFER_POOL_CLASS (parent_class)->start (pool); +} + +static gboolean +gst_wayland_tizen_buffer_pool_stop (GstBufferPool * pool) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL (pool); + + GST_DEBUG_OBJECT (self, "Stopping tizen buffer pool"); + + self->size = 0; + self->used = 0; + + self->display->tbm_bufmgr = NULL; + + /* all buffers are about to be destroyed; + * we should no longer do anything with them */ + g_mutex_lock (&self->buffers_map_mutex); + g_hash_table_remove_all (self->buffers_map); + g_mutex_unlock (&self->buffers_map_mutex); + + g_mutex_lock (&self->displaying_buffers_map_mutex); + g_hash_table_remove_all (self->displaying_buffers_map); + g_mutex_unlock (&self->displaying_buffers_map_mutex); + + return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool); +} + +static GstFlowReturn +gst_wayland_tizen_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer, + GstBufferPoolAcquireParams * params) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL_CAST (pool); + + gint width, height; + gsize size; + uint32_t format; + gint data_offset; + void *data; + GstWlMeta *meta; + tbm_bo_handle vitual_addr; + tbm_surface_info_s info; + int num_bo; + + if (self->display->is_native_format == TRUE) { + /*in case of SN12 or ST12 video format */ + width = GST_VIDEO_INFO_WIDTH (&self->info); + height = GST_VIDEO_INFO_HEIGHT (&self->info); + size = self->display->native_video_size; + + format = + gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT + (&self->info)); + + + vitual_addr = tbm_bo_get_handle (self->display->bo[0], TBM_DEVICE_CPU); + if (!vitual_addr.ptr) { + GST_ERROR_OBJECT (pool, "get tbm bo handle failed: %s", strerror (errno)); + return FALSE; + } + self->data = vitual_addr.ptr; + data = ((gchar *) self->data); +#ifdef DUMP_BUFFER + int ret; + char file_name[128]; + if (dump_cnt < 10) { + sprintf (file_name, "/root/WLSINK_OUT_DUMP_%2.2d.dump", dump_cnt++); + ret = _write_rawdata (file_name, vitual_addr.ptr, size); + if (ret) { + GST_ERROR_OBJECT (pool, "_write_rawdata() failed"); + } + } +#endif + /* create buffer and its metadata object */ + *buffer = gst_buffer_new (); + meta = (GstWlMeta *) gst_buffer_add_meta (*buffer, GST_WL_META_INFO, NULL); + meta->pool = self; + GST_DEBUG ("TBM bo %p %p %p", self->display->bo[0], + self->display->bo[1], 0); + + info.width = width; + info.height = height; + info.format = format; + info.bpp = tbm_surface_internal_get_bpp (info.format); + info.num_planes = tbm_surface_internal_get_num_planes (info.format); + info.planes[0].stride = self->display->stride_width[0]; + info.planes[1].stride = self->display->stride_width[1]; + info.planes[0].offset = 0; + info.planes[1].offset = + (self->display->bo[1]) ? 0 : self->display->plane_size[0]; + num_bo = (self->display->bo[1]) ? 2 : 1; + + meta->tsurface = + tbm_surface_internal_create_with_bos (&info, self->display->bo, num_bo); + meta->wbuffer = + wayland_tbm_client_create_buffer (self->display->tbm_client, + meta->tsurface); + wl_proxy_set_queue ((struct wl_proxy *) meta->wbuffer, + self->display->queue); + meta->used_by_compositor = FALSE; + + GST_DEBUG ("tizen_buffer_pool_create_planar_buffer create wl_buffer %p", + meta->wbuffer); + } else { + int stride; + + /*in case of normal video format */ + width = GST_VIDEO_INFO_WIDTH (&self->info); + height = GST_VIDEO_INFO_HEIGHT (&self->info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0); + size = GST_VIDEO_INFO_SIZE (&self->info); + format = + gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT + (&self->info)); + + GST_DEBUG_OBJECT (self, "Allocating buffer of size %" G_GSSIZE_FORMAT + " (%d x %d, stride %d), format %s", size, width, height, stride, + gst_wayland_format_to_string (format)); + + /* try to reserve another memory block from the shm pool */ + if (self->used + size > self->size) + goto no_buffer; + + data_offset = self->used; + self->used += size; + + data = ((gchar *) self->data) + data_offset; + + /* create buffer and its metadata object */ + *buffer = gst_buffer_new (); + meta = (GstWlMeta *) gst_buffer_add_meta (*buffer, GST_WL_META_INFO, NULL); + meta->pool = self; + + info.width = width; + info.height = height; + info.format = format; + info.bpp = tbm_surface_internal_get_bpp (info.format); + info.num_planes = tbm_surface_internal_get_num_planes (info.format); + info.planes[0].stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 0); + info.planes[1].stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 1); + info.planes[2].stride = GST_VIDEO_INFO_PLANE_STRIDE (&self->info, 2); + info.planes[0].offset = GST_VIDEO_INFO_PLANE_OFFSET (&self->info, 0); + info.planes[1].offset = GST_VIDEO_INFO_PLANE_OFFSET (&self->info, 1); + info.planes[2].offset = GST_VIDEO_INFO_PLANE_OFFSET (&self->info, 2); + + meta->tsurface = + tbm_surface_internal_create_with_bos (&info, &self->display->tbm_bo, 1); + meta->wbuffer = + wayland_tbm_client_create_buffer (self->display->tbm_client, + meta->tsurface); + wl_proxy_set_queue ((struct wl_proxy *) meta->wbuffer, + self->display->queue); + meta->used_by_compositor = FALSE; + } + + /* configure listening to wl_buffer.release */ + g_mutex_lock (&self->buffers_map_mutex); + g_hash_table_insert (self->buffers_map, meta->wbuffer, *buffer); + g_mutex_unlock (&self->buffers_map_mutex); + + wl_buffer_add_listener (meta->wbuffer, &buffer_listener, self); + + /* add the allocated memory on the GstBuffer */ + gst_buffer_append_memory (*buffer, + gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, data, + size, 0, size, NULL, NULL)); + return GST_FLOW_OK; + + /* ERROR */ +no_buffer: + { + GST_WARNING_OBJECT (pool, "can't create buffer"); + return GST_FLOW_ERROR; + } +} + +static void +gst_wayland_tizen_buffer_pool_finalize (GObject * object) +{ + FUNCTION_ENTER (); + + GstWaylandBufferPool *pool = GST_WAYLAND_BUFFER_POOL_CAST (object); + + if (pool->display->tbm_bufmgr) { + gst_wayland_tizen_buffer_pool_stop (GST_BUFFER_POOL (pool)); + } else { + /*already stop */ + return; + } + g_mutex_clear (&pool->buffers_map_mutex); + g_hash_table_unref (pool->buffers_map); + + g_mutex_clear (&pool->displaying_buffers_map_mutex); + g_hash_table_unref (pool->displaying_buffers_map); + + g_object_unref (pool->display); + + G_OBJECT_CLASS (gst_wayland_buffer_pool_parent_class)->finalize (object); +} + +void +gst_wayland_buffer_pool_add_displaying_buffer (GstBufferPool * pool, + GstWlMeta * meta, GstBuffer * buffer) +{ + FUNCTION_ENTER (); + g_return_val_if_fail (pool, NULL); + g_return_val_if_fail (meta, NULL); + g_return_val_if_fail (buffer, NULL); + + GstWaylandBufferPool *self = GST_WAYLAND_BUFFER_POOL_CAST (pool); + + g_mutex_lock (&self->displaying_buffers_map_mutex); + + GST_LOG_OBJECT (self, "key value is meta->wbuffer(%p)", meta->wbuffer); + GST_LOG_OBJECT (self, "Increase ref count of buffer(%p) from omx", buffer); + gst_buffer_ref (buffer); + g_hash_table_insert (self->displaying_buffers_map, meta->wbuffer, buffer); + + g_mutex_unlock (&self->displaying_buffers_map_mutex); +} + +void +gst_wayland_buffer_pool_remove_displaying_buffer (GstWaylandBufferPool * self, + struct wl_buffer *wl_buffer) +{ + FUNCTION_ENTER (); + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (wl_buffer, NULL); + + GstBuffer *buffer; + g_mutex_lock (&self->displaying_buffers_map_mutex); + buffer = g_hash_table_lookup (self->displaying_buffers_map, wl_buffer); + if (buffer) { + GST_LOG_OBJECT (self, "Decrease ref count of buffer(%p) from omx", buffer); + g_hash_table_remove (self->displaying_buffers_map, wl_buffer); + gst_buffer_unref (buffer); + } + g_mutex_unlock (&self->displaying_buffers_map_mutex); +} +#endif +#ifdef DUMP_BUFFER +int +_write_rawdata (const char *file, const void *data, unsigned int size) +{ + FILE *fp; + + fp = fopen (file, "wb"); + if (fp == NULL) + return -1; + + fwrite ((char *) data, sizeof (char), size, fp); + fclose (fp); + + return 0; +} +#endif diff --git a/waylandsink/src/waylandpool.h b/waylandsink/src/waylandpool.h new file mode 100644 index 0000000..85fdd0e --- /dev/null +++ b/waylandsink/src/waylandpool.h @@ -0,0 +1,111 @@ +/* GStreamer Wayland buffer pool + * Copyright (C) 2012 Intel Corporation + * Copyright (C) 2012 Sreerenj Balachandran + * Copyright (C) 2014 Collabora Ltd. + * + * 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_WAYLAND_BUFFER_POOL_H__ +#define __GST_WAYLAND_BUFFER_POOL_H__ + +#include +#include + +#include "wldisplay.h" +#ifdef GST_WLSINK_ENHANCEMENT +#include +#include +#include +#endif + +G_BEGIN_DECLS +#define GST_TYPE_WAYLAND_BUFFER_POOL (gst_wayland_buffer_pool_get_type()) +#define GST_IS_WAYLAND_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WAYLAND_BUFFER_POOL)) +#define GST_WAYLAND_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WAYLAND_BUFFER_POOL, GstWaylandBufferPool)) +#define GST_WAYLAND_BUFFER_POOL_CAST(obj) ((GstWaylandBufferPool*)(obj)) +#if 1 +#define FUNCTION_ENTER() GST_INFO("") +#else +#define FUNCTION_ENTER() +#endif +typedef struct _GstWaylandBufferPool GstWaylandBufferPool; +typedef struct _GstWaylandBufferPoolClass GstWaylandBufferPoolClass; + +/* buffer meta */ +typedef struct _GstWlMeta GstWlMeta; + +GType gst_wl_meta_api_get_type (void); +#define GST_WL_META_API_TYPE (gst_wl_meta_api_get_type()) + +const GstMetaInfo *gst_wl_meta_get_info (void); +#define GST_WL_META_INFO (gst_wl_meta_get_info()) + +#define gst_buffer_get_wl_meta(b) ((GstWlMeta*)gst_buffer_get_meta((b),GST_WL_META_API_TYPE)) + +struct _GstWlMeta +{ + GstMeta meta; + + GstWaylandBufferPool *pool; + struct wl_buffer *wbuffer; + gboolean used_by_compositor; +#ifdef GST_WLSINK_ENHANCEMENT + tbm_surface_h tsurface; +#endif +}; + +/* buffer pool */ +struct _GstWaylandBufferPool +{ + GstBufferPool bufferpool; + GstWlDisplay *display; + + /* external configuration */ + GstVideoInfo info; + + /* allocation data */ + struct wl_shm_pool *wl_pool; + size_t size; + size_t used; + void *data; + + GMutex buffers_map_mutex; + GHashTable *buffers_map; +#ifdef GST_WLSINK_ENHANCEMENT + GMutex displaying_buffers_map_mutex; + GHashTable *displaying_buffers_map; +#endif +}; + +struct _GstWaylandBufferPoolClass +{ + GstBufferPoolClass parent_class; +}; + +GType gst_wayland_buffer_pool_get_type (void); + +GstBufferPool *gst_wayland_buffer_pool_new (GstWlDisplay * display); + + +void gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self, + GstBuffer * buffer); +void gst_wayland_compositor_release_all_buffers (GstWaylandBufferPool * self); +void gst_wayland_buffer_pool_add_displaying_buffer (GstBufferPool * pool, + GstWlMeta * meta, GstBuffer * buffer); + +G_END_DECLS +#endif /*__GST_WAYLAND_BUFFER_POOL_H__*/ diff --git a/waylandsink/src/wldisplay.c b/waylandsink/src/wldisplay.c new file mode 100644 index 0000000..4a1ec63 --- /dev/null +++ b/waylandsink/src/wldisplay.c @@ -0,0 +1,358 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "wldisplay.h" +#include + +#ifdef GST_WLSINK_ENHANCEMENT +#include +#include +#include +#include +#include + +static void +handle_tizen_video_format (void *data, struct tizen_video *tizen_video, + uint32_t format) +{ + FUNCTION_ENTER (); + GstWlDisplay *self = data; + + g_return_if_fail (self != NULL); + + GST_INFO ("format is %d", format); + g_array_append_val (self->formats, format); +} + +static const struct tizen_video_listener tz_video_listener = { + handle_tizen_video_format +}; +#endif + +GST_DEBUG_CATEGORY_EXTERN (gsttizenwl_debug); +#define GST_CAT_DEFAULT gsttizenwl_debug + +G_DEFINE_TYPE (GstWlDisplay, gst_wl_display, G_TYPE_OBJECT); + +static void gst_wl_display_finalize (GObject * gobject); + +static void +gst_wl_display_class_init (GstWlDisplayClass * klass) +{ + FUNCTION_ENTER (); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = gst_wl_display_finalize; +} + +static void +gst_wl_display_init (GstWlDisplay * self) +{ + FUNCTION_ENTER (); + + self->formats = g_array_new (FALSE, FALSE, sizeof (uint32_t)); + self->wl_fd_poll = gst_poll_new (TRUE); +} + +static void +gst_wl_display_finalize (GObject * gobject) +{ + FUNCTION_ENTER (); + + GstWlDisplay *self = GST_WL_DISPLAY (gobject); + + gst_poll_set_flushing (self->wl_fd_poll, TRUE); + + if (self->thread) + g_thread_join (self->thread); + +#ifdef GST_WLSINK_ENHANCEMENT + if (self->is_native_format == FALSE) { + /*in case of normal video format */ + if (self->tbm_bo) + tbm_bo_unref (self->tbm_bo); + self->tbm_bo = NULL; + } + if (self->tbm_client) { + wayland_tbm_client_deinit (self->tbm_client); + self->tbm_client = NULL; + } + self->tbm_bufmgr = NULL; +#endif + + g_array_unref (self->formats); + gst_poll_free (self->wl_fd_poll); + +#ifndef GST_WLSINK_ENHANCEMENT + if (self->shm) + wl_shm_destroy (self->shm); +#endif + + if (self->shell) + wl_shell_destroy (self->shell); + + if (self->compositor) + wl_compositor_destroy (self->compositor); + + if (self->subcompositor) + wl_subcompositor_destroy (self->subcompositor); + + if (self->registry) + wl_registry_destroy (self->registry); + + if (self->queue) + wl_event_queue_destroy (self->queue); + + if (self->own_display) { + wl_display_flush (self->display); + wl_display_disconnect (self->display); + } +#ifdef GST_WLSINK_ENHANCEMENT + if (self->tizen_policy) + tizen_policy_destroy (self->tizen_policy); + if (self->tizen_video) + tizen_video_destroy (self->tizen_video); +#endif + + G_OBJECT_CLASS (gst_wl_display_parent_class)->finalize (gobject); +} + +static void +sync_callback (void *data, struct wl_callback *callback, uint32_t serial) +{ + FUNCTION_ENTER (); + + gboolean *done = data; + *done = TRUE; +} + +static const struct wl_callback_listener sync_listener = { + sync_callback +}; + +static gint +gst_wl_display_roundtrip (GstWlDisplay * self) +{ + FUNCTION_ENTER (); + + struct wl_callback *callback; + gint ret = 0; + gboolean done = FALSE; + + g_return_val_if_fail (self != NULL, -1); + + /* We don't own the display, process only our queue */ + callback = wl_display_sync (self->display); + wl_callback_add_listener (callback, &sync_listener, &done); + wl_proxy_set_queue ((struct wl_proxy *) callback, self->queue); + while (ret != -1 && !done) + ret = wl_display_dispatch_queue (self->display, self->queue); + wl_callback_destroy (callback); + + return ret; +} + +static void +shm_format (void *data, struct wl_shm *wl_shm, uint32_t format) +{ + FUNCTION_ENTER (); + + GstWlDisplay *self = data; + + g_array_append_val (self->formats, format); +} + +static const struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +registry_handle_global (void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + + FUNCTION_ENTER (); + GstWlDisplay *self = data; + + if (g_strcmp0 (interface, "wl_compositor") == 0) { + self->compositor = wl_registry_bind (registry, id, &wl_compositor_interface, + MIN (version, 3)); + } else if (g_strcmp0 (interface, "wl_subcompositor") == 0) { + self->subcompositor = + wl_registry_bind (registry, id, &wl_subcompositor_interface, 1); + } else if (g_strcmp0 (interface, "wl_shell") == 0) { + self->shell = wl_registry_bind (registry, id, &wl_shell_interface, 1); +#ifndef GST_WLSINK_ENHANCEMENT + } else if (g_strcmp0 (interface, "wl_shm") == 0) { + self->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1); + wl_shm_add_listener (self->shm, &shm_listener, self); +#endif + } else if (g_strcmp0 (interface, "wl_scaler") == 0) { + self->scaler = wl_registry_bind (registry, id, &wl_scaler_interface, 2); +#ifdef GST_WLSINK_ENHANCEMENT + } else if (g_strcmp0 (interface, "tizen_policy") == 0) { + self->tizen_policy = + wl_registry_bind (registry, id, &tizen_policy_interface, 1); + } else if (g_strcmp0 (interface, "tizen_video") == 0) { + self->tizen_video = + wl_registry_bind (registry, id, &tizen_video_interface, version); + g_return_if_fail (self->tizen_video != NULL); + + GST_INFO ("id(%d)", id); + + tizen_video_add_listener (self->tizen_video, &tz_video_listener, self); + } +#endif +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global +}; + +static gpointer +gst_wl_display_thread_run (gpointer data) +{ + FUNCTION_ENTER (); + + GstWlDisplay *self = data; + GstPollFD pollfd = GST_POLL_FD_INIT; + + pollfd.fd = wl_display_get_fd (self->display); + gst_poll_add_fd (self->wl_fd_poll, &pollfd); + gst_poll_fd_ctl_read (self->wl_fd_poll, &pollfd, TRUE); + + /* main loop */ + while (1) { + while (wl_display_prepare_read_queue (self->display, self->queue) != 0) + wl_display_dispatch_queue_pending (self->display, self->queue); + wl_display_flush (self->display); + + if (gst_poll_wait (self->wl_fd_poll, GST_CLOCK_TIME_NONE) < 0) { + gboolean normal = (errno == EBUSY); + wl_display_cancel_read (self->display); + if (normal) + break; + else + goto error; + } else { + wl_display_read_events (self->display); + wl_display_dispatch_queue_pending (self->display, self->queue); + } + } + + return NULL; + +error: + GST_ERROR ("Error communicating with the wayland server"); + return NULL; +} + +GstWlDisplay * +gst_wl_display_new (const gchar * name, GError ** error) +{ + FUNCTION_ENTER (); + + struct wl_display *display; + + display = wl_display_connect (name); + + if (!display) { + *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0, + "Failed to connect to the wayland display '%s'", + name ? name : "(default)"); + return NULL; + } else { + return gst_wl_display_new_existing (display, TRUE, error); + } +} + +GstWlDisplay * +gst_wl_display_new_existing (struct wl_display * display, + gboolean take_ownership, GError ** error) +{ + FUNCTION_ENTER (); + + GstWlDisplay *self; + GError *err = NULL; + gint i; + + g_return_val_if_fail (display != NULL, NULL); + + self = g_object_new (GST_TYPE_WL_DISPLAY, NULL); + self->display = display; + self->own_display = take_ownership; + + self->queue = wl_display_create_queue (self->display); + self->registry = wl_display_get_registry (self->display); + wl_proxy_set_queue ((struct wl_proxy *) self->registry, self->queue); + wl_registry_add_listener (self->registry, ®istry_listener, self); + + /* we need exactly 2 roundtrips to discover global objects and their state */ + for (i = 0; i < 2; i++) { + if (gst_wl_display_roundtrip (self) < 0) { + *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0, + "Error communicating with the wayland display"); + g_object_unref (self); + return NULL; + } + } + + /* verify we got all the required interfaces */ +#define VERIFY_INTERFACE_EXISTS(var, interface) \ + if (!self->var) { \ + g_set_error (error, g_quark_from_static_string ("GstWlDisplay"), 0, \ + "Could not bind to " interface ". Either it is not implemented in " \ + "the compositor, or the implemented version doesn't match"); \ + g_object_unref (self); \ + return NULL; \ + } + + VERIFY_INTERFACE_EXISTS (compositor, "wl_compositor"); + VERIFY_INTERFACE_EXISTS (subcompositor, "wl_subcompositor"); + VERIFY_INTERFACE_EXISTS (shell, "wl_shell"); +#ifdef GST_WLSINK_ENHANCEMENT + VERIFY_INTERFACE_EXISTS (tizen_video, "tizen_video"); + self->tbm_client = wayland_tbm_client_init (self->display); + if (!self->tbm_client) { + *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0, + "Error initializing wayland-tbm"); + g_object_unref (self); + return NULL; + } +#else + VERIFY_INTERFACE_EXISTS (shm, "wl_shm"); +#endif + VERIFY_INTERFACE_EXISTS (scaler, "wl_scaler"); + +#undef VERIFY_INTERFACE_EXISTS + + self->thread = g_thread_try_new ("GstWlDisplay", gst_wl_display_thread_run, + self, &err); + if (err) { + g_propagate_prefixed_error (error, err, + "Failed to start thread for the display's events"); + g_object_unref (self); + return NULL; + } + + return self; +} diff --git a/waylandsink/src/wldisplay.h b/waylandsink/src/wldisplay.h new file mode 100644 index 0000000..f782534 --- /dev/null +++ b/waylandsink/src/wldisplay.h @@ -0,0 +1,100 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_WL_DISPLAY_H__ +#define __GST_WL_DISPLAY_H__ + +#include +#include +#include "scaler-client-protocol.h" +#ifdef GST_WLSINK_ENHANCEMENT +#include +#include +#include +#define NV_BUF_PLANE_NUM 2 /*SN12 or ST12 has 2 plane */ +#endif +G_BEGIN_DECLS +#define GST_TYPE_WL_DISPLAY (gst_wl_display_get_type ()) +#define GST_WL_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_DISPLAY, GstWlDisplay)) +#define GST_IS_WL_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_DISPLAY)) +#define GST_WL_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_DISPLAY, GstWlDisplayClass)) +#define GST_IS_WL_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_DISPLAY)) +#define GST_WL_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_DISPLAY, GstWlDisplayClass)) +#if 1 +#define FUNCTION_ENTER() GST_INFO("") +#else +#define FUNCTION_ENTER() +#endif +typedef struct _GstWlDisplay GstWlDisplay; +typedef struct _GstWlDisplayClass GstWlDisplayClass; + +struct _GstWlDisplay +{ + GObject parent_instance; + + /* public objects */ + struct wl_display *display; + struct wl_event_queue *queue; + + /* globals */ + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_subcompositor *subcompositor; + struct wl_shell *shell; + struct wl_shm *shm; + struct wl_scaler *scaler; + GArray *formats; + + /* private */ + gboolean own_display; + GThread *thread; + GstPoll *wl_fd_poll; + +#ifdef GST_WLSINK_ENHANCEMENT + /*video output layer */ + struct tizen_policy *tizen_policy; + struct tizen_video *tizen_video; + + struct wayland_tbm_client *tbm_client; + tbm_bufmgr tbm_bufmgr; + tbm_bo tbm_bo; + + gboolean is_native_format; /*SN12, ST12 */ + void *bo[NV_BUF_PLANE_NUM]; + int plane_size[NV_BUF_PLANE_NUM]; + int stride_width[NV_BUF_PLANE_NUM]; + int stride_height[NV_BUF_PLANE_NUM]; + int native_video_size; +#endif +}; + +struct _GstWlDisplayClass +{ + GObjectClass parent_class; +}; + +GType gst_wl_display_get_type (void); + +GstWlDisplay *gst_wl_display_new (const gchar * name, GError ** error); +GstWlDisplay *gst_wl_display_new_existing (struct wl_display *display, + gboolean take_ownership, GError ** error); + +G_END_DECLS +#endif /* __GST_WL_DISPLAY_H__ */ diff --git a/waylandsink/src/wlvideoformat.c b/waylandsink/src/wlvideoformat.c new file mode 100644 index 0000000..a0c3ff6 --- /dev/null +++ b/waylandsink/src/wlvideoformat.c @@ -0,0 +1,114 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * Copyright (C) 2012 Wim Taymans + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "wlvideoformat.h" + +GST_DEBUG_CATEGORY_EXTERN (gsttizenwl_debug); +#define GST_CAT_DEFAULT gsttizenwl_debug + +#ifndef GST_WLSINK_ENHANCEMENT + +typedef struct +{ + enum wl_shm_format wl_format; + GstVideoFormat gst_format; +} wl_VideoFormat; + +static const wl_VideoFormat formats[] = { +#if G_BYTE_ORDER == G_BIG_ENDIAN + {WL_SHM_FORMAT_XRGB8888, GST_VIDEO_FORMAT_xRGB}, + {WL_SHM_FORMAT_ARGB8888, GST_VIDEO_FORMAT_ARGB}, + {WL_SHM_FORMAT_XBGR8888, GST_VIDEO_FORMAT_xBGR}, + {WL_SHM_FORMAT_RGBX8888, GST_VIDEO_FORMAT_RGBx}, + {WL_SHM_FORMAT_BGRX8888, GST_VIDEO_FORMAT_BGRx}, + {WL_SHM_FORMAT_ABGR8888, GST_VIDEO_FORMAT_ABGR}, + {WL_SHM_FORMAT_RGBA8888, GST_VIDEO_FORMAT_RGBA}, + {WL_SHM_FORMAT_BGRA8888, GST_VIDEO_FORMAT_BGRA}, +#else + {WL_SHM_FORMAT_XRGB8888, GST_VIDEO_FORMAT_BGRx}, + {WL_SHM_FORMAT_ARGB8888, GST_VIDEO_FORMAT_BGRA}, + {WL_SHM_FORMAT_XBGR8888, GST_VIDEO_FORMAT_RGBx}, + {WL_SHM_FORMAT_RGBX8888, GST_VIDEO_FORMAT_xBGR}, + {WL_SHM_FORMAT_BGRX8888, GST_VIDEO_FORMAT_xRGB}, + {WL_SHM_FORMAT_ABGR8888, GST_VIDEO_FORMAT_RGBA}, + {WL_SHM_FORMAT_RGBA8888, GST_VIDEO_FORMAT_ABGR}, + {WL_SHM_FORMAT_BGRA8888, GST_VIDEO_FORMAT_ARGB}, +#endif + {WL_SHM_FORMAT_RGB888, GST_VIDEO_FORMAT_RGB}, + {WL_SHM_FORMAT_BGR888, GST_VIDEO_FORMAT_BGR}, + {WL_SHM_FORMAT_RGB565, GST_VIDEO_FORMAT_RGB16}, + {WL_SHM_FORMAT_BGR565, GST_VIDEO_FORMAT_BGR16}, + + {WL_SHM_FORMAT_YUYV, GST_VIDEO_FORMAT_YUY2}, + {WL_SHM_FORMAT_YVYU, GST_VIDEO_FORMAT_YVYU}, + {WL_SHM_FORMAT_UYVY, GST_VIDEO_FORMAT_UYVY}, + {WL_SHM_FORMAT_AYUV, GST_VIDEO_FORMAT_AYUV}, + {WL_SHM_FORMAT_NV12, GST_VIDEO_FORMAT_NV12}, + {WL_SHM_FORMAT_NV21, GST_VIDEO_FORMAT_NV21}, + {WL_SHM_FORMAT_NV16, GST_VIDEO_FORMAT_NV16}, + {WL_SHM_FORMAT_YUV410, GST_VIDEO_FORMAT_YUV9}, + {WL_SHM_FORMAT_YVU410, GST_VIDEO_FORMAT_YVU9}, + {WL_SHM_FORMAT_YUV411, GST_VIDEO_FORMAT_Y41B}, + {WL_SHM_FORMAT_YUV420, GST_VIDEO_FORMAT_I420}, + {WL_SHM_FORMAT_YVU420, GST_VIDEO_FORMAT_YV12}, + {WL_SHM_FORMAT_YUV422, GST_VIDEO_FORMAT_Y42B}, + {WL_SHM_FORMAT_YUV444, GST_VIDEO_FORMAT_v308}, +}; + +enum wl_shm_format +gst_video_format_to_wayland_format (GstVideoFormat format) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (formats); i++) + if (formats[i].gst_format == format) + return formats[i].wl_format; + + GST_WARNING ("wayland video format not found"); + return -1; +} + +GstVideoFormat +gst_wayland_format_to_video_format (enum wl_shm_format wl_format) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (formats); i++) + if (formats[i].wl_format == wl_format) + return formats[i].gst_format; + + GST_WARNING ("gst video format not found"); + return GST_VIDEO_FORMAT_UNKNOWN; +} + +const gchar * +gst_wayland_format_to_string (enum wl_shm_format wl_format) +{ + return gst_video_format_to_string + (gst_wayland_format_to_video_format (wl_format)); +} +#endif diff --git a/waylandsink/src/wlvideoformat.h b/waylandsink/src/wlvideoformat.h new file mode 100644 index 0000000..c03525b --- /dev/null +++ b/waylandsink/src/wlvideoformat.h @@ -0,0 +1,42 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * Copyright (C) 2012 Wim Taymans + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_WL_VIDEO_FORMAT_H__ +#define __GST_WL_VIDEO_FORMAT_H__ + +#include +#include + +#ifndef GST_WLSINK_ENHANCEMENT + +G_BEGIN_DECLS + enum wl_shm_format gst_video_format_to_wayland_format (GstVideoFormat + format); +GstVideoFormat gst_wayland_format_to_video_format (enum wl_shm_format + wl_format); + +const gchar *gst_wayland_format_to_string (enum wl_shm_format wl_format); + +G_END_DECLS +#endif +#endif diff --git a/waylandsink/src/wlwindow.c b/waylandsink/src/wlwindow.c new file mode 100644 index 0000000..9258b81 --- /dev/null +++ b/waylandsink/src/wlwindow.c @@ -0,0 +1,484 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011 Sreerenj Balachandran + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "wlwindow.h" +#ifdef GST_WLSINK_ENHANCEMENT +#include "gstwaylandsink.h" +#define SWAP(a, b) { (a) ^= (b) ^= (a) ^= (b); } +#endif + +GST_DEBUG_CATEGORY_EXTERN (gsttizenwl_debug); +#define GST_CAT_DEFAULT gsttizenwl_debug + +G_DEFINE_TYPE (GstWlWindow, gst_wl_window, G_TYPE_OBJECT); + +static void gst_wl_window_finalize (GObject * gobject); + +static void +handle_ping (void *data, struct wl_shell_surface *shell_surface, + uint32_t serial) +{ + FUNCTION_ENTER (); + + wl_shell_surface_pong (shell_surface, serial); +} + +static void +handle_configure (void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height) +{ + FUNCTION_ENTER (); + +} + +static void +handle_popup_done (void *data, struct wl_shell_surface *shell_surface) +{ + FUNCTION_ENTER (); + +} + +static const struct wl_shell_surface_listener shell_surface_listener = { + handle_ping, + handle_configure, + handle_popup_done +}; + +static void +gst_wl_window_class_init (GstWlWindowClass * klass) +{ + FUNCTION_ENTER (); + + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = gst_wl_window_finalize; +} + +static void +gst_wl_window_init (GstWlWindow * self) +{ + FUNCTION_ENTER (); + +} + +static void +gst_wl_window_finalize (GObject * gobject) +{ + FUNCTION_ENTER (); + + GstWlWindow *self = GST_WL_WINDOW (gobject); + + if (self->shell_surface) { + wl_shell_surface_destroy (self->shell_surface); + } + + if (self->subsurface) { + wl_subsurface_destroy (self->subsurface); + } + + wl_viewport_destroy (self->viewport); + wl_surface_destroy (self->surface); + + g_clear_object (&self->display); + + G_OBJECT_CLASS (gst_wl_window_parent_class)->finalize (gobject); +} + +static GstWlWindow * +gst_wl_window_new_internal (GstWlDisplay * display, struct wl_surface *surface) +{ + FUNCTION_ENTER (); + + GstWlWindow *window; + struct wl_region *region; + + g_return_val_if_fail (surface != NULL, NULL); + + window = g_object_new (GST_TYPE_WL_WINDOW, NULL); + window->display = g_object_ref (display); + window->surface = surface; + + /* make sure the surface runs on our local queue */ + wl_proxy_set_queue ((struct wl_proxy *) surface, display->queue); + + window->viewport = wl_scaler_get_viewport (display->scaler, window->surface); + + /* do not accept input */ + region = wl_compositor_create_region (display->compositor); + wl_surface_set_input_region (surface, region); + wl_region_destroy (region); + + return window; +} + +GstWlWindow * +gst_wl_window_new_toplevel (GstWlDisplay * display, GstVideoInfo * video_info) +{ + FUNCTION_ENTER (); + + GstWlWindow *window; + + window = gst_wl_window_new_internal (display, + wl_compositor_create_surface (display->compositor)); + + gst_wl_window_set_video_info (window, video_info); + gst_wl_window_set_render_rectangle (window, 0, 0, window->video_width, + window->video_height); + + window->shell_surface = wl_shell_get_shell_surface (display->shell, + window->surface); + + if (window->shell_surface) { + wl_shell_surface_add_listener (window->shell_surface, + &shell_surface_listener, window); + wl_shell_surface_set_toplevel (window->shell_surface); + } else { + GST_ERROR ("Unable to get wl_shell_surface"); + + g_object_unref (window); + return NULL; + } + + return window; +} + +GstWlWindow * +gst_wl_window_new_in_surface (GstWlDisplay * display, + struct wl_surface * parent) +{ + FUNCTION_ENTER (); + + GstWlWindow *window; + + window = gst_wl_window_new_internal (display, + wl_compositor_create_surface (display->compositor)); + + window->subsurface = wl_subcompositor_get_subsurface (display->subcompositor, + window->surface, parent); + wl_subsurface_set_desync (window->subsurface); +#ifdef GST_WLSINK_ENHANCEMENT + if (display->tizen_policy) + tizen_policy_place_subsurface_below_parent (display->tizen_policy, + window->subsurface); + + wl_surface_commit (parent); +#endif + return window; +} + +GstWlDisplay * +gst_wl_window_get_display (GstWlWindow * window) +{ + FUNCTION_ENTER (); + + g_return_val_if_fail (window != NULL, NULL); + + return g_object_ref (window->display); +} + +struct wl_surface * +gst_wl_window_get_wl_surface (GstWlWindow * window) +{ + FUNCTION_ENTER (); + + g_return_val_if_fail (window != NULL, NULL); + + return window->surface; +} + +gboolean +gst_wl_window_is_toplevel (GstWlWindow * window) +{ + FUNCTION_ENTER (); + + g_return_val_if_fail (window != NULL, FALSE); + + return (window->shell_surface != NULL); +} + +static void +gst_wl_window_resize_internal (GstWlWindow * window, gboolean commit) +{ + FUNCTION_ENTER (); + + GstVideoRectangle src = { 0, }; + GstVideoRectangle res; //dst + + src.w = window->video_width; + src.h = window->video_height; +#ifdef GST_WLSINK_ENHANCEMENT // need to change ifndef to ifdef + + GstVideoRectangle src_origin = { 0, 0, 0, 0 }; + GstVideoRectangle src_input = { 0, 0, 0, 0 }; + GstVideoRectangle dst = { 0, 0, 0, 0 }; + + gint rotate = 0; + gint transform = WL_OUTPUT_TRANSFORM_NORMAL; + + src.x = src.y = 0; + src_input.w = src_origin.w = window->video_width; + src_input.h = src_origin.h = window->video_height; + GST_INFO ("video (%d x %d)", window->video_width, window->video_height); + GST_INFO ("src_input(%d, %d, %d x %d)", src_input.x, src_input.y, src_input.w, + src_input.h); + GST_INFO ("src_origin(%d, %d, %d x %d)", src_origin.x, src_origin.y, + src_origin.w, src_origin.h); + + if (window->rotate_angle == DEGREE_0 || window->rotate_angle == DEGREE_180) { + src.w = window->video_width; //video_width + src.h = window->video_height; //video_height + } else { + src.w = window->video_height; + src.h = window->video_width; + } + GST_INFO ("src(%d, %d, %d x %d)", src.x, src.y, src.w, src.h); + + /*default res.w and res.h */ + dst.w = window->render_rectangle.w; + dst.h = window->render_rectangle.h; + GST_INFO ("dst(%d,%d,%d x %d)", dst.x, dst.y, dst.w, dst.h); + GST_INFO ("window->render_rectangle(%d,%d,%d x %d)", + window->render_rectangle.x, window->render_rectangle.y, + window->render_rectangle.w, window->render_rectangle.h); + switch (window->disp_geo_method) { + case DISP_GEO_METHOD_LETTER_BOX: + GST_INFO ("DISP_GEO_METHOD_LETTER_BOX"); + gst_video_sink_center_rect (src, dst, &res, TRUE); + gst_video_sink_center_rect (dst, src, &src_input, FALSE); + res.x += window->render_rectangle.x; + res.y += window->render_rectangle.y; + break; + case DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX: + if (src.w > dst.w || src.h > dst.h) { + /*LETTER BOX */ + GST_INFO + ("DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX -> set LETTER BOX"); + gst_video_sink_center_rect (src, dst, &res, TRUE); + gst_video_sink_center_rect (dst, src, &src_input, FALSE); + res.x += window->render_rectangle.x; + res.y += window->render_rectangle.y; + } else { + /*ORIGIN SIZE */ + GST_INFO ("DISP_GEO_METHOD_ORIGIN_SIZE"); + gst_video_sink_center_rect (src, dst, &res, FALSE); + gst_video_sink_center_rect (dst, src, &src_input, FALSE); + } + break; + case DISP_GEO_METHOD_ORIGIN_SIZE: //is working + GST_INFO ("DISP_GEO_METHOD_ORIGIN_SIZE"); + gst_video_sink_center_rect (src, dst, &res, FALSE); + gst_video_sink_center_rect (dst, src, &src_input, FALSE); + break; + case DISP_GEO_METHOD_FULL_SCREEN: //is working + GST_INFO ("DISP_GEO_METHOD_FULL_SCREEN"); + res.x = res.y = 0; + res.w = window->render_rectangle.w; + res.h = window->render_rectangle.h; + break; + case DISP_GEO_METHOD_CROPPED_FULL_SCREEN: + GST_INFO ("DISP_GEO_METHOD_CROPPED_FULL_SCREEN"); + gst_video_sink_center_rect (src, dst, &res, FALSE); + gst_video_sink_center_rect (dst, src, &src_input, FALSE); + res.x = res.y = 0; + res.w = dst.w; + res.h = dst.h; + break; + default: + break; + } + + switch (window->rotate_angle) { + case DEGREE_0: + transform = WL_OUTPUT_TRANSFORM_NORMAL; + break; + case DEGREE_90: + transform = WL_OUTPUT_TRANSFORM_90; + break; + case DEGREE_180: + transform = WL_OUTPUT_TRANSFORM_180; + break; + case DEGREE_270: + transform = WL_OUTPUT_TRANSFORM_270; + break; + + default: + GST_ERROR ("Unsupported rotation [%d]... set DEGREE 0.", + window->rotate_angle); + break; + } + + switch (window->flip) { + case FLIP_NONE: + break; + case FLIP_VERTICAL: + transform = WL_OUTPUT_TRANSFORM_FLIPPED; + break; + case FLIP_HORIZONTAL: + transform = WL_OUTPUT_TRANSFORM_FLIPPED_180; + break; + case FLIP_BOTH: + transform = WL_OUTPUT_TRANSFORM_180; + break; + default: + GST_ERROR ("Unsupported flip [%d]... set FLIP_NONE.", window->flip); + } + + GST_INFO + ("window[%d x %d] src[%d,%d,%d x %d],dst[%d,%d,%d x %d],input[%d,%d,%d x %d],result[%d,%d,%d x %d]", + window->render_rectangle.w, window->render_rectangle.h, + src.x, src.y, src.w, src.h, + dst.x, dst.y, dst.w, dst.h, + src_input.x, src_input.y, src_input.w, src_input.h, + res.x, res.y, res.w, res.h); + + GST_INFO ("video (%d x %d)", window->video_width, window->video_height); + GST_INFO ("src_input(%d, %d, %d x %d)", src_input.x, src_input.y, src_input.w, + src_input.h); + GST_INFO ("src_origin(%d, %d, %d x %d)", src_origin.x, src_origin.y, + src_origin.w, src_origin.h); + GST_INFO ("src(%d, %d, %d x %d)", src.x, src.y, src.w, src.h); + GST_INFO ("dst(%d,%d,%d x %d)", dst.x, dst.y, dst.w, dst.h); + GST_INFO ("window->render_rectangle(%d,%d,%d x %d)", + window->render_rectangle.x, window->render_rectangle.y, + window->render_rectangle.w, window->render_rectangle.h); + GST_INFO ("res(%d, %d, %d x %d)", res.x, res.y, res.w, res.h); + + if (window->subsurface) { + GST_INFO ("have window->subsurface"); + wl_subsurface_set_position (window->subsurface, + window->render_rectangle.x + res.x, window->render_rectangle.y + res.y); + GST_INFO ("wl_subsurface_set_position(%d,%d)", + window->render_rectangle.x + res.x, window->render_rectangle.y + res.y); + } + wl_viewport_set_destination (window->viewport, res.w, res.h); + GST_INFO ("wl_viewport_set_destination(%d,%d)", res.w, res.h); + + wl_viewport_set_source (window->viewport, wl_fixed_from_int (src_input.x), + wl_fixed_from_int (src_input.y), wl_fixed_from_int (src_input.w), + wl_fixed_from_int (src_input.h)); + GST_INFO ("wl_viewport_set_source(%d,%d, %d x %d)", src_input.x, src_input.y, + src_input.w, src_input.h); + + wl_surface_set_buffer_transform (window->surface, transform); + GST_INFO ("wl_surface_set_buffer_transform (%d)", transform); + + if (commit) { + wl_surface_damage (window->surface, 0, 0, res.w, res.h); + wl_surface_commit (window->surface); + } + + /* this is saved for use in wl_surface_damage */ + window->surface_width = res.w; + window->surface_height = res.h; + +#else + gst_video_sink_center_rect (src, window->render_rectangle, &res, TRUE); + if (window->subsurface) + wl_subsurface_set_position (window->subsurface, + window->render_rectangle.x + res.x, window->render_rectangle.y + res.y); + + wl_viewport_set_destination (window->viewport, res.w, res.h); + + if (commit) { + wl_surface_damage (window->surface, 0, 0, res.w, res.h); + wl_surface_commit (window->surface); + } + + /* this is saved for use in wl_surface_damage */ + window->surface_width = res.w; + window->surface_height = res.h; +#endif +} + +void +gst_wl_window_set_video_info (GstWlWindow * window, GstVideoInfo * info) +{ + FUNCTION_ENTER (); + + g_return_if_fail (window != NULL); + + window->video_width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + window->video_height = info->height; + + if (window->render_rectangle.w != 0) + gst_wl_window_resize_internal (window, FALSE); +} + +void +gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, + gint w, gint h) +{ + FUNCTION_ENTER (); + + g_return_if_fail (window != NULL); + + window->render_rectangle.x = x; + window->render_rectangle.y = y; + window->render_rectangle.w = w; + window->render_rectangle.h = h; + + if (window->video_width != 0) + gst_wl_window_resize_internal (window, TRUE); +} + +#ifdef GST_WLSINK_ENHANCEMENT +void +gst_wl_window_set_rotate_angle (GstWlWindow * window, guint rotate_angle) +{ + FUNCTION_ENTER (); + g_return_if_fail (window != NULL); + window->rotate_angle = rotate_angle; + GST_INFO ("rotate_angle value is (%d)", window->rotate_angle); + +} + +void +gst_wl_window_set_disp_geo_method (GstWlWindow * window, guint disp_geo_method) +{ + FUNCTION_ENTER (); + g_return_if_fail (window != NULL); + window->disp_geo_method = disp_geo_method; + GST_INFO ("disp_geo_method value is (%d)", window->disp_geo_method); +} + +void +gst_wl_window_set_orientation (GstWlWindow * window, guint orientation) +{ + FUNCTION_ENTER (); + g_return_if_fail (window != NULL); + window->orientation = orientation; + GST_INFO ("orientation value is (%d)", window->orientation); +} + +void +gst_wl_window_set_flip (GstWlWindow * window, guint flip) +{ + FUNCTION_ENTER (); + g_return_if_fail (window != NULL); + window->flip = flip; + GST_INFO ("flip value is (%d)", window->flip); +} +#endif diff --git a/waylandsink/src/wlwindow.h b/waylandsink/src/wlwindow.h new file mode 100644 index 0000000..3dc3cda --- /dev/null +++ b/waylandsink/src/wlwindow.h @@ -0,0 +1,98 @@ +/* GStreamer Wayland video sink + * + * Copyright (C) 2014 Collabora Ltd. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef __GST_WL_WINDOW_H__ +#define __GST_WL_WINDOW_H__ + +#include "wldisplay.h" +#include + +G_BEGIN_DECLS +#define GST_TYPE_WL_WINDOW (gst_wl_window_get_type ()) +#define GST_WL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_WINDOW, GstWlWindow)) +#define GST_IS_WL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_WINDOW)) +#define GST_WL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_WINDOW, GstWlWindowClass)) +#define GST_IS_WL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_WINDOW)) +#define GST_WL_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_WINDOW, GstWlWindowClass)) +#if 1 +#define FUNCTION_ENTER() GST_INFO("") +#else +#define FUNCTION_ENTER() +#endif +typedef struct _GstWlWindow GstWlWindow; +typedef struct _GstWlWindowClass GstWlWindowClass; + +struct _GstWlWindow +{ + GObject parent_instance; + + GstWlDisplay *display; + struct wl_surface *surface; + struct wl_subsurface *subsurface; + struct wl_viewport *viewport; + struct wl_shell_surface *shell_surface; + + /* the size of the destination area where we are overlaying our subsurface */ + GstVideoRectangle render_rectangle; + /* the size of the video in the buffers */ + gint video_width, video_height; + /* the size of the (sub)surface */ + gint surface_width, surface_height; +#ifdef GST_WLSINK_ENHANCEMENT + /*Display geometry method */ + guint disp_geo_method; + guint rotate_angle; + guint orientation; + guint flip; +#endif +}; + +struct _GstWlWindowClass +{ + GObjectClass parent_class; +}; + +GType gst_wl_window_get_type (void); + +GstWlWindow *gst_wl_window_new_toplevel (GstWlDisplay * display, + GstVideoInfo * video_info); +GstWlWindow *gst_wl_window_new_in_surface (GstWlDisplay * display, + struct wl_surface *parent); + +GstWlDisplay *gst_wl_window_get_display (GstWlWindow * window); +struct wl_surface *gst_wl_window_get_wl_surface (GstWlWindow * window); +gboolean gst_wl_window_is_toplevel (GstWlWindow * window); + +/* functions to manipulate the size on non-toplevel windows */ +void gst_wl_window_set_video_info (GstWlWindow * window, GstVideoInfo * info); +void gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, + gint w, gint h); + +#ifdef GST_WLSINK_ENHANCEMENT +void gst_wl_window_set_rotate_angle (GstWlWindow * window, guint rotate_angle); +void gst_wl_window_set_disp_geo_method (GstWlWindow * window, + guint disp_geo_method); +void gst_wl_window_set_orientation (GstWlWindow * window, guint orientation); +void gst_wl_window_set_flip (GstWlWindow * window, guint flip); +#endif + + +G_END_DECLS +#endif /* __GST_WL_WINDOW_H__ */ -- 2.7.4