Merge remote-tracking branch 'remotes/origin/upstream/1.6' into tizen
[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 #include "wlbuffer.h"
51 #include "wlshmallocator.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 static void render_last_buffer (GstWaylandSink * sink);
200 static void gst_wayland_sink_render_last_buffer (GstWaylandSink * sink);
201
202 #endif
203
204 #define gst_wayland_sink_parent_class parent_class
205 G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
206     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
207         gst_wayland_sink_videooverlay_init)
208     G_IMPLEMENT_INTERFACE (GST_TYPE_WAYLAND_VIDEO,
209         gst_wayland_sink_waylandvideo_init));
210
211 static void
212 gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
213 {
214   FUNCTION_ENTER ();
215   GObjectClass *gobject_class;
216   GstElementClass *gstelement_class;
217   GstBaseSinkClass *gstbasesink_class;
218
219   gobject_class = (GObjectClass *) klass;
220   gstelement_class = (GstElementClass *) klass;
221   gstbasesink_class = (GstBaseSinkClass *) klass;
222
223   gobject_class->set_property = gst_wayland_sink_set_property;
224   gobject_class->get_property = gst_wayland_sink_get_property;
225   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_wayland_sink_finalize);
226
227   gst_element_class_add_pad_template (gstelement_class,
228       gst_static_pad_template_get (&sink_template));
229
230   gst_element_class_set_static_metadata (gstelement_class,
231       "wayland video sink", "Sink/Video",
232       "Output to wayland surface",
233       "Sreerenj Balachandran <sreerenj.balachandran@intel.com>, "
234       "George Kiagiadakis <george.kiagiadakis@collabora.com>");
235
236   gstelement_class->change_state =
237       GST_DEBUG_FUNCPTR (gst_wayland_sink_change_state);
238   gstelement_class->set_context =
239       GST_DEBUG_FUNCPTR (gst_wayland_sink_set_context);
240
241   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_get_caps);
242   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_wayland_sink_set_caps);
243   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_wayland_sink_preroll);
244   gstbasesink_class->propose_allocation =
245       GST_DEBUG_FUNCPTR (gst_wayland_sink_propose_allocation);
246   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_wayland_sink_render);
247
248   g_object_class_install_property (gobject_class, PROP_DISPLAY,
249       g_param_spec_string ("display", "Wayland Display name", "Wayland "
250           "display name to connect to, if not supplied via the GstContext",
251           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
252 #ifdef GST_WLSINK_ENHANCEMENT
253   g_object_class_install_property (gobject_class, PROP_ROTATE_ANGLE,
254       g_param_spec_enum ("rotate", "Rotate angle",
255           "Rotate angle of display output",
256           GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
257           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
258
259   g_object_class_install_property (gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
260       g_param_spec_enum ("display-geometry-method", "Display geometry method",
261           "Geometrical method for display",
262           GST_TYPE_WAYLANDSINK_DISPLAY_GEOMETRY_METHOD,
263           DEF_DISPLAY_GEOMETRY_METHOD,
264           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265
266   g_object_class_install_property (gobject_class, PROP_ORIENTATION,
267       g_param_spec_enum ("orientation",
268           "Orientation information used for ROI/ZOOM",
269           "Orientation information for display",
270           GST_TYPE_WAYLANDSINK_ROTATE_ANGLE, DEGREE_0,
271           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
272
273   g_object_class_install_property (gobject_class, PROP_FLIP,
274       g_param_spec_enum ("flip", "Display flip",
275           "Flip for display",
276           GST_TYPE_WAYLANDSINK_FLIP, DEF_DISPLAY_FLIP,
277           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278
279 #endif
280 }
281
282 static void
283 gst_wayland_sink_init (GstWaylandSink * sink)
284 {
285   FUNCTION_ENTER ();
286
287   sink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
288   sink->flip = DEF_DISPLAY_FLIP;
289   sink->rotate_angle = DEGREE_0;
290   sink->orientation = DEGREE_0;
291
292   g_mutex_init (&sink->display_lock);
293   g_mutex_init (&sink->render_lock);
294 }
295
296 static void
297 gst_wayland_sink_get_property (GObject * object,
298     guint prop_id, GValue * value, GParamSpec * pspec)
299 {
300   FUNCTION_ENTER ();
301
302   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
303   switch (prop_id) {
304     case PROP_DISPLAY:
305       GST_OBJECT_LOCK (sink);
306       g_value_set_string (value, sink->display_name);
307       GST_OBJECT_UNLOCK (sink);
308       break;
309 #ifdef GST_WLSINK_ENHANCEMENT
310     case PROP_ROTATE_ANGLE:
311       g_value_set_enum (value, sink->rotate_angle);
312       break;
313     case PROP_DISPLAY_GEOMETRY_METHOD:
314       g_value_set_enum (value, sink->display_geometry_method);
315       break;
316     case PROP_ORIENTATION:
317       g_value_set_enum (value, sink->orientation);
318       break;
319     case PROP_FLIP:
320       g_value_set_enum (value, sink->flip);
321       break;
322 #endif
323     default:
324       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
325       break;
326   }
327 }
328
329 static void
330 gst_wayland_sink_set_property (GObject * object,
331     guint prop_id, const GValue * value, GParamSpec * pspec)
332 {
333   FUNCTION_ENTER ();
334
335   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
336   switch (prop_id) {
337     case PROP_DISPLAY:
338       GST_OBJECT_LOCK (sink);
339       sink->display_name = g_value_dup_string (value);
340       GST_OBJECT_UNLOCK (sink);
341       break;
342 #ifdef GST_WLSINK_ENHANCEMENT
343     case PROP_ROTATE_ANGLE:
344       sink->rotate_angle = g_value_get_enum (value);
345       GST_INFO_OBJECT (sink, "Rotate angle is set (%d)", sink->rotate_angle);
346       if (sink->window) {
347         gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
348       }
349       sink->video_info_changed = TRUE;
350       if (GST_STATE (sink) == GST_STATE_PAUSED) {
351         /*need to render last buffer */
352         gst_wayland_sink_render_last_buffer (sink);
353       }
354       break;
355     case PROP_DISPLAY_GEOMETRY_METHOD:
356       sink->display_geometry_method = g_value_get_enum (value);
357       GST_INFO_OBJECT (sink, "Display geometry method is set (%d)",
358           sink->display_geometry_method);
359       if (sink->window) {
360         gst_wl_window_set_disp_geo_method (sink->window,
361             sink->display_geometry_method);
362       }
363       sink->video_info_changed = TRUE;
364       if (GST_STATE (sink) == GST_STATE_PAUSED) {
365         /*need to render last buffer */
366         gst_wayland_sink_render_last_buffer (sink);
367       }
368       break;
369     case PROP_ORIENTATION:
370       sink->orientation = g_value_get_enum (value);
371       GST_INFO_OBJECT (sink, "Orientation is set (%d)", sink->orientation);
372       if (sink->window) {
373         gst_wl_window_set_orientation (sink->window, sink->orientation);
374       }
375       sink->video_info_changed = TRUE;
376       if (GST_STATE (sink) == GST_STATE_PAUSED) {
377         /*need to render last buffer */
378         gst_wayland_sink_render_last_buffer (sink);
379       }
380       break;
381     case PROP_FLIP:
382       sink->flip = g_value_get_enum (value);
383       GST_INFO_OBJECT (sink, "flip is set (%d)", sink->flip);
384       if (sink->flip) {
385         gst_wl_window_set_flip (sink->window, sink->flip);
386       }
387       sink->video_info_changed = TRUE;
388       if (GST_STATE (sink) == GST_STATE_PAUSED) {
389         /*need to render last buffer */
390         gst_wayland_sink_render_last_buffer (sink);
391       }
392       break;
393 #endif
394     default:
395       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
396       break;
397   }
398 }
399
400 static void
401 gst_wayland_sink_finalize (GObject * object)
402 {
403   FUNCTION_ENTER ();
404
405   GstWaylandSink *sink = GST_WAYLAND_SINK (object);
406   GST_DEBUG_OBJECT (sink, "Finalizing the sink..");
407
408   if (sink->last_buffer)
409     gst_buffer_unref (sink->last_buffer);
410   if (sink->display)
411     g_object_unref (sink->display);
412   if (sink->window)
413     g_object_unref (sink->window);
414     sink->window = NULL;
415   }
416   if (sink->pool) {
417     gst_object_unref (sink->pool);
418     sink->pool = NULL;
419   }
420
421   if (sink->display_name) {
422     g_free (sink->display_name);
423     sink->display_name = NULL;
424   }
425
426   g_mutex_clear (&sink->display_lock);
427   g_mutex_clear (&sink->render_lock);
428
429   G_OBJECT_CLASS (parent_class)->finalize (object);
430 }
431
432 /* must be called with the display_lock */
433 static void
434 gst_wayland_sink_set_display_from_context (GstWaylandSink * sink,
435     GstContext * context)
436 {
437   FUNCTION_ENTER ();
438
439   struct wl_display *display;
440   GError *error = NULL;
441
442   display = gst_wayland_display_handle_context_get_handle (context);
443   sink->display = gst_wl_display_new_existing (display, FALSE, &error);
444
445   if (error) {
446     GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
447         ("Could not set display handle"),
448         ("Failed to use the external wayland display: '%s'", error->message));
449     g_error_free (error);
450   }
451 }
452
453 static gboolean
454 gst_wayland_sink_find_display (GstWaylandSink * sink)
455 {
456   FUNCTION_ENTER ();
457
458   GstQuery *query;
459   GstMessage *msg;
460   GstContext *context = NULL;
461   GError *error = NULL;
462   gboolean ret = TRUE;
463
464   g_mutex_lock (&sink->display_lock);
465
466   if (!sink->display) {
467     /* first query upstream for the needed display handle */
468     query = gst_query_new_context (GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
469     if (gst_pad_peer_query (GST_VIDEO_SINK_PAD (sink), query)) {
470       gst_query_parse_context (query, &context);
471       gst_wayland_sink_set_display_from_context (sink, context);
472     }
473     gst_query_unref (query);
474
475     if (G_LIKELY (!sink->display)) {
476       /* now ask the application to set the display handle */
477       msg = gst_message_new_need_context (GST_OBJECT_CAST (sink),
478           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
479
480       g_mutex_unlock (&sink->display_lock);
481       gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
482       /* at this point we expect gst_wayland_sink_set_context
483        * to get called and fill sink->display */
484       g_mutex_lock (&sink->display_lock);
485
486       if (!sink->display) {
487         /* if the application didn't set a display, let's create it ourselves */
488         GST_OBJECT_LOCK (sink);
489         sink->display = gst_wl_display_new (sink->display_name, &error);
490         GST_OBJECT_UNLOCK (sink);
491
492         if (error) {
493           GST_ELEMENT_WARNING (sink, RESOURCE, OPEN_READ_WRITE,
494               ("Could not initialise Wayland output"),
495               ("Failed to create GstWlDisplay: '%s'", error->message));
496           g_error_free (error);
497           ret = FALSE;
498         }
499       }
500     }
501   }
502
503   g_mutex_unlock (&sink->display_lock);
504
505   return ret;
506 }
507
508 static GstStateChangeReturn
509 gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
510 {
511   FUNCTION_ENTER ();
512
513   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
514   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
515
516   switch (transition) {
517     case GST_STATE_CHANGE_NULL_TO_READY:
518       if (!gst_wayland_sink_find_display (sink))
519         return GST_STATE_CHANGE_FAILURE;
520       break;
521     default:
522       break;
523   }
524
525   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
526   if (ret == GST_STATE_CHANGE_FAILURE)
527     return ret;
528
529   switch (transition) {
530     case GST_STATE_CHANGE_PAUSED_TO_READY:
531       gst_buffer_replace (&sink->last_buffer, NULL);
532       if (sink->window) {
533         if (gst_wl_window_is_toplevel (sink->window)) {
534           g_clear_object (&sink->window);
535         } else {
536           /* remove buffer from surface, show nothing */
537           gst_wl_window_render (sink->window, NULL, NULL);
538         }
539       }
540       break;
541     case GST_STATE_CHANGE_READY_TO_NULL:
542       g_mutex_lock (&sink->display_lock);
543       /* If we had a toplevel window, we most likely have our own connection
544        * to the display too, and it is a good idea to disconnect and allow
545        * potentially the application to embed us with GstVideoOverlay
546        * (which requires to re-use the same display connection as the parent
547        * surface). If we didn't have a toplevel window, then the display
548        * connection that we have is definitely shared with the application
549        * and it's better to keep it around (together with the window handle)
550        * to avoid requesting them again from the application if/when we are
551        * restarted (GstVideoOverlay behaves like that in other sinks)
552        */
553       if (sink->display && !sink->window) {     /* -> the window was toplevel */
554         g_clear_object (&sink->display);
555       }
556       g_mutex_unlock (&sink->display_lock);
557       g_clear_object (&sink->pool);
558       break;
559     default:
560       break;
561   }
562
563   return ret;
564 }
565
566 static void
567 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
568 {
569   FUNCTION_ENTER ();
570
571   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
572   if (gst_context_has_context_type (context,
573           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
574     g_mutex_lock (&sink->display_lock);
575     if (G_LIKELY (!sink->display)) {
576       gst_wayland_sink_set_display_from_context (sink, context);
577     } else {
578       GST_WARNING_OBJECT (element, "changing display handle is not supported");
579       g_mutex_unlock (&sink->display_lock);
580       return;
581     }
582     g_mutex_unlock (&sink->display_lock);
583   }
584
585   GST_INFO ("element %p context %p", element, context);
586   if (GST_ELEMENT_CLASS (parent_class)->set_context)
587     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
588 }
589
590 static GstCaps *
591 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
592 {
593   FUNCTION_ENTER ();
594
595   GstWaylandSink *sink;
596   GstCaps *caps;
597   sink = GST_WAYLAND_SINK (bsink);
598
599   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
600
601   g_mutex_lock (&sink->display_lock);
602
603   if (sink->display) {
604     GValue list = G_VALUE_INIT;
605     GValue value = G_VALUE_INIT;
606     GArray *formats;
607     gint i;
608 #ifdef GST_WLSINK_ENHANCEMENT
609     uint32_t fmt;
610 #else
611     enum wl_shm_format fmt;
612 #endif
613
614     g_value_init (&list, GST_TYPE_LIST);
615     g_value_init (&value, G_TYPE_STRING);
616
617     formats = sink->display->formats;
618     for (i = 0; i < formats->len; i++) {
619       fmt = g_array_index (formats, uint32_t, i);
620       g_value_set_string (&value, gst_wl_shm_format_to_string (fmt));
621       gst_value_list_append_value (&list, &value);
622 #ifdef GST_WLSINK_ENHANCEMENT
623       /* TBM doesn't support SN12. So we add SN12 manually as supported format.
624        * SN12 is exactly same with NV12.
625        */
626       if (fmt == TBM_FORMAT_NV12) {
627         g_value_set_string (&value,
628             gst_video_format_to_string (GST_VIDEO_FORMAT_SN12));
629         gst_value_list_append_value (&list, &value);
630       }
631 #endif
632     }
633
634     caps = gst_caps_make_writable (caps);
635     gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
636
637     GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
638   }
639
640   g_mutex_unlock (&sink->display_lock);
641
642   if (filter) {
643     GstCaps *intersection;
644
645     intersection =
646         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
647     gst_caps_unref (caps);
648     caps = intersection;
649   }
650
651   return caps;
652 }
653
654 static gboolean
655 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
656 {
657   FUNCTION_ENTER ();
658
659   GstWaylandSink *sink;
660   GstBufferPool *newpool;
661   GstVideoInfo info;
662 #ifdef GST_WLSINK_ENHANCEMENT
663   uint32_t format;
664 #else
665   enum wl_shm_format format;
666 #endif
667   GArray *formats;
668   gint i;
669   GstStructure *structure;
670
671   sink = GST_WAYLAND_SINK (bsink);
672
673   GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
674
675   /* extract info from caps */
676   if (!gst_video_info_from_caps (&info, caps))
677     goto invalid_format;
678 #ifdef GST_WLSINK_ENHANCEMENT
679   sink->caps = gst_caps_copy (caps);
680 #endif
681
682   format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&info));
683   if ((gint) format == -1)
684     goto invalid_format;
685
686   /* verify we support the requested format */
687   formats = sink->display->formats;
688   for (i = 0; i < formats->len; i++) {
689     if (g_array_index (formats, uint32_t, i) == format)
690       break;
691   }
692
693   if (i >= formats->len)
694     goto unsupported_format;
695
696 #ifdef GST_WLSINK_ENHANCEMENT
697   if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
698       GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
699     sink->display->is_native_format = TRUE;
700   } else {
701     sink->display->is_native_format = FALSE;
702
703     /* create a new pool for the new configuration */
704     newpool = gst_wayland_buffer_pool_new (sink->display);
705     if (!newpool)
706       goto pool_failed;
707
708     structure = gst_buffer_pool_get_config (newpool);
709     gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
710     gst_buffer_pool_config_set_allocator (structure, NULL, &params);
711     if (!gst_buffer_pool_set_config (newpool, structure))
712       goto config_failed;
713
714     gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
715     gst_object_unref (newpool);
716
717   }
718   /* store the video info */
719   sink->video_info = info;
720   sink->video_info_changed = TRUE;
721 #else
722   /* create a new pool for the new configuration */
723   newpool = gst_video_buffer_pool_new ();
724   if (!newpool)
725     goto pool_failed;
726
727   structure = gst_buffer_pool_get_config (newpool);
728   gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
729   gst_buffer_pool_config_set_allocator (structure, gst_wl_shm_allocator_get (),
730       NULL);
731   if (!gst_buffer_pool_set_config (newpool, structure))
732     goto config_failed;
733
734   /* store the video info */
735   sink->video_info = info;
736   sink->video_info_changed = TRUE;
737
738   gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
739   gst_object_unref (newpool);
740 #endif
741   return TRUE;
742
743 invalid_format:
744   {
745     GST_DEBUG_OBJECT (sink,
746         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
747     return FALSE;
748   }
749 unsupported_format:
750   {
751     GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
752         gst_wl_shm_format_to_string (format));
753     return FALSE;
754   }
755 pool_failed:
756   {
757     GST_DEBUG_OBJECT (sink, "Failed to create new pool");
758     return FALSE;
759   }
760 config_failed:
761   {
762     GST_DEBUG_OBJECT (bsink, "failed setting config");
763     gst_object_unref (newpool);
764     return FALSE;
765   }
766 }
767
768 static gboolean
769 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
770 {
771   FUNCTION_ENTER ();
772
773   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
774   GstStructure *config;
775   guint size, min_bufs, max_bufs;
776
777   config = gst_buffer_pool_get_config (sink->pool);
778   gst_buffer_pool_config_get_params (config, NULL, &size, &min_bufs, &max_bufs);
779
780   /* we do have a pool for sure (created in set_caps),
781    * so let's propose it anyway, but also propose the allocator on its own */
782   gst_query_add_allocation_pool (query, sink->pool, size, min_bufs, max_bufs);
783   gst_query_add_allocation_param (query, gst_wl_shm_allocator_get (), NULL);
784
785   gst_structure_free (config);
786
787   return TRUE;
788 }
789
790 static GstFlowReturn
791 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
792 {
793   FUNCTION_ENTER ();
794
795   GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
796   return gst_wayland_sink_render (bsink, buffer);
797 }
798
799 static void
800 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
801 {
802   FUNCTION_ENTER ();
803
804   GstWaylandSink *sink = data;
805
806   GST_LOG ("frame_redraw_cb");
807
808   g_atomic_int_set (&sink->redraw_pending, FALSE);
809   wl_callback_destroy (callback);
810 }
811
812 static const struct wl_callback_listener frame_callback_listener = {
813   frame_redraw_callback
814 };
815
816 /* must be called with the render lock */
817 static void
818 render_last_buffer (GstWaylandSink * sink)
819 {
820   GstWlBuffer *wlbuffer;
821   const GstVideoInfo *info = NULL;
822   struct wl_surface *surface;
823   struct wl_callback *callback;
824
825   wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
826   surface = gst_wl_window_get_wl_surface (sink->window);
827
828   g_atomic_int_set (&sink->redraw_pending, TRUE);
829   callback = wl_surface_frame (surface);
830   wl_callback_add_listener (callback, &frame_callback_listener, sink);
831
832   if (G_UNLIKELY (sink->video_info_changed)) {
833     info = &sink->video_info;
834     sink->video_info_changed = FALSE;
835   }
836   gst_wl_window_render (sink->window, wlbuffer, info);
837 }
838
839 static GstFlowReturn
840 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
841 {
842   FUNCTION_ENTER ();
843
844   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
845   GstBuffer *to_render;
846   GstWlBuffer *wlbuffer;
847   GstFlowReturn ret = GST_FLOW_OK;
848
849 #ifdef GST_WLSINK_ENHANCEMENT
850   GstBufferPool *newpool;
851   GstStructure *structure;
852   static GstAllocationParams params = { 0, 0, 0, 15, };
853 #endif
854
855   g_mutex_lock (&sink->render_lock);
856
857   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
858
859   if (G_UNLIKELY (!sink->window)) {
860     /* ask for window handle. Unlock render_lock while doing that because
861      * set_window_handle & friends will lock it in this context */
862     g_mutex_unlock (&sink->render_lock);
863     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
864     g_mutex_lock (&sink->render_lock);
865
866     if (!sink->window) {
867       /* if we were not provided a window, create one ourselves */
868       sink->window =
869           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
870     }
871   }
872
873   /* drop buffers until we get a frame callback */
874   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
875     goto done;
876
877   /* make sure that the application has called set_render_rectangle() */
878   if (G_UNLIKELY (sink->window->render_rectangle.w == 0))
879     goto no_window_size;
880
881   wlbuffer = gst_buffer_get_wl_buffer (buffer);
882
883   if (G_LIKELY (wlbuffer && wlbuffer->display == sink->display)) {
884     GST_LOG_OBJECT (sink, "buffer %p has a wl_buffer from our display, "
885         "writing directly", buffer);
886     to_render = buffer;
887   } else {
888     GstMemory *mem;
889     struct wl_buffer *wbuf = NULL;
890
891     GST_LOG_OBJECT (sink, "buffer %p does not have a wl_buffer from our "
892         "display, creating it", buffer);
893
894     mem = gst_buffer_peek_memory (buffer, 0);
895
896     if (gst_is_wl_shm_memory (mem)) {
897       wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
898           &sink->video_info);
899     }
900
901     if (wbuf) {
902       gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
903       to_render = buffer;
904     } else {
905       GstMapInfo src;
906       /* we don't know how to create a wl_buffer directly from the provided
907        * memory, so we have to copy the data to a memory that we know how
908        * to handle... */
909
910       GST_LOG_OBJECT (sink, "buffer %p cannot have a wl_buffer, "
911           "copying to wl_shm memory", buffer);
912
913       /* sink->pool always exists (created in set_caps), but it may not
914        * be active if upstream is not using it */
915       if (!gst_buffer_pool_is_active (sink->pool) &&
916           !gst_buffer_pool_set_active (sink->pool, TRUE))
917         goto activate_failed;
918
919       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
920       if (ret != GST_FLOW_OK)
921         goto no_buffer;
922
923       /* the first time we acquire a buffer,
924        * we need to attach a wl_buffer on it */
925       wlbuffer = gst_buffer_get_wl_buffer (buffer);
926       if (G_UNLIKELY (!wlbuffer)) {
927         mem = gst_buffer_peek_memory (to_render, 0);
928         wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, sink->display,
929             &sink->video_info);
930         if (G_UNLIKELY (!wbuf))
931           goto no_wl_buffer;
932
933         gst_buffer_add_wl_buffer (buffer, wbuf, sink->display);
934       }
935
936       gst_buffer_map (buffer, &src, GST_MAP_READ);
937       gst_buffer_fill (to_render, 0, src.data, src.size);
938       gst_buffer_unmap (buffer, &src);
939     }
940   }
941
942   /* drop double rendering */
943   if (G_UNLIKELY (to_render == sink->last_buffer)) {
944     GST_LOG_OBJECT (sink, "Buffer already being rendered");
945     goto done;
946   }
947
948   gst_buffer_replace (&sink->last_buffer, to_render);
949   render_last_buffer (sink);
950
951   if (buffer != to_render) {
952     GST_LOG_OBJECT (sink, "Decrease ref count of buffer");
953     gst_buffer_unref (to_render);
954   }
955   goto done;
956
957 no_window_size:
958   {
959     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
960         ("Window has no size set"),
961         ("Make sure you set the size after calling set_window_handle"));
962     ret = GST_FLOW_ERROR;
963     goto done;
964   }
965 no_buffer:
966   {
967     GST_WARNING_OBJECT (sink, "could not create buffer");
968     goto done;
969   }
970 no_wl_buffer:
971   {
972     GST_ERROR_OBJECT (sink, "could not create wl_buffer out of wl_shm memory");
973     ret = GST_FLOW_ERROR;
974     goto done;
975   }
976 activate_failed:
977   {
978     GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
979     ret = GST_FLOW_ERROR;
980     goto done;
981   }
982 done:
983   {
984     g_mutex_unlock (&sink->render_lock);
985     return ret;
986   }
987 }
988
989 static void
990 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
991 {
992   FUNCTION_ENTER ();
993
994   iface->set_window_handle = gst_wayland_sink_set_window_handle;
995   iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
996   iface->expose = gst_wayland_sink_expose;
997 }
998
999 static void
1000 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
1001 {
1002   FUNCTION_ENTER ();
1003
1004   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1005   struct wl_surface *surface = (struct wl_surface *) handle;
1006
1007   g_return_if_fail (sink != NULL);
1008
1009   if (sink->window != NULL) {
1010     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
1011     return;
1012   }
1013
1014   g_mutex_lock (&sink->render_lock);
1015
1016   GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
1017       (void *) handle);
1018
1019   g_clear_object (&sink->window);
1020
1021   if (handle) {
1022     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1023       /* we cannot use our own display with an external window handle */
1024       if (G_UNLIKELY (sink->display->own_display)) {
1025         GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
1026             ("Application did not provide a wayland display handle"),
1027             ("waylandsink cannot use an externally-supplied surface without "
1028                 "an externally-supplied display handle. Consider providing a "
1029                 "display handle from your application with GstContext"));
1030       } else {
1031         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1032         GST_DEBUG ("sink->window %p", sink->window);
1033       }
1034     } else {
1035       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1036           "ignoring window handle");
1037     }
1038   }
1039 #ifdef GST_WLSINK_ENHANCEMENT
1040   gst_wayland_sink_update_window_geometry (sink);
1041 #endif
1042
1043   g_mutex_unlock (&sink->render_lock);
1044 }
1045
1046 #ifdef GST_WLSINK_ENHANCEMENT
1047 static void
1048 gst_wayland_sink_update_window_geometry (GstWaylandSink * sink)
1049 {
1050   FUNCTION_ENTER ();
1051   g_return_if_fail (sink != NULL);
1052   g_return_if_fail (sink->window != NULL);
1053
1054   gst_wl_window_set_rotate_angle (sink->window, sink->rotate_angle);
1055   gst_wl_window_set_disp_geo_method (sink->window,
1056       sink->display_geometry_method);
1057   gst_wl_window_set_orientation (sink->window, sink->orientation);
1058   gst_wl_window_set_flip (sink->window, sink->flip);
1059 }
1060
1061 static void
1062 gst_wayland_sink_render_last_buffer (GstWaylandSink * sink)
1063 {
1064   FUNCTION_ENTER ();
1065   g_return_if_fail (sink != NULL);
1066
1067   g_mutex_lock (&sink->render_lock);
1068   gst_wl_window_set_video_info (sink->window, &sink->video_info);
1069   sink->video_info_changed = FALSE;
1070   if (sink->last_buffer)
1071     render_last_buffer (sink);
1072   g_mutex_unlock (&sink->render_lock);
1073 }
1074 #endif
1075 static void
1076 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1077     gint x, gint y, gint w, gint h)
1078 {
1079   FUNCTION_ENTER ();
1080
1081   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1082
1083   g_return_if_fail (sink != NULL);
1084
1085   g_mutex_lock (&sink->render_lock);
1086   if (!sink->window) {
1087     g_mutex_unlock (&sink->render_lock);
1088     GST_WARNING_OBJECT (sink,
1089         "set_render_rectangle called without window, ignoring");
1090     return;
1091   }
1092
1093   GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1094       x, y, w, h);
1095   gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1096
1097   g_mutex_unlock (&sink->render_lock);
1098 }
1099
1100 static void
1101 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1102 {
1103   FUNCTION_ENTER ();
1104
1105   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1106
1107   g_return_if_fail (sink != NULL);
1108
1109   GST_DEBUG_OBJECT (sink, "expose");
1110
1111   g_mutex_lock (&sink->render_lock);
1112   if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1113     GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1114     render_last_buffer (sink);
1115   }
1116   g_mutex_unlock (&sink->render_lock);
1117 }
1118
1119 static void
1120 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1121 {
1122   FUNCTION_ENTER ();
1123
1124   iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1125   iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1126 }
1127
1128 static void
1129 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1130 {
1131   FUNCTION_ENTER ();
1132
1133   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1134   g_return_if_fail (sink != NULL);
1135
1136   g_mutex_lock (&sink->render_lock);
1137   if (!sink->window || !sink->window->area_subsurface) {
1138     g_mutex_unlock (&sink->render_lock);
1139     GST_INFO_OBJECT (sink,
1140         "begin_geometry_change called without window, ignoring");
1141     return;
1142   }
1143
1144   wl_subsurface_set_sync (sink->window->area_subsurface);
1145   g_mutex_unlock (&sink->render_lock);
1146 }
1147
1148 static void
1149 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1150 {
1151   FUNCTION_ENTER ();
1152
1153   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1154   g_return_if_fail (sink != NULL);
1155
1156   g_mutex_lock (&sink->render_lock);
1157   if (!sink->window || !sink->window->area_subsurface) {
1158     g_mutex_unlock (&sink->render_lock);
1159     GST_INFO_OBJECT (sink,
1160         "end_geometry_change called without window, ignoring");
1161     return;
1162   }
1163
1164   wl_subsurface_set_desync (sink->window->area_subsurface);
1165   g_mutex_unlock (&sink->render_lock);
1166 }
1167
1168 static gboolean
1169 plugin_init (GstPlugin * plugin)
1170 {
1171   FUNCTION_ENTER ();
1172
1173   GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1174       " wayland video sink");
1175
1176   gst_wl_shm_allocator_register ();
1177
1178   return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1179       GST_TYPE_WAYLAND_SINK);
1180 }
1181
1182 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1183     GST_VERSION_MINOR,
1184     waylandsink,
1185     "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1186     GST_PACKAGE_ORIGIN)