Waylandsink : Add handling displaying buffer
[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 #else
49 #include "wlvideoformat.h"
50 #endif
51 #include "waylandpool.h"
52
53 #include <gst/wayland/wayland.h>
54 #include <gst/video/videooverlay.h>
55
56 #ifdef GST_WLSINK_ENHANCEMENT
57 #define GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD (gst_waylandsink_display_geometry_method_get_type())
58 #define GST_TYPE_WAYLANDSINK_ROTATE_ANGLE (gst_waylandsink_rotate_angle_get_type())
59 #define GST_TYPE_WAYLANDSINK_FLIP (gst_waylandsink_flip_get_type())
60
61 static GType
62 gst_waylandsink_rotate_angle_get_type (void)
63 {
64   static GType waylandsink_rotate_angle_type = 0;
65   static const GEnumValue rotate_angle_type[] = {
66     {0, "No rotate", "DEGREE_0"},
67     {1, "Rotate 90 degree", "DEGREE_90"},
68     {2, "Rotate 180 degree", "DEGREE_180"},
69     {3, "Rotate 270 degree", "DEGREE_270"},
70     {4, NULL, NULL},
71   };
72
73   if (!waylandsink_rotate_angle_type) {
74     waylandsink_rotate_angle_type =
75         g_enum_register_static ("GstWaylandSinkRotateAngleType",
76         rotate_angle_type);
77   }
78
79   return waylandsink_rotate_angle_type;
80 }
81
82
83 static GType
84 gst_waylandsink_display_geometry_method_get_type (void)
85 {
86   static GType waylandsink_display_geometry_method_type = 0;
87   static const GEnumValue display_geometry_method_type[] = {
88     {0, "Letter box", "LETTER_BOX"},
89     {1, "Origin size", "ORIGIN_SIZE"},
90     {2, "Full-screen", "FULL_SCREEN"},
91     {3, "Cropped full-screen", "CROPPED_FULL_SCREEN"},
92     {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"},
93     {5, NULL, NULL},
94   };
95
96   if (!waylandsink_display_geometry_method_type) {
97     waylandsink_display_geometry_method_type =
98         g_enum_register_static ("GstWaylandSinkDisplayGeometryMethodType",
99         display_geometry_method_type);
100   }
101   return waylandsink_display_geometry_method_type;
102 }
103
104 static GType
105 gst_waylandsink_flip_get_type (void)
106 {
107   static GType waylandsink_flip_type = 0;
108   static const GEnumValue flip_type[] = {
109     {FLIP_NONE, "Flip NONE", "FLIP_NONE"},
110     {FLIP_HORIZONTAL, "Flip HORIZONTAL", "FLIP_HORIZONTAL"},
111     {FLIP_VERTICAL, "Flip VERTICAL", "FLIP_VERTICAL"},
112     {FLIP_BOTH, "Flip BOTH", "FLIP_BOTH"},
113     {FLIP_NUM, NULL, NULL},
114   };
115
116   if (!waylandsink_flip_type) {
117     waylandsink_flip_type =
118         g_enum_register_static ("GstWaylandSinkFlipType", flip_type);
119   }
120
121   return waylandsink_flip_type;
122 }
123
124 #endif
125
126
127 /* signals */
128 enum
129 {
130   SIGNAL_0,
131   LAST_SIGNAL
132 };
133
134 /* Properties */
135 enum
136 {
137   PROP_0,
138   PROP_DISPLAY,
139 #ifdef GST_WLSINK_ENHANCEMENT
140   PROP_ROTATE_ANGLE,
141   PROP_DISPLAY_GEOMETRY_METHOD,
142   PROP_ORIENTATION,
143   PROP_FLIP
144 #endif
145 };
146
147 GST_DEBUG_CATEGORY (gstwayland_debug);
148 #define GST_CAT_DEFAULT gstwayland_debug
149
150 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
151     GST_PAD_SINK,
152     GST_PAD_ALWAYS,
153     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
154         ("{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, "
155             "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, "
156 #ifdef GST_WLSINK_ENHANCEMENT
157             "SN12, ST12, "
158 #endif
159             "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"))
160     );
161
162 static void gst_wayland_sink_get_property (GObject * object,
163     guint prop_id, GValue * value, GParamSpec * pspec);
164 static void gst_wayland_sink_set_property (GObject * object,
165     guint prop_id, const GValue * value, GParamSpec * pspec);
166 static void gst_wayland_sink_finalize (GObject * object);
167
168 static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
169     GstStateChange transition);
170 static void gst_wayland_sink_set_context (GstElement * element,
171     GstContext * context);
172
173 static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
174     GstCaps * filter);
175 static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
176 static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink,
177     GstBuffer * buffer);
178 static gboolean
179 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query);
180 static gboolean gst_wayland_sink_render (GstBaseSink * bsink,
181     GstBuffer * buffer);
182
183 /* VideoOverlay interface */
184 static void gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface *
185     iface);
186 static void gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay,
187     guintptr handle);
188 static void gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
189     gint x, gint y, gint w, gint h);
190 static void gst_wayland_sink_expose (GstVideoOverlay * overlay);
191
192 /* WaylandVideo interface */
193 static void gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface *
194     iface);
195 static void gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video);
196 static void gst_wayland_sink_end_geometry_change (GstWaylandVideo * video);
197 #ifdef GST_WLSINK_ENHANCEMENT
198 static void gst_wayland_sink_update_window_geometry (GstWaylandSink * sink);
199 #endif
200
201 #define gst_wayland_sink_parent_class parent_class
202 G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
203     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
204         gst_wayland_sink_videooverlay_init)
205     G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO,
206         gst_wayland_sink_waylandvideo_init));
207
208 static void
209 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
210 {
211   FUNCTION_ENTER ();
212   GObjectClass *gobject_class;
213   GstElementClass *gstelement_class;
214   GstBaseSinkClass *gstbasesink_class;
215
216   gobject_class = (GObjectClass *) klass;
217   gstelement_class = (GstElementClass *) klass;
218   gstbasesink_class = (GstBaseSinkClass *) klass;
219
220   gobject_class->set_property = gst_wayland_sink_set_property;
221   gobject_class->get_property = gst_wayland_sink_get_property;
222   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize);
223
224   gst_element_class_add_pad_template (gstelement_class,
225       gst_static_pad_template_get (&sink_template));
226
227   gst_element_class_set_static_metadata (gstelement_class,
228       "wayland video sink", "Sink/Video",
229       "Output to wayland surface",
230       "Sreerenj Balachandran <sreerenj.balachandran@intel.com>, "
231       "George Kiagiadakis <george.kiagiadakis@collabora.com>");
232
233   gstelement_class->change_state =
234       GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
235   gstelement_class->set_context =
236       GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
237
238   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
239   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
240   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
241   gstbasesink_class->propose_allocation =
242       GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
243   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render);
244
245   g_object_class_install_property (gobject_class, PROP_DISPLAY,
246       g_param_spec_string ("display", "Wayland Display name", "Wayland "
247           "display name to connect to, if not supplied via the GstContext",
248           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
249 #ifdef GST_WLSINK_ENHANCEMENT
250   g_object_class_install_property (gobject_class, PROP_ROTATE_ANGLE,
251       g_param_spec_enum ("rotate", "Rotate angle",
252           "Rotate angle of display output",
253           GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
254           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
256   g_object_class_install_property (gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
257       g_param_spec_enum ("display-geometry-method", "Display geometry method",
258           "Geometrical method for display",
259           GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD,
260           DEF_DISPLAY_GEOMETRY_METHOD,
261           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
262
263   g_object_class_install_property (gobject_class, PROP_ORIENTATION,
264       g_param_spec_enum ("orientation",
265           "Orientation information used for ROI/ZOOM",
266           "Orientation information for display",
267           GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
268           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
269
270   g_object_class_install_property (gobject_class, PROP_FLIP,
271       g_param_spec_enum ("flip", "Display flip",
272           "Flip for display",
273           GST_TYPE_WAYLANDSINK_FLIP, DEF_DISPLAY_FLIP,
274           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
275
276 #endif
277 }
278
279 static void
280 gst_wayland_sink_init (GstWaylandSink * sink)
281 {
282   FUNCTION_ENTER ();
283
284   sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
285   sink->flip = DEF_DISPLAY_FLIP;
286   sink->rotate_angle = DEGREE_0;
287   sink->orientation = DEGREE_0;
288
289   g_mutex_init (&sink->display_lock);
290   g_mutex_init (&sink->render_lock);
291 }
292
293 static void
294 gst_wayland_sink_get_property (GObject * object,
295     guint prop_id, GValue * value, GParamSpec * pspec)
296 {
297   FUNCTION_ENTER ();
298
299   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
300   switch (prop_id) {
301     case PROP_DISPLAY:
302       GST_OBJECT_LOCK (sink);
303       g_value_set_string (value, sink->display_name);
304       GST_OBJECT_UNLOCK (sink);
305       break;
306 #ifdef GST_WLSINK_ENHANCEMENT
307     case PROP_ROTATE_ANGLE:
308       g_value_set_enum (value, sink->rotate_angle);
309       break;
310     case PROP_DISPLAY_GEOMETRY_METHOD:
311       g_value_set_enum (value, sink->display_geometry_method);
312       break;
313     case PROP_ORIENTATION:
314       g_value_set_enum (value, sink->orientation);
315       break;
316     case PROP_FLIP:
317       g_value_set_enum (value, sink->flip);
318       break;
319 #endif
320     default:
321       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
322       break;
323   }
324 }
325
326 static void
327 gst_wayland_sink_set_property (GObject * object,
328     guint prop_id, const GValue * value, GParamSpec * pspec)
329 {
330   FUNCTION_ENTER ();
331
332   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
333   switch (prop_id) {
334     case PROP_DISPLAY:
335       GST_OBJECT_LOCK (sink);
336       sink->display_name = g_value_dup_string (value);
337       GST_OBJECT_UNLOCK (sink);
338       break;
339 #ifdef GST_WLSINK_ENHANCEMENT
340     case PROP_ROTATE_ANGLE:
341       sink->rotate_angle = g_value_get_enum (value);
342       GST_INFO_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
343       if (sink->window) {
344         gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
345       }
346       sink->video_info_changed = TRUE;
347       if (GST_STATE (sink) == GST_STATE_PAUSED) {
348         /*need to render current buffer */
349       }
350       break;
351     case PROP_DISPLAY_GEOMETRY_METHOD:
352       sink->display_geometry_method = g_value_get_enum (value);
353       GST_INFO_OBJECT (sink, "Display geometry method is set (%d)",
354           sink->display_geometry_method);
355       if (sink->window) {
356         gst_wl_window_set_disp_geo_method (sink->window,
357             sink->display_geometry_method);
358       }
359       sink->video_info_changed = TRUE;
360       if (GST_STATE (sink) == GST_STATE_PAUSED) {
361         /*need to render current buffer */
362       }
363       break;
364     case PROP_ORIENTATION:
365       sink->orientation = g_value_get_enum (value);
366       GST_INFO_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
367       if (sink->window) {
368         gst_wl_window_set_orientation (sink->window, sink->orientation);
369       }
370       sink->video_info_changed = TRUE;
371       if (GST_STATE (sink) == GST_STATE_PAUSED) {
372         /*need to render current buffer */
373       }
374       break;
375     case PROP_FLIP:
376       sink->flip = g_value_get_enum (value);
377       GST_INFO_OBJECT (sink, "flip is set (%d)", sink->flip);
378       if (sink->flip) {
379         gst_wl_window_set_flip (sink->window, sink->flip);
380       }
381       sink->video_info_changed = TRUE;
382       if (GST_STATE (sink) == GST_STATE_PAUSED) {
383         /*need to render current buffer */
384       }
385       break;
386 #endif
387     default:
388       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
389       break;
390   }
391 }
392
393 static void
394 gst_wayland_sink_finalize (GObject * object)
395 {
396   FUNCTION_ENTER ();
397
398   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
399   GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
400
401   if (sink->last_buffer)
402     gst_buffer_unref (sink->last_buffer);
403   if (sink->display) {
404     /* see comment about this call in gst_wayland_sink_change_state() */
405 #ifdef GST_WLSINK_ENHANCEMENT
406     if (sink->pool && !sink->display->is_native_format)
407 #else
408     if (sink->pool)
409 #endif
410       gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
411           (sink->pool));
412
413     g_object_unref (sink->display);
414     sink->display = NULL;
415   }
416   if (sink->window) {
417     g_object_unref (sink->window);
418     sink->window = NULL;
419   }
420   if (sink->pool) {
421     gst_object_unref (sink->pool);
422     sink->pool = NULL;
423   }
424
425   if (sink->display_name) {
426     g_free (sink->display_name);
427     sink->display_name = NULL;
428   }
429
430   g_mutex_clear (&sink->display_lock);
431   g_mutex_clear (&sink->render_lock);
432
433   G_OBJECT_CLASS (parent_class)->finalize (object);
434 }
435
436 /* must be called with the display_lock */
437 static void
438 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
439     GstContext * context)
440 {
441   FUNCTION_ENTER ();
442
443   struct wl_display *display;
444   GError *error = NULL;
445
446   display = gst_wayland_display_handle_context_get_handle (context);
447   sink->display = gst_wl_display_new_existing (display, FALSE, &error);
448
449   if (error) {
450     GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
451         ("Could not set display handle"),
452         ("Failed to use the external wayland display: '%s'", error->message));
453     g_error_free (error);
454   }
455 }
456
457 static gboolean
458 gst_wayland_sink_find_display (GstWaylandSink * sink)
459 {
460   FUNCTION_ENTER ();
461
462   GstQuery *query;
463   GstMessage *msg;
464   GstContext *context = NULL;
465   GError *error = NULL;
466   gboolean ret = TRUE;
467
468   g_mutex_lock (&sink->display_lock);
469
470   if (!sink->display) {
471     /* first query upstream for the needed display handle */
472     query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
473     if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
474       gst_query_parse_context (query, &context);
475       gst_wayland_sink_set_display_from_context (sink, context);
476     }
477     gst_query_unref (query);
478
479     if (G_LIKELY (!sink->display)) {
480       /* now ask the application to set the display handle */
481       msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
482           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
483
484       g_mutex_unlock (&sink->display_lock);
485       gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
486       /* at this point we expect gst_wayland_sink_set_context
487        * to get called and fill sink->display */
488       g_mutex_lock (&sink->display_lock);
489
490       if (!sink->display) {
491         /* if the application didn't set a display, let's create it ourselves */
492         GST_OBJECT_LOCK (sink);
493         sink->display = gst_wl_display_new (sink->display_name, &error);
494         GST_OBJECT_UNLOCK (sink);
495
496         if (error) {
497           GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
498               ("Could not initialise Wayland output"),
499               ("Failed to create GstWlDisplay: '%s'", error->message));
500           g_error_free (error);
501           ret = FALSE;
502         } else {
503           /* inform the world about the new display */
504           context =
505               gst_wayland_display_handle_context_new (sink->display->display);
506           msg = gst_message_new_have_context (GST_OBJECT_CAST (sink), context);
507           gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
508         }
509       }
510     }
511   }
512
513   g_mutex_unlock (&sink->display_lock);
514
515   return ret;
516 }
517
518 static GstStateChangeReturn
519 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
520 {
521   FUNCTION_ENTER ();
522
523   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
524   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
525
526   switch (transition) {
527     case GST_STATE_CHANGE_NULL_TO_READY:
528       if (!gst_wayland_sink_find_display (sink))
529         return GST_STATE_CHANGE_FAILURE;
530       break;
531     default:
532       break;
533   }
534
535   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
536   if (ret == GST_STATE_CHANGE_FAILURE)
537     return ret;
538
539   switch (transition) {
540     case GST_STATE_CHANGE_PAUSED_TO_READY:
541       gst_buffer_replace (&sink->last_buffer, NULL);
542       if (sink->window) {
543         if (gst_wl_window_is_toplevel (sink->window)) {
544           g_clear_object (&sink->window);
545         } else {
546           /* remove buffer from surface, show nothing */
547           wl_surface_attach (sink->window->surface, NULL, 0, 0);
548           wl_surface_damage (sink->window->surface, 0, 0,
549               sink->window->surface_width, sink->window->surface_height);
550           wl_surface_commit (sink->window->surface);
551           wl_display_flush (sink->display->display);
552         }
553       }
554       break;
555     case GST_STATE_CHANGE_READY_TO_NULL:
556       g_mutex_lock (&sink->display_lock);
557       /* If we had a toplevel window, we most likely have our own connection
558        * to the display too, and it is a good idea to disconnect and allow
559        * potentially the application to embed us with GstVideoOverlay
560        * (which requires to re-use the same display connection as the parent
561        * surface). If we didn't have a toplevel window, then the display
562        * connection that we have is definitely shared with the application
563        * and it's better to keep it around (together with the window handle)
564        * to avoid requesting them again from the application if/when we are
565        * restarted (GstVideoOverlay behaves like that in other sinks)
566        */
567       if (sink->display && !sink->window) {     /* -> the window was toplevel */
568         /* Force all buffers to return to the pool, regardless of
569          * whether the compositor has released them or not. We are
570          * going to kill the display, so we need to return all buffers
571          * to be destroyed before this happens.
572          * Note that this is done here instead of the pool destructor
573          * because the buffers hold a reference to the pool. Also,
574          * the buffers can only be unref'ed from the display's event loop
575          * and the pool holds a reference to the display. If we drop
576          * our references here, when the compositor releases the buffers,
577          * they will be unref'ed from the event loop thread, which will
578          * unref the pool and therefore the display, which will try to
579          * stop the thread from within itself and cause a deadlock.
580          */
581         if (sink->pool) {
582           gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
583               (sink->pool));
584         }
585         g_clear_object (&sink->display);
586         g_clear_object (&sink->pool);
587       }
588       g_mutex_unlock (&sink->display_lock);
589       break;
590     default:
591       break;
592   }
593
594   return ret;
595 }
596
597 static void
598 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
599 {
600   FUNCTION_ENTER ();
601
602   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
603   if (gst_context_has_context_type (context,
604           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
605     g_mutex_lock (&sink->display_lock);
606     if (G_LIKELY (!sink->display)) {
607       gst_wayland_sink_set_display_from_context (sink, context);
608     } else {
609       GST_WARNING_OBJECT (element, "changing display handle is not supported");
610       g_mutex_unlock (&sink->display_lock);
611       return;
612     }
613     g_mutex_unlock (&sink->display_lock);
614   }
615
616   GST_INFO ("element %p context %p", element, context);
617   if (GST_ELEMENT_CLASS (parent_class)->set_context)
618     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
619 }
620
621 static GstCaps *
622 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
623 {
624   FUNCTION_ENTER ();
625
626   GstWaylandSink *sink;
627   GstCaps *caps;
628   sink = GST_WAYLAND_SINK (bsink);
629
630   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
631
632   g_mutex_lock (&sink->display_lock);
633
634   if (sink->display) {
635     GValue list = G_VALUE_INIT;
636     GValue value = G_VALUE_INIT;
637     GArray *formats;
638     gint i;
639 #ifdef GST_WLSINK_ENHANCEMENT
640     enum tizen_buffer_pool_format fmt;
641 #else
642     enum wl_shm_format fmt;
643 #endif
644
645     g_value_init (&list, GST_TYPE_LIST);
646     g_value_init (&value, G_TYPE_STRING);
647
648     formats = sink->display->formats;
649     for (i = 0; i < formats->len; i++) {
650       fmt = g_array_index (formats, uint32_t, i);
651       g_value_set_string (&value, gst_wayland_format_to_string (fmt));
652       gst_value_list_append_value (&list, &value);
653     }
654
655     caps = gst_caps_make_writable (caps);
656     gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
657
658     GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
659   }
660
661   g_mutex_unlock (&sink->display_lock);
662
663   if (filter) {
664     GstCaps *intersection;
665
666     intersection =
667         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
668     gst_caps_unref (caps);
669     caps = intersection;
670   }
671
672   return caps;
673 }
674
675 static gboolean
676 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
677 {
678   FUNCTION_ENTER ();
679
680   GstWaylandSink *sink;
681   GstBufferPool *newpool;
682   GstVideoInfo info;
683 #ifdef GST_WLSINK_ENHANCEMENT
684   enum tizen_buffer_pool_format format;
685 #else
686   enum wl_shm_format format;
687 #endif
688   GArray *formats;
689   gint i;
690   GstStructure *structure;
691   static GstAllocationParams params = { 0, 0, 0, 15, };
692   sink = GST_WAYLAND_SINK (bsink);
693
694   GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
695
696   /* extract info from caps */
697   if (!gst_video_info_from_caps (&info, caps))
698     goto invalid_format;
699 #ifdef GST_WLSINK_ENHANCEMENT
700   sink->caps = gst_caps_copy (caps);
701 #endif
702
703   format = gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT (&info));
704   if ((gint) format == -1)
705     goto invalid_format;
706
707   /* verify we support the requested format */
708   formats = sink->display->formats;
709   for (i = 0; i < formats->len; i++) {
710     if (g_array_index (formats, uint32_t, i) == format)
711       break;
712   }
713
714   if (i >= formats->len)
715     goto unsupported_format;
716
717 #ifdef GST_WLSINK_ENHANCEMENT
718   if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
719       GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
720     sink->display->is_native_format = TRUE;
721   } else {
722     sink->display->is_native_format = FALSE;
723
724     /* create a new pool for the new configuration */
725     newpool = gst_wayland_buffer_pool_new (sink->display);
726     if (!newpool)
727       goto pool_failed;
728
729     structure = gst_buffer_pool_get_config (newpool);
730     gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
731     gst_buffer_pool_config_set_allocator (structure, NULL, &params);
732     if (!gst_buffer_pool_set_config (newpool, structure))
733       goto config_failed;
734
735     gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
736     gst_object_unref (newpool);
737
738   }
739   /* store the video info */
740   sink->video_info = info;
741   sink->video_info_changed = TRUE;
742 #else
743   /* create a new pool for the new configuration */
744   newpool = gst_wayland_buffer_pool_new (sink->display);
745   if (!newpool)
746     goto pool_failed;
747
748   structure = gst_buffer_pool_get_config (newpool);
749   gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
750   gst_buffer_pool_config_set_allocator (structure, NULL, &params);
751   if (!gst_buffer_pool_set_config (newpool, structure))
752     goto config_failed;
753
754   /* store the video info */
755   sink->video_info = info;
756   sink->video_info_changed = TRUE;
757
758   gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
759   gst_object_unref (newpool);
760 #endif
761   return TRUE;
762
763 invalid_format:
764   {
765     GST_DEBUG_OBJECT (sink,
766         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
767     return FALSE;
768   }
769 unsupported_format:
770   {
771     GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
772         gst_wayland_format_to_string (format));
773     return FALSE;
774   }
775 pool_failed:
776   {
777     GST_DEBUG_OBJECT (sink, "Failed to create new pool");
778     return FALSE;
779   }
780 config_failed:
781   {
782     GST_DEBUG_OBJECT (bsink, "failed setting config");
783     gst_object_unref (newpool);
784     return FALSE;
785   }
786 }
787
788 static gboolean
789 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
790 {
791   FUNCTION_ENTER ();
792
793   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
794   GstBufferPool *pool = NULL;
795   GstStructure *config;
796   GstCaps *caps;
797   guint size;
798   gboolean need_pool;
799
800   if (sink->display->is_native_format == TRUE)
801     return TRUE;
802
803   gst_query_parse_allocation (query, &caps, &need_pool);
804
805   if (caps == NULL)
806     goto no_caps;
807
808   if (sink->pool)
809     pool = gst_object_ref (sink->pool);
810
811   if (pool != NULL) {
812     GstCaps *pcaps;
813
814     /* we had a pool, check caps */
815     config = gst_buffer_pool_get_config (pool);
816     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
817
818     if (!gst_caps_is_equal (caps, pcaps)) {
819       /* different caps, we can't use this pool */
820       gst_object_unref (pool);
821       pool = NULL;
822     }
823     gst_structure_free (config);
824   }
825
826   if (pool == NULL && need_pool) {
827     GstVideoInfo info;
828
829     if (!gst_video_info_from_caps (&info, caps))
830       goto invalid_caps;
831
832     GST_DEBUG_OBJECT (sink, "create new pool");
833     pool = gst_wayland_buffer_pool_new (sink->display);
834
835     /* the normal size of a frame */
836     size = info.size;
837
838     config = gst_buffer_pool_get_config (pool);
839     gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
840     if (!gst_buffer_pool_set_config (pool, config))
841       goto config_failed;
842   }
843   if (pool) {
844     gst_query_add_allocation_pool (query, pool, size, 2, 0);
845     gst_object_unref (pool);
846   }
847
848   return TRUE;
849
850   /* ERRORS */
851 no_caps:
852   {
853     GST_DEBUG_OBJECT (bsink, "no caps specified");
854     return FALSE;
855   }
856 invalid_caps:
857   {
858     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
859     return FALSE;
860   }
861 config_failed:
862   {
863     GST_DEBUG_OBJECT (bsink, "failed setting config");
864     gst_object_unref (pool);
865     return FALSE;
866   }
867 }
868
869 static GstFlowReturn
870 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
871 {
872   FUNCTION_ENTER ();
873
874   GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
875   return gst_wayland_sink_render (bsink, buffer);
876 }
877
878 static void
879 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
880 {
881   FUNCTION_ENTER ();
882
883   GstWaylandSink *sink = data;
884
885   GST_LOG ("frame_redraw_cb");
886
887   g_atomic_int_set (&sink->redraw_pending, FALSE);
888   wl_callback_destroy (callback);
889 }
890
891 static const struct wl_callback_listener frame_callback_listener = {
892   frame_redraw_callback
893 };
894
895 /* must be called with the render lock */
896 static void
897 render_last_buffer (GstWaylandSink * sink)
898 {
899   FUNCTION_ENTER ();
900
901   GstWlMeta *meta;
902   struct wl_surface *surface;
903   struct wl_callback *callback;
904
905   meta = gst_buffer_get_wl_meta (sink->last_buffer);
906   surface = gst_wl_window_get_wl_surface (sink->window);
907
908   g_atomic_int_set (&sink->redraw_pending, TRUE);
909   callback = wl_surface_frame (surface);
910   wl_callback_add_listener (callback, &frame_callback_listener, sink);
911
912   /* Here we essentially add a reference to the buffer. This represents
913    * the fact that the compositor is using the buffer and it should
914    * not return back to the pool and be reused until the compositor
915    * releases it. The release is handled internally in the pool */
916   gst_wayland_compositor_acquire_buffer (meta->pool, sink->last_buffer);
917
918   GST_DEBUG ("wl_surface_attach wl_buffer %p", meta->wbuffer);
919
920   wl_surface_attach (surface, meta->wbuffer, 0, 0);
921   wl_surface_damage (surface, 0, 0, sink->window->surface_width,
922       sink->window->surface_height);
923
924   wl_surface_commit (surface);
925   wl_display_flush (sink->display->display);
926 }
927
928 static GstFlowReturn
929 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
930 {
931   FUNCTION_ENTER ();
932
933   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
934   GstBuffer *to_render;
935   GstWlMeta *meta;
936   GstFlowReturn ret = GST_FLOW_OK;
937
938 #ifdef GST_WLSINK_ENHANCEMENT
939   GstBufferPool *newpool;
940   GstStructure *structure;
941   static GstAllocationParams params = { 0, 0, 0, 15, };
942 #endif
943
944   g_mutex_lock (&sink->render_lock);
945
946   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
947
948   if (G_UNLIKELY (!sink->window)) {
949     /* ask for window handle. Unlock render_lock while doing that because
950      * set_window_handle & friends will lock it in this context */
951     g_mutex_unlock (&sink->render_lock);
952     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
953     g_mutex_lock (&sink->render_lock);
954
955     if (sink->window) {
956       /* inform the window about our caps */
957       gst_wl_window_set_video_info (sink->window, &sink->video_info);
958     } else {
959       /* if we were not provided a window, create one ourselves */
960       sink->window =
961           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
962     }
963 #ifdef GST_WLSINK_ENHANCEMENT
964     gst_wayland_sink_update_window_geometry (sink);
965     sink->video_info_changed = TRUE;
966 #else
967     sink->video_info_changed = FALSE;
968 #endif
969   }
970
971   /* drop buffers until we get a frame callback */
972   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
973     goto done;
974
975   if (G_UNLIKELY (sink->video_info_changed)) {
976     gst_wl_window_set_video_info (sink->window, &sink->video_info);
977     sink->video_info_changed = FALSE;
978
979   }
980   GST_INFO ("window->render_rectangle(%d,%d %d x %d)",
981       sink->window->render_rectangle.x,
982       sink->window->render_rectangle.y,
983       sink->window->render_rectangle.w, sink->window->render_rectangle.h);
984   GST_INFO ("window->surface_width(%d),window->surface_height(%d)",
985       sink->window->surface_width, sink->window->surface_height);
986
987   /* now that we have for sure set the video info on the window, it must have
988    * a valid size, otherwise this means that the application has called
989    * set_window_handle() without calling set_render_rectangle(), which is
990    * absolutely necessary for us.
991    */
992   if (G_UNLIKELY (sink->window->surface_width == 0 ||
993           sink->window->surface_height == 0))
994     goto no_window_size;
995
996   meta = gst_buffer_get_wl_meta (buffer);
997
998   if (meta && meta->pool->display == sink->display) {
999     GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
1000     to_render = buffer;
1001   } else {
1002     GstMapInfo src;
1003     GST_LOG_OBJECT (sink, "buffer %p not from our pool, copying", buffer);
1004
1005 #ifdef GST_WLSINK_ENHANCEMENT
1006     if (sink->display->is_native_format == TRUE) {
1007       /*in case of SN12 or ST12 video  format */
1008       GstMemory *mem;
1009       GstMapInfo mem_info = GST_MAP_INFO_INIT;
1010       MMVideoBuffer *mm_video_buf = NULL;
1011
1012       mem = gst_buffer_peek_memory (buffer, 1);
1013       gst_memory_map (mem, &mem_info, GST_MAP_READ);
1014       mm_video_buf = (MMVideoBuffer *) mem_info.data;
1015       gst_memory_unmap (mem, &mem_info);
1016
1017       if (mm_video_buf == NULL) {
1018         GST_WARNING_OBJECT (sink, "mm_video_buf is NULL. Skip rendering");
1019         return ret;
1020       }
1021       /* assign mm_video_buf info */
1022       if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
1023         GST_DEBUG_OBJECT (sink, "TBM bo %p %p %p", mm_video_buf->handle.bo[0],
1024             mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
1025
1026         sink->display->native_video_size = 0;
1027
1028         for (int i = 0; i < NV_BUF_PLANE_NUM; i++) {
1029           if (mm_video_buf->handle.bo[i] != NULL) {
1030             sink->display->bo[i] = mm_video_buf->handle.bo[i];
1031           } else {
1032             sink->display->bo[i] = 0;
1033           }
1034           sink->display->plane_size[i] = mm_video_buf->size[i];
1035           sink->display->stride_width[i] = mm_video_buf->stride_width[i];
1036           sink->display->stride_height[i] = mm_video_buf->stride_height[i];
1037           sink->display->native_video_size += sink->display->plane_size[i];
1038         }
1039       } else {
1040         GST_ERROR_OBJECT (sink, "Buffer type is not TBM");
1041         return ret;
1042       }
1043
1044       if (!sink->pool) {
1045
1046         /* create a new pool for the new configuration */
1047         newpool = gst_wayland_buffer_pool_new (sink->display);
1048         if (!newpool) {
1049           GST_DEBUG_OBJECT (sink, "Failed to create new pool");
1050           return FALSE;
1051         }
1052         structure = gst_buffer_pool_get_config (newpool);
1053         gst_buffer_pool_config_set_params (structure, sink->caps,
1054             sink->video_info.size, 2, 0);
1055         gst_buffer_pool_config_set_allocator (structure, NULL, &params);
1056         if (!gst_buffer_pool_set_config (newpool, structure)) {
1057           GST_DEBUG_OBJECT (bsink, "failed setting config");
1058           gst_object_unref (newpool);
1059           return FALSE;
1060         }
1061
1062         gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
1063         gst_object_unref (newpool);
1064
1065       }
1066
1067       if (!gst_buffer_pool_set_active (sink->pool, TRUE))
1068         goto activate_failed;
1069
1070       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1071       if (ret != GST_FLOW_OK)
1072         goto no_buffer;
1073
1074
1075       /*add displaying buffer */
1076       GstWlMeta *meta;
1077       meta = gst_buffer_get_wl_meta (to_render);
1078       gst_wayland_buffer_pool_add_displaying_buffer (sink->pool, meta, buffer);
1079
1080     } else {
1081       /*in case of normal video format and pool is not our pool */
1082
1083       if (!sink->pool)
1084         goto no_pool;
1085
1086       if (!gst_buffer_pool_set_active (sink->pool, TRUE))
1087         goto activate_failed;
1088
1089       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1090       if (ret != GST_FLOW_OK)
1091         goto no_buffer;
1092
1093       gst_buffer_map (buffer, &src, GST_MAP_READ);
1094       gst_buffer_fill (to_render, 0, src.data, src.size);
1095       gst_buffer_unmap (buffer, &src);
1096     }
1097 #else
1098     if (!sink->pool)
1099       goto no_pool;
1100
1101     if (!gst_buffer_pool_set_active (sink->pool, TRUE))
1102       goto activate_failed;
1103
1104     ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
1105     if (ret != GST_FLOW_OK)
1106       goto no_buffer;
1107
1108     gst_buffer_map (buffer, &src, GST_MAP_READ);
1109     gst_buffer_fill (to_render, 0, src.data, src.size);
1110     gst_buffer_unmap (buffer, &src);
1111 #endif
1112   }
1113
1114   gst_buffer_replace (&sink->last_buffer, to_render);
1115   render_last_buffer (sink);
1116
1117   if (buffer != to_render) {
1118     GST_LOG_OBJECT (sink, "Decrease ref count of buffer");
1119     gst_buffer_unref (to_render);
1120   }
1121   goto done;
1122
1123 no_window_size:
1124   {
1125     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1126         ("Window has no size set"),
1127         ("Make sure you set the size after calling set_window_handle"));
1128     ret = GST_FLOW_ERROR;
1129     goto done;
1130   }
1131 no_buffer:
1132   {
1133     GST_WARNING_OBJECT (sink, "could not create image");
1134     goto done;
1135   }
1136 no_pool:
1137   {
1138     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
1139         ("Internal error: can't allocate images"),
1140         ("We don't have a bufferpool negotiated"));
1141     ret = GST_FLOW_ERROR;
1142     goto done;
1143   }
1144 activate_failed:
1145   {
1146     GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
1147     ret = GST_FLOW_ERROR;
1148     goto done;
1149   }
1150 done:
1151   {
1152     g_mutex_unlock (&sink->render_lock);
1153     return ret;
1154   }
1155 }
1156
1157 static void
1158 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
1159 {
1160   FUNCTION_ENTER ();
1161
1162   iface->set_window_handle = gst_wayland_sink_set_window_handle;
1163   iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
1164   iface->expose = gst_wayland_sink_expose;
1165 }
1166
1167 static void
1168 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1169 {
1170   FUNCTION_ENTER ();
1171
1172   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1173   struct wl_surface *surface = (struct wl_surface *) handle;
1174
1175   g_return_if_fail (sink != NULL);
1176
1177   if (sink->window != NULL) {
1178     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1179     return;
1180   }
1181
1182   g_mutex_lock (&sink->render_lock);
1183
1184   GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1185       (void *) handle);
1186
1187   g_clear_object (&sink->window);
1188
1189   if (handle) {
1190     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1191       /* we cannot use our own display with an external window handle */
1192       if (G_UNLIKELY (sink->display->own_display)) {
1193         GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
1194             ("Application did not provide a wayland display handle"),
1195             ("waylandsink cannot use an externally-supplied surface without "
1196                 "an externally-supplied display handle. Consider providing a "
1197                 "display handle from your application with GstContext"));
1198       } else {
1199         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1200         GST_DEBUG ("sink->window %p", sink->window);
1201       }
1202     } else {
1203       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1204           "ignoring window handle");
1205     }
1206   }
1207 #ifdef GST_WLSINK_ENHANCEMENT
1208   gst_wayland_sink_update_window_geometry (sink);
1209 #endif
1210
1211   g_mutex_unlock (&sink->render_lock);
1212 }
1213
1214 #ifdef GST_WLSINK_ENHANCEMENT
1215 static void
1216 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1217 {
1218   FUNCTION_ENTER ();
1219
1220   if (sink == NULL || sink->window == NULL)
1221     return;
1222
1223   gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1224   gst_wl_window_set_disp_geo_method (sink->window,
1225       sink->display_geometry_method);
1226   gst_wl_window_set_orientation (sink->window, sink->orientation);
1227   gst_wl_window_set_flip (sink->window, sink->flip);
1228 }
1229 #endif
1230 static void
1231 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1232     gint x, gint y, gint w, gint h)
1233 {
1234   FUNCTION_ENTER ();
1235
1236   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1237
1238   g_return_if_fail (sink != NULL);
1239
1240   g_mutex_lock (&sink->render_lock);
1241   if (!sink->window) {
1242     g_mutex_unlock (&sink->render_lock);
1243     GST_WARNING_OBJECT (sink,
1244         "set_render_rectangle called without window, ignoring");
1245     return;
1246   }
1247
1248   GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1249       x, y, w, h);
1250   gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1251
1252   g_mutex_unlock (&sink->render_lock);
1253 }
1254
1255 static void
1256 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1257 {
1258   FUNCTION_ENTER ();
1259
1260   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1261
1262   g_return_if_fail (sink != NULL);
1263
1264   GST_DEBUG_OBJECT (sink, "expose");
1265
1266   g_mutex_lock (&sink->render_lock);
1267   if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1268     GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1269     render_last_buffer (sink);
1270   }
1271   g_mutex_unlock (&sink->render_lock);
1272 }
1273
1274 static void
1275 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1276 {
1277   FUNCTION_ENTER ();
1278
1279   iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1280   iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1281 }
1282
1283 static void
1284 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1285 {
1286   FUNCTION_ENTER ();
1287
1288   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1289   g_return_if_fail (sink != NULL);
1290
1291   g_mutex_lock (&sink->render_lock);
1292   if (!sink->window || !sink->window->subsurface) {
1293     g_mutex_unlock (&sink->render_lock);
1294     GST_INFO_OBJECT (sink,
1295         "begin_geometry_change called without window, ignoring");
1296     return;
1297   }
1298
1299   wl_subsurface_set_sync (sink->window->subsurface);
1300   g_mutex_unlock (&sink->render_lock);
1301 }
1302
1303 static void
1304 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1305 {
1306   FUNCTION_ENTER ();
1307
1308   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1309   g_return_if_fail (sink != NULL);
1310
1311   g_mutex_lock (&sink->render_lock);
1312   if (!sink->window || !sink->window->subsurface) {
1313     g_mutex_unlock (&sink->render_lock);
1314     GST_INFO_OBJECT (sink,
1315         "end_geometry_change called without window, ignoring");
1316     return;
1317   }
1318
1319   wl_subsurface_set_desync (sink->window->subsurface);
1320   g_mutex_unlock (&sink->render_lock);
1321 }
1322
1323 static gboolean
1324 plugin_init (GstPlugin * plugin)
1325 {
1326   FUNCTION_ENTER ();
1327
1328   GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1329       " wayland video sink");
1330
1331   return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1332       GST_TYPE_WAYLAND_SINK);
1333 }
1334
1335 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1336     GST_VERSION_MINOR,
1337     waylandsink,
1338     "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1339     GST_PACKAGE_ORIGIN)