waylandsink : fix crash issue
[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         if (sink->display)
753           sink->display->USE_TBM = sink->USE_TBM;
754 #endif
755       }
756     }
757   }
758
759   g_mutex_unlock (&sink->display_lock);
760
761   return ret;
762 }
763
764 static GstStateChangeReturn
765 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
766 {
767   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
768   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
769   FUNCTION;
770
771   switch (transition) {
772     case GST_STATE_CHANGE_NULL_TO_READY:
773       if (!gst_wayland_sink_find_display (sink))
774         return GST_STATE_CHANGE_FAILURE;
775       break;
776     default:
777       break;
778   }
779
780   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
781   if (ret == GST_STATE_CHANGE_FAILURE)
782     return ret;
783
784   switch (transition) {
785     case GST_STATE_CHANGE_PAUSED_TO_READY:
786       gst_buffer_replace (&sink->last_buffer, NULL);
787       if (sink->window) {
788         if (gst_wl_window_is_toplevel (sink->window)) {
789           g_clear_object (&sink->window);
790         } else {
791           /* remove buffer from surface, show nothing */
792 #ifdef USE_WL_FLUSH_BUFFER
793           sink->display->flush_request = 0;
794 #endif
795           gst_wl_window_render (sink->window, NULL, NULL);
796         }
797       }
798       break;
799     case GST_STATE_CHANGE_READY_TO_NULL:
800       g_mutex_lock (&sink->display_lock);
801       /* If we had a toplevel window, we most likely have our own connection
802        * to the display too, and it is a good idea to disconnect and allow
803        * potentially the application to embed us with GstVideoOverlay
804        * (which requires to re-use the same display connection as the parent
805        * surface). If we didn't have a toplevel window, then the display
806        * connection that we have is definitely shared with the application
807        * and it's better to keep it around (together with the window handle)
808        * to avoid requesting them again from the application if/when we are
809        * restarted (GstVideoOverlay behaves like that in other sinks)
810        */
811       if (sink->display && !sink->window) {     /* -> the window was toplevel */
812         g_clear_object (&sink->display);
813       }
814       g_mutex_unlock (&sink->display_lock);
815       g_clear_object (&sink->pool);
816       break;
817     default:
818       break;
819   }
820
821   return ret;
822 }
823
824 static void
825 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
826 {
827   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
828   FUNCTION;
829
830   if (gst_context_has_context_type (context,
831           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
832     g_mutex_lock (&sink->display_lock);
833     if (G_LIKELY (!sink->display))
834       gst_wayland_sink_set_display_from_context (sink, context);
835     else {
836       GST_WARNING_OBJECT (element, "changing display handle is not supported");
837 #ifdef GST_WLSINK_ENHANCEMENT
838       g_mutex_unlock (&sink->display_lock);
839       return;
840 #endif
841     }
842     g_mutex_unlock (&sink->display_lock);
843   }
844
845   if (GST_ELEMENT_CLASS (parent_class)->set_context)
846     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
847 }
848
849 static GstCaps *
850 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
851 {
852   GstWaylandSink *sink;
853   GstCaps *caps;
854   FUNCTION;
855
856   sink = GST_WAYLAND_SINK (bsink);
857
858   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
859
860   g_mutex_lock (&sink->display_lock);
861
862   if (sink->display) {
863     GValue list = G_VALUE_INIT;
864     GValue value = G_VALUE_INIT;
865     GArray *formats;
866     gint i;
867 #ifdef GST_WLSINK_ENHANCEMENT
868     uint32_t tbm_fmt;
869 #endif
870     enum wl_shm_format fmt;
871
872     g_value_init (&list, GST_TYPE_LIST);
873     g_value_init (&value, G_TYPE_STRING);
874 #ifdef GST_WLSINK_ENHANCEMENT
875     if (sink->display->USE_TBM)
876       formats = sink->display->tbm_formats;
877     else                        /* SHM */
878 #endif
879       formats = sink->display->formats;
880
881     for (i = 0; i < formats->len; i++) {
882 #ifdef GST_WLSINK_ENHANCEMENT
883       if (sink->USE_TBM) {
884         tbm_fmt = g_array_index (formats, uint32_t, i);
885         g_value_set_string (&value, gst_wl_tbm_format_to_string (tbm_fmt));
886         gst_value_list_append_value (&list, &value);
887         /* TBM doesn't support SN12 and ST12. So we add SN12 and ST12 manually as supported format.
888          * SN12 is same with NV12, ST12 is same with NV12MT
889          */
890         if (tbm_fmt == TBM_FORMAT_NV12) {
891           g_value_set_string (&value,
892               gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
893           gst_value_list_append_value (&list, &value);
894         } else if (tbm_fmt == TBM_FORMAT_NV12MT) {
895           g_value_set_string (&value,
896               gst_video_format_to_string (GST_VIDEO_FORMAT_ST12));
897           gst_value_list_append_value (&list, &value);
898         }
899       } else {                  /* USE SHM */
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       }
904 #else /* open source */
905       fmt = g_array_index (formats, uint32_t, i);
906       g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
907       gst_value_list_append_value (&list, &value);
908 #endif
909     }
910
911     caps = gst_caps_make_writable (caps);
912     gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
913
914     GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
915   }
916
917   g_mutex_unlock (&sink->display_lock);
918
919   if (filter) {
920     GstCaps *intersection;
921
922     intersection =
923         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
924     gst_caps_unref (caps);
925     caps = intersection;
926   }
927
928   return caps;
929 }
930
931 static gboolean
932 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
933 {
934   GstWaylandSink *sink;
935   GstBufferPool *newpool;
936   GstVideoInfo info;
937 #ifdef GST_WLSINK_ENHANCEMENT
938   uint32_t tbm_format;
939 #endif
940   enum wl_shm_format format;
941
942   GArray *formats;
943   gint i;
944   GstStructure *structure;
945   GstWlShmAllocator *self = NULL;
946
947   FUNCTION;
948
949   sink = GST_WAYLAND_SINK (bsink);
950
951   GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
952
953   /* extract info from caps */
954   if (!gst_video_info_from_caps (&info, caps))
955     goto invalid_format;
956 #ifdef GST_WLSINK_ENHANCEMENT
957   sink->caps = gst_caps_copy (caps);
958   if (sink->USE_TBM) {
959     tbm_format =
960         gst_video_format_to_wl_tbm_format (GST_VIDEO_INFO_FORMAT (&info));
961     if ((gint) tbm_format == -1)
962       goto invalid_format;
963   } else {
964     format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
965     if ((gint) format == -1)
966       goto invalid_format;
967   }
968 #else /* open source */
969   format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
970
971   if ((gint) format == -1)
972     goto invalid_format;
973 #endif
974
975   /* verify we support the requested format */
976 #ifdef GST_WLSINK_ENHANCEMENT
977   if (sink->display->USE_TBM) {
978     GST_INFO ("USE TBM FORMAT");
979     formats = sink->display->tbm_formats;
980     for (i = 0; i < formats->len; i++) {
981       if (g_array_index (formats, uint32_t, i) == tbm_format)
982         break;
983     }
984   } else {                      /* USE SHM */
985     GST_INFO ("USE SHM FORMAT");
986     formats = sink->display->formats;
987     for (i = 0; i < formats->len; i++) {
988       if (g_array_index (formats, uint32_t, i) == format)
989         break;
990     }
991   }
992 #else /* open source */
993
994   formats = sink->display->formats;
995   for (i = 0; i < formats->len; i++) {
996     if (g_array_index (formats, uint32_t, i) == format)
997       break;
998   }
999 #endif
1000   if (i >= formats->len)
1001     goto unsupported_format;
1002
1003 #ifdef GST_WLSINK_ENHANCEMENT
1004   if (sink->USE_TBM) {
1005     if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
1006         GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
1007       sink->display->is_native_format = TRUE;
1008
1009       /* store the video info */
1010       sink->video_info = info;
1011       sink->video_info_changed = TRUE;
1012     } else {
1013       sink->display->is_native_format = FALSE;
1014       self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1015       self->display = sink->display;
1016       /* create a new pool for the new configuration */
1017       newpool = gst_video_buffer_pool_new ();
1018       if (!newpool)
1019         goto pool_failed;
1020
1021       structure = gst_buffer_pool_get_config (newpool);
1022       gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1023       gst_buffer_pool_config_set_allocator (structure,
1024           gst_wl_shm_allocator_get (), NULL);
1025       if (!gst_buffer_pool_set_config (newpool, structure))
1026         goto config_failed;
1027
1028       /* store the video info */
1029       sink->video_info = info;
1030       sink->video_info_changed = TRUE;
1031
1032       gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1033       gst_object_unref (newpool);
1034     }
1035   } else {                      /* USE SHM */
1036
1037     self = GST_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get ());
1038     self->display = sink->display;
1039
1040     /* create a new pool for the new configuration */
1041     newpool = gst_video_buffer_pool_new ();
1042     if (!newpool)
1043       goto pool_failed;
1044
1045     structure = gst_buffer_pool_get_config (newpool);
1046     gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1047     gst_buffer_pool_config_set_allocator (structure,
1048         gst_wl_shm_allocator_get (), NULL);
1049     if (!gst_buffer_pool_set_config (newpool, structure))
1050       goto config_failed;
1051
1052     /* store the video info */
1053     sink->video_info = info;
1054     sink->video_info_changed = TRUE;
1055
1056     gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1057     gst_object_unref (newpool);
1058   }
1059 #else /*open source */
1060   /* create a new pool for the new configuration */
1061   newpool = gst_video_buffer_pool_new ();
1062   if (!newpool)
1063     goto pool_failed;
1064
1065   structure = gst_buffer_pool_get_config (newpool);
1066   gst_buffer_pool_config_set_params (structure, caps, info.size, 6, 0);
1067   gst_buffer_pool_config_set_allocator (structure,
1068       gst_wl_shm_allocator_get (), NULL);
1069   if (!gst_buffer_pool_set_config (newpool, structure))
1070     goto config_failed;
1071
1072   /* store the video info */
1073   sink->video_info = info;
1074   sink->video_info_changed = TRUE;
1075
1076   gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1077   gst_object_unref (newpool);
1078 #endif
1079
1080   return TRUE;
1081
1082 invalid_format:
1083   {
1084     GST_DEBUG_OBJECT (sink,
1085         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1086     return FALSE;
1087   }
1088 unsupported_format:
1089   {
1090 #ifdef GST_WLSINK_ENHANCEMENT
1091     if (sink->USE_TBM)
1092       GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1093           gst_wl_tbm_format_to_string (tbm_format));
1094     else                        /*USE SHM */
1095       GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1096           gst_wl_shm_format_to_string (format));
1097 #else /*open source */
1098     GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
1099         gst_wl_shm_format_to_string (format));
1100 #endif
1101     return FALSE;
1102   }
1103 pool_failed:
1104   {
1105     GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1106     return FALSE;
1107   }
1108 config_failed:
1109   {
1110     GST_DEBUG_OBJECT (bsink, "failed setting config");
1111     gst_object_unref (newpool);
1112     return FALSE;
1113   }
1114 }
1115
1116 static gboolean
1117 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1118 {
1119   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1120   GstStructure *config;
1121   guint size, min_bufs, max_bufs;
1122 #ifdef GST_WLSINK_ENHANCEMENT
1123   gboolean need_pool;
1124   GstCaps *caps;
1125   FUNCTION;
1126
1127   if (sink->USE_TBM) {
1128     if (sink->display->is_native_format == TRUE)
1129       return TRUE;
1130
1131     gst_query_parse_allocation (query, &caps, &need_pool);
1132
1133     if (caps == NULL) {
1134       GST_DEBUG_OBJECT (bsink, "no caps specified");
1135       return FALSE;
1136     }
1137   }
1138 #endif
1139   config = gst_buffer_pool_get_config (sink->pool);
1140   gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
1141
1142   /* we do have a pool for sure (created in set_caps),
1143    * so let's propose it anyway, but also propose the allocator on its own */
1144   gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
1145   gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
1146
1147   gst_structure_free (config);
1148
1149   return TRUE;
1150 }
1151
1152 static GstFlowReturn
1153 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
1154 {
1155   FUNCTION;
1156   GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
1157   return gst_wayland_sink_render (bsink, buffer);
1158 }
1159
1160 static void
1161 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
1162 {
1163   GstWaylandSink *sink = data;
1164   FUNCTION;
1165
1166   GST_LOG ("frame_redraw_cb");
1167
1168   g_atomic_int_set (&sink->redraw_pending, FALSE);
1169   wl_callback_destroy (callback);
1170 }
1171
1172 static const struct wl_callback_listener frame_callback_listener = {
1173   frame_redraw_callback
1174 };
1175
1176 #ifdef GST_WLSINK_ENHANCEMENT
1177 static void
1178 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1179 {
1180   FUNCTION;
1181   g_return_if_fail (sink != NULL);
1182   g_return_if_fail (sink->window != NULL);
1183
1184   gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1185   gst_wl_window_set_disp_geo_method (sink->window,
1186       sink->display_geometry_method);
1187   gst_wl_window_set_orientation (sink->window, sink->orientation);
1188   gst_wl_window_set_flip (sink->window, sink->flip);
1189 }
1190 #endif
1191 /* must be called with the render lock */
1192 static void
1193 render_last_buffer (GstWaylandSink * sink)
1194 {
1195   GstWlBuffer *wlbuffer;
1196   const GstVideoInfo *info = NULL;
1197   struct wl_surface *surface;
1198   struct wl_callback *callback;
1199   FUNCTION;
1200
1201   wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
1202   surface = gst_wl_window_get_wl_surface (sink->window);
1203
1204   g_atomic_int_set (&sink->redraw_pending, TRUE);
1205   callback = wl_surface_frame (surface);
1206   /* frame_callback_listener is called when wayland-client finish rendering the wl_buffer */
1207   wl_callback_add_listener (callback, &frame_callback_listener, sink);
1208
1209   if (G_UNLIKELY (sink->video_info_changed)) {
1210     info = &sink->video_info;
1211     sink->video_info_changed = FALSE;
1212   }
1213 #ifdef GST_WLSINK_ENHANCEMENT
1214   if (sink->last_buffer)
1215     gst_wl_window_render (sink->window, wlbuffer, info);
1216   else {
1217     if (G_UNLIKELY (info)) {
1218       gst_wl_window_set_video_info (sink->window, info);
1219     }
1220   }
1221 #else
1222   gst_wl_window_render (sink->window, wlbuffer, info);
1223 #endif
1224 }
1225
1226 static GstFlowReturn
1227 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
1228 {
1229   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
1230   GstBuffer *to_render;
1231   GstWlBuffer *wlbuffer;
1232   GstFlowReturn ret = GST_FLOW_OK;
1233   FUNCTION;
1234
1235   g_mutex_lock (&sink->render_lock);
1236
1237   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
1238
1239   if (G_UNLIKELY (!sink->window)) {
1240     /* ask for window handle. Unlock render_lock while doing that because
1241      * set_window_handle & friends will lock it in this context */
1242     g_mutex_unlock (&sink->render_lock);
1243     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1244     g_mutex_lock (&sink->render_lock);
1245
1246     if (!sink->window) {
1247       /* if we were not provided a window, create one ourselves */
1248       sink->window =
1249           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
1250     }
1251   }
1252 #ifdef GST_WLSINK_ENHANCEMENT
1253   gst_wayland_sink_update_window_geometry (sink);
1254   sink->video_info_changed = TRUE;
1255 #endif
1256   /* drop buffers until we get a frame callback */
1257   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
1258     goto done;
1259   /* make sure that the application has called set_render_rectangle() */
1260   if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
1261     goto no_window_size;
1262
1263 #ifdef GST_WLSINK_ENHANCEMENT
1264
1265   wlbuffer = gst_buffer_get_wl_buffer (buffer);
1266   if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1267     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
1268     GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1269     to_render = buffer;
1270 #ifdef DUMP_BUFFER
1271     GstMemory *mem;
1272     GstMapInfo mem_info = GST_MAP_INFO_INIT;
1273     int size = GST_VIDEO_INFO_SIZE (&sink->video_info);
1274     mem = gst_buffer_peek_memory (to_render, 0);
1275     gst_memory_map (mem, &mem_info, GST_MAP_READ);
1276     void *data;
1277     data = mem_info.data;
1278     int ret;
1279     char file_name[128];
1280
1281     sprintf (file_name, "/home/owner/DUMP/_WLSINK_OUT_DUMP_%2.2d.dump",
1282         dump__cnt++);
1283     ret = __write_rawdata (file_name, data, size);
1284     if (ret) {
1285       GST_ERROR ("_write_rawdata() failed");
1286     }
1287     GST_INFO ("DUMP IMAGE %d, size (%d)", dump__cnt, size);
1288     gst_memory_unmap (mem, &mem_info);
1289 #endif
1290   } else {
1291     GstMemory *mem;
1292     struct wl_buffer *wbuf = NULL;
1293
1294     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
1295     mem = gst_buffer_peek_memory (buffer, 0);
1296     if (gst_is_wl_shm_memory (mem)) {
1297       FUNCTION;
1298       wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1299           &sink->video_info);
1300       if (wbuf) {
1301         gst_buffer_add_wl_buffer (buffer, wbuf, sink->display); //careat GstWlBuffer and add  gstbuffer, wlbuffer, display and etc
1302         to_render = buffer;
1303       }
1304     } else {                    //buffer is not from our pool and have not wl_buffer
1305       GstMapInfo src;
1306       /* we don't know how to create a wl_buffer directly from the provided
1307        * memory, so we have to copy the data to a memory that we know how
1308        * to handle... */
1309
1310       GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1311       GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1312           buffer);
1313
1314       if (sink->USE_TBM && sink->display->is_native_format) {
1315         /* in case of SN12 or ST12 */
1316         if (!gst_wayland_sink_get_mm_video_buf_info (sink->display, buffer))
1317           return GST_FLOW_ERROR;
1318
1319         wlbuffer = gst_buffer_get_wl_buffer (buffer);
1320         if (G_UNLIKELY (!wlbuffer)) {
1321           wbuf =
1322               gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1323               &sink->video_info);
1324           if (G_UNLIKELY (!wbuf))
1325             goto no_wl_buffer;
1326           gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1327         }
1328       } else if (sink->USE_TBM && !sink->display->is_native_format) {
1329
1330         /* sink->pool always exists (created in set_caps), but it may not
1331          * be active if upstream is not using it */
1332         if (!gst_buffer_pool_is_active (sink->pool)
1333             && !gst_buffer_pool_set_active (sink->pool, TRUE))
1334           goto activate_failed;
1335
1336         ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1337         if (ret != GST_FLOW_OK)
1338           goto no_buffer;
1339
1340         //GstMemory *mem;
1341         //mem = gst_buffer_peek_memory (to_render, 0);
1342         //if (gst_is_wl_shm_memory (mem)) {
1343         GST_INFO ("to_render buffer is our buffer");
1344         //}
1345         /* the first time we acquire a buffer,
1346          * we need to attach a wl_buffer on it */
1347         wlbuffer = gst_buffer_get_wl_buffer (buffer);
1348         if (G_UNLIKELY (!wlbuffer)) {
1349           mem = gst_buffer_peek_memory (to_render, 0);
1350           wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1351               &sink->video_info);
1352           if (G_UNLIKELY (!wbuf))
1353             goto no_wl_buffer;
1354
1355           wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1356         }
1357
1358         gst_buffer_map (buffer, &src, GST_MAP_READ);
1359         gst_buffer_fill (to_render, 0, src.data, src.size);
1360         gst_buffer_unmap (buffer, &src);
1361       } else {                  /* USE SHM */
1362         /* sink->pool always exists (created in set_caps), but it may not
1363          * be active if upstream is not using it */
1364         if (!gst_buffer_pool_is_active (sink->pool) &&
1365             !gst_buffer_pool_set_active (sink->pool, TRUE))
1366           goto activate_failed;
1367         ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1368         if (ret != GST_FLOW_OK)
1369           goto no_buffer;
1370         /* the first time we acquire a buffer,
1371          * we need to attach a wl_buffer on it */
1372         wlbuffer = gst_buffer_get_wl_buffer (buffer);
1373         if (G_UNLIKELY (!wlbuffer)) {
1374           mem = gst_buffer_peek_memory (to_render, 0);
1375           wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1376               &sink->video_info);
1377           if (G_UNLIKELY (!wbuf))
1378             goto no_wl_buffer;
1379
1380           gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1381
1382         }
1383
1384         gst_buffer_map (buffer, &src, GST_MAP_READ);
1385         gst_buffer_fill (to_render, 0, src.data, src.size);
1386         gst_buffer_unmap (buffer, &src);
1387       }
1388     }
1389   }
1390
1391   if (sink->USE_TBM && sink->display->is_native_format) {
1392     if (G_UNLIKELY (buffer == sink->last_buffer)) {
1393       GST_LOG_OBJECT (sink, "Buffer already being rendered");
1394       goto done;
1395     }
1396     gst_buffer_replace (&sink->last_buffer, buffer);
1397
1398     if (sink->visible)
1399       render_last_buffer (sink);
1400
1401     goto done;
1402
1403   } else {                      /* USE SHM or normal format */
1404     /* drop double rendering */
1405     if (G_UNLIKELY (buffer == sink->last_buffer)) {
1406       GST_LOG_OBJECT (sink, "Buffer already being rendered");
1407       goto done;
1408     }
1409     gst_buffer_replace (&sink->last_buffer, to_render);
1410
1411     if (sink->visible)
1412       render_last_buffer (sink);
1413
1414     if (buffer != to_render)
1415       gst_buffer_unref (to_render);
1416
1417     goto done;
1418   }
1419
1420 #else /* open source */
1421
1422   wlbuffer = gst_buffer_get_wl_buffer (buffer);
1423
1424   if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
1425     GST_LOG_OBJECT (sink,
1426         "buffer %p has a wl_buffer from our display, " "writing directly",
1427         buffer);
1428     GST_INFO ("wl_buffer (%p)", wlbuffer->wlbuffer);
1429     to_render = buffer;
1430
1431   } else {
1432     GstMemory *mem;
1433     struct wl_buffer *wbuf = NULL;
1434
1435     GST_LOG_OBJECT (sink,
1436         "buffer %p does not have a wl_buffer from our " "display, creating it",
1437         buffer);
1438     mem = gst_buffer_peek_memory (buffer, 0);
1439     if (gst_is_wl_shm_memory (mem)) {
1440       FUNCTION;
1441       wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1442           &sink->video_info);
1443     }
1444     if (wbuf) {
1445       gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
1446       to_render = buffer;
1447
1448     } else {
1449       GstMapInfo src;
1450       /* we don't know how to create a wl_buffer directly from the provided
1451        * memory, so we have to copy the data to a memory that we know how
1452        * to handle... */
1453
1454       GST_LOG_OBJECT (sink, "buffer %p is not from our pool", buffer);
1455       GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, " "copying",
1456           buffer);
1457       /* sink->pool always exists (created in set_caps), but it may not
1458        * be active if upstream is not using it */
1459       if (!gst_buffer_pool_is_active (sink->pool) &&
1460           !gst_buffer_pool_set_active (sink->pool, TRUE))
1461         goto activate_failed;
1462
1463       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1464       if (ret != GST_FLOW_OK)
1465         goto no_buffer;
1466
1467       /* the first time we acquire a buffer,
1468        * we need to attach a wl_buffer on it */
1469       wlbuffer = gst_buffer_get_wl_buffer (buffer);
1470       if (G_UNLIKELY (!wlbuffer)) {
1471         mem = gst_buffer_peek_memory (to_render, 0);
1472         wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
1473             &sink->video_info);
1474         if (G_UNLIKELY (!wbuf))
1475           goto no_wl_buffer;
1476
1477         gst_buffer_add_wl_buffer (to_render, wbuf, sink->display);
1478       }
1479
1480       gst_buffer_map (buffer, &src, GST_MAP_READ);
1481       gst_buffer_fill (to_render, 0, src.data, src.size);
1482       gst_buffer_unmap (buffer, &src);
1483     }
1484   }
1485   /* drop double rendering */
1486   if (G_UNLIKELY (buffer == sink->last_buffer)) {
1487     GST_LOG_OBJECT (sink, "Buffer already being rendered");
1488     goto done;
1489   }
1490
1491   gst_buffer_replace (&sink->last_buffer, to_render);
1492   render_last_buffer (sink);
1493
1494   if (buffer != to_render)
1495     gst_buffer_unref (to_render);
1496
1497   goto done;
1498
1499 #endif /* GST_WLSINK_ENHANCEMENT */
1500
1501 no_window_size:
1502   {
1503     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1504         ("Window has no size set"),
1505         ("Make sure you set the size after calling set_window_handle"));
1506     ret = GST_FLOW_ERROR;
1507     goto done;
1508   }
1509 no_buffer:
1510   {
1511     GST_WARNING_OBJECT (sink, "could not create buffer");
1512     goto done;
1513   }
1514 no_wl_buffer:
1515   {
1516     GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
1517     ret = GST_FLOW_ERROR;
1518     goto done;
1519   }
1520 activate_failed:
1521   {
1522     GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1523     ret = GST_FLOW_ERROR;
1524     goto done;
1525   }
1526 done:
1527   {
1528     g_mutex_unlock (&sink->render_lock);
1529     return ret;
1530   }
1531 }
1532
1533 static void
1534 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1535 {
1536   iface->set_window_handle = gst_wayland_sink_set_window_handle;
1537   iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1538   iface->expose = gst_wayland_sink_expose;
1539 #ifdef GST_WLSINK_ENHANCEMENT   /* use  unique_id */
1540   iface->set_wl_window_wl_surface_id =
1541       gst_wayland_sink_set_wl_window_wl_surface_id;
1542 #endif
1543 }
1544
1545 #ifdef GST_WLSINK_ENHANCEMENT   /* use  unique_id */
1546 static void
1547 gst_wayland_sink_set_wl_window_wl_surface_id (GstVideoOverlay * overlay,
1548     guintptr wl_surface_id)
1549 {
1550   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1551   FUNCTION;
1552   g_return_if_fail (sink != NULL);
1553
1554   if (sink->window != NULL) {
1555     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1556     return;
1557   }
1558   g_mutex_lock (&sink->render_lock);
1559   g_clear_object (&sink->window);
1560
1561   GST_INFO ("wl_surface_id %d %x", (int) wl_surface_id,
1562       (guintptr) wl_surface_id);
1563
1564   if (wl_surface_id) {
1565     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1566       /* we cannot use our own display with an external window handle */
1567       if (G_UNLIKELY (sink->display->own_display)) {
1568         sink->display->wl_surface_id = (int) wl_surface_id;
1569         sink->window = gst_wl_window_new_in_surface (sink->display, NULL);
1570       }
1571     } else {
1572       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1573           "ignoring window handle");
1574     }
1575   }
1576   gst_wayland_sink_update_window_geometry (sink);
1577
1578   g_mutex_unlock (&sink->render_lock);
1579
1580 }
1581 #endif
1582
1583 static void
1584 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1585 {
1586   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1587   struct wl_surface *surface = (struct wl_surface *) handle;
1588   FUNCTION;
1589
1590   g_return_if_fail (sink != NULL);
1591
1592 #ifdef GST_WLSINK_ENHANCEMENT   /* use  unique_id */
1593   if (sink->window != NULL) {
1594     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1595     return;
1596   }
1597 #endif
1598   g_mutex_lock (&sink->render_lock);
1599
1600   GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1601       (void *) handle);
1602
1603   g_clear_object (&sink->window);
1604
1605   if (handle) {
1606     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1607       /* we cannot use our own display with an external window handle */
1608       if (G_UNLIKELY (sink->display->own_display)) {
1609         GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
1610             ("Application did not provide a wayland display handle"),
1611             ("Now waylandsink use internal display handle "
1612                 "which is created ourselves. Consider providing a "
1613                 "display handle from your application with GstContext"));
1614         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1615       } else {
1616         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1617       }
1618     } else {
1619       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1620           "ignoring window handle");
1621     }
1622   }
1623   g_mutex_unlock (&sink->render_lock);
1624
1625 }
1626
1627 static void
1628 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1629     gint x, gint y, gint w, gint h)
1630 {
1631   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1632   FUNCTION;
1633
1634   g_return_if_fail (sink != NULL);
1635
1636   g_mutex_lock (&sink->render_lock);
1637   if (!sink->window) {
1638     g_mutex_unlock (&sink->render_lock);
1639     GST_WARNING_OBJECT (sink,
1640         "set_render_rectangle called without window, ignoring");
1641     return;
1642   }
1643
1644   GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1645       x, y, w, h);
1646   gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1647
1648   g_mutex_unlock (&sink->render_lock);
1649 }
1650
1651 static void
1652 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1653 {
1654   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1655   FUNCTION;
1656
1657   g_return_if_fail (sink != NULL);
1658
1659   GST_DEBUG_OBJECT (sink, "expose");
1660
1661   g_mutex_lock (&sink->render_lock);
1662   if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1663     GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1664     render_last_buffer (sink);
1665   }
1666   g_mutex_unlock (&sink->render_lock);
1667 }
1668
1669 static void
1670 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1671 {
1672   iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1673   iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1674 }
1675
1676 static void
1677 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1678 {
1679   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1680   FUNCTION;
1681   g_return_if_fail (sink != NULL);
1682
1683   g_mutex_lock (&sink->render_lock);
1684   if (!sink->window || !sink->window->area_subsurface) {
1685     g_mutex_unlock (&sink->render_lock);
1686     GST_INFO_OBJECT (sink,
1687         "begin_geometry_change called without window, ignoring");
1688     return;
1689   }
1690
1691   wl_subsurface_set_sync (sink->window->area_subsurface);
1692   g_mutex_unlock (&sink->render_lock);
1693 }
1694
1695 static void
1696 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1697 {
1698   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1699   FUNCTION;
1700   g_return_if_fail (sink != NULL);
1701
1702   g_mutex_lock (&sink->render_lock);
1703   if (!sink->window || !sink->window->area_subsurface) {
1704     g_mutex_unlock (&sink->render_lock);
1705     GST_INFO_OBJECT (sink,
1706         "end_geometry_change called without window, ignoring");
1707     return;
1708   }
1709
1710   wl_subsurface_set_desync (sink->window->area_subsurface);
1711   g_mutex_unlock (&sink->render_lock);
1712 }
1713
1714 static gboolean
1715 plugin_init (GstPlugin * plugin)
1716 {
1717   GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1718       " wayland video sink");
1719
1720   gst_wl_shm_allocator_register ();
1721
1722   return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1723       GST_TYPE_WAYLAND_SINK);
1724 }
1725
1726 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1727     GST_VERSION_MINOR,
1728     waylandsink,
1729     "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1730     GST_PACKAGE_ORIGIN)