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);
398 GST_DEBUG ("gstbuffer ref count is %d",
399 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
400 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
402 wlbuffer->used_by_compositor = FALSE;
403 /*need to render last buffer, reuse current GstWlBuffer */
404 render_last_buffer (sink);
405 /* ref count is incresed in gst_wl_buffer_attach() of render_last_buffer(),
406 to call gst_wl_buffer_finalize(), we need to decrease buffer ref count.
407 wayland can not release buffer if we attach same buffer,
408 if we use no visible, we need to attach null buffer and wayland can release buffer,
409 so we don't need to below if() code. */
411 gst_buffer_unref (wlbuffer->gstbuffer);
414 #ifdef USE_WL_FLUSH_BUFFER
416 gst_wayland_sink_make_flush_buffer (GstWlDisplay * display,
417 MMVideoBuffer * mm_video_buf)
419 GstWlFlushBuffer *flush_buffer = NULL;
425 g_return_val_if_fail (display != NULL, FALSE);
426 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
428 flush_buffer = (GstWlFlushBuffer *) malloc (sizeof (GstWlFlushBuffer));
430 GST_ERROR ("GstWlFlushBuffer alloc faile");
433 memset (flush_buffer, 0x0, sizeof (GstWlFlushBuffer));
435 display->flush_tbm_bufmgr =
436 wayland_tbm_client_get_bufmgr (display->tbm_client);
437 g_return_val_if_fail (display->flush_tbm_bufmgr != NULL, FALSE);
439 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
440 if (mm_video_buf->handle.bo[i] != NULL) {
445 bo_size = tbm_bo_size (mm_video_buf->handle.bo[i]);
446 GST_LOG ("tbm bo size: %d", bo_size);
448 bo = tbm_bo_alloc (display->flush_tbm_bufmgr, bo_size, TBM_DEVICE_CPU);
450 GST_ERROR ("alloc tbm bo(size:%d) failed: %s", bo_size,
454 GST_INFO ("flush buffer tbm_bo =(%p)", bo);
455 flush_buffer->bo[i] = bo;
456 /* get virtual address */
457 src.ptr = dst.ptr = NULL;
458 /* bo map, we can use tbm_bo_map too. */
459 src = tbm_bo_get_handle (mm_video_buf->handle.bo[i], TBM_DEVICE_CPU);
460 dst = tbm_bo_get_handle (bo, TBM_DEVICE_CPU);
461 if (!src.ptr || !dst.ptr) {
462 GST_ERROR ("get tbm bo handle failed src(%p) dst(%p): %s", src.ptr,
463 dst.ptr, strerror (errno));
464 tbm_bo_unref (mm_video_buf->handle.bo[i]);
469 memcpy (dst.ptr, src.ptr, bo_size);
471 tbm_bo_unmap (mm_video_buf->handle.bo[i]);
475 display->flush_buffer = flush_buffer;
480 gst_wayland_sink_copy_mm_video_buf_info_to_flush (GstWlDisplay * display,
481 MMVideoBuffer * mm_video_buf)
484 g_return_val_if_fail (display != NULL, FALSE);
485 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
488 ret = gst_wayland_sink_make_flush_buffer (display, mm_video_buf);
491 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
492 if (display->flush_buffer->bo[i] != NULL) {
493 display->bo[i] = display->flush_buffer->bo[i];
494 GST_LOG ("bo %p", display->bo[i]);
498 display->plane_size[i] = mm_video_buf->size[i];
499 display->stride_width[i] = mm_video_buf->stride_width[i];
500 display->stride_height[i] = mm_video_buf->stride_height[i];
501 display->native_video_size += display->plane_size[i];
509 gst_wayland_sink_add_mm_video_buf_info (GstWlDisplay * display,
510 MMVideoBuffer * mm_video_buf)
513 g_return_if_fail (display != NULL);
514 g_return_if_fail (mm_video_buf != NULL);
517 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
518 if (mm_video_buf->handle.bo[i] != NULL) {
519 display->bo[i] = mm_video_buf->handle.bo[i];
523 display->plane_size[i] = mm_video_buf->size[i];
524 display->stride_width[i] = mm_video_buf->stride_width[i];
525 display->stride_height[i] = mm_video_buf->stride_height[i];
526 display->native_video_size += display->plane_size[i];
531 gst_wayland_sink_get_mm_video_buf_info (GstWaylandSink * sink,
534 GstWlDisplay *display;
536 GstMapInfo mem_info = GST_MAP_INFO_INIT;
537 MMVideoBuffer *mm_video_buf = NULL;
539 g_return_val_if_fail (sink != NULL, FALSE);
540 g_return_val_if_fail (buffer != NULL, FALSE);
543 display = sink->display;
544 g_return_val_if_fail (sink->display != NULL, FALSE);
546 mem = gst_buffer_peek_memory (buffer, 1);
547 gst_memory_map (mem, &mem_info, GST_MAP_READ);
548 mm_video_buf = (MMVideoBuffer *) mem_info.data;
549 gst_memory_unmap (mem, &mem_info);
551 if (mm_video_buf == NULL) {
552 GST_WARNING ("mm_video_buf is NULL. Skip rendering");
555 /* assign mm_video_buf info */
556 if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
557 GST_DEBUG ("TBM bo %p %p %p", mm_video_buf->handle.bo[0],
558 mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
559 display->native_video_size = 0;
560 display->flush_request = mm_video_buf->flush_request;
561 GST_DEBUG ("flush_request value is %d", display->flush_request);
562 #ifdef USE_WL_FLUSH_BUFFER
563 if (gst_wayland_sink_need_to_make_flush_buffer (sink)) {
564 if (!gst_wayland_sink_copy_mm_video_buf_info_to_flush (display,
566 GST_ERROR ("cat not copy mm_video_buf info to flush");
572 gst_wayland_sink_add_mm_video_buf_info (display, mm_video_buf);
574 GST_ERROR ("Buffer type is not TBM");
581 gst_wayland_sink_render_flush_buffer (GstBaseSink * bsink)
583 GstWaylandSink *sink;
585 sink = GST_WAYLAND_SINK (bsink);
587 g_return_if_fail (sink != NULL);
588 g_return_if_fail (sink->last_buffer != NULL);
590 buffer = gst_buffer_copy (sink->last_buffer);
592 g_mutex_lock (&sink->render_flush_buffer_lock);
593 g_cond_wait (&sink->render_flush_buffer_cond,
594 &sink->render_flush_buffer_lock);
596 gst_wayland_sink_render (bsink, buffer);
598 gst_buffer_unref (buffer);
599 g_mutex_unlock (&sink->render_flush_buffer_lock);
603 gst_wayland_sink_gapless_render (GstBaseSink * bsink)
605 g_return_if_fail (bsink != NULL);
607 gst_wayland_sink_render_flush_buffer (bsink);
611 gst_wayland_sink_keep_camera_preview (GstBaseSink * bsink)
613 g_return_if_fail (bsink != NULL);
615 gst_wayland_sink_render_flush_buffer (bsink);
621 gst_wayland_sink_get_property (GObject * object,
622 guint prop_id, GValue * value, GParamSpec * pspec)
624 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
629 GST_OBJECT_LOCK (sink);
630 g_value_set_string (value, sink->display_name);
631 GST_OBJECT_UNLOCK (sink);
633 #ifdef GST_WLSINK_ENHANCEMENT
634 case PROP_USE_GAPLESS:
635 g_value_set_boolean (value, sink->use_gapless);
637 case PROP_KEEP_CAMERA_PREVIEW:
638 g_value_set_boolean (value, sink->keep_camera_preview);
641 g_value_set_boolean (value, sink->USE_TBM);
643 case PROP_ROTATE_ANGLE:
644 g_value_set_enum (value, sink->rotate_angle);
646 case PROP_DISPLAY_GEOMETRY_METHOD:
647 g_value_set_enum (value, sink->display_geometry_method);
649 case PROP_ORIENTATION:
650 g_value_set_enum (value, sink->orientation);
653 g_value_set_enum (value, sink->flip);
656 g_value_set_boolean (value, sink->visible);
660 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
666 gst_wayland_sink_set_property (GObject * object,
667 guint prop_id, const GValue * value, GParamSpec * pspec)
669 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
671 g_mutex_lock (&sink->render_lock);
675 GST_OBJECT_LOCK (sink);
676 sink->display_name = g_value_dup_string (value);
677 GST_OBJECT_UNLOCK (sink);
679 #ifdef GST_WLSINK_ENHANCEMENT
680 case PROP_USE_GAPLESS:
681 sink->use_gapless = g_value_get_boolean (value);
682 GST_LOG ("use gapless is (%d)", sink->use_gapless);
684 case PROP_KEEP_CAMERA_PREVIEW:
685 sink->keep_camera_preview = g_value_get_boolean (value);
686 GST_LOG ("keep_camera_preview (%d)", sink->keep_camera_preview);
689 sink->USE_TBM = g_value_get_boolean (value);
690 GST_LOG ("1:USE TBM 0: USE SHM set(%d)", sink->USE_TBM);
692 case PROP_ROTATE_ANGLE:
693 if (sink->rotate_angle == g_value_get_enum (value))
695 sink->rotate_angle = g_value_get_enum (value);
696 GST_WARNING_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
697 sink->video_info_changed = TRUE;
699 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
702 case PROP_DISPLAY_GEOMETRY_METHOD:
703 if (sink->display_geometry_method == g_value_get_enum (value))
705 sink->display_geometry_method = g_value_get_enum (value);
706 GST_WARNING_OBJECT (sink, "Display geometry method is set (%d)",
707 sink->display_geometry_method);
708 sink->video_info_changed = TRUE;
710 gst_wl_window_set_disp_geo_method (sink->window,
711 sink->display_geometry_method);
714 case PROP_ORIENTATION:
715 if (sink->orientation == g_value_get_enum (value))
717 sink->orientation = g_value_get_enum (value);
718 GST_WARNING_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
719 sink->video_info_changed = TRUE;
721 gst_wl_window_set_orientation (sink->window, sink->orientation);
725 if (sink->flip == g_value_get_enum (value))
727 sink->flip = g_value_get_enum (value);
728 GST_WARNING_OBJECT (sink, "flip is set (%d)", sink->flip);
729 sink->video_info_changed = TRUE;
731 gst_wl_window_set_flip (sink->window, sink->flip);
735 if (sink->visible == g_value_get_boolean (value))
737 sink->visible = g_value_get_boolean (value);
738 GST_WARNING_OBJECT (sink, "visible is set (%d)", sink->visible);
739 if (sink->visible && GST_STATE (sink) == GST_STATE_PAUSED) {
740 /* need to attatch last buffer */
741 sink->video_info_changed = TRUE;
742 } else if (!sink->visible && GST_STATE (sink) >= GST_STATE_PAUSED) {
745 gst_wayland_sink_stop_video (sink);
751 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
754 if (sink->video_info_changed && sink->window
755 && GST_STATE (sink) == GST_STATE_PAUSED) {
756 gst_wayland_sink_update_last_buffer_geometry (sink);
758 g_mutex_unlock (&sink->render_lock);
763 gst_wayland_sink_finalize (GObject * object)
765 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
767 GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
769 if (sink->last_buffer)
770 gst_buffer_unref (sink->last_buffer);
772 g_object_unref (sink->display);
774 g_object_unref (sink->window);
776 gst_object_unref (sink->pool);
778 if (sink->display_name)
779 g_free (sink->display_name);
781 g_mutex_clear (&sink->display_lock);
782 g_mutex_clear (&sink->render_lock);
783 #ifdef GST_WLSINK_ENHANCEMENT
784 g_mutex_clear (&sink->render_flush_buffer_lock);
785 g_cond_clear (&sink->render_flush_buffer_cond);
788 G_OBJECT_CLASS (parent_class)->finalize (object);
791 #ifdef GST_WLSINK_ENHANCEMENT
793 gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event)
795 GstWaylandSink *sink;
796 sink = GST_WAYLAND_SINK (bsink);
798 switch (GST_EVENT_TYPE (event)) {
800 GST_LOG ("get EOS event..state is %d", GST_STATE (sink));
801 sink->got_eos_event = TRUE;
802 if (gst_wayland_sink_check_use_gapless (sink)) {
803 gst_wayland_sink_gapless_render (bsink);
804 sink->got_eos_event = FALSE;
806 sink->got_eos_event = FALSE;
811 return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
815 /* must be called with the display_lock */
817 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
818 GstContext * context)
820 struct wl_display *display;
821 GError *error = NULL;
824 display = gst_wayland_display_handle_context_get_handle (context);
825 sink->display = gst_wl_display_new_existing (display, FALSE, &error);
828 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
829 ("Could not set display handle"),
830 ("Failed to use the external wayland display: '%s'", error->message));
831 g_error_free (error);
833 #ifdef GST_WLSINK_ENHANCEMENT
834 sink->display->USE_TBM = sink->USE_TBM;
839 gst_wayland_sink_find_display (GstWaylandSink * sink)
843 GstContext *context = NULL;
844 GError *error = NULL;
848 g_mutex_lock (&sink->display_lock);
850 if (!sink->display) {
851 /* first query upstream for the needed display handle */
852 query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
853 if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
854 gst_query_parse_context (query, &context);
855 gst_wayland_sink_set_display_from_context (sink, context);
857 gst_query_unref (query);
859 if (G_LIKELY (!sink->display)) {
860 /* now ask the application to set the display handle */
861 msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
862 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
864 g_mutex_unlock (&sink->display_lock);
865 gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
866 /* at this point we expect gst_wayland_sink_set_context
867 * to get called and fill sink->display */
868 g_mutex_lock (&sink->display_lock);
870 if (!sink->display) {
871 /* if the application didn't set a display, let's create it ourselves */
872 GST_OBJECT_LOCK (sink);
873 sink->display = gst_wl_display_new (sink->display_name, &error);
874 GST_OBJECT_UNLOCK (sink);
877 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
878 ("Could not initialise Wayland output"),
879 ("Failed to create GstWlDisplay: '%s'", error->message));
880 g_error_free (error);
883 #ifdef GST_WLSINK_ENHANCEMENT
885 sink->display->USE_TBM = sink->USE_TBM;
891 g_mutex_unlock (&sink->display_lock);
896 static GstStateChangeReturn
897 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
899 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
900 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
903 switch (transition) {
904 case GST_STATE_CHANGE_NULL_TO_READY:
905 if (!gst_wayland_sink_find_display (sink))
906 return GST_STATE_CHANGE_FAILURE;
912 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
913 if (ret == GST_STATE_CHANGE_FAILURE)
916 switch (transition) {
917 case GST_STATE_CHANGE_PAUSED_TO_READY:
918 #ifdef GST_WLSINK_ENHANCEMENT
919 if (sink->keep_camera_preview) {
921 if (!gst_wl_window_is_toplevel (sink->window)) {
922 GstBaseSink *bsink = GST_BASE_SINK (element);
923 gst_wayland_sink_keep_camera_preview (bsink);
929 gst_buffer_replace (&sink->last_buffer, NULL);
931 if (gst_wl_window_is_toplevel (sink->window)) {
932 GST_DEBUG ("internal window");
933 g_clear_object (&sink->window);
935 /* remove buffer from surface, show nothing */
936 GST_DEBUG ("external window");
937 gst_wl_window_render (sink->window, NULL, NULL);
941 case GST_STATE_CHANGE_READY_TO_NULL:
942 g_mutex_lock (&sink->display_lock);
943 /* If we had a toplevel window, we most likely have our own connection
944 * to the display too, and it is a good idea to disconnect and allow
945 * potentially the application to embed us with GstVideoOverlay
946 * (which requires to re-use the same display connection as the parent
947 * surface). If we didn't have a toplevel window, then the display
948 * connection that we have is definitely shared with the application
949 * and it's better to keep it around (together with the window handle)
950 * to avoid requesting them again from the application if/when we are
951 * restarted (GstVideoOverlay behaves like that in other sinks)
953 if (sink->display && !sink->window) { /* -> the window was toplevel */
954 g_clear_object (&sink->display);
956 g_mutex_unlock (&sink->display_lock);
957 g_clear_object (&sink->pool);
967 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
969 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
972 if (gst_context_has_context_type (context,
973 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
974 g_mutex_lock (&sink->display_lock);
975 if (G_LIKELY (!sink->display))
976 gst_wayland_sink_set_display_from_context (sink, context);
978 GST_WARNING_OBJECT (element, "changing display handle is not supported");
979 #ifdef GST_WLSINK_ENHANCEMENT
980 g_mutex_unlock (&sink->display_lock);
984 g_mutex_unlock (&sink->display_lock);
987 if (GST_ELEMENT_CLASS (parent_class)->set_context)
988 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
992 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
994 GstWaylandSink *sink;
998 sink = GST_WAYLAND_SINK (bsink);
1000 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
1002 g_mutex_lock (&sink->display_lock);
1004 if (sink->display) {
1005 GValue list = G_VALUE_INIT;
1006 GValue value = G_VALUE_INIT;
1009 #ifdef GST_WLSINK_ENHANCEMENT
1012 enum wl_shm_format fmt;
1014 g_value_init (&list, GST_TYPE_LIST);
1015 g_value_init (&value, G_TYPE_STRING);
1016 #ifdef GST_WLSINK_ENHANCEMENT
1017 if (sink->display->USE_TBM)
1018 formats = sink->display->tbm_formats;
1021 formats = sink->display->formats;
1023 for (i = 0; i < formats->len; i++) {
1024 #ifdef GST_WLSINK_ENHANCEMENT
1025 if (sink->USE_TBM) {
1026 tbm_fmt = g_array_index (formats, uint32_t, i);
1027 g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt));
1028 gst_value_list_append_value (&list, &value);
1029 /* TBM doesn't support SN12 and ST12. So we add SN12 and ST12 manually as supported format.
1030 * SN12 is same with NV12, ST12 is same with NV12MT
1032 if (tbm_fmt == TBM_FORMAT_NV12) {
1033 g_value_set_string (&value,
1034 gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
1035 gst_value_list_append_value (&list, &value);
1036 } else if (tbm_fmt == TBM_FORMAT_NV12MT) {
1037 g_value_set_string (&value,
1038 gst_video_format_to_string (GST_VIDEO_FORMAT_ST12));
1039 gst_value_list_append_value (&list, &value);
1041 } else { /* USE SHM */
1042 fmt = g_array_index (formats, uint32_t, i);
1043 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
1044 gst_value_list_append_value (&list, &value);
1046 #else /* open source */
1047 fmt = g_array_index (formats, uint32_t, i);
1048 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
1049 gst_value_list_append_value (&list, &value);
1053 caps = gst_caps_make_writable (caps);
1054 gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
1056 GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
1059 g_mutex_unlock (&sink->display_lock);
1062 GstCaps *intersection;
1065 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1066 gst_caps_unref (caps);
1067 caps = intersection;
1074 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
1076 GstWaylandSink *sink;
1077 GstBufferPool *newpool;
1079 #ifdef GST_WLSINK_ENHANCEMENT
1080 uint32_t tbm_format;
1082 enum wl_shm_format format;
1086 GstStructure *structure;
1087 GstWlShmAllocator *self = NULL;
1091 sink = GST_WAYLAND_SINK (bsink);
1093 GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
1095 /* extract info from caps */
1096 if (!gst_video_info_from_caps (&info, caps))
1097 goto invalid_format;
1098 #ifdef GST_WLSINK_ENHANCEMENT
1099 sink->caps = gst_caps_copy (caps);
1100 if (sink->USE_TBM) {
1102 gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info));
1103 if ((gint) tbm_format == -1)
1104 goto invalid_format;
1106 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1107 if ((gint) format == -1)
1108 goto invalid_format;
1110 #else /* open source */
1111 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1113 if ((gint) format == -1)
1114 goto invalid_format;
1117 /* verify we support the requested format */
1118 #ifdef GST_WLSINK_ENHANCEMENT
1119 if (sink->display->USE_TBM) {
1120 GST_INFO ("USE TBM FORMAT");
1121 formats = sink->display->tbm_formats;
1122 for (i = 0; i < formats->len; i++) {
1123 if (g_array_index (formats, uint32_t, i) == tbm_format)
1126 } else { /* USE SHM */
1127 GST_INFO ("USE SHM FORMAT");
1128 formats = sink->display->formats;
1129 for (i = 0; i < formats->len; i++) {
1130 if (g_array_index (formats, uint32_t, i) == format)
1134 #else /* open source */
1136 formats = sink->display->formats;
1137 for (i = 0; i < formats->len; i++) {
1138 if (g_array_index (formats, uint32_t, i) == format)
1142 if (i >= formats->len)
1143 goto unsupported_format;
1145 #ifdef GST_WLSINK_ENHANCEMENT
1146 if (sink->USE_TBM) {
1147 if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
1148 GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
1149 sink->display->is_native_format = TRUE;
1151 /* store the video info */
1152 sink->video_info = info;
1153 sink->video_info_changed = TRUE;
1155 sink->display->is_native_format = FALSE;
1156 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1157 self->display = sink->display;
1158 /* create a new pool for the new configuration */
1159 newpool = gst_video_buffer_pool_new ();
1163 structure = gst_buffer_pool_get_config (newpool);
1164 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1165 gst_buffer_pool_config_set_allocator (structure,
1166 gst_wl_shm_allocator_get (), NULL);
1167 if (!gst_buffer_pool_set_config (newpool, structure))
1170 /* store the video info */
1171 sink->video_info = info;
1172 sink->video_info_changed = TRUE;
1174 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1175 gst_object_unref (newpool);
1177 } else { /* USE SHM */
1179 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1180 self->display = sink->display;
1182 /* create a new pool for the new configuration */
1183 newpool = gst_video_buffer_pool_new ();
1187 structure = gst_buffer_pool_get_config (newpool);
1188 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1189 gst_buffer_pool_config_set_allocator (structure,
1190 gst_wl_shm_allocator_get (), NULL);
1191 if (!gst_buffer_pool_set_config (newpool, structure))
1194 /* store the video info */
1195 sink->video_info = info;
1196 sink->video_info_changed = TRUE;
1198 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1199 gst_object_unref (newpool);
1201 #else /*open source */
1202 /* create a new pool for the new configuration */
1203 newpool = gst_video_buffer_pool_new ();
1207 structure = gst_buffer_pool_get_config (newpool);
1208 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1209 gst_buffer_pool_config_set_allocator (structure,
1210 gst_wl_shm_allocator_get (), NULL);
1211 if (!gst_buffer_pool_set_config (newpool, structure))
1214 /* store the video info */
1215 sink->video_info = info;
1216 sink->video_info_changed = TRUE;
1218 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1219 gst_object_unref (newpool);
1226 GST_DEBUG_OBJECT (sink,
1227 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1232 #ifdef GST_WLSINK_ENHANCEMENT
1234 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1235 gst_wl_tbm_format_to_string (tbm_format));
1237 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1238 gst_wl_shm_format_to_string (format));
1239 #else /*open source */
1240 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1241 gst_wl_shm_format_to_string (format));
1247 GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1252 GST_DEBUG_OBJECT (bsink, "failed setting config");
1253 gst_object_unref (newpool);
1259 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1261 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1262 GstStructure *config;
1263 guint size, min_bufs, max_bufs;
1264 #ifdef GST_WLSINK_ENHANCEMENT
1269 if (sink->USE_TBM) {
1270 if (sink->display->is_native_format == TRUE)
1273 gst_query_parse_allocation (query, &caps, &need_pool);
1276 GST_DEBUG_OBJECT (bsink, "no caps specified");
1281 config = gst_buffer_pool_get_config (sink->pool);
1282 gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
1284 /* we do have a pool for sure (created in set_caps),
1285 * so let's propose it anyway, but also propose the allocator on its own */
1286 gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
1287 gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
1289 gst_structure_free (config);
1294 static GstFlowReturn
1295 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
1298 GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
1299 return gst_wayland_sink_render (bsink, buffer);
1303 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
1305 GstWaylandSink *sink = data;
1308 GST_LOG ("frame_redraw_cb");
1310 g_atomic_int_set (&sink->redraw_pending, FALSE);
1311 wl_callback_destroy (callback);
1312 #ifdef GST_WLSINK_ENHANCEMENT
1313 if (gst_wayland_sink_check_use_gapless (sink) || sink->keep_camera_preview) {
1314 g_mutex_lock (&sink->render_flush_buffer_lock);
1315 g_cond_signal (&sink->render_flush_buffer_cond);
1316 g_mutex_unlock (&sink->render_flush_buffer_lock);
1321 static const struct wl_callback_listener frame_callback_listener = {
1322 frame_redraw_callback
1325 #ifdef GST_WLSINK_ENHANCEMENT
1327 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1330 g_return_if_fail (sink != NULL);
1331 g_return_if_fail (sink->window != NULL);
1333 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1334 gst_wl_window_set_disp_geo_method (sink->window,
1335 sink->display_geometry_method);
1336 gst_wl_window_set_orientation (sink->window, sink->orientation);
1337 gst_wl_window_set_flip (sink->window, sink->flip);
1340 /* must be called with the render lock */
1342 render_last_buffer (GstWaylandSink * sink)
1344 GstWlBuffer *wlbuffer;
1345 const GstVideoInfo *info = NULL;
1346 struct wl_surface *surface;
1347 struct wl_callback *callback;
1350 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
1351 surface = gst_wl_window_get_wl_surface (sink->window);
1353 g_atomic_int_set (&sink->redraw_pending, TRUE);
1354 callback = wl_surface_frame (surface);
1355 /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */
1356 wl_callback_add_listener (callback, &frame_callback_listener, sink);
1358 if (G_UNLIKELY (sink->video_info_changed)) {
1359 info = &sink->video_info;
1360 sink->video_info_changed = FALSE;
1362 #ifdef GST_WLSINK_ENHANCEMENT
1363 if (sink->last_buffer)
1364 gst_wl_window_render (sink->window, wlbuffer, info);
1366 if (G_UNLIKELY (info)) {
1367 gst_wl_window_set_video_info (sink->window, info);
1371 gst_wl_window_render (sink->window, wlbuffer, info);
1375 static GstFlowReturn
1376 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
1378 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1379 GstBuffer *to_render;
1380 GstWlBuffer *wlbuffer;
1381 GstFlowReturn ret = GST_FLOW_OK;
1384 g_mutex_lock (&sink->render_lock);
1386 GST_LOG_OBJECT (sink, "render buffer %p", buffer);
1388 if (G_UNLIKELY (!sink->window)) {
1389 /* ask for window handle. Unlock render_lock while doing that because
1390 * set_window_handle & friends will lock it in this context */
1391 g_mutex_unlock (&sink->render_lock);
1392 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1393 g_mutex_lock (&sink->render_lock);
1395 if (!sink->window) {
1396 /* if we were not provided a window, create one ourselves */
1398 gst_wl_window_new_toplevel (sink->display, &sink->video_info);
1401 #ifdef GST_WLSINK_ENHANCEMENT
1402 gst_wayland_sink_update_window_geometry (sink);
1403 sink->video_info_changed = TRUE;
1405 /* drop buffers until we get a frame callback */
1406 if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
1408 /* make sure that the application has called set_render_rectangle() */
1409 if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
1410 goto no_window_size;
1412 #ifdef GST_WLSINK_ENHANCEMENT
1414 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1415 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)
1416 && !(gst_wayland_sink_check_use_gapless (sink))
1417 && !sink->keep_camera_preview) {
1418 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
1419 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1423 GstMapInfo mem_info = GST_MAP_INFO_INIT;
1424 int size = GST_VIDEO_INFO_SIZE (&sink->video_info);
1425 mem = gst_buffer_peek_memory (to_render, 0);
1426 gst_memory_map (mem, &mem_info, GST_MAP_READ);
1428 data = mem_info.data;
1430 char file_name[128];
1432 sprintf (file_name, "/home/owner/DUMP/_WLSINK_OUT_DUMP_%2.2d.dump",
1434 ret = __write_rawdata (file_name, data, size);
1436 GST_ERROR ("_write_rawdata() failed");
1438 GST_INFO ("DUMP IMAGE %d, size (%d)", dump__cnt, size);
1439 gst_memory_unmap (mem, &mem_info);
1443 struct wl_buffer *wbuf = NULL;
1445 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
1446 mem = gst_buffer_peek_memory (buffer, 0);
1447 if (gst_is_wl_shm_memory (mem)) {
1449 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1452 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); //careat GstWlBuffer and add gstbuffer, wlbuffer, display and etc
1455 } else { //buffer is not from our pool and have not wl_buffer
1457 /* we don't know how to create a wl_buffer directly from the provided
1458 * memory, so we have to copy the data to a memory that we know how
1461 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1462 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1465 if (sink->USE_TBM && sink->display->is_native_format) {
1466 /* in case of SN12 or ST12 */
1467 if (!gst_wayland_sink_get_mm_video_buf_info (sink, buffer))
1468 return GST_FLOW_ERROR;
1470 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1471 /* last_buffer from gaplasee have wlbuffer */
1472 if (G_UNLIKELY (!wlbuffer)
1473 || (gst_wayland_sink_check_use_gapless (sink))
1474 || sink->keep_camera_preview) {
1476 gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1478 if (G_UNLIKELY (!wbuf))
1480 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1482 } else if (sink->USE_TBM && !sink->display->is_native_format) {
1484 /* sink->pool always exists (created in set_caps), but it may not
1485 * be active if upstream is not using it */
1486 if (!gst_buffer_pool_is_active (sink->pool)
1487 && !gst_buffer_pool_set_active (sink->pool, TRUE))
1488 goto activate_failed;
1490 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1491 if (ret != GST_FLOW_OK)
1495 //mem = gst_buffer_peek_memory (to_render, 0);
1496 //if (gst_is_wl_shm_memory (mem)) {
1497 GST_INFO ("to_render buffer is our buffer");
1499 /* the first time we acquire a buffer,
1500 * we need to attach a wl_buffer on it */
1501 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1502 if (G_UNLIKELY (!wlbuffer)) {
1503 mem = gst_buffer_peek_memory (to_render, 0);
1504 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1506 if (G_UNLIKELY (!wbuf))
1509 wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1512 gst_buffer_map (buffer, &src, GST_MAP_READ);
1513 gst_buffer_fill (to_render, 0, src.data, src.size);
1514 gst_buffer_unmap (buffer, &src);
1515 } else { /* USE SHM */
1516 /* sink->pool always exists (created in set_caps), but it may not
1517 * be active if upstream is not using it */
1518 if (!gst_buffer_pool_is_active (sink->pool) &&
1519 !gst_buffer_pool_set_active (sink->pool, TRUE))
1520 goto activate_failed;
1521 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1522 if (ret != GST_FLOW_OK)
1524 /* the first time we acquire a buffer,
1525 * we need to attach a wl_buffer on it */
1526 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1527 if (G_UNLIKELY (!wlbuffer)) {
1528 mem = gst_buffer_peek_memory (to_render, 0);
1529 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1531 if (G_UNLIKELY (!wbuf))
1534 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1538 gst_buffer_map (buffer, &src, GST_MAP_READ);
1539 gst_buffer_fill (to_render, 0, src.data, src.size);
1540 gst_buffer_unmap (buffer, &src);
1545 if (sink->USE_TBM && sink->display->is_native_format) {
1546 if ((G_UNLIKELY (buffer == sink->last_buffer)
1547 && !(gst_wayland_sink_check_use_gapless (sink)))
1548 || (G_UNLIKELY (buffer == sink->last_buffer)
1549 && !sink->keep_camera_preview)) {
1550 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1553 gst_buffer_replace (&sink->last_buffer, buffer);
1556 render_last_buffer (sink);
1560 } else { /* USE SHM or normal format */
1561 /* drop double rendering */
1562 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1563 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1566 gst_buffer_replace (&sink->last_buffer, to_render);
1569 render_last_buffer (sink);
1571 if (buffer != to_render)
1572 gst_buffer_unref (to_render);
1577 #else /* open source */
1579 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1581 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1582 GST_LOG_OBJECT (sink,
1583 "buffer %p has a wl_buffer from our display, " "writing directly",
1585 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1590 struct wl_buffer *wbuf = NULL;
1592 GST_LOG_OBJECT (sink,
1593 "buffer %p does not have a wl_buffer from our " "display, creating it",
1595 mem = gst_buffer_peek_memory (buffer, 0);
1596 if (gst_is_wl_shm_memory (mem)) {
1598 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1602 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1607 /* we don't know how to create a wl_buffer directly from the provided
1608 * memory, so we have to copy the data to a memory that we know how
1611 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1612 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1614 /* sink->pool always exists (created in set_caps), but it may not
1615 * be active if upstream is not using it */
1616 if (!gst_buffer_pool_is_active (sink->pool) &&
1617 !gst_buffer_pool_set_active (sink->pool, TRUE))
1618 goto activate_failed;
1620 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1621 if (ret != GST_FLOW_OK)
1624 /* the first time we acquire a buffer,
1625 * we need to attach a wl_buffer on it */
1626 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1627 if (G_UNLIKELY (!wlbuffer)) {
1628 mem = gst_buffer_peek_memory (to_render, 0);
1629 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1631 if (G_UNLIKELY (!wbuf))
1634 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1637 gst_buffer_map (buffer, &src, GST_MAP_READ);
1638 gst_buffer_fill (to_render, 0, src.data, src.size);
1639 gst_buffer_unmap (buffer, &src);
1642 /* drop double rendering */
1643 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1644 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1648 gst_buffer_replace (&sink->last_buffer, to_render);
1649 render_last_buffer (sink);
1651 if (buffer != to_render)
1652 gst_buffer_unref (to_render);
1656 #endif /* GST_WLSINK_ENHANCEMENT */
1660 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1661 ("Window has no size set"),
1662 ("Make sure you set the size after calling set_window_handle"));
1663 ret = GST_FLOW_ERROR;
1668 GST_WARNING_OBJECT (sink, "could not create buffer");
1673 GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
1674 ret = GST_FLOW_ERROR;
1679 GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1680 ret = GST_FLOW_ERROR;
1685 g_mutex_unlock (&sink->render_lock);
1691 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1693 iface->set_window_handle = gst_wayland_sink_set_window_handle;
1694 iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1695 iface->expose = gst_wayland_sink_expose;
1696 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1697 iface->set_wl_window_wl_surface_id =
1698 gst_wayland_sink_set_wl_window_wl_surface_id;
1702 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1704 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
1705 guintptr wl_surface_id)
1707 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1709 g_return_if_fail (sink != NULL);
1711 if (sink->window != NULL) {
1712 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1715 g_mutex_lock (&sink->render_lock);
1716 g_clear_object (&sink->window);
1718 GST_INFO ("wl_surface_id %d %x", (int) wl_surface_id,
1719 (guintptr) wl_surface_id);
1721 if (wl_surface_id) {
1722 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1723 /* we cannot use our own display with an external window handle */
1724 if (G_UNLIKELY (sink->display->own_display)) {
1725 sink->display->wl_surface_id = (int) wl_surface_id;
1726 sink->window = gst_wl_window_new_in_surface (sink->display, NULL);
1729 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1730 "ignoring window handle");
1733 gst_wayland_sink_update_window_geometry (sink);
1735 g_mutex_unlock (&sink->render_lock);
1741 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1743 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1744 struct wl_surface *surface = (struct wl_surface *) handle;
1747 g_return_if_fail (sink != NULL);
1749 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1750 if (sink->window != NULL) {
1751 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1755 g_mutex_lock (&sink->render_lock);
1757 GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1760 g_clear_object (&sink->window);
1763 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1764 /* we cannot use our own display with an external window handle */
1765 if (G_UNLIKELY (sink->display->own_display)) {
1766 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
1767 ("Application did not provide a wayland display handle"),
1768 ("Now waylandsink use internal display handle "
1769 "which is created ourselves. Consider providing a "
1770 "display handle from your application with GstContext"));
1771 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1773 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1776 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1777 "ignoring window handle");
1780 g_mutex_unlock (&sink->render_lock);
1785 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1786 gint x, gint y, gint w, gint h)
1788 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1791 g_return_if_fail (sink != NULL);
1793 g_mutex_lock (&sink->render_lock);
1794 if (!sink->window) {
1795 g_mutex_unlock (&sink->render_lock);
1796 GST_WARNING_OBJECT (sink,
1797 "set_render_rectangle called without window, ignoring");
1801 GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1803 gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1805 g_mutex_unlock (&sink->render_lock);
1809 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1811 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1814 g_return_if_fail (sink != NULL);
1816 GST_DEBUG_OBJECT (sink, "expose");
1818 g_mutex_lock (&sink->render_lock);
1819 if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1820 GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1821 render_last_buffer (sink);
1823 g_mutex_unlock (&sink->render_lock);
1827 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1829 iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1830 iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1834 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1836 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1838 g_return_if_fail (sink != NULL);
1840 g_mutex_lock (&sink->render_lock);
1841 if (!sink->window || !sink->window->area_subsurface) {
1842 g_mutex_unlock (&sink->render_lock);
1843 GST_INFO_OBJECT (sink,
1844 "begin_geometry_change called without window, ignoring");
1848 wl_subsurface_set_sync (sink->window->area_subsurface);
1849 g_mutex_unlock (&sink->render_lock);
1853 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1855 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1857 g_return_if_fail (sink != NULL);
1859 g_mutex_lock (&sink->render_lock);
1860 if (!sink->window || !sink->window->area_subsurface) {
1861 g_mutex_unlock (&sink->render_lock);
1862 GST_INFO_OBJECT (sink,
1863 "end_geometry_change called without window, ignoring");
1867 wl_subsurface_set_desync (sink->window->area_subsurface);
1868 g_mutex_unlock (&sink->render_lock);
1872 plugin_init (GstPlugin * plugin)
1874 GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1875 " wayland video sink");
1877 gst_wl_shm_allocator_register ();
1879 return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1880 GST_TYPE_WAYLAND_SINK);
1883 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1886 "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,