Waylandsink : 1. change video format name for SN12 and ST12
[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 #ifdef GST_WLSINK_ENHANCEMENT
399         if (sink->pool && !sink->display->is_native_format)
400 #else
401         if (sink->pool)
402 #endif
403           gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
404               (sink->pool));
405         g_clear_object (&sink->display);
406         g_clear_object (&sink->pool);
407       }
408       g_mutex_unlock (&sink->display_lock);
409       break;
410     default:
411       break;
412   }
413
414   return ret;
415 }
416
417 static void
418 gst_wayland_sink_set_context (GstElement * element, GstContext * context)
419 {
420   FUNCTION_ENTER ();
421
422   GstWaylandSink *sink = GST_WAYLAND_SINK (element);
423   if (gst_context_has_context_type (context,
424           GST_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE)) {
425     g_mutex_lock (&sink->display_lock);
426     if (G_LIKELY (!sink->display)) {
427       gst_wayland_sink_set_display_from_context (sink, context);
428     } else {
429       GST_WARNING_OBJECT (element, "changing display handle is not supported");
430       g_mutex_unlock (&sink->display_lock);
431       return;
432     }
433     g_mutex_unlock (&sink->display_lock);
434   }
435
436   GST_INFO ("element %p context %p", element, context);
437   if (GST_ELEMENT_CLASS (parent_class)->set_context)
438     GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
439 }
440
441 static GstCaps *
442 gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
443 {
444   FUNCTION_ENTER ();
445
446   GstWaylandSink *sink;
447   GstCaps *caps;
448   sink = GST_WAYLAND_SINK (bsink);
449
450   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
451
452   g_mutex_lock (&sink->display_lock);
453
454   if (sink->display) {
455     GValue list = G_VALUE_INIT;
456     GValue value = G_VALUE_INIT;
457     GArray *formats;
458     gint i;
459 #ifdef GST_WLSINK_ENHANCEMENT
460     enum tizen_buffer_pool_format fmt;
461 #else
462     enum wl_shm_format fmt;
463 #endif
464
465     g_value_init (&list, GST_TYPE_LIST);
466     g_value_init (&value, G_TYPE_STRING);
467
468     formats = sink->display->formats;
469     for (i = 0; i < formats->len; i++) {
470       fmt = g_array_index (formats, uint32_t, i);
471       g_value_set_string (&value, gst_wayland_format_to_string (fmt));
472       gst_value_list_append_value (&list, &value);
473     }
474
475     caps = gst_caps_make_writable (caps);
476     gst_structure_set_value (gst_caps_get_structure (caps, 0), "format", &list);
477
478     GST_DEBUG_OBJECT (sink, "display caps: %" GST_PTR_FORMAT, caps);
479   }
480
481   g_mutex_unlock (&sink->display_lock);
482
483   if (filter) {
484     GstCaps *intersection;
485
486     intersection =
487         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
488     gst_caps_unref (caps);
489     caps = intersection;
490   }
491
492   return caps;
493 }
494
495 static gboolean
496 gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
497 {
498   FUNCTION_ENTER ();
499
500   GstWaylandSink *sink;
501   GstBufferPool *newpool;
502   GstVideoInfo info;
503 #ifdef GST_WLSINK_ENHANCEMENT
504   enum tizen_buffer_pool_format format;
505 #else
506   enum wl_shm_format format;
507 #endif
508   GArray *formats;
509   gint i;
510   GstStructure *structure;
511   static GstAllocationParams params = { 0, 0, 0, 15, };
512   sink = GST_WAYLAND_SINK (bsink);
513
514   GST_DEBUG_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
515
516   /* extract info from caps */
517   if (!gst_video_info_from_caps (&info, caps))
518     goto invalid_format;
519 #ifdef GST_WLSINK_ENHANCEMENT
520   sink->caps = gst_caps_copy (caps);
521 #endif
522
523   format = gst_video_format_to_wayland_format (GST_VIDEO_INFO_FORMAT (&info));
524   if ((gint) format == -1)
525     goto invalid_format;
526
527   /* verify we support the requested format */
528   formats = sink->display->formats;
529   for (i = 0; i < formats->len; i++) {
530     if (g_array_index (formats, uint32_t, i) == format)
531       break;
532   }
533
534   if (i >= formats->len)
535     goto unsupported_format;
536
537 #ifdef GST_WLSINK_ENHANCEMENT
538   if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_SN12 ||
539       GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_ST12) {
540     sink->display->is_native_format = TRUE;
541   } else {
542     sink->display->is_native_format = FALSE;
543
544     /* create a new pool for the new configuration */
545     newpool = gst_wayland_buffer_pool_new (sink->display);
546     if (!newpool)
547       goto pool_failed;
548
549     structure = gst_buffer_pool_get_config (newpool);
550     gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
551     gst_buffer_pool_config_set_allocator (structure, NULL, &params);
552     if (!gst_buffer_pool_set_config (newpool, structure))
553       goto config_failed;
554
555     gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
556     gst_object_unref (newpool);
557
558   }
559   /* store the video info */
560   sink->video_info = info;
561   sink->video_info_changed = TRUE;
562 #else
563   /* create a new pool for the new configuration */
564   newpool = gst_wayland_buffer_pool_new (sink->display);
565   if (!newpool)
566     goto pool_failed;
567
568   structure = gst_buffer_pool_get_config (newpool);
569   gst_buffer_pool_config_set_params (structure, caps, info.size, 2, 0);
570   gst_buffer_pool_config_set_allocator (structure, NULL, &params);
571   if (!gst_buffer_pool_set_config (newpool, structure))
572     goto config_failed;
573
574   /* store the video info */
575   sink->video_info = info;
576   sink->video_info_changed = TRUE;
577
578   gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
579   gst_object_unref (newpool);
580 #endif
581   return TRUE;
582
583 invalid_format:
584   {
585     GST_DEBUG_OBJECT (sink,
586         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
587     return FALSE;
588   }
589 unsupported_format:
590   {
591     GST_DEBUG_OBJECT (sink, "Format %s is not available on the display",
592         gst_wayland_format_to_string (format));
593     return FALSE;
594   }
595 pool_failed:
596   {
597     GST_DEBUG_OBJECT (sink, "Failed to create new pool");
598     return FALSE;
599   }
600 config_failed:
601   {
602     GST_DEBUG_OBJECT (bsink, "failed setting config");
603     gst_object_unref (newpool);
604     return FALSE;
605   }
606 }
607
608 static gboolean
609 gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
610 {
611   FUNCTION_ENTER ();
612
613   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
614   GstBufferPool *pool = NULL;
615   GstStructure *config;
616   GstCaps *caps;
617   guint size;
618   gboolean need_pool;
619
620   if (sink->display->is_native_format == TRUE)
621     return TRUE;
622
623   gst_query_parse_allocation (query, &caps, &need_pool);
624
625   if (caps == NULL)
626     goto no_caps;
627
628   if (sink->pool)
629     pool = gst_object_ref (sink->pool);
630
631   if (pool != NULL) {
632     GstCaps *pcaps;
633
634     /* we had a pool, check caps */
635     config = gst_buffer_pool_get_config (pool);
636     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
637
638     if (!gst_caps_is_equal (caps, pcaps)) {
639       /* different caps, we can't use this pool */
640       gst_object_unref (pool);
641       pool = NULL;
642     }
643     gst_structure_free (config);
644   }
645
646   if (pool == NULL && need_pool) {
647     GstVideoInfo info;
648
649     if (!gst_video_info_from_caps (&info, caps))
650       goto invalid_caps;
651
652     GST_DEBUG_OBJECT (sink, "create new pool");
653     pool = gst_wayland_buffer_pool_new (sink->display);
654
655     /* the normal size of a frame */
656     size = info.size;
657
658     config = gst_buffer_pool_get_config (pool);
659     gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
660     if (!gst_buffer_pool_set_config (pool, config))
661       goto config_failed;
662   }
663   if (pool) {
664     gst_query_add_allocation_pool (query, pool, size, 2, 0);
665     gst_object_unref (pool);
666   }
667
668   return TRUE;
669
670   /* ERRORS */
671 no_caps:
672   {
673     GST_DEBUG_OBJECT (bsink, "no caps specified");
674     return FALSE;
675   }
676 invalid_caps:
677   {
678     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
679     return FALSE;
680   }
681 config_failed:
682   {
683     GST_DEBUG_OBJECT (bsink, "failed setting config");
684     gst_object_unref (pool);
685     return FALSE;
686   }
687 }
688
689 static GstFlowReturn
690 gst_wayland_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
691 {
692   FUNCTION_ENTER ();
693
694   GST_DEBUG_OBJECT (bsink, "preroll buffer %p", buffer);
695   return gst_wayland_sink_render (bsink, buffer);
696 }
697
698 static void
699 frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
700 {
701   FUNCTION_ENTER ();
702
703   GstWaylandSink *sink = data;
704
705   GST_LOG ("frame_redraw_cb");
706
707   g_atomic_int_set (&sink->redraw_pending, FALSE);
708   wl_callback_destroy (callback);
709 }
710
711 static const struct wl_callback_listener frame_callback_listener = {
712   frame_redraw_callback
713 };
714
715 /* must be called with the render lock */
716 static void
717 render_last_buffer (GstWaylandSink * sink)
718 {
719   FUNCTION_ENTER ();
720
721   GstWlMeta *meta;
722   struct wl_surface *surface;
723   struct wl_callback *callback;
724
725   meta = gst_buffer_get_wl_meta (sink->last_buffer);
726   surface = gst_wl_window_get_wl_surface (sink->window);
727
728   g_atomic_int_set (&sink->redraw_pending, TRUE);
729   callback = wl_surface_frame (surface);
730   wl_callback_add_listener (callback, &frame_callback_listener, sink);
731
732   /* Here we essentially add a reference to the buffer. This represents
733    * the fact that the compositor is using the buffer and it should
734    * not return back to the pool and be reused until the compositor
735    * releases it. The release is handled internally in the pool */
736   gst_wayland_compositor_acquire_buffer (meta->pool, sink->last_buffer);
737
738   GST_DEBUG ("wl_surface_attach wl_buffer %p", meta->wbuffer);
739
740   wl_surface_attach (surface, meta->wbuffer, 0, 0);
741   wl_surface_damage (surface, 0, 0, sink->window->surface_width,
742       sink->window->surface_height);
743
744   wl_surface_commit (surface);
745   wl_display_flush (sink->display->display);
746 }
747
748 static GstFlowReturn
749 gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
750 {
751   FUNCTION_ENTER ();
752
753   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
754   GstBuffer *to_render;
755   GstWlMeta *meta;
756   GstFlowReturn ret = GST_FLOW_OK;
757
758 #ifdef GST_WLSINK_ENHANCEMENT
759   GstBufferPool *newpool;
760   GstStructure *structure;
761   static GstAllocationParams params = { 0, 0, 0, 15, };
762 #endif
763
764   g_mutex_lock (&sink->render_lock);
765
766   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
767
768   if (G_UNLIKELY (!sink->window)) {
769     /* ask for window handle. Unlock render_lock while doing that because
770      * set_window_handle & friends will lock it in this context */
771     g_mutex_unlock (&sink->render_lock);
772     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
773     g_mutex_lock (&sink->render_lock);
774
775     if (sink->window) {
776       /* inform the window about our caps */
777       gst_wl_window_set_video_info (sink->window, &sink->video_info);
778     } else {
779       /* if we were not provided a window, create one ourselves */
780       sink->window =
781           gst_wl_window_new_toplevel (sink->display, &sink->video_info);
782     }
783     sink->video_info_changed = FALSE;
784   }
785
786   /* drop buffers until we get a frame callback */
787   if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
788     goto done;
789
790   if (G_UNLIKELY (sink->video_info_changed)) {
791     gst_wl_window_set_video_info (sink->window, &sink->video_info);
792     sink->video_info_changed = FALSE;
793
794   }
795   GST_INFO ("window->render_rectangle(%d,%d %d x %d)",
796       sink->window->render_rectangle.x,
797       sink->window->render_rectangle.y,
798       sink->window->render_rectangle.w, sink->window->render_rectangle.h);
799   GST_INFO ("window->surface_width(%d),window->surface_height(%d)",
800       sink->window->surface_width, sink->window->surface_height);
801
802   /* now that we have for sure set the video info on the window, it must have
803    * a valid size, otherwise this means that the application has called
804    * set_window_handle() without calling set_render_rectangle(), which is
805    * absolutely necessary for us.
806    */
807   if (G_UNLIKELY (sink->window->surface_width == 0 ||
808           sink->window->surface_height == 0))
809     goto no_window_size;
810
811   meta = gst_buffer_get_wl_meta (buffer);
812
813   if (meta && meta->pool->display == sink->display) {
814     GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
815     to_render = buffer;
816   } else {
817     GstMapInfo src;
818     GST_LOG_OBJECT (sink, "buffer %p not from our pool, copying", buffer);
819
820 #ifdef GST_WLSINK_ENHANCEMENT
821     if (sink->display->is_native_format == TRUE) {
822       /*in case of SN12 or ST12 video  format */
823       GstMemory *mem;
824       GstMapInfo mem_info = GST_MAP_INFO_INIT;
825       MMVideoBuffer *mm_video_buf = NULL;
826
827       mem = gst_buffer_peek_memory (buffer, 1);
828       gst_memory_map (mem, &mem_info, GST_MAP_READ);
829       mm_video_buf = (MMVideoBuffer *) mem_info.data;
830       gst_memory_unmap (mem, &mem_info);
831
832       if (mm_video_buf == NULL) {
833         GST_WARNING_OBJECT (sink, "mm_video_buf is NULL. Skip rendering");
834         return ret;
835       }
836       /* assign mm_video_buf info */
837       if (mm_video_buf->type == MM_VIDEO_BUFFER_TYPE_TBM_BO) {
838         GST_DEBUG_OBJECT (sink, "TBM bo %p %p %p", mm_video_buf->handle.bo[0],
839             mm_video_buf->handle.bo[1], mm_video_buf->handle.bo[2]);
840
841         sink->display->native_video_size = 0;
842
843         for (int i = 0; i < NV_BUF_PLANE_NUM; i++) {
844           if (mm_video_buf->handle.bo[i] != NULL) {
845             sink->display->bo[i] = mm_video_buf->handle.bo[i];
846           } else {
847             sink->display->bo[i] = 0;
848           }
849           sink->display->plane_size[i] = mm_video_buf->size[i];
850           sink->display->stride_width[i] = mm_video_buf->stride_width[i];
851           sink->display->stride_height[i] = mm_video_buf->stride_height[i];
852           sink->display->native_video_size += sink->display->plane_size[i];
853         }
854       } else {
855         GST_ERROR_OBJECT (sink, "Buffer type is not TBM");
856         return ret;
857       }
858
859       if (!sink->pool) {
860
861         /* create a new pool for the new configuration */
862         newpool = gst_wayland_buffer_pool_new (sink->display);
863         if (!newpool) {
864           GST_DEBUG_OBJECT (sink, "Failed to create new pool");
865           return FALSE;
866         }
867         structure = gst_buffer_pool_get_config (newpool);
868         gst_buffer_pool_config_set_params (structure, sink->caps,
869             sink->video_info.size, 2, 0);
870         gst_buffer_pool_config_set_allocator (structure, NULL, &params);
871         if (!gst_buffer_pool_set_config (newpool, structure)) {
872           GST_DEBUG_OBJECT (bsink, "failed setting config");
873           gst_object_unref (newpool);
874           return FALSE;
875         }
876
877         gst_object_replace ((GstObject **) & sink->pool, (GstObject *) newpool);
878         gst_object_unref (newpool);
879
880       }
881
882       if (!gst_buffer_pool_set_active (sink->pool, TRUE))
883         goto activate_failed;
884
885       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
886       if (ret != GST_FLOW_OK)
887         goto no_buffer;
888
889
890
891     } else {
892       /*in case of normal video format and pool is not our pool */
893
894       if (!sink->pool)
895         goto no_pool;
896
897       if (!gst_buffer_pool_set_active (sink->pool, TRUE))
898         goto activate_failed;
899
900       ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
901       if (ret != GST_FLOW_OK)
902         goto no_buffer;
903
904       gst_buffer_map (buffer, &src, GST_MAP_READ);
905       gst_buffer_fill (to_render, 0, src.data, src.size);
906       gst_buffer_unmap (buffer, &src);
907     }
908
909 #else
910     if (!sink->pool)
911       goto no_pool;
912
913     if (!gst_buffer_pool_set_active (sink->pool, TRUE))
914       goto activate_failed;
915
916     ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
917     if (ret != GST_FLOW_OK)
918       goto no_buffer;
919
920     gst_buffer_map (buffer, &src, GST_MAP_READ);
921     gst_buffer_fill (to_render, 0, src.data, src.size);
922     gst_buffer_unmap (buffer, &src);
923 #endif
924   }
925
926   gst_buffer_replace (&sink->last_buffer, to_render);
927   render_last_buffer (sink);
928
929   if (buffer != to_render)
930     gst_buffer_unref (to_render);
931   goto done;
932
933 no_window_size:
934   {
935     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
936         ("Window has no size set"),
937         ("Make sure you set the size after calling set_window_handle"));
938     ret = GST_FLOW_ERROR;
939     goto done;
940   }
941 no_buffer:
942   {
943     GST_WARNING_OBJECT (sink, "could not create image");
944     goto done;
945   }
946 no_pool:
947   {
948     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
949         ("Internal error: can't allocate images"),
950         ("We don't have a bufferpool negotiated"));
951     ret = GST_FLOW_ERROR;
952     goto done;
953   }
954 activate_failed:
955   {
956     GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
957     ret = GST_FLOW_ERROR;
958     goto done;
959   }
960 done:
961   {
962     g_mutex_unlock (&sink->render_lock);
963     return ret;
964   }
965 }
966
967 static void
968 gst_wayland_sink_videooverlay_init (GstVideoOverlayInterface * iface)
969 {
970   FUNCTION_ENTER ();
971
972   iface->set_window_handle = gst_wayland_sink_set_window_handle;
973   iface->set_render_rectangle = gst_wayland_sink_set_render_rectangle;
974   iface->expose = gst_wayland_sink_expose;
975 }
976
977 static void
978 gst_wayland_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle)
979 {
980   FUNCTION_ENTER ();
981
982   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
983   struct wl_surface *surface = (struct wl_surface *) handle;
984
985   g_return_if_fail (sink != NULL);
986
987   if (sink->window != NULL) {
988     GST_WARNING_OBJECT (sink, "changing window handle is not supported");
989     return;
990   }
991
992   g_mutex_lock (&sink->render_lock);
993
994   GST_DEBUG_OBJECT (sink, "Setting window handle %" GST_PTR_FORMAT,
995       (void *) handle);
996
997   g_clear_object (&sink->window);
998
999   if (handle) {
1000     if (G_LIKELY (gst_wayland_sink_find_display (sink))) {
1001       /* we cannot use our own display with an external window handle */
1002       if (G_UNLIKELY (sink->display->own_display)) {
1003         GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
1004             ("Application did not provide a wayland display handle"),
1005             ("waylandsink cannot use an externally-supplied surface without "
1006                 "an externally-supplied display handle. Consider providing a "
1007                 "display handle from your application with GstContext"));
1008       } else {
1009         sink->window = gst_wl_window_new_in_surface (sink->display, surface);
1010         GST_DEBUG ("sink->window %p", sink->window);
1011       }
1012     } else {
1013       GST_ERROR_OBJECT (sink, "Failed to find display handle, "
1014           "ignoring window handle");
1015     }
1016   }
1017
1018   g_mutex_unlock (&sink->render_lock);
1019 }
1020
1021 static void
1022 gst_wayland_sink_set_render_rectangle (GstVideoOverlay * overlay,
1023     gint x, gint y, gint w, gint h)
1024 {
1025   FUNCTION_ENTER ();
1026
1027   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1028
1029   g_return_if_fail (sink != NULL);
1030
1031   g_mutex_lock (&sink->render_lock);
1032   if (!sink->window) {
1033     g_mutex_unlock (&sink->render_lock);
1034     GST_WARNING_OBJECT (sink,
1035         "set_render_rectangle called without window, ignoring");
1036     return;
1037   }
1038
1039   GST_DEBUG_OBJECT (sink, "window geometry changed to (%d, %d) %d x %d",
1040       x, y, w, h);
1041   gst_wl_window_set_render_rectangle (sink->window, x, y, w, h);
1042
1043   g_mutex_unlock (&sink->render_lock);
1044 }
1045
1046 static void
1047 gst_wayland_sink_expose (GstVideoOverlay * overlay)
1048 {
1049   FUNCTION_ENTER ();
1050
1051   GstWaylandSink *sink = GST_WAYLAND_SINK (overlay);
1052
1053   g_return_if_fail (sink != NULL);
1054
1055   GST_DEBUG_OBJECT (sink, "expose");
1056
1057   g_mutex_lock (&sink->render_lock);
1058   if (sink->last_buffer && g_atomic_int_get (&sink->redraw_pending) == FALSE) {
1059     GST_DEBUG_OBJECT (sink, "redrawing last buffer");
1060     render_last_buffer (sink);
1061   }
1062   g_mutex_unlock (&sink->render_lock);
1063 }
1064
1065 static void
1066 gst_wayland_sink_waylandvideo_init (GstWaylandVideoInterface * iface)
1067 {
1068   FUNCTION_ENTER ();
1069
1070   iface->begin_geometry_change = gst_wayland_sink_begin_geometry_change;
1071   iface->end_geometry_change = gst_wayland_sink_end_geometry_change;
1072 }
1073
1074 static void
1075 gst_wayland_sink_begin_geometry_change (GstWaylandVideo * video)
1076 {
1077   FUNCTION_ENTER ();
1078
1079   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1080   g_return_if_fail (sink != NULL);
1081
1082   g_mutex_lock (&sink->render_lock);
1083   if (!sink->window || !sink->window->subsurface) {
1084     g_mutex_unlock (&sink->render_lock);
1085     GST_INFO_OBJECT (sink,
1086         "begin_geometry_change called without window, ignoring");
1087     return;
1088   }
1089
1090   wl_subsurface_set_sync (sink->window->subsurface);
1091   g_mutex_unlock (&sink->render_lock);
1092 }
1093
1094 static void
1095 gst_wayland_sink_end_geometry_change (GstWaylandVideo * video)
1096 {
1097   FUNCTION_ENTER ();
1098
1099   GstWaylandSink *sink = GST_WAYLAND_SINK (video);
1100   g_return_if_fail (sink != NULL);
1101
1102   g_mutex_lock (&sink->render_lock);
1103   if (!sink->window || !sink->window->subsurface) {
1104     g_mutex_unlock (&sink->render_lock);
1105     GST_INFO_OBJECT (sink,
1106         "end_geometry_change called without window, ignoring");
1107     return;
1108   }
1109
1110   wl_subsurface_set_desync (sink->window->subsurface);
1111   g_mutex_unlock (&sink->render_lock);
1112 }
1113
1114 static gboolean
1115 plugin_init (GstPlugin * plugin)
1116 {
1117   FUNCTION_ENTER ();
1118
1119   GST_DEBUG_CATEGORY_INIT (gstwayland_debug, "waylandsink", 0,
1120       " wayland video sink");
1121
1122   return gst_element_register (plugin, "waylandsink", GST_RANK_MARGINAL,
1123       GST_TYPE_WAYLAND_SINK);
1124 }
1125
1126 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1127     GST_VERSION_MINOR,
1128     waylandsink,
1129     "Wayland Video Sink", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1130     GST_PACKAGE_ORIGIN)