• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-vulkansink
23  * @title: vulkansink
24  *
25  * vulkansink renders video frames to a drawable on a local or remote
26  * display using Vulkan.
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 //#include <gst/video/videooverlay.h>
34 
35 #include "gstvulkanelements.h"
36 #include "vksink.h"
37 
38 GST_DEBUG_CATEGORY (gst_debug_vulkan_sink);
39 #define GST_CAT_DEFAULT gst_debug_vulkan_sink
40 
41 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
42 #define DEFAULT_PIXEL_ASPECT_RATIO_N 0
43 #define DEFAULT_PIXEL_ASPECT_RATIO_D 1
44 
45 static void gst_vulkan_sink_finalize (GObject * object);
46 static void gst_vulkan_sink_set_property (GObject * object, guint prop_id,
47     const GValue * value, GParamSpec * param_spec);
48 static void gst_vulkan_sink_get_property (GObject * object, guint prop_id,
49     GValue * value, GParamSpec * param_spec);
50 
51 static gboolean gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query);
52 static void gst_vulkan_sink_set_context (GstElement * element,
53     GstContext * context);
54 
55 static GstStateChangeReturn
56 gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition);
57 
58 static void gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
59     GstClockTime * start, GstClockTime * end);
60 static gboolean gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
61 static GstCaps *gst_vulkan_sink_get_caps (GstBaseSink * bsink,
62     GstCaps * filter);
63 static GstFlowReturn gst_vulkan_sink_prepare (GstBaseSink * bsink,
64     GstBuffer * buf);
65 static GstFlowReturn gst_vulkan_sink_show_frame (GstVideoSink * bsink,
66     GstBuffer * buf);
67 
68 static void gst_vulkan_sink_video_overlay_init (GstVideoOverlayInterface *
69     iface);
70 
71 static void gst_vulkan_sink_navigation_interface_init (GstNavigationInterface *
72     iface);
73 static void gst_vulkan_sink_key_event_cb (GstVulkanWindow * window,
74     char *event_name, char *key_string, GstVulkanSink * vk_sink);
75 static void gst_vulkan_sink_mouse_event_cb (GstVulkanWindow * window,
76     char *event_name, int button, double posx, double posy,
77     GstVulkanSink * vk_sink);
78 
79 
80 static GstStaticPadTemplate gst_vulkan_sink_template =
81 GST_STATIC_PAD_TEMPLATE ("sink",
82     GST_PAD_SINK,
83     GST_PAD_ALWAYS,
84     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
85         (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
86             GST_VULKAN_SWAPPER_VIDEO_FORMATS)));
87 
88 enum
89 {
90   PROP_0,
91   PROP_FORCE_ASPECT_RATIO,
92   PROP_PIXEL_ASPECT_RATIO,
93   PROP_DEVICE,
94 };
95 
96 enum
97 {
98   SIGNAL_0,
99   LAST_SIGNAL
100 };
101 
102 /* static guint gst_vulkan_sink_signals[LAST_SIGNAL] = { 0 }; */
103 
104 #define gst_vulkan_sink_parent_class parent_class
105 G_DEFINE_TYPE_WITH_CODE (GstVulkanSink, gst_vulkan_sink,
106     GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_sink,
107         "vulkansink", 0, "Vulkan Video Sink");
108     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
109         gst_vulkan_sink_video_overlay_init);
110     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
111         gst_vulkan_sink_navigation_interface_init));
112 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (vulkansink, "vulkansink", GST_RANK_NONE,
113     GST_TYPE_VULKAN_SINK, vulkan_element_init (plugin));
114 
115 static void
gst_vulkan_sink_class_init(GstVulkanSinkClass * klass)116 gst_vulkan_sink_class_init (GstVulkanSinkClass * klass)
117 {
118   GObjectClass *gobject_class;
119   GstElementClass *gstelement_class;
120   GstBaseSinkClass *gstbasesink_class;
121   GstVideoSinkClass *gstvideosink_class;
122   GstElementClass *element_class;
123 
124   gobject_class = (GObjectClass *) klass;
125   gstelement_class = (GstElementClass *) klass;
126   gstbasesink_class = (GstBaseSinkClass *) klass;
127   gstvideosink_class = (GstVideoSinkClass *) klass;
128   element_class = GST_ELEMENT_CLASS (klass);
129 
130   gobject_class->set_property = gst_vulkan_sink_set_property;
131   gobject_class->get_property = gst_vulkan_sink_get_property;
132 
133   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
134       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
135           "When enabled, scaling will respect original aspect ratio",
136           DEFAULT_FORCE_ASPECT_RATIO,
137           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
138 
139   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
140       gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
141           "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
142           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
143 
144   g_object_class_install_property (gobject_class, PROP_DEVICE,
145       g_param_spec_object ("device", "Device", "Vulkan device",
146           GST_TYPE_VULKAN_DEVICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
147 
148   gst_element_class_set_metadata (element_class, "Vulkan video sink",
149       "Sink/Video", "A videosink based on Vulkan",
150       "Matthew Waters <matthew@centricular.com>");
151 
152   gst_element_class_add_static_pad_template (element_class,
153       &gst_vulkan_sink_template);
154 
155   gobject_class->finalize = gst_vulkan_sink_finalize;
156 
157   gstelement_class->change_state = gst_vulkan_sink_change_state;
158   gstelement_class->set_context = gst_vulkan_sink_set_context;
159   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_vulkan_sink_query);
160   gstbasesink_class->set_caps = gst_vulkan_sink_set_caps;
161   gstbasesink_class->get_caps = gst_vulkan_sink_get_caps;
162   gstbasesink_class->get_times = gst_vulkan_sink_get_times;
163   gstbasesink_class->prepare = gst_vulkan_sink_prepare;
164 
165   gstvideosink_class->show_frame =
166       GST_DEBUG_FUNCPTR (gst_vulkan_sink_show_frame);
167 }
168 
169 static void
gst_vulkan_sink_init(GstVulkanSink * vk_sink)170 gst_vulkan_sink_init (GstVulkanSink * vk_sink)
171 {
172   vk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
173   vk_sink->par_n = DEFAULT_PIXEL_ASPECT_RATIO_N;
174   vk_sink->par_d = DEFAULT_PIXEL_ASPECT_RATIO_D;
175 
176 //  g_mutex_init (&vk_sink->drawing_lock);
177 }
178 
179 static void
gst_vulkan_sink_finalize(GObject * object)180 gst_vulkan_sink_finalize (GObject * object)
181 {
182 //  GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
183 //  g_mutex_clear (&vk_sink->drawing_lock);
184 
185 //  GST_DEBUG ("finalized");
186   G_OBJECT_CLASS (parent_class)->finalize (object);
187 }
188 
189 static void
gst_vulkan_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)190 gst_vulkan_sink_set_property (GObject * object, guint prop_id,
191     const GValue * value, GParamSpec * pspec)
192 {
193   GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
194 
195   switch (prop_id) {
196     case PROP_FORCE_ASPECT_RATIO:
197       vk_sink->force_aspect_ratio = g_value_get_boolean (value);
198       if (vk_sink->swapper)
199         g_object_set_property (G_OBJECT (vk_sink->swapper),
200             "force-aspect-ratio", value);
201       break;
202     case PROP_PIXEL_ASPECT_RATIO:
203       vk_sink->par_n = gst_value_get_fraction_numerator (value);
204       vk_sink->par_d = gst_value_get_fraction_denominator (value);
205       if (vk_sink->swapper)
206         g_object_set_property (G_OBJECT (vk_sink->swapper),
207             "pixel-aspect-ratio", value);
208       break;
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211       break;
212   }
213 }
214 
215 static void
gst_vulkan_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)216 gst_vulkan_sink_get_property (GObject * object, guint prop_id,
217     GValue * value, GParamSpec * pspec)
218 {
219   GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
220 
221   switch (prop_id) {
222     case PROP_FORCE_ASPECT_RATIO:
223       g_value_set_boolean (value, vk_sink->force_aspect_ratio);
224       break;
225     case PROP_PIXEL_ASPECT_RATIO:
226       gst_value_set_fraction (value, vk_sink->par_n, vk_sink->par_d);
227       break;
228     case PROP_DEVICE:
229       g_value_set_object (value, vk_sink->device);
230       break;
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233       break;
234   }
235 }
236 
237 static gboolean
gst_vulkan_sink_query(GstBaseSink * bsink,GstQuery * query)238 gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query)
239 {
240   GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
241 
242   switch (GST_QUERY_TYPE (query)) {
243     case GST_QUERY_CONTEXT:{
244       if (gst_vulkan_handle_context_query (GST_ELEMENT (vk_sink), query,
245               vk_sink->display, vk_sink->instance, vk_sink->device))
246         return TRUE;
247       if (vk_sink->swapper &&
248           gst_vulkan_queue_handle_context_query (GST_ELEMENT (vk_sink), query,
249               vk_sink->swapper->queue))
250         return TRUE;
251 
252       break;
253     }
254     default:
255       break;
256   }
257 
258   return GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
259 }
260 
261 static void
gst_vulkan_sink_set_context(GstElement * element,GstContext * context)262 gst_vulkan_sink_set_context (GstElement * element, GstContext * context)
263 {
264   GstVulkanSink *vk_sink = GST_VULKAN_SINK (element);
265 
266   gst_vulkan_handle_set_context (element, context, &vk_sink->display,
267       &vk_sink->instance);
268 
269   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
270 }
271 
272 static GstStateChangeReturn
gst_vulkan_sink_change_state(GstElement * element,GstStateChange transition)273 gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition)
274 {
275   GstVulkanSink *vk_sink = GST_VULKAN_SINK (element);
276   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
277   GError *error = NULL;
278 
279   GST_DEBUG ("changing state: %s => %s",
280       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
281       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
282 
283   switch (transition) {
284     case GST_STATE_CHANGE_NULL_TO_READY:
285       if (!gst_vulkan_ensure_element_data (element, &vk_sink->display,
286               &vk_sink->instance)) {
287         GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
288             ("Failed to retrieve vulkan instance/display"), (NULL));
289         return GST_STATE_CHANGE_FAILURE;
290       }
291 
292       if (!vk_sink->device) {
293         if (!gst_vulkan_device_run_context_query (GST_ELEMENT (vk_sink),
294                 &vk_sink->device)) {
295           if (!(vk_sink->device =
296                   gst_vulkan_instance_create_device (vk_sink->instance,
297                       &error))) {
298             GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
299                 ("Failed to create vulkan device"), ("%s",
300                     error ? error->message : ""));
301             g_clear_error (&error);
302             return GST_STATE_CHANGE_FAILURE;
303           }
304         }
305       }
306       break;
307     case GST_STATE_CHANGE_READY_TO_PAUSED:
308       /* FIXME: this probably doesn't need to be so early in the setup process */
309       if (!(vk_sink->window =
310               gst_vulkan_display_create_window (vk_sink->display))) {
311         GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
312             ("Failed to create a window"), (NULL));
313         return GST_STATE_CHANGE_FAILURE;
314       }
315 
316       if (!vk_sink->set_window_handle)
317         gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (vk_sink));
318 
319       if (vk_sink->set_window_handle)
320         gst_vulkan_window_set_window_handle (vk_sink->window,
321             vk_sink->set_window_handle);
322 
323       if (!gst_vulkan_window_open (vk_sink->window, &error)) {
324         GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
325             ("Failed to open window"), ("%s", error ? error->message : ""));
326         g_clear_error (&error);
327         return GST_STATE_CHANGE_FAILURE;
328       }
329 
330       if (!(vk_sink->swapper =
331               gst_vulkan_swapper_new (vk_sink->device, vk_sink->window))) {
332         GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
333             ("Failed to create a swapper"), (NULL));
334         return GST_STATE_CHANGE_FAILURE;
335       }
336 
337       g_object_set (vk_sink->swapper, "force_aspect-ratio",
338           vk_sink->force_aspect_ratio, "pixel-aspect-ratio", vk_sink->par_n,
339           vk_sink->par_d, NULL);
340 
341       {
342         GstVulkanQueue *queue = NULL;
343         GError *error = NULL;
344 
345         gst_vulkan_queue_run_context_query (GST_ELEMENT (vk_sink), &queue);
346         if (!gst_vulkan_swapper_choose_queue (vk_sink->swapper, queue, &error)) {
347           GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
348               ("Swapper failed to choose a compatible Vulkan Queue"),
349               ("%s", error ? error->message : ""));
350           return GST_STATE_CHANGE_FAILURE;
351         }
352       }
353 
354       vk_sink->key_sig_id =
355           g_signal_connect (vk_sink->window, "key-event",
356           G_CALLBACK (gst_vulkan_sink_key_event_cb), vk_sink);
357       vk_sink->mouse_sig_id =
358           g_signal_connect (vk_sink->window, "mouse-event",
359           G_CALLBACK (gst_vulkan_sink_mouse_event_cb), vk_sink);
360 
361       break;
362     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
363       break;
364     default:
365       break;
366   }
367 
368   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
369   if (ret == GST_STATE_CHANGE_FAILURE)
370     return ret;
371 
372   switch (transition) {
373     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
374       break;
375     case GST_STATE_CHANGE_PAUSED_TO_READY:
376       if (vk_sink->swapper)
377         gst_object_unref (vk_sink->swapper);
378       vk_sink->swapper = NULL;
379       if (vk_sink->window) {
380         gst_vulkan_window_close (vk_sink->window);
381 
382         if (vk_sink->key_sig_id)
383           g_signal_handler_disconnect (vk_sink->window, vk_sink->key_sig_id);
384         vk_sink->key_sig_id = 0;
385         if (vk_sink->mouse_sig_id)
386           g_signal_handler_disconnect (vk_sink->window, vk_sink->mouse_sig_id);
387         vk_sink->mouse_sig_id = 0;
388 
389         gst_object_unref (vk_sink->window);
390       }
391       vk_sink->window = NULL;
392       break;
393     case GST_STATE_CHANGE_READY_TO_NULL:
394       if (vk_sink->display)
395         gst_object_unref (vk_sink->display);
396       vk_sink->display = NULL;
397       if (vk_sink->device)
398         gst_object_unref (vk_sink->device);
399       vk_sink->device = NULL;
400       if (vk_sink->instance)
401         gst_object_unref (vk_sink->instance);
402       vk_sink->instance = NULL;
403       break;
404     default:
405       break;
406   }
407 
408   return ret;
409 }
410 
411 static void
gst_vulkan_sink_get_times(GstBaseSink * bsink,GstBuffer * buf,GstClockTime * start,GstClockTime * end)412 gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
413     GstClockTime * start, GstClockTime * end)
414 {
415   GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
416 
417   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
418     *start = GST_BUFFER_TIMESTAMP (buf);
419     if (GST_BUFFER_DURATION_IS_VALID (buf))
420       *end = *start + GST_BUFFER_DURATION (buf);
421     else {
422       if (GST_VIDEO_INFO_FPS_N (&vk_sink->v_info) > 0) {
423         *end = *start +
424             gst_util_uint64_scale_int (GST_SECOND,
425             GST_VIDEO_INFO_FPS_D (&vk_sink->v_info),
426             GST_VIDEO_INFO_FPS_N (&vk_sink->v_info));
427       }
428     }
429   }
430 }
431 
432 static GstCaps *
gst_vulkan_sink_get_caps(GstBaseSink * bsink,GstCaps * filter)433 gst_vulkan_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
434 {
435   GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
436   GstCaps *tmp = NULL;
437   GstCaps *result = NULL;
438   GError *error = NULL;
439 
440   if (vk_sink->swapper) {
441     if (!(result =
442             gst_vulkan_swapper_get_supported_caps (vk_sink->swapper, &error))) {
443       GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
444               error ? error->message : ""), (NULL));
445       g_clear_error (&error);
446       return NULL;
447     }
448     return result;
449   }
450 
451   tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
452 
453   if (filter) {
454     GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
455         filter);
456 
457     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
458     gst_caps_unref (tmp);
459   } else {
460     result = tmp;
461   }
462 
463   GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
464 
465   return result;
466 }
467 
468 static gboolean
_configure_display_from_info(GstVulkanSink * vk_sink,GstVideoInfo * vinfo)469 _configure_display_from_info (GstVulkanSink * vk_sink, GstVideoInfo * vinfo)
470 {
471   guint display_ratio_num, display_ratio_den;
472   gint display_par_n, display_par_d;
473   gint par_n, par_d;
474   gint width, height;
475   gboolean ok;
476 
477   width = GST_VIDEO_INFO_WIDTH (vinfo);
478   height = GST_VIDEO_INFO_HEIGHT (vinfo);
479 
480   par_n = GST_VIDEO_INFO_PAR_N (vinfo);
481   par_d = GST_VIDEO_INFO_PAR_D (vinfo);
482 
483   if (!par_n)
484     par_n = 1;
485 
486   /* get display's PAR */
487   if (vk_sink->par_n != 0 && vk_sink->par_d != 0) {
488     display_par_n = vk_sink->par_n;
489     display_par_d = vk_sink->par_d;
490   } else {
491     display_par_n = 1;
492     display_par_d = 1;
493   }
494 
495   ok = gst_video_calculate_display_ratio (&display_ratio_num,
496       &display_ratio_den, width, height, par_n, par_d, display_par_n,
497       display_par_d);
498 
499   if (!ok)
500     return FALSE;
501 
502   GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
503       display_par_d);
504 
505   if (height % display_ratio_den == 0) {
506     GST_DEBUG ("keeping video height");
507     GST_VIDEO_SINK_WIDTH (vk_sink) = (guint)
508         gst_util_uint64_scale_int (height, display_ratio_num,
509         display_ratio_den);
510     GST_VIDEO_SINK_HEIGHT (vk_sink) = height;
511   } else if (width % display_ratio_num == 0) {
512     GST_DEBUG ("keeping video width");
513     GST_VIDEO_SINK_WIDTH (vk_sink) = width;
514     GST_VIDEO_SINK_HEIGHT (vk_sink) = (guint)
515         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
516   } else {
517     GST_DEBUG ("approximating while keeping video height");
518     GST_VIDEO_SINK_WIDTH (vk_sink) = (guint)
519         gst_util_uint64_scale_int (height, display_ratio_num,
520         display_ratio_den);
521     GST_VIDEO_SINK_HEIGHT (vk_sink) = height;
522   }
523   GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (vk_sink),
524       GST_VIDEO_SINK_HEIGHT (vk_sink));
525 
526   return TRUE;
527 }
528 
529 static gboolean
gst_vulkan_sink_set_caps(GstBaseSink * bsink,GstCaps * caps)530 gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
531 {
532   GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
533   GError *error = NULL;
534   GstVideoInfo v_info;
535 
536   GST_DEBUG_OBJECT (bsink, "set caps with %" GST_PTR_FORMAT, caps);
537 
538   if (!gst_video_info_from_caps (&v_info, caps))
539     return FALSE;
540 
541   if (!_configure_display_from_info (vk_sink, &v_info))
542     return FALSE;
543 
544   if (!gst_vulkan_swapper_set_caps (vk_sink->swapper, caps, &error)) {
545     GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
546         ("Failed to configure caps"), ("%s", error ? error->message : ""));
547     g_clear_error (&error);
548     return FALSE;
549   }
550 
551   vk_sink->v_info = v_info;
552 
553   return TRUE;
554 }
555 
556 static GstFlowReturn
gst_vulkan_sink_prepare(GstBaseSink * bsink,GstBuffer * buf)557 gst_vulkan_sink_prepare (GstBaseSink * bsink, GstBuffer * buf)
558 {
559   GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
560 
561   GST_TRACE_OBJECT (vk_sink, "preparing buffer %" GST_PTR_FORMAT, buf);
562 
563   if (GST_VIDEO_SINK_WIDTH (vk_sink) < 1 || GST_VIDEO_SINK_HEIGHT (vk_sink) < 1) {
564     return GST_FLOW_NOT_NEGOTIATED;
565   }
566 
567   return GST_FLOW_OK;
568 }
569 
570 static GstFlowReturn
gst_vulkan_sink_show_frame(GstVideoSink * vsink,GstBuffer * buf)571 gst_vulkan_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
572 {
573   GstVulkanSink *vk_sink = GST_VULKAN_SINK (vsink);
574   GError *error = NULL;
575 
576   GST_TRACE_OBJECT (vk_sink, "rendering buffer %" GST_PTR_FORMAT, buf);
577 
578   if (!gst_vulkan_swapper_render_buffer (vk_sink->swapper, buf, &error)) {
579     GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
580         ("Failed to render buffer"), ("%s", error ? error->message : ""));
581     g_clear_error (&error);
582     return GST_FLOW_ERROR;
583   }
584 
585   return GST_FLOW_OK;
586 }
587 
588 static void
gst_vulkan_sink_set_window_handle(GstVideoOverlay * voverlay,guintptr handle)589 gst_vulkan_sink_set_window_handle (GstVideoOverlay * voverlay, guintptr handle)
590 {
591   GstVulkanSink *vk_sink = GST_VULKAN_SINK (voverlay);
592 
593   vk_sink->set_window_handle = handle;
594 }
595 
596 static void
gst_vulkan_sink_video_overlay_init(GstVideoOverlayInterface * iface)597 gst_vulkan_sink_video_overlay_init (GstVideoOverlayInterface * iface)
598 {
599   iface->set_window_handle = gst_vulkan_sink_set_window_handle;
600 }
601 
602 static void
_display_size_to_stream_size(GstVulkanSink * vk_sink,GstVideoRectangle * display_rect,gdouble x,gdouble y,gdouble * stream_x,gdouble * stream_y)603 _display_size_to_stream_size (GstVulkanSink * vk_sink,
604     GstVideoRectangle * display_rect, gdouble x, gdouble y, gdouble * stream_x,
605     gdouble * stream_y)
606 {
607   gdouble stream_width, stream_height;
608 
609   stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&vk_sink->v_info);
610   stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&vk_sink->v_info);
611 
612   /* from display coordinates to stream coordinates */
613   if (display_rect->w > 0)
614     *stream_x = (x - display_rect->x) / display_rect->w * stream_width;
615   else
616     *stream_x = 0.;
617 
618   /* clip to stream size */
619   *stream_x = CLAMP (*stream_x, 0., stream_width);
620 
621   /* same for y-axis */
622   if (display_rect->h > 0)
623     *stream_y = (y - display_rect->y) / display_rect->h * stream_height;
624   else
625     *stream_y = 0.;
626 
627   *stream_y = CLAMP (*stream_y, 0., stream_height);
628 
629   GST_TRACE_OBJECT (vk_sink, "transform %fx%f into %fx%f", x, y, *stream_x,
630       *stream_y);
631 }
632 
633 static void
gst_vulkan_sink_navigation_send_event(GstNavigation * navigation,GstStructure * structure)634 gst_vulkan_sink_navigation_send_event (GstNavigation * navigation,
635     GstStructure * structure)
636 {
637   GstVulkanSink *vk_sink = GST_VULKAN_SINK (navigation);
638   GstVideoRectangle display_rect;
639   GstEvent *event = NULL;
640   gdouble x, y;
641 
642   if (!vk_sink->swapper || !vk_sink->swapper->window) {
643     gst_structure_free (structure);
644     return;
645   }
646 
647   gst_vulkan_swapper_get_surface_rectangles (vk_sink->swapper, NULL, NULL,
648       &display_rect);
649 
650   /* Converting pointer coordinates to the non scaled geometry */
651   if (display_rect.w != 0 && display_rect.h != 0
652       && gst_structure_get_double (structure, "pointer_x", &x)
653       && gst_structure_get_double (structure, "pointer_y", &y)) {
654     gdouble stream_x, stream_y;
655 
656     _display_size_to_stream_size (vk_sink, &display_rect, x, y, &stream_x,
657         &stream_y);
658 
659     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
660         stream_x, "pointer_y", G_TYPE_DOUBLE, stream_y, NULL);
661   }
662 
663   event = gst_event_new_navigation (structure);
664   if (event) {
665     gboolean handled;
666 
667     gst_event_ref (event);
668     handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (vk_sink), event);
669 
670     if (!handled)
671       gst_element_post_message ((GstElement *) vk_sink,
672           gst_navigation_message_new_event ((GstObject *) vk_sink, event));
673 
674     gst_event_unref (event);
675   }
676 }
677 
678 static void
gst_vulkan_sink_navigation_interface_init(GstNavigationInterface * iface)679 gst_vulkan_sink_navigation_interface_init (GstNavigationInterface * iface)
680 {
681   iface->send_event = gst_vulkan_sink_navigation_send_event;
682 }
683 
684 static void
gst_vulkan_sink_key_event_cb(GstVulkanWindow * window,char * event_name,char * key_string,GstVulkanSink * vk_sink)685 gst_vulkan_sink_key_event_cb (GstVulkanWindow * window, char *event_name, char
686     *key_string, GstVulkanSink * vk_sink)
687 {
688   GST_DEBUG_OBJECT (vk_sink, "event %s key %s pressed", event_name, key_string);
689   gst_navigation_send_key_event (GST_NAVIGATION (vk_sink),
690       event_name, key_string);
691 }
692 
693 static void
gst_vulkan_sink_mouse_event_cb(GstVulkanWindow * window,char * event_name,int button,double posx,double posy,GstVulkanSink * vk_sink)694 gst_vulkan_sink_mouse_event_cb (GstVulkanWindow * window, char *event_name,
695     int button, double posx, double posy, GstVulkanSink * vk_sink)
696 {
697   GST_DEBUG_OBJECT (vk_sink, "event %s at %g, %g", event_name, posx, posy);
698   gst_navigation_send_mouse_event (GST_NAVIGATION (vk_sink),
699       event_name, button, posx, posy);
700 }
701