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