waylandsink : add visible property
[platform/upstream/gstreamer.git] / ext / wayland / gstwaylandsink.c
1 /* GStreamer Wayland video sink
2  *
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.
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 /**
25  * SECTION:element-waylandsink
26  *
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.
31  *
32  * <refsect2>
33  * <title>Example pipelines</title>
34  * |[
35  * gst-launch -v videotestsrc ! waylandsink
36  * ]| test the video rendering in wayland
37  * </refsect2>
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include "gstwaylandsink.h"
45 #ifdef GST_WLSINK_ENHANCEMENT
46 #include <mm_types.h>
47 #include "tizen-wlvideoformat.h"
48 #endif
49 #include "wlvideoformat.h"
50 #include "wlbuffer.h"
51 #include "wlshmallocator.h"
52
53 #include <gst/wayland/wayland.h>
54 #include <gst/video/videooverlay.h>
55
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60
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())
65
66 static GType
67 gst_waylandsink_rotate_angle_get_type (void)
68 {
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"},
75     {4, NULL, NULL},
76   };
77
78   if (!waylandsink_rotate_angle_type) {
79     waylandsink_rotate_angle_type =
80         g_enum_register_static ("GstWaylandSinkRotateAngleType",
81         rotate_angle_type);
82   }
83
84   return waylandsink_rotate_angle_type;
85 }
86
87
88 static GType
89 gst_waylandsink_display_geometry_method_get_type (void)
90 {
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"},
98     {5, NULL, NULL},
99   };
100
101   if (!waylandsink_display_geometry_method_type) {
102     waylandsink_display_geometry_method_type =
103         g_enum_register_static ("GstWaylandSinkDisplayGeometryMethodType",
104         display_geometry_method_type);
105   }
106   return waylandsink_display_geometry_method_type;
107 }
108
109 static GType
110 gst_waylandsink_flip_get_type (void)
111 {
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},
119   };
120
121   if (!waylandsink_flip_type) {
122     waylandsink_flip_type =
123         g_enum_register_static ("GstWaylandSinkFlipType", flip_type);
124   }
125
126   return waylandsink_flip_type;
127 }
128
129 #endif
130
131 /* signals */
132 enum
133 {
134   SIGNAL_0,
135   LAST_SIGNAL
136 };
137
138 /* Properties */
139 enum
140 {
141   PROP_0,
142   PROP_DISPLAY,
143 #ifdef GST_WLSINK_ENHANCEMENT
144   PROP_USE_TBM,
145   PROP_ROTATE_ANGLE,
146   PROP_DISPLAY_GEOMETRY_METHOD,
147   PROP_ORIENTATION,
148   PROP_FLIP,
149   PROP_VISIBLE
150 #endif
151 };
152 int dump__cnt = 0;
153
154 GST_DEBUG_CATEGORY (gstwayland_debug);
155 #define GST_CAT_DEFAULT gstwayland_debug
156
157 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
158     GST_PAD_SINK,
159     GST_PAD_ALWAYS,
160     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
161         ("{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, "
162             "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, "
163 #ifdef GST_WLSINK_ENHANCEMENT
164             "SN12, ST12, "
165 #endif
166             "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"))
167     );
168
169 static void gst_wayland_sink_get_property (GObject * object,
170     guint prop_id, GValue * value, GParamSpec * pspec);
171 static void gst_wayland_sink_set_property (GObject * object,
172     guint prop_id, const GValue * value, GParamSpec * pspec);
173 static void gst_wayland_sink_finalize (GObject * object);
174
175 static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
176     GstStateChange transition);
177 static void gst_wayland_sink_set_context (GstElement * element,
178     GstContext * context);
179
180 static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
181     GstCaps * filter);
182 static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
183 static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink,
184     GstBuffer * buffer);
185 static gboolean
186 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query);
187 static gboolean gst_wayland_sink_render (GstBaseSink * bsink,
188     GstBuffer * buffer);
189
190 /* VideoOverlay interface */
191 static void gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface *
192     iface);
193 static void gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay,
194     guintptr handle);
195 static void
196 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
197     guintptr wl_surface_id);
198 static void gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
199     gint x, gint y, gint w, gint h);
200 static void gst_wayland_sink_expose (GstVideoOverlay * overlay);
201
202 /* WaylandVideo interface */
203 static void gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface *
204     iface);
205 static void gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video);
206 static void gst_wayland_sink_end_geometry_change (GstWaylandVideo * video);
207 #ifdef GST_WLSINK_ENHANCEMENT
208 static void gst_wayland_sink_update_window_geometry (GstWaylandSink * sink);
209 static void render_last_buffer (GstWaylandSink * sink);
210 #endif
211 #define gst_wayland_sink_parent_class parent_class
212 G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
213     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
214         gst_wayland_sink_videooverlay_init)
215     G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO,
216         gst_wayland_sink_waylandvideo_init));
217
218 static void
219 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
220 {
221   GObjectClass *gobject_class;
222   GstElementClass *gstelement_class;
223   GstBaseSinkClass *gstbasesink_class;
224   FUNCTION;
225
226   gobject_class = (GObjectClass *) klass;
227   gstelement_class = (GstElementClass *) klass;
228   gstbasesink_class = (GstBaseSinkClass *) klass;
229
230   gobject_class->set_property = gst_wayland_sink_set_property;
231   gobject_class->get_property = gst_wayland_sink_get_property;
232   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize);
233
234   gst_element_class_add_pad_template (gstelement_class,
235       gst_static_pad_template_get (&sink_template));
236
237   gst_element_class_set_static_metadata (gstelement_class,
238       "wayland video sink", "Sink/Video",
239       "Output to wayland surface",
240       "Sreerenj Balachandran <sreerenj.balachandran@intel.com>, "
241       "George Kiagiadakis <george.kiagiadakis@collabora.com>");
242
243   gstelement_class->change_state =
244       GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
245   gstelement_class->set_context =
246       GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
247
248   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
249   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
250   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
251   gstbasesink_class->propose_allocation =
252       GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
253   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render);
254
255   g_object_class_install_property (gobject_class, PROP_DISPLAY,
256       g_param_spec_string ("display", "Wayland Display name", "Wayland "
257           "display name to connect to, if not supplied via the GstContext",
258           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259 #ifdef GST_WLSINK_ENHANCEMENT
260   g_object_class_install_property (gobject_class, PROP_USE_TBM,
261       g_param_spec_boolean ("use-tbm",
262           "Use Tizen Buffer Memory insted of Shared memory",
263           "When enabled, Memory is alloced by TBM insted of SHM ", TRUE,
264           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265
266   g_object_class_install_property (gobject_class, PROP_ROTATE_ANGLE,
267       g_param_spec_enum ("rotate", "Rotate angle",
268           "Rotate angle of display output",
269           GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
270           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271
272   g_object_class_install_property (gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
273       g_param_spec_enum ("display-geometry-method", "Display geometry method",
274           "Geometrical method for display",
275           GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD,
276           DEF_DISPLAY_GEOMETRY_METHOD,
277           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278
279   g_object_class_install_property (gobject_class, PROP_ORIENTATION,
280       g_param_spec_enum ("orientation",
281           "Orientation information used for ROI/ZOOM",
282           "Orientation information for display",
283           GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
284           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
285
286   g_object_class_install_property (gobject_class, PROP_FLIP,
287       g_param_spec_enum ("flip", "Display flip",
288           "Flip for display",
289           GST_TYPE_WAYLANDSINK_FLIP, DEF_DISPLAY_FLIP,
290           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
291
292   g_object_class_install_property (gobject_class, PROP_VISIBLE,
293       g_param_spec_boolean ("visible", "Visible",
294           "Draws screen or blacks out, true means visible, false blacks out",
295           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
296 #endif
297 }
298
299 #ifdef DUMP_BUFFER
300 int
301 __write_rawdata (const char *file, const void *data, unsigned int size)
302 {
303   FILE *fp;
304
305   fp = fopen (file, "wb");
306   if (fp == NULL)
307     return -1;
308
309   fwrite ((char *) data, sizeof (char), size, fp);
310   fclose (fp);
311
312   return 0;
313 }
314 #endif
315 static void
316 gst_wayland_sink_init (GstWaylandSink * sink)
317 {
318   FUNCTION;
319 #ifdef GST_WLSINK_ENHANCEMENT
320   sink->USE_TBM = TRUE;
321   sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
322   sink->flip = DEF_DISPLAY_FLIP;
323   sink->rotate_angle = DEGREE_0;
324   sink->orientation = DEGREE_0;
325   sink->visible = TRUE;
326 #endif
327   g_mutex_init (&sink->display_lock);
328   g_mutex_init (&sink->render_lock);
329 }
330
331 #ifdef GST_WLSINK_ENHANCEMENT
332 static void
333 gst_wayland_sink_stop_video (GstWaylandSink * sink)
334 {
335   FUNCTION;
336   g_return_if_fail (sink != NULL);
337   gst_wl_window_render (sink->window, NULL, NULL);
338 }
339
340 static void
341 gst_wayland_sink_update_last_buffer_geometry (GstWaylandSink * sink)
342 {
343   GstWlBuffer *wlbuffer;
344   FUNCTION;
345   g_return_if_fail (sink != NULL);
346   GST_DEBUG ("gstbuffer ref count is %d",
347       GST_OBJECT_REFCOUNT_VALUE (sink->last_buffer));
348   wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
349   wlbuffer->used_by_compositor = FALSE;
350   /*need to render last buffer, reuse current GstWlBuffer */
351   render_last_buffer (sink);
352   /* ref count is incresed in gst_wl_buffer_attach() of render_last_buffer(),
353      to call gst_wl_buffer_finalize(), we need to decrease buffer ref count.
354      wayland can not release buffer if we attach same buffer,
355      if we use visible but we need to attach null buffer and wayland can release buffer,
356      so we don't need to below code. */
357   if (!sink->visible)
358     gst_buffer_unref (wlbuffer->gstbuffer);
359 }
360
361 #ifdef USE_WL_FLUSH_BUFFER
362 static int
363 gst_wayland_sink_make_flush_buffer (GstWlDisplay * display,
364     MMVideoBuffer * mm_video_buf)
365 {
366   GstWlFlushBuffer *flush_buffer = NULL;
367   tbm_bo bo = NULL;
368   int bo_size = 0;
369   int i;
370   FUNCTION;
371
372   g_return_val_if_fail (display != NULL, FALSE);
373   g_return_val_if_fail (mm_video_buf != NULL, FALSE);
374
375   flush_buffer = (GstWlFlushBuffer *) malloc (sizeof (GstWlFlushBuffer));
376   if (!flush_buffer) {
377     GST_ERROR ("GstWlFlushBuffer alloc faile");
378     return FALSE;
379   }
380   memset (flush_buffer, 0x0, sizeof (GstWlFlushBuffer));
381
382   display->flush_tbm_bufmgr =
383       wayland_tbm_client_get_bufmgr (display->tbm_client);
384   g_return_if_fail (display->flush_tbm_bufmgr != NULL);
385
386   for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
387     if (mm_video_buf->handle.bo[i] != NULL) {
388       tbm_bo_handle src;
389       tbm_bo_handle dst;
390
391       /* get bo size */
392       bo_size = tbm_bo_size (mm_video_buf->handle.bo[i]);
393       GST_LOG ("tbm bo size: %d", bo_size);
394       /* alloc bo */
395       bo = tbm_bo_alloc (display->flush_tbm_bufmgr, bo_size, TBM_DEVICE_CPU);
396       if (!bo) {
397         GST_ERROR ("alloc tbm bo(size:%d) failed: %s", bo_size,
398             strerror (errno));
399         return FALSE;
400       }
401       GST_INFO ("flush buffer tbm_bo =(%p)", bo);
402       flush_buffer->bo[i] = bo;
403       /* get virtual address */
404       src.ptr = dst.ptr = NULL;
405       /* bo map, we can use tbm_bo_map too. */
406       src = tbm_bo_get_handle (mm_video_buf->handle.bo[i], TBM_DEVICE_CPU);
407       dst = tbm_bo_get_handle (bo, TBM_DEVICE_CPU);
408       if (!src.ptr || !dst.ptr) {
409         GST_ERROR ("get tbm bo handle failed src(%p) dst(%p): %s", src.ptr,
410             dst.ptr, strerror (errno));
411         tbm_bo_unref (mm_video_buf->handle.bo[i]);
412         tbm_bo_unref (bo);
413         return FALSE;
414       }
415       /* copy */
416       memcpy (dst.ptr, src.ptr, bo_size);
417       /* bo unmap */
418       tbm_bo_unmap (mm_video_buf->handle.bo[i]);
419       tbm_bo_unmap (bo);
420     }
421   }
422   display->flush_buffer = flush_buffer;
423   return TRUE;
424 }
425
426 static int
427 gst_wayland_sink_copy_mm_video_buf_info_to_flush (GstWlDisplay * display,
428     MMVideoBuffer * mm_video_buf)
429 {
430   int ret = FALSE;
431   g_return_val_if_fail (display != NULL, FALSE);
432   g_return_val_if_fail (mm_video_buf != NULL, FALSE);
433   FUNCTION;
434
435   ret = gst_wayland_sink_make_flush_buffer (display, mm_video_buf);
436   if (ret) {
437     int i;
438     for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
439       if (display->flush_buffer->bo[i] != NULL) {
440         display->bo[i] = display->flush_buffer->bo[i];
441         GST_LOG ("bo %p", display->bo[i]);
442       } else {
443         display->bo[i] = 0;
444       }
445       display->plane_size[i] = mm_video_buf->size[i];
446       display->stride_width[i] = mm_video_buf->stride_width[i];
447       display->stride_height[i] = mm_video_buf->stride_height[i];
448       display->native_video_size += display->plane_size[i];
449     }
450   }
451   return ret;
452 }
453 #endif
454
455 static void
456 gst_wayland_sink_add_mm_video_buf_info (GstWlDisplay * display,
457     MMVideoBuffer * mm_video_buf)
458 {
459   int i;
460   g_return_if_fail (display != NULL);
461   g_return_if_fail (mm_video_buf != NULL);
462   FUNCTION;
463
464   for (i = 0; i < NV_BUF_PLANE_NUM; i++) {
465     if (mm_video_buf->handle.bo[i] != NULL) {
466       display->bo[i] = mm_video_buf->handle.bo[i];
467     } else {
468       display->bo[i] = 0;
469     }
470     display->plane_size[i] = mm_video_buf->size[i];
471     display->stride_width[i] = mm_video_buf->stride_width[i];
472     display->stride_height[i] = mm_video_buf->stride_height[i];
473     display->native_video_size += display->plane_size[i];
474   }
475 }
476
477 static int
478 gst_wayland_sink_get_mm_video_buf_info (GstWlDisplay * display,
479     GstBuffer * buffer)
480 {
481   GstMemory *mem;
482   GstMapInfo mem_info = GST_MAP_INFO_INIT;
483   MMVideoBuffer *mm_video_buf = NULL;
484
485   g_return_val_if_fail (display != NULL, FALSE);
486   g_return_val_if_fail (buffer != NULL, FALSE);
487
488   FUNCTION;
489
490   mem = gst_buffer_peek_memory (buffer, 1);
491   gst_memory_map (mem, &mem_info, GST_MAP_READ);
492   mm_video_buf = (MMVideoBuffer *) mem_info.data;
493   gst_memory_unmap (mem, &mem_info);
494
495   if (mm_video_buf == NULL) {
496     GST_WARNING ("mm_video_buf is NULL. Skip rendering");
497     return FALSE;
498   }
499   /* assign mm_video_buf info */
500   if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
501     GST_DEBUG ("TBM bo %p %p %p", mm_video_buf->handle.bo[0],
502         mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
503     display->native_video_size = 0;
504     display->flush_request = mm_video_buf->flush_request;
505     GST_DEBUG ("flush_request value is %d", display->flush_request);
506 #ifdef USE_WL_FLUSH_BUFFER
507     if (display->flush_request) {
508       if (!gst_wayland_sink_copy_mm_video_buf_info_to_flush (display,
509               mm_video_buf)) {
510         GST_ERROR ("cat not copy mm_video_buf info to flush");
511         return FALSE;
512       }
513     } else
514 #endif
515       /* normal routine */
516       gst_wayland_sink_add_mm_video_buf_info (display, mm_video_buf);
517   } else {
518     GST_ERROR ("Buffer type is not TBM");
519     return FALSE;
520   }
521   return TRUE;
522 }
523
524 #endif
525 static void
526 gst_wayland_sink_get_property (GObject * object,
527     guint prop_id, GValue * value, GParamSpec * pspec)
528 {
529   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
530   FUNCTION;
531
532   switch (prop_id) {
533     case PROP_DISPLAY:
534       GST_OBJECT_LOCK (sink);
535       g_value_set_string (value, sink->display_name);
536       GST_OBJECT_UNLOCK (sink);
537       break;
538 #ifdef GST_WLSINK_ENHANCEMENT
539     case PROP_USE_TBM:
540       g_value_set_boolean (value, sink->USE_TBM);
541       break;
542     case PROP_ROTATE_ANGLE:
543       g_value_set_enum (value, sink->rotate_angle);
544       break;
545     case PROP_DISPLAY_GEOMETRY_METHOD:
546       g_value_set_enum (value, sink->display_geometry_method);
547       break;
548     case PROP_ORIENTATION:
549       g_value_set_enum (value, sink->orientation);
550       break;
551     case PROP_FLIP:
552       g_value_set_enum (value, sink->flip);
553       break;
554     case PROP_VISIBLE:
555       g_value_set_boolean (value, sink->visible);
556       break;
557 #endif
558     default:
559       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560       break;
561   }
562 }
563
564 static void
565 gst_wayland_sink_set_property (GObject * object,
566     guint prop_id, const GValue * value, GParamSpec * pspec)
567 {
568   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
569   FUNCTION;
570   g_mutex_lock (&sink->render_lock);
571
572   switch (prop_id) {
573     case PROP_DISPLAY:
574       GST_OBJECT_LOCK (sink);
575       sink->display_name = g_value_dup_string (value);
576       GST_OBJECT_UNLOCK (sink);
577       break;
578 #ifdef GST_WLSINK_ENHANCEMENT
579     case PROP_USE_TBM:
580       sink->USE_TBM = g_value_get_boolean (value);
581       GST_LOG ("1:USE TBM 0: USE SHM set(%d)", sink->USE_TBM);
582       break;
583
584     case PROP_ROTATE_ANGLE:
585       if (sink->rotate_angle == g_value_get_enum (value))
586         break;
587       sink->rotate_angle = g_value_get_enum (value);
588       GST_WARNING_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
589       sink->video_info_changed = TRUE;
590       if (sink->window) {
591         gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
592       }
593       break;
594
595     case PROP_DISPLAY_GEOMETRY_METHOD:
596       if (sink->display_geometry_method == g_value_get_enum (value))
597         break;
598       sink->display_geometry_method = g_value_get_enum (value);
599       GST_WARNING_OBJECT (sink, "Display geometry method is set (%d)",
600           sink->display_geometry_method);
601       sink->video_info_changed = TRUE;
602       if (sink->window) {
603         gst_wl_window_set_disp_geo_method (sink->window,
604             sink->display_geometry_method);
605       }
606       break;
607
608     case PROP_ORIENTATION:
609       if (sink->orientation == g_value_get_enum (value))
610         break;
611       sink->orientation = g_value_get_enum (value);
612       GST_WARNING_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
613       sink->video_info_changed = TRUE;
614       if (sink->window) {
615         gst_wl_window_set_orientation (sink->window, sink->orientation);
616       }
617       break;
618
619     case PROP_FLIP:
620       if (sink->flip == g_value_get_enum (value))
621         break;
622       sink->flip = g_value_get_enum (value);
623       GST_WARNING_OBJECT (sink, "flip is set (%d)", sink->flip);
624       sink->video_info_changed = TRUE;
625       if (sink->window) {
626         gst_wl_window_set_flip (sink->window, sink->flip);
627       }
628       break;
629
630     case PROP_VISIBLE:
631       if (sink->visible == g_value_get_boolean (value))
632         break;
633       sink->visible = g_value_get_boolean (value);
634       GST_WARNING_OBJECT (sink, "visible is set (%d)", sink->visible);
635       if (sink->visible && GST_STATE (sink) == GST_STATE_PAUSED) {
636         /* need to attatch last buffer */
637         sink->video_info_changed = TRUE;
638       } else if (!sink->visible && GST_STATE (sink) >= GST_STATE_PAUSED) {
639         /* video stop */
640         if (sink->window) {
641           gst_wayland_sink_stop_video (sink);
642         }
643       }
644       break;
645 #endif
646     default:
647       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648       break;
649   }
650   if (sink->video_info_changed && sink->window
651       && GST_STATE (sink) == GST_STATE_PAUSED) {
652     gst_wayland_sink_update_last_buffer_geometry (sink);
653   }
654   g_mutex_unlock (&sink->render_lock);
655
656 }
657
658 static void
659 gst_wayland_sink_finalize (GObject * object)
660 {
661   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
662   FUNCTION;
663   GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
664
665   if (sink->last_buffer)
666     gst_buffer_unref (sink->last_buffer);
667   if (sink->display)
668     g_object_unref (sink->display);
669   if (sink->window)
670     g_object_unref (sink->window);
671   if (sink->pool)
672     gst_object_unref (sink->pool);
673
674   if (sink->display_name)
675     g_free (sink->display_name);
676
677   g_mutex_clear (&sink->display_lock);
678   g_mutex_clear (&sink->render_lock);
679
680   G_OBJECT_CLASS (parent_class)->finalize (object);
681 }
682
683 /* must be called with the display_lock */
684 static void
685 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
686     GstContext * context)
687 {
688   struct wl_display *display;
689   GError *error = NULL;
690   FUNCTION;
691
692   display = gst_wayland_display_handle_context_get_handle (context);
693   sink->display = gst_wl_display_new_existing (display, FALSE, &error);
694
695   if (error) {
696     GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
697         ("Could not set display handle"),
698         ("Failed to use the external wayland display: '%s'", error->message));
699     g_error_free (error);
700   }
701 #ifdef GST_WLSINK_ENHANCEMENT
702   sink->display->USE_TBM = sink->USE_TBM;
703 #endif
704 }
705
706 static gboolean
707 gst_wayland_sink_find_display (GstWaylandSink * sink)
708 {
709   GstQuery *query;
710   GstMessage *msg;
711   GstContext *context = NULL;
712   GError *error = NULL;
713   gboolean ret = TRUE;
714   FUNCTION;
715
716   g_mutex_lock (&sink->display_lock);
717
718   if (!sink->display) {
719     /* first query upstream for the needed display handle */
720     query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
721     if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
722       gst_query_parse_context (query, &context);
723       gst_wayland_sink_set_display_from_context (sink, context);
724     }
725     gst_query_unref (query);
726
727     if (G_LIKELY (!sink->display)) {
728       /* now ask the application to set the display handle */
729       msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
730           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
731
732       g_mutex_unlock (&sink->display_lock);
733       gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
734       /* at this point we expect gst_wayland_sink_set_context
735        * to get called and fill sink->display */
736       g_mutex_lock (&sink->display_lock);
737
738       if (!sink->display) {
739         /* if the application didn't set a display, let's create it ourselves */
740         GST_OBJECT_LOCK (sink);
741         sink->display = gst_wl_display_new (sink->display_name, &error);
742         GST_OBJECT_UNLOCK (sink);
743
744         if (error) {
745           GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
746               ("Could not initialise Wayland output"),
747               ("Failed to create GstWlDisplay: '%s'", error->message));
748           g_error_free (error);
749           ret = FALSE;
750         }
751 #ifdef GST_WLSINK_ENHANCEMENT
752         sink->display->USE_TBM = sink->USE_TBM;
753 #endif
754       }
755     }
756   }
757
758   g_mutex_unlock (&sink->display_lock);
759
760   return ret;
761 }
762
763 static GstStateChangeReturn
764 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
765 {
766   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
767   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
768   FUNCTION;
769
770   switch (transition) {
771     case GST_STATE_CHANGE_NULL_TO_READY:
772       if (!gst_wayland_sink_find_display (sink))
773         return GST_STATE_CHANGE_FAILURE;
774       break;
775     default:
776       break;
777   }
778
779   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
780   if (ret == GST_STATE_CHANGE_FAILURE)
781     return ret;
782
783   switch (transition) {
784     case GST_STATE_CHANGE_PAUSED_TO_READY:
785       gst_buffer_replace (&sink->last_buffer, NULL);
786       if (sink->window) {
787         if (gst_wl_window_is_toplevel (sink->window)) {
788           g_clear_object (&sink->window);
789         } else {
790           /* remove buffer from surface, show nothing */
791 #ifdef USE_WL_FLUSH_BUFFER
792           sink->display->flush_request = 0;
793 #endif
794           gst_wl_window_render (sink->window, NULL, NULL);
795         }
796       }
797       break;
798     case GST_STATE_CHANGE_READY_TO_NULL:
799       g_mutex_lock (&sink->display_lock);
800       /* If we had a toplevel window, we most likely have our own connection
801        * to the display too, and it is a good idea to disconnect and allow
802        * potentially the application to embed us with GstVideoOverlay
803        * (which requires to re-use the same display connection as the parent
804        * surface). If we didn't have a toplevel window, then the display
805        * connection that we have is definitely shared with the application
806        * and it's better to keep it around (together with the window handle)
807        * to avoid requesting them again from the application if/when we are
808        * restarted (GstVideoOverlay behaves like that in other sinks)
809        */
810       if (sink->display && !sink->window) {     /* -> the window was toplevel */
811         g_clear_object (&sink->display);
812       }
813       g_mutex_unlock (&sink->display_lock);
814       g_clear_object (&sink->pool);
815       break;
816     default:
817       break;
818   }
819
820   return ret;
821 }
822
823 static void
824 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
825 {
826   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
827   FUNCTION;
828
829   if (gst_context_has_context_type (context,
830           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
831     g_mutex_lock (&sink->display_lock);
832     if (G_LIKELY (!sink->display))
833       gst_wayland_sink_set_display_from_context (sink, context);
834     else {
835       GST_WARNING_OBJECT (element, "changing display handle is not supported");
836 #ifdef GST_WLSINK_ENHANCEMENT
837       g_mutex_unlock (&sink->display_lock);
838       return;
839 #endif
840     }
841     g_mutex_unlock (&sink->display_lock);
842   }
843
844   if (GST_ELEMENT_CLASS (parent_class)->set_context)
845     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
846 }
847
848 static GstCaps *
849 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
850 {
851   GstWaylandSink *sink;
852   GstCaps *caps;
853   FUNCTION;
854
855   sink = GST_WAYLAND_SINK (bsink);
856
857   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
858
859   g_mutex_lock (&sink->display_lock);
860
861   if (sink->display) {
862     GValue list = G_VALUE_INIT;
863     GValue value = G_VALUE_INIT;
864     GArray *formats;
865     gint i;
866 #ifdef GST_WLSINK_ENHANCEMENT
867     uint32_t tbm_fmt;
868 #endif
869     enum wl_shm_format fmt;
870
871     g_value_init (&list, GST_TYPE_LIST);
872     g_value_init (&value, G_TYPE_STRING);
873 #ifdef GST_WLSINK_ENHANCEMENT
874     if (sink->display->USE_TBM)
875       formats = sink->display->tbm_formats;
876     else                        /* SHM */
877 #endif
878       formats = sink->display->formats;
879
880     for (i = 0; i < formats->len; i++) {
881 #ifdef GST_WLSINK_ENHANCEMENT
882       if (sink->USE_TBM) {
883         tbm_fmt = g_array_index (formats, uint32_t, i);
884         g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt));
885         gst_value_list_append_value (&list, &value);
886         /* TBM doesn't support SN12. So we add SN12 manually as supported format.
887          * SN12 is exactly same with NV12.
888          */
889         if (tbm_fmt == TBM_FORMAT_NV12) {
890           g_value_set_string (&value,
891               gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
892           gst_value_list_append_value (&list, &value);
893         }
894       } else {                  /* USE SHM */
895         fmt = g_array_index (formats, uint32_t, i);
896         g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
897         gst_value_list_append_value (&list, &value);
898       }
899 #else /* open source */
900       fmt = g_array_index (formats, uint32_t, i);
901       g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
902       gst_value_list_append_value (&list, &value);
903 #endif
904     }
905
906     caps = gst_caps_make_writable (caps);
907     gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
908
909     GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
910   }
911
912   g_mutex_unlock (&sink->display_lock);
913
914   if (filter) {
915     GstCaps *intersection;
916
917     intersection =
918         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
919     gst_caps_unref (caps);
920     caps = intersection;
921   }
922
923   return caps;
924 }
925
926 static gboolean
927 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
928 {
929   GstWaylandSink *sink;
930   GstBufferPool *newpool;
931   GstVideoInfo info;
932 #ifdef GST_WLSINK_ENHANCEMENT
933   uint32_t tbm_format;
934 #endif
935   enum wl_shm_format format;
936
937   GArray *formats;
938   gint i;
939   GstStructure *structure;
940   GstWlShmAllocator *self = NULL;
941
942   FUNCTION;
943
944   sink = GST_WAYLAND_SINK (bsink);
945
946   GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
947
948   /* extract info from caps */
949   if (!gst_video_info_from_caps (&info, caps))
950     goto invalid_format;
951 #ifdef GST_WLSINK_ENHANCEMENT
952   sink->caps = gst_caps_copy (caps);
953   if (sink->USE_TBM) {
954     tbm_format =
955         gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info));
956     if ((gint) tbm_format == -1)
957       goto invalid_format;
958   } else {
959     format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
960     if ((gint) format == -1)
961       goto invalid_format;
962   }
963 #else /* open source */
964   format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
965
966   if ((gint) format == -1)
967     goto invalid_format;
968 #endif
969
970   /* verify we support the requested format */
971 #ifdef GST_WLSINK_ENHANCEMENT
972   if (sink->display->USE_TBM) {
973     GST_INFO ("USE TBM FORMAT");
974     formats = sink->display->tbm_formats;
975     for (i = 0; i < formats->len; i++) {
976       if (g_array_index (formats, uint32_t, i) == tbm_format)
977         break;
978     }
979   } else {                      /* USE SHM */
980     GST_INFO ("USE SHM FORMAT");
981     formats = sink->display->formats;
982     for (i = 0; i < formats->len; i++) {
983       if (g_array_index (formats, uint32_t, i) == format)
984         break;
985     }
986   }
987 #else /* open source */
988
989   formats = sink->display->formats;
990   for (i = 0; i < formats->len; i++) {
991     if (g_array_index (formats, uint32_t, i) == format)
992       break;
993   }
994 #endif
995   if (i >= formats->len)
996     goto unsupported_format;
997
998 #ifdef GST_WLSINK_ENHANCEMENT
999   if (sink->USE_TBM) {
1000     if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
1001         GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
1002       sink->display->is_native_format = TRUE;
1003
1004       /* store the video info */
1005       sink->video_info = info;
1006       sink->video_info_changed = TRUE;
1007     } else {
1008       sink->display->is_native_format = FALSE;
1009       self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1010       self->display = sink->display;
1011       /* create a new pool for the new configuration */
1012       newpool = gst_video_buffer_pool_new ();
1013       if (!newpool)
1014         goto pool_failed;
1015
1016       structure = gst_buffer_pool_get_config (newpool);
1017       gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1018       gst_buffer_pool_config_set_allocator (structure,
1019           gst_wl_shm_allocator_get (), NULL);
1020       if (!gst_buffer_pool_set_config (newpool, structure))
1021         goto config_failed;
1022
1023       /* store the video info */
1024       sink->video_info = info;
1025       sink->video_info_changed = TRUE;
1026
1027       gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1028       gst_object_unref (newpool);
1029     }
1030   } else {                      /* USE SHM */
1031
1032     self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1033     self->display = sink->display;
1034
1035     /* create a new pool for the new configuration */
1036     newpool = gst_video_buffer_pool_new ();
1037     if (!newpool)
1038       goto pool_failed;
1039
1040     structure = gst_buffer_pool_get_config (newpool);
1041     gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1042     gst_buffer_pool_config_set_allocator (structure,
1043         gst_wl_shm_allocator_get (), NULL);
1044     if (!gst_buffer_pool_set_config (newpool, structure))
1045       goto config_failed;
1046
1047     /* store the video info */
1048     sink->video_info = info;
1049     sink->video_info_changed = TRUE;
1050
1051     gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1052     gst_object_unref (newpool);
1053   }
1054 #else /*open source */
1055   /* create a new pool for the new configuration */
1056   newpool = gst_video_buffer_pool_new ();
1057   if (!newpool)
1058     goto pool_failed;
1059
1060   structure = gst_buffer_pool_get_config (newpool);
1061   gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1062   gst_buffer_pool_config_set_allocator (structure,
1063       gst_wl_shm_allocator_get (), NULL);
1064   if (!gst_buffer_pool_set_config (newpool, structure))
1065     goto config_failed;
1066
1067   /* store the video info */
1068   sink->video_info = info;
1069   sink->video_info_changed = TRUE;
1070
1071   gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1072   gst_object_unref (newpool);
1073 #endif
1074
1075   return TRUE;
1076
1077 invalid_format:
1078   {
1079     GST_DEBUG_OBJECT (sink,
1080         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1081     return FALSE;
1082   }
1083 unsupported_format:
1084   {
1085 #ifdef GST_WLSINK_ENHANCEMENT
1086     if (sink->USE_TBM)
1087       GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1088           gst_wl_tbm_format_to_string (tbm_format));
1089     else                        /*USE SHM */
1090       GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1091           gst_wl_shm_format_to_string (format));
1092 #else /*open source */
1093     GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1094         gst_wl_shm_format_to_string (format));
1095 #endif
1096     return FALSE;
1097   }
1098 pool_failed:
1099   {
1100     GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1101     return FALSE;
1102   }
1103 config_failed:
1104   {
1105     GST_DEBUG_OBJECT (bsink, "failed setting config");
1106     gst_object_unref (newpool);
1107     return FALSE;
1108   }
1109 }
1110
1111 static gboolean
1112 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1113 {
1114   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1115   GstStructure *config;
1116   guint size, min_bufs, max_bufs;
1117 #ifdef GST_WLSINK_ENHANCEMENT
1118   gboolean need_pool;
1119   GstCaps *caps;
1120   FUNCTION;
1121
1122   if (sink->USE_TBM) {
1123     if (sink->display->is_native_format == TRUE)
1124       return TRUE;
1125
1126     gst_query_parse_allocation (query, &caps, &need_pool);
1127
1128     if (caps == NULL) {
1129       GST_DEBUG_OBJECT (bsink, "no caps specified");
1130       return FALSE;
1131     }
1132   }
1133 #endif
1134   config = gst_buffer_pool_get_config (sink->pool);
1135   gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
1136
1137   /* we do have a pool for sure (created in set_caps),
1138    * so let's propose it anyway, but also propose the allocator on its own */
1139   gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
1140   gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
1141
1142   gst_structure_free (config);
1143
1144   return TRUE;
1145 }
1146
1147 static GstFlowReturn
1148 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
1149 {
1150   FUNCTION;
1151   GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
1152   return gst_wayland_sink_render (bsink, buffer);
1153 }
1154
1155 static void
1156 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
1157 {
1158   GstWaylandSink *sink = data;
1159   FUNCTION;
1160
1161   GST_LOG ("frame_redraw_cb");
1162
1163   g_atomic_int_set (&sink->redraw_pending, FALSE);
1164   wl_callback_destroy (callback);
1165 }
1166
1167 static const struct wl_callback_listener frame_callback_listener = {
1168   frame_redraw_callback
1169 };
1170
1171 #ifdef GST_WLSINK_ENHANCEMENT
1172 static void
1173 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1174 {
1175   FUNCTION;
1176   g_return_if_fail (sink != NULL);
1177   g_return_if_fail (sink->window != NULL);
1178
1179   gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1180   gst_wl_window_set_disp_geo_method (sink->window,
1181       sink->display_geometry_method);
1182   gst_wl_window_set_orientation (sink->window, sink->orientation);
1183   gst_wl_window_set_flip (sink->window, sink->flip);
1184 }
1185 #endif
1186 /* must be called with the render lock */
1187 static void
1188 render_last_buffer (GstWaylandSink * sink)
1189 {
1190   GstWlBuffer *wlbuffer;
1191   const GstVideoInfo *info = NULL;
1192   struct wl_surface *surface;
1193   struct wl_callback *callback;
1194   FUNCTION;
1195
1196   wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
1197   surface = gst_wl_window_get_wl_surface (sink->window);
1198
1199   g_atomic_int_set (&sink->redraw_pending, TRUE);
1200   callback = wl_surface_frame (surface);
1201   /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */
1202   wl_callback_add_listener (callback, &frame_callback_listener, sink);
1203
1204   if (G_UNLIKELY (sink->video_info_changed)) {
1205     info = &sink->video_info;
1206     sink->video_info_changed = FALSE;
1207   }
1208 #ifdef GST_WLSINK_ENHANCEMENT
1209   if (sink->last_buffer)
1210     gst_wl_window_render (sink->window, wlbuffer, info);
1211   else {
1212     if (G_UNLIKELY (info)) {
1213       gst_wl_window_set_video_info (sink->window, &info);
1214     }
1215   }
1216 #else
1217   gst_wl_window_render (sink->window, wlbuffer, info);
1218 #endif
1219 }
1220
1221 static GstFlowReturn
1222 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
1223 {
1224   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1225   GstBuffer *to_render;
1226   GstWlBuffer *wlbuffer;
1227   GstFlowReturn ret = GST_FLOW_OK;
1228   FUNCTION;
1229
1230   g_mutex_lock (&sink->render_lock);
1231
1232   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
1233
1234   if (G_UNLIKELY (!sink->window)) {
1235     /* ask for window handle. Unlock render_lock while doing that because
1236      * set_window_handle & friends will lock it in this context */
1237     g_mutex_unlock (&sink->render_lock);
1238     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1239     g_mutex_lock (&sink->render_lock);
1240
1241     if (!sink->window) {
1242       /* if we were not provided a window, create one ourselves */
1243       sink->window =
1244           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
1245     }
1246   }
1247 #ifdef GST_WLSINK_ENHANCEMENT
1248   gst_wayland_sink_update_window_geometry (sink);
1249   sink->video_info_changed = TRUE;
1250 #endif
1251   /* drop buffers until we get a frame callback */
1252   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
1253     goto done;
1254   /* make sure that the application has called set_render_rectangle() */
1255   if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
1256     goto no_window_size;
1257
1258 #ifdef GST_WLSINK_ENHANCEMENT
1259
1260   wlbuffer = gst_buffer_get_wl_buffer (buffer);
1261   if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1262     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
1263     GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1264     to_render = buffer;
1265 #ifdef DUMP_BUFFER
1266     GstMemory *mem;
1267     GstMapInfo mem_info = GST_MAP_INFO_INIT;
1268     int size = GST_VIDEO_INFO_SIZE (&sink->video_info);
1269     mem = gst_buffer_peek_memory (to_render, 0);
1270     gst_memory_map (mem, &mem_info, GST_MAP_READ);
1271     void *data;
1272     data = mem_info.data;
1273     int ret;
1274     char file_name[128];
1275
1276     sprintf (file_name, "/home/owner/DUMP/_WLSINK_OUT_DUMP_%2.2d.dump",
1277         dump__cnt++);
1278     ret = __write_rawdata (file_name, data, size);
1279     if (ret) {
1280       GST_ERROR ("_write_rawdata() failed");
1281     }
1282     GST_INFO ("DUMP IMAGE %d, size (%d)", dump__cnt, size);
1283     gst_memory_unmap (mem, &mem_info);
1284 #endif
1285   } else {
1286     GstMemory *mem;
1287     struct wl_buffer *wbuf = NULL;
1288
1289     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
1290     mem = gst_buffer_peek_memory (buffer, 0);
1291     if (gst_is_wl_shm_memory (mem)) {
1292       FUNCTION;
1293       wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1294           &sink->video_info);
1295       if (wbuf) {
1296         gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); //careat GstWlBuffer and add  gstbuffer, wlbuffer, display and etc
1297         to_render = buffer;
1298       }
1299     } else {                    //buffer is not from our pool and have not wl_buffer
1300       GstMapInfo src;
1301       /* we don't know how to create a wl_buffer directly from the provided
1302        * memory, so we have to copy the data to a memory that we know how
1303        * to handle... */
1304
1305       GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1306       GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1307           buffer);
1308
1309       if (sink->USE_TBM && sink->display->is_native_format) {
1310         /* in case of SN12 or ST12 */
1311         if (!gst_wayland_sink_get_mm_video_buf_info (sink->display, buffer))
1312           return GST_FLOW_ERROR;
1313
1314         wlbuffer = gst_buffer_get_wl_buffer (buffer);
1315         if (G_UNLIKELY (!wlbuffer)) {
1316           wbuf =
1317               gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1318               &sink->video_info);
1319           if (G_UNLIKELY (!wbuf))
1320             goto no_wl_buffer;
1321           gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1322         }
1323       } else if (sink->USE_TBM && !sink->display->is_native_format) {
1324
1325         /* sink->pool always exists (created in set_caps), but it may not
1326          * be active if upstream is not using it */
1327         if (!gst_buffer_pool_is_active (sink->pool)
1328             && !gst_buffer_pool_set_active (sink->pool, TRUE))
1329           goto activate_failed;
1330
1331         ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1332         if (ret != GST_FLOW_OK)
1333           goto no_buffer;
1334
1335         //GstMemory *mem;
1336         //mem = gst_buffer_peek_memory (to_render, 0);
1337         //if (gst_is_wl_shm_memory (mem)) {
1338         GST_INFO ("to_render buffer is our buffer");
1339         //}
1340         /* the first time we acquire a buffer,
1341          * we need to attach a wl_buffer on it */
1342         wlbuffer = gst_buffer_get_wl_buffer (buffer);
1343         if (G_UNLIKELY (!wlbuffer)) {
1344           mem = gst_buffer_peek_memory (to_render, 0);
1345           wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1346               &sink->video_info);
1347           if (G_UNLIKELY (!wbuf))
1348             goto no_wl_buffer;
1349
1350           wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1351         }
1352
1353         gst_buffer_map (buffer, &src, GST_MAP_READ);
1354         gst_buffer_fill (to_render, 0, src.data, src.size);
1355         gst_buffer_unmap (buffer, &src);
1356       } else {                  /* USE SHM */
1357         /* sink->pool always exists (created in set_caps), but it may not
1358          * be active if upstream is not using it */
1359         if (!gst_buffer_pool_is_active (sink->pool) &&
1360             !gst_buffer_pool_set_active (sink->pool, TRUE))
1361           goto activate_failed;
1362         ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1363         if (ret != GST_FLOW_OK)
1364           goto no_buffer;
1365         /* the first time we acquire a buffer,
1366          * we need to attach a wl_buffer on it */
1367         wlbuffer = gst_buffer_get_wl_buffer (buffer);
1368         if (G_UNLIKELY (!wlbuffer)) {
1369           mem = gst_buffer_peek_memory (to_render, 0);
1370           wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1371               &sink->video_info);
1372           if (G_UNLIKELY (!wbuf))
1373             goto no_wl_buffer;
1374
1375           gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1376
1377         }
1378
1379         gst_buffer_map (buffer, &src, GST_MAP_READ);
1380         gst_buffer_fill (to_render, 0, src.data, src.size);
1381         gst_buffer_unmap (buffer, &src);
1382       }
1383     }
1384   }
1385
1386   if (sink->USE_TBM && sink->display->is_native_format) {
1387     if (G_UNLIKELY (buffer == sink->last_buffer)) {
1388       GST_LOG_OBJECT (sink, "Buffer already being rendered");
1389       goto done;
1390     }
1391     gst_buffer_replace (&sink->last_buffer, buffer);
1392
1393     if (sink->visible)
1394       render_last_buffer (sink);
1395
1396     goto done;
1397
1398   } else {                      /* USE SHM or normal format */
1399     /* drop double rendering */
1400     if (G_UNLIKELY (buffer == sink->last_buffer)) {
1401       GST_LOG_OBJECT (sink, "Buffer already being rendered");
1402       goto done;
1403     }
1404     gst_buffer_replace (&sink->last_buffer, to_render);
1405
1406     if (sink->visible)
1407       render_last_buffer (sink);
1408
1409     if (buffer != to_render)
1410       gst_buffer_unref (to_render);
1411
1412     goto done;
1413   }
1414
1415 #else /* open source */
1416
1417   wlbuffer = gst_buffer_get_wl_buffer (buffer);
1418
1419   if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1420     GST_LOG_OBJECT (sink,
1421         "buffer %p has a wl_buffer from our display, " "writing directly",
1422         buffer);
1423     GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1424     to_render = buffer;
1425
1426   } else {
1427     GstMemory *mem;
1428     struct wl_buffer *wbuf = NULL;
1429
1430     GST_LOG_OBJECT (sink,
1431         "buffer %p does not have a wl_buffer from our " "display, creating it",
1432         buffer);
1433     mem = gst_buffer_peek_memory (buffer, 0);
1434     if (gst_is_wl_shm_memory (mem)) {
1435       FUNCTION;
1436       wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1437           &sink->video_info);
1438     }
1439     if (wbuf) {
1440       gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1441       to_render = buffer;
1442
1443     } else {
1444       GstMapInfo src;
1445       /* we don't know how to create a wl_buffer directly from the provided
1446        * memory, so we have to copy the data to a memory that we know how
1447        * to handle... */
1448
1449       GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1450       GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1451           buffer);
1452       /* sink->pool always exists (created in set_caps), but it may not
1453        * be active if upstream is not using it */
1454       if (!gst_buffer_pool_is_active (sink->pool) &&
1455           !gst_buffer_pool_set_active (sink->pool, TRUE))
1456         goto activate_failed;
1457
1458       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1459       if (ret != GST_FLOW_OK)
1460         goto no_buffer;
1461
1462       /* the first time we acquire a buffer,
1463        * we need to attach a wl_buffer on it */
1464       wlbuffer = gst_buffer_get_wl_buffer (buffer);
1465       if (G_UNLIKELY (!wlbuffer)) {
1466         mem = gst_buffer_peek_memory (to_render, 0);
1467         wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1468             &sink->video_info);
1469         if (G_UNLIKELY (!wbuf))
1470           goto no_wl_buffer;
1471
1472         gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1473       }
1474
1475       gst_buffer_map (buffer, &src, GST_MAP_READ);
1476       gst_buffer_fill (to_render, 0, src.data, src.size);
1477       gst_buffer_unmap (buffer, &src);
1478     }
1479   }
1480   /* drop double rendering */
1481   if (G_UNLIKELY (buffer == sink->last_buffer)) {
1482     GST_LOG_OBJECT (sink, "Buffer already being rendered");
1483     goto done;
1484   }
1485
1486   gst_buffer_replace (&sink->last_buffer, to_render);
1487   render_last_buffer (sink);
1488
1489   if (buffer != to_render)
1490     gst_buffer_unref (to_render);
1491
1492   goto done;
1493
1494 #endif /* GST_WLSINK_ENHANCEMENT */
1495
1496 no_window_size:
1497   {
1498     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1499         ("Window has no size set"),
1500         ("Make sure you set the size after calling set_window_handle"));
1501     ret = GST_FLOW_ERROR;
1502     goto done;
1503   }
1504 no_buffer:
1505   {
1506     GST_WARNING_OBJECT (sink, "could not create buffer");
1507     goto done;
1508   }
1509 no_wl_buffer:
1510   {
1511     GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
1512     ret = GST_FLOW_ERROR;
1513     goto done;
1514   }
1515 activate_failed:
1516   {
1517     GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1518     ret = GST_FLOW_ERROR;
1519     goto done;
1520   }
1521 done:
1522   {
1523     g_mutex_unlock (&sink->render_lock);
1524     return ret;
1525   }
1526 }
1527
1528 static void
1529 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1530 {
1531   iface->set_window_handle = gst_wayland_sink_set_window_handle;
1532   iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1533   iface->expose = gst_wayland_sink_expose;
1534 #ifdef GST_WLSINK_ENHANCEMENT   /* use  unique_id */
1535   iface->set_wl_window_wl_surface_id =
1536       gst_wayland_sink_set_wl_window_wl_surface_id;
1537 #endif
1538 }
1539
1540 #ifdef GST_WLSINK_ENHANCEMENT   /* use  unique_id */
1541 static void
1542 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
1543     guintptr wl_surface_id)
1544 {
1545   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1546   FUNCTION;
1547   g_return_if_fail (sink != NULL);
1548
1549   if (sink->window != NULL) {
1550     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1551     return;
1552   }
1553   g_mutex_lock (&sink->render_lock);
1554   g_clear_object (&sink->window);
1555
1556   GST_INFO ("wl_surface_id %d %p", (int) wl_surface_id,
1557       (guintptr) wl_surface_id);
1558
1559   if (wl_surface_id) {
1560     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1561       /* we cannot use our own display with an external window handle */
1562       if (G_UNLIKELY (sink->display->own_display)) {
1563         sink->display->wl_surface_id = (int) wl_surface_id;
1564         sink->window = gst_wl_window_new_in_surface (sink->display, NULL);
1565       }
1566     } else {
1567       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1568           "ignoring window handle");
1569     }
1570   }
1571   gst_wayland_sink_update_window_geometry (sink);
1572
1573   g_mutex_unlock (&sink->render_lock);
1574
1575 }
1576 #endif
1577
1578 static void
1579 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1580 {
1581   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1582   struct wl_surface *surface = (struct wl_surface *) handle;
1583   FUNCTION;
1584
1585   g_return_if_fail (sink != NULL);
1586
1587 #ifdef GST_WLSINK_ENHANCEMENT   /* use  unique_id */
1588   if (sink->window != NULL) {
1589     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1590     return;
1591   }
1592 #endif
1593   g_mutex_lock (&sink->render_lock);
1594
1595   GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1596       (void *) handle);
1597
1598   g_clear_object (&sink->window);
1599
1600   if (handle) {
1601     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1602       /* we cannot use our own display with an external window handle */
1603       if (G_UNLIKELY (sink->display->own_display)) {
1604         GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
1605             ("Application did not provide a wayland display handle"),
1606             ("Now waylandsink use internal display handle "
1607                 "which is created ourselves. Consider providing a "
1608                 "display handle from your application with GstContext"));
1609         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1610       } else {
1611         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1612       }
1613     } else {
1614       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1615           "ignoring window handle");
1616     }
1617   }
1618   g_mutex_unlock (&sink->render_lock);
1619
1620 }
1621
1622 static void
1623 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1624     gint x, gint y, gint w, gint h)
1625 {
1626   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1627   FUNCTION;
1628
1629   g_return_if_fail (sink != NULL);
1630
1631   g_mutex_lock (&sink->render_lock);
1632   if (!sink->window) {
1633     g_mutex_unlock (&sink->render_lock);
1634     GST_WARNING_OBJECT (sink,
1635         "set_render_rectangle called without window, ignoring");
1636     return;
1637   }
1638
1639   GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1640       x, y, w, h);
1641   gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1642
1643   g_mutex_unlock (&sink->render_lock);
1644 }
1645
1646 static void
1647 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1648 {
1649   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1650   FUNCTION;
1651
1652   g_return_if_fail (sink != NULL);
1653
1654   GST_DEBUG_OBJECT (sink, "expose");
1655
1656   g_mutex_lock (&sink->render_lock);
1657   if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1658     GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1659     render_last_buffer (sink);
1660   }
1661   g_mutex_unlock (&sink->render_lock);
1662 }
1663
1664 static void
1665 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1666 {
1667   iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1668   iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1669 }
1670
1671 static void
1672 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1673 {
1674   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1675   FUNCTION;
1676   g_return_if_fail (sink != NULL);
1677
1678   g_mutex_lock (&sink->render_lock);
1679   if (!sink->window || !sink->window->area_subsurface) {
1680     g_mutex_unlock (&sink->render_lock);
1681     GST_INFO_OBJECT (sink,
1682         "begin_geometry_change called without window, ignoring");
1683     return;
1684   }
1685
1686   wl_subsurface_set_sync (sink->window->area_subsurface);
1687   g_mutex_unlock (&sink->render_lock);
1688 }
1689
1690 static void
1691 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1692 {
1693   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1694   FUNCTION;
1695   g_return_if_fail (sink != NULL);
1696
1697   g_mutex_lock (&sink->render_lock);
1698   if (!sink->window || !sink->window->area_subsurface) {
1699     g_mutex_unlock (&sink->render_lock);
1700     GST_INFO_OBJECT (sink,
1701         "end_geometry_change called without window, ignoring");
1702     return;
1703   }
1704
1705   wl_subsurface_set_desync (sink->window->area_subsurface);
1706   g_mutex_unlock (&sink->render_lock);
1707 }
1708
1709 static gboolean
1710 plugin_init (GstPlugin * plugin)
1711 {
1712   GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1713       " wayland video sink");
1714
1715   gst_wl_shm_allocator_register ();
1716
1717   return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1718       GST_TYPE_WAYLAND_SINK);
1719 }
1720
1721 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1722     GST_VERSION_MINOR,
1723     waylandsink,
1724     "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1725     GST_PACKAGE_ORIGIN)