[Waylandsink] 1. Add exception code for resetting wl_surface and wl_display
[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 #include "wlvideoformat.h"
46 #include "waylandpool.h"
47
48 #include <gst/wayland/wayland.h>
49 #include <gst/video/videooverlay.h>
50
51 /* signals */
52 enum
53 {
54   SIGNAL_0,
55   LAST_SIGNAL
56 };
57
58 /* Properties */
59 enum
60 {
61   PROP_0,
62   PROP_DISPLAY
63 };
64
65 GST_DEBUG_CATEGORY (gstwayland_debug);
66 #define GST_CAT_DEFAULT gstwayland_debug
67
68 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
69     GST_PAD_SINK,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
72         ("{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, "
73             "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, "
74             "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"))
75     );
76
77 static void gst_wayland_sink_get_property (GObject * object,
78     guint prop_id, GValue * value, GParamSpec * pspec);
79 static void gst_wayland_sink_set_property (GObject * object,
80     guint prop_id, const GValue * value, GParamSpec * pspec);
81 static void gst_wayland_sink_finalize (GObject * object);
82
83 static GstStateChangeReturn gst_wayland_sink_change_state (GstElement * element,
84     GstStateChange transition);
85 static void gst_wayland_sink_set_context (GstElement * element,
86     GstContext * context);
87
88 static GstCaps *gst_wayland_sink_get_caps (GstBaseSink * bsink,
89     GstCaps * filter);
90 static gboolean gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
91 static gboolean gst_wayland_sink_preroll (GstBaseSink * bsink,
92     GstBuffer * buffer);
93 static gboolean
94 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query);
95 static gboolean gst_wayland_sink_render (GstBaseSink * bsink,
96     GstBuffer * buffer);
97
98 /* VideoOverlay interface */
99 static void gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface *
100     iface);
101 static void gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay,
102     guintptr handle);
103 static void gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
104     gint x, gint y, gint w, gint h);
105 static void gst_wayland_sink_expose (GstVideoOverlay * overlay);
106
107 /* WaylandVideo interface */
108 static void gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface *
109     iface);
110 static void gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video);
111 static void gst_wayland_sink_end_geometry_change (GstWaylandVideo * video);
112
113 #define gst_wayland_sink_parent_class parent_class
114 G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
115     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
116         gst_wayland_sink_videooverlay_init)
117     G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO,
118         gst_wayland_sink_waylandvideo_init));
119
120 static void
121 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
122 {
123   FUNCTION_ENTER ();
124   GObjectClass *gobject_class;
125   GstElementClass *gstelement_class;
126   GstBaseSinkClass *gstbasesink_class;
127
128   gobject_class = (GObjectClass *) klass;
129   gstelement_class = (GstElementClass *) klass;
130   gstbasesink_class = (GstBaseSinkClass *) klass;
131
132   gobject_class->set_property = gst_wayland_sink_set_property;
133   gobject_class->get_property = gst_wayland_sink_get_property;
134   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize);
135
136   gst_element_class_add_pad_template (gstelement_class,
137       gst_static_pad_template_get (&sink_template));
138
139   gst_element_class_set_static_metadata (gstelement_class,
140       "wayland video sink", "Sink/Video",
141       "Output to wayland surface",
142       "Sreerenj Balachandran <sreerenj.balachandran@intel.com>, "
143       "George Kiagiadakis <george.kiagiadakis@collabora.com>");
144
145   gstelement_class->change_state =
146       GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
147   gstelement_class->set_context =
148       GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
149
150   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
151   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
152   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
153   gstbasesink_class->propose_allocation =
154       GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
155   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render);
156
157   g_object_class_install_property (gobject_class, PROP_DISPLAY,
158       g_param_spec_string ("display", "Wayland Display name", "Wayland "
159           "display name to connect to, if not supplied via the GstContext",
160           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
161 }
162
163 static void
164 gst_wayland_sink_init (GstWaylandSink * sink)
165 {
166   FUNCTION_ENTER ();
167   g_mutex_init (&sink->display_lock);
168   g_mutex_init (&sink->render_lock);
169 }
170
171 static void
172 gst_wayland_sink_get_property (GObject * object,
173     guint prop_id, GValue * value, GParamSpec * pspec)
174 {
175   FUNCTION_ENTER ();
176
177   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
178   switch (prop_id) {
179     case PROP_DISPLAY:
180       GST_OBJECT_LOCK (sink);
181       g_value_set_string (value, sink->display_name);
182       GST_OBJECT_UNLOCK (sink);
183       break;
184     default:
185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186       break;
187   }
188 }
189
190 static void
191 gst_wayland_sink_set_property (GObject * object,
192     guint prop_id, const GValue * value, GParamSpec * pspec)
193 {
194   FUNCTION_ENTER ();
195
196   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
197   switch (prop_id) {
198     case PROP_DISPLAY:
199       GST_OBJECT_LOCK (sink);
200       sink->display_name = g_value_dup_string (value);
201       GST_OBJECT_UNLOCK (sink);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205       break;
206   }
207 }
208
209 static void
210 gst_wayland_sink_finalize (GObject * object)
211 {
212   FUNCTION_ENTER ();
213
214   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
215   GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
216
217   if (sink->last_buffer)
218     gst_buffer_unref (sink->last_buffer);
219   if (sink->display) {
220     /* see comment about this call in gst_wayland_sink_change_state() */
221     if (sink->pool) {
222       gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
223           (sink->pool));
224     }
225     g_object_unref (sink->display);
226   }
227   if (sink->window)
228     g_object_unref (sink->window);
229   if (sink->pool)
230     gst_object_unref (sink->pool);
231
232   if (sink->display_name)
233     g_free (sink->display_name);
234
235   g_mutex_clear (&sink->display_lock);
236   g_mutex_clear (&sink->render_lock);
237
238   G_OBJECT_CLASS (parent_class)->finalize (object);
239 }
240
241 /* must be called with the display_lock */
242 static void
243 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
244     GstContext * context)
245 {
246   FUNCTION_ENTER ();
247
248   struct wl_display *display;
249   GError *error = NULL;
250
251   display = gst_wayland_display_handle_context_get_handle (context);
252   sink->display = gst_wl_display_new_existing (display, FALSE, &error);
253
254   if (error) {
255     GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
256         ("Could not set display handle"),
257         ("Failed to use the external wayland display: '%s'", error->message));
258     g_error_free (error);
259   }
260 }
261
262 static gboolean
263 gst_wayland_sink_find_display (GstWaylandSink * sink)
264 {
265   FUNCTION_ENTER ();
266
267   GstQuery *query;
268   GstMessage *msg;
269   GstContext *context = NULL;
270   GError *error = NULL;
271   gboolean ret = TRUE;
272
273   g_mutex_lock (&sink->display_lock);
274
275   if (!sink->display) {
276     /* first query upstream for the needed display handle */
277     query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
278     if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
279       gst_query_parse_context (query, &context);
280       gst_wayland_sink_set_display_from_context (sink, context);
281     }
282     gst_query_unref (query);
283
284     if (G_LIKELY (!sink->display)) {
285       /* now ask the application to set the display handle */
286       msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
287           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
288
289       g_mutex_unlock (&sink->display_lock);
290       gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
291       /* at this point we expect gst_wayland_sink_set_context
292        * to get called and fill sink->display */
293       g_mutex_lock (&sink->display_lock);
294
295       if (!sink->display) {
296         /* if the application didn't set a display, let's create it ourselves */
297         GST_OBJECT_LOCK (sink);
298         sink->display = gst_wl_display_new (sink->display_name, &error);
299         GST_OBJECT_UNLOCK (sink);
300
301         if (error) {
302           GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
303               ("Could not initialise Wayland output"),
304               ("Failed to create GstWlDisplay: '%s'", error->message));
305           g_error_free (error);
306           ret = FALSE;
307         } else {
308           /* inform the world about the new display */
309           context =
310               gst_wayland_display_handle_context_new (sink->display->display);
311           msg = gst_message_new_have_context (GST_OBJECT_CAST (sink), context);
312           gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
313         }
314       }
315     }
316   }
317
318   g_mutex_unlock (&sink->display_lock);
319
320   return ret;
321 }
322
323 static GstStateChangeReturn
324 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
325 {
326   FUNCTION_ENTER ();
327
328   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
329   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
330
331   switch (transition) {
332     case GST_STATE_CHANGE_NULL_TO_READY:
333       if (!gst_wayland_sink_find_display (sink))
334         return GST_STATE_CHANGE_FAILURE;
335       break;
336     default:
337       break;
338   }
339
340   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
341   if (ret == GST_STATE_CHANGE_FAILURE)
342     return ret;
343
344   switch (transition) {
345     case GST_STATE_CHANGE_PAUSED_TO_READY:
346       gst_buffer_replace (&sink->last_buffer, NULL);
347       if (sink->window) {
348         if (gst_wl_window_is_toplevel (sink->window)) {
349           g_clear_object (&sink->window);
350         } else {
351           /* remove buffer from surface, show nothing */
352           wl_surface_attach (sink->window->surface, NULL, 0, 0);
353           wl_surface_damage (sink->window->surface, 0, 0,
354               sink->window->surface_width, sink->window->surface_height);
355           wl_surface_commit (sink->window->surface);
356           wl_display_flush (sink->display->display);
357         }
358       }
359       break;
360     case GST_STATE_CHANGE_READY_TO_NULL:
361       g_mutex_lock (&sink->display_lock);
362       /* If we had a toplevel window, we most likely have our own connection
363        * to the display too, and it is a good idea to disconnect and allow
364        * potentially the application to embed us with GstVideoOverlay
365        * (which requires to re-use the same display connection as the parent
366        * surface). If we didn't have a toplevel window, then the display
367        * connection that we have is definitely shared with the application
368        * and it's better to keep it around (together with the window handle)
369        * to avoid requesting them again from the application if/when we are
370        * restarted (GstVideoOverlay behaves like that in other sinks)
371        */
372       if (sink->display && !sink->window) {     /* -> the window was toplevel */
373         /* Force all buffers to return to the pool, regardless of
374          * whether the compositor has released them or not. We are
375          * going to kill the display, so we need to return all buffers
376          * to be destroyed before this happens.
377          * Note that this is done here instead of the pool destructor
378          * because the buffers hold a reference to the pool. Also,
379          * the buffers can only be unref'ed from the display's event loop
380          * and the pool holds a reference to the display. If we drop
381          * our references here, when the compositor releases the buffers,
382          * they will be unref'ed from the event loop thread, which will
383          * unref the pool and therefore the display, which will try to
384          * stop the thread from within itself and cause a deadlock.
385          */
386         if (sink->pool) {
387           gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
388               (sink->pool));
389         }
390         g_clear_object (&sink->display);
391         g_clear_object (&sink->pool);
392       }
393       g_mutex_unlock (&sink->display_lock);
394       break;
395     default:
396       break;
397   }
398
399   return ret;
400 }
401
402 static void
403 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
404 {
405   FUNCTION_ENTER ();
406
407   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
408   if (gst_context_has_context_type (context,
409           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
410     g_mutex_lock (&sink->display_lock);
411     if (G_LIKELY (!sink->display)) {
412       gst_wayland_sink_set_display_from_context (sink, context);
413     } else {
414       GST_WARNING_OBJECT (element, "changing display handle is not supported");
415       g_mutex_unlock (&sink->display_lock);
416       return;
417     }
418     g_mutex_unlock (&sink->display_lock);
419   }
420
421   GST_INFO ("element %p context %p", element, context);
422   if (GST_ELEMENT_CLASS (parent_class)->set_context)
423     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
424 }
425
426 static GstCaps *
427 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
428 {
429   FUNCTION_ENTER ();
430
431   GstWaylandSink *sink;
432   GstCaps *caps;
433   sink = GST_WAYLAND_SINK (bsink);
434
435   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
436
437   g_mutex_lock (&sink->display_lock);
438
439   if (sink->display) {
440     GValue list = G_VALUE_INIT;
441     GValue value = G_VALUE_INIT;
442     GArray *formats;
443     gint i;
444     enum wl_shm_format fmt;
445
446     g_value_init (&list, GST_TYPE_LIST);
447     g_value_init (&value, G_TYPE_STRING);
448
449     formats = sink->display->formats;
450     for (i = 0; i < formats->len; i++) {
451       fmt = g_array_index (formats, uint32_t, i);
452       g_value_set_string (&value, gst_wayland_format_to_string (fmt));
453       gst_value_list_append_value (&list, &value);
454     }
455
456     caps = gst_caps_make_writable (caps);
457     gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
458
459     GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
460   }
461
462   g_mutex_unlock (&sink->display_lock);
463
464   if (filter) {
465     GstCaps *intersection;
466
467     intersection =
468         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
469     gst_caps_unref (caps);
470     caps = intersection;
471   }
472
473   return caps;
474 }
475
476 static gboolean
477 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
478 {
479   FUNCTION_ENTER ();
480
481   GstWaylandSink *sink;
482   GstBufferPool *newpool;
483   GstVideoInfo info;
484   enum wl_shm_format format;
485   GArray *formats;
486   gint i;
487   GstStructure *structure;
488   static GstAllocationParams params = { 0, 0, 0, 15, };
489   sink = GST_WAYLAND_SINK (bsink);
490
491   GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
492
493   /* extract info from caps */
494   if (!gst_video_info_from_caps (&info, caps))
495     goto invalid_format;
496
497   format = gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT (&info));
498   if ((gint) format == -1)
499     goto invalid_format;
500
501   /* verify we support the requested format */
502   formats = sink->display->formats;
503   for (i = 0; i < formats->len; i++) {
504     if (g_array_index (formats, uint32_t, i) == format)
505       break;
506   }
507
508   if (i >= formats->len)
509     goto unsupported_format;
510
511   /* create a new pool for the new configuration */
512   newpool = gst_wayland_buffer_pool_new (sink->display);
513   if (!newpool)
514     goto pool_failed;
515
516   structure = gst_buffer_pool_get_config (newpool);
517   gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
518   gst_buffer_pool_config_set_allocator (structure, NULL, &params);
519   if (!gst_buffer_pool_set_config (newpool, structure))
520     goto config_failed;
521
522   /* store the video info */
523   sink->video_info = info;
524   sink->video_info_changed = TRUE;
525
526   gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
527   gst_object_unref (newpool);
528
529   return TRUE;
530
531 invalid_format:
532   {
533     GST_DEBUG_OBJECT (sink,
534         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
535     return FALSE;
536   }
537 unsupported_format:
538   {
539     GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
540         gst_wayland_format_to_string (format));
541     return FALSE;
542   }
543 pool_failed:
544   {
545     GST_DEBUG_OBJECT (sink, "Failed to create new pool");
546     return FALSE;
547   }
548 config_failed:
549   {
550     GST_DEBUG_OBJECT (bsink, "failed setting config");
551     gst_object_unref (newpool);
552     return FALSE;
553   }
554 }
555
556 static gboolean
557 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
558 {
559   FUNCTION_ENTER ();
560
561   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
562   GstBufferPool *pool = NULL;
563   GstStructure *config;
564   GstCaps *caps;
565   guint size;
566   gboolean need_pool;
567
568   gst_query_parse_allocation (query, &caps, &need_pool);
569
570   if (caps == NULL)
571     goto no_caps;
572
573   if (sink->pool)
574     pool = gst_object_ref (sink->pool);
575
576   if (pool != NULL) {
577     GstCaps *pcaps;
578
579     /* we had a pool, check caps */
580     config = gst_buffer_pool_get_config (pool);
581     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
582
583     if (!gst_caps_is_equal (caps, pcaps)) {
584       /* different caps, we can't use this pool */
585       gst_object_unref (pool);
586       pool = NULL;
587     }
588     gst_structure_free (config);
589   }
590
591   if (pool == NULL && need_pool) {
592     GstVideoInfo info;
593
594     if (!gst_video_info_from_caps (&info, caps))
595       goto invalid_caps;
596
597     GST_DEBUG_OBJECT (sink, "create new pool");
598     pool = gst_wayland_buffer_pool_new (sink->display);
599
600     /* the normal size of a frame */
601     size = info.size;
602
603     config = gst_buffer_pool_get_config (pool);
604     gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
605     if (!gst_buffer_pool_set_config (pool, config))
606       goto config_failed;
607   }
608   if (pool) {
609     gst_query_add_allocation_pool (query, pool, size, 2, 0);
610     gst_object_unref (pool);
611   }
612
613   return TRUE;
614
615   /* ERRORS */
616 no_caps:
617   {
618     GST_DEBUG_OBJECT (bsink, "no caps specified");
619     return FALSE;
620   }
621 invalid_caps:
622   {
623     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
624     return FALSE;
625   }
626 config_failed:
627   {
628     GST_DEBUG_OBJECT (bsink, "failed setting config");
629     gst_object_unref (pool);
630     return FALSE;
631   }
632 }
633
634 static GstFlowReturn
635 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
636 {
637   FUNCTION_ENTER ();
638
639   GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
640   return gst_wayland_sink_render (bsink, buffer);
641 }
642
643 static void
644 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
645 {
646   FUNCTION_ENTER ();
647
648   GstWaylandSink *sink = data;
649
650   GST_LOG ("frame_redraw_cb");
651
652   g_atomic_int_set (&sink->redraw_pending, FALSE);
653   wl_callback_destroy (callback);
654 }
655
656 static const struct wl_callback_listener frame_callback_listener = {
657   frame_redraw_callback
658 };
659
660 /* must be called with the render lock */
661 static void
662 render_last_buffer (GstWaylandSink * sink)
663 {
664   FUNCTION_ENTER ();
665
666   GstWlMeta *meta;
667   struct wl_surface *surface;
668   struct wl_callback *callback;
669
670   meta = gst_buffer_get_wl_meta (sink->last_buffer);
671   surface = gst_wl_window_get_wl_surface (sink->window);
672
673   g_atomic_int_set (&sink->redraw_pending, TRUE);
674   callback = wl_surface_frame (surface);
675   wl_callback_add_listener (callback, &frame_callback_listener, sink);
676
677   /* Here we essentially add a reference to the buffer. This represents
678    * the fact that the compositor is using the buffer and it should
679    * not return back to the pool and be reused until the compositor
680    * releases it. The release is handled internally in the pool */
681   gst_wayland_compositor_acquire_buffer (meta->pool, sink->last_buffer);
682
683   wl_surface_attach (surface, meta->wbuffer, 0, 0);
684   wl_surface_damage (surface, 0, 0, sink->window->surface_width,
685       sink->window->surface_height);
686
687   wl_surface_commit (surface);
688   wl_display_flush (sink->display->display);
689 }
690
691 static GstFlowReturn
692 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
693 {
694   FUNCTION_ENTER ();
695
696   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
697   GstBuffer *to_render;
698   GstWlMeta *meta;
699   GstFlowReturn ret = GST_FLOW_OK;
700
701   g_mutex_lock (&sink->render_lock);
702
703   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
704
705   if (G_UNLIKELY (!sink->window)) {
706     /* ask for window handle. Unlock render_lock while doing that because
707      * set_window_handle & friends will lock it in this context */
708     g_mutex_unlock (&sink->render_lock);
709     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
710     g_mutex_lock (&sink->render_lock);
711
712     if (sink->window) {
713       /* inform the window about our caps */
714       gst_wl_window_set_video_info (sink->window, &sink->video_info);
715     } else {
716       /* if we were not provided a window, create one ourselves */
717       sink->window =
718           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
719     }
720     sink->video_info_changed = FALSE;
721   }
722
723   /* drop buffers until we get a frame callback */
724   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
725     goto done;
726
727   if (G_UNLIKELY (sink->video_info_changed)) {
728     gst_wl_window_set_video_info (sink->window, &sink->video_info);
729     sink->video_info_changed = FALSE;
730
731   }
732   GST_INFO ("window->render_rectangle(%d,%d %d x %d)",
733       sink->window->render_rectangle.x,
734       sink->window->render_rectangle.y,
735       sink->window->render_rectangle.w, sink->window->render_rectangle.h);
736   GST_INFO ("window->surface_width(%d),window->surface_height(%d)",
737       sink->window->surface_width, sink->window->surface_height);
738
739   /* now that we have for sure set the video info on the window, it must have
740    * a valid size, otherwise this means that the application has called
741    * set_window_handle() without calling set_render_rectangle(), which is
742    * absolutely necessary for us.
743    */
744   if (G_UNLIKELY (sink->window->surface_width == 0 ||
745           sink->window->surface_height == 0))
746     goto no_window_size;
747
748   meta = gst_buffer_get_wl_meta (buffer);
749
750   if (meta && meta->pool->display == sink->display) {
751     GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
752     to_render = buffer;
753   } else {
754     GstMapInfo src;
755     GST_LOG_OBJECT (sink, "buffer %p not from our pool, copying", buffer);
756
757     if (!sink->pool)
758       goto no_pool;
759
760     if (!gst_buffer_pool_set_active (sink->pool, TRUE))
761       goto activate_failed;
762
763     ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
764     if (ret != GST_FLOW_OK)
765       goto no_buffer;
766
767     gst_buffer_map (buffer, &src, GST_MAP_READ);
768     gst_buffer_fill (to_render, 0, src.data, src.size);
769     gst_buffer_unmap (buffer, &src);
770   }
771
772   gst_buffer_replace (&sink->last_buffer, to_render);
773   render_last_buffer (sink);
774
775   if (buffer != to_render)
776     gst_buffer_unref (to_render);
777   goto done;
778
779 no_window_size:
780   {
781     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
782         ("Window has no size set"),
783         ("Make sure you set the size after calling set_window_handle"));
784     ret = GST_FLOW_ERROR;
785     goto done;
786   }
787 no_buffer:
788   {
789     GST_WARNING_OBJECT (sink, "could not create image");
790     goto done;
791   }
792 no_pool:
793   {
794     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
795         ("Internal error: can't allocate images"),
796         ("We don't have a bufferpool negotiated"));
797     ret = GST_FLOW_ERROR;
798     goto done;
799   }
800 activate_failed:
801   {
802     GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
803     ret = GST_FLOW_ERROR;
804     goto done;
805   }
806 done:
807   {
808     g_mutex_unlock (&sink->render_lock);
809     return ret;
810   }
811 }
812
813 static void
814 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
815 {
816   FUNCTION_ENTER ();
817
818   iface->set_window_handle = gst_wayland_sink_set_window_handle;
819   iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
820   iface->expose = gst_wayland_sink_expose;
821 }
822
823 static void
824 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
825 {
826   FUNCTION_ENTER ();
827
828   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
829   struct wl_surface *surface = (struct wl_surface *) handle;
830
831   g_return_if_fail (sink != NULL);
832
833   if (sink->window != NULL) {
834     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
835     return;
836   }
837
838   g_mutex_lock (&sink->render_lock);
839
840   GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
841       (void *) handle);
842
843   g_clear_object (&sink->window);
844
845   if (handle) {
846     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
847       /* we cannot use our own display with an external window handle */
848       if (G_UNLIKELY (sink->display->own_display)) {
849         GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
850             ("Application did not provide a wayland display handle"),
851             ("waylandsink cannot use an externally-supplied surface without "
852                 "an externally-supplied display handle. Consider providing a "
853                 "display handle from your application with GstContext"));
854       } else {
855         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
856         GST_ERROR ("sink->window %p", sink->window);
857       }
858     } else {
859       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
860           "ignoring window handle");
861     }
862   }
863
864   g_mutex_unlock (&sink->render_lock);
865 }
866
867 static void
868 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
869     gint x, gint y, gint w, gint h)
870 {
871   FUNCTION_ENTER ();
872
873   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
874
875   g_return_if_fail (sink != NULL);
876
877   g_mutex_lock (&sink->render_lock);
878   if (!sink->window) {
879     g_mutex_unlock (&sink->render_lock);
880     GST_WARNING_OBJECT (sink,
881         "set_render_rectangle called without window, ignoring");
882     return;
883   }
884
885   GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
886       x, y, w, h);
887   gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
888
889   g_mutex_unlock (&sink->render_lock);
890 }
891
892 static void
893 gst_wayland_sink_expose (GstVideoOverlay * overlay)
894 {
895   FUNCTION_ENTER ();
896
897   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
898
899   g_return_if_fail (sink != NULL);
900
901   GST_DEBUG_OBJECT (sink, "expose");
902
903   g_mutex_lock (&sink->render_lock);
904   if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
905     GST_DEBUG_OBJECT (sink, "redrawing last buffer");
906     render_last_buffer (sink);
907   }
908   g_mutex_unlock (&sink->render_lock);
909 }
910
911 static void
912 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
913 {
914   FUNCTION_ENTER ();
915
916   iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
917   iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
918 }
919
920 static void
921 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
922 {
923   FUNCTION_ENTER ();
924
925   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
926   g_return_if_fail (sink != NULL);
927
928   g_mutex_lock (&sink->render_lock);
929   if (!sink->window || !sink->window->subsurface) {
930     g_mutex_unlock (&sink->render_lock);
931     GST_INFO_OBJECT (sink,
932         "begin_geometry_change called without window, ignoring");
933     return;
934   }
935
936   wl_subsurface_set_sync (sink->window->subsurface);
937   g_mutex_unlock (&sink->render_lock);
938 }
939
940 static void
941 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
942 {
943   FUNCTION_ENTER ();
944
945   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
946   g_return_if_fail (sink != NULL);
947
948   g_mutex_lock (&sink->render_lock);
949   if (!sink->window || !sink->window->subsurface) {
950     g_mutex_unlock (&sink->render_lock);
951     GST_INFO_OBJECT (sink,
952         "end_geometry_change called without window, ignoring");
953     return;
954   }
955
956   wl_subsurface_set_desync (sink->window->subsurface);
957   g_mutex_unlock (&sink->render_lock);
958 }
959
960 static gboolean
961 plugin_init (GstPlugin * plugin)
962 {
963   FUNCTION_ENTER ();
964
965   GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
966       " wayland video sink");
967
968   return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
969       GST_TYPE_WAYLAND_SINK);
970 }
971
972 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
973     GST_VERSION_MINOR,
974     waylandsink,
975     "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
976     GST_PACKAGE_ORIGIN)