From: Hyunil Date: Thu, 9 Nov 2017 05:51:36 +0000 (+0900) Subject: tizenwlsink: get source from gst-plugins-bad/ext/wayland and create new tizen wayland... X-Git-Tag: submit/tizen/20171110.011647^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F66%2F159466%2F3;p=platform%2Fupstream%2Fgst-plugins-tizen.git tizenwlsink: get source from gst-plugins-bad/ext/wayland and create new tizen wayland video sink Change-Id: Id60188662450ac0c142b19ebff59df08fd4b9423 Signed-off-by: Hyunil --- diff --git a/Makefile.am b/Makefile.am index ffad5e7..7dfb351 100755 --- a/Makefile.am +++ b/Makefile.am @@ -61,6 +61,10 @@ if GST_TIZEN_USE_VIDEO360 SUBDIRS += video360 endif +if GST_TIZEN_USE_WAYLANDSINK +SUBDIRS += tizenwlsink +endif + DIST_SUBDIRS = common if GST_TIZEN_USE_ENCODEBIN @@ -104,6 +108,10 @@ if GST_TIZEN_USE_VIDEO360 DIST_SUBDIRS += video360 endif +if GST_TIZEN_USE_WAYLANDSINK +DIST_SUBDIRS += tizenwlsink +endif + EXTRA_DIST = \ gstreamer.spec gstreamer.spec.in \ configure.ac autogen.sh depcomp \ diff --git a/configure.ac b/configure.ac old mode 100755 new mode 100644 index 2f59780..a385065 --- a/configure.ac +++ b/configure.ac @@ -386,6 +386,24 @@ AS_IF([test "x$with_gles2" = "xyes"], [ CFLAGS="$CFLAGS -DGST_TIZEN_GL_API_GLES2" ]) +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(WAYLAND, wayland-client >= 1.4.0 wayland-tbm-client tizen-extension-client wayland-scanner) +AC_PATH_PROG([wayland_scanner], [wayland-scanner]) +AC_SUBST(WAYLAND_CFLAGS) +AC_SUBST(WAYLAND_LIBS) +fi + AC_OUTPUT( Makefile common/Makefile @@ -415,4 +433,6 @@ wfdtizenmanager/Makefile alfec/Makefile video360/Makefile video360/src/Makefile +tizenwlsink/Makefile +tizenwlsink/src/Makefile ) diff --git a/packaging/gst-plugins-tizen.spec b/packaging/gst-plugins-tizen.spec index b9ed39d..30bd6c5 100644 --- a/packaging/gst-plugins-tizen.spec +++ b/packaging/gst-plugins-tizen.spec @@ -9,7 +9,7 @@ Name: gst-plugins-tizen Version: 1.0.0 Summary: GStreamer tizen plugins (common) -Release: 49 +Release: 50 Group: Multimedia/Framework Url: http://gstreamer.freedesktop.org/ License: LGPL-2.1+ @@ -42,7 +42,6 @@ BuildRequires: pkgconfig(gles20) 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 @@ -67,7 +66,7 @@ GStreamer tizen plugins Extension for mobile TM1 %build -export CFLAGS+=" -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" " +export CFLAGS+=" -DTIZEN_FEATURE_WLSINK_ENHANCEMENT -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" " export CFLAGS_DEFAULT="$CFLAGS" %ifarch %{arm} diff --git a/tizenwlsink/Makefile.am b/tizenwlsink/Makefile.am new file mode 100644 index 0000000..af437a6 --- /dev/null +++ b/tizenwlsink/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/tizenwlsink/src/.gitignore b/tizenwlsink/src/.gitignore new file mode 100644 index 0000000..44cd427 --- /dev/null +++ b/tizenwlsink/src/.gitignore @@ -0,0 +1,2 @@ +*-protocol.c +*-client-protocol.h diff --git a/tizenwlsink/src/Makefile.am b/tizenwlsink/src/Makefile.am new file mode 100644 index 0000000..998787f --- /dev/null +++ b/tizenwlsink/src/Makefile.am @@ -0,0 +1,48 @@ +plugin_LTLIBRARIES = libgsttizenwlsink.la + +libgsttizenwlsink_la_SOURCES = \ + gstwaylandsink.c \ + wlshmallocator.c \ + wlbuffer.c \ + wldisplay.c \ + wlwindow.c \ + scaler-protocol.c \ + wlvideoformat.c \ + tizen-wlvideoformat.c + +libgsttizenwlsink_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)$ $(GST_PLUGINS_BASE_CFLAGS) $(GST_VIDEO_CFLAGS) \ + $(WAYLAND_CFLAGS) $(DRM_CFLAGS) $(TBM_CFLAGS) $(MMCOMMON_CFLAGS) +libgsttizenwlsink_la_LIBADD = \ + $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_VIDEO_LIBS)\ + $(WAYLAND_LIBS) $(DRM_LIBS) $(TBM_LIBS) $(MMCOMMON_LIBS) +libgsttizenwlsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgsttizenwlsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = \ + gstwaylandsink.h \ + wlshmallocator.h \ + wlbuffer.h \ + wldisplay.h \ + wlwindow.h \ + scaler-client-protocol.h \ + wlvideoformat.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 + +wlshmallocator.c: scaler-client-protocol.h + +wlbuffer.c: scaler-client-protocol.h + +wldisplay.c: scaler-client-protocol.h + +wlwindow.c: scaler-client-protocol.h diff --git a/tizenwlsink/src/gstwaylandsink.c b/tizenwlsink/src/gstwaylandsink.c new file mode 100755 index 0000000..4a33e8c --- /dev/null +++ b/tizenwlsink/src/gstwaylandsink.c @@ -0,0 +1,2713 @@ +/* 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 TIZEN_FEATURE_WLSINK_ENHANCEMENT +#include +#include "tizen-wlvideoformat.h" +#endif +#include "wlvideoformat.h" +#include "wlbuffer.h" +#include "wlshmallocator.h" + +#include + + +#include +#include +#include + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +#define GST_APP_EVENT_FLUSH_BUFFER_NAME "application/flush-buffer" + +#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, "Specially described destination ROI", "DISP_GEO_METHOD_CUSTOM_ROI"}, + {6, 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, +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + SIGNAL_HANDOFF, + SIGNAL_PREROLL_HANDOFF, +#endif + LAST_SIGNAL +}; + +/* Properties */ +enum +{ + PROP_0, + PROP_DISPLAY, +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + PROP_DUMP_VIDEO, + PROP_DISABLE_OVERLAY, + PROP_DUMP_COUNT, + PROP_SIGNAL_HANDOFFS, + PROP_USE_GAPLESS, + PROP_KEEP_CAMERA_PREVIEW, + PROP_USE_TBM, + PROP_ROTATE_ANGLE, + PROP_DISPLAY_GEOMETRY_METHOD, + PROP_FLIP, + PROP_VISIBLE, + PROP_FOLLOW_PARENT_TRANSFORM, + PROP_CROP_X, + PROP_CROP_Y, + PROP_CROP_WIDTH, + PROP_CROP_HEIGHT, + PROP_RATIO_WIDTH, + PROP_RATIO_HEIGHT, + PROP_SCALE_WIDTH, + PROP_SCALE_HEIGHT, + PROP_OFFSET_X, + PROP_OFFSET_Y, + PROP_OFFSET_WIDTH, + PROP_OFFSET_HEIGHT, + PROP_ALIGN_WIDTH, + PROP_ALIGN_HEIGHT +#endif +}; + +GST_DEBUG_CATEGORY (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_debug + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE + ("{BGRx, BGRA, I420, NV12, NV12MT, NV21, SN12, ST12, SN21, SR32, S420}"))); +#else + 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, " + "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"))); +#endif + +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 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_wl_window_wl_surface_id (GstVideoOverlay * overlay, + guintptr wl_surface_id); +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 */ +#if 0 +static void gst_wayland_sink_set_context (GstElement * element, + GstContext * context); +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); +#endif +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +static gboolean gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event); +static void gst_wayland_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end); +static void gst_wayland_sink_update_window_geometry (GstWaylandSink * sink); +static void render_last_buffer (GstWaylandSink * sink); + +static guint gst_waylandsink_signals[LAST_SIGNAL] = { 0 }; + +#endif +#define gst_wayland_sink_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK, + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, + gst_wayland_sink_videooverlay_init) +#if 0 + G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO, + gst_wayland_sink_waylandvideo_init) +#endif +); + +static void +gst_wayland_sink_class_init (GstWaylandSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + FUNCTION; + + 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", + "Hyunil Park , " + "Sreerenj Balachandran , " + "George Kiagiadakis "); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state); +#if 0 + gstelement_class->set_context = + GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context); +#endif + 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); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_wayland_sink_event); + gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_times); +#endif + + 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 TIZEN_FEATURE_WLSINK_ENHANCEMENT + g_object_class_install_property (gobject_class, PROP_DUMP_VIDEO, + g_param_spec_boolean ("dump-video", "dump raw video buffer", + "Dump raw video buffer", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_DUMP_COUNT, + g_param_spec_uint ("dump-video-count", "dump video count", + "Dump video count", 1, G_MAXUINT, DEFAULT_DUMP_COUNT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_DISABLE_OVERLAY, + g_param_spec_boolean ("disable-overlay", "disable overlay", + "Stop using overlay by destroying wl_window and wl_display, " + "Use gst_video_overlay_set_wl_window_wl_surface_id before setting FALSE " + "to use overlay", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_KEEP_CAMERA_PREVIEW, + g_param_spec_boolean ("keep-camera-preview", "use flush buffer mechanism", + "Last tbm buffer is copied and returned to camerasrc immediately " + "when state change(PAUSED_TO_READY)", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_USE_TBM, + g_param_spec_boolean ("use-tbm", "use tbm buffer", + "Use Tizen Buffer Memory insted of Shared memory, " + "Memory is alloced by TBM insted of SHM when enabled", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + 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_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)); + + g_object_class_install_property (gobject_class, PROP_VISIBLE, + g_param_spec_boolean ("visible", "Visible", + "Draws screen or blacks out, true means visible, false blacks out", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +#ifdef ENABLE_FUNCTION + g_object_class_install_property (gobject_class, PROP_SCALE_WIDTH, + g_param_spec_double ("scale-w", "ratio width", + "scale width for rendering video," + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0.0, + G_MAXDOUBLE, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_SCALE_HEIGHT, + g_param_spec_double ("scale-h", "scale height", + "scale width for rendering video, " + "Function is not support in DISP_GEO_METHOD_CUSTOM_ROI. ", 0.0, + G_MAXDOUBLE, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FOLLOW_PARENT_TRANSFORM, + g_param_spec_boolean ("follow-parent-transform", + "follow parent transform", + "Video is rotated automatically without setting rotate property by rotating Display" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CROP_X, + g_param_spec_uint ("crop-x", "crop x", + "x-coordinate for cropping video. " + "Please set crop-x, crop-y, crop-w and crop-h togethrer. " + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CROP_Y, + g_param_spec_uint ("crop-y", "crop y", + "y-coordinate for cropping video. " + "Please set crop-x, crop-y, crop-w and crop-h togethrer. " + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CROP_WIDTH, + g_param_spec_uint ("crop-w", "crop width", + "width for cropping video. " + "If value is not set or is set 0, Width is set to video width after set_caps. " + "Please set crop-x, crop-y, crop-w and crop-h togethrer. " + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CROP_HEIGHT, + g_param_spec_uint ("crop-h", "crop height", + "height for cropping video. " + "If value is not set or is set 0, Hight is set to video height after set_caps. " + "Please set crop-x, crop-y, crop-w and crop-h togethrer. " + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_RATIO_WIDTH, + g_param_spec_double ("ratio-w", "ratio width", + "ratio width for rendering video," + "If value is set, Original video ratio is ignored. to restore original size, set to -1" + "Please set ratio-w and ratio-h togethrer. " + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", -1.0, + G_MAXDOUBLE, -1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_RATIO_HEIGHT, + g_param_spec_double ("ratio-h", "ratio height", + "ratio width for rendering video, " + "If value is set, Original video ratio is ignored. to restore original size, set to -1" + "Please set ratio-w and ratio-h togethrer. " + "Function is not support in DISP_GEO_METHOD_CUSTOM_ROI. ", -1.0, + G_MAXDOUBLE, -1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_OFFSET_X, + g_param_spec_uint ("offset-x", "offset x", + "x offset for moving x-coordinate of video pixel, " + "Please set x, y, w and h offset togethrer" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_OFFSET_Y, + g_param_spec_uint ("offset-y", "offset y", + "y offset for moving y-coordinate of video pixel, " + "Please set x, y, w and h offset togethrer" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_OFFSET_WIDTH, + g_param_spec_uint ("offset-w", "offset width", + "width offset for adjusting width of of video pixel, " + "Please set x, y, w and h offset togethrer" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_OFFSET_HEIGHT, + g_param_spec_uint ("offset-h", "offset height", + "height offset for adjusting height of of video pixel" + "Please set x, y, w and h offset togethrer" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0, + G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ALIGN_WIDTH, + g_param_spec_double ("align-w", "align width", + "Align with, Left: 0.0, Middle: 0.5, Right: 1.0, " + "Please set align-w and align-h togethrer" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0.0, + 1.0, 0.5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ALIGN_HEIGHT, + g_param_spec_double ("align-h", "align height", + "Align height, Left: 0.0, Middle: 0.5, Right: 1.0, " + "Please set align-w and align-h togethrer" + "Function is not supported in DISP_GEO_METHOD_CUSTOM_ROI. ", 0.0, + 1.0, 0.5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#endif + + /* fakesink function for stream callback of MSL with browser */ + g_object_class_install_property (gobject_class, PROP_SIGNAL_HANDOFFS, + g_param_spec_boolean ("signal-handoffs", "Signal handoffs", + "Send a signal before unreffing the buffer", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_waylandsink_signals[SIGNAL_HANDOFF] = + g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstWaylandSinkClass, handoff), NULL, NULL, + g_cclosure_marshal_generic, G_TYPE_NONE, 2, + GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD); + + gst_waylandsink_signals[SIGNAL_PREROLL_HANDOFF] = + g_signal_new ("preroll-handoff", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstWaylandSinkClass, preroll_handoff), + NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, + GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD); + +#endif +} + +static void +gst_wayland_sink_dump_raw_video (GstWaylandSink * sink, GstBuffer * buffer, + guint dump_count, guint dump_total) +{ + GstMemory *mem; + GstMapInfo mem_info = GST_MAP_INFO_INIT; + gpointer data; + gint ret, size; + gchar file_name[128]; + + g_return_if_fail (sink != NULL); + g_return_if_fail (buffer != NULL); + + if (dump_count > dump_total) { + sink->dump_video = FALSE; + return; + } + + size = GST_VIDEO_INFO_SIZE (&sink->video_info); + mem = gst_buffer_peek_memory (buffer, 0); + gst_memory_map (mem, &mem_info, GST_MAP_READ); + + data = mem_info.data; + snprintf (file_name, sizeof (file_name), "/tmp/WLSINK_OUT_DUMP_%2.2d.dump", + dump_count); + ret = gst_wl_fwrite_data (file_name, data, size); + if (ret) { + GST_ERROR ("_write_rawdata() failed"); + } + + GST_LOG ("DUMP IMAGE %d, size (%d)", dump_count, size); + gst_memory_unmap (mem, &mem_info); +} + +static void +gst_wayland_sink_init (GstWaylandSink * sink) +{ + FUNCTION; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + sink->dump_video = FALSE; + sink->total_dump = DEFAULT_DUMP_COUNT; + sink->disable_overlay = FALSE; + sink->signal_handoffs = FALSE; + sink->request_camera_flush_buf = FALSE; + sink->keep_camera_preview = FALSE; + sink->got_costum_event = FALSE; + sink->USE_TBM = TRUE; + sink->is_native_format = FALSE; + sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD; + sink->flip = DEF_DISPLAY_FLIP; + sink->rotate_angle = DEGREE_0; + sink->visible = TRUE; + sink->crop_x = sink->crop_y = sink->crop_w = sink->crop_h = 0; +#ifdef ENABLE_FUNCTION + sink->follow_parent_transform = FALSE; + sink->ratio_w = sink->ratio_h = -1.0; //need to set -1.0 for original video ratio + sink->align_w = sink->align_h = 0.5; + sink->scale_w = sink->scale_h = 1.0; + sink->offset_x = sink->offset_y = sink->offset_w = sink->offset_h = 0; +#endif +#endif + g_mutex_init (&sink->display_lock); + g_mutex_init (&sink->render_lock); +} + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +static void +gst_wayland_sink_recover_display_window_info (GstWaylandSink * sink) +{ + GstWlShmAllocator *self = NULL; + FUNCTION; + g_return_if_fail (sink != NULL); + g_return_if_fail (sink->display != NULL); + g_return_if_fail (sink->window != NULL); + + sink->display->USE_TBM = sink->USE_TBM; + sink->display->is_native_format = sink->is_native_format; + self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ()); + self->display = sink->display; + + gst_wayland_sink_update_window_geometry (sink); + sink->video_info_changed = TRUE; +} + +static gboolean +gst_wayland_sink_is_disabled_overlay (GstWaylandSink * sink) +{ + g_return_val_if_fail (sink != NULL, FALSE); + //Ensure display is null and the display info has been copied by unsetting disable_overlay + //If using the toplevel window, window is null + if (!sink->disable_overlay && sink->display) { + return FALSE; + } + sink->redraw_pending = FALSE; + return TRUE; +} + +static void +gst_wayland_sink_stop_video (GstWaylandSink * sink) +{ + FUNCTION; + g_return_if_fail (sink != NULL); + gst_wl_window_render (sink->window, NULL, NULL); +} + +static int +gst_wayland_sink_is_gapless (GstWaylandSink * sink) +{ + g_return_val_if_fail (sink != NULL, FALSE); + g_return_val_if_fail (sink->display != NULL, FALSE); + + if (sink->got_costum_event && sink->USE_TBM + && sink->display->is_native_format) + return TRUE; + + return FALSE; +} + +static int +gst_wayland_sink_need_flush_buffer (GstWaylandSink * sink) +{ + g_return_val_if_fail (sink != NULL, FALSE); + g_return_val_if_fail (sink->display != NULL, FALSE); + + if ((gst_wayland_sink_is_gapless (sink)) + || sink->request_camera_flush_buf || sink->display->flush_request == 1) { + sink->display->flush_request = TRUE; + return TRUE; + } + return FALSE; +} + +static void +gst_wayland_sink_update_last_buffer_geometry (GstWaylandSink * sink) +{ + GstWlBuffer *wlbuffer; + gboolean no_render_buffer = FALSE; + FUNCTION; + g_return_if_fail (sink != NULL); + g_return_if_fail (sink->last_buffer != NULL); + wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer); + + g_return_if_fail (wlbuffer != NULL); + + if (wlbuffer->used_by_compositor) { + /* used last buffer by compositor don't receive buffer-release-event when attach */ + wlbuffer->used_by_compositor = FALSE; + } else { + /* unused last buffer by compositor will receive buffer release event when attach */ + no_render_buffer = TRUE; + } + + GST_LOG ("gstbuffer(%p) ref_count(%d)", sink->last_buffer, + GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer)); + + if (sink->visible) { + /* need to render last buffer, reuse current GstWlBuffer */ + render_last_buffer (sink); + + /* ref count is incresed in gst_wl_buffer_attach() of render_last_buffer(), + to call gst_wl_buffer_finalize(), we need to decrease buffer ref count. + wayland server can not release buffer if we attach same buffer and + videosink can not receive buffer-release-event. if we use no visible, + we need to attach null buffer and videosink can receive buffer-release-event */ + + /* need to decrease buffer ref_count, if no_render_buffer is TRUE, + buffer is attached firstly so, videosink can receive buffer-release-event */ + if (no_render_buffer) { + GST_LOG ("skip unref.. will get buffer-release-event"); + } else { + gst_buffer_unref (wlbuffer->gstbuffer); + } + + } else { + GST_LOG ("skip rendering"); + } + + GST_LOG ("gstbuffer(%p) ref_count(%d)", sink->last_buffer, + GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer)); + +} + +#ifdef USE_WL_FLUSH_BUFFER +static int +gst_wayland_sink_make_flush_buffer (GstWlDisplay * display, + MMVideoBuffer * mm_video_buf) +{ + GstWlFlushBuffer *flush_buffer = NULL; + tbm_bo bo = NULL; + int bo_size = 0; + int i; + FUNCTION; + + g_return_val_if_fail (display != NULL, FALSE); + g_return_val_if_fail (mm_video_buf != NULL, FALSE); + + flush_buffer = (GstWlFlushBuffer *) malloc (sizeof (GstWlFlushBuffer)); + if (G_UNLIKELY (!flush_buffer)) { + GST_ERROR ("GstWlFlushBuffer alloc faile"); + return FALSE; + } + memset (flush_buffer, 0x0, sizeof (GstWlFlushBuffer)); + + display->flush_tbm_bufmgr = + wayland_tbm_client_get_bufmgr (display->tbm_client); + g_return_val_if_fail (display->flush_tbm_bufmgr != NULL, FALSE); + + for (i = 0; i < display->tbm_bo_num; i++) { + if (mm_video_buf->handle.bo[i] != NULL) { + tbm_bo_handle src; + tbm_bo_handle dst; + gchar err_str[256]; + + /* get bo size */ + bo_size = tbm_bo_size (mm_video_buf->handle.bo[i]); + GST_LOG ("tbm bo size: %d", bo_size); + /* alloc bo */ + bo = tbm_bo_alloc (display->flush_tbm_bufmgr, bo_size, TBM_DEVICE_CPU); + if (G_UNLIKELY (!bo)) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR ("alloc tbm bo(size:%d) failed: %s(%d)", bo_size, err_str, + errno); + return FALSE; + } + GST_LOG ("flush buffer tbm_bo =(%p)", bo); + flush_buffer->bo[i] = bo; + /* get virtual address */ + src.ptr = dst.ptr = NULL; + /* bo map, we can use tbm_bo_map too. */ + src = + tbm_bo_map (mm_video_buf->handle.bo[i], TBM_DEVICE_CPU, + TBM_OPTION_READ); + dst = tbm_bo_map (bo, TBM_DEVICE_CPU, TBM_OPTION_READ | TBM_OPTION_WRITE); + if (G_UNLIKELY ((!src.ptr || !dst.ptr))) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR ("get tbm bo handle failed src(%p) dst(%p): %s(%d)", src.ptr, + dst.ptr, err_str, errno); + if (src.ptr) + tbm_bo_unmap (mm_video_buf->handle.bo[i]); + if (dst.ptr) + tbm_bo_unmap (bo); + return FALSE; + } + /* copy */ + memcpy (dst.ptr, src.ptr, bo_size); + /* bo unmap */ + tbm_bo_unmap (mm_video_buf->handle.bo[i]); + tbm_bo_unmap (bo); + } + } + display->flush_buffer = flush_buffer; + return TRUE; +} + +static int +gst_wayland_sink_copy_mm_video_buf_info_to_flush_buffer (GstWlDisplay * display, + MMVideoBuffer * mm_video_buf) +{ + int ret = FALSE; + g_return_val_if_fail (display != NULL, FALSE); + g_return_val_if_fail (mm_video_buf != NULL, FALSE); + FUNCTION; + + display->plane_num = mm_video_buf->plane_num; + display->tbm_bo_num = mm_video_buf->handle_num; + ret = gst_wayland_sink_make_flush_buffer (display, mm_video_buf); + if (ret) { + int i; + for (i = 0; i < display->plane_num; i++) { + if (display->flush_buffer->bo[i] != NULL) { + display->bo[i] = display->flush_buffer->bo[i]; + GST_LOG ("bo %p", display->bo[i]); + } else { + display->bo[i] = 0; + } + display->plane_size[i] = mm_video_buf->size[i]; + display->width[i] = mm_video_buf->width[i]; + display->height[i] = mm_video_buf->height[i]; + display->stride_width[i] = mm_video_buf->stride_width[i]; + display->stride_height[i] = mm_video_buf->stride_height[i]; + display->native_video_size += display->plane_size[i]; + GST_LOG ("TBM bo[%d]:%p", i, display->bo[i]); + GST_LOG ("width[%d]:%d, height[%d]:%d", i, display->width[i], i, + display->height[i]); + GST_LOG ("stride_width[%d]:%d, stride_height[%d]:%d", i, + display->stride_width[i], i, display->stride_height[i]); + GST_LOG ("plane size[%d]:%d", i, display->plane_size[i]); + } + memset (mm_video_buf, 0, sizeof (MMVideoBuffer)); + } + return ret; +} +#endif + +static void +gst_wayland_sink_add_mm_video_buf_info (GstWlDisplay * display, + MMVideoBuffer * mm_video_buf) +{ + int i; + g_return_if_fail (display != NULL); + g_return_if_fail (mm_video_buf != NULL); + FUNCTION; + + display->plane_num = mm_video_buf->plane_num; + display->tbm_bo_num = mm_video_buf->handle_num; + for (i = 0; i < display->plane_num; i++) { + if (mm_video_buf->handle.bo[i] != NULL) { + display->bo[i] = mm_video_buf->handle.bo[i]; + } else { + display->bo[i] = 0; + } + display->plane_size[i] = mm_video_buf->size[i]; + display->width[i] = mm_video_buf->width[i]; + display->height[i] = mm_video_buf->height[i]; + display->stride_width[i] = mm_video_buf->stride_width[i]; + display->stride_height[i] = mm_video_buf->stride_height[i]; + display->native_video_size += display->plane_size[i]; + GST_LOG ("TBM bo[%d]:%p", i, display->bo[i]); + GST_LOG ("width[%d]:%d, height[%d]:%d", i, display->width[i], i, + display->height[i]); + GST_LOG ("stride_width[%d]:%d, stride_height[%d]:%d", i, + display->stride_width[i], i, display->stride_height[i]); + GST_LOG ("plane size[%d]:%d", i, display->plane_size[i]); + } +} + +static MMVideoBuffer * +gst_wayland_sink_get_mm_video_buf (GstBuffer * buffer) +{ + GstMemory *mem; + GstMapInfo mem_info = GST_MAP_INFO_INIT; + MMVideoBuffer *mm_video_buf = NULL; + + g_return_val_if_fail (buffer != NULL, NULL); + FUNCTION; + + 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 ("mm_video_buf of gstbuffer(@%p) is NULL.", buffer); + return NULL; + } + + if (mm_video_buf->type != MM_VIDEO_BUFFER_TYPE_TBM_BO) { + GST_ERROR ("Buffer type is not TBM"); + return NULL; + } + GST_DEBUG ("TBM bo: handle.bo[0]:%p, handle.bo[1]:%p, handle.bo[2]:%p", + mm_video_buf->handle.bo[0], mm_video_buf->handle.bo[1], + mm_video_buf->handle.bo[2]); + GST_DEBUG ("Number of TBM bo is %d", mm_video_buf->handle_num); + GST_DEBUG ("Number of plane is %d", mm_video_buf->plane_num); + return mm_video_buf; +} + +static int +gst_wayland_sink_get_mm_video_buf_info (GstWaylandSink * sink, + GstBuffer * buffer) +{ + GstWlDisplay *display; + MMVideoBuffer *mm_video_buf = NULL; + + g_return_val_if_fail (sink != NULL, FALSE); + g_return_val_if_fail (buffer != NULL, FALSE); + + FUNCTION; + display = sink->display; + g_return_val_if_fail (sink->display != NULL, FALSE); + + /* get MMVideoBuffer from buffer */ + mm_video_buf = gst_wayland_sink_get_mm_video_buf (buffer); + g_return_val_if_fail (mm_video_buf != NULL, FALSE); + + /* assign mm_video_buf info */ + display->native_video_size = 0; + display->flush_request = 0; + display->flush_request = mm_video_buf->flush_request; + GST_DEBUG ("flush_request value is %d", display->flush_request); +#ifdef USE_WL_FLUSH_BUFFER + if (gst_wayland_sink_need_flush_buffer (sink)) { + /* Sometimes there isn't video data in MMVideoBuffer from buffer + when flush_request is true */ + if (mm_video_buf->flush_request && !mm_video_buf->size[0] + && sink->last_buffer) { + /* replace MMVideoBuffer from last buffer */ + mm_video_buf = gst_wayland_sink_get_mm_video_buf (sink->last_buffer); + g_return_val_if_fail (mm_video_buf != NULL, FALSE); + } + if (!gst_wayland_sink_copy_mm_video_buf_info_to_flush_buffer (display, + mm_video_buf)) { + GST_ERROR ("cat not copy mm_video_buf info to flush"); + return FALSE; + } + } else +#endif + /* normal routine */ + gst_wayland_sink_add_mm_video_buf_info (display, mm_video_buf); + + return TRUE; +} + +static void +gst_wayland_sink_render_flush_buffer (GstBaseSink * bsink) +{ + GstWaylandSink *sink; + sink = GST_WAYLAND_SINK (bsink); + FUNCTION; + g_return_if_fail (sink != NULL); + g_return_if_fail (sink->last_buffer != NULL); + + gst_wayland_sink_render (bsink, sink->last_buffer); +} + +static void +gst_wayland_sink_render_last_sample (GstWaylandSink * sink) +{ + GstSample *last_sample = NULL; + GstBuffer *last_buffer = NULL; + FUNCTION; + g_return_if_fail (sink != NULL); + + g_object_get (G_OBJECT (sink), "last-sample", &last_sample, NULL); + if (last_sample) { + last_buffer = gst_sample_get_buffer (last_sample); + if (last_buffer) { + GST_LOG ("PAUSED state : last buffer %p", last_buffer); + gst_buffer_ref (last_buffer); + gst_wayland_sink_render (GST_BASE_SINK (sink), last_buffer); + gst_buffer_unref (last_buffer); + last_buffer = NULL; + } + gst_sample_unref (last_sample); + last_sample = NULL; + } +} + +#endif + +static void +gst_wayland_sink_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (object); + FUNCTION; + + switch (prop_id) { + case PROP_DISPLAY: + GST_OBJECT_LOCK (sink); + g_value_set_string (value, sink->display_name); + GST_OBJECT_UNLOCK (sink); + break; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + case PROP_DUMP_VIDEO: + g_value_set_boolean (value, sink->dump_video); + break; + case PROP_DUMP_COUNT: + g_value_set_uint (value, sink->total_dump); + break; + case PROP_DISABLE_OVERLAY: + g_value_set_boolean (value, sink->disable_overlay); + break; + case PROP_KEEP_CAMERA_PREVIEW: + g_value_set_boolean (value, sink->keep_camera_preview); + break; + case PROP_SIGNAL_HANDOFFS: + g_value_set_boolean (value, sink->signal_handoffs); + break; + case PROP_USE_TBM: + g_value_set_boolean (value, sink->USE_TBM); + break; + 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_FLIP: + g_value_set_enum (value, sink->flip); + break; + case PROP_VISIBLE: + g_value_set_boolean (value, sink->visible); + break; +#ifdef ENABLE_FUNCTION + case PROP_SCALE_WIDTH: + g_value_set_double (value, sink->scale_w); + break; + case PROP_SCALE_HEIGHT: + g_value_set_double (value, sink->scale_h); + break; + case PROP_FOLLOW_PARENT_TRANSFORM: + g_value_set_boolean (value, sink->follow_parent_transform); + break; + case PROP_CROP_X: + g_value_set_uint (value, sink->crop_x); + break; + case PROP_CROP_Y: + g_value_set_uint (value, sink->crop_y); + break; + case PROP_CROP_WIDTH: + g_value_set_uint (value, sink->crop_w); + break; + case PROP_CROP_HEIGHT: + g_value_set_uint (value, sink->crop_h); + break; + case PROP_RATIO_WIDTH: + g_value_set_double (value, sink->ratio_w); + break; + case PROP_RATIO_HEIGHT: + g_value_set_double (value, sink->ratio_h); + break; + case PROP_OFFSET_X: + g_value_set_uint (value, sink->offset_x); + break; + case PROP_OFFSET_Y: + g_value_set_uint (value, sink->offset_y); + break; + case PROP_OFFSET_WIDTH: + g_value_set_uint (value, sink->offset_w); + break; + case PROP_OFFSET_HEIGHT: + g_value_set_uint (value, sink->offset_h); + break; + case PROP_ALIGN_WIDTH: + g_value_set_double (value, sink->align_w); + break; + case PROP_ALIGN_HEIGHT: + g_value_set_double (value, sink->align_h); + break; +#endif +#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) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (object); + FUNCTION; + g_mutex_lock (&sink->render_lock); + + switch (prop_id) { + case PROP_DISPLAY: + GST_OBJECT_LOCK (sink); + sink->display_name = g_value_dup_string (value); + GST_OBJECT_UNLOCK (sink); + break; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + case PROP_DUMP_VIDEO: + sink->dump_video = g_value_get_boolean (value); + if (sink->display) { + sink->display->dump_video = sink->dump_video; + GST_LOG ("Dump video set (%d):", sink->dump_video); + } + break; + case PROP_DUMP_COUNT: + sink->total_dump = g_value_get_uint (value); + if (sink->display) + sink->display->total_dump = sink->total_dump; + break; + case PROP_DISABLE_OVERLAY: + sink->disable_overlay = g_value_get_boolean (value); + if (sink->window && sink->display) { + if (sink->disable_overlay) { + g_clear_object (&sink->window); + g_clear_object (&sink->display); + } else + gst_wayland_sink_recover_display_window_info (sink); + } + break; + case PROP_SIGNAL_HANDOFFS: + sink->signal_handoffs = g_value_get_boolean (value); + GST_LOG ("set signal_handoffs(%d)", sink->signal_handoffs); + if (sink->window) { + if (sink->signal_handoffs) { + /* overlay -> hand-off */ + if (GST_STATE (sink) == GST_STATE_PAUSED) { + /* check if the prerolled buffer has been rendered in GST_STATE_PAUSED */ + if (sink->last_buffer) { + GST_LOG ("g_signal_emit: preroll-handoff"); + g_signal_emit (sink, + gst_waylandsink_signals[SIGNAL_PREROLL_HANDOFF], 0, + sink->last_buffer, GST_BASE_SINK (object)->sinkpad); + } + } + gst_wayland_sink_stop_video (sink); + } else { + if (GST_STATE (sink) == GST_STATE_PAUSED) { + g_mutex_unlock (&sink->render_lock); + /* scenario: inline video mode->paused->fullscreen mode->signal_handoffs(0) + we need to update preroll buffer on wayland surface */ + gst_wayland_sink_render_last_sample (sink); + g_mutex_lock (&sink->render_lock); + } + } + } + break; + case PROP_KEEP_CAMERA_PREVIEW: + sink->keep_camera_preview = g_value_get_boolean (value); + GST_LOG ("keep_camera_preview (%d)", sink->keep_camera_preview); + break; + case PROP_USE_TBM: + sink->USE_TBM = g_value_get_boolean (value); + GST_LOG ("1:USE TBM 0: USE SHM set(%d)", sink->USE_TBM); + break; + case PROP_ROTATE_ANGLE: + if (sink->rotate_angle == g_value_get_enum (value)) + break; + sink->rotate_angle = g_value_get_enum (value); + GST_WARNING_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle); + sink->video_info_changed = TRUE; + if (sink->window) { + gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle); + } + break; + case PROP_DISPLAY_GEOMETRY_METHOD: + if (sink->display_geometry_method == g_value_get_enum (value)) + break; + sink->display_geometry_method = g_value_get_enum (value); + GST_WARNING_OBJECT (sink, "Display geometry method is set (%d)", + sink->display_geometry_method); + sink->video_info_changed = TRUE; + if (sink->window) { + gst_wl_window_set_destination_mode (sink->window, + sink->display_geometry_method); + } + break; + case PROP_FLIP: + if (sink->flip == g_value_get_enum (value)) + break; + sink->flip = g_value_get_enum (value); + GST_WARNING_OBJECT (sink, "flip is set (%d)", sink->flip); + sink->video_info_changed = TRUE; + if (sink->window) { + gst_wl_window_set_flip (sink->window, sink->flip); + } + break; + case PROP_VISIBLE: + if (sink->visible == g_value_get_boolean (value)) + break; + sink->visible = g_value_get_boolean (value); + GST_WARNING_OBJECT (sink, "visible is set (%d)", sink->visible); + if (sink->visible && GST_STATE (sink) == GST_STATE_PAUSED) { + /* need to attatch last buffer */ + sink->video_info_changed = TRUE; + } else if (!sink->visible && GST_STATE (sink) >= GST_STATE_PAUSED) { + /* video stop */ + if (sink->window) { + gst_wayland_sink_stop_video (sink); + } + } + break; +#ifdef ENABLE_FUNCTION + case PROP_SCALE_WIDTH: + if (sink->scale_w == g_value_get_double (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->scale_w = g_value_get_double (value); + GST_WARNING_OBJECT (sink, "scale-w is set (%f)", sink->scale_w); + sink->video_info_changed = TRUE; + if (sink->window) + gst_wl_window_set_destination_mode_scale (sink->window, sink->scale_w, + sink->scale_h); + break; + case PROP_SCALE_HEIGHT: + if (sink->scale_h == g_value_get_double (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->scale_h = g_value_get_double (value); + GST_WARNING_OBJECT (sink, "scale-h is set (%f)", sink->scale_h); + sink->video_info_changed = TRUE; + if (sink->window) + gst_wl_window_set_destination_mode_scale (sink->window, sink->scale_w, + sink->scale_h); + break; + case PROP_FOLLOW_PARENT_TRANSFORM: + if (sink->follow_parent_transform == g_value_get_boolean (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->follow_parent_transform = g_value_get_boolean (value); + GST_WARNING_OBJECT (sink, "follow parent transform is set (%d)", + sink->follow_parent_transform); + sink->video_info_changed = TRUE; + if (sink->window) { + gst_wl_window_set_destination_mode_follow_parent_transform + (sink->window, sink->follow_parent_transform); + } + break; + case PROP_CROP_X: + if (sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->crop_x = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "crop-x is set (%d)", sink->crop_x); + break; + case PROP_CROP_Y: + if (sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->crop_y = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "crop-y is set (%d)", sink->crop_y); + break; + case PROP_CROP_WIDTH: + if (sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->crop_w = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "crop_w is set (%d)", sink->crop_w); + /* crop-w is unset by 0, set to video width size */ + if (sink->crop_w == 0 && sink->video_info.width > 0) { + sink->crop_w = + gst_util_uint64_scale_int_round (sink->video_info.width, + sink->video_info.par_n, sink->video_info.par_d); + GST_LOG ("crop-w is unset by 0, set to video width size(%d)", + sink->crop_w); + } + break; + case PROP_CROP_HEIGHT: + if (sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->crop_h = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "crop-h is set (%d)", sink->crop_h); + /* crop-h unset by 0, set to video height size */ + if (sink->crop_h == 0 && sink->video_info.height > 0) { + sink->crop_h = sink->video_info.height; + GST_LOG ("crop-h is unset by 0, set to video height size(%d)", + sink->crop_h); + } + sink->video_info_changed = TRUE; + if (sink->window && sink->crop_w > 0 && sink->crop_h > 0) { + gst_wl_window_set_destination_mode_crop_wl_buffer (sink->window, + sink->crop_x, sink->crop_y, sink->crop_w, sink->crop_h); + } + break; + case PROP_RATIO_WIDTH: + if (sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->ratio_w = g_value_get_double (value); + GST_WARNING_OBJECT (sink, "ratio-w is set (%f)", sink->ratio_w); + break; + case PROP_RATIO_HEIGHT: + if (sink->scale_w == g_value_get_double (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->ratio_h = g_value_get_double (value); + GST_WARNING_OBJECT (sink, "ratio-h is set (%f)", sink->ratio_h); + sink->video_info_changed = TRUE; + if (sink->window) + gst_wl_window_set_destination_mode_ratio (sink->window, sink->ratio_w, + sink->ratio_h); + break; + case PROP_OFFSET_X: + if (sink->offset_x == g_value_get_uint (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->offset_x = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "offset-x is set (%d)", sink->offset_x); + break; + case PROP_OFFSET_Y: + if (sink->offset_y == g_value_get_uint (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->offset_y = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "offset-y is set (%d)", sink->offset_y); + break; + case PROP_OFFSET_WIDTH: + if (sink->offset_w == g_value_get_uint (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->offset_w = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "offset-w is set (%d)", sink->offset_w); + break; + case PROP_OFFSET_HEIGHT: + if (sink->offset_h == g_value_get_uint (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->offset_h = g_value_get_uint (value); + GST_WARNING_OBJECT (sink, "offset-h is set (%d)", sink->offset_h); + sink->video_info_changed = TRUE; + if (sink->window) + gst_wl_window_set_destination_mode_offset (sink->window, sink->offset_x, + sink->offset_y, sink->offset_w, sink->offset_h); + break; + case PROP_ALIGN_WIDTH: + if (sink->align_w == g_value_get_double (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->align_w = g_value_get_double (value); + GST_WARNING_OBJECT (sink, "align_w is set (%f)", sink->align_w); + break; + case PROP_ALIGN_HEIGHT: + if (sink->align_h == g_value_get_double (value) + || sink->display_geometry_method == DISP_GEO_METHOD_CUSTOM_ROI) + break; + sink->align_h = g_value_get_double (value); + GST_WARNING_OBJECT (sink, "align_h is set (%f)", sink->align_h); + sink->video_info_changed = TRUE; + if (sink->window) + gst_wl_window_set_destination_mode_align (sink->window, sink->align_w, + sink->align_h); + break; +#endif +#endif + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->video_info_changed && sink->window) { + gst_wl_window_set_video_info_change (sink->window, TRUE); + if (GST_STATE (sink) == GST_STATE_PAUSED) + gst_wayland_sink_update_last_buffer_geometry (sink); + } +#endif + g_mutex_unlock (&sink->render_lock); + +} + +static void +gst_wayland_sink_finalize (GObject * object) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (object); + FUNCTION; + GST_DEBUG_OBJECT (sink, "Finalizing the sink.."); + + if (sink->last_buffer) + gst_buffer_unref (sink->last_buffer); + if (sink->display) + g_object_unref (sink->display); + if (sink->window) + g_object_unref (sink->window); + if (sink->pool) + gst_object_unref (sink->pool); + + if (sink->display_name) + g_free (sink->display_name); + + g_mutex_clear (&sink->display_lock); + g_mutex_clear (&sink->render_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +static gboolean +gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event) +{ + GstWaylandSink *sink; + const GstStructure *s; + + sink = GST_WAYLAND_SINK (bsink); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + GST_LOG ("get GST_EVENT_EOS event..state is %d", GST_STATE (sink)); + break; + case GST_EVENT_CUSTOM_DOWNSTREAM: + s = gst_event_get_structure (event); + if (s == NULL + || !gst_structure_has_name (s, GST_APP_EVENT_FLUSH_BUFFER_NAME)) + break; + + GST_LOG ("get GST_EVENT_CUSTOM_DOWNSTREAM EVENT: %s..state is %d", + gst_structure_get_name (s), GST_STATE (sink)); + + sink->got_costum_event = TRUE; + if (gst_wayland_sink_is_gapless (sink)) { + gst_wayland_sink_render_flush_buffer (bsink); + sink->got_costum_event = FALSE; + } + sink->got_costum_event = FALSE; + break; + default: + break; + } + return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); +} + +static void +gst_wayland_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end) +{ + /* If basesink need to drop buffer, basesink ask waylandsink to start and end */ + GstWaylandSink *sink; + + sink = GST_WAYLAND_SINK (bsink); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + *start = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) { + *end = *start + GST_BUFFER_DURATION (buf); + } else { + if (sink->fps_n > 0) { + *end = + *start + gst_util_uint64_scale_int (GST_SECOND, sink->fps_d, + sink->fps_n); + } + } + } +} +#endif + +#if 0 +/* must be called with the display_lock */ +static void +gst_wayland_sink_set_display_from_context (GstWaylandSink * sink, + GstContext * context) +{ + struct wl_display *display; + GError *error = NULL; + FUNCTION; + + 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); + } +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + sink->display->USE_TBM = sink->USE_TBM; +#endif +} +#endif +static gboolean +gst_wayland_sink_find_display (GstWaylandSink * sink) +{ +// GstQuery *query; +// GstMessage *msg; +// GstContext *context = NULL; + GError *error = NULL; + gboolean ret = TRUE; + FUNCTION; + + g_mutex_lock (&sink->display_lock); + + if (!sink->display) { +#if 0 + /* 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); +#endif + if (G_LIKELY (!sink->display)) { +#if 0 + /* 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); +#endif + 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; + } +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (G_LIKELY (sink->display)) + sink->display->USE_TBM = sink->USE_TBM; +#endif + } + } + } + + g_mutex_unlock (&sink->display_lock); + + return ret; +} + +static GstStateChangeReturn +gst_wayland_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + FUNCTION; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + GST_LOG ("WAYLANDSINK TRANSITION: NULL_TO_READY"); + if (!gst_wayland_sink_find_display (sink)) + return GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_LOG ("WAYLANDSINK TRANSITION: READY_TO_PAUSED"); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + GST_LOG ("WAYLANDSINK TRANSITION: PAUSED_TO_PLAYING"); + 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_PLAYING_TO_PAUSED: + GST_LOG ("WAYLANDSINK TRANSITION: PLAYING_TO_PAUSED"); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_LOG ("WAYLANDSINK TRANSITION: PAUSED_TO_READY"); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->keep_camera_preview) { + if (sink->window) { + if (!gst_wl_window_is_toplevel (sink->window)) { + GstBaseSink *bsink = GST_BASE_SINK (element); + if (sink->USE_TBM && sink->display->is_native_format + && !sink->display->flush_buffer) { + /* To avoid duplicate request by App, check flush_buffer by flush_request of MMVideoBuffer */ + sink->request_camera_flush_buf = TRUE; + gst_wayland_sink_render_flush_buffer (bsink); + sink->request_camera_flush_buf = FALSE; + } + break; + } + } + } +#endif + gst_buffer_replace (&sink->last_buffer, NULL); + if (sink->window) { + if (gst_wl_window_is_toplevel (sink->window)) { + GST_DEBUG ("internal window"); + g_clear_object (&sink->window); + } else { + /* remove buffer from surface, show nothing */ + GST_DEBUG ("external window"); + gst_wl_window_render (sink->window, NULL, NULL); + } + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + GST_LOG ("WAYLANDSINK TRANSITION: 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 */ + g_clear_object (&sink->display); + } + g_mutex_unlock (&sink->display_lock); + g_clear_object (&sink->pool); + break; + default: + break; + } + + return ret; +} +#if 0 +static void +gst_wayland_sink_set_context (GstElement * element, GstContext * context) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (element); + FUNCTION; + + 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"); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + g_mutex_unlock (&sink->display_lock); + return; +#endif + } + g_mutex_unlock (&sink->display_lock); + } + + if (GST_ELEMENT_CLASS (parent_class)->set_context) + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} +#endif +static GstCaps * +gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) +{ + GstWaylandSink *sink; + GstCaps *caps; + FUNCTION; + + sink = GST_WAYLAND_SINK (bsink); + + caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink)); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + /* To Do : When caps negotiation is newly needed by such as gapless, + waylandsink may have to make display when disable-overlay is TRUE in here */ +#endif + g_mutex_lock (&sink->display_lock); + + if (sink->display) { + GValue list = G_VALUE_INIT; + GValue value = G_VALUE_INIT; + GArray *formats; + gint i; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + uint32_t tbm_fmt; +#endif + enum wl_shm_format fmt; + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&value, G_TYPE_STRING); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->USE_TBM) + formats = sink->display->tbm_formats; + else /* SHM */ +#endif + formats = sink->display->formats; + + for (i = 0; i < formats->len; i++) { +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->USE_TBM) { + tbm_fmt = g_array_index (formats, uint32_t, i); + g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt)); + gst_value_list_append_value (&list, &value); + + /* TBM doesn't support Native formats(SN12, ST12, SN21, SR32 and S420), + So we add Native formats manually as supported format. */ + if (tbm_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); + } else if (tbm_fmt == TBM_FORMAT_NV12MT) { + g_value_set_string (&value, + gst_video_format_to_string (GST_VIDEO_FORMAT_ST12)); + gst_value_list_append_value (&list, &value); + } else if (tbm_fmt == TBM_FORMAT_NV21) { + g_value_set_string (&value, + gst_video_format_to_string (GST_VIDEO_FORMAT_SN21)); + gst_value_list_append_value (&list, &value); + } else if (tbm_fmt == TBM_FORMAT_ARGB8888) { + g_value_set_string (&value, + gst_video_format_to_string (GST_VIDEO_FORMAT_SR32)); + gst_value_list_append_value (&list, &value); + } else if (tbm_fmt == TBM_FORMAT_YUV420) { + g_value_set_string (&value, + gst_video_format_to_string (GST_VIDEO_FORMAT_S420)); + gst_value_list_append_value (&list, &value); + } + } else { /* USE SHM */ + fmt = g_array_index (formats, uint32_t, i); + g_value_set_string (&value, gst_wl_shm_format_to_string (fmt)); + gst_value_list_append_value (&list, &value); + } +#else /* open source */ + fmt = g_array_index (formats, uint32_t, i); + g_value_set_string (&value, gst_wl_shm_format_to_string (fmt)); + 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_value_unset (&value); + g_value_unset (&list); + + } + + 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) +{ + GstWaylandSink *sink; + GstBufferPool *newpool; + GstVideoInfo info; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + uint32_t tbm_format = -1; +#endif + enum wl_shm_format format = -1; + + GArray *formats; + gint i; + GstStructure *structure; + GstWlShmAllocator *self = NULL; + + FUNCTION; + + 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 TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->USE_TBM) { + tbm_format = + gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info)); + if ((gint) tbm_format == -1) + goto invalid_format; + } else { + format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info)); + if ((gint) format == -1) + goto invalid_format; + } +#else /* open source */ + format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info)); + + if ((gint) format == -1) + goto invalid_format; +#endif + + /* verify we support the requested format */ +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->USE_TBM) { + GST_LOG ("USE TBM FORMAT"); + formats = sink->display->tbm_formats; + for (i = 0; i < formats->len; i++) { + if (g_array_index (formats, uint32_t, i) == tbm_format) + break; + } + } else { /* USE SHM */ + GST_LOG ("USE SHM FORMAT"); + formats = sink->display->formats; + for (i = 0; i < formats->len; i++) { + if (g_array_index (formats, uint32_t, i) == format) + break; + } + } +#else /* open source */ + formats = sink->display->formats; + for (i = 0; i < formats->len; i++) { + if (g_array_index (formats, uint32_t, i) == format) + break; + } +#endif + if (i >= formats->len) + goto unsupported_format; + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + /* init frame rate for baseink */ + sink->fps_n = info.fps_n; + sink->fps_d = info.fps_d; + + /* init value for set source */ + sink->crop_x = sink->crop_y = sink->crop_w = sink->crop_h = 0; + + if (sink->USE_TBM) { + if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 || + GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12 || + GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN21 || + GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SR32 || + GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_S420) { + sink->display->is_native_format = TRUE; + + /* store the video info */ + sink->video_info = info; + sink->video_info_changed = TRUE; + } else { + sink->display->is_native_format = FALSE; + self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ()); + self->display = sink->display; + /* create a new pool for the new configuration */ + newpool = gst_video_buffer_pool_new (); + if (!newpool) + goto pool_failed; + + structure = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0); + gst_buffer_pool_config_set_allocator (structure, + gst_wl_shm_allocator_get (), NULL); + 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); + } + } else { /* USE SHM */ + + self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ()); + self->display = sink->display; + + /* create a new pool for the new configuration */ + newpool = gst_video_buffer_pool_new (); + if (!newpool) + goto pool_failed; + + structure = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0); + gst_buffer_pool_config_set_allocator (structure, + gst_wl_shm_allocator_get (), NULL); + 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); + } + + sink->is_native_format = sink->display->is_native_format; + + if (sink->window) + gst_wayland_sink_update_window_geometry (sink); + + +#else /*open source */ + /* create a new pool for the new configuration */ + newpool = gst_video_buffer_pool_new (); + if (!newpool) + goto pool_failed; + + structure = gst_buffer_pool_get_config (newpool); + gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0); + gst_buffer_pool_config_set_allocator (structure, + gst_wl_shm_allocator_get (), NULL); + 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: + { +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (sink->USE_TBM) + GST_DEBUG_OBJECT (sink, "Format %s is not available on the display", + gst_wl_tbm_format_to_string (tbm_format)); + else /*USE SHM */ + GST_DEBUG_OBJECT (sink, "Format %s is not available on the display", + gst_wl_shm_format_to_string (format)); +#else /*open source */ + GST_DEBUG_OBJECT (sink, "Format %s is not available on the display", + gst_wl_shm_format_to_string (format)); +#endif + 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) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); + GstStructure *config; + guint size, min_bufs, max_bufs; + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + gboolean need_pool; + GstCaps *caps; + FUNCTION; + + if (sink->USE_TBM) { + if (sink->display->is_native_format == TRUE) + return FALSE; + + gst_query_parse_allocation (query, &caps, &need_pool); + + if (caps == NULL) { + GST_DEBUG_OBJECT (bsink, "no caps specified"); + return FALSE; + } + } +#endif + config = gst_buffer_pool_get_config (sink->pool); + gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs); + + /* we do have a pool for sure (created in set_caps), + * so let's propose it anyway, but also propose the allocator on its own */ + gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs); + gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL); + + gst_structure_free (config); + + return TRUE; +} + +static GstFlowReturn +gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer) +{ +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); + FUNCTION; + + GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer); + + if (sink->signal_handoffs) { + GST_LOG ("g_signal_emit: preroll-handoff"); + g_signal_emit (sink, + gst_waylandsink_signals[SIGNAL_PREROLL_HANDOFF], 0, buffer, + bsink->sinkpad); + + return GST_FLOW_OK; + } +#endif + return gst_wayland_sink_render (bsink, buffer); +} + +static void +frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time) +{ + GstWaylandSink *sink = data; + FUNCTION; + + GST_LOG ("frame_redraw_cb"); + + g_atomic_int_set (&sink->redraw_pending, FALSE); + GST_INFO ("wl_callback_destroy (wl_callback@%p)", callback); + wl_callback_destroy (callback); +} + +static const struct wl_callback_listener frame_callback_listener = { + frame_redraw_callback +}; + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +static void +gst_wayland_sink_update_window_geometry (GstWaylandSink * sink) +{ + FUNCTION; + 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_destination_mode (sink->window, + sink->display_geometry_method); + gst_wl_window_set_flip (sink->window, sink->flip); + if (sink->crop_w == 0 && sink->crop_h == 0) { + sink->crop_w = + gst_util_uint64_scale_int_round (sink->video_info.width, + sink->video_info.par_n, sink->video_info.par_d); + sink->crop_h = sink->video_info.height; + } + gst_wl_window_set_destination_mode_crop_wl_buffer (sink->window, sink->crop_x, + sink->crop_y, sink->crop_w, sink->crop_h); +#ifdef ENABLE_FUNCTION + gst_wl_window_set_destination_mode_follow_parent_transform (sink->window, + sink->follow_parent_transform); + gst_wl_window_set_destination_mode_ratio (sink->window, sink->ratio_w, + sink->ratio_h); + gst_wl_window_set_destination_mode_scale (sink->window, sink->scale_w, + sink->scale_h); + gst_wl_window_set_destination_mode_offset (sink->window, sink->offset_x, + sink->offset_y, sink->offset_w, sink->offset_h); + gst_wl_window_set_destination_mode_align (sink->window, sink->align_w, + sink->align_h); +#endif +} +#endif +/* must be called with the render lock */ +static void +render_last_buffer (GstWaylandSink * sink) +{ + GstWlBuffer *wlbuffer; + const GstVideoInfo *info = NULL; + struct wl_surface *surface; + struct wl_callback *callback; + FUNCTION; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + g_return_if_fail (sink->last_buffer != NULL); + g_return_if_fail (sink->window != NULL); +#endif + + wlbuffer = gst_buffer_get_wl_buffer (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); + GST_INFO ("wl_callback@%p = wl_surface_frame (video_surface@%p)", callback, + surface); + + /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */ + GST_INFO + ("wl_callback_add_listener (wl_callback@%p, wl_callback_listener@%p, GstWaylandSink@%p)", + callback, &frame_callback_listener, sink); + wl_callback_add_listener (callback, &frame_callback_listener, sink); + + if (G_UNLIKELY (sink->video_info_changed)) { +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + gst_wl_window_set_video_info_change (sink->window, TRUE); +#endif + info = &sink->video_info; + sink->video_info_changed = FALSE; + } + gst_wl_window_render (sink->window, wlbuffer, info); +} + + +static gboolean +gst_wayland_sink_has_wlbuffer (GstWaylandSink * sink, GstBuffer * buffer) +{ + GstWlBuffer *gstwlbuffer; + FUNCTION; + g_return_val_if_fail (sink != NULL, FALSE); + g_return_val_if_fail (buffer != NULL, FALSE); + + gstwlbuffer = gst_buffer_get_wl_buffer (buffer); + if (gstwlbuffer && gstwlbuffer->display == sink->display + && !gst_wayland_sink_is_gapless (sink) + && !sink->request_camera_flush_buf) { + /* gstbuffer has wlbuffer */ + /* e.g) last_buffer, buffer is created by previous plugin or waylandsink with BufferPool */ + GST_LOG ("buffer(%p) has GstWlBuffer(%p):wlbuffer(%p)", buffer, gstwlbuffer, + gstwlbuffer->wlbuffer); + return TRUE; + } + GST_LOG ("buffer(%p) has not wlbuffer", buffer); + return FALSE; +} + +static GstFlowReturn +gst_wayland_sink_no_create_wlbuffer (GstWaylandSink * sink, GstBuffer * buffer) +{ + GstMemory *mem; + MMVideoBuffer *mm_video_buf = NULL; + GstFlowReturn ret = GST_FLOW_OK; + FUNCTION; + g_return_val_if_fail (sink != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + + /* the last_buffer for flushing has wlbuffer or + buffer is created by previous plugins or waylandsink has wlbuffer */ + mem = gst_buffer_peek_memory (buffer, 0); + if (gst_is_wl_memory (mem)) { + GST_LOG ("buffer(%p) is created by waylandsink has a wl_buffer, " + "writing directly", buffer); + } else { + GST_LOG + ("buffer(%p) is created by previous plugins with BufferPool has a wl_buffer, " + "writing directly", buffer); + GST_LOG ("previous plugins must manage buffer index well"); + /* check MMVideoBuffer */ + mm_video_buf = gst_wayland_sink_get_mm_video_buf (buffer); + if (mm_video_buf != NULL) { + GST_LOG ("GstBuffer(%p) has MMVideoBuffer(%p)", buffer, mm_video_buf); + } else { + GST_ERROR ("GstBuffer(%p) has not MMVideoBuffer(%p)", buffer, + mm_video_buf); + return GST_FLOW_ERROR; + } + } + + sink->to_render = buffer; + + if (sink->dump_video && !sink->display->is_native_format) + gst_wayland_sink_dump_raw_video (sink, sink->to_render, + sink->display->dump_count++, sink->total_dump); + + return ret; +} + +static void +gst_wayland_sink_create_wlbuffer_with_wl_mem (GstWaylandSink * sink, + GstBuffer * buffer) +{ + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + FUNCTION; + g_return_if_fail (sink != NULL); + g_return_if_fail (buffer != NULL); + + GST_LOG ("gstbuffer(%p) is created by wayland has not wlbuffer", buffer); + mem = gst_buffer_peek_memory (buffer, 0); + wbuf = + gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, + &sink->video_info); + if (wbuf) { + gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); + sink->to_render = buffer; + } +} + +static GstFlowReturn +gst_wayland_sink_create_wlbuffer_with_previous_plugin_tbm (GstWaylandSink * + sink, GstBuffer * buffer) +{ + GstMemory *mem; + GstWlBuffer *wlbuffer; + struct wl_buffer *wbuf = NULL; + GstFlowReturn ret = GST_FLOW_OK; + FUNCTION; + g_return_val_if_fail (sink != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + sink->flush_gstbuf = NULL; + + GST_LOG + ("buffer(%p) is created by previous plugin with no BufferPool does not have a wl_buffer", + buffer); + GST_LOG ("Use native format with previous plugins TBM"); + /* in case of native format (SN12, ST12, SN21, SR32 and S420) */ + if (!gst_wayland_sink_get_mm_video_buf_info (sink, buffer)) { + return GST_FLOW_ERROR; + } + + wlbuffer = gst_buffer_get_wl_buffer (buffer); + /* last_buffer from gaplasee have wlbuffer */ + if (G_UNLIKELY (!wlbuffer) || sink->display->flush_request) { + mem = gst_buffer_peek_memory (buffer, 0); + wbuf = + gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, + &sink->video_info); + if (G_UNLIKELY (!wbuf)) { + GST_ERROR ("could not create wl_buffer"); + return GST_FLOW_ERROR; + } + if (sink->display->flush_request) { + sink->flush_gstbuf = gst_buffer_new (); + GST_LOG ("To flush, new gstBuffer(%p)", sink->flush_gstbuf); + gst_buffer_add_wl_buffer (sink->flush_gstbuf, wbuf, sink->display); + } else { + gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); + } + } + return ret; +} + +static GstFlowReturn + gst_wayland_sink_copy_input_gstbuffer_to_wl_gstbuffer_with_wl_mem + (GstWaylandSink * sink, GstBuffer * buffer) +{ + GstWlBuffer *wlbuffer; + GstMemory *mem; + GstWlShmMemory *shm_mem; + GstMapInfo src; + struct wl_buffer *wbuf = NULL; + GstFlowReturn ret = GST_FLOW_OK; + FUNCTION; + g_return_val_if_fail (sink != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + + GST_LOG ("Use normal format with wayland TBM or SHM"); + /* sink->pool always exists (created in set_caps), but it may not + * be active if upstream is not using it */ + if (!gst_buffer_pool_is_active (sink->pool) && + !gst_buffer_pool_set_active (sink->pool, TRUE)) { + GST_ERROR ("failed to activate bufferpool."); + return GST_FLOW_ERROR; + } + + ret = gst_buffer_pool_acquire_buffer (sink->pool, &sink->to_render, NULL); + if (ret != GST_FLOW_OK) { + GST_WARNING ("could not create buffer"); + return ret; + } + /* the first time we acquire a buffer, + * we need to attach a wl_buffer on it */ + wlbuffer = gst_buffer_get_wl_buffer (buffer); + if (G_UNLIKELY (!wlbuffer)) { + mem = gst_buffer_peek_memory (sink->to_render, 0); + shm_mem = (GstWlShmMemory *) mem; + GST_LOG ("to_render(%p), shm_mem->fd(%d)", sink->to_render, shm_mem->fd); + wbuf = + gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, + &sink->video_info); + if (G_UNLIKELY (!wbuf)) { + GST_ERROR ("could not create wl_buffer out of wl memory"); + return GST_FLOW_ERROR; + } + wlbuffer = gst_buffer_add_wl_buffer (sink->to_render, wbuf, sink->display); + } + + gst_buffer_map (buffer, &src, GST_MAP_READ); + gst_buffer_fill (sink->to_render, 0, src.data, src.size); + gst_buffer_unmap (buffer, &src); + + return ret; +} + +static GstFlowReturn +gst_wayland_sink_create_wlbuffer (GstWaylandSink * sink, GstBuffer * buffer) +{ + GstMemory *mem; + GstFlowReturn ret = GST_FLOW_OK; + FUNCTION; + g_return_val_if_fail (sink != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + + sink->to_render = NULL; + + if (gst_wayland_sink_has_wlbuffer (sink, buffer)) { + ret = gst_wayland_sink_no_create_wlbuffer (sink, buffer); + } else { + mem = gst_buffer_peek_memory (buffer, 0); + if (gst_is_wl_memory (mem)) { + /* SHM or TBM */ + gst_wayland_sink_create_wlbuffer_with_wl_mem (sink, buffer); + } else { + /* gstbuffer is not wl memory */ + if (sink->USE_TBM && sink->display->is_native_format) { + /* Use tbm of buffer directly */ + ret = + gst_wayland_sink_create_wlbuffer_with_previous_plugin_tbm (sink, + buffer); + } else { + /* Copy virtual addr to wayland SHM or TBM */ + ret = + gst_wayland_sink_copy_input_gstbuffer_to_wl_gstbuffer_with_wl_mem + (sink, buffer); + } + } + } + return ret; +} + +static void +gst_wayland_sink_buffer_replace (GstWaylandSink * sink, GstBuffer * buffer) +{ + FUNCTION; + g_return_if_fail (sink != NULL); + + if (sink->USE_TBM && sink->display->is_native_format) { + if (sink->flush_gstbuf && sink->display->flush_request) { + GST_LOG_OBJECT (sink, "replace last_buffer:(%p)->(%p)", sink->last_buffer, + sink->flush_gstbuf); + /* increase ref count of sink->fflush_gstbuf, decrease ref count of sink->last_buffer */ + gst_buffer_replace (&sink->last_buffer, sink->flush_gstbuf); + GST_LOG_OBJECT (sink, "after gst_buffer_replace buffer %p, ref_count(%d)", + sink->flush_gstbuf, GST_OBJECT_REFCOUNT_VALUE (sink->flush_gstbuf)); + /* decrease ref count of flush_buffer */ + gst_buffer_unref (sink->flush_gstbuf); + } else { + /* normal case */ + GST_LOG_OBJECT (sink, "replace last_buffer:(%p)->(%p)", sink->last_buffer, + buffer); + /* increase ref count of buffer decrease ref count of sink->last_buffer */ + gst_buffer_replace (&sink->last_buffer, buffer); + GST_LOG_OBJECT (sink, "after gst_buffer_replace buffer %p, ref_count(%d)", + buffer, GST_OBJECT_REFCOUNT_VALUE (buffer)); + } + } else { + gst_buffer_replace (&sink->last_buffer, sink->to_render); + GST_LOG_OBJECT (sink, "after gst_buffer_replace buffer %p, ref_count(%d)", + sink->to_render, GST_OBJECT_REFCOUNT_VALUE (sink->to_render)); + } +} + +static void +gst_wayland_sink_get_window (GstWaylandSink * sink) +{ + g_return_if_fail (sink != NULL); + FUNCTION; + + /* 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) { + /* if we were not provided a window, create one ourselves */ + sink->window = + gst_wl_window_new_toplevel (sink->display, &sink->video_info); + } + gst_wayland_sink_update_window_geometry (sink); +} + +static GstFlowReturn +gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer) +{ +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + + GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); + GstFlowReturn ret = GST_FLOW_OK; + FUNCTION; + + g_mutex_lock (&sink->render_lock); + + GST_LOG_OBJECT (sink, "input gstbuffer %p, ref_count(%d)", buffer, + GST_OBJECT_REFCOUNT_VALUE (buffer)); + + /* check overlay */ + if (gst_wayland_sink_is_disabled_overlay (sink)) { + GST_LOG ("set disable_overlay, so skip"); + goto done; + } + + /* check window */ + if (G_UNLIKELY (!sink->window)) { + gst_wayland_sink_get_window (sink); + } + + /* fakesink function for media stream callback case */ + if (sink->signal_handoffs) { + GST_LOG ("g_signal_emit: hand-off "); + g_signal_emit (sink, gst_waylandsink_signals[SIGNAL_HANDOFF], 0, buffer, + bsink->sinkpad); + goto done; + } + + /* drop buffers until we get a frame callback */ + if (g_atomic_int_get (&sink->redraw_pending) == TRUE + && !gst_wayland_sink_is_gapless (sink)) + goto done; + + /* create wl_buffer */ + ret = gst_wayland_sink_create_wlbuffer (sink, buffer); + if (ret != GST_FLOW_OK) + goto done; + + /* drop double rendering */ + if ((G_UNLIKELY (buffer == sink->last_buffer) + && !(sink->display->flush_request))) { + GST_LOG_OBJECT (sink, "Buffer already being rendered"); + goto done; + } + + /* replace last_buffer */ + gst_wayland_sink_buffer_replace (sink, buffer); + + /* rendering */ + if (sink->visible) { + render_last_buffer (sink); + } else { + GST_LOG ("skip rendering"); + } + + if (sink->to_render) { + if (buffer != sink->to_render) + gst_buffer_unref (sink->to_render); + } + + goto done; + +#else /* open source */ + + GstWaylandSink *sink = GST_WAYLAND_SINK (bsink); + GstBuffer *to_render = NULL; + GstFlowReturn ret = GST_FLOW_OK; + + g_mutex_lock (&sink->render_lock); + + 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) { + /* if we were not provided a window, create one ourselves */ + sink->window = + gst_wl_window_new_toplevel (sink->display, &sink->video_info); + } + } + + /* drop buffers until we get a frame callback */ + if (g_atomic_int_get (&sink->redraw_pending) == TRUE + && !gst_wayland_sink_is_gapless (sink)) + goto done; + + /* make sure that the application has called set_render_rectangle() */ + if (G_UNLIKELY (sink->window->render_rectangle.w == 0)) + goto no_window_size; + + wlbuffer = gst_buffer_get_wl_buffer (buffer); + + if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) { + GST_LOG_OBJECT (sink, + "buffer %p has a wl_buffer from our display, " "writing directly", + buffer); + GST_LOG ("wl_buffer (%p)", wlbuffer->wlbuffer); + to_render = buffer; + + } else { + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + + GST_LOG_OBJECT (sink, + "buffer %p does not have a wl_buffer from our " "display, creating it", + buffer); + mem = gst_buffer_peek_memory (buffer, 0); + if (gst_is_wl_shm_memory (mem)) { /* is wayland memory */ + FUNCTION; + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, + &sink->video_info); + } + if (wbuf) { + gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); + to_render = buffer; + + } else { + GstMapInfo src; + /* we don't know how to create a wl_buffer directly from the provided + * memory, so we have to copy the data to a memory that we know how + * to handle... */ + + GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer); + GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying", + buffer); + /* sink->pool always exists (created in set_caps), but it may not + * be active if upstream is not using it */ + if (!gst_buffer_pool_is_active (sink->pool) && + !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; + + /* the first time we acquire a buffer, + * we need to attach a wl_buffer on it */ + wlbuffer = gst_buffer_get_wl_buffer (buffer); + if (G_UNLIKELY (!wlbuffer)) { + mem = gst_buffer_peek_memory (to_render, 0); + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display, + &sink->video_info); + if (G_UNLIKELY (!wbuf)) + goto no_wl_buffer; + + gst_buffer_add_wl_buffer (to_render, wbuf, sink->display); + } + + gst_buffer_map (buffer, &src, GST_MAP_READ); + gst_buffer_fill (to_render, 0, src.data, src.size); + gst_buffer_unmap (buffer, &src); + } + } + /* drop double rendering */ + if (G_UNLIKELY (buffer == sink->last_buffer)) { + GST_LOG_OBJECT (sink, "Buffer already being rendered"); + goto done; + } + + gst_buffer_replace (&sink->last_buffer, to_render); + render_last_buffer (sink); + + if (buffer != to_render) + gst_buffer_unref (to_render); + + goto done; + +#endif /* TIZEN_FEATURE_WLSINK_ENHANCEMENT */ + +#ifndef TIZEN_FEATURE_WLSINK_ENHANCEMENT +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 buffer"); + goto done; + } +no_wl_buffer: + { + GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory"); + ret = GST_FLOW_ERROR; + goto done; + } +activate_failed: + { + GST_ERROR_OBJECT (sink, "failed to activate bufferpool."); + ret = GST_FLOW_ERROR; + goto done; + } +#endif +done: + { + g_mutex_unlock (&sink->render_lock); + return ret; + } +} + +static void +gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface) +{ + 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; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT /* use unique_id */ + iface->set_wl_window_wl_surface_id = + gst_wayland_sink_set_wl_window_wl_surface_id; +#endif +} + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +/* use unique_id */ +static void +gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay, + guintptr wl_surface_id) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (overlay); + FUNCTION; + 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); + g_clear_object (&sink->window); + + GST_LOG ("wl_surface_id %d %x", (int) wl_surface_id, + (guintptr) wl_surface_id); + + if (wl_surface_id) { + if (G_LIKELY (gst_wayland_sink_find_display (sink))) { + /* we can use our own display with an external window handle */ + if (G_LIKELY (sink->display->own_display)) { + sink->display->wl_surface_id = (int) wl_surface_id; + sink->window = gst_wl_window_new_in_surface (sink->display, NULL); + } + } else { + GST_ERROR_OBJECT (sink, "Failed to find display handle, " + "ignoring window handle"); + } + } + g_mutex_unlock (&sink->render_lock); + +} +#endif + +static void +gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (overlay); + struct wl_surface *surface = (struct wl_surface *) handle; + FUNCTION; + + g_return_if_fail (sink != NULL); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT /* use unique_id */ + if (sink->window != NULL) { + GST_WARNING_OBJECT (sink, "changing window handle is not supported"); + return; + } +#endif + 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_WARNING (sink, RESOURCE, OPEN_READ_WRITE, + ("Application did not provide a wayland display handle"), + ("Now waylandsink use internal display handle " + "which is created ourselves. Consider providing a " + "display handle from your application with GstContext")); + sink->window = gst_wl_window_new_in_surface (sink->display, surface); + } else { + sink->window = gst_wl_window_new_in_surface (sink->display, surface); + } + } else { + GST_ERROR_OBJECT (sink, "Failed to find display handle, " + "ignoring window handle"); + } + } + g_mutex_unlock (&sink->render_lock); +} + +static void +gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay, + gint x, gint y, gint w, gint h) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (overlay); + FUNCTION; + + 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); + if (gst_wl_window_set_render_rectangle (sink->window, x, y, w, h)) { + sink->video_info_changed = TRUE; + if (sink->window && GST_STATE (sink) == GST_STATE_PAUSED) + gst_wayland_sink_update_last_buffer_geometry (sink); + } + g_mutex_unlock (&sink->render_lock); +} + +static void +gst_wayland_sink_expose (GstVideoOverlay * overlay) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (overlay); + FUNCTION; + + 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); +} +#if 0 +static void +gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface) +{ + 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) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (video); + FUNCTION; + g_return_if_fail (sink != NULL); + + g_mutex_lock (&sink->render_lock); + if (!sink->window || !sink->window->area_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->area_subsurface); + g_mutex_unlock (&sink->render_lock); +} + +static void +gst_wayland_sink_end_geometry_change (GstWaylandVideo * video) +{ + GstWaylandSink *sink = GST_WAYLAND_SINK (video); + FUNCTION; + g_return_if_fail (sink != NULL); + + g_mutex_lock (&sink->render_lock); + if (!sink->window || !sink->window->area_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->area_subsurface); + g_mutex_unlock (&sink->render_lock); +} +#endif + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "tizenwlsink", 0, + " tizen wayland video sink"); + + gst_wl_shm_allocator_register (); + + return gst_element_register (plugin, "tizenwlsink", GST_RANK_MARGINAL, + GST_TYPE_WAYLAND_SINK); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + tizenwlsink, + "Tizen Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/tizenwlsink/src/gstwaylandsink.h b/tizenwlsink/src/gstwaylandsink.h new file mode 100644 index 0000000..58c1e11 --- /dev/null +++ b/tizenwlsink/src/gstwaylandsink.h @@ -0,0 +1,130 @@ +/* + * 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,GstWaylandSink)) +#define GST_WAYLAND_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAYLAND_SINK,GstWaylandSinkClass)) +#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, GstWaylandSinkClass)) +#ifdef TIZEN_FEATURE_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_CUSTOM_ROI, + DISP_GEO_METHOD_NUM, +}; + +#define DEF_DISPLAY_FLIP FLIP_NONE +#define DEF_DISPLAY_GEOMETRY_METHOD DISP_GEO_METHOD_LETTER_BOX +#define DEFAULT_DUMP_COUNT 10 +#endif + +typedef struct _GstWaylandSink GstWaylandSink; +typedef struct _GstWaylandSinkClass GstWaylandSinkClass; + +struct _GstWaylandSink +{ + GstVideoSink parent; + + GMutex display_lock; + GstWlDisplay *display; + GstWlWindow *window; + GstBufferPool *pool; + + gboolean video_info_changed; + GstVideoInfo video_info; + + gchar *display_name; + + gboolean redraw_pending; + GMutex render_lock; + GstBuffer *last_buffer; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GstBuffer *to_render; + GstBuffer *flush_gstbuf; + /* Framerate numerator and denominator */ + gint fps_n; + gint fps_d; + + gboolean dump_video; + gboolean signal_handoffs; + gboolean request_camera_flush_buf; + gboolean keep_camera_preview; + gboolean got_costum_event; + gboolean visible; + gboolean follow_parent_transform; + gboolean USE_TBM; + gboolean is_native_format; + gboolean flush_request; + gboolean disable_overlay; + guint total_dump; + guint rotate_angle; + guint display_geometry_method; + guint flip; + guint crop_x, crop_y, crop_w, crop_h; +#ifdef ENABLE_FUNCTION + guint offset_x, offset_y, offset_w, offset_h; + gdouble ratio_w, ratio_h; + gdouble scale_w, scale_h; + gdouble align_w, align_h; +#endif +#endif +}; + +struct _GstWaylandSinkClass +{ + GstVideoSinkClass parent; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + /* signals */ + void (*handoff) (GstElement *element, GstBuffer *buf, GstPad *pad); + void (*preroll_handoff) (GstElement *element, GstBuffer *buf, GstPad *pad); +#endif +}; + +GType +gst_wayland_sink_get_type (void) + G_GNUC_CONST; + +G_END_DECLS +#endif /* __GST_WAYLAND_VIDEO_SINK_H__ */ diff --git a/tizenwlsink/src/scaler.xml b/tizenwlsink/src/scaler.xml new file mode 100644 index 0000000..e21ae5b --- /dev/null +++ b/tizenwlsink/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/tizenwlsink/src/tizen-wlvideoformat.c b/tizenwlsink/src/tizen-wlvideoformat.c new file mode 100644 index 0000000..8ebf505 --- /dev/null +++ b/tizenwlsink/src/tizen-wlvideoformat.c @@ -0,0 +1,115 @@ +/* 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 TIZEN_FEATURE_WLSINK_ENHANCEMENT + +GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_debug + +typedef struct +{ + uint32_t wl_format; + GstVideoFormat gst_format; +} wl_TbmVideoFormat; + +static const wl_TbmVideoFormat tbm_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}, + {TBM_FORMAT_BGRA8888, GST_VIDEO_FORMAT_SR32}, +#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_ARGB8888, GST_VIDEO_FORMAT_SR32}, + {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}, + {TBM_FORMAT_NV21, GST_VIDEO_FORMAT_SN21}, + {TBM_FORMAT_YUV420, GST_VIDEO_FORMAT_S420} +}; + +uint32_t +gst_video_format_to_wl_tbm_format (GstVideoFormat format) +{ + guint i; + for (i = 0; i < G_N_ELEMENTS (tbm_formats); i++) + if (tbm_formats[i].gst_format == format) + return tbm_formats[i].wl_format; + GST_WARNING ("wayland tbm video format not found"); + return -1; +} + +GstVideoFormat +gst_wl_tbm_format_to_video_format (uint32_t wl_format) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (tbm_formats); i++) + if (tbm_formats[i].wl_format == wl_format) + return tbm_formats[i].gst_format; + + GST_WARNING ("gst video format not found"); + return GST_VIDEO_FORMAT_UNKNOWN; +} + +const gchar * +gst_wl_tbm_format_to_string (uint32_t wl_format) +{ + return gst_video_format_to_string + (gst_wl_tbm_format_to_video_format (wl_format)); +} +#endif diff --git a/tizenwlsink/src/tizen-wlvideoformat.h b/tizenwlsink/src/tizen-wlvideoformat.h new file mode 100755 index 0000000..711c154 --- /dev/null +++ b/tizenwlsink/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 TIZEN_FEATURE_WLSINK_ENHANCEMENT +#include + +G_BEGIN_DECLS + uint32_t gst_video_format_to_wl_tbm_format (GstVideoFormat format); +GstVideoFormat gst_wl_tbm_format_to_video_format (uint32_t wl_format); + +const gchar *gst_wl_tbm_format_to_string (uint32_t wl_format); + +G_END_DECLS +#endif +#endif diff --git a/tizenwlsink/src/wlbuffer.c b/tizenwlsink/src/wlbuffer.c new file mode 100644 index 0000000..c37e61e --- /dev/null +++ b/tizenwlsink/src/wlbuffer.c @@ -0,0 +1,327 @@ +/* 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. + */ + +/* GstWlBuffer wraps wl_buffer and provides a mechanism for preventing + * buffers from being re-used while the compositor is using them. This + * is achieved by adding a reference to the GstBuffer as soon as its + * associated wl_buffer is sent to the compositor and by removing this + * reference as soon as the compositor sends a wl_buffer::release message. + * + * This mechanism is a bit complicated, though, because it adds cyclic + * references that can be dangerous. The reference cycles looks like: + * + * ---------------- + * | GstWlDisplay | ----------------------------> + * ---------------- | + * | + * V + * ----------------- ------------- --------------- + * | GstBufferPool | --> | GstBuffer | ==> | GstWlBuffer | + * | | <-- | | <-- | | + * ----------------- ------------- --------------- + * + * A GstBufferPool normally holds references to its GstBuffers and each buffer + * holds a reference to a GstWlBuffer (saved in the GstMiniObject qdata). + * When a GstBuffer is in use, it holds a reference back to the pool and the + * pool doesn't hold a reference to the GstBuffer. When the GstBuffer is unrefed + * externally, it returns back to the pool and the pool holds again a reference + * to the buffer. + * + * Now when the compositor is using a buffer, the GstWlBuffer also holds a ref + * to the GstBuffer, which prevents it from returning to the pool. When the + * last GstWlBuffer receives a release event and unrefs the last GstBuffer, + * the GstBufferPool will be able to stop and if no-one is holding a strong + * ref to it, it will be destroyed. This will destroy the pool's GstBuffers and + * also the GstWlBuffers. This will all happen in the same context of the last + * gst_buffer_unref, which will be called from the buffer_release() callback. + * + * The problem here lies in the fact that buffer_release() will be called + * from the event loop thread of GstWlDisplay, so it's as if the display + * holds a reference to the GstWlBuffer, but without having an actual reference. + * When we kill the display, there is no way for the GstWlBuffer, the associated + * GstBuffer and the GstBufferPool to get destroyed, so we are going to leak a + * fair ammount of memory. + * + * Normally, this rarely happens, because the compositor releases buffers + * almost immediately and when waylandsink stops, they are already released. + * + * However, we want to be absolutely certain, so a solution is introduced + * by registering all the GstWlBuffers with the display and explicitly + * releasing all the buffer references as soon as the display is destroyed. + * + * When the GstWlDisplay is finalized, it takes a reference to all the + * registered GstWlBuffers and then calls gst_wl_buffer_force_release_and_unref, + * which releases the potential reference to the GstBuffer, destroys the + * underlying wl_buffer and removes the reference that GstWlDisplay is holding. + * At that point, either the GstBuffer is alive somewhere and still holds a ref + * to the GstWlBuffer, which it will release when it gets destroyed, or the + * GstBuffer was destroyed in the meantime and the GstWlBuffer gets destroyed + * as soon as we remove the reference that GstWlDisplay holds. + */ + +#include "wlbuffer.h" + +GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_debug + +G_DEFINE_TYPE (GstWlBuffer, gst_wl_buffer, G_TYPE_OBJECT); + +static G_DEFINE_QUARK (GstWlBufferQDataQuark, gst_wl_buffer_qdata); + +static void +gst_wl_buffer_dispose (GObject * gobject) +{ + GstWlBuffer *self = GST_WL_BUFFER (gobject); + FUNCTION; + GST_LOG ("GstWlBuffer:%p", self); + GST_TRACE_OBJECT (self, "dispose"); + + /* if the display is shutting down and we are trying to dipose + * the GstWlBuffer from another thread, unregister_buffer() will + * block and in the end the display will increase the refcount + * of this GstWlBuffer, so it will not be finalized */ + if (self->display) + gst_wl_display_unregister_buffer (self->display, self); + + G_OBJECT_CLASS (gst_wl_buffer_parent_class)->dispose (gobject); +} + +static void +gst_wl_buffer_finalize (GObject * gobject) +{ + GstWlBuffer *self = GST_WL_BUFFER (gobject); + int i; + + FUNCTION; + + GST_TRACE_OBJECT (self, "finalize"); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (self->tsurface) { + GST_LOG ("tbm_surface_destroy (tbm_surface_h@%p)", self->tsurface); + tbm_surface_destroy (self->tsurface); + } +#endif + if (self->wlbuffer) { + GST_INFO ("wl_buffer_destroy (wl_buffer@%p)", self->wlbuffer); + wl_buffer_destroy (self->wlbuffer); + } +#ifdef USE_WL_FLUSH_BUFFER + if (self->display) { + if (self->is_flush_request) { + self->display->flush_tbm_bufmgr = NULL; + if (self->display->flush_buffer) { + for (i = 0; i < self->display->tbm_bo_num; i++) { + if (self->display->flush_buffer->bo[i]) { + GST_LOG ("flush buffer: tbm_bo_unref (bo@%p)", + self->display->flush_buffer->bo[i]); + tbm_bo_unref (self->display->flush_buffer->bo[i]); + self->display->flush_buffer->bo[i] = NULL; + } + } + g_free (self->display->flush_buffer); + self->display->flush_buffer = NULL; + } + } + } +#endif + + G_OBJECT_CLASS (gst_wl_buffer_parent_class)->finalize (gobject); +} + +static void +gst_wl_buffer_class_init (GstWlBufferClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + FUNCTION; + object_class->dispose = gst_wl_buffer_dispose; + object_class->finalize = gst_wl_buffer_finalize; +} + +static void +gst_wl_buffer_init (GstWlBuffer * self) +{ +} + +static void +buffer_release (void *data, struct wl_buffer *wl_buffer) +{ + GstWlBuffer *self = data; + FUNCTION; + g_return_if_fail (self != NULL); + + GST_INFO_OBJECT (self, + "get event : wl_buffer@%p ::release GstBuffer@%p:: tsurface@%p", + wl_buffer, self->gstbuffer, self->tsurface); + + self->used_by_compositor = FALSE; + +#ifdef USE_WL_FLUSH_BUFFER + /* unref should be last, because it may end up destroying the GstWlBuffer */ + if (!self->is_flush_request) { + /*in case of is_flush_request, gstbuffer ref-count has already decreased. */ + GST_LOG_OBJECT (self, "gstbuffer(%p), ref_count(%d)", self->gstbuffer, + GST_OBJECT_REFCOUNT_VALUE (self->gstbuffer)); + gst_buffer_unref (self->gstbuffer); + if (self->gstbuffer) { + GST_LOG_OBJECT (self, + "buffer is our pool..so is kept by bufferpool :: gstbuffer(%p), ref_count(%d)", + self->gstbuffer, GST_OBJECT_REFCOUNT_VALUE (self->gstbuffer)); + } + } else { + /*we blocked below code at gstbuffer_disposed() */ + /* unref(GstWlBuffer), now gst_wl_buffer_dispose() will be called by below code */ + g_object_unref (self); + } +#else + gst_buffer_unref (self->gstbuffer); +#endif +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static void +gstbuffer_disposed (GstWlBuffer * self) +{ + FUNCTION; + g_assert (!self->used_by_compositor); + GST_INFO ("GstBuffer: %p", self->gstbuffer); + self->gstbuffer = NULL; + + GST_TRACE_OBJECT (self, "owning GstBuffer was finalized"); + + /* this will normally destroy the GstWlBuffer, unless the display is + * finalizing and it has taken an additional reference to it */ +#ifdef USE_WL_FLUSH_BUFFER + /* in case of normal routine, gstbuffer_disposed() is called by buffer_release() + but in case of is_flush_request, this func() is called when basesink unref gstbuffer. + buffer_release() is not called if we do 'g_object_unref (self)' */ + if (!self->is_flush_request) +#endif + g_object_unref (self); +} + +GstWlBuffer * +gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer, + GstWlDisplay * display) +{ + GstWlBuffer *self; + FUNCTION; + + self = g_object_new (GST_TYPE_WL_BUFFER, NULL); + self->gstbuffer = gstbuffer; + self->wlbuffer = wlbuffer; + self->display = display; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (display->tsurface) + self->tsurface = display->tsurface; + else + self->tsurface = NULL; + GST_LOG ("self->tsurface(%p)", self->tsurface); +#endif +#ifdef USE_WL_FLUSH_BUFFER + self->is_flush_request = FALSE; + if (display->flush_request) + self->is_flush_request = TRUE; +#endif + + gst_wl_display_register_buffer (self->display, self); //register GstWlBuffer + + GST_INFO + ("wl_buffer_add_listener (wl_buffer@%p, wl_buffer_listener@%p, GstWlBuffer@%p)", + self->wlbuffer, &buffer_listener, self); + wl_buffer_add_listener (self->wlbuffer, &buffer_listener, self); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT //need to contribute to upstream !! + wl_proxy_set_queue ((struct wl_proxy *) self->wlbuffer, self->display->queue); +#endif + /* called gstbuffer_disposed when the gstbuffer is disposed, + or the same gstbuffer is being overwritten by below api */ + gst_mini_object_set_qdata ((GstMiniObject *) gstbuffer, + gst_wl_buffer_qdata_quark (), self, (GDestroyNotify) gstbuffer_disposed); + GST_LOG ("GstWlBuffer (%p)", self); + return self; +} + +GstWlBuffer * +gst_buffer_get_wl_buffer (GstBuffer * gstbuffer) +{ + FUNCTION; + return gst_mini_object_get_qdata ((GstMiniObject *) gstbuffer, + gst_wl_buffer_qdata_quark ()); +} + +void +gst_wl_buffer_force_release_and_unref (GstWlBuffer * self) +{ + FUNCTION; + /* Force a buffer release. + * At this point, the GstWlDisplay has killed its event loop, + * so we don't need to worry about buffer_release() being called + * at the same time from the event loop thread */ + if (self->used_by_compositor) { + GST_DEBUG_OBJECT (self, "forcing wl_buffer::release (GstBuffer: %p)", + self->gstbuffer); + self->used_by_compositor = FALSE; + gst_buffer_unref (self->gstbuffer); + } + + /* Finalize this GstWlBuffer early. + * This method has been called as a result of the display shutting down, + * so we need to stop using any wayland resources and disconnect from + * the display. The GstWlBuffer stays alive, though, to avoid race + * conditions with the GstBuffer being destroyed from another thread. + * The last reference is either owned by the GstBuffer or by us and + * it will be released at the end of this function. */ + GST_TRACE_OBJECT (self, "finalizing early"); + wl_buffer_destroy (self->wlbuffer); + self->wlbuffer = NULL; + self->display = NULL; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GST_LOG ("self->tsurface(%p)", self->tsurface); + if (self->tsurface) + tbm_surface_destroy (self->tsurface); +#endif + /* remove the reference that the caller (GstWlDisplay) owns */ + g_object_unref (self); +} + +void +gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface) +{ + FUNCTION; + g_return_if_fail (self->used_by_compositor == FALSE); + GST_INFO ("wl_surface_attach (video_surface@%p, wl_buffer@%p, 0, 0)", surface, + self->wlbuffer); + wl_surface_attach (surface, self->wlbuffer, 0, 0); + + /* 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 re-used until the compositor releases it. */ +#ifdef USE_WL_FLUSH_BUFFER + /* in case of is_flush_request, we need to copy info and unref gstbuffer + so, we need not to increase ref count. */ + if (!self->is_flush_request) +#endif + gst_buffer_ref (self->gstbuffer); + GST_LOG_OBJECT (self, "gstbuffer(%p), ref_count(%d)", self->gstbuffer, + GST_OBJECT_REFCOUNT_VALUE (self->gstbuffer)); + self->used_by_compositor = TRUE; +} diff --git a/tizenwlsink/src/wlbuffer.h b/tizenwlsink/src/wlbuffer.h new file mode 100644 index 0000000..d9713eb --- /dev/null +++ b/tizenwlsink/src/wlbuffer.h @@ -0,0 +1,69 @@ +/* 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_BUFFER_H__ +#define __GST_WL_BUFFER_H__ + +#include "wldisplay.h" + +G_BEGIN_DECLS +#define GST_TYPE_WL_BUFFER (gst_wl_buffer_get_type ()) +#define GST_WL_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_BUFFER, GstWlBuffer)) +#define GST_IS_WL_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_BUFFER)) +#define GST_WL_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_BUFFER, GstWlBufferClass)) +#define GST_IS_WL_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_BUFFER)) +#define GST_WL_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_BUFFER, GstWlBufferClass)) +typedef struct _GstWlBuffer GstWlBuffer; +typedef struct _GstWlBufferClass GstWlBufferClass; + +struct _GstWlBuffer +{ + GObject parent_instance; + + struct wl_buffer *wlbuffer; + GstBuffer *gstbuffer; + + GstWlDisplay *display; + + gboolean used_by_compositor; + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + gboolean is_flush_request; + tbm_surface_h tsurface; +#endif +}; + +struct _GstWlBufferClass +{ + GObjectClass parent_class; +}; + +GType gst_wl_buffer_get_type (void); + +GstWlBuffer *gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, + struct wl_buffer *wlbuffer, GstWlDisplay * display); +GstWlBuffer *gst_buffer_get_wl_buffer (GstBuffer * gstbuffer); + +void gst_wl_buffer_force_release_and_unref (GstWlBuffer * self); + +void gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface); + +G_END_DECLS +#endif /* __GST_WL_BUFFER_H__ */ diff --git a/tizenwlsink/src/wldisplay.c b/tizenwlsink/src/wldisplay.c new file mode 100644 index 0000000..7e9e39e --- /dev/null +++ b/tizenwlsink/src/wldisplay.c @@ -0,0 +1,468 @@ +/* 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 "wlbuffer.h" + +#include +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +#include +#include +#include +#include +#include + +#define TBM_BO_MAX_IDX 6 + +static void +handle_tizen_video_format (void *data, struct tizen_video *tizen_video, + uint32_t format) +{ + GstWlDisplay *self = data; + FUNCTION; + + g_return_if_fail (self != NULL); + + GST_LOG ("format is %d", format); + g_array_append_val (self->tbm_formats, format); +} + +static const struct tizen_video_listener tizen_video_listener = { + handle_tizen_video_format +}; +#endif + +GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_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) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + FUNCTION; + gobject_class->finalize = gst_wl_display_finalize; +} + +static void +gst_wl_display_init (GstWlDisplay * self) +{ + FUNCTION; + self->formats = g_array_new (FALSE, FALSE, sizeof (uint32_t)); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + self->tbm_formats = g_array_new (FALSE, FALSE, sizeof (uint32_t)); +#endif + self->wl_fd_poll = gst_poll_new (TRUE); + self->buffers = g_hash_table_new (g_direct_hash, g_direct_equal); + g_mutex_init (&self->buffers_mutex); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + self->tbm_bo_idx = 0; + self->wl_surface_id = -1; + self->dump_count = 1; +#endif +} + +static void +gst_wl_display_finalize (GObject * gobject) +{ + GstWlDisplay *self = GST_WL_DISPLAY (gobject); + FUNCTION; + + gst_poll_set_flushing (self->wl_fd_poll, TRUE); + g_thread_join (self->thread); + + /* to avoid buffers being unregistered from another thread + * at the same time, take their ownership */ + g_mutex_lock (&self->buffers_mutex); + self->shutting_down = TRUE; + g_hash_table_foreach (self->buffers, (GHFunc) g_object_ref, NULL); + g_mutex_unlock (&self->buffers_mutex); + + g_hash_table_foreach (self->buffers, + (GHFunc) gst_wl_buffer_force_release_and_unref, NULL); + g_hash_table_remove_all (self->buffers); + + g_array_unref (self->formats); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + g_array_unref (self->tbm_formats); +#endif + gst_poll_free (self->wl_fd_poll); + g_hash_table_unref (self->buffers); + g_mutex_clear (&self->buffers_mutex); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (self->USE_TBM) { + if (self->is_native_format == FALSE) { + /*in case of normal video format */ + for (int i = 0; i < TBM_BO_NUM; i++) { + if (self->tbm_bo[i]) + tbm_bo_unref (self->tbm_bo[i]); + self->tbm_bo[i] = NULL; + } + } + self->tbm_bufmgr = NULL; + } + if (self->tbm_client) { + wayland_tbm_client_deinit (self->tbm_client); + self->tbm_client = NULL; + } +#endif + if (self->shm) + wl_shm_destroy (self->shm); + + if (self->shell) + wl_shell_destroy (self->shell); + + if (self->scaler) + wl_scaler_destroy (self->scaler); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (self->tizen_policy) + tizen_policy_destroy (self->tizen_policy); + + if (self->tizen_video) + tizen_video_destroy (self->tizen_video); +#endif + + 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); + } + + G_OBJECT_CLASS (gst_wl_display_parent_class)->finalize (gobject); +} + +static void +sync_callback (void *data, struct wl_callback *callback, uint32_t serial) +{ + gboolean *done = data; + *done = TRUE; +} + +static const struct wl_callback_listener sync_listener = { + sync_callback +}; + +static gint +gst_wl_display_roundtrip (GstWlDisplay * self) +{ + struct wl_callback *callback; + gint ret = 0; + gboolean done = FALSE; + FUNCTION; + + g_return_val_if_fail (self != NULL, -1); + + /* We don't own the display, process only our queue */ + callback = wl_display_sync (self->display); + GST_INFO ("wl_callback@%p = wl_display_sync (wl_display@%p)", callback, + self->display); + wl_callback_add_listener (callback, &sync_listener, &done); + GST_INFO + ("wl_callback_add_listener (wl_callback@%p, sync_listener@%p, done@%p)", + callback, &sync_listener, &done); + wl_proxy_set_queue ((struct wl_proxy *) callback, self->queue); + GST_INFO ("wl_proxy_set_queue (wl_callback@%p, wl_event_queue@%p)", callback, + self->queue); + while (ret != -1 && !done) { + ret = wl_display_dispatch_queue (self->display, self->queue); + GST_INFO + ("ret(%d) = wl_display_dispatch_queue (wl_display@%p, wl_event_queue@%p)", + ret, self->display, self->queue); + } + GST_INFO ("wl_callback_destroy (wl_callback@%p)", callback); + wl_callback_destroy (callback); + + return ret; +} + +static void +shm_format (void *data, struct wl_shm *wl_shm, uint32_t format) +{ + 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) +{ + GstWlDisplay *self = data; + FUNCTION; + + if (g_strcmp0 (interface, "wl_compositor") == 0) { + self->compositor = wl_registry_bind (registry, id, &wl_compositor_interface, + MIN (version, 4)); + GST_INFO + ("wl_compositor@%p = wl_registry_bind (wl_registry@%p, id@%d, wl_compositor_interface@%p, version@%d)", + self->compositor, registry, id, &wl_compositor_interface, MIN (version, + 4)); + } else if (g_strcmp0 (interface, "wl_subcompositor") == 0) { + self->subcompositor = + wl_registry_bind (registry, id, &wl_subcompositor_interface, 1); + GST_INFO + ("wl_subcompositor@%p = wl_registry_bind (wl_registry@%p, id@%d, wl_subcompositor_interface@%p, version@%d)", + self->subcompositor, 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); + GST_INFO + ("wl_shell@%p = wl_registry_bind (wl_registry@%p, id@%d, wl_shell_interface@%p, version@%d)", + self->shell, registry, id, &wl_shell_interface, 1); + } else if (g_strcmp0 (interface, "wl_shm") == 0) { + self->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1); + GST_INFO + ("wl_shm@%p = wl_registry_bind (wl_registry@%p, id@%d, wl_shm_interface@%p, version@%d)", + self->shm, registry, id, &wl_shm_interface, 1); + GST_INFO + ("wl_shm_add_listener (wl_shm@%p, wl_shm_listener@%p, GstWlDisplay@%p)", + self->shm, &shm_listener, self); + wl_shm_add_listener (self->shm, &shm_listener, self); + + + } else if (g_strcmp0 (interface, "wl_scaler") == 0) { + self->scaler = wl_registry_bind (registry, id, &wl_scaler_interface, 2); + GST_INFO + ("wl_scaler@%p = wl_registry_bind (wl_registry@%p, id@%d, wl_scaler_interface@%p, version@%d)", + self->scaler, registry, id, &wl_scaler_interface, 2); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + } else if (g_strcmp0 (interface, "tizen_policy") == 0) { + self->tizen_policy = + wl_registry_bind (registry, id, &tizen_policy_interface, 7); + GST_INFO + ("tizen_policy@%p = wl_registry_bind (wl_registry@%p, id@%d, tizen_policy_interface@%p, version@%d)", + self->tizen_policy, registry, id, &tizen_policy_interface, 7); + } 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 + ("tizen_video@%p = wl_registry_bind (wl_registry@%p, id@%d, tizen_video_interface@%p, version@%d)", + self->tizen_video, registry, id, &tizen_video_interface, version); + + GST_INFO + ("tizen_video_add_listener (tizen_video@%p, tizen_video_listener@%p, GstWlDisplay@%p)", + self->tizen_video, &tizen_video_listener, self); + tizen_video_add_listener (self->tizen_video, &tizen_video_listener, self); +#endif + } +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global +}; + +static gpointer +gst_wl_display_thread_run (gpointer data) +{ + GstWlDisplay *self = data; + GstPollFD pollfd = GST_POLL_FD_INIT; + FUNCTION; + + 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) { + GST_INFO + ("while (wl_display_prepare_read_queue (display@%p, queue@%p != 0)", + self->display, self->queue); + while (wl_display_prepare_read_queue (self->display, self->queue) != 0) { + GST_INFO ("wl_display_dispatch_queue_pending (display@%p, queue@%p)", + self->display, self->queue); + wl_display_dispatch_queue_pending (self->display, self->queue); + } + GST_INFO ("wl_display_flush (display@%p)", self->display); + wl_display_flush (self->display); + + if (gst_poll_wait (self->wl_fd_poll, GST_CLOCK_TIME_NONE) < 0) { + gboolean normal = (errno == EBUSY); + GST_INFO ("wl_display_cancel_read (display@%p)", self->display); + wl_display_cancel_read (self->display); + if (normal) + break; + else + goto error; + } else { + GST_INFO ("wl_display_read_events (display@%p)", self->display); + wl_display_read_events (self->display); + GST_INFO ("wl_display_dispatch_queue_pending (display@%p, queue@%p)", + self->display, self->queue); + 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) +{ + struct wl_display *display; + FUNCTION; + + display = wl_display_connect (name); + GST_INFO ("wl_display@%p = wl_display_connect (name@%p)", display, 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) +{ + GstWlDisplay *self; + GError *err = NULL; + gint i; + FUNCTION; + + 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); + GST_INFO ("wl_event_queue@%p = wl_display_create_queue (wl_display@%p)", + self->queue, self->display); + self->registry = wl_display_get_registry (self->display); + GST_INFO ("wl_registry@%p = wl_display_get_registry (wl_display@%p)", + self->registry, self->display); + wl_proxy_set_queue ((struct wl_proxy *) self->registry, self->queue); + GST_INFO ("wl_proxy_set_queue (wl_registry@%p, wl_event_queue@%p)", + self->registry, self->queue); + wl_registry_add_listener (self->registry, ®istry_listener, self); + GST_INFO + ("wl_registry_add_listener (wl_registry@%p, wl_registry_listener@%p, GstWlDisplay@%p)", + 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 TIZEN_FEATURE_WLSINK_ENHANCEMENT + VERIFY_INTERFACE_EXISTS (tizen_video, "tizen_video"); + + self->tbm_client = wayland_tbm_client_init (self->display); + GST_INFO ("tbm_client@%p = wayland_tbm_client_init (wl_display@%p)", + self->tbm_client, 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; + } + GST_LOG ("tbm_client(%p)", self->tbm_client); +#endif + VERIFY_INTERFACE_EXISTS (shm, "wl_shm"); + 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; +} + +void +gst_wl_display_register_buffer (GstWlDisplay * self, gpointer buf) +{ + FUNCTION; + g_assert (!self->shutting_down); + + GST_TRACE_OBJECT (self, "registering GstWlBuffer %p", buf); + + g_mutex_lock (&self->buffers_mutex); + g_hash_table_add (self->buffers, buf); + g_mutex_unlock (&self->buffers_mutex); +} + +void +gst_wl_display_unregister_buffer (GstWlDisplay * self, gpointer buf) +{ + FUNCTION; + GST_TRACE_OBJECT (self, "unregistering GstWlBuffer %p", buf); + + g_mutex_lock (&self->buffers_mutex); + if (G_LIKELY (!self->shutting_down)) + g_hash_table_remove (self->buffers, buf); + g_mutex_unlock (&self->buffers_mutex); +} diff --git a/tizenwlsink/src/wldisplay.h b/tizenwlsink/src/wldisplay.h new file mode 100644 index 0000000..c3e4481 --- /dev/null +++ b/tizenwlsink/src/wldisplay.h @@ -0,0 +1,141 @@ +/* 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 TIZEN_FEATURE_WLSINK_ENHANCEMENT +#include +#include +#include +#include +#define USE_WL_FLUSH_BUFFER +#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)) +#define FUNCTION GST_LOG ("") + +#ifdef USE_WL_FLUSH_BUFFER +typedef struct +{ + void *bo[GST_VIDEO_MAX_PLANES]; +} GstWlFlushBuffer; +#endif + +#define TBM_BO_NUM 20 + +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; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GArray *tbm_formats; +#endif + /* private */ + gboolean own_display; + GThread *thread; + GstPoll *wl_fd_poll; + + GMutex buffers_mutex; + GHashTable *buffers; + gboolean shutting_down; + +#ifdef TIZEN_FEATURE_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[TBM_BO_NUM]; + gint tbm_bo_idx; + tbm_surface_h tsurface; + gboolean USE_TBM; + +#ifdef USE_WL_FLUSH_BUFFER + GstWlFlushBuffer *flush_buffer; + tbm_bufmgr flush_tbm_bufmgr; + gint flush_request; +#endif + gboolean dump_video; + guint total_dump; + guint dump_count; + + gboolean is_native_format; /*SN12, ST12, SR32, S420 */ + gpointer bo[GST_VIDEO_MAX_PLANES]; + gint plane_size[GST_VIDEO_MAX_PLANES]; + gint stride_width[GST_VIDEO_MAX_PLANES]; + gint stride_height[GST_VIDEO_MAX_PLANES]; + gint width[GST_VIDEO_MAX_PLANES]; + gint height[GST_VIDEO_MAX_PLANES]; + gint native_video_size; + guint wl_surface_id; + gint buffer_width, buffer_height; + gint plane_num; + gint tbm_bo_num; +#endif + +#if 1 + gboolean need_shell_surface; + gboolean use_parent_wl_surface; +#endif +}; + +struct _GstWlDisplayClass +{ + GObjectClass parent_class; +}; + +typedef struct _GstWlDisplay GstWlDisplay; +typedef struct _GstWlDisplayClass GstWlDisplayClass; + +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); + +/* see wlbuffer.c for explanation */ +void gst_wl_display_register_buffer (GstWlDisplay * self, gpointer buf); +void gst_wl_display_unregister_buffer (GstWlDisplay * self, gpointer buf); + +G_END_DECLS +#endif /* __GST_WL_DISPLAY_H__ */ diff --git a/tizenwlsink/src/wlshmallocator.c b/tizenwlsink/src/wlshmallocator.c new file mode 100644 index 0000000..81e2cfd --- /dev/null +++ b/tizenwlsink/src/wlshmallocator.c @@ -0,0 +1,545 @@ +/* GStreamer Wayland video sink + * + * 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. + */ + +#include "wlshmallocator.h" +#include "wlvideoformat.h" +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +#include "tizen-wlvideoformat.h" +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_debug + +G_DEFINE_TYPE (GstWlShmAllocator, gst_wl_shm_allocator, GST_TYPE_ALLOCATOR); + +gint +gst_wl_fwrite_data (gchar * file, gpointer data, guint size) +{ + FILE *fp; + + fp = fopen (file, "wb"); + if (fp == NULL) + return -1; + + fwrite ((gchar *) data, sizeof (gchar), size, fp); + fclose (fp); + + return 0; +} + +static void +gst_wl_tbm_dump_normal_raw_video (gpointer bo, guint size, guint dump_count, + guint dump_total) +{ + tbm_bo_handle virtual_addr; + gint ret; + gchar file_name[128]; + gchar err_str[256]; + g_return_if_fail (bo != NULL); + + virtual_addr = tbm_bo_get_handle (bo, TBM_DEVICE_CPU); + if (!virtual_addr.ptr) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR ("get tbm bo handle failed: %s(%d)", err_str, errno); + return; + } + + snprintf (file_name, sizeof (file_name), "/tmp/WLSINK_OUT_DUMP_%2.2d.dump", + dump_count); + ret = gst_wl_fwrite_data (file_name, virtual_addr.ptr, size); + if (ret) { + GST_ERROR ("_write_rawdata() failed"); + } + +} + +static void +gst_wl_tbm_dump_native_raw_video (GstWlDisplay * display, guint dump_count) +{ + gchar file_name[128]; + gchar err_str[256]; + FILE *fp; + tbm_bo_handle virtual_addr; + gchar *data; + int i; + g_return_if_fail (display != NULL); + + if (dump_count > display->total_dump) { + display->dump_video = FALSE; + return; + } + /* get virtual addr with bo and TBM_DEVICD_CPU */ + virtual_addr = tbm_bo_get_handle (display->bo[0], TBM_DEVICE_CPU); + if (!virtual_addr.ptr) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR ("get tbm bo handle failed: %s(%d)", err_str, errno); + return; + } + + snprintf (file_name, sizeof (file_name), "/tmp/WLSINK_OUT_DUMP_%2.2d.dump", + dump_count); + + fp = fopen (file_name, "wb"); + if (fp == NULL) + return; + data = (gchar *) virtual_addr.ptr; + + /* Y */ + for (i = 0; i < display->height[0]; i++) { + fwrite (data, display->width[0], 1, fp); + data += display->stride_width[0]; + } + + if (display->bo[1] == NULL) { + /* sprd */ + data = (gchar *) virtual_addr.ptr + + (display->stride_width[0] * display->stride_height[0]); + GST_LOG ("UV: virtual_addr.ptr(%p)", data); + } else { + /* omx */ + virtual_addr = tbm_bo_get_handle (display->bo[1], TBM_DEVICE_CPU); + if (!virtual_addr.ptr) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR ("get tbm bo handle failed: %s(%d)", err_str, errno); + fclose (fp); + return; + } + data = (gchar *) virtual_addr.ptr; + } + + /* UV */ + for (i = 0; i < display->height[1]; i++) { + fwrite (data, display->width[1], 1, fp); + data += display->stride_width[1]; + } + + fclose (fp); +} + +static GstMemory * +gst_wl_shm_allocator_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + GstWlShmAllocator *self = GST_WL_SHM_ALLOCATOR (allocator); + char filename[1024]; + static int init = 0; + int fd; + int idx; + gpointer data; + GstWlShmMemory *mem; + gchar err_str[256]; + FUNCTION; + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (self->display->USE_TBM) { + tbm_bo_handle virtual_addr; + + idx = self->display->tbm_bo_idx++; + + self->display->tbm_bufmgr = + wayland_tbm_client_get_bufmgr (self->display->tbm_client); + g_return_val_if_fail (self->display->tbm_bufmgr != NULL, NULL); + + self->display->tbm_bo[idx] = + tbm_bo_alloc (self->display->tbm_bufmgr, size, TBM_BO_DEFAULT); + if (G_UNLIKELY (!self->display->tbm_bo[idx])) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR_OBJECT (self, "alloc tbm bo(size:%d) failed: %s(%d)", size, + err_str, errno); + return FALSE; + } + GST_LOG ("display->tbm_bo[%d]=(%p)", idx, self->display->tbm_bo[idx]); + virtual_addr.ptr = NULL; + virtual_addr = + tbm_bo_get_handle (self->display->tbm_bo[idx], TBM_DEVICE_CPU); + if (G_UNLIKELY (!virtual_addr.ptr)) { + strerror_r (errno, err_str, sizeof (err_str)); + GST_ERROR_OBJECT (self, "get tbm bo handle failed: %s(%d)", err_str, + errno); + tbm_bo_unref (self->display->tbm_bo[idx]); + self->display->tbm_bo[idx] = NULL; + self->display->tbm_bo_idx--; + return FALSE; + } + + mem = g_slice_new0 (GstWlShmMemory); + gst_memory_init ((GstMemory *) mem, GST_MEMORY_FLAG_NO_SHARE, allocator, + NULL, size, 0, 0, size); + mem->data = virtual_addr.ptr; + mem->tbm_bo_ptr = self->display->tbm_bo[idx]; + GST_LOG ("mem(%p) mem->data(%p) virtual_addr.ptr(%p) size(%d)", mem, + mem->data, virtual_addr.ptr, size); + + return (GstMemory *) mem; + + } else { /* USE SHM */ + /* TODO: make use of the allocation params, if necessary */ + + /* allocate shm pool */ + snprintf (filename, 1024, "%s/%s-%d-%s", g_get_user_runtime_dir (), + "wayland-shm", init++, "XXXXXX"); + GST_LOG ("opening temp file %s", filename); + + fd = g_mkstemp (filename); + if (fd < 0) { + GST_ERROR_OBJECT (self, "opening temp file %s failed: %s", filename, + strerror (errno)); + return NULL; + } + if (ftruncate (fd, size) < 0) { + GST_ERROR_OBJECT (self, "ftruncate failed: %s", strerror (errno)); + close (fd); + return NULL; + } + + data = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + GST_ERROR_OBJECT (self, "mmap failed: %s", strerror (errno)); + close (fd); + return NULL; + } + + unlink (filename); + + mem = g_slice_new0 (GstWlShmMemory); + gst_memory_init ((GstMemory *) mem, GST_MEMORY_FLAG_NO_SHARE, allocator, + NULL, size, 0, 0, size); + mem->data = data; + mem->fd = fd; + + return (GstMemory *) mem; + } + +#else /* open source */ + /* TODO: make use of the allocation params, if necessary */ + + /* allocate shm pool */ + snprintf (filename, 1024, "%s/%s-%d-%s", g_get_user_runtime_dir (), + "wayland-shm", init++, "XXXXXX"); + + fd = g_mkstemp (filename); + if (fd < 0) { + GST_ERROR_OBJECT (self, "opening temp file %s failed: %s", filename, + strerror (errno)); + return NULL; + } + if (ftruncate (fd, size) < 0) { + GST_ERROR_OBJECT (self, "ftruncate failed: %s", strerror (errno)); + close (fd); + return NULL; + } + + data = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + GST_ERROR_OBJECT (self, "mmap failed: %s", strerror (errno)); + close (fd); + return NULL; + } + + unlink (filename); + + mem = g_slice_new0 (GstWlShmMemory); + gst_memory_init ((GstMemory *) mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL, + size, 0, 0, size); + mem->data = data; + mem->fd = fd; + + return (GstMemory *) mem; +#endif +} + +static void +gst_wl_shm_allocator_free (GstAllocator * allocator, GstMemory * memory) +{ + GstWlShmMemory *shm_mem = (GstWlShmMemory *) memory; + FUNCTION; + GST_LOG ("shm_mem->fd(%d)", shm_mem->fd); + if (shm_mem->fd != -1) + close (shm_mem->fd); + munmap (shm_mem->data, memory->maxsize); + + g_slice_free (GstWlShmMemory, shm_mem); +} + +static gpointer +gst_wl_shm_mem_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) +{ + FUNCTION; + return ((GstWlShmMemory *) mem)->data; +} + +static void +gst_wl_shm_mem_unmap (GstMemory * mem) +{ +} + +static void +gst_wl_shm_allocator_class_init (GstWlShmAllocatorClass * klass) +{ + GstAllocatorClass *alloc_class = (GstAllocatorClass *) klass; + FUNCTION; + + alloc_class->alloc = GST_DEBUG_FUNCPTR (gst_wl_shm_allocator_alloc); + alloc_class->free = GST_DEBUG_FUNCPTR (gst_wl_shm_allocator_free); +} + +static void +gst_wl_shm_allocator_init (GstWlShmAllocator * self) +{ + FUNCTION; + self->parent_instance.mem_type = GST_ALLOCATOR_WL_SHM; + self->parent_instance.mem_map = gst_wl_shm_mem_map; + self->parent_instance.mem_unmap = gst_wl_shm_mem_unmap; + + GST_OBJECT_FLAG_SET (self, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +void +gst_wl_shm_allocator_register (void) +{ + FUNCTION; + gst_allocator_register (GST_ALLOCATOR_WL_SHM, + g_object_new (GST_TYPE_WL_SHM_ALLOCATOR, NULL)); +} + +GstAllocator * +gst_wl_shm_allocator_get (void) +{ + FUNCTION; + return gst_allocator_find (GST_ALLOCATOR_WL_SHM); +} + +gboolean +gst_is_wl_shm_memory (GstMemory * mem) +{ + FUNCTION; + return gst_memory_is_type (mem, GST_ALLOCATOR_WL_SHM); +} + +gboolean +gst_is_wl_memory (GstMemory * mem) +{ + FUNCTION; + return gst_memory_is_type (mem, GST_ALLOCATOR_WL_SHM); +} + +struct wl_buffer * +gst_wl_shm_memory_construct_wl_buffer (GstMemory * mem, GstWlDisplay * display, + const GstVideoInfo * info) +{ + GstWlShmMemory *shm_mem = (GstWlShmMemory *) mem; + gint width, height, stride; + gsize size; + gint plane_size[GST_VIDEO_MAX_PLANES], n_planes, i; + enum wl_shm_format format; + struct wl_shm_pool *wl_pool; + struct wl_buffer *wbuffer; + FUNCTION; + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (display->USE_TBM) { + tbm_surface_info_s ts_info; + int offset[GST_VIDEO_MAX_PLANES]; + + if (display->is_native_format == TRUE) { + /* In case of native format, use MMVideoBuffer data instead of GstVideoInfo */ + if (display->dump_video) + gst_wl_tbm_dump_native_raw_video (display, display->dump_count++); + + width = display->width[0]; + height = display->height[0]; + format = gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (info)); + ts_info.width = width; + ts_info.height = height; + ts_info.format = format; + ts_info.bpp = tbm_surface_internal_get_bpp (ts_info.format); + ts_info.num_planes = tbm_surface_internal_get_num_planes (ts_info.format); + + for (i = 0; i < display->plane_num; i++) { + ts_info.planes[i].size = display->plane_size[i]; + ts_info.planes[i].stride = display->stride_width[i]; + offset[i] = display->stride_width[i] * display->stride_height[i]; + } + + if (display->tbm_bo_num == 1) { + ts_info.planes[0].offset = 0; + ts_info.planes[1].offset = offset[0]; + if (display->plane_num == 3) + ts_info.planes[2].offset = offset[0] + offset[1]; + } else if (display->tbm_bo_num == 2) { + ts_info.planes[0].offset = 0; + ts_info.planes[1].offset = 0; + if (display->plane_num == 3) + ts_info.planes[2].offset = offset[1]; + } else if (display->tbm_bo_num == 3) { + ts_info.planes[0].offset = 0; + ts_info.planes[1].offset = 0; + ts_info.planes[2].offset = 0; + } + + GST_LOG + ("set tbm_surface_info_s: width(%d) height(%d) format(%s) bpp(%d) num_planes(%d)", + ts_info.width, ts_info.height, gst_wl_tbm_format_to_string (format), + ts_info.bpp, ts_info.num_planes); + GST_LOG + ("set tbm_surface_info_s: planse[0].stride(%d) planes[1].stride(%d) planes[2].stride(%d) planes[0].offset(%d) planes[1].offset(%d) planes[2].offset(%d)", + ts_info.planes[0].stride, ts_info.planes[1].stride, + ts_info.planes[2].stride, ts_info.planes[0].offset, + ts_info.planes[1].offset, ts_info.planes[2].offset); + + display->tsurface = + tbm_surface_internal_create_with_bos (&ts_info, + (tbm_bo *) display->bo, display->tbm_bo_num); + GST_LOG ("create tbm surface(%p)", display->tsurface); + wbuffer = + wayland_tbm_client_create_buffer (display->tbm_client, + display->tsurface); + } else { + + width = GST_VIDEO_INFO_WIDTH (info); + height = GST_VIDEO_INFO_HEIGHT (info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (info, 0); + size = GST_VIDEO_INFO_SIZE (info); + + format = gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (info)); + g_return_val_if_fail (gst_is_wl_memory (mem), NULL); + g_return_val_if_fail (size <= mem->size, NULL); + g_return_val_if_fail (shm_mem->fd != -1, NULL); + + GST_DEBUG_OBJECT (mem->allocator, "Creating wl_buffer of size %" + G_GSSIZE_FORMAT " (%d x %d, stride %d)", size, width, height, stride); + + if (display->dump_video) { + gst_wl_tbm_dump_normal_raw_video (shm_mem->tbm_bo_ptr, size, + display->dump_count++, display->total_dump); + if (display->dump_count > display->total_dump) + display->dump_video = FALSE; + } + + n_planes = GST_VIDEO_INFO_N_PLANES (info); + switch (n_planes) { + case 1: + plane_size[0] = info->size; + break; + case 2: + plane_size[0] = GST_VIDEO_INFO_PLANE_OFFSET (info, 1); + plane_size[1] = info->size - plane_size[0]; + break; + case 3: + plane_size[0] = GST_VIDEO_INFO_PLANE_OFFSET (info, 1); + plane_size[1] = GST_VIDEO_INFO_PLANE_OFFSET (info, 2) - plane_size[0]; + plane_size[2] = info->size - GST_VIDEO_INFO_PLANE_OFFSET (info, 2); + break; + case 4: + plane_size[0] = GST_VIDEO_INFO_PLANE_OFFSET (info, 1); + plane_size[1] = GST_VIDEO_INFO_PLANE_OFFSET (info, 2) - plane_size[0]; + plane_size[2] = GST_VIDEO_INFO_PLANE_OFFSET (info, 3) + - GST_VIDEO_INFO_PLANE_OFFSET (info, 2); + plane_size[3] = info->size - GST_VIDEO_INFO_PLANE_OFFSET (info, 3); + break; + + default: + break; + } + + ts_info.width = width; + ts_info.height = height; + ts_info.format = format; + ts_info.bpp = tbm_surface_internal_get_bpp (ts_info.format); + ts_info.num_planes = tbm_surface_internal_get_num_planes (ts_info.format); + + for (i = 0; i < n_planes; i++) { + ts_info.planes[i].stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i); + ts_info.planes[i].offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); + ts_info.planes[i].size = plane_size[i]; + } + + GST_LOG ("tbm_bo (%p)", shm_mem->tbm_bo_ptr); + + display->tsurface = + tbm_surface_internal_create_with_bos (&ts_info, + (tbm_bo *) & shm_mem->tbm_bo_ptr, 1); + wbuffer = + wayland_tbm_client_create_buffer (display->tbm_client, + display->tsurface); + } + GST_LOG ("create wbuffer(%p)", wbuffer); + + } else { /* USE SHM */ + width = GST_VIDEO_INFO_WIDTH (info); + height = GST_VIDEO_INFO_HEIGHT (info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (info, 0); + size = GST_VIDEO_INFO_SIZE (info); + format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (info)); + g_return_val_if_fail (gst_is_wl_memory (mem), NULL); + g_return_val_if_fail (size <= mem->size, NULL); + g_return_val_if_fail (shm_mem->fd != -1, NULL); + + GST_DEBUG_OBJECT (mem->allocator, "Creating wl_buffer of size %" + G_GSSIZE_FORMAT " (%d x %d, stride %d), format %s", size, width, height, + stride, gst_wl_shm_format_to_string (format)); + + wl_pool = wl_shm_create_pool (display->shm, shm_mem->fd, mem->size); + wbuffer = + wl_shm_pool_create_buffer (wl_pool, 0, width, height, stride, format); + + wl_shm_pool_destroy (wl_pool); + } + display->buffer_width = width; + display->buffer_height = height; + GST_LOG ("buffer_width(%d) buffer_height(%d)", display->buffer_width, + display->buffer_height); + return wbuffer; + +#else /* open source */ + width = GST_VIDEO_INFO_WIDTH (info); + height = GST_VIDEO_INFO_HEIGHT (info); + stride = GST_VIDEO_INFO_PLANE_STRIDE (info, 0); + size = GST_VIDEO_INFO_SIZE (info); + format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (info)); + g_return_val_if_fail (gst_is_wl_shm_memory (mem), NULL); + g_return_val_if_fail (size <= mem->size, NULL); + g_return_val_if_fail (shm_mem->fd != -1, NULL); + + GST_DEBUG_OBJECT (mem->allocator, "Creating wl_buffer of size %" + G_GSSIZE_FORMAT " (%d x %d, stride %d), format %s", size, width, height, + stride, gst_wl_shm_format_to_string (format)); + + wl_pool = wl_shm_create_pool (display->shm, shm_mem->fd, mem->size); + wbuffer = wl_shm_pool_create_buffer (wl_pool, 0, width, height, stride, + format); + + close (shm_mem->fd); + shm_mem->fd = -1; + wl_shm_pool_destroy (wl_pool); + + return wbuffer; +#endif +} diff --git a/tizenwlsink/src/wlshmallocator.h b/tizenwlsink/src/wlshmallocator.h new file mode 100644 index 0000000..9f111d3 --- /dev/null +++ b/tizenwlsink/src/wlshmallocator.h @@ -0,0 +1,79 @@ +/* GStreamer Wayland video sink + * + * 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_WL_SHM_ALLOCATOR_H__ +#define __GST_WL_SHM_ALLOCATOR_H__ + +#include +#include "wldisplay.h" + +G_BEGIN_DECLS +#define GST_TYPE_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get_type ()) +#define GST_WL_SHM_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_SHM_ALLOCATOR, GstWlShmAllocator)) +#define GST_IS_WL_SHM_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_SHM_ALLOCATOR)) +#define GST_WL_SHM_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_SHM_ALLOCATOR, GstWlShmAllocatorClass)) +#define GST_IS_WL_SHM_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_SHM_ALLOCATOR)) +#define GST_WL_SHM_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_SHM_ALLOCATOR, GstWlShmAllocatorClass)) +#define GST_ALLOCATOR_WL_SHM "wl_shm" +typedef struct _GstWlShmMemory GstWlShmMemory; +typedef struct _GstWlShmAllocator GstWlShmAllocator; +typedef struct _GstWlShmAllocatorClass GstWlShmAllocatorClass; + +struct _GstWlShmMemory +{ + GstMemory parent; + + gpointer data; + gint fd; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + gpointer tbm_bo_ptr; +#endif +}; + +struct _GstWlShmAllocator +{ + GstAllocator parent_instance; +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GstWlDisplay *display; +#endif +}; + +struct _GstWlShmAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +GType gst_wl_shm_allocator_get_type (void); + +void gst_wl_shm_allocator_register (void); +GstAllocator *gst_wl_shm_allocator_get (void); + +gboolean gst_is_wl_shm_memory (GstMemory * mem); +struct wl_buffer *gst_wl_shm_memory_construct_wl_buffer (GstMemory * mem, + GstWlDisplay * display, const GstVideoInfo * info); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +gint gst_wl_fwrite_data (gchar * file, gpointer data, guint size); +gboolean gst_is_wl_memory (GstMemory * mem); +#endif +G_END_DECLS +#endif /* __GST_WL_SHM_ALLOCATOR_H__ */ diff --git a/tizenwlsink/src/wlvideoformat.c b/tizenwlsink/src/wlvideoformat.c new file mode 100644 index 0000000..59e9c95 --- /dev/null +++ b/tizenwlsink/src/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 "wlvideoformat.h" + +GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_debug +#define FUNCTION GST_LOG ("") +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_wl_shm_format (GstVideoFormat format) +{ + guint i; + FUNCTION; + + for (i = 0; i < G_N_ELEMENTS (formats); i++) + if (formats[i].gst_format == format) + return formats[i].wl_format; + + GST_WARNING ("wayland shm video format not found"); + return -1; +} + +GstVideoFormat +gst_wl_shm_format_to_video_format (enum wl_shm_format wl_format) +{ + guint i; + FUNCTION; + + 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_wl_shm_format_to_string (enum wl_shm_format wl_format) +{ + FUNCTION; + return gst_video_format_to_string + (gst_wl_shm_format_to_video_format (wl_format)); +} diff --git a/tizenwlsink/src/wlvideoformat.h b/tizenwlsink/src/wlvideoformat.h new file mode 100644 index 0000000..7377cd1 --- /dev/null +++ b/tizenwlsink/src/wlvideoformat.h @@ -0,0 +1,37 @@ +/* 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 + +G_BEGIN_DECLS + enum wl_shm_format gst_video_format_to_wl_shm_format (GstVideoFormat + format); +GstVideoFormat gst_wl_shm_format_to_video_format (enum wl_shm_format wl_format); + +const gchar *gst_wl_shm_format_to_string (enum wl_shm_format wl_format); + +G_END_DECLS +#endif diff --git a/tizenwlsink/src/wlwindow.c b/tizenwlsink/src/wlwindow.c new file mode 100644 index 0000000..c8d8366 --- /dev/null +++ b/tizenwlsink/src/wlwindow.c @@ -0,0 +1,1103 @@ +/* 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 +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +#include "gstwaylandsink.h" +#else +#include "wlwindow.h" +#endif +#include "wlshmallocator.h" +#include "wlbuffer.h" + +GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); +#define GST_CAT_DEFAULT gstwayland_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) +{ + 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) +{ +} + +static void +handle_popup_done (void *data, struct wl_shell_surface *shell_surface) +{ +} + +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) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + FUNCTION; + gobject_class->finalize = gst_wl_window_finalize; +} + +static void +gst_wl_window_init (GstWlWindow * self) +{ + g_return_if_fail (self != NULL); + self->buffer_width = self->buffer_height = 0; + self->buffer_x = self->buffer_y = 0; + self->roi.x = self->roi.y = self->roi.w = self->roi.h = 0; + self->flip.changed = FALSE; +} + +static void +gst_wl_window_finalize (GObject * gobject) +{ + GstWlWindow *self = GST_WL_WINDOW (gobject); + FUNCTION; + + if (self->shell_surface) { + wl_shell_surface_destroy (self->shell_surface); + } +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (self->video_object) + tizen_video_object_destroy (self->video_object); + if (self->tizen_area_viewport) + tizen_viewport_destroy (self->tizen_area_viewport); + if (self->tizen_video_viewport) + tizen_viewport_destroy (self->tizen_video_viewport); + if (self->tizen_video_dest_mode) + tizen_destination_mode_destroy (self->tizen_video_dest_mode); + if (self->tizen_area_dest_mode) + tizen_destination_mode_destroy (self->tizen_area_dest_mode); +#else + wl_viewport_destroy (self->video_viewport); + wl_viewport_destroy (self->area_viewport); +#endif + wl_subsurface_destroy (self->video_subsurface); + wl_surface_destroy (self->video_surface); + + if (self->area_subsurface) { + wl_subsurface_destroy (self->area_subsurface); + } + + wl_surface_destroy (self->area_surface); + g_clear_object (&self->display); + + G_OBJECT_CLASS (gst_wl_window_parent_class)->finalize (gobject); +} + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +static void +gst_wl_window_map_sub_surface (GstWlDisplay * display, GstWlWindow * window, + GstVideoInfo * info) +{ + /* A sub-surface becomes mapped, when a non-NULL wl_buffer is applied + * and the parent surface is mapped */ + GstBuffer *buf; + GstMapInfo mapinfo; + struct wl_buffer *wlbuf; + GstWlBuffer *gwlbuf; + GstWlShmAllocator *self = NULL; + FUNCTION; + + g_return_if_fail (display != NULL); + g_return_if_fail (window != NULL); + g_return_if_fail (info != NULL); + + self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ()); + self->display = display; + + buf = gst_buffer_new_allocate (gst_wl_shm_allocator_get (), info->size, NULL); + gst_buffer_map (buf, &mapinfo, GST_MAP_WRITE); + *((guint32 *) mapinfo.data) = 0; /* paint it black */ + gst_buffer_unmap (buf, &mapinfo); + wlbuf = + gst_wl_shm_memory_construct_wl_buffer (gst_buffer_peek_memory (buf, 0), + display, info); + + gwlbuf = gst_buffer_add_wl_buffer (buf, wlbuf, display); + gst_wl_buffer_attach (gwlbuf, window->area_surface); + + /* for tizen view port + When change area_surface, we don't need to commit anymore if we do below code. + such as gst_wl_window_set_render_rectangle() and */ + GST_INFO + ("wl_surface_damage_buffer (area_surface(wl_surface)@%p, x@%d, y@%d, w@%d, h@%d)", + window->area_surface, 0, 0, info->width, info->height); + wl_surface_damage_buffer (window->area_surface, 0, 0, info->width, + info->height); + GST_INFO ("wl_surface_commit (area_surface(wl_surface)@%p)", + window->area_surface); + wl_surface_commit (window->area_surface); + + /* at this point, the GstWlBuffer keeps the buffer + * alive and will free it on wl_buffer::release */ + gst_buffer_unref (buf); +} +#endif + +static GstWlWindow * +#if 1 +/* for enlightment, we need to get parent to create area_subsurface */ +gst_wl_window_new_internal (GstWlDisplay * display, struct wl_surface *parent) +#else +gst_wl_window_new_internal (GstWlDisplay * display) +#endif +{ + GstWlWindow *window; + GstVideoInfo info; +#ifndef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GstBuffer *buf; + GstMapInfo mapinfo; + struct wl_buffer *wlbuf; + GstWlBuffer *gwlbuf; +#endif + struct wl_region *region; + FUNCTION; + + window = g_object_new (GST_TYPE_WL_WINDOW, NULL); + window->display = g_object_ref (display); + + window->area_surface = wl_compositor_create_surface (display->compositor); + GST_INFO + ("area_surface(wl_surface)@%p = wl_compositor_create_surface(wl_compositor@%p)", + window->area_surface, display->compositor); + window->video_surface = wl_compositor_create_surface (display->compositor); + GST_INFO + ("video_surface(wl_surface)@%p = wl_compositor_create_surface(wl_compositor@%p)", + window->video_surface, display->compositor); + + GST_INFO ("wl_proxy_set_queue (area_surface@%p, wl_event_queue@%p)", + window->area_surface, display->queue); + wl_proxy_set_queue ((struct wl_proxy *) window->area_surface, display->queue); + GST_INFO ("wl_proxy_set_queue (video_surface@%p, wl_event_queue@%p)", + window->video_surface, display->queue); + wl_proxy_set_queue ((struct wl_proxy *) window->video_surface, + display->queue); + +#if 1 /* create shell_surface here for enlightenment */ + /* go toplevel */ + if (display->need_shell_surface) { + /* for internal window */ + GST_INFO + ("wl_shell_surface@%p = wl_shell_get_shell_surface (wl_shell@%p, area_subsurface(wl_surface)@%p)", + window->shell_surface, display->shell, window->area_surface); + window->shell_surface = + wl_shell_get_shell_surface (display->shell, window->area_surface); + } else if (display->use_parent_wl_surface) { +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (display->wl_surface_id && parent == NULL) { + window->area_subsurface = + tizen_policy_get_subsurface (display->tizen_policy, + window->area_surface, display->wl_surface_id); + GST_INFO + ("area_subsurface(wl_subsurface)@%p = tizen_policy_get_subsurface(tizen_policy@%p, area_surface(wl_surface)@%p, wl_surface_id@%d)", + window->area_subsurface, display->tizen_policy, window->area_surface, + display->wl_surface_id); + GST_INFO ("wl_subsurface_set_desync (area_subsurface(wl_subsurface)@%p)", + window->area_subsurface); + wl_subsurface_set_desync (window->area_subsurface); + GST_INFO ("wl_surface_commit (%p)", window->area_surface); + wl_surface_commit (window->area_surface); + } else { + GST_INFO (" wl_surface parent %p", parent); + window->area_subsurface = + wl_subcompositor_get_subsurface (display->subcompositor, + window->area_surface, parent); + GST_INFO + ("area_subsurface(wl_subsurface)@%p = wl_subcompositor_get_subsurface(wl_subcompositor@%p, area_surface(wl_surface)@%p, parent@%p)", + window->area_subsurface, display->subcompositor, window->area_surface, + parent); + GST_INFO ("wl_subsurface_set_desync (area_subsurface(wl_subsurface)@%p)", + window->area_subsurface); + wl_subsurface_set_desync (window->area_subsurface); + } +#else + /*for enlightment , below code is moved */ + window->area_subsurface = + wl_subcompositor_get_subsurface (display->subcompositor, + window->area_surface, parent); + wl_subsurface_set_desync (window->area_subsurface); +#endif + } +#endif + + /* embed video_surface in area_surface */ + window->video_subsurface = + wl_subcompositor_get_subsurface (display->subcompositor, + window->video_surface, window->area_surface); + GST_INFO + ("video_subsurface(wl_subsurface)@%p = wl_subcompositor_get_subsurface(wl_subcompositor@%p, video_surface(wl_surface)@%p, area_surface(wl_surface)@%p)", + window->video_subsurface, display->subcompositor, window->video_surface, + window->area_surface); + + GST_INFO ("wl_subsurface_set_desync (video_subsurface(wl_subsurface)@%p)", + window->video_subsurface); + wl_subsurface_set_desync (window->video_subsurface); + GST_INFO ("wl_surface_commit (%p)", window->video_surface); + wl_surface_commit (window->video_surface); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + window->tizen_area_viewport = + tizen_video_get_viewport (display->tizen_video, window->area_surface); + GST_INFO + ("tizen_area_viewport(tizen_viewport)@%p = tizen_video_get_viewport(tizen_video@%p, area_surface(wl_surface)@%p)", + window->tizen_area_viewport, display->tizen_video, window->area_surface); + window->tizen_video_viewport = + tizen_video_get_viewport (display->tizen_video, window->video_surface); + GST_INFO + ("tizen_video_viewport(tizen_viewport)@%p = tizen_video_get_viewport(tizen_video@%p, video_surface(wl_surface)@%p)", + window->tizen_video_viewport, display->tizen_video, + window->video_surface); + window->tizen_video_dest_mode = + tizen_viewport_get_destination_mode (window->tizen_video_viewport); + GST_INFO + ("tizen_video_dest_mode(tizen_destination_mode)@%p = tizen_viewport_get_destination_mode (tizen_video_viewport@%p)", + window->tizen_video_dest_mode, window->tizen_video_viewport); + /* video surface always follow area surface by below code. */ + GST_INFO ("tizen_viewport_follow_parent_transform(tizen_video_viewport@%p)", + window->tizen_video_viewport); + tizen_viewport_follow_parent_transform (window->tizen_video_viewport); + +#else + window->area_viewport = wl_scaler_get_viewport (display->scaler, + window->area_surface); + window->video_viewport = wl_scaler_get_viewport (display->scaler, + window->video_surface); +#endif + /* draw the area_subsurface */ + gst_video_info_set_format (&info, + /* we want WL_SHM_FORMAT_XRGB8888 */ +#if G_BYTE_ORDER == G_BIG_ENDIAN + GST_VIDEO_FORMAT_xRGB, +#else + GST_VIDEO_FORMAT_BGRx, +#endif + 1, 1); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + if (window->display->USE_TBM) { + /* Inform enlightenment of surface which render video */ + /* tizen_video(tbm) render on video_surface */ + window->video_object = + tizen_video_get_object (display->tizen_video, window->video_surface); + + /* to use shm memory for mapping sub-surface, set FALSE to USE_TBM */ + window->display->USE_TBM = FALSE; + gst_wl_window_map_sub_surface (display, window, &info); + /*restore USE_TBM */ + window->display->USE_TBM = TRUE; + } else { + gst_wl_window_map_sub_surface (display, window, &info); + } +#else /* open source */ + buf = gst_buffer_new_allocate (gst_wl_shm_allocator_get (), info.size, NULL); + gst_buffer_map (buf, &mapinfo, GST_MAP_WRITE); + *((guint32 *) mapinfo.data) = 0; /* paint it black */ + gst_buffer_unmap (buf, &mapinfo); + wlbuf = + gst_wl_shm_memory_construct_wl_buffer (gst_buffer_peek_memory (buf, 0), + display, &info); + gwlbuf = gst_buffer_add_wl_buffer (buf, wlbuf, display); + gst_wl_buffer_attach (gwlbuf, window->area_surface); + + /* at this point, the GstWlBuffer keeps the buffer + * alive and will free it on wl_buffer::release */ + gst_buffer_unref (buf); +#endif + + /* do not accept input */ + region = wl_compositor_create_region (display->compositor); + GST_INFO ("wl_region@%p = wl_compositor_create_region (wl_compositor@%p)", + region, display->compositor); + GST_INFO ("wl_surface_set_input_region (area_surface@%p, wl_region@%p)", + window->area_surface, region); + wl_surface_set_input_region (window->area_surface, region); + GST_INFO ("wl_region_destroy (wl_region@%p)", region); + wl_region_destroy (region); + + region = wl_compositor_create_region (display->compositor); + GST_INFO ("wl_region@%p = wl_compositor_create_region (wl_compositor@%p)", + region, display->compositor); + GST_INFO ("wl_surface_set_input_region (video_surface@%p, wl_region@%p)", + window->video_surface, region); + wl_surface_set_input_region (window->video_surface, region); + GST_INFO ("wl_region_destroy (wl_region@%p)", region); + wl_region_destroy (region); + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + /* set area surface size by full mode(full size of parent window) , toplevel is set to fullmode too for convenient test */ + if (window->tizen_area_viewport) { + int tizen_disp_mode = TIZEN_DESTINATION_MODE_TYPE_FULL; + + window->tizen_area_dest_mode = + tizen_viewport_get_destination_mode (window->tizen_area_viewport); + GST_INFO + ("tizen_area_dest_mode(tizen_destination_mode)@%p = tizen_viewport_get_destination_mode (tizen_area_viewport@%p)", + window->tizen_area_dest_mode, window->tizen_area_viewport); + if (window->tizen_area_dest_mode) { + GST_INFO + ("tizen_destination_mode_set (tizen_destination_mode@%p, @%d, 3 is FULL)", + window->tizen_area_dest_mode, tizen_disp_mode); + tizen_destination_mode_set (window->tizen_area_dest_mode, + tizen_disp_mode); + } + GST_INFO ("wl_surface_commit (area_surface@%p)", window->area_surface); + wl_surface_commit (window->area_surface); + } +#endif + return window; +} + +GstWlWindow * +gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info) +{ +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + GstWlWindow *window; + FUNCTION; + +/* not create shell_surface here for enlightenment */ + display->need_shell_surface = TRUE; + window = gst_wl_window_new_internal (display, NULL); + + /* for tizen enlightenment */ +#if 0 + /* go toplevel */ + window->shell_surface = wl_shell_get_shell_surface (display->shell, + window->area_surface); +#endif + if (window->shell_surface) { + GST_INFO + ("wl_shell_surface_add_listener (shell_surface@%p, wl_shell_surface_listener@%p, GstWlWindow@%p", + window->shell_surface, &shell_surface_listener, window); + wl_shell_surface_add_listener (window->shell_surface, + &shell_surface_listener, window); + GST_INFO ("wl_shell_surface_set_toplevel (shell_surface@%p", + window->shell_surface); + wl_shell_surface_set_toplevel (window->shell_surface); + } else { + GST_ERROR ("Unable to get wl_shell_surface"); + + g_object_unref (window); + return NULL; + } + + /* toplevel is set to fullmode for convenient test in tizen_viewport enviroment, don't use below code */ +#if 0 + /* set the initial size to be the same as the reported video size */ + width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height); +#endif + return window; + +#else /* open source */ + GstWlWindow *window; + gint width; + + window = gst_wl_window_new_internal (display); + + /* go toplevel */ + window->shell_surface = wl_shell_get_shell_surface (display->shell, + window->area_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; + } + + /* set the initial size to be the same as the reported video size */ + width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + gst_wl_window_set_render_rectangle (window, 0, 0, width, info->height); + + return window; +#endif +} + + +GstWlWindow * +gst_wl_window_new_in_surface (GstWlDisplay * display, + struct wl_surface * parent) +{ +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT + /* use App window */ + GstWlWindow *window; + FUNCTION; + + display->use_parent_wl_surface = TRUE; + if (parent) { + /*use wl_surface */ + window = gst_wl_window_new_internal (display, parent); + } else { + /* use wl_surface id */ + window = gst_wl_window_new_internal (display, NULL); + } + + /*Area surface from App need to be under parent surface */ + if (display->tizen_policy) { + GST_INFO + ("tizen_policy_place_subsurface_below_parent (tizen_policy@%p, area_subsurface@%p)", + display->tizen_policy, window->area_subsurface); + tizen_policy_place_subsurface_below_parent (display->tizen_policy, + window->area_subsurface); + GST_INFO + ("tizen_policy_place_subsurface_below_parent (tizen_policy@%p, video_subsurface@%p)", + display->tizen_policy, window->video_subsurface); + tizen_policy_place_subsurface_below_parent (display->tizen_policy, + window->video_subsurface); + } + return window; + +#else /* open source */ + + GstWlWindow *window; + window = gst_wl_window_new_internal (display, parent); //add parent for enlightment + + /*for enlightment , move to gst_wl_window_new_internal() */ +#if 0 + /* embed in parent */ + window->area_subsurface = + wl_subcompositor_get_subsurface (display->subcompositor, + window->area_surface, parent); + wl_subsurface_set_desync (window->area_subsurface); +#endif + + return window; + +#endif +} + +GstWlDisplay * +gst_wl_window_get_display (GstWlWindow * window) +{ + FUNCTION; + 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; + g_return_val_if_fail (window != NULL, NULL); + + return window->video_surface; +} + +gboolean +gst_wl_window_is_toplevel (GstWlWindow * window) +{ + FUNCTION; + g_return_val_if_fail (window != NULL, FALSE); + + return (window->shell_surface != NULL); +} + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +static gint +gst_wl_window_find_rotate_transform (guint rotate_angle) +{ + gint transform = WL_OUTPUT_TRANSFORM_NORMAL; + FUNCTION; + + GST_DEBUG ("rotate (%d)", rotate_angle); + switch (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; + } + return transform; +} + +static gint +gst_wl_window_find_flip_transform (guint flip) +{ + gint transform = WL_OUTPUT_TRANSFORM_NORMAL; + FUNCTION; + + GST_DEBUG ("flip (%d)", flip); + switch (flip) { + case FLIP_NONE: + transform = WL_OUTPUT_TRANSFORM_NORMAL; + break; + case FLIP_HORIZONTAL: + transform = WL_OUTPUT_TRANSFORM_FLIPPED; + break; + case FLIP_VERTICAL: + transform = WL_OUTPUT_TRANSFORM_FLIPPED_180; + break; + case FLIP_BOTH: + transform = WL_OUTPUT_TRANSFORM_180; + break; + } + return transform; + +} + +#endif +#if TIZEN_FEATURE_WLSINK_ENHANCEMENT +static void +gst_wl_window_resize_tizen_video_viewport (GstWlWindow * window, + gboolean commit) +{ + gint transform = WL_OUTPUT_TRANSFORM_NORMAL; + + FUNCTION; + g_return_if_fail (window->tizen_video_viewport != NULL); + g_return_if_fail (window->tizen_video_dest_mode != NULL); + + /* Set source, wayland need to set "tizen_viewport_set_source" always when change video info, + aligned video issue=> ex) 854 x 480 video : aligned buffer size 864 x 480, so we need to set original video size by set source */ + if (window->mode_crop.changed) { + /* we have known issue about mobile team kernel, when set orign green line can be shown with tbm */ + GST_INFO + ("tizen_viewport_set_source (tizen_video_viewport@%p, x@%d, y@%d, w@%d, h@%d)", + window->tizen_video_viewport, window->mode_crop.x, window->mode_crop.y, + window->mode_crop.w, window->mode_crop.h); + tizen_viewport_set_source (window->tizen_video_viewport, + window->mode_crop.x, window->mode_crop.y, window->mode_crop.w, + window->mode_crop.h); + window->mode_crop.changed = FALSE; + } + + /*set tizen destination mode */ + if (window->disp_geo_method.changed || window->roi_area_changed) { + int tizen_disp_mode = -1; + switch (window->disp_geo_method.value) { + + case DISP_GEO_METHOD_LETTER_BOX: + GST_LOG ("TIZEN_DESTINATION_MODE_TYPE_LETTER_BOX"); + tizen_disp_mode = TIZEN_DESTINATION_MODE_TYPE_LETTER_BOX; + break; + case DISP_GEO_METHOD_ORIGIN_SIZE: + GST_LOG ("TIZEN_DESTINATION_MODE_TYPE_ORIGIN"); + tizen_disp_mode = TIZEN_DESTINATION_MODE_TYPE_ORIGIN; + break; + case DISP_GEO_METHOD_FULL_SCREEN: + GST_LOG ("TIZEN_DESTINATION_MODE_TYPE_FULL"); + tizen_disp_mode = TIZEN_DESTINATION_MODE_TYPE_FULL; + break; + case DISP_GEO_METHOD_CROPPED_FULL_SCREEN: + GST_LOG ("TIZEN_DESTINATION_MODE_TYPE_CROPPED_FULL"); + tizen_disp_mode = TIZEN_DESTINATION_MODE_TYPE_CROPPED_FULL; + break; + case DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX: + GST_LOG ("TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER"); + tizen_disp_mode = TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER; + break; + case DISP_GEO_METHOD_CUSTOM_ROI: + GST_LOG ("DISP_GEO_METHOD_CUSTOM_ROI..need to set destination ROI"); + tizen_disp_mode = -1; + break; + default: + break; + } + if (tizen_disp_mode > -1) { + GST_INFO + ("tizen_destination_mode_set (tizen_destination_mode@%p, tizen_disp_mode@%d)", + window->tizen_video_dest_mode, tizen_disp_mode); + tizen_destination_mode_set (window->tizen_video_dest_mode, + tizen_disp_mode); + } + if (window->disp_geo_method.value == DISP_GEO_METHOD_CUSTOM_ROI) { + /* set ROI destination */ + GST_INFO + ("tizen_viewport_set_destination (tizen_video_viewport(tizen_viewport)@%p, x@%d, y@%d, w@%d, h@%d)", + window->tizen_video_viewport, window->roi.x, window->roi.y, + window->roi.w, window->roi.h); + tizen_viewport_set_destination (window->tizen_video_viewport, + window->roi.x, window->roi.y, window->roi.w, window->roi.h); + window->roi_area_changed = FALSE; + } + window->disp_geo_method.changed = FALSE; + } + + + goto done; + + /* Even though area_viewport is set to ROI mode, we can set below functions too if video_viewport is set to tizen_destination_mode_set. */ +#ifdef ENABLE_FUNCTION + /* set or unset follow parent transform */ + if (window->follow_parent_transform.changed + && !gst_wl_window_is_toplevel (window)) { + if (window->follow_parent_transform.value) { + GST_INFO + ("tizen_destination_mode_follow_parent_transform (tizen_destination_mode@%p)", + window->tizen_area_dest_mode); + tizen_destination_mode_follow_parent_transform + (window->tizen_area_dest_mode); + GST_INFO + ("tizen_destination_mode_follow_parent_transform (tizen_destination_mode@%p)", + window->tizen_video_dest_mode); + tizen_destination_mode_follow_parent_transform + (window->tizen_video_dest_mode); + + } else { + GST_INFO + ("tizen_destination_mode_unfollow_parent_transform (tizen_destination_mode@%p)", + window->tizen_area_dest_mode); + tizen_destination_mode_unfollow_parent_transform + (window->tizen_area_dest_mode); + GST_INFO + ("tizen_destination_mode_unfollow_parent_transform (tizen_destination_mode@%p)", + window->tizen_video_dest_mode); + tizen_destination_mode_unfollow_parent_transform + (window->tizen_video_dest_mode); + } + window->follow_parent_transform.changed = FALSE; + } + + /* set ratio */ + if (window->mode_ratio.changed) { + wl_fixed_t f_width, f_height; + f_width = wl_fixed_from_double (window->mode_ratio.w); + f_height = wl_fixed_from_double (window->mode_ratio.h); + + GST_INFO + ("tizen_destination_mode_set_ratio (tizen_destination_mode@%p, wl_fixed width@%f, wl_fixed height@%f)", + window->tizen_video_dest_mode, window->mode_ratio.w, + window->mode_ratio.h); + tizen_destination_mode_set_ratio (window->tizen_video_dest_mode, f_width, + f_height); + window->mode_ratio.changed = FALSE; + } + + /* set offset */ + if (window->mode_offset.changed) { + GST_INFO + ("tizen_destination_mode_set_offset (tizen_destination_mode@%p, x@%d, y@%d, w@%d, h@%d)", + window->tizen_video_dest_mode, window->mode_offset.x, + window->mode_offset.y, window->mode_offset.w, window->mode_offset.h); + tizen_destination_mode_set_offset (window->tizen_video_dest_mode, + window->mode_offset.x, window->mode_offset.y, window->mode_offset.w, + window->mode_offset.h); + window->mode_offset.changed = FALSE; + } + + /* set scale */ + if (window->mode_scale.changed) { + wl_fixed_t f_width, f_height; + f_width = wl_fixed_from_double (window->mode_scale.w); + f_height = wl_fixed_from_double (window->mode_scale.h); + + GST_INFO + ("tizen_destination_mode_set_scale (tizen_destination_mode@%p, wl_fixed width@%f, wl_fixed height@%f)", + window->tizen_video_dest_mode, window->mode_scale.w, + window->mode_scale.h); + tizen_destination_mode_set_scale (window->tizen_video_dest_mode, f_width, + f_height); + window->mode_scale.changed = FALSE; + } + + /* set align */ + if (window->mode_align.changed) { + wl_fixed_t f_width, f_height; + f_width = wl_fixed_from_double (window->mode_align.w); + f_height = wl_fixed_from_double (window->mode_align.h); + GST_INFO + ("tizen_destination_mode_set_align (tizen_destination_mode@%p, wl_fixed_width@%f, wl_fixed_height@%f)", + window->tizen_video_dest_mode, window->mode_align.w, + window->mode_align.h); + tizen_destination_mode_set_align (window->tizen_video_dest_mode, f_width, + f_height); + window->mode_align.changed = FALSE; + } +#endif + +done: + /* set rotate */ + if (window->rotate_angle.changed) { + GST_LOG ("set rotate_angle(%d)", window->rotate_angle.value); + transform = + gst_wl_window_find_rotate_transform (window->rotate_angle.value); + GST_INFO + ("tizen_viewport_set_transform(tizen_area_viewport@%p, transform@%d)", + window->tizen_area_viewport, transform); + tizen_viewport_set_transform (window->tizen_area_viewport, transform); + window->rotate_angle.changed = FALSE; + } + /* set flip */ + if (window->flip.changed) { + GST_LOG ("set flip(%d)", window->flip.value); + + transform = gst_wl_window_find_flip_transform (window->flip.value); + GST_INFO + ("tizen_viewport_set_transform(tizen_video_viewport@%p, transform@%d)", + window->tizen_video_viewport, transform); + tizen_viewport_set_transform (window->tizen_video_viewport, transform); + window->flip.changed = FALSE; + } + + if (commit) { + GST_LOG ("need to commit"); + GST_INFO + ("wl_surface_damage_buffer (video_surface@%p, buffer_@x%d, buffer_y@%d, buffer_w@%d, buffer_h@%d)", + window->video_surface, window->buffer_x, window->buffer_y, + window->buffer_width, window->buffer_height); + wl_surface_damage_buffer (window->video_surface, window->buffer_x, + window->buffer_y, window->buffer_width, window->buffer_height); + GST_INFO ("wl_surface_commit (video_surface@%p)", window->video_surface); + wl_surface_commit (window->video_surface); + } +} +#else +static void +gst_wl_window_resize_video_surface (GstWlWindow * window, gboolean commit) +{ + GstVideoRectangle src = { 0, }; + GstVideoRectangle dst = { 0, }; + GstVideoRectangle res; + + /* center the video_subsurface inside area_subsurface */ + src.w = window->video_width; + src.h = window->video_height; + dst.w = window->render_rectangle.w; + dst.h = window->render_rectangle.h; + gst_video_sink_center_rect (src, dst, &res, TRUE); + + wl_subsurface_set_position (window->video_subsurface, res.x, res.y); + wl_viewport_set_destination (window->video_viewport, res.w, res.h); + + if (commit) { + wl_surface_damage (window->video_surface, 0, 0, res.w, res.h); + wl_surface_commit (window->video_surface); + } + + if (gst_wl_window_is_toplevel (window)) { + struct wl_region *region; + + region = wl_compositor_create_region (window->display->compositor); + wl_region_add (region, 0, 0, window->render_rectangle.w, + window->render_rectangle.h); + wl_surface_set_input_region (window->area_surface, region); + wl_region_destroy (region); + } + + /* this is saved for use in wl_surface_damage */ + window->surface_width = res.w; + window->surface_height = res.h; +} +#endif +void +gst_wl_window_render (GstWlWindow * window, GstWlBuffer * buffer, + const GstVideoInfo * info) +{ +#if TIZEN_FEATURE_WLSINK_ENHANCEMENT + FUNCTION; + /* check video buffer size for wl_surface_damage_buffer */ + if (window->buffer_width != window->display->buffer_width + || window->buffer_height != window->display->buffer_height) { + window->buffer_width = window->display->buffer_width; + window->buffer_height = window->display->buffer_height; + GST_LOG ("buffer_width(%d) buffer_height(%d)", + window->display->buffer_width, window->display->buffer_height); + } + GST_LOG ("buffer_width(%d) buffer_height(%d)", window->display->buffer_width, + window->display->buffer_height); + + if (G_UNLIKELY (info)) { + window->video_width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + window->video_height = info->height; + + wl_subsurface_set_sync (window->video_subsurface); + GST_INFO ("wl_subsurface_set_sync (video_subsurface@%p)", + window->video_subsurface); + /* check video_info_changed to remove repetitive IPC */ + if (window->video_info_changed) { + gst_wl_window_resize_tizen_video_viewport (window, FALSE); + window->video_info_changed = FALSE; + } + } + + GST_LOG ("GstWlBuffer(%p)", buffer); + if (G_LIKELY (buffer)) + gst_wl_buffer_attach (buffer, window->video_surface); + else { + GST_INFO ("wl_surface_attach (video_surface@%p, NULL, 0, 0)", + window->video_surface); + wl_surface_attach (window->video_surface, NULL, 0, 0); + } + /* use tizen view port */ + GST_INFO + ("wl_surface_damage_buffer (video_surface@%p, buffer_x@%d, buffer_y@%d, buffer_w@%d, buffer_h@%d)", + window->video_surface, window->buffer_x, window->buffer_y, + window->buffer_width, window->buffer_height); + wl_surface_damage_buffer (window->video_surface, window->buffer_x, + window->buffer_y, window->buffer_width, window->buffer_height); + /* wl_surface_commit change surface state, if wl_buffer is not attached newly, then surface is not changed */ + GST_INFO ("wl_surface_commit (video_surface@%p)", window->video_surface); + wl_surface_commit (window->video_surface); + + if (G_UNLIKELY (info)) { + GST_INFO ("wl_surface_commit (area_surface@%p)", window->area_surface); + wl_surface_commit (window->area_surface); + GST_INFO ("wl_subsurface_set_desync (video_subsurface@%p)", + window->video_subsurface); + wl_subsurface_set_desync (window->video_subsurface); + } + + wl_display_flush (window->display->display); + +#else /* open source */ + + if (G_UNLIKELY (info)) { + window->video_width = + gst_util_uint64_scale_int_round (info->width, info->par_n, info->par_d); + window->video_height = info->height; + + wl_subsurface_set_sync (window->video_subsurface); + gst_wl_window_resize_video_surface (window, FALSE); + } + + GST_LOG ("GstWlBuffer(%p)", buffer); + if (G_LIKELY (buffer)) + gst_wl_buffer_attach (buffer, window->video_surface); + else + wl_surface_attach (window->video_surface, NULL, 0, 0); + + wl_surface_damage (window->video_surface, 0, 0, window->surface_width, + window->surface_height); + wl_surface_commit (window->video_surface); + + if (G_UNLIKELY (info)) { + /* commit also the parent (area_surface) in order to change + * the position of the video_subsurface */ + wl_surface_damage (window->area_surface, 0, 0, window->render_rectangle.w, + window->render_rectangle.h); + wl_surface_commit (window->area_surface); + wl_subsurface_set_desync (window->video_subsurface); + } + + wl_display_flush (window->display->display); +#endif +} + +#if TIZEN_FEATURE_WLSINK_ENHANCEMENT +gboolean +gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, + gint w, gint h) +{ + FUNCTION; + g_return_val_if_fail (window != NULL, FALSE); + + if (window->disp_geo_method.value != DISP_GEO_METHOD_CUSTOM_ROI) { + GST_ERROR + ("must be set display-geometry-method to DISP_GEO_METHOD_CUSTOM_ROI before setting render rectangle()"); + return FALSE; + } + + if (w < 0 || h < 0) { + GST_ERROR ("Error : wrong roi size w(%d), h(%d)", w, h); + return FALSE; + } + + window->roi.x = x; + window->roi.y = y; + window->roi.w = w; + window->roi.h = h; + GST_LOG ("set roi x(%d), y(%d), w(%d), h(%d)", x, y, w, h); + window->roi_area_changed = TRUE; + + return TRUE; +} +#else /* open source */ +void +gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, + gint w, gint h) +{ + + 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; + + /* position the area inside the parent - needs a parent commit to apply */ + if (window->area_subsurface) + wl_subsurface_set_position (window->area_subsurface, x, y); + + /* change the size of the area */ + wl_viewport_set_destination (window->area_viewport, w, h); + + if (window->video_width != 0) { + wl_subsurface_set_sync (window->video_subsurface); + gst_wl_window_resize_video_surface (window, TRUE); + } + + wl_surface_damage (window->area_surface, 0, 0, w, h); + wl_surface_commit (window->area_surface); + + if (window->video_width != 0) + wl_subsurface_set_desync (window->video_subsurface); + +} +#endif + +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +void +gst_wl_window_set_rotate_angle (GstWlWindow * window, guint rotate_angle) +{ + FUNCTION; + g_return_if_fail (window != NULL); + window->rotate_angle.value = rotate_angle; + GST_LOG ("rotate_angle value is (%d)", window->rotate_angle.value); + window->rotate_angle.changed = TRUE; +} + +void +gst_wl_window_set_destination_mode (GstWlWindow * window, guint disp_geo_method) +{ + FUNCTION; + g_return_if_fail (window != NULL); + window->disp_geo_method.value = disp_geo_method; + GST_LOG ("disp_geo_method value is (%d)", window->disp_geo_method.value); + window->disp_geo_method.changed = TRUE; +} + +void +gst_wl_window_set_flip (GstWlWindow * window, guint flip) +{ + FUNCTION; + g_return_if_fail (window != NULL); + window->flip.value = flip; + GST_LOG ("flip value is (%d)", window->flip.value); + window->flip.changed = TRUE; +} + +void +gst_wl_window_set_destination_mode_crop_wl_buffer (GstWlWindow * window, + guint x, guint y, guint w, guint h) +{ + FUNCTION; + g_return_if_fail (window != NULL); + GST_LOG ("set crop x@%d, y@%d, w@%d, h@%d", x, y, w, h); + window->mode_crop.x = x; + window->mode_crop.y = y; + window->mode_crop.w = w; + window->mode_crop.h = h; + window->mode_crop.changed = TRUE; +} + +#ifdef ENABLE_FUNCTION +void +gst_wl_window_set_destination_mode_follow_parent_transform (GstWlWindow * + window, gboolean follow_parent_transform) +{ + FUNCTION; + g_return_if_fail (window != NULL); + window->follow_parent_transform.value = follow_parent_transform; + GST_LOG ("follow_parent_transform value is (%d)", + window->follow_parent_transform.value); + window->follow_parent_transform.changed = TRUE; +} + + +void +gst_wl_window_set_destination_mode_offset (GstWlWindow * window, guint x, + guint y, guint w, guint h) +{ + FUNCTION; + g_return_if_fail (window != NULL); + GST_LOG ("set offset x@%d, y@%d", x, y); + window->mode_offset.x = x; + window->mode_offset.y = y; + window->mode_offset.w = w; + window->mode_offset.h = h; + window->mode_offset.changed = TRUE; +} + +void +gst_wl_window_set_destination_mode_ratio (GstWlWindow * window, gdouble w, + gdouble h) +{ + FUNCTION; + g_return_if_fail (window != NULL); + GST_LOG ("set ratio w@%f, h@%f", w, h); + window->mode_ratio.w = w; + window->mode_ratio.h = h; + window->mode_ratio.changed = TRUE; +} + +void +gst_wl_window_set_destination_mode_scale (GstWlWindow * window, gdouble w, + gdouble h) +{ + FUNCTION; + g_return_if_fail (window != NULL); + GST_LOG ("set scale w@%f, h@%f", w, h); + window->mode_scale.w = w; + window->mode_scale.h = h; + window->mode_scale.changed = TRUE; +} + +void +gst_wl_window_set_destination_mode_align (GstWlWindow * window, gdouble w, + gdouble h) +{ + FUNCTION; + g_return_if_fail (window != NULL); + GST_LOG ("set align w@%f, h@%f", w, h); + window->mode_align.w = w; + window->mode_align.h = h; + window->mode_align.changed = TRUE; +} +#endif +void +gst_wl_window_set_video_info_change (GstWlWindow * window, guint changed) +{ + FUNCTION; + g_return_if_fail (window != NULL); + window->video_info_changed = changed; + GST_LOG ("video_info_changed value is (%d)", window->video_info_changed); +} +#endif diff --git a/tizenwlsink/src/wlwindow.h b/tizenwlsink/src/wlwindow.h new file mode 100644 index 0000000..6f5397d --- /dev/null +++ b/tizenwlsink/src/wlwindow.h @@ -0,0 +1,167 @@ +/* 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 "wlbuffer.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)) + +enum +{ + DEGREE_0, + DEGREE_90, + DEGREE_180, + DEGREE_270, + DEGREE_NUM, +}; + +enum +{ + FLIP_NONE = 0, + FLIP_HORIZONTAL, + FLIP_VERTICAL, + FLIP_BOTH, + FLIP_NUM, +}; + +typedef struct +{ + guint value; + gboolean changed; +} WinGeometryValue; + +typedef struct +{ + guint x; + guint y; + guint w; + guint h; + gboolean changed; +} WinGeometryRect; + +typedef struct +{ + gdouble w; + gdouble h; + gboolean changed; +} WinGeometryRange; + +typedef struct _GstWlWindow GstWlWindow; +typedef struct _GstWlWindowClass GstWlWindowClass; + +struct _GstWlWindow +{ + GObject parent_instance; + + GstWlDisplay *display; + struct wl_surface *area_surface; + struct wl_subsurface *area_subsurface; + struct wl_surface *video_surface; + struct wl_subsurface *video_subsurface; + struct wl_shell_surface *shell_surface; +#ifndef TIZEN_FEATURE_WLSINK_ENHANCEMENT /* no define */ + struct wl_viewport *video_viewport; + struct wl_viewport *area_viewport; +#else + struct tizen_video_object *video_object; + struct tizen_viewport *tizen_area_viewport; + struct tizen_viewport *tizen_video_viewport; + struct tizen_destination_mode *tizen_video_dest_mode; + struct tizen_destination_mode *tizen_area_dest_mode; + guint video_info_changed; +/*Display geometry method */ + guint buffer_width, buffer_height; + guint buffer_x, buffer_y; + WinGeometryValue disp_geo_method; + WinGeometryValue rotate_angle; + WinGeometryValue flip; + WinGeometryRect mode_crop; +#ifdef ENABLE_FUNCTION + WinGeometryValue follow_parent_transform; + WinGeometryRect mode_offset; + WinGeometryRange mode_ratio; + WinGeometryRange mode_scale; + WinGeometryRange mode_align; +#endif + GstVideoRectangle roi; + gboolean roi_area_changed; +#endif + + /* the size and position of the area_(sub)surface */ + GstVideoRectangle render_rectangle; + /* the size of the video in the buffers */ + gint video_width, video_height; + /* the size of the video_(sub)surface */ + gint surface_width, surface_height; +}; + +struct _GstWlWindowClass +{ + GObjectClass parent_class; +}; + +GType gst_wl_window_get_type (void); + +GstWlWindow *gst_wl_window_new_toplevel (GstWlDisplay * display, + const GstVideoInfo * 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); + +void gst_wl_window_render (GstWlWindow * window, GstWlBuffer * buffer, + const GstVideoInfo * info); +#ifdef TIZEN_FEATURE_WLSINK_ENHANCEMENT +gboolean gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, + gint w, gint h); +void gst_wl_window_set_video_info (GstWlWindow * window, + const GstVideoInfo * info); +void gst_wl_window_set_rotate_angle (GstWlWindow * window, guint rotate_angle); +void gst_wl_window_set_flip (GstWlWindow * window, guint flip); +void gst_wl_window_set_destination_mode_crop_wl_buffer (GstWlWindow * window, guint x, guint y, guint w, guint h); +void gst_wl_window_set_destination_mode (GstWlWindow * window, guint disp_geo_method); +#ifdef ENABLE_FUNCTION +/* if video mode is set, below function can use */ +void gst_wl_window_set_destination_mode_follow_parent_transform (GstWlWindow * window, gboolean follow_parent_transform); +void gst_wl_window_set_destination_mode_offset (GstWlWindow * window, guint x, guint y, guint w, guint h); +void gst_wl_window_set_destination_mode_ratio (GstWlWindow * window, gdouble w, gdouble h); +void gst_wl_window_set_destination_mode_scale (GstWlWindow * window, gdouble w, gdouble h); +void gst_wl_window_set_destination_mode_align (GstWlWindow * window, gdouble w, gdouble h); +#endif +void gst_wl_window_set_video_info_change (GstWlWindow * window, guint changed); +#else +void gst_wl_window_set_render_rectangle (GstWlWindow * window, gint x, gint y, + gint w, gint h); +#endif + +G_END_DECLS +#endif /* __GST_WL_WINDOW_H__ */