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