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