1 /* GStreamer Wayland video sink
3 * Copyright (C) 2011 Intel Corporation
4 * Copyright (C) 2011 Sreerenj Balachandran <sreerenj.balachandran@intel.com>
5 * Copyright (C) 2012 Wim Taymans <wim.taymans@gmail.com>
6 * Copyright (C) 2014 Collabora Ltd.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
25 * SECTION:element-waylandsink
27 * The waylandsink is creating its own window and render the decoded video frames to that.
28 * Setup the Wayland environment as described in
29 * <ulink url="http://wayland.freedesktop.org/building.html">Wayland</ulink> home page.
30 * The current implementaion is based on weston compositor.
33 * <title>Example pipelines</title>
35 * gst-launch -v videotestsrc ! waylandsink
36 * ]| test the video rendering in wayland
44 #include "gstwaylandsink.h"
45 #ifdef GST_WLSINK_ENHANCEMENT
47 #include "tizen-wlvideoformat.h"
49 #include "wlvideoformat.h"
51 #include "wlshmallocator.h"
53 #include <gst/wayland/wayland.h>
54 #include <gst/video/videooverlay.h>
61 #ifdef GST_WLSINK_ENHANCEMENT
62 #define GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD (gst_waylandsink_display_geometry_method_get_type())
63 #define GST_TYPE_WAYLANDSINK_ROTATE_ANGLE (gst_waylandsink_rotate_angle_get_type())
64 #define GST_TYPE_WAYLANDSINK_FLIP (gst_waylandsink_flip_get_type())
67 gst_waylandsink_rotate_angle_get_type (void)
69 static GType waylandsink_rotate_angle_type = 0;
70 static const GEnumValue rotate_angle_type[] = {
71 {0, "No rotate", "DEGREE_0"},
72 {1, "Rotate 90 degree", "DEGREE_90"},
73 {2, "Rotate 180 degree", "DEGREE_180"},
74 {3, "Rotate 270 degree", "DEGREE_270"},
78 if (!waylandsink_rotate_angle_type) {
79 waylandsink_rotate_angle_type =
80 g_enum_register_static ("GstWaylandSinkRotateAngleType",
84 return waylandsink_rotate_angle_type;
89 gst_waylandsink_display_geometry_method_get_type (void)
91 static GType waylandsink_display_geometry_method_type = 0;
92 static const GEnumValue display_geometry_method_type[] = {
93 {0, "Letter box", "LETTER_BOX"},
94 {1, "Origin size", "ORIGIN_SIZE"},
95 {2, "Full-screen", "FULL_SCREEN"},
96 {3, "Cropped full-screen", "CROPPED_FULL_SCREEN"},
97 {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"},
101 if (!waylandsink_display_geometry_method_type) {
102 waylandsink_display_geometry_method_type =
103 g_enum_register_static ("GstWaylandSinkDisplayGeometryMethodType",
104 display_geometry_method_type);
106 return waylandsink_display_geometry_method_type;
110 gst_waylandsink_flip_get_type (void)
112 static GType waylandsink_flip_type = 0;
113 static const GEnumValue flip_type[] = {
114 {FLIP_NONE, "Flip NONE", "FLIP_NONE"},
115 {FLIP_HORIZONTAL, "Flip HORIZONTAL", "FLIP_HORIZONTAL"},
116 {FLIP_VERTICAL, "Flip VERTICAL", "FLIP_VERTICAL"},
117 {FLIP_BOTH, "Flip BOTH", "FLIP_BOTH"},
118 {FLIP_NUM, NULL, NULL},
121 if (!waylandsink_flip_type) {
122 waylandsink_flip_type =
123 g_enum_register_static ("GstWaylandSinkFlipType", flip_type);
126 return waylandsink_flip_type;
143 #ifdef GST_WLSINK_ENHANCEMENT
147 PROP_DISPLAY_GEOMETRY_METHOD,
155 GST_DEBUG_CATEGORY (gstwayland_debug);
156 #define GST_CAT_DEFAULT gstwayland_debug
158 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
161 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
162 ("{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, "
163 "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, "
164 #ifdef GST_WLSINK_ENHANCEMENT
167 "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"))
170 static void gst_wayland_sink_get_property (GObject * object,
171 guint prop_id, GValue * value, GParamSpec * pspec);
172 static void gst_wayland_sink_set_property (GObject * object,
173 guint prop_id, const GValue * value, GParamSpec * pspec);
174 static void gst_wayland_sink_finalize (GObject * object);
176 static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
177 GstStateChange transition);
178 static void gst_wayland_sink_set_context (GstElement * element,
179 GstContext * context);
181 static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
183 static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
184 static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink,
187 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query);
188 static gboolean gst_wayland_sink_render (GstBaseSink * bsink,
191 /* VideoOverlay interface */
192 static void gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface *
194 static void gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay,
197 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
198 guintptr wl_surface_id);
199 static void gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
200 gint x, gint y, gint w, gint h);
201 static void gst_wayland_sink_expose (GstVideoOverlay * overlay);
203 /* WaylandVideo interface */
204 static void gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface *
206 static void gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video);
207 static void gst_wayland_sink_end_geometry_change (GstWaylandVideo * video);
208 #ifdef GST_WLSINK_ENHANCEMENT
209 static gboolean gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event);
210 static void gst_wayland_sink_update_window_geometry (GstWaylandSink * sink);
211 static void render_last_buffer (GstWaylandSink * sink);
213 #define gst_wayland_sink_parent_class parent_class
214 G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
215 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
216 gst_wayland_sink_videooverlay_init)
217 G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO,
218 gst_wayland_sink_waylandvideo_init));
221 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
223 GObjectClass *gobject_class;
224 GstElementClass *gstelement_class;
225 GstBaseSinkClass *gstbasesink_class;
228 gobject_class = (GObjectClass *) klass;
229 gstelement_class = (GstElementClass *) klass;
230 gstbasesink_class = (GstBaseSinkClass *) klass;
232 gobject_class->set_property = gst_wayland_sink_set_property;
233 gobject_class->get_property = gst_wayland_sink_get_property;
234 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize);
236 gst_element_class_add_pad_template (gstelement_class,
237 gst_static_pad_template_get (&sink_template));
239 gst_element_class_set_static_metadata (gstelement_class,
240 "wayland video sink", "Sink/Video",
241 "Output to wayland surface",
242 "Sreerenj Balachandran <sreerenj.balachandran@intel.com>, "
243 "George Kiagiadakis <george.kiagiadakis@collabora.com>");
245 gstelement_class->change_state =
246 GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
247 gstelement_class->set_context =
248 GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
250 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
251 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
252 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
253 gstbasesink_class->propose_allocation =
254 GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
255 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render);
256 #ifdef GST_WLSINK_ENHANCEMENT
257 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_wayland_sink_event);
260 g_object_class_install_property (gobject_class, PROP_DISPLAY,
261 g_param_spec_string ("display", "Wayland Display name", "Wayland "
262 "display name to connect to, if not supplied via the GstContext",
263 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
264 #ifdef GST_WLSINK_ENHANCEMENT
265 g_object_class_install_property (gobject_class, PROP_USE_GAPLESS,
266 g_param_spec_boolean ("use-gapless", "use gapless",
267 "Use gapless playback on GST_STATE_PLAYING state, "
268 "Last tbm buffer is copied and returned to codec immediately when enabled",
269 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271 g_object_class_install_property (gobject_class, PROP_USE_TBM,
272 g_param_spec_boolean ("use-tbm", "use tbm buffer",
273 "Use Tizen Buffer Memory insted of Shared memory, "
274 "Memory is alloced by TBM insted of SHM when enabled", TRUE,
275 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277 g_object_class_install_property (gobject_class, PROP_ROTATE_ANGLE,
278 g_param_spec_enum ("rotate", "Rotate angle",
279 "Rotate angle of display output",
280 GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
281 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
284 g_param_spec_enum ("display-geometry-method", "Display geometry method",
285 "Geometrical method for display",
286 GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD,
287 DEF_DISPLAY_GEOMETRY_METHOD,
288 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
290 g_object_class_install_property (gobject_class, PROP_ORIENTATION,
291 g_param_spec_enum ("orientation",
292 "Orientation information used for ROI/ZOOM",
293 "Orientation information for display",
294 GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
297 g_object_class_install_property (gobject_class, PROP_FLIP,
298 g_param_spec_enum ("flip", "Display flip",
300 GST_TYPE_WAYLANDSINK_FLIP, DEF_DISPLAY_FLIP,
301 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
303 g_object_class_install_property (gobject_class, PROP_VISIBLE,
304 g_param_spec_boolean ("visible", "Visible",
305 "Draws screen or blacks out, true means visible, false blacks out",
306 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
312 __write_rawdata (const char *file, const void *data, unsigned int size)
316 fp = fopen (file, "wb");
320 fwrite ((char *) data, sizeof (char), size, fp);
327 gst_wayland_sink_init (GstWaylandSink * sink)
330 #ifdef GST_WLSINK_ENHANCEMENT
331 sink->use_gapless = FALSE;
332 sink->got_eos_event = FALSE;
333 sink->USE_TBM = TRUE;
334 sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
335 sink->flip = DEF_DISPLAY_FLIP;
336 sink->rotate_angle = DEGREE_0;
337 sink->orientation = DEGREE_0;
338 sink->visible = TRUE;
339 g_mutex_init (&sink->gapless_lock);
340 g_cond_init (&sink->gapless_cond);
342 g_mutex_init (&sink->display_lock);
343 g_mutex_init (&sink->render_lock);
346 #ifdef GST_WLSINK_ENHANCEMENT
348 gst_wayland_sink_stop_video (GstWaylandSink * sink)
351 g_return_if_fail (sink != NULL);
352 gst_wl_window_render (sink->window, NULL, NULL);
356 gst_wayland_sink_need_to_make_flush_buffer (GstWaylandSink * sink)
358 g_return_if_fail (sink != NULL);
359 g_return_if_fail (sink->display != NULL);
361 if ((sink->use_gapless && sink->got_eos_event)
362 || sink->display->flush_request) {
363 sink->display->flush_request = TRUE;
370 gst_wayland_sink_update_last_buffer_geometry (GstWaylandSink * sink)
372 GstWlBuffer *wlbuffer;
374 g_return_if_fail (sink != NULL);
375 g_return_if_fail (sink->last_buffer != NULL);
377 GST_DEBUG ("gstbuffer ref count is %d",
378 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
379 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
380 wlbuffer->used_by_compositor = FALSE;
381 /*need to render last buffer, reuse current GstWlBuffer */
382 render_last_buffer (sink);
383 /* ref count is incresed in gst_wl_buffer_attach() of render_last_buffer(),
384 to call gst_wl_buffer_finalize(), we need to decrease buffer ref count.
385 wayland can not release buffer if we attach same buffer,
386 if we use no visible, we need to attach null buffer and wayland can release buffer,
387 so we don't need to below if() code. */
389 gst_buffer_unref (wlbuffer->gstbuffer);
392 #ifdef USE_WL_FLUSH_BUFFER
394 gst_wayland_sink_make_flush_buffer (GstWlDisplay * display,
395 MMVideoBuffer * mm_video_buf)
397 GstWlFlushBuffer *flush_buffer = NULL;
403 g_return_val_if_fail (display != NULL, FALSE);
404 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
406 flush_buffer = (GstWlFlushBuffer *) malloc (sizeof (GstWlFlushBuffer));
408 GST_ERROR ("GstWlFlushBuffer alloc faile");
411 memset (flush_buffer, 0x0, sizeof (GstWlFlushBuffer));
413 display->flush_tbm_bufmgr =
414 wayland_tbm_client_get_bufmgr (display->tbm_client);
415 g_return_val_if_fail (display->flush_tbm_bufmgr != NULL, FALSE);
417 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
418 if (mm_video_buf->handle.bo[i] != NULL) {
423 bo_size = tbm_bo_size (mm_video_buf->handle.bo[i]);
424 GST_LOG ("tbm bo size: %d", bo_size);
426 bo = tbm_bo_alloc (display->flush_tbm_bufmgr, bo_size, TBM_DEVICE_CPU);
428 GST_ERROR ("alloc tbm bo(size:%d) failed: %s", bo_size,
432 GST_INFO ("flush buffer tbm_bo =(%p)", bo);
433 flush_buffer->bo[i] = bo;
434 /* get virtual address */
435 src.ptr = dst.ptr = NULL;
436 /* bo map, we can use tbm_bo_map too. */
437 src = tbm_bo_get_handle (mm_video_buf->handle.bo[i], TBM_DEVICE_CPU);
438 dst = tbm_bo_get_handle (bo, TBM_DEVICE_CPU);
439 if (!src.ptr || !dst.ptr) {
440 GST_ERROR ("get tbm bo handle failed src(%p) dst(%p): %s", src.ptr,
441 dst.ptr, strerror (errno));
442 tbm_bo_unref (mm_video_buf->handle.bo[i]);
447 memcpy (dst.ptr, src.ptr, bo_size);
449 tbm_bo_unmap (mm_video_buf->handle.bo[i]);
453 display->flush_buffer = flush_buffer;
458 gst_wayland_sink_copy_mm_video_buf_info_to_flush (GstWlDisplay * display,
459 MMVideoBuffer * mm_video_buf)
462 g_return_val_if_fail (display != NULL, FALSE);
463 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
466 ret = gst_wayland_sink_make_flush_buffer (display, mm_video_buf);
469 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
470 if (display->flush_buffer->bo[i] != NULL) {
471 display->bo[i] = display->flush_buffer->bo[i];
472 GST_LOG ("bo %p", display->bo[i]);
476 display->plane_size[i] = mm_video_buf->size[i];
477 display->stride_width[i] = mm_video_buf->stride_width[i];
478 display->stride_height[i] = mm_video_buf->stride_height[i];
479 display->native_video_size += display->plane_size[i];
487 gst_wayland_sink_add_mm_video_buf_info (GstWlDisplay * display,
488 MMVideoBuffer * mm_video_buf)
491 g_return_if_fail (display != NULL);
492 g_return_if_fail (mm_video_buf != NULL);
495 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
496 if (mm_video_buf->handle.bo[i] != NULL) {
497 display->bo[i] = mm_video_buf->handle.bo[i];
501 display->plane_size[i] = mm_video_buf->size[i];
502 display->stride_width[i] = mm_video_buf->stride_width[i];
503 display->stride_height[i] = mm_video_buf->stride_height[i];
504 display->native_video_size += display->plane_size[i];
509 gst_wayland_sink_get_mm_video_buf_info (GstWaylandSink * sink,
512 GstWlDisplay *display;
514 GstMapInfo mem_info = GST_MAP_INFO_INIT;
515 MMVideoBuffer *mm_video_buf = NULL;
517 g_return_val_if_fail (sink != NULL, FALSE);
518 g_return_val_if_fail (buffer != NULL, FALSE);
521 display = sink->display;
522 g_return_val_if_fail (sink->display != NULL, FALSE);
524 mem = gst_buffer_peek_memory (buffer, 1);
525 gst_memory_map (mem, &mem_info, GST_MAP_READ);
526 mm_video_buf = (MMVideoBuffer *) mem_info.data;
527 gst_memory_unmap (mem, &mem_info);
529 if (mm_video_buf == NULL) {
530 GST_WARNING ("mm_video_buf is NULL. Skip rendering");
533 /* assign mm_video_buf info */
534 if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
535 GST_DEBUG ("TBM bo %p %p %p", mm_video_buf->handle.bo[0],
536 mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
537 display->native_video_size = 0;
538 display->flush_request = mm_video_buf->flush_request;
539 GST_DEBUG ("flush_request value is %d", display->flush_request);
540 #ifdef USE_WL_FLUSH_BUFFER
541 if (gst_wayland_sink_need_to_make_flush_buffer (sink)) {
542 if (!gst_wayland_sink_copy_mm_video_buf_info_to_flush (display,
544 GST_ERROR ("cat not copy mm_video_buf info to flush");
550 gst_wayland_sink_add_mm_video_buf_info (display, mm_video_buf);
552 GST_ERROR ("Buffer type is not TBM");
559 gst_wayland_sink_gapless_render_flush_buffer (GstBaseSink * bsink)
561 GstWaylandSink *sink;
563 sink = GST_WAYLAND_SINK (bsink);
565 g_return_if_fail (sink != NULL);
566 g_return_if_fail (sink->last_buffer != NULL);
568 buffer = gst_buffer_copy (sink->last_buffer);
570 g_mutex_lock (&sink->gapless_lock);
571 g_cond_wait (&sink->gapless_cond, &sink->gapless_lock);
573 gst_wayland_sink_render (bsink, buffer);
575 gst_buffer_unref (buffer);
576 g_mutex_unlock (&sink->gapless_lock);
581 gst_wayland_sink_get_property (GObject * object,
582 guint prop_id, GValue * value, GParamSpec * pspec)
584 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
589 GST_OBJECT_LOCK (sink);
590 g_value_set_string (value, sink->display_name);
591 GST_OBJECT_UNLOCK (sink);
593 #ifdef GST_WLSINK_ENHANCEMENT
594 case PROP_USE_GAPLESS:
595 g_value_set_boolean (value, sink->use_gapless);
598 g_value_set_boolean (value, sink->USE_TBM);
600 case PROP_ROTATE_ANGLE:
601 g_value_set_enum (value, sink->rotate_angle);
603 case PROP_DISPLAY_GEOMETRY_METHOD:
604 g_value_set_enum (value, sink->display_geometry_method);
606 case PROP_ORIENTATION:
607 g_value_set_enum (value, sink->orientation);
610 g_value_set_enum (value, sink->flip);
613 g_value_set_boolean (value, sink->visible);
617 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
623 gst_wayland_sink_set_property (GObject * object,
624 guint prop_id, const GValue * value, GParamSpec * pspec)
626 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
628 g_mutex_lock (&sink->render_lock);
632 GST_OBJECT_LOCK (sink);
633 sink->display_name = g_value_dup_string (value);
634 GST_OBJECT_UNLOCK (sink);
636 #ifdef GST_WLSINK_ENHANCEMENT
637 case PROP_USE_GAPLESS:
638 sink->use_gapless = g_value_get_boolean (value);
639 GST_LOG ("use gapless is (%d)", sink->use_gapless);
642 sink->USE_TBM = g_value_get_boolean (value);
643 GST_LOG ("1:USE TBM 0: USE SHM set(%d)", sink->USE_TBM);
646 case PROP_ROTATE_ANGLE:
647 if (sink->rotate_angle == g_value_get_enum (value))
649 sink->rotate_angle = g_value_get_enum (value);
650 GST_WARNING_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
651 sink->video_info_changed = TRUE;
653 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
657 case PROP_DISPLAY_GEOMETRY_METHOD:
658 if (sink->display_geometry_method == g_value_get_enum (value))
660 sink->display_geometry_method = g_value_get_enum (value);
661 GST_WARNING_OBJECT (sink, "Display geometry method is set (%d)",
662 sink->display_geometry_method);
663 sink->video_info_changed = TRUE;
665 gst_wl_window_set_disp_geo_method (sink->window,
666 sink->display_geometry_method);
670 case PROP_ORIENTATION:
671 if (sink->orientation == g_value_get_enum (value))
673 sink->orientation = g_value_get_enum (value);
674 GST_WARNING_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
675 sink->video_info_changed = TRUE;
677 gst_wl_window_set_orientation (sink->window, sink->orientation);
682 if (sink->flip == g_value_get_enum (value))
684 sink->flip = g_value_get_enum (value);
685 GST_WARNING_OBJECT (sink, "flip is set (%d)", sink->flip);
686 sink->video_info_changed = TRUE;
688 gst_wl_window_set_flip (sink->window, sink->flip);
693 if (sink->visible == g_value_get_boolean (value))
695 sink->visible = g_value_get_boolean (value);
696 GST_WARNING_OBJECT (sink, "visible is set (%d)", sink->visible);
697 if (sink->visible && GST_STATE (sink) == GST_STATE_PAUSED) {
698 /* need to attatch last buffer */
699 sink->video_info_changed = TRUE;
700 } else if (!sink->visible && GST_STATE (sink) >= GST_STATE_PAUSED) {
703 gst_wayland_sink_stop_video (sink);
709 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
712 if (sink->video_info_changed && sink->window
713 && GST_STATE (sink) == GST_STATE_PAUSED) {
714 gst_wayland_sink_update_last_buffer_geometry (sink);
716 g_mutex_unlock (&sink->render_lock);
721 gst_wayland_sink_finalize (GObject * object)
723 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
725 GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
727 if (sink->last_buffer)
728 gst_buffer_unref (sink->last_buffer);
730 g_object_unref (sink->display);
732 g_object_unref (sink->window);
734 gst_object_unref (sink->pool);
736 if (sink->display_name)
737 g_free (sink->display_name);
739 g_mutex_clear (&sink->display_lock);
740 g_mutex_clear (&sink->render_lock);
741 #ifdef GST_WLSINK_ENHANCEMENT
742 g_mutex_clear (&sink->gapless_lock);
743 g_cond_clear (&sink->gapless_cond);
746 G_OBJECT_CLASS (parent_class)->finalize (object);
749 #ifdef GST_WLSINK_ENHANCEMENT
751 gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event)
753 GstWaylandSink *sink;
754 sink = GST_WAYLAND_SINK (bsink);
756 switch (GST_EVENT_TYPE (event)) {
758 GST_LOG ("get EOS event..state is %d", GST_STATE (sink));
759 if (sink->USE_TBM && sink->display->is_native_format && sink->use_gapless) {
760 sink->got_eos_event = TRUE;
761 gst_wayland_sink_gapless_render_flush_buffer (bsink);
762 sink->got_eos_event = FALSE;
768 return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
772 /* must be called with the display_lock */
774 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
775 GstContext * context)
777 struct wl_display *display;
778 GError *error = NULL;
781 display = gst_wayland_display_handle_context_get_handle (context);
782 sink->display = gst_wl_display_new_existing (display, FALSE, &error);
785 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
786 ("Could not set display handle"),
787 ("Failed to use the external wayland display: '%s'", error->message));
788 g_error_free (error);
790 #ifdef GST_WLSINK_ENHANCEMENT
791 sink->display->USE_TBM = sink->USE_TBM;
796 gst_wayland_sink_find_display (GstWaylandSink * sink)
800 GstContext *context = NULL;
801 GError *error = NULL;
805 g_mutex_lock (&sink->display_lock);
807 if (!sink->display) {
808 /* first query upstream for the needed display handle */
809 query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
810 if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
811 gst_query_parse_context (query, &context);
812 gst_wayland_sink_set_display_from_context (sink, context);
814 gst_query_unref (query);
816 if (G_LIKELY (!sink->display)) {
817 /* now ask the application to set the display handle */
818 msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
819 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
821 g_mutex_unlock (&sink->display_lock);
822 gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
823 /* at this point we expect gst_wayland_sink_set_context
824 * to get called and fill sink->display */
825 g_mutex_lock (&sink->display_lock);
827 if (!sink->display) {
828 /* if the application didn't set a display, let's create it ourselves */
829 GST_OBJECT_LOCK (sink);
830 sink->display = gst_wl_display_new (sink->display_name, &error);
831 GST_OBJECT_UNLOCK (sink);
834 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
835 ("Could not initialise Wayland output"),
836 ("Failed to create GstWlDisplay: '%s'", error->message));
837 g_error_free (error);
840 #ifdef GST_WLSINK_ENHANCEMENT
842 sink->display->USE_TBM = sink->USE_TBM;
848 g_mutex_unlock (&sink->display_lock);
853 static GstStateChangeReturn
854 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
856 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
857 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
860 switch (transition) {
861 case GST_STATE_CHANGE_NULL_TO_READY:
862 if (!gst_wayland_sink_find_display (sink))
863 return GST_STATE_CHANGE_FAILURE;
869 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
870 if (ret == GST_STATE_CHANGE_FAILURE)
873 switch (transition) {
874 case GST_STATE_CHANGE_PAUSED_TO_READY:
875 gst_buffer_replace (&sink->last_buffer, NULL);
877 if (gst_wl_window_is_toplevel (sink->window)) {
878 g_clear_object (&sink->window);
880 /* remove buffer from surface, show nothing */
881 #ifdef USE_WL_FLUSH_BUFFER
882 sink->display->flush_request = 0;
884 gst_wl_window_render (sink->window, NULL, NULL);
888 case GST_STATE_CHANGE_READY_TO_NULL:
889 g_mutex_lock (&sink->display_lock);
890 /* If we had a toplevel window, we most likely have our own connection
891 * to the display too, and it is a good idea to disconnect and allow
892 * potentially the application to embed us with GstVideoOverlay
893 * (which requires to re-use the same display connection as the parent
894 * surface). If we didn't have a toplevel window, then the display
895 * connection that we have is definitely shared with the application
896 * and it's better to keep it around (together with the window handle)
897 * to avoid requesting them again from the application if/when we are
898 * restarted (GstVideoOverlay behaves like that in other sinks)
900 if (sink->display && !sink->window) { /* -> the window was toplevel */
901 g_clear_object (&sink->display);
903 g_mutex_unlock (&sink->display_lock);
904 g_clear_object (&sink->pool);
914 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
916 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
919 if (gst_context_has_context_type (context,
920 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
921 g_mutex_lock (&sink->display_lock);
922 if (G_LIKELY (!sink->display))
923 gst_wayland_sink_set_display_from_context (sink, context);
925 GST_WARNING_OBJECT (element, "changing display handle is not supported");
926 #ifdef GST_WLSINK_ENHANCEMENT
927 g_mutex_unlock (&sink->display_lock);
931 g_mutex_unlock (&sink->display_lock);
934 if (GST_ELEMENT_CLASS (parent_class)->set_context)
935 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
939 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
941 GstWaylandSink *sink;
945 sink = GST_WAYLAND_SINK (bsink);
947 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
949 g_mutex_lock (&sink->display_lock);
952 GValue list = G_VALUE_INIT;
953 GValue value = G_VALUE_INIT;
956 #ifdef GST_WLSINK_ENHANCEMENT
959 enum wl_shm_format fmt;
961 g_value_init (&list, GST_TYPE_LIST);
962 g_value_init (&value, G_TYPE_STRING);
963 #ifdef GST_WLSINK_ENHANCEMENT
964 if (sink->display->USE_TBM)
965 formats = sink->display->tbm_formats;
968 formats = sink->display->formats;
970 for (i = 0; i < formats->len; i++) {
971 #ifdef GST_WLSINK_ENHANCEMENT
973 tbm_fmt = g_array_index (formats, uint32_t, i);
974 g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt));
975 gst_value_list_append_value (&list, &value);
976 /* TBM doesn't support SN12 and ST12. So we add SN12 and ST12 manually as supported format.
977 * SN12 is same with NV12, ST12 is same with NV12MT
979 if (tbm_fmt == TBM_FORMAT_NV12) {
980 g_value_set_string (&value,
981 gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
982 gst_value_list_append_value (&list, &value);
983 } else if (tbm_fmt == TBM_FORMAT_NV12MT) {
984 g_value_set_string (&value,
985 gst_video_format_to_string (GST_VIDEO_FORMAT_ST12));
986 gst_value_list_append_value (&list, &value);
988 } else { /* USE SHM */
989 fmt = g_array_index (formats, uint32_t, i);
990 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
991 gst_value_list_append_value (&list, &value);
993 #else /* open source */
994 fmt = g_array_index (formats, uint32_t, i);
995 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
996 gst_value_list_append_value (&list, &value);
1000 caps = gst_caps_make_writable (caps);
1001 gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
1003 GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
1006 g_mutex_unlock (&sink->display_lock);
1009 GstCaps *intersection;
1012 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1013 gst_caps_unref (caps);
1014 caps = intersection;
1021 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
1023 GstWaylandSink *sink;
1024 GstBufferPool *newpool;
1026 #ifdef GST_WLSINK_ENHANCEMENT
1027 uint32_t tbm_format;
1029 enum wl_shm_format format;
1033 GstStructure *structure;
1034 GstWlShmAllocator *self = NULL;
1038 sink = GST_WAYLAND_SINK (bsink);
1040 GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
1042 /* extract info from caps */
1043 if (!gst_video_info_from_caps (&info, caps))
1044 goto invalid_format;
1045 #ifdef GST_WLSINK_ENHANCEMENT
1046 sink->caps = gst_caps_copy (caps);
1047 if (sink->USE_TBM) {
1049 gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info));
1050 if ((gint) tbm_format == -1)
1051 goto invalid_format;
1053 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1054 if ((gint) format == -1)
1055 goto invalid_format;
1057 #else /* open source */
1058 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1060 if ((gint) format == -1)
1061 goto invalid_format;
1064 /* verify we support the requested format */
1065 #ifdef GST_WLSINK_ENHANCEMENT
1066 if (sink->display->USE_TBM) {
1067 GST_INFO ("USE TBM FORMAT");
1068 formats = sink->display->tbm_formats;
1069 for (i = 0; i < formats->len; i++) {
1070 if (g_array_index (formats, uint32_t, i) == tbm_format)
1073 } else { /* USE SHM */
1074 GST_INFO ("USE SHM FORMAT");
1075 formats = sink->display->formats;
1076 for (i = 0; i < formats->len; i++) {
1077 if (g_array_index (formats, uint32_t, i) == format)
1081 #else /* open source */
1083 formats = sink->display->formats;
1084 for (i = 0; i < formats->len; i++) {
1085 if (g_array_index (formats, uint32_t, i) == format)
1089 if (i >= formats->len)
1090 goto unsupported_format;
1092 #ifdef GST_WLSINK_ENHANCEMENT
1093 if (sink->USE_TBM) {
1094 if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
1095 GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
1096 sink->display->is_native_format = TRUE;
1098 /* store the video info */
1099 sink->video_info = info;
1100 sink->video_info_changed = TRUE;
1102 sink->display->is_native_format = FALSE;
1103 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1104 self->display = sink->display;
1105 /* create a new pool for the new configuration */
1106 newpool = gst_video_buffer_pool_new ();
1110 structure = gst_buffer_pool_get_config (newpool);
1111 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1112 gst_buffer_pool_config_set_allocator (structure,
1113 gst_wl_shm_allocator_get (), NULL);
1114 if (!gst_buffer_pool_set_config (newpool, structure))
1117 /* store the video info */
1118 sink->video_info = info;
1119 sink->video_info_changed = TRUE;
1121 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1122 gst_object_unref (newpool);
1124 } else { /* USE SHM */
1126 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1127 self->display = sink->display;
1129 /* create a new pool for the new configuration */
1130 newpool = gst_video_buffer_pool_new ();
1134 structure = gst_buffer_pool_get_config (newpool);
1135 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1136 gst_buffer_pool_config_set_allocator (structure,
1137 gst_wl_shm_allocator_get (), NULL);
1138 if (!gst_buffer_pool_set_config (newpool, structure))
1141 /* store the video info */
1142 sink->video_info = info;
1143 sink->video_info_changed = TRUE;
1145 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1146 gst_object_unref (newpool);
1148 #else /*open source */
1149 /* create a new pool for the new configuration */
1150 newpool = gst_video_buffer_pool_new ();
1154 structure = gst_buffer_pool_get_config (newpool);
1155 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1156 gst_buffer_pool_config_set_allocator (structure,
1157 gst_wl_shm_allocator_get (), NULL);
1158 if (!gst_buffer_pool_set_config (newpool, structure))
1161 /* store the video info */
1162 sink->video_info = info;
1163 sink->video_info_changed = TRUE;
1165 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1166 gst_object_unref (newpool);
1173 GST_DEBUG_OBJECT (sink,
1174 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1179 #ifdef GST_WLSINK_ENHANCEMENT
1181 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1182 gst_wl_tbm_format_to_string (tbm_format));
1184 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1185 gst_wl_shm_format_to_string (format));
1186 #else /*open source */
1187 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1188 gst_wl_shm_format_to_string (format));
1194 GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1199 GST_DEBUG_OBJECT (bsink, "failed setting config");
1200 gst_object_unref (newpool);
1206 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1208 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1209 GstStructure *config;
1210 guint size, min_bufs, max_bufs;
1211 #ifdef GST_WLSINK_ENHANCEMENT
1216 if (sink->USE_TBM) {
1217 if (sink->display->is_native_format == TRUE)
1220 gst_query_parse_allocation (query, &caps, &need_pool);
1223 GST_DEBUG_OBJECT (bsink, "no caps specified");
1228 config = gst_buffer_pool_get_config (sink->pool);
1229 gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
1231 /* we do have a pool for sure (created in set_caps),
1232 * so let's propose it anyway, but also propose the allocator on its own */
1233 gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
1234 gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
1236 gst_structure_free (config);
1241 static GstFlowReturn
1242 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
1245 GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
1246 return gst_wayland_sink_render (bsink, buffer);
1250 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
1252 GstWaylandSink *sink = data;
1255 GST_LOG ("frame_redraw_cb");
1257 g_atomic_int_set (&sink->redraw_pending, FALSE);
1258 wl_callback_destroy (callback);
1259 #ifdef GST_WLSINK_ENHANCEMENT
1260 if (sink->got_eos_event && sink->use_gapless && sink->USE_TBM
1261 && sink->display->is_native_format) {
1262 g_mutex_lock (&sink->gapless_lock);
1263 g_cond_signal (&sink->gapless_cond);
1264 g_mutex_unlock (&sink->gapless_lock);
1269 static const struct wl_callback_listener frame_callback_listener = {
1270 frame_redraw_callback
1273 #ifdef GST_WLSINK_ENHANCEMENT
1275 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1278 g_return_if_fail (sink != NULL);
1279 g_return_if_fail (sink->window != NULL);
1281 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1282 gst_wl_window_set_disp_geo_method (sink->window,
1283 sink->display_geometry_method);
1284 gst_wl_window_set_orientation (sink->window, sink->orientation);
1285 gst_wl_window_set_flip (sink->window, sink->flip);
1288 /* must be called with the render lock */
1290 render_last_buffer (GstWaylandSink * sink)
1292 GstWlBuffer *wlbuffer;
1293 const GstVideoInfo *info = NULL;
1294 struct wl_surface *surface;
1295 struct wl_callback *callback;
1298 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
1299 surface = gst_wl_window_get_wl_surface (sink->window);
1301 g_atomic_int_set (&sink->redraw_pending, TRUE);
1302 callback = wl_surface_frame (surface);
1303 /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */
1304 wl_callback_add_listener (callback, &frame_callback_listener, sink);
1306 if (G_UNLIKELY (sink->video_info_changed)) {
1307 info = &sink->video_info;
1308 sink->video_info_changed = FALSE;
1310 #ifdef GST_WLSINK_ENHANCEMENT
1311 if (sink->last_buffer)
1312 gst_wl_window_render (sink->window, wlbuffer, info);
1314 if (G_UNLIKELY (info)) {
1315 gst_wl_window_set_video_info (sink->window, info);
1319 gst_wl_window_render (sink->window, wlbuffer, info);
1323 static GstFlowReturn
1324 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
1326 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1327 GstBuffer *to_render;
1328 GstWlBuffer *wlbuffer;
1329 GstFlowReturn ret = GST_FLOW_OK;
1332 g_mutex_lock (&sink->render_lock);
1334 GST_LOG_OBJECT (sink, "render buffer %p", buffer);
1336 if (G_UNLIKELY (!sink->window)) {
1337 /* ask for window handle. Unlock render_lock while doing that because
1338 * set_window_handle & friends will lock it in this context */
1339 g_mutex_unlock (&sink->render_lock);
1340 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1341 g_mutex_lock (&sink->render_lock);
1343 if (!sink->window) {
1344 /* if we were not provided a window, create one ourselves */
1346 gst_wl_window_new_toplevel (sink->display, &sink->video_info);
1349 #ifdef GST_WLSINK_ENHANCEMENT
1350 gst_wayland_sink_update_window_geometry (sink);
1351 sink->video_info_changed = TRUE;
1353 /* drop buffers until we get a frame callback */
1354 if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
1356 /* make sure that the application has called set_render_rectangle() */
1357 if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
1358 goto no_window_size;
1360 #ifdef GST_WLSINK_ENHANCEMENT
1362 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1363 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)
1364 && !(sink->use_gapless && sink->got_eos_event)) {
1365 GST_LOG_OBJECT (sink, "buffer %p has a wl_buffer from our display, " "writing directly", buffer); //buffer is from our pool and have wl_buffer
1366 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1370 GstMapInfo mem_info = GST_MAP_INFO_INIT;
1371 int size = GST_VIDEO_INFO_SIZE (&sink->video_info);
1372 mem = gst_buffer_peek_memory (to_render, 0);
1373 gst_memory_map (mem, &mem_info, GST_MAP_READ);
1375 data = mem_info.data;
1377 char file_name[128];
1379 sprintf (file_name, "/home/owner/DUMP/_WLSINK_OUT_DUMP_%2.2d.dump",
1381 ret = __write_rawdata (file_name, data, size);
1383 GST_ERROR ("_write_rawdata() failed");
1385 GST_INFO ("DUMP IMAGE %d, size (%d)", dump__cnt, size);
1386 gst_memory_unmap (mem, &mem_info);
1390 struct wl_buffer *wbuf = NULL;
1392 GST_LOG_OBJECT (sink, "buffer %p does not have a wl_buffer from our " "display, creating it", buffer); //buffer is from our pool but have not wl_buffer
1393 mem = gst_buffer_peek_memory (buffer, 0);
1394 if (gst_is_wl_shm_memory (mem)) {
1396 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1399 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); //careat GstWlBuffer and add gstbuffer, wlbuffer, display and etc
1402 } else { //buffer is not from our pool and have not wl_buffer
1404 /* we don't know how to create a wl_buffer directly from the provided
1405 * memory, so we have to copy the data to a memory that we know how
1408 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1409 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1412 if (sink->USE_TBM && sink->display->is_native_format) {
1413 /* in case of SN12 or ST12 */
1414 if (!gst_wayland_sink_get_mm_video_buf_info (sink, buffer))
1415 return GST_FLOW_ERROR;
1417 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1418 if (G_UNLIKELY (!wlbuffer) || (sink->use_gapless
1419 && sink->got_eos_event)) {
1421 gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1423 if (G_UNLIKELY (!wbuf))
1425 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1427 } else if (sink->USE_TBM && !sink->display->is_native_format) {
1429 /* sink->pool always exists (created in set_caps), but it may not
1430 * be active if upstream is not using it */
1431 if (!gst_buffer_pool_is_active (sink->pool)
1432 && !gst_buffer_pool_set_active (sink->pool, TRUE))
1433 goto activate_failed;
1435 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1436 if (ret != GST_FLOW_OK)
1440 //mem = gst_buffer_peek_memory (to_render, 0);
1441 //if (gst_is_wl_shm_memory (mem)) {
1442 GST_INFO ("to_render buffer is our buffer");
1444 /* the first time we acquire a buffer,
1445 * we need to attach a wl_buffer on it */
1446 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1447 if (G_UNLIKELY (!wlbuffer)) {
1448 mem = gst_buffer_peek_memory (to_render, 0);
1449 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1451 if (G_UNLIKELY (!wbuf))
1454 wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1457 gst_buffer_map (buffer, &src, GST_MAP_READ);
1458 gst_buffer_fill (to_render, 0, src.data, src.size);
1459 gst_buffer_unmap (buffer, &src);
1460 } else { /* USE SHM */
1461 /* sink->pool always exists (created in set_caps), but it may not
1462 * be active if upstream is not using it */
1463 if (!gst_buffer_pool_is_active (sink->pool) &&
1464 !gst_buffer_pool_set_active (sink->pool, TRUE))
1465 goto activate_failed;
1466 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1467 if (ret != GST_FLOW_OK)
1469 /* the first time we acquire a buffer,
1470 * we need to attach a wl_buffer on it */
1471 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1472 if (G_UNLIKELY (!wlbuffer)) {
1473 mem = gst_buffer_peek_memory (to_render, 0);
1474 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1476 if (G_UNLIKELY (!wbuf))
1479 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1483 gst_buffer_map (buffer, &src, GST_MAP_READ);
1484 gst_buffer_fill (to_render, 0, src.data, src.size);
1485 gst_buffer_unmap (buffer, &src);
1490 if (sink->USE_TBM && sink->display->is_native_format) {
1491 if (G_UNLIKELY (buffer == sink->last_buffer) && !(sink->use_gapless
1492 && sink->got_eos_event)) {
1493 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1496 gst_buffer_replace (&sink->last_buffer, buffer);
1499 render_last_buffer (sink);
1503 } else { /* USE SHM or normal format */
1504 /* drop double rendering */
1505 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1506 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1509 gst_buffer_replace (&sink->last_buffer, to_render);
1512 render_last_buffer (sink);
1514 if (buffer != to_render)
1515 gst_buffer_unref (to_render);
1520 #else /* open source */
1522 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1524 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1525 GST_LOG_OBJECT (sink,
1526 "buffer %p has a wl_buffer from our display, " "writing directly",
1528 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1533 struct wl_buffer *wbuf = NULL;
1535 GST_LOG_OBJECT (sink,
1536 "buffer %p does not have a wl_buffer from our " "display, creating it",
1538 mem = gst_buffer_peek_memory (buffer, 0);
1539 if (gst_is_wl_shm_memory (mem)) {
1541 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1545 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1550 /* we don't know how to create a wl_buffer directly from the provided
1551 * memory, so we have to copy the data to a memory that we know how
1554 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1555 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1557 /* sink->pool always exists (created in set_caps), but it may not
1558 * be active if upstream is not using it */
1559 if (!gst_buffer_pool_is_active (sink->pool) &&
1560 !gst_buffer_pool_set_active (sink->pool, TRUE))
1561 goto activate_failed;
1563 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1564 if (ret != GST_FLOW_OK)
1567 /* the first time we acquire a buffer,
1568 * we need to attach a wl_buffer on it */
1569 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1570 if (G_UNLIKELY (!wlbuffer)) {
1571 mem = gst_buffer_peek_memory (to_render, 0);
1572 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1574 if (G_UNLIKELY (!wbuf))
1577 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1580 gst_buffer_map (buffer, &src, GST_MAP_READ);
1581 gst_buffer_fill (to_render, 0, src.data, src.size);
1582 gst_buffer_unmap (buffer, &src);
1585 /* drop double rendering */
1586 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1587 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1591 gst_buffer_replace (&sink->last_buffer, to_render);
1592 render_last_buffer (sink);
1594 if (buffer != to_render)
1595 gst_buffer_unref (to_render);
1599 #endif /* GST_WLSINK_ENHANCEMENT */
1603 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1604 ("Window has no size set"),
1605 ("Make sure you set the size after calling set_window_handle"));
1606 ret = GST_FLOW_ERROR;
1611 GST_WARNING_OBJECT (sink, "could not create buffer");
1616 GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
1617 ret = GST_FLOW_ERROR;
1622 GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1623 ret = GST_FLOW_ERROR;
1628 g_mutex_unlock (&sink->render_lock);
1634 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1636 iface->set_window_handle = gst_wayland_sink_set_window_handle;
1637 iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1638 iface->expose = gst_wayland_sink_expose;
1639 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1640 iface->set_wl_window_wl_surface_id =
1641 gst_wayland_sink_set_wl_window_wl_surface_id;
1645 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1647 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
1648 guintptr wl_surface_id)
1650 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1652 g_return_if_fail (sink != NULL);
1654 if (sink->window != NULL) {
1655 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1658 g_mutex_lock (&sink->render_lock);
1659 g_clear_object (&sink->window);
1661 GST_INFO ("wl_surface_id %d %x", (int) wl_surface_id,
1662 (guintptr) wl_surface_id);
1664 if (wl_surface_id) {
1665 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1666 /* we cannot use our own display with an external window handle */
1667 if (G_UNLIKELY (sink->display->own_display)) {
1668 sink->display->wl_surface_id = (int) wl_surface_id;
1669 sink->window = gst_wl_window_new_in_surface (sink->display, NULL);
1672 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1673 "ignoring window handle");
1676 gst_wayland_sink_update_window_geometry (sink);
1678 g_mutex_unlock (&sink->render_lock);
1684 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1686 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1687 struct wl_surface *surface = (struct wl_surface *) handle;
1690 g_return_if_fail (sink != NULL);
1692 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1693 if (sink->window != NULL) {
1694 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1698 g_mutex_lock (&sink->render_lock);
1700 GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1703 g_clear_object (&sink->window);
1706 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1707 /* we cannot use our own display with an external window handle */
1708 if (G_UNLIKELY (sink->display->own_display)) {
1709 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
1710 ("Application did not provide a wayland display handle"),
1711 ("Now waylandsink use internal display handle "
1712 "which is created ourselves. Consider providing a "
1713 "display handle from your application with GstContext"));
1714 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1716 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1719 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1720 "ignoring window handle");
1723 g_mutex_unlock (&sink->render_lock);
1728 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1729 gint x, gint y, gint w, gint h)
1731 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1734 g_return_if_fail (sink != NULL);
1736 g_mutex_lock (&sink->render_lock);
1737 if (!sink->window) {
1738 g_mutex_unlock (&sink->render_lock);
1739 GST_WARNING_OBJECT (sink,
1740 "set_render_rectangle called without window, ignoring");
1744 GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1746 gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1748 g_mutex_unlock (&sink->render_lock);
1752 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1754 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1757 g_return_if_fail (sink != NULL);
1759 GST_DEBUG_OBJECT (sink, "expose");
1761 g_mutex_lock (&sink->render_lock);
1762 if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1763 GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1764 render_last_buffer (sink);
1766 g_mutex_unlock (&sink->render_lock);
1770 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1772 iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1773 iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1777 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1779 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1781 g_return_if_fail (sink != NULL);
1783 g_mutex_lock (&sink->render_lock);
1784 if (!sink->window || !sink->window->area_subsurface) {
1785 g_mutex_unlock (&sink->render_lock);
1786 GST_INFO_OBJECT (sink,
1787 "begin_geometry_change called without window, ignoring");
1791 wl_subsurface_set_sync (sink->window->area_subsurface);
1792 g_mutex_unlock (&sink->render_lock);
1796 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1798 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1800 g_return_if_fail (sink != NULL);
1802 g_mutex_lock (&sink->render_lock);
1803 if (!sink->window || !sink->window->area_subsurface) {
1804 g_mutex_unlock (&sink->render_lock);
1805 GST_INFO_OBJECT (sink,
1806 "end_geometry_change called without window, ignoring");
1810 wl_subsurface_set_desync (sink->window->area_subsurface);
1811 g_mutex_unlock (&sink->render_lock);
1815 plugin_init (GstPlugin * plugin)
1817 GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1818 " wayland video sink");
1820 gst_wl_shm_allocator_register ();
1822 return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1823 GST_TYPE_WAYLAND_SINK);
1826 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1829 "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,