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