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
145 PROP_KEEP_CAMERA_PREVIEW,
148 PROP_DISPLAY_GEOMETRY_METHOD,
156 GST_DEBUG_CATEGORY (gstwayland_debug);
157 #define GST_CAT_DEFAULT gstwayland_debug
159 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
162 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
163 ("{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, "
164 "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, "
165 #ifdef GST_WLSINK_ENHANCEMENT
168 "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"))
171 static void gst_wayland_sink_get_property (GObject * object,
172 guint prop_id, GValue * value, GParamSpec * pspec);
173 static void gst_wayland_sink_set_property (GObject * object,
174 guint prop_id, const GValue * value, GParamSpec * pspec);
175 static void gst_wayland_sink_finalize (GObject * object);
177 static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
178 GstStateChange transition);
179 static void gst_wayland_sink_set_context (GstElement * element,
180 GstContext * context);
182 static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
184 static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
185 static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink,
188 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query);
189 static gboolean gst_wayland_sink_render (GstBaseSink * bsink,
192 /* VideoOverlay interface */
193 static void gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface *
195 static void gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay,
198 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
199 guintptr wl_surface_id);
200 static void gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
201 gint x, gint y, gint w, gint h);
202 static void gst_wayland_sink_expose (GstVideoOverlay * overlay);
204 /* WaylandVideo interface */
205 static void gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface *
207 static void gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video);
208 static void gst_wayland_sink_end_geometry_change (GstWaylandVideo * video);
209 #ifdef GST_WLSINK_ENHANCEMENT
210 static gboolean gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event);
211 static void gst_wayland_sink_update_window_geometry (GstWaylandSink * sink);
212 static void render_last_buffer (GstWaylandSink * sink);
214 #define gst_wayland_sink_parent_class parent_class
215 G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
216 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
217 gst_wayland_sink_videooverlay_init)
218 G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO,
219 gst_wayland_sink_waylandvideo_init));
222 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
224 GObjectClass *gobject_class;
225 GstElementClass *gstelement_class;
226 GstBaseSinkClass *gstbasesink_class;
229 gobject_class = (GObjectClass *) klass;
230 gstelement_class = (GstElementClass *) klass;
231 gstbasesink_class = (GstBaseSinkClass *) klass;
233 gobject_class->set_property = gst_wayland_sink_set_property;
234 gobject_class->get_property = gst_wayland_sink_get_property;
235 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize);
237 gst_element_class_add_pad_template (gstelement_class,
238 gst_static_pad_template_get (&sink_template));
240 gst_element_class_set_static_metadata (gstelement_class,
241 "wayland video sink", "Sink/Video",
242 "Output to wayland surface",
243 "Sreerenj Balachandran <sreerenj.balachandran@intel.com>, "
244 "George Kiagiadakis <george.kiagiadakis@collabora.com>");
246 gstelement_class->change_state =
247 GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
248 gstelement_class->set_context =
249 GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
251 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
252 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
253 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
254 gstbasesink_class->propose_allocation =
255 GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
256 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render);
257 #ifdef GST_WLSINK_ENHANCEMENT
258 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_wayland_sink_event);
261 g_object_class_install_property (gobject_class, PROP_DISPLAY,
262 g_param_spec_string ("display", "Wayland Display name", "Wayland "
263 "display name to connect to, if not supplied via the GstContext",
264 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265 #ifdef GST_WLSINK_ENHANCEMENT
266 g_object_class_install_property (gobject_class, PROP_USE_GAPLESS,
267 g_param_spec_boolean ("use-gapless", "use flush buffer mechanism",
268 "Use gapless playback on GST_STATE_PLAYING state, "
269 "Last tbm buffer is copied and returned to codec immediately when enabled",
270 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
272 g_object_class_install_property (gobject_class, PROP_KEEP_CAMERA_PREVIEW,
273 g_param_spec_boolean ("keep-camera-preview", "use flush buffer mechanism",
274 "Last tbm buffer is copied and returned to camerasrc immediately "
275 "when state change(PAUSED_TO_READY)", FALSE,
276 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278 g_object_class_install_property (gobject_class, PROP_USE_TBM,
279 g_param_spec_boolean ("use-tbm", "use tbm buffer",
280 "Use Tizen Buffer Memory insted of Shared memory, "
281 "Memory is alloced by TBM insted of SHM when enabled", TRUE,
282 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
284 g_object_class_install_property (gobject_class, PROP_ROTATE_ANGLE,
285 g_param_spec_enum ("rotate", "Rotate angle",
286 "Rotate angle of display output",
287 GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
288 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
290 g_object_class_install_property (gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
291 g_param_spec_enum ("display-geometry-method", "Display geometry method",
292 "Geometrical method for display",
293 GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD,
294 DEF_DISPLAY_GEOMETRY_METHOD,
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
297 g_object_class_install_property (gobject_class, PROP_ORIENTATION,
298 g_param_spec_enum ("orientation",
299 "Orientation information used for ROI/ZOOM",
300 "Orientation information for display",
301 GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
302 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
304 g_object_class_install_property (gobject_class, PROP_FLIP,
305 g_param_spec_enum ("flip", "Display flip",
307 GST_TYPE_WAYLANDSINK_FLIP, DEF_DISPLAY_FLIP,
308 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
310 g_object_class_install_property (gobject_class, PROP_VISIBLE,
311 g_param_spec_boolean ("visible", "Visible",
312 "Draws screen or blacks out, true means visible, false blacks out",
313 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
319 __write_rawdata (const char *file, const void *data, unsigned int size)
323 fp = fopen (file, "wb");
327 fwrite ((char *) data, sizeof (char), size, fp);
334 gst_wayland_sink_init (GstWaylandSink * sink)
337 #ifdef GST_WLSINK_ENHANCEMENT
338 sink->use_gapless = FALSE;
339 sink->keep_camera_preview = FALSE;
340 sink->got_eos_event = FALSE;
341 sink->USE_TBM = TRUE;
342 sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
343 sink->flip = DEF_DISPLAY_FLIP;
344 sink->rotate_angle = DEGREE_0;
345 sink->orientation = DEGREE_0;
346 sink->visible = TRUE;
347 g_mutex_init (&sink->render_flush_buffer_lock);
348 g_cond_init (&sink->render_flush_buffer_cond);
350 g_mutex_init (&sink->display_lock);
351 g_mutex_init (&sink->render_lock);
354 #ifdef GST_WLSINK_ENHANCEMENT
356 gst_wayland_sink_stop_video (GstWaylandSink * sink)
359 g_return_if_fail (sink != NULL);
360 gst_wl_window_render (sink->window, NULL, NULL);
364 gst_wayland_sink_check_use_gapless (GstWaylandSink * sink)
366 g_return_val_if_fail (sink != NULL, FALSE);
367 g_return_val_if_fail (sink->display != NULL, FALSE);
369 if (sink->use_gapless && sink->got_eos_event && sink->USE_TBM
370 && sink->display->is_native_format)
377 gst_wayland_sink_need_to_make_flush_buffer (GstWaylandSink * sink)
379 g_return_val_if_fail (sink != NULL, FALSE);
380 g_return_val_if_fail (sink->display != NULL, FALSE);
382 if ((gst_wayland_sink_check_use_gapless (sink))
383 || sink->keep_camera_preview || sink->display->flush_request) {
384 sink->display->flush_request = TRUE;
391 gst_wayland_sink_update_last_buffer_geometry (GstWaylandSink * sink)
393 GstWlBuffer *wlbuffer;
395 g_return_if_fail (sink != NULL);
396 g_return_if_fail (sink->last_buffer != NULL);
397 g_return_if_fail (sink->display != NULL);
398 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
399 g_return_if_fail (wlbuffer != NULL);
400 wlbuffer->used_by_compositor = FALSE;
402 GST_LOG ("gstbuffer(%p) ref count(%d)", sink->last_buffer,
403 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
405 if (!sink->display->is_native_format) {
406 /* use SHM , use TBM with normal video format*/
407 render_last_buffer (sink);
409 gst_buffer_unref (wlbuffer->gstbuffer);
410 GST_LOG ("gstbuffer(%p) ref count(%d)", sink->last_buffer,
411 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
415 /*need to render last buffer, reuse current GstWlBuffer */
416 render_last_buffer (sink);
417 /* ref count is incresed in gst_wl_buffer_attach() of render_last_buffer(),
418 to call gst_wl_buffer_finalize(), we need to decrease buffer ref count.
419 wayland can not release buffer if we attach same buffer,
420 if we use no visible, we need to attach null buffer and wayland can release buffer,
421 so we don't need to below if() code. */
422 gst_buffer_unref (wlbuffer->gstbuffer);
424 GST_LOG ("skip rendering");
427 GST_LOG ("gstbuffer(%p) ref count(%d)", sink->last_buffer,
428 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
432 #ifdef USE_WL_FLUSH_BUFFER
434 gst_wayland_sink_make_flush_buffer (GstWlDisplay * display,
435 MMVideoBuffer * mm_video_buf)
437 GstWlFlushBuffer *flush_buffer = NULL;
443 g_return_val_if_fail (display != NULL, FALSE);
444 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
446 flush_buffer = (GstWlFlushBuffer *) malloc (sizeof (GstWlFlushBuffer));
448 GST_ERROR ("GstWlFlushBuffer alloc faile");
451 memset (flush_buffer, 0x0, sizeof (GstWlFlushBuffer));
453 display->flush_tbm_bufmgr =
454 wayland_tbm_client_get_bufmgr (display->tbm_client);
455 g_return_val_if_fail (display->flush_tbm_bufmgr != NULL, FALSE);
457 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
458 if (mm_video_buf->handle.bo[i] != NULL) {
463 bo_size = tbm_bo_size (mm_video_buf->handle.bo[i]);
464 GST_LOG ("tbm bo size: %d", bo_size);
466 bo = tbm_bo_alloc (display->flush_tbm_bufmgr, bo_size, TBM_DEVICE_CPU);
468 GST_ERROR ("alloc tbm bo(size:%d) failed: %s", bo_size,
472 GST_INFO ("flush buffer tbm_bo =(%p)", bo);
473 flush_buffer->bo[i] = bo;
474 /* get virtual address */
475 src.ptr = dst.ptr = NULL;
476 /* bo map, we can use tbm_bo_map too. */
477 src = tbm_bo_get_handle (mm_video_buf->handle.bo[i], TBM_DEVICE_CPU);
478 dst = tbm_bo_get_handle (bo, TBM_DEVICE_CPU);
479 if (!src.ptr || !dst.ptr) {
480 GST_ERROR ("get tbm bo handle failed src(%p) dst(%p): %s", src.ptr,
481 dst.ptr, strerror (errno));
482 tbm_bo_unref (mm_video_buf->handle.bo[i]);
487 memcpy (dst.ptr, src.ptr, bo_size);
489 tbm_bo_unmap (mm_video_buf->handle.bo[i]);
493 display->flush_buffer = flush_buffer;
498 gst_wayland_sink_copy_mm_video_buf_info_to_flush (GstWlDisplay * display,
499 MMVideoBuffer * mm_video_buf)
502 g_return_val_if_fail (display != NULL, FALSE);
503 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
506 ret = gst_wayland_sink_make_flush_buffer (display, mm_video_buf);
509 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
510 if (display->flush_buffer->bo[i] != NULL) {
511 display->bo[i] = display->flush_buffer->bo[i];
512 GST_LOG ("bo %p", display->bo[i]);
516 display->plane_size[i] = mm_video_buf->size[i];
517 display->stride_width[i] = mm_video_buf->stride_width[i];
518 display->stride_height[i] = mm_video_buf->stride_height[i];
519 display->native_video_size += display->plane_size[i];
527 gst_wayland_sink_add_mm_video_buf_info (GstWlDisplay * display,
528 MMVideoBuffer * mm_video_buf)
531 g_return_if_fail (display != NULL);
532 g_return_if_fail (mm_video_buf != NULL);
535 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
536 if (mm_video_buf->handle.bo[i] != NULL) {
537 display->bo[i] = mm_video_buf->handle.bo[i];
541 display->plane_size[i] = mm_video_buf->size[i];
542 display->stride_width[i] = mm_video_buf->stride_width[i];
543 display->stride_height[i] = mm_video_buf->stride_height[i];
544 display->native_video_size += display->plane_size[i];
549 gst_wayland_sink_get_mm_video_buf_info (GstWaylandSink * sink,
552 GstWlDisplay *display;
554 GstMapInfo mem_info = GST_MAP_INFO_INIT;
555 MMVideoBuffer *mm_video_buf = NULL;
557 g_return_val_if_fail (sink != NULL, FALSE);
558 g_return_val_if_fail (buffer != NULL, FALSE);
561 display = sink->display;
562 g_return_val_if_fail (sink->display != NULL, FALSE);
564 mem = gst_buffer_peek_memory (buffer, 1);
565 gst_memory_map (mem, &mem_info, GST_MAP_READ);
566 mm_video_buf = (MMVideoBuffer *) mem_info.data;
567 gst_memory_unmap (mem, &mem_info);
569 if (mm_video_buf == NULL) {
570 GST_WARNING ("mm_video_buf is NULL. Skip rendering");
573 /* assign mm_video_buf info */
574 if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
575 GST_DEBUG ("TBM bo %p %p %p", mm_video_buf->handle.bo[0],
576 mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
577 display->native_video_size = 0;
578 display->flush_request = mm_video_buf->flush_request;
579 GST_DEBUG ("flush_request value is %d", display->flush_request);
580 #ifdef USE_WL_FLUSH_BUFFER
581 if (gst_wayland_sink_need_to_make_flush_buffer (sink)) {
582 if (!gst_wayland_sink_copy_mm_video_buf_info_to_flush (display,
584 GST_ERROR ("cat not copy mm_video_buf info to flush");
590 gst_wayland_sink_add_mm_video_buf_info (display, mm_video_buf);
592 GST_ERROR ("Buffer type is not TBM");
599 gst_wayland_sink_render_flush_buffer (GstBaseSink * bsink)
601 GstWaylandSink *sink;
603 sink = GST_WAYLAND_SINK (bsink);
605 g_return_if_fail (sink != NULL);
606 g_return_if_fail (sink->last_buffer != NULL);
608 buffer = gst_buffer_copy (sink->last_buffer);
610 g_mutex_lock (&sink->render_flush_buffer_lock);
611 g_cond_wait (&sink->render_flush_buffer_cond,
612 &sink->render_flush_buffer_lock);
614 gst_wayland_sink_render (bsink, buffer);
616 gst_buffer_unref (buffer);
617 g_mutex_unlock (&sink->render_flush_buffer_lock);
621 gst_wayland_sink_gapless_render (GstBaseSink * bsink)
623 g_return_if_fail (bsink != NULL);
625 gst_wayland_sink_render_flush_buffer (bsink);
629 gst_wayland_sink_keep_camera_preview (GstBaseSink * bsink)
631 g_return_if_fail (bsink != NULL);
633 gst_wayland_sink_render_flush_buffer (bsink);
639 gst_wayland_sink_get_property (GObject * object,
640 guint prop_id, GValue * value, GParamSpec * pspec)
642 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
647 GST_OBJECT_LOCK (sink);
648 g_value_set_string (value, sink->display_name);
649 GST_OBJECT_UNLOCK (sink);
651 #ifdef GST_WLSINK_ENHANCEMENT
652 case PROP_USE_GAPLESS:
653 g_value_set_boolean (value, sink->use_gapless);
655 case PROP_KEEP_CAMERA_PREVIEW:
656 g_value_set_boolean (value, sink->keep_camera_preview);
659 g_value_set_boolean (value, sink->USE_TBM);
661 case PROP_ROTATE_ANGLE:
662 g_value_set_enum (value, sink->rotate_angle);
664 case PROP_DISPLAY_GEOMETRY_METHOD:
665 g_value_set_enum (value, sink->display_geometry_method);
667 case PROP_ORIENTATION:
668 g_value_set_enum (value, sink->orientation);
671 g_value_set_enum (value, sink->flip);
674 g_value_set_boolean (value, sink->visible);
678 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
684 gst_wayland_sink_set_property (GObject * object,
685 guint prop_id, const GValue * value, GParamSpec * pspec)
687 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
689 g_mutex_lock (&sink->render_lock);
693 GST_OBJECT_LOCK (sink);
694 sink->display_name = g_value_dup_string (value);
695 GST_OBJECT_UNLOCK (sink);
697 #ifdef GST_WLSINK_ENHANCEMENT
698 case PROP_USE_GAPLESS:
699 sink->use_gapless = g_value_get_boolean (value);
700 GST_LOG ("use gapless is (%d)", sink->use_gapless);
702 case PROP_KEEP_CAMERA_PREVIEW:
703 sink->keep_camera_preview = g_value_get_boolean (value);
704 GST_LOG ("keep_camera_preview (%d)", sink->keep_camera_preview);
707 sink->USE_TBM = g_value_get_boolean (value);
708 GST_LOG ("1:USE TBM 0: USE SHM set(%d)", sink->USE_TBM);
710 case PROP_ROTATE_ANGLE:
711 if (sink->rotate_angle == g_value_get_enum (value))
713 sink->rotate_angle = g_value_get_enum (value);
714 GST_WARNING_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
715 sink->video_info_changed = TRUE;
717 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
720 case PROP_DISPLAY_GEOMETRY_METHOD:
721 if (sink->display_geometry_method == g_value_get_enum (value))
723 sink->display_geometry_method = g_value_get_enum (value);
724 GST_WARNING_OBJECT (sink, "Display geometry method is set (%d)",
725 sink->display_geometry_method);
726 sink->video_info_changed = TRUE;
728 gst_wl_window_set_disp_geo_method (sink->window,
729 sink->display_geometry_method);
732 case PROP_ORIENTATION:
733 if (sink->orientation == g_value_get_enum (value))
735 sink->orientation = g_value_get_enum (value);
736 GST_WARNING_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
737 sink->video_info_changed = TRUE;
739 gst_wl_window_set_orientation (sink->window, sink->orientation);
743 if (sink->flip == g_value_get_enum (value))
745 sink->flip = g_value_get_enum (value);
746 GST_WARNING_OBJECT (sink, "flip is set (%d)", sink->flip);
747 sink->video_info_changed = TRUE;
749 gst_wl_window_set_flip (sink->window, sink->flip);
753 if (sink->visible == g_value_get_boolean (value))
755 sink->visible = g_value_get_boolean (value);
756 GST_WARNING_OBJECT (sink, "visible is set (%d)", sink->visible);
757 if (sink->visible && GST_STATE (sink) == GST_STATE_PAUSED) {
758 /* need to attatch last buffer */
759 sink->video_info_changed = TRUE;
760 } else if (!sink->visible && GST_STATE (sink) >= GST_STATE_PAUSED) {
763 gst_wayland_sink_stop_video (sink);
769 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
772 #ifdef GST_WLSINK_ENHANCEMENT
773 if (sink->video_info_changed && sink->window) {
774 gst_wl_window_set_video_info_change (sink->window, TRUE);
775 if (GST_STATE (sink) == GST_STATE_PAUSED)
776 gst_wayland_sink_update_last_buffer_geometry (sink);
779 g_mutex_unlock (&sink->render_lock);
784 gst_wayland_sink_finalize (GObject * object)
786 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
788 GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
790 if (sink->last_buffer)
791 gst_buffer_unref (sink->last_buffer);
793 g_object_unref (sink->display);
795 g_object_unref (sink->window);
797 gst_object_unref (sink->pool);
799 if (sink->display_name)
800 g_free (sink->display_name);
802 g_mutex_clear (&sink->display_lock);
803 g_mutex_clear (&sink->render_lock);
804 #ifdef GST_WLSINK_ENHANCEMENT
805 g_mutex_clear (&sink->render_flush_buffer_lock);
806 g_cond_clear (&sink->render_flush_buffer_cond);
809 G_OBJECT_CLASS (parent_class)->finalize (object);
812 #ifdef GST_WLSINK_ENHANCEMENT
814 gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event)
816 GstWaylandSink *sink;
817 sink = GST_WAYLAND_SINK (bsink);
819 switch (GST_EVENT_TYPE (event)) {
821 GST_LOG ("get EOS event..state is %d", GST_STATE (sink));
822 sink->got_eos_event = TRUE;
823 if (gst_wayland_sink_check_use_gapless (sink)) {
824 gst_wayland_sink_gapless_render (bsink);
825 sink->got_eos_event = FALSE;
827 sink->got_eos_event = FALSE;
832 return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
836 /* must be called with the display_lock */
838 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
839 GstContext * context)
841 struct wl_display *display;
842 GError *error = NULL;
845 display = gst_wayland_display_handle_context_get_handle (context);
846 sink->display = gst_wl_display_new_existing (display, FALSE, &error);
849 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
850 ("Could not set display handle"),
851 ("Failed to use the external wayland display: '%s'", error->message));
852 g_error_free (error);
854 #ifdef GST_WLSINK_ENHANCEMENT
855 sink->display->USE_TBM = sink->USE_TBM;
860 gst_wayland_sink_find_display (GstWaylandSink * sink)
864 GstContext *context = NULL;
865 GError *error = NULL;
869 g_mutex_lock (&sink->display_lock);
871 if (!sink->display) {
872 /* first query upstream for the needed display handle */
873 query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
874 if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
875 gst_query_parse_context (query, &context);
876 gst_wayland_sink_set_display_from_context (sink, context);
878 gst_query_unref (query);
880 if (G_LIKELY (!sink->display)) {
881 /* now ask the application to set the display handle */
882 msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
883 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
885 g_mutex_unlock (&sink->display_lock);
886 gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
887 /* at this point we expect gst_wayland_sink_set_context
888 * to get called and fill sink->display */
889 g_mutex_lock (&sink->display_lock);
891 if (!sink->display) {
892 /* if the application didn't set a display, let's create it ourselves */
893 GST_OBJECT_LOCK (sink);
894 sink->display = gst_wl_display_new (sink->display_name, &error);
895 GST_OBJECT_UNLOCK (sink);
898 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
899 ("Could not initialise Wayland output"),
900 ("Failed to create GstWlDisplay: '%s'", error->message));
901 g_error_free (error);
904 #ifdef GST_WLSINK_ENHANCEMENT
906 sink->display->USE_TBM = sink->USE_TBM;
912 g_mutex_unlock (&sink->display_lock);
917 static GstStateChangeReturn
918 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
920 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
921 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
924 switch (transition) {
925 case GST_STATE_CHANGE_NULL_TO_READY:
926 if (!gst_wayland_sink_find_display (sink))
927 return GST_STATE_CHANGE_FAILURE;
933 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
934 if (ret == GST_STATE_CHANGE_FAILURE)
937 switch (transition) {
938 case GST_STATE_CHANGE_PAUSED_TO_READY:
939 #ifdef GST_WLSINK_ENHANCEMENT
940 if (sink->keep_camera_preview) {
942 if (!gst_wl_window_is_toplevel (sink->window)) {
943 GstBaseSink *bsink = GST_BASE_SINK (element);
944 gst_wayland_sink_keep_camera_preview (bsink);
950 gst_buffer_replace (&sink->last_buffer, NULL);
952 if (gst_wl_window_is_toplevel (sink->window)) {
953 GST_DEBUG ("internal window");
954 g_clear_object (&sink->window);
956 /* remove buffer from surface, show nothing */
957 GST_DEBUG ("external window");
958 gst_wl_window_render (sink->window, NULL, NULL);
962 case GST_STATE_CHANGE_READY_TO_NULL:
963 g_mutex_lock (&sink->display_lock);
964 /* If we had a toplevel window, we most likely have our own connection
965 * to the display too, and it is a good idea to disconnect and allow
966 * potentially the application to embed us with GstVideoOverlay
967 * (which requires to re-use the same display connection as the parent
968 * surface). If we didn't have a toplevel window, then the display
969 * connection that we have is definitely shared with the application
970 * and it's better to keep it around (together with the window handle)
971 * to avoid requesting them again from the application if/when we are
972 * restarted (GstVideoOverlay behaves like that in other sinks)
974 if (sink->display && !sink->window) { /* -> the window was toplevel */
975 g_clear_object (&sink->display);
977 g_mutex_unlock (&sink->display_lock);
978 g_clear_object (&sink->pool);
988 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
990 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
993 if (gst_context_has_context_type (context,
994 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
995 g_mutex_lock (&sink->display_lock);
996 if (G_LIKELY (!sink->display))
997 gst_wayland_sink_set_display_from_context (sink, context);
999 GST_WARNING_OBJECT (element, "changing display handle is not supported");
1000 #ifdef GST_WLSINK_ENHANCEMENT
1001 g_mutex_unlock (&sink->display_lock);
1005 g_mutex_unlock (&sink->display_lock);
1008 if (GST_ELEMENT_CLASS (parent_class)->set_context)
1009 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1013 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
1015 GstWaylandSink *sink;
1019 sink = GST_WAYLAND_SINK (bsink);
1021 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
1023 g_mutex_lock (&sink->display_lock);
1025 if (sink->display) {
1026 GValue list = G_VALUE_INIT;
1027 GValue value = G_VALUE_INIT;
1030 #ifdef GST_WLSINK_ENHANCEMENT
1033 enum wl_shm_format fmt;
1035 g_value_init (&list, GST_TYPE_LIST);
1036 g_value_init (&value, G_TYPE_STRING);
1037 #ifdef GST_WLSINK_ENHANCEMENT
1038 if (sink->display->USE_TBM)
1039 formats = sink->display->tbm_formats;
1042 formats = sink->display->formats;
1044 for (i = 0; i < formats->len; i++) {
1045 #ifdef GST_WLSINK_ENHANCEMENT
1046 if (sink->USE_TBM) {
1047 tbm_fmt = g_array_index (formats, uint32_t, i);
1048 g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt));
1049 gst_value_list_append_value (&list, &value);
1050 /* TBM doesn't support SN12 and ST12. So we add SN12 and ST12 manually as supported format.
1051 * SN12 is same with NV12, ST12 is same with NV12MT
1053 if (tbm_fmt == TBM_FORMAT_NV12) {
1054 g_value_set_string (&value,
1055 gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
1056 gst_value_list_append_value (&list, &value);
1057 } else if (tbm_fmt == TBM_FORMAT_NV12MT) {
1058 g_value_set_string (&value,
1059 gst_video_format_to_string (GST_VIDEO_FORMAT_ST12));
1060 gst_value_list_append_value (&list, &value);
1062 } else { /* USE SHM */
1063 fmt = g_array_index (formats, uint32_t, i);
1064 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
1065 gst_value_list_append_value (&list, &value);
1067 #else /* open source */
1068 fmt = g_array_index (formats, uint32_t, i);
1069 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
1070 gst_value_list_append_value (&list, &value);
1074 caps = gst_caps_make_writable (caps);
1075 gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
1077 GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
1078 g_value_unset (&value);
1079 g_value_unset (&list);
1083 g_mutex_unlock (&sink->display_lock);
1086 GstCaps *intersection;
1089 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1090 gst_caps_unref (caps);
1091 caps = intersection;
1098 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
1100 GstWaylandSink *sink;
1101 GstBufferPool *newpool;
1103 #ifdef GST_WLSINK_ENHANCEMENT
1104 uint32_t tbm_format;
1106 enum wl_shm_format format;
1110 GstStructure *structure;
1111 GstWlShmAllocator *self = NULL;
1115 sink = GST_WAYLAND_SINK (bsink);
1117 GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
1119 /* extract info from caps */
1120 if (!gst_video_info_from_caps (&info, caps))
1121 goto invalid_format;
1122 #ifdef GST_WLSINK_ENHANCEMENT
1123 if (sink->USE_TBM) {
1125 gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info));
1126 if ((gint) tbm_format == -1)
1127 goto invalid_format;
1129 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1130 if ((gint) format == -1)
1131 goto invalid_format;
1133 #else /* open source */
1134 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1136 if ((gint) format == -1)
1137 goto invalid_format;
1140 /* verify we support the requested format */
1141 #ifdef GST_WLSINK_ENHANCEMENT
1142 if (sink->display->USE_TBM) {
1143 GST_INFO ("USE TBM FORMAT");
1144 formats = sink->display->tbm_formats;
1145 for (i = 0; i < formats->len; i++) {
1146 if (g_array_index (formats, uint32_t, i) == tbm_format)
1149 } else { /* USE SHM */
1150 GST_INFO ("USE SHM FORMAT");
1151 formats = sink->display->formats;
1152 for (i = 0; i < formats->len; i++) {
1153 if (g_array_index (formats, uint32_t, i) == format)
1157 #else /* open source */
1159 formats = sink->display->formats;
1160 for (i = 0; i < formats->len; i++) {
1161 if (g_array_index (formats, uint32_t, i) == format)
1165 if (i >= formats->len)
1166 goto unsupported_format;
1168 #ifdef GST_WLSINK_ENHANCEMENT
1169 if (sink->USE_TBM) {
1170 if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
1171 GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
1172 sink->display->is_native_format = TRUE;
1174 /* store the video info */
1175 sink->video_info = info;
1176 sink->video_info_changed = TRUE;
1178 sink->display->is_native_format = FALSE;
1179 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1180 self->display = sink->display;
1181 /* create a new pool for the new configuration */
1182 newpool = gst_video_buffer_pool_new ();
1186 structure = gst_buffer_pool_get_config (newpool);
1187 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1188 gst_buffer_pool_config_set_allocator (structure,
1189 gst_wl_shm_allocator_get (), NULL);
1190 if (!gst_buffer_pool_set_config (newpool, structure))
1193 /* store the video info */
1194 sink->video_info = info;
1195 sink->video_info_changed = TRUE;
1197 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1198 gst_object_unref (newpool);
1200 } else { /* USE SHM */
1202 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1203 self->display = sink->display;
1205 /* create a new pool for the new configuration */
1206 newpool = gst_video_buffer_pool_new ();
1210 structure = gst_buffer_pool_get_config (newpool);
1211 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1212 gst_buffer_pool_config_set_allocator (structure,
1213 gst_wl_shm_allocator_get (), NULL);
1214 if (!gst_buffer_pool_set_config (newpool, structure))
1217 /* store the video info */
1218 sink->video_info = info;
1219 sink->video_info_changed = TRUE;
1221 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1222 gst_object_unref (newpool);
1224 #else /*open source */
1225 /* create a new pool for the new configuration */
1226 newpool = gst_video_buffer_pool_new ();
1230 structure = gst_buffer_pool_get_config (newpool);
1231 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1232 gst_buffer_pool_config_set_allocator (structure,
1233 gst_wl_shm_allocator_get (), NULL);
1234 if (!gst_buffer_pool_set_config (newpool, structure))
1237 /* store the video info */
1238 sink->video_info = info;
1239 sink->video_info_changed = TRUE;
1241 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1242 gst_object_unref (newpool);
1249 GST_DEBUG_OBJECT (sink,
1250 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1255 #ifdef GST_WLSINK_ENHANCEMENT
1257 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1258 gst_wl_tbm_format_to_string (tbm_format));
1260 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1261 gst_wl_shm_format_to_string (format));
1262 #else /*open source */
1263 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1264 gst_wl_shm_format_to_string (format));
1270 GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1275 GST_DEBUG_OBJECT (bsink, "failed setting config");
1276 gst_object_unref (newpool);
1282 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1284 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1285 GstStructure *config;
1286 guint size, min_bufs, max_bufs;
1287 #ifdef GST_WLSINK_ENHANCEMENT
1292 if (sink->USE_TBM) {
1293 if (sink->display->is_native_format == TRUE)
1296 gst_query_parse_allocation (query, &caps, &need_pool);
1299 GST_DEBUG_OBJECT (bsink, "no caps specified");
1304 config = gst_buffer_pool_get_config (sink->pool);
1305 gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
1307 /* we do have a pool for sure (created in set_caps),
1308 * so let's propose it anyway, but also propose the allocator on its own */
1309 gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
1310 gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
1312 gst_structure_free (config);
1317 static GstFlowReturn
1318 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
1321 GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
1322 return gst_wayland_sink_render (bsink, buffer);
1326 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
1328 GstWaylandSink *sink = data;
1331 GST_LOG ("frame_redraw_cb");
1333 g_atomic_int_set (&sink->redraw_pending, FALSE);
1334 wl_callback_destroy (callback);
1335 #ifdef GST_WLSINK_ENHANCEMENT
1336 if (gst_wayland_sink_check_use_gapless (sink) || sink->keep_camera_preview) {
1337 g_mutex_lock (&sink->render_flush_buffer_lock);
1338 g_cond_signal (&sink->render_flush_buffer_cond);
1339 g_mutex_unlock (&sink->render_flush_buffer_lock);
1344 static const struct wl_callback_listener frame_callback_listener = {
1345 frame_redraw_callback
1348 #ifdef GST_WLSINK_ENHANCEMENT
1350 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1353 g_return_if_fail (sink != NULL);
1354 g_return_if_fail (sink->window != NULL);
1356 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1357 gst_wl_window_set_disp_geo_method (sink->window,
1358 sink->display_geometry_method);
1359 gst_wl_window_set_orientation (sink->window, sink->orientation);
1360 gst_wl_window_set_flip (sink->window, sink->flip);
1363 /* must be called with the render lock */
1365 render_last_buffer (GstWaylandSink * sink)
1367 GstWlBuffer *wlbuffer;
1368 const GstVideoInfo *info = NULL;
1369 struct wl_surface *surface;
1370 struct wl_callback *callback;
1372 #ifdef GST_WLSINK_ENHANCEMENT
1373 g_return_if_fail (sink->last_buffer != NULL);
1374 g_return_if_fail (sink->window != NULL);
1377 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
1378 surface = gst_wl_window_get_wl_surface (sink->window);
1380 g_atomic_int_set (&sink->redraw_pending, TRUE);
1381 callback = wl_surface_frame (surface);
1382 /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */
1383 wl_callback_add_listener (callback, &frame_callback_listener, sink);
1385 if (G_UNLIKELY (sink->video_info_changed)) {
1386 #ifdef GST_WLSINK_ENHANCEMENT
1387 gst_wl_window_set_video_info_change (sink->window, TRUE);
1389 info = &sink->video_info;
1390 sink->video_info_changed = FALSE;
1392 gst_wl_window_render (sink->window, wlbuffer, info);
1395 static GstFlowReturn
1396 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
1398 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1399 GstBuffer *to_render;
1400 GstWlBuffer *wlbuffer;
1401 GstFlowReturn ret = GST_FLOW_OK;
1404 g_mutex_lock (&sink->render_lock);
1406 GST_LOG_OBJECT (sink, "render gstbuffer %p, ref_count(%d)", buffer,
1407 GST_OBJECT_REFCOUNT_VALUE (buffer));
1409 if (G_UNLIKELY (!sink->window)) {
1410 /* ask for window handle. Unlock render_lock while doing that because
1411 * set_window_handle & friends will lock it in this context */
1412 g_mutex_unlock (&sink->render_lock);
1413 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1414 g_mutex_lock (&sink->render_lock);
1416 if (!sink->window) {
1417 /* if we were not provided a window, create one ourselves */
1419 gst_wl_window_new_toplevel (sink->display, &sink->video_info);
1421 #ifdef GST_WLSINK_ENHANCEMENT
1422 gst_wayland_sink_update_window_geometry (sink);
1425 /* drop buffers until we get a frame callback */
1426 if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
1428 /* make sure that the application has called set_render_rectangle() */
1429 if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
1430 goto no_window_size;
1432 #ifdef GST_WLSINK_ENHANCEMENT
1434 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1435 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)
1436 && !(gst_wayland_sink_check_use_gapless (sink))
1437 && !sink->keep_camera_preview) {
1438 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
1439 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1443 GstMapInfo mem_info = GST_MAP_INFO_INIT;
1444 int size = GST_VIDEO_INFO_SIZE (&sink->video_info);
1445 mem = gst_buffer_peek_memory (to_render, 0);
1446 gst_memory_map (mem, &mem_info, GST_MAP_READ);
1448 data = mem_info.data;
1450 char file_name[128];
1452 sprintf (file_name, "/home/owner/DUMP/_WLSINK_OUT_DUMP_%2.2d.dump",
1454 ret = __write_rawdata (file_name, data, size);
1456 GST_ERROR ("_write_rawdata() failed");
1458 GST_INFO ("DUMP IMAGE %d, size (%d)", dump__cnt, size);
1459 gst_memory_unmap (mem, &mem_info);
1463 struct wl_buffer *wbuf = NULL;
1465 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
1466 mem = gst_buffer_peek_memory (buffer, 0);
1467 if (gst_is_wl_shm_memory (mem)) {
1469 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1472 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); //careat GstWlBuffer and add gstbuffer, wlbuffer, display and etc
1475 } else { //buffer is not from our pool and have not wl_buffer
1477 /* we don't know how to create a wl_buffer directly from the provided
1478 * memory, so we have to copy the data to a memory that we know how
1481 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1482 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1485 if (sink->USE_TBM && sink->display->is_native_format) {
1486 /* in case of SN12 or ST12 */
1487 if (!gst_wayland_sink_get_mm_video_buf_info (sink, buffer))
1488 return GST_FLOW_ERROR;
1490 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1491 /* last_buffer from gaplasee have wlbuffer */
1492 if (G_UNLIKELY (!wlbuffer)
1493 || (gst_wayland_sink_check_use_gapless (sink))
1494 || sink->keep_camera_preview) {
1496 gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1498 if (G_UNLIKELY (!wbuf))
1500 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1502 } else if (sink->USE_TBM && !sink->display->is_native_format) {
1504 /* sink->pool always exists (created in set_caps), but it may not
1505 * be active if upstream is not using it */
1506 if (!gst_buffer_pool_is_active (sink->pool)
1507 && !gst_buffer_pool_set_active (sink->pool, TRUE))
1508 goto activate_failed;
1510 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1511 if (ret != GST_FLOW_OK)
1515 //mem = gst_buffer_peek_memory (to_render, 0);
1516 //if (gst_is_wl_shm_memory (mem)) {
1517 GST_INFO ("to_render buffer is our buffer");
1519 /* the first time we acquire a buffer,
1520 * we need to attach a wl_buffer on it */
1521 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1522 if (G_UNLIKELY (!wlbuffer)) {
1523 mem = gst_buffer_peek_memory (to_render, 0);
1524 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1526 if (G_UNLIKELY (!wbuf))
1529 wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1532 gst_buffer_map (buffer, &src, GST_MAP_READ);
1533 gst_buffer_fill (to_render, 0, src.data, src.size);
1534 gst_buffer_unmap (buffer, &src);
1535 } else { /* USE SHM */
1536 /* sink->pool always exists (created in set_caps), but it may not
1537 * be active if upstream is not using it */
1538 if (!gst_buffer_pool_is_active (sink->pool) &&
1539 !gst_buffer_pool_set_active (sink->pool, TRUE))
1540 goto activate_failed;
1541 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1542 if (ret != GST_FLOW_OK)
1544 /* the first time we acquire a buffer,
1545 * we need to attach a wl_buffer on it */
1546 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1547 if (G_UNLIKELY (!wlbuffer)) {
1548 mem = gst_buffer_peek_memory (to_render, 0);
1549 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1551 if (G_UNLIKELY (!wbuf))
1554 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1558 gst_buffer_map (buffer, &src, GST_MAP_READ);
1559 gst_buffer_fill (to_render, 0, src.data, src.size);
1560 gst_buffer_unmap (buffer, &src);
1565 if (sink->USE_TBM && sink->display->is_native_format) {
1566 if ((G_UNLIKELY (buffer == sink->last_buffer)
1567 && !(gst_wayland_sink_check_use_gapless (sink)))
1568 || (G_UNLIKELY (buffer == sink->last_buffer)
1569 && !sink->keep_camera_preview)) {
1570 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1573 gst_buffer_replace (&sink->last_buffer, buffer);
1575 if (sink->visible) {
1576 render_last_buffer (sink);
1578 GST_LOG ("skip rendering");
1583 } else { /* USE SHM or normal format */
1584 /* drop double rendering */
1585 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1586 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1589 gst_buffer_replace (&sink->last_buffer, to_render);
1592 render_last_buffer (sink);
1594 if (buffer != to_render)
1595 gst_buffer_unref (to_render);
1600 #else /* open source */
1602 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1604 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1605 GST_LOG_OBJECT (sink,
1606 "buffer %p has a wl_buffer from our display, " "writing directly",
1608 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1613 struct wl_buffer *wbuf = NULL;
1615 GST_LOG_OBJECT (sink,
1616 "buffer %p does not have a wl_buffer from our " "display, creating it",
1618 mem = gst_buffer_peek_memory (buffer, 0);
1619 if (gst_is_wl_shm_memory (mem)) {
1621 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1625 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1630 /* we don't know how to create a wl_buffer directly from the provided
1631 * memory, so we have to copy the data to a memory that we know how
1634 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1635 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1637 /* sink->pool always exists (created in set_caps), but it may not
1638 * be active if upstream is not using it */
1639 if (!gst_buffer_pool_is_active (sink->pool) &&
1640 !gst_buffer_pool_set_active (sink->pool, TRUE))
1641 goto activate_failed;
1643 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1644 if (ret != GST_FLOW_OK)
1647 /* the first time we acquire a buffer,
1648 * we need to attach a wl_buffer on it */
1649 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1650 if (G_UNLIKELY (!wlbuffer)) {
1651 mem = gst_buffer_peek_memory (to_render, 0);
1652 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1654 if (G_UNLIKELY (!wbuf))
1657 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1660 gst_buffer_map (buffer, &src, GST_MAP_READ);
1661 gst_buffer_fill (to_render, 0, src.data, src.size);
1662 gst_buffer_unmap (buffer, &src);
1665 /* drop double rendering */
1666 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1667 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1671 gst_buffer_replace (&sink->last_buffer, to_render);
1672 render_last_buffer (sink);
1674 if (buffer != to_render)
1675 gst_buffer_unref (to_render);
1679 #endif /* GST_WLSINK_ENHANCEMENT */
1683 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1684 ("Window has no size set"),
1685 ("Make sure you set the size after calling set_window_handle"));
1686 ret = GST_FLOW_ERROR;
1691 GST_WARNING_OBJECT (sink, "could not create buffer");
1696 GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
1697 ret = GST_FLOW_ERROR;
1702 GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1703 ret = GST_FLOW_ERROR;
1708 g_mutex_unlock (&sink->render_lock);
1714 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1716 iface->set_window_handle = gst_wayland_sink_set_window_handle;
1717 iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1718 iface->expose = gst_wayland_sink_expose;
1719 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1720 iface->set_wl_window_wl_surface_id =
1721 gst_wayland_sink_set_wl_window_wl_surface_id;
1725 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1727 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
1728 guintptr wl_surface_id)
1730 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1732 g_return_if_fail (sink != NULL);
1734 if (sink->window != NULL) {
1735 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1738 g_mutex_lock (&sink->render_lock);
1739 g_clear_object (&sink->window);
1741 GST_INFO ("wl_surface_id %d %x", (int) wl_surface_id,
1742 (guintptr) wl_surface_id);
1744 if (wl_surface_id) {
1745 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1746 /* we cannot use our own display with an external window handle */
1747 if (G_UNLIKELY (sink->display->own_display)) {
1748 sink->display->wl_surface_id = (int) wl_surface_id;
1749 sink->window = gst_wl_window_new_in_surface (sink->display, NULL);
1752 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1753 "ignoring window handle");
1756 gst_wayland_sink_update_window_geometry (sink);
1757 g_mutex_unlock (&sink->render_lock);
1763 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1765 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1766 struct wl_surface *surface = (struct wl_surface *) handle;
1769 g_return_if_fail (sink != NULL);
1771 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1772 if (sink->window != NULL) {
1773 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1777 g_mutex_lock (&sink->render_lock);
1779 GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1782 g_clear_object (&sink->window);
1785 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1786 /* we cannot use our own display with an external window handle */
1787 if (G_UNLIKELY (sink->display->own_display)) {
1788 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
1789 ("Application did not provide a wayland display handle"),
1790 ("Now waylandsink use internal display handle "
1791 "which is created ourselves. Consider providing a "
1792 "display handle from your application with GstContext"));
1793 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1795 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1798 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1799 "ignoring window handle");
1802 #ifdef GST_WLSINK_ENHANCEMENT
1803 gst_wayland_sink_update_window_geometry (sink);
1805 g_mutex_unlock (&sink->render_lock);
1810 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1811 gint x, gint y, gint w, gint h)
1813 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1816 g_return_if_fail (sink != NULL);
1818 g_mutex_lock (&sink->render_lock);
1819 if (!sink->window) {
1820 g_mutex_unlock (&sink->render_lock);
1821 GST_WARNING_OBJECT (sink,
1822 "set_render_rectangle called without window, ignoring");
1826 GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1828 gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1830 g_mutex_unlock (&sink->render_lock);
1834 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1836 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1839 g_return_if_fail (sink != NULL);
1841 GST_DEBUG_OBJECT (sink, "expose");
1843 g_mutex_lock (&sink->render_lock);
1844 if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1845 GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1846 render_last_buffer (sink);
1848 g_mutex_unlock (&sink->render_lock);
1852 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1854 iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1855 iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1859 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1861 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1863 g_return_if_fail (sink != NULL);
1865 g_mutex_lock (&sink->render_lock);
1866 if (!sink->window || !sink->window->area_subsurface) {
1867 g_mutex_unlock (&sink->render_lock);
1868 GST_INFO_OBJECT (sink,
1869 "begin_geometry_change called without window, ignoring");
1873 wl_subsurface_set_sync (sink->window->area_subsurface);
1874 g_mutex_unlock (&sink->render_lock);
1878 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1880 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1882 g_return_if_fail (sink != NULL);
1884 g_mutex_lock (&sink->render_lock);
1885 if (!sink->window || !sink->window->area_subsurface) {
1886 g_mutex_unlock (&sink->render_lock);
1887 GST_INFO_OBJECT (sink,
1888 "end_geometry_change called without window, ignoring");
1892 wl_subsurface_set_desync (sink->window->area_subsurface);
1893 g_mutex_unlock (&sink->render_lock);
1897 plugin_init (GstPlugin * plugin)
1899 GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1900 " wayland video sink");
1902 gst_wl_shm_allocator_register ();
1904 return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1905 GST_TYPE_WAYLAND_SINK);
1908 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1911 "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,