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