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 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
398 g_return_if_fail (wlbuffer != NULL);
399 wlbuffer->used_by_compositor = FALSE;
401 GST_LOG ("gstbuffer(%p) ref count(%d)", sink->last_buffer,
402 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
405 /*need to render last buffer, reuse current GstWlBuffer */
406 render_last_buffer (sink);
407 /* ref count is incresed in gst_wl_buffer_attach() of render_last_buffer(),
408 to call gst_wl_buffer_finalize(), we need to decrease buffer ref count.
409 wayland can not release buffer if we attach same buffer,
410 if we use no visible, we need to attach null buffer and wayland can release buffer,
411 so we don't need to below if() code. */
412 gst_buffer_unref (wlbuffer->gstbuffer);
414 GST_LOG ("skip rendering");
417 GST_LOG ("gstbuffer(%p) ref count(%d)", sink->last_buffer,
418 GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
422 #ifdef USE_WL_FLUSH_BUFFER
424 gst_wayland_sink_make_flush_buffer (GstWlDisplay * display,
425 MMVideoBuffer * mm_video_buf)
427 GstWlFlushBuffer *flush_buffer = NULL;
433 g_return_val_if_fail (display != NULL, FALSE);
434 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
436 flush_buffer = (GstWlFlushBuffer *) malloc (sizeof (GstWlFlushBuffer));
438 GST_ERROR ("GstWlFlushBuffer alloc faile");
441 memset (flush_buffer, 0x0, sizeof (GstWlFlushBuffer));
443 display->flush_tbm_bufmgr =
444 wayland_tbm_client_get_bufmgr (display->tbm_client);
445 g_return_val_if_fail (display->flush_tbm_bufmgr != NULL, FALSE);
447 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
448 if (mm_video_buf->handle.bo[i] != NULL) {
453 bo_size = tbm_bo_size (mm_video_buf->handle.bo[i]);
454 GST_LOG ("tbm bo size: %d", bo_size);
456 bo = tbm_bo_alloc (display->flush_tbm_bufmgr, bo_size, TBM_DEVICE_CPU);
458 GST_ERROR ("alloc tbm bo(size:%d) failed: %s", bo_size,
462 GST_INFO ("flush buffer tbm_bo =(%p)", bo);
463 flush_buffer->bo[i] = bo;
464 /* get virtual address */
465 src.ptr = dst.ptr = NULL;
466 /* bo map, we can use tbm_bo_map too. */
467 src = tbm_bo_get_handle (mm_video_buf->handle.bo[i], TBM_DEVICE_CPU);
468 dst = tbm_bo_get_handle (bo, TBM_DEVICE_CPU);
469 if (!src.ptr || !dst.ptr) {
470 GST_ERROR ("get tbm bo handle failed src(%p) dst(%p): %s", src.ptr,
471 dst.ptr, strerror (errno));
472 tbm_bo_unref (mm_video_buf->handle.bo[i]);
477 memcpy (dst.ptr, src.ptr, bo_size);
479 tbm_bo_unmap (mm_video_buf->handle.bo[i]);
483 display->flush_buffer = flush_buffer;
488 gst_wayland_sink_copy_mm_video_buf_info_to_flush (GstWlDisplay * display,
489 MMVideoBuffer * mm_video_buf)
492 g_return_val_if_fail (display != NULL, FALSE);
493 g_return_val_if_fail (mm_video_buf != NULL, FALSE);
496 ret = gst_wayland_sink_make_flush_buffer (display, mm_video_buf);
499 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
500 if (display->flush_buffer->bo[i] != NULL) {
501 display->bo[i] = display->flush_buffer->bo[i];
502 GST_LOG ("bo %p", display->bo[i]);
506 display->plane_size[i] = mm_video_buf->size[i];
507 display->stride_width[i] = mm_video_buf->stride_width[i];
508 display->stride_height[i] = mm_video_buf->stride_height[i];
509 display->native_video_size += display->plane_size[i];
517 gst_wayland_sink_add_mm_video_buf_info (GstWlDisplay * display,
518 MMVideoBuffer * mm_video_buf)
521 g_return_if_fail (display != NULL);
522 g_return_if_fail (mm_video_buf != NULL);
525 for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
526 if (mm_video_buf->handle.bo[i] != NULL) {
527 display->bo[i] = mm_video_buf->handle.bo[i];
531 display->plane_size[i] = mm_video_buf->size[i];
532 display->stride_width[i] = mm_video_buf->stride_width[i];
533 display->stride_height[i] = mm_video_buf->stride_height[i];
534 display->native_video_size += display->plane_size[i];
539 gst_wayland_sink_get_mm_video_buf_info (GstWaylandSink * sink,
542 GstWlDisplay *display;
544 GstMapInfo mem_info = GST_MAP_INFO_INIT;
545 MMVideoBuffer *mm_video_buf = NULL;
547 g_return_val_if_fail (sink != NULL, FALSE);
548 g_return_val_if_fail (buffer != NULL, FALSE);
551 display = sink->display;
552 g_return_val_if_fail (sink->display != NULL, FALSE);
554 mem = gst_buffer_peek_memory (buffer, 1);
555 gst_memory_map (mem, &mem_info, GST_MAP_READ);
556 mm_video_buf = (MMVideoBuffer *) mem_info.data;
557 gst_memory_unmap (mem, &mem_info);
559 if (mm_video_buf == NULL) {
560 GST_WARNING ("mm_video_buf is NULL. Skip rendering");
563 /* assign mm_video_buf info */
564 if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
565 GST_DEBUG ("TBM bo %p %p %p", mm_video_buf->handle.bo[0],
566 mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
567 display->native_video_size = 0;
568 display->flush_request = mm_video_buf->flush_request;
569 GST_DEBUG ("flush_request value is %d", display->flush_request);
570 #ifdef USE_WL_FLUSH_BUFFER
571 if (gst_wayland_sink_need_to_make_flush_buffer (sink)) {
572 if (!gst_wayland_sink_copy_mm_video_buf_info_to_flush (display,
574 GST_ERROR ("cat not copy mm_video_buf info to flush");
580 gst_wayland_sink_add_mm_video_buf_info (display, mm_video_buf);
582 GST_ERROR ("Buffer type is not TBM");
589 gst_wayland_sink_render_flush_buffer (GstBaseSink * bsink)
591 GstWaylandSink *sink;
593 sink = GST_WAYLAND_SINK (bsink);
595 g_return_if_fail (sink != NULL);
596 g_return_if_fail (sink->last_buffer != NULL);
598 buffer = gst_buffer_copy (sink->last_buffer);
600 g_mutex_lock (&sink->render_flush_buffer_lock);
601 g_cond_wait (&sink->render_flush_buffer_cond,
602 &sink->render_flush_buffer_lock);
604 gst_wayland_sink_render (bsink, buffer);
606 gst_buffer_unref (buffer);
607 g_mutex_unlock (&sink->render_flush_buffer_lock);
611 gst_wayland_sink_gapless_render (GstBaseSink * bsink)
613 g_return_if_fail (bsink != NULL);
615 gst_wayland_sink_render_flush_buffer (bsink);
619 gst_wayland_sink_keep_camera_preview (GstBaseSink * bsink)
621 g_return_if_fail (bsink != NULL);
623 gst_wayland_sink_render_flush_buffer (bsink);
629 gst_wayland_sink_get_property (GObject * object,
630 guint prop_id, GValue * value, GParamSpec * pspec)
632 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
637 GST_OBJECT_LOCK (sink);
638 g_value_set_string (value, sink->display_name);
639 GST_OBJECT_UNLOCK (sink);
641 #ifdef GST_WLSINK_ENHANCEMENT
642 case PROP_USE_GAPLESS:
643 g_value_set_boolean (value, sink->use_gapless);
645 case PROP_KEEP_CAMERA_PREVIEW:
646 g_value_set_boolean (value, sink->keep_camera_preview);
649 g_value_set_boolean (value, sink->USE_TBM);
651 case PROP_ROTATE_ANGLE:
652 g_value_set_enum (value, sink->rotate_angle);
654 case PROP_DISPLAY_GEOMETRY_METHOD:
655 g_value_set_enum (value, sink->display_geometry_method);
657 case PROP_ORIENTATION:
658 g_value_set_enum (value, sink->orientation);
661 g_value_set_enum (value, sink->flip);
664 g_value_set_boolean (value, sink->visible);
668 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
674 gst_wayland_sink_set_property (GObject * object,
675 guint prop_id, const GValue * value, GParamSpec * pspec)
677 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
679 g_mutex_lock (&sink->render_lock);
683 GST_OBJECT_LOCK (sink);
684 sink->display_name = g_value_dup_string (value);
685 GST_OBJECT_UNLOCK (sink);
687 #ifdef GST_WLSINK_ENHANCEMENT
688 case PROP_USE_GAPLESS:
689 sink->use_gapless = g_value_get_boolean (value);
690 GST_LOG ("use gapless is (%d)", sink->use_gapless);
692 case PROP_KEEP_CAMERA_PREVIEW:
693 sink->keep_camera_preview = g_value_get_boolean (value);
694 GST_LOG ("keep_camera_preview (%d)", sink->keep_camera_preview);
697 sink->USE_TBM = g_value_get_boolean (value);
698 GST_LOG ("1:USE TBM 0: USE SHM set(%d)", sink->USE_TBM);
700 case PROP_ROTATE_ANGLE:
701 if (sink->rotate_angle == g_value_get_enum (value))
703 sink->rotate_angle = g_value_get_enum (value);
704 GST_WARNING_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
705 sink->video_info_changed = TRUE;
707 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
710 case PROP_DISPLAY_GEOMETRY_METHOD:
711 if (sink->display_geometry_method == g_value_get_enum (value))
713 sink->display_geometry_method = g_value_get_enum (value);
714 GST_WARNING_OBJECT (sink, "Display geometry method is set (%d)",
715 sink->display_geometry_method);
716 sink->video_info_changed = TRUE;
718 gst_wl_window_set_disp_geo_method (sink->window,
719 sink->display_geometry_method);
722 case PROP_ORIENTATION:
723 if (sink->orientation == g_value_get_enum (value))
725 sink->orientation = g_value_get_enum (value);
726 GST_WARNING_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
727 sink->video_info_changed = TRUE;
729 gst_wl_window_set_orientation (sink->window, sink->orientation);
733 if (sink->flip == g_value_get_enum (value))
735 sink->flip = g_value_get_enum (value);
736 GST_WARNING_OBJECT (sink, "flip is set (%d)", sink->flip);
737 sink->video_info_changed = TRUE;
739 gst_wl_window_set_flip (sink->window, sink->flip);
743 if (sink->visible == g_value_get_boolean (value))
745 sink->visible = g_value_get_boolean (value);
746 GST_WARNING_OBJECT (sink, "visible is set (%d)", sink->visible);
747 if (sink->visible && GST_STATE (sink) == GST_STATE_PAUSED) {
748 /* need to attatch last buffer */
749 sink->video_info_changed = TRUE;
750 } else if (!sink->visible && GST_STATE (sink) >= GST_STATE_PAUSED) {
753 gst_wayland_sink_stop_video (sink);
759 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
762 #ifdef GST_WLSINK_ENHANCEMENT
763 if (sink->video_info_changed && sink->window) {
764 gst_wl_window_set_video_info_change (sink->window, TRUE);
765 if (GST_STATE (sink) == GST_STATE_PAUSED)
766 gst_wayland_sink_update_last_buffer_geometry (sink);
769 g_mutex_unlock (&sink->render_lock);
774 gst_wayland_sink_finalize (GObject * object)
776 GstWaylandSink *sink = GST_WAYLAND_SINK (object);
778 GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
780 if (sink->last_buffer)
781 gst_buffer_unref (sink->last_buffer);
783 g_object_unref (sink->display);
785 g_object_unref (sink->window);
787 gst_object_unref (sink->pool);
789 if (sink->display_name)
790 g_free (sink->display_name);
792 g_mutex_clear (&sink->display_lock);
793 g_mutex_clear (&sink->render_lock);
794 #ifdef GST_WLSINK_ENHANCEMENT
795 g_mutex_clear (&sink->render_flush_buffer_lock);
796 g_cond_clear (&sink->render_flush_buffer_cond);
799 G_OBJECT_CLASS (parent_class)->finalize (object);
802 #ifdef GST_WLSINK_ENHANCEMENT
804 gst_wayland_sink_event (GstBaseSink * bsink, GstEvent * event)
806 GstWaylandSink *sink;
807 sink = GST_WAYLAND_SINK (bsink);
809 switch (GST_EVENT_TYPE (event)) {
811 GST_LOG ("get EOS event..state is %d", GST_STATE (sink));
812 sink->got_eos_event = TRUE;
813 if (gst_wayland_sink_check_use_gapless (sink)) {
814 gst_wayland_sink_gapless_render (bsink);
815 sink->got_eos_event = FALSE;
817 sink->got_eos_event = FALSE;
822 return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
826 /* must be called with the display_lock */
828 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
829 GstContext * context)
831 struct wl_display *display;
832 GError *error = NULL;
835 display = gst_wayland_display_handle_context_get_handle (context);
836 sink->display = gst_wl_display_new_existing (display, FALSE, &error);
839 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
840 ("Could not set display handle"),
841 ("Failed to use the external wayland display: '%s'", error->message));
842 g_error_free (error);
844 #ifdef GST_WLSINK_ENHANCEMENT
845 sink->display->USE_TBM = sink->USE_TBM;
850 gst_wayland_sink_find_display (GstWaylandSink * sink)
854 GstContext *context = NULL;
855 GError *error = NULL;
859 g_mutex_lock (&sink->display_lock);
861 if (!sink->display) {
862 /* first query upstream for the needed display handle */
863 query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
864 if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
865 gst_query_parse_context (query, &context);
866 gst_wayland_sink_set_display_from_context (sink, context);
868 gst_query_unref (query);
870 if (G_LIKELY (!sink->display)) {
871 /* now ask the application to set the display handle */
872 msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
873 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
875 g_mutex_unlock (&sink->display_lock);
876 gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
877 /* at this point we expect gst_wayland_sink_set_context
878 * to get called and fill sink->display */
879 g_mutex_lock (&sink->display_lock);
881 if (!sink->display) {
882 /* if the application didn't set a display, let's create it ourselves */
883 GST_OBJECT_LOCK (sink);
884 sink->display = gst_wl_display_new (sink->display_name, &error);
885 GST_OBJECT_UNLOCK (sink);
888 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
889 ("Could not initialise Wayland output"),
890 ("Failed to create GstWlDisplay: '%s'", error->message));
891 g_error_free (error);
894 #ifdef GST_WLSINK_ENHANCEMENT
896 sink->display->USE_TBM = sink->USE_TBM;
902 g_mutex_unlock (&sink->display_lock);
907 static GstStateChangeReturn
908 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
910 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
911 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
914 switch (transition) {
915 case GST_STATE_CHANGE_NULL_TO_READY:
916 if (!gst_wayland_sink_find_display (sink))
917 return GST_STATE_CHANGE_FAILURE;
923 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
924 if (ret == GST_STATE_CHANGE_FAILURE)
927 switch (transition) {
928 case GST_STATE_CHANGE_PAUSED_TO_READY:
929 #ifdef GST_WLSINK_ENHANCEMENT
930 if (sink->keep_camera_preview) {
932 if (!gst_wl_window_is_toplevel (sink->window)) {
933 GstBaseSink *bsink = GST_BASE_SINK (element);
934 gst_wayland_sink_keep_camera_preview (bsink);
940 gst_buffer_replace (&sink->last_buffer, NULL);
942 if (gst_wl_window_is_toplevel (sink->window)) {
943 GST_DEBUG ("internal window");
944 g_clear_object (&sink->window);
946 /* remove buffer from surface, show nothing */
947 GST_DEBUG ("external window");
948 gst_wl_window_render (sink->window, NULL, NULL);
952 case GST_STATE_CHANGE_READY_TO_NULL:
953 g_mutex_lock (&sink->display_lock);
954 /* If we had a toplevel window, we most likely have our own connection
955 * to the display too, and it is a good idea to disconnect and allow
956 * potentially the application to embed us with GstVideoOverlay
957 * (which requires to re-use the same display connection as the parent
958 * surface). If we didn't have a toplevel window, then the display
959 * connection that we have is definitely shared with the application
960 * and it's better to keep it around (together with the window handle)
961 * to avoid requesting them again from the application if/when we are
962 * restarted (GstVideoOverlay behaves like that in other sinks)
964 if (sink->display && !sink->window) { /* -> the window was toplevel */
965 g_clear_object (&sink->display);
967 g_mutex_unlock (&sink->display_lock);
968 g_clear_object (&sink->pool);
978 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
980 GstWaylandSink *sink = GST_WAYLAND_SINK (element);
983 if (gst_context_has_context_type (context,
984 GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
985 g_mutex_lock (&sink->display_lock);
986 if (G_LIKELY (!sink->display))
987 gst_wayland_sink_set_display_from_context (sink, context);
989 GST_WARNING_OBJECT (element, "changing display handle is not supported");
990 #ifdef GST_WLSINK_ENHANCEMENT
991 g_mutex_unlock (&sink->display_lock);
995 g_mutex_unlock (&sink->display_lock);
998 if (GST_ELEMENT_CLASS (parent_class)->set_context)
999 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1003 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
1005 GstWaylandSink *sink;
1009 sink = GST_WAYLAND_SINK (bsink);
1011 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
1013 g_mutex_lock (&sink->display_lock);
1015 if (sink->display) {
1016 GValue list = G_VALUE_INIT;
1017 GValue value = G_VALUE_INIT;
1020 #ifdef GST_WLSINK_ENHANCEMENT
1023 enum wl_shm_format fmt;
1025 g_value_init (&list, GST_TYPE_LIST);
1026 g_value_init (&value, G_TYPE_STRING);
1027 #ifdef GST_WLSINK_ENHANCEMENT
1028 if (sink->display->USE_TBM)
1029 formats = sink->display->tbm_formats;
1032 formats = sink->display->formats;
1034 for (i = 0; i < formats->len; i++) {
1035 #ifdef GST_WLSINK_ENHANCEMENT
1036 if (sink->USE_TBM) {
1037 tbm_fmt = g_array_index (formats, uint32_t, i);
1038 g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt));
1039 gst_value_list_append_value (&list, &value);
1040 /* TBM doesn't support SN12 and ST12. So we add SN12 and ST12 manually as supported format.
1041 * SN12 is same with NV12, ST12 is same with NV12MT
1043 if (tbm_fmt == TBM_FORMAT_NV12) {
1044 g_value_set_string (&value,
1045 gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
1046 gst_value_list_append_value (&list, &value);
1047 } else if (tbm_fmt == TBM_FORMAT_NV12MT) {
1048 g_value_set_string (&value,
1049 gst_video_format_to_string (GST_VIDEO_FORMAT_ST12));
1050 gst_value_list_append_value (&list, &value);
1052 } else { /* USE SHM */
1053 fmt = g_array_index (formats, uint32_t, i);
1054 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
1055 gst_value_list_append_value (&list, &value);
1057 #else /* open source */
1058 fmt = g_array_index (formats, uint32_t, i);
1059 g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
1060 gst_value_list_append_value (&list, &value);
1064 caps = gst_caps_make_writable (caps);
1065 gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
1067 GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
1068 g_value_unset (&value);
1069 g_value_unset (&list);
1073 g_mutex_unlock (&sink->display_lock);
1076 GstCaps *intersection;
1079 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1080 gst_caps_unref (caps);
1081 caps = intersection;
1088 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
1090 GstWaylandSink *sink;
1091 GstBufferPool *newpool;
1093 #ifdef GST_WLSINK_ENHANCEMENT
1094 uint32_t tbm_format;
1096 enum wl_shm_format format;
1100 GstStructure *structure;
1101 GstWlShmAllocator *self = NULL;
1105 sink = GST_WAYLAND_SINK (bsink);
1107 GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
1109 /* extract info from caps */
1110 if (!gst_video_info_from_caps (&info, caps))
1111 goto invalid_format;
1112 #ifdef GST_WLSINK_ENHANCEMENT
1113 if (sink->USE_TBM) {
1115 gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info));
1116 if ((gint) tbm_format == -1)
1117 goto invalid_format;
1119 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1120 if ((gint) format == -1)
1121 goto invalid_format;
1123 #else /* open source */
1124 format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
1126 if ((gint) format == -1)
1127 goto invalid_format;
1130 /* verify we support the requested format */
1131 #ifdef GST_WLSINK_ENHANCEMENT
1132 if (sink->display->USE_TBM) {
1133 GST_INFO ("USE TBM FORMAT");
1134 formats = sink->display->tbm_formats;
1135 for (i = 0; i < formats->len; i++) {
1136 if (g_array_index (formats, uint32_t, i) == tbm_format)
1139 } else { /* USE SHM */
1140 GST_INFO ("USE SHM FORMAT");
1141 formats = sink->display->formats;
1142 for (i = 0; i < formats->len; i++) {
1143 if (g_array_index (formats, uint32_t, i) == format)
1147 #else /* open source */
1149 formats = sink->display->formats;
1150 for (i = 0; i < formats->len; i++) {
1151 if (g_array_index (formats, uint32_t, i) == format)
1155 if (i >= formats->len)
1156 goto unsupported_format;
1158 #ifdef GST_WLSINK_ENHANCEMENT
1159 if (sink->USE_TBM) {
1160 if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
1161 GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
1162 sink->display->is_native_format = TRUE;
1164 /* store the video info */
1165 sink->video_info = info;
1166 sink->video_info_changed = TRUE;
1168 sink->display->is_native_format = FALSE;
1169 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1170 self->display = sink->display;
1171 /* create a new pool for the new configuration */
1172 newpool = gst_video_buffer_pool_new ();
1176 structure = gst_buffer_pool_get_config (newpool);
1177 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1178 gst_buffer_pool_config_set_allocator (structure,
1179 gst_wl_shm_allocator_get (), NULL);
1180 if (!gst_buffer_pool_set_config (newpool, structure))
1183 /* store the video info */
1184 sink->video_info = info;
1185 sink->video_info_changed = TRUE;
1187 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1188 gst_object_unref (newpool);
1190 } else { /* USE SHM */
1192 self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1193 self->display = sink->display;
1195 /* create a new pool for the new configuration */
1196 newpool = gst_video_buffer_pool_new ();
1200 structure = gst_buffer_pool_get_config (newpool);
1201 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1202 gst_buffer_pool_config_set_allocator (structure,
1203 gst_wl_shm_allocator_get (), NULL);
1204 if (!gst_buffer_pool_set_config (newpool, structure))
1207 /* store the video info */
1208 sink->video_info = info;
1209 sink->video_info_changed = TRUE;
1211 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1212 gst_object_unref (newpool);
1214 #else /*open source */
1215 /* create a new pool for the new configuration */
1216 newpool = gst_video_buffer_pool_new ();
1220 structure = gst_buffer_pool_get_config (newpool);
1221 gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1222 gst_buffer_pool_config_set_allocator (structure,
1223 gst_wl_shm_allocator_get (), NULL);
1224 if (!gst_buffer_pool_set_config (newpool, structure))
1227 /* store the video info */
1228 sink->video_info = info;
1229 sink->video_info_changed = TRUE;
1231 gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1232 gst_object_unref (newpool);
1239 GST_DEBUG_OBJECT (sink,
1240 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1245 #ifdef GST_WLSINK_ENHANCEMENT
1247 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1248 gst_wl_tbm_format_to_string (tbm_format));
1250 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1251 gst_wl_shm_format_to_string (format));
1252 #else /*open source */
1253 GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1254 gst_wl_shm_format_to_string (format));
1260 GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1265 GST_DEBUG_OBJECT (bsink, "failed setting config");
1266 gst_object_unref (newpool);
1272 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1274 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1275 GstStructure *config;
1276 guint size, min_bufs, max_bufs;
1277 #ifdef GST_WLSINK_ENHANCEMENT
1282 if (sink->USE_TBM) {
1283 if (sink->display->is_native_format == TRUE)
1286 gst_query_parse_allocation (query, &caps, &need_pool);
1289 GST_DEBUG_OBJECT (bsink, "no caps specified");
1294 config = gst_buffer_pool_get_config (sink->pool);
1295 gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
1297 /* we do have a pool for sure (created in set_caps),
1298 * so let's propose it anyway, but also propose the allocator on its own */
1299 gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
1300 gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
1302 gst_structure_free (config);
1307 static GstFlowReturn
1308 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
1311 GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
1312 return gst_wayland_sink_render (bsink, buffer);
1316 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
1318 GstWaylandSink *sink = data;
1321 GST_LOG ("frame_redraw_cb");
1323 g_atomic_int_set (&sink->redraw_pending, FALSE);
1324 wl_callback_destroy (callback);
1325 #ifdef GST_WLSINK_ENHANCEMENT
1326 if (gst_wayland_sink_check_use_gapless (sink) || sink->keep_camera_preview) {
1327 g_mutex_lock (&sink->render_flush_buffer_lock);
1328 g_cond_signal (&sink->render_flush_buffer_cond);
1329 g_mutex_unlock (&sink->render_flush_buffer_lock);
1334 static const struct wl_callback_listener frame_callback_listener = {
1335 frame_redraw_callback
1338 #ifdef GST_WLSINK_ENHANCEMENT
1340 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1343 g_return_if_fail (sink != NULL);
1344 g_return_if_fail (sink->window != NULL);
1346 gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1347 gst_wl_window_set_disp_geo_method (sink->window,
1348 sink->display_geometry_method);
1349 gst_wl_window_set_orientation (sink->window, sink->orientation);
1350 gst_wl_window_set_flip (sink->window, sink->flip);
1353 /* must be called with the render lock */
1355 render_last_buffer (GstWaylandSink * sink)
1357 GstWlBuffer *wlbuffer;
1358 const GstVideoInfo *info = NULL;
1359 struct wl_surface *surface;
1360 struct wl_callback *callback;
1362 #ifdef GST_WLSINK_ENHANCEMENT
1363 g_return_if_fail (sink->last_buffer != NULL);
1364 g_return_if_fail (sink->window != NULL);
1367 wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
1368 surface = gst_wl_window_get_wl_surface (sink->window);
1370 g_atomic_int_set (&sink->redraw_pending, TRUE);
1371 callback = wl_surface_frame (surface);
1372 /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */
1373 wl_callback_add_listener (callback, &frame_callback_listener, sink);
1375 if (G_UNLIKELY (sink->video_info_changed)) {
1376 #ifdef GST_WLSINK_ENHANCEMENT
1377 gst_wl_window_set_video_info_change (sink->window, TRUE);
1379 info = &sink->video_info;
1380 sink->video_info_changed = FALSE;
1382 gst_wl_window_render (sink->window, wlbuffer, info);
1385 static GstFlowReturn
1386 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
1388 GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1389 GstBuffer *to_render;
1390 GstWlBuffer *wlbuffer;
1391 GstFlowReturn ret = GST_FLOW_OK;
1394 g_mutex_lock (&sink->render_lock);
1396 GST_LOG_OBJECT (sink, "render gstbuffer %p, ref_count(%d)", buffer,
1397 GST_OBJECT_REFCOUNT_VALUE (buffer));
1399 if (G_UNLIKELY (!sink->window)) {
1400 /* ask for window handle. Unlock render_lock while doing that because
1401 * set_window_handle & friends will lock it in this context */
1402 g_mutex_unlock (&sink->render_lock);
1403 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1404 g_mutex_lock (&sink->render_lock);
1406 if (!sink->window) {
1407 /* if we were not provided a window, create one ourselves */
1409 gst_wl_window_new_toplevel (sink->display, &sink->video_info);
1411 #ifdef GST_WLSINK_ENHANCEMENT
1412 gst_wayland_sink_update_window_geometry (sink);
1415 /* drop buffers until we get a frame callback */
1416 if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
1418 /* make sure that the application has called set_render_rectangle() */
1419 if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
1420 goto no_window_size;
1422 #ifdef GST_WLSINK_ENHANCEMENT
1424 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1425 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)
1426 && !(gst_wayland_sink_check_use_gapless (sink))
1427 && !sink->keep_camera_preview) {
1428 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
1429 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1433 GstMapInfo mem_info = GST_MAP_INFO_INIT;
1434 int size = GST_VIDEO_INFO_SIZE (&sink->video_info);
1435 mem = gst_buffer_peek_memory (to_render, 0);
1436 gst_memory_map (mem, &mem_info, GST_MAP_READ);
1438 data = mem_info.data;
1440 char file_name[128];
1442 sprintf (file_name, "/home/owner/DUMP/_WLSINK_OUT_DUMP_%2.2d.dump",
1444 ret = __write_rawdata (file_name, data, size);
1446 GST_ERROR ("_write_rawdata() failed");
1448 GST_INFO ("DUMP IMAGE %d, size (%d)", dump__cnt, size);
1449 gst_memory_unmap (mem, &mem_info);
1453 struct wl_buffer *wbuf = NULL;
1455 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
1456 mem = gst_buffer_peek_memory (buffer, 0);
1457 if (gst_is_wl_shm_memory (mem)) {
1459 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1462 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); //careat GstWlBuffer and add gstbuffer, wlbuffer, display and etc
1465 } else { //buffer is not from our pool and have not wl_buffer
1467 /* we don't know how to create a wl_buffer directly from the provided
1468 * memory, so we have to copy the data to a memory that we know how
1471 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1472 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1475 if (sink->USE_TBM && sink->display->is_native_format) {
1476 /* in case of SN12 or ST12 */
1477 if (!gst_wayland_sink_get_mm_video_buf_info (sink, buffer))
1478 return GST_FLOW_ERROR;
1480 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1481 /* last_buffer from gaplasee have wlbuffer */
1482 if (G_UNLIKELY (!wlbuffer)
1483 || (gst_wayland_sink_check_use_gapless (sink))
1484 || sink->keep_camera_preview) {
1486 gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1488 if (G_UNLIKELY (!wbuf))
1490 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1492 } else if (sink->USE_TBM && !sink->display->is_native_format) {
1494 /* sink->pool always exists (created in set_caps), but it may not
1495 * be active if upstream is not using it */
1496 if (!gst_buffer_pool_is_active (sink->pool)
1497 && !gst_buffer_pool_set_active (sink->pool, TRUE))
1498 goto activate_failed;
1500 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1501 if (ret != GST_FLOW_OK)
1505 //mem = gst_buffer_peek_memory (to_render, 0);
1506 //if (gst_is_wl_shm_memory (mem)) {
1507 GST_INFO ("to_render buffer is our buffer");
1509 /* the first time we acquire a buffer,
1510 * we need to attach a wl_buffer on it */
1511 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1512 if (G_UNLIKELY (!wlbuffer)) {
1513 mem = gst_buffer_peek_memory (to_render, 0);
1514 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1516 if (G_UNLIKELY (!wbuf))
1519 wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1522 gst_buffer_map (buffer, &src, GST_MAP_READ);
1523 gst_buffer_fill (to_render, 0, src.data, src.size);
1524 gst_buffer_unmap (buffer, &src);
1525 } else { /* USE SHM */
1526 /* sink->pool always exists (created in set_caps), but it may not
1527 * be active if upstream is not using it */
1528 if (!gst_buffer_pool_is_active (sink->pool) &&
1529 !gst_buffer_pool_set_active (sink->pool, TRUE))
1530 goto activate_failed;
1531 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1532 if (ret != GST_FLOW_OK)
1534 /* the first time we acquire a buffer,
1535 * we need to attach a wl_buffer on it */
1536 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1537 if (G_UNLIKELY (!wlbuffer)) {
1538 mem = gst_buffer_peek_memory (to_render, 0);
1539 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1541 if (G_UNLIKELY (!wbuf))
1544 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1548 gst_buffer_map (buffer, &src, GST_MAP_READ);
1549 gst_buffer_fill (to_render, 0, src.data, src.size);
1550 gst_buffer_unmap (buffer, &src);
1555 if (sink->USE_TBM && sink->display->is_native_format) {
1556 if ((G_UNLIKELY (buffer == sink->last_buffer)
1557 && !(gst_wayland_sink_check_use_gapless (sink)))
1558 || (G_UNLIKELY (buffer == sink->last_buffer)
1559 && !sink->keep_camera_preview)) {
1560 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1563 gst_buffer_replace (&sink->last_buffer, buffer);
1565 if (sink->visible) {
1566 render_last_buffer (sink);
1568 GST_LOG ("skip rendering");
1573 } else { /* USE SHM or normal format */
1574 /* drop double rendering */
1575 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1576 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1579 gst_buffer_replace (&sink->last_buffer, to_render);
1582 render_last_buffer (sink);
1584 if (buffer != to_render)
1585 gst_buffer_unref (to_render);
1590 #else /* open source */
1592 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1594 if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1595 GST_LOG_OBJECT (sink,
1596 "buffer %p has a wl_buffer from our display, " "writing directly",
1598 GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1603 struct wl_buffer *wbuf = NULL;
1605 GST_LOG_OBJECT (sink,
1606 "buffer %p does not have a wl_buffer from our " "display, creating it",
1608 mem = gst_buffer_peek_memory (buffer, 0);
1609 if (gst_is_wl_shm_memory (mem)) {
1611 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1615 gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1620 /* we don't know how to create a wl_buffer directly from the provided
1621 * memory, so we have to copy the data to a memory that we know how
1624 GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1625 GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1627 /* sink->pool always exists (created in set_caps), but it may not
1628 * be active if upstream is not using it */
1629 if (!gst_buffer_pool_is_active (sink->pool) &&
1630 !gst_buffer_pool_set_active (sink->pool, TRUE))
1631 goto activate_failed;
1633 ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1634 if (ret != GST_FLOW_OK)
1637 /* the first time we acquire a buffer,
1638 * we need to attach a wl_buffer on it */
1639 wlbuffer = gst_buffer_get_wl_buffer (buffer);
1640 if (G_UNLIKELY (!wlbuffer)) {
1641 mem = gst_buffer_peek_memory (to_render, 0);
1642 wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1644 if (G_UNLIKELY (!wbuf))
1647 gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1650 gst_buffer_map (buffer, &src, GST_MAP_READ);
1651 gst_buffer_fill (to_render, 0, src.data, src.size);
1652 gst_buffer_unmap (buffer, &src);
1655 /* drop double rendering */
1656 if (G_UNLIKELY (buffer == sink->last_buffer)) {
1657 GST_LOG_OBJECT (sink, "Buffer already being rendered");
1661 gst_buffer_replace (&sink->last_buffer, to_render);
1662 render_last_buffer (sink);
1664 if (buffer != to_render)
1665 gst_buffer_unref (to_render);
1669 #endif /* GST_WLSINK_ENHANCEMENT */
1673 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1674 ("Window has no size set"),
1675 ("Make sure you set the size after calling set_window_handle"));
1676 ret = GST_FLOW_ERROR;
1681 GST_WARNING_OBJECT (sink, "could not create buffer");
1686 GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
1687 ret = GST_FLOW_ERROR;
1692 GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1693 ret = GST_FLOW_ERROR;
1698 g_mutex_unlock (&sink->render_lock);
1704 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1706 iface->set_window_handle = gst_wayland_sink_set_window_handle;
1707 iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1708 iface->expose = gst_wayland_sink_expose;
1709 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1710 iface->set_wl_window_wl_surface_id =
1711 gst_wayland_sink_set_wl_window_wl_surface_id;
1715 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1717 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
1718 guintptr wl_surface_id)
1720 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1722 g_return_if_fail (sink != NULL);
1724 if (sink->window != NULL) {
1725 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1728 g_mutex_lock (&sink->render_lock);
1729 g_clear_object (&sink->window);
1731 GST_INFO ("wl_surface_id %d %x", (int) wl_surface_id,
1732 (guintptr) wl_surface_id);
1734 if (wl_surface_id) {
1735 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1736 /* we cannot use our own display with an external window handle */
1737 if (G_UNLIKELY (sink->display->own_display)) {
1738 sink->display->wl_surface_id = (int) wl_surface_id;
1739 sink->window = gst_wl_window_new_in_surface (sink->display, NULL);
1742 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1743 "ignoring window handle");
1746 gst_wayland_sink_update_window_geometry (sink);
1747 g_mutex_unlock (&sink->render_lock);
1753 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1755 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1756 struct wl_surface *surface = (struct wl_surface *) handle;
1759 g_return_if_fail (sink != NULL);
1761 #ifdef GST_WLSINK_ENHANCEMENT /* use unique_id */
1762 if (sink->window != NULL) {
1763 GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1767 g_mutex_lock (&sink->render_lock);
1769 GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1772 g_clear_object (&sink->window);
1775 if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1776 /* we cannot use our own display with an external window handle */
1777 if (G_UNLIKELY (sink->display->own_display)) {
1778 GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
1779 ("Application did not provide a wayland display handle"),
1780 ("Now waylandsink use internal display handle "
1781 "which is created ourselves. Consider providing a "
1782 "display handle from your application with GstContext"));
1783 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1785 sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1788 GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1789 "ignoring window handle");
1792 #ifdef GST_WLSINK_ENHANCEMENT
1793 gst_wayland_sink_update_window_geometry (sink);
1795 g_mutex_unlock (&sink->render_lock);
1800 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1801 gint x, gint y, gint w, gint h)
1803 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1806 g_return_if_fail (sink != NULL);
1808 g_mutex_lock (&sink->render_lock);
1809 if (!sink->window) {
1810 g_mutex_unlock (&sink->render_lock);
1811 GST_WARNING_OBJECT (sink,
1812 "set_render_rectangle called without window, ignoring");
1816 GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1818 gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1820 g_mutex_unlock (&sink->render_lock);
1824 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1826 GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1829 g_return_if_fail (sink != NULL);
1831 GST_DEBUG_OBJECT (sink, "expose");
1833 g_mutex_lock (&sink->render_lock);
1834 if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1835 GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1836 render_last_buffer (sink);
1838 g_mutex_unlock (&sink->render_lock);
1842 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1844 iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1845 iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1849 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1851 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1853 g_return_if_fail (sink != NULL);
1855 g_mutex_lock (&sink->render_lock);
1856 if (!sink->window || !sink->window->area_subsurface) {
1857 g_mutex_unlock (&sink->render_lock);
1858 GST_INFO_OBJECT (sink,
1859 "begin_geometry_change called without window, ignoring");
1863 wl_subsurface_set_sync (sink->window->area_subsurface);
1864 g_mutex_unlock (&sink->render_lock);
1868 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1870 GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1872 g_return_if_fail (sink != NULL);
1874 g_mutex_lock (&sink->render_lock);
1875 if (!sink->window || !sink->window->area_subsurface) {
1876 g_mutex_unlock (&sink->render_lock);
1877 GST_INFO_OBJECT (sink,
1878 "end_geometry_change called without window, ignoring");
1882 wl_subsurface_set_desync (sink->window->area_subsurface);
1883 g_mutex_unlock (&sink->render_lock);
1887 plugin_init (GstPlugin * plugin)
1889 GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1890 " wayland video sink");
1892 gst_wl_shm_allocator_register ();
1894 return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1895 GST_TYPE_WAYLAND_SINK);
1898 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1901 "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,