3 * Copyright (C) 2019 Matthew Waters <matthew@centricular.com>
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.
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.
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.
22 * SECTION:element-vulkanimageidentity
23 * @title: vulkanimgeidentity
25 * vulkanimageidentity produces a vulkan image that is a copy of the input image.
34 #include "vkviewconvert.h"
36 #include "vkelementutils.h"
38 #include "shaders/identity.vert.h"
39 #include "shaders/view_convert.frag.h"
40 #include "gstvulkan-plugins-enumtypes.h"
42 GST_DEBUG_CATEGORY (gst_debug_vulkan_view_convert);
43 #define GST_CAT_DEFAULT gst_debug_vulkan_view_convert
46 /* These match the order and number of DOWNMIX_ANAGLYPH_* modes */
47 static float downmix_matrices[][2][12] = {
48 { /* Green-Magenta Dubois */
49 {-0.062f, 0.284f, -0.015f, 0.0, -0.158f, 0.668f, -0.027f, 0.0, -0.039f, 0.143f, 0.021f, 0.0},
50 {0.529f, -0.016f, 0.009f, 0.0, 0.705f, -0.015f, 0.075f, 0.0, 0.024f, -0.065f, 0.937f, 0.0}
52 { /* Red-Cyan Dubois */
53 /* Source of this matrix: http://www.site.uottawa.ca/~edubois/anaglyph/LeastSquaresHowToPhotoshop.pdf */
54 {0.437f, -0.062f, -0.048f, 0.0, 0.449f, -0.062f, -0.050f, 0.0, 0.164f, -0.024f, -0.017f},
55 {-0.011f, 0.377f, -0.026f, 0.0, -0.032f, 0.761f, -0.093f, 0.0, -0.007f, 0.009f, 1.234f}
57 { /* Amber-blue Dubois */
58 {1.062f, -0.026f, -0.038f, 0.0, -0.205f, 0.908f, -0.173f, 0.0, 0.299f, 0.068f, 0.022f},
59 {-0.016f, 0.006f, 0.094f, 0.0, -0.123f, 0.062f, 0.185f, 0.0, -0.017f, -0.017f, 0.911f}
66 int in_reorder_idx[4];
67 int out_reorder_idx[4];
68 float tex_offset[2][2];
69 float tex_scale[2][2];
77 get_rgb_format_swizzle_order (GstVideoFormat format,
78 gint swizzle[GST_VIDEO_MAX_COMPONENTS])
80 const GstVideoFormatInfo *finfo = gst_video_format_get_info (format);
83 g_return_if_fail (finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB
84 || format == GST_VIDEO_FORMAT_AYUV);
86 for (i = 0; i < finfo->n_components; i++) {
87 swizzle[c_i++] = finfo->poffset[i];
90 /* special case spaced RGB formats as the space does not contain a poffset
91 * value and we need all four components to be valid in order to swizzle
93 if (format == GST_VIDEO_FORMAT_xRGB || format == GST_VIDEO_FORMAT_xBGR) {
95 } else if (format == GST_VIDEO_FORMAT_RGBx || format == GST_VIDEO_FORMAT_BGRx) {
98 for (i = finfo->n_components; i < GST_VIDEO_MAX_COMPONENTS; i++) {
105 get_vulkan_rgb_format_swizzle_order (VkFormat format, gint * swizzle,
106 guint swizzle_count, guint offset)
108 const GstVulkanFormatInfo *finfo = gst_vulkan_format_get_info (format);
111 g_return_if_fail (finfo->flags & GST_VULKAN_FORMAT_FLAG_RGB);
112 g_return_if_fail (finfo->n_components <= swizzle_count);
114 for (i = 0; i < finfo->n_components; i++) {
115 swizzle[i] = offset + finfo->poffset[i];
117 for (i = finfo->n_components; i < swizzle_count; i++) {
122 /* given a swizzle index, produce an index such that:
124 * swizzle[idx[i]] == identity[i] where:
125 * - swizzle is the original swizzle
126 * - idx is the result
127 * - identity = {0, 1, 2,...}
128 * - unset fields are marked by -1
131 swizzle_identity_order (gint * swizzle, gint * idx)
135 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
139 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
140 if (swizzle[i] >= 0 && swizzle[i] < 4 && idx[swizzle[i]] == -1) {
147 video_format_to_reorder (GstVideoFormat v_format, gint * reorder,
151 case GST_VIDEO_FORMAT_RGBA:
152 case GST_VIDEO_FORMAT_RGBx:
153 case GST_VIDEO_FORMAT_BGRA:
154 case GST_VIDEO_FORMAT_BGRx:
155 case GST_VIDEO_FORMAT_ARGB:
156 case GST_VIDEO_FORMAT_xRGB:
157 case GST_VIDEO_FORMAT_ABGR:
158 case GST_VIDEO_FORMAT_xBGR:
159 case GST_VIDEO_FORMAT_AYUV:
160 get_rgb_format_swizzle_order (v_format, reorder);
162 case GST_VIDEO_FORMAT_UYVY:
165 reorder[2] = input ? 3 : 2;
168 case GST_VIDEO_FORMAT_YUY2:
172 reorder[3] = input ? 3 : 2;
174 case GST_VIDEO_FORMAT_NV12:
181 g_assert_not_reached ();
185 GST_TRACE ("swizzle: %u, %u, %u, %u", reorder[0], reorder[1], reorder[2],
190 finfo_get_plane_n_components (const GstVideoFormatInfo * finfo, guint plane)
192 guint n_components = 0, i;
194 switch (finfo->format) {
195 case GST_VIDEO_FORMAT_RGBx:
196 case GST_VIDEO_FORMAT_xRGB:
197 case GST_VIDEO_FORMAT_BGRx:
198 case GST_VIDEO_FORMAT_xBGR:
199 /* fixup spaced RGB formats as we treat the space as a normal alpha
201 return plane == 0 ? 4 : 0;
206 for (i = 0; i < finfo->n_components; i++) {
207 if (finfo->plane[i] == plane)
215 get_vulkan_format_swizzle_order (GstVideoFormat v_format,
216 VkFormat vk_format[GST_VIDEO_MAX_PLANES],
217 gint swizzle[GST_VIDEO_MAX_COMPONENTS])
219 const GstVideoFormatInfo *finfo;
220 int i, prev_in_i = 0;
222 finfo = gst_video_format_get_info (v_format);
223 for (i = 0; i < finfo->n_planes; i++) {
224 guint plane_components = finfo_get_plane_n_components (finfo, i);
225 get_vulkan_rgb_format_swizzle_order (vk_format[i],
226 &swizzle[prev_in_i], plane_components, prev_in_i);
227 prev_in_i += plane_components;
230 if (v_format == GST_VIDEO_FORMAT_YUY2 || v_format == GST_VIDEO_FORMAT_UYVY) {
231 /* Fixup these packed YUV formats as we use a two component format for
232 * a 4-component pixel and access two samples in the shader */
233 g_assert (swizzle[0] == 0);
234 g_assert (swizzle[1] == 1);
239 GST_TRACE ("%s: %i, %i, %i, %i", finfo->name, swizzle[0], swizzle[1],
240 swizzle[2], swizzle[3]);
244 calculate_reorder_indexes (GstVideoFormat in_format,
245 GstVulkanImageView * in_views[GST_VIDEO_MAX_COMPONENTS],
246 GstVideoFormat out_format,
247 GstVulkanImageView * out_views[GST_VIDEO_MAX_COMPONENTS],
248 int ret_in[GST_VIDEO_MAX_COMPONENTS], int ret_out[GST_VIDEO_MAX_COMPONENTS])
250 const GstVideoFormatInfo *in_finfo, *out_finfo;
251 VkFormat in_vk_formats[GST_VIDEO_MAX_COMPONENTS];
252 VkFormat out_vk_formats[GST_VIDEO_MAX_COMPONENTS];
253 int in_vk_order[GST_VIDEO_MAX_COMPONENTS],
254 in_reorder[GST_VIDEO_MAX_COMPONENTS];
255 int out_vk_order[GST_VIDEO_MAX_COMPONENTS],
256 out_reorder[GST_VIDEO_MAX_COMPONENTS];
257 int tmp[GST_VIDEO_MAX_PLANES];
260 in_finfo = gst_video_format_get_info (in_format);
261 out_finfo = gst_video_format_get_info (out_format);
263 for (i = 0; i < in_finfo->n_planes; i++)
264 in_vk_formats[i] = in_views[i]->image->create_info.format;
265 for (i = 0; i < out_finfo->n_planes; i++)
266 out_vk_formats[i] = out_views[i]->image->create_info.format;
268 get_vulkan_format_swizzle_order (in_format, in_vk_formats, in_vk_order);
269 video_format_to_reorder (in_format, in_reorder, TRUE);
271 video_format_to_reorder (out_format, out_reorder, FALSE);
272 get_vulkan_format_swizzle_order (out_format, out_vk_formats, out_vk_order);
274 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
275 tmp[i] = out_vk_order[out_reorder[i]];
276 /* find the identity order for RGBA->$format */
277 GST_TRACE ("pre-invert: %u, %u, %u, %u", tmp[0], tmp[1], tmp[2], tmp[3]);
278 if (out_format == GST_VIDEO_FORMAT_YUY2
279 || out_format == GST_VIDEO_FORMAT_UYVY) {
280 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
283 swizzle_identity_order (tmp, ret_out);
286 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
287 ret_in[i] = in_reorder[in_vk_order[i]];
288 GST_TRACE ("in reorder: %u, %u, %u, %u", ret_in[0], ret_in[1], ret_in[2],
290 GST_TRACE ("out reorder: %u, %u, %u, %u", ret_out[0], ret_out[1], ret_out[2],
295 update_descriptor_set (GstVulkanViewConvert * conv,
296 GstVulkanImageView ** in_views)
298 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
299 VkDescriptorImageInfo image_info[GST_VIDEO_MAX_PLANES];
300 VkWriteDescriptorSet writes[GST_VIDEO_MAX_PLANES];
301 GstVideoMultiviewMode in_mode;
302 GstVideoMultiviewFlags in_flags, out_flags;
303 VkImageView views[GST_VIDEO_MAX_PLANES];
306 in_mode = conv->input_mode_override;
307 in_flags = conv->input_flags_override;
308 if (in_mode == GST_VIDEO_MULTIVIEW_MODE_NONE)
309 in_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&vfilter->in_info);
310 out_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&vfilter->out_info);
312 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vfilter->in_info); i++) {
313 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST) ==
314 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)) {
315 views[2 * i] = in_views[i]->view;
316 views[2 * i + 1] = in_views[i]->view;
318 views[2 * i] = in_views[i]->view;
319 views[2 * i + 1] = in_views[i]->view;
323 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vfilter->in_info) * 2; i++) {
325 image_info[i] = (VkDescriptorImageInfo) {
326 .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
327 .imageView = views[i],
328 .sampler = (VkSampler) conv->quad->sampler->handle
331 g_assert (i < GST_VIDEO_MAX_PLANES);
333 writes[i] = (VkWriteDescriptorSet) {
334 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
336 .dstSet = conv->quad->descriptor_set->set,
338 .dstArrayElement = 0,
339 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
340 .descriptorCount = 1,
341 .pImageInfo = &image_info[i]
345 g_assert (i <= G_N_ELEMENTS (writes));
347 vkUpdateDescriptorSets (vfilter->device->device, i, writes, 0, NULL);
351 create_uniform_buffer (GstVulkanViewConvert * conv)
353 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
356 gst_vulkan_buffer_memory_alloc (vfilter->device,
357 sizeof (struct ViewUpdate),
358 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
359 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
360 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
366 update_uniform (GstVulkanViewConvert * conv, GstVulkanImageView ** in_views,
367 GstVulkanImageView ** out_views)
369 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
370 GstVideoMultiviewMode in_mode, out_mode;
371 GstVideoMultiviewFlags in_flags, out_flags;
372 struct ViewUpdate data;
374 guint l_index, r_index;
375 gboolean mono_input = FALSE;
377 calculate_reorder_indexes (GST_VIDEO_INFO_FORMAT (&vfilter->in_info),
378 in_views, GST_VIDEO_INFO_FORMAT (&vfilter->out_info),
379 out_views, data.in_reorder_idx, data.out_reorder_idx);
381 data.tex_scale[0][0] = data.tex_scale[0][1] = 1.;
382 data.tex_scale[1][0] = data.tex_scale[1][1] = 1.;
383 data.tex_offset[0][0] = data.tex_offset[0][1] = 0.;
384 data.tex_offset[1][0] = data.tex_offset[1][1] = 0.;
386 in_mode = conv->input_mode_override;
387 in_flags = conv->input_flags_override;
388 if (in_mode == GST_VIDEO_MULTIVIEW_MODE_NONE) {
389 in_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&vfilter->in_info);
390 in_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&vfilter->in_info);
393 /* Configured output mode already takes any override
395 out_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&vfilter->out_info);
396 out_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&vfilter->out_info);
398 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST) ==
399 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)) {
403 GST_LOG_OBJECT (conv, "Switching left/right views");
409 if (in_mode < GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE) { /* unknown/mono/left/right single image */
410 } else if (in_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE ||
411 in_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
412 /* Side-by-side input */
413 data.tex_offset[r_index][0] += 0.5 * data.tex_scale[r_index][0];
414 data.tex_scale[0][0] *= 0.5f; /* Half horizontal scale */
415 data.tex_scale[1][0] *= 0.5f;
416 } else if (in_mode == GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM) { /* top-bottom */
417 data.tex_offset[r_index][1] += 0.5 * data.tex_scale[r_index][1];
418 data.tex_scale[0][1] *= 0.5f; /* Half vertical scale */
419 data.tex_scale[1][1] *= 0.5f;
422 /* Flipped is vertical, flopped is horizontal.
423 * Adjust and offset per-view scaling. This needs to be done
424 * after the input scaling already splits the views, before
425 * adding any output scaling. */
426 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED) !=
427 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED)) {
428 data.tex_offset[l_index][1] += data.tex_scale[l_index][1];
429 data.tex_scale[l_index][1] *= -1.0;
431 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED) !=
432 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED)) {
433 data.tex_offset[l_index][0] += data.tex_scale[l_index][0];
434 data.tex_scale[l_index][0] *= -1.0;
436 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED) !=
437 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED)) {
438 data.tex_offset[r_index][1] += data.tex_scale[r_index][1];
439 data.tex_scale[r_index][1] *= -1.0;
441 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED) !=
442 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED)) {
443 data.tex_offset[r_index][0] += data.tex_scale[r_index][0];
444 data.tex_scale[r_index][0] *= -1.0;
447 if (out_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE ||
448 out_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
450 data.tex_offset[1][0] -= data.tex_scale[1][0];
451 data.tex_scale[0][0] *= 2.0f;
452 data.tex_scale[1][0] *= 2.0f;
453 } else if (out_mode == GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM) {
454 data.tex_offset[1][1] -= data.tex_scale[1][1];
455 data.tex_scale[0][1] *= 2.0f;
456 data.tex_scale[1][1] *= 2.0f;
459 GST_DEBUG_OBJECT (conv,
460 "Scaling matrix [ %f, %f ] [ %f %f]. Offsets [ %f, %f ] [ %f, %f ]",
461 data.tex_scale[0][0], data.tex_scale[0][1],
462 data.tex_scale[1][0], data.tex_scale[1][1],
463 data.tex_offset[0][0], data.tex_offset[0][1], data.tex_offset[1][0],
464 data.tex_offset[1][1]);
466 if (in_mode == GST_VIDEO_MULTIVIEW_MODE_NONE ||
467 in_mode == GST_VIDEO_MULTIVIEW_MODE_MONO ||
468 in_mode == GST_VIDEO_MULTIVIEW_MODE_LEFT ||
469 in_mode == GST_VIDEO_MULTIVIEW_MODE_RIGHT)
472 data.output_type = out_mode;
473 if (data.output_type == GST_VIDEO_MULTIVIEW_MODE_NONE ||
474 data.output_type == GST_VIDEO_MULTIVIEW_MODE_MONO) {
476 data.output_type = GST_VIDEO_MULTIVIEW_MODE_LEFT;
478 data.output_type = GST_VIDEO_MULTIVIEW_MODE_MONO;
479 } else if (data.output_type == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
480 data.output_type = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
483 data.tex_size[0] = GST_VIDEO_INFO_WIDTH (&vfilter->out_info);
484 data.tex_size[1] = GST_VIDEO_INFO_HEIGHT (&vfilter->out_info);
485 memcpy (&data.downmix[0], &downmix_matrices[conv->downmix_mode][0],
486 sizeof (data.downmix[0]));
487 memcpy (&data.downmix[1], &downmix_matrices[conv->downmix_mode][1],
488 sizeof (data.downmix[1]));
490 if (!gst_memory_map (conv->uniform, &map_info, GST_MAP_WRITE)) {
493 memcpy (map_info.data, &data, sizeof (data));
494 gst_memory_unmap (conv->uniform, &map_info);
500 get_uniforms (GstVulkanViewConvert * conv,
501 GstVulkanImageView ** in_views, GstVulkanImageView ** out_views)
503 if (!conv->uniform) {
504 if (!create_uniform_buffer (conv))
506 if (!update_uniform (conv, in_views, out_views)) {
507 gst_memory_unref (conv->uniform);
508 conv->uniform = NULL;
513 return gst_memory_ref (conv->uniform);
516 static void gst_vulkan_view_convert_set_property (GObject * object,
517 guint prop_id, const GValue * value, GParamSpec * pspec);
518 static void gst_vulkan_view_convert_get_property (GObject * object,
519 guint prop_id, GValue * value, GParamSpec * pspec);
521 static gboolean gst_vulkan_view_convert_start (GstBaseTransform * bt);
522 static gboolean gst_vulkan_view_convert_stop (GstBaseTransform * bt);
524 static GstCaps *gst_vulkan_view_convert_transform_caps (GstBaseTransform * bt,
525 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
526 static GstCaps *gst_vulkan_view_convert_fixate_caps (GstBaseTransform * bt,
527 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
528 static GstFlowReturn gst_vulkan_view_convert_transform (GstBaseTransform * bt,
529 GstBuffer * inbuf, GstBuffer * outbuf);
530 static gboolean gst_vulkan_view_convert_set_caps (GstBaseTransform * bt,
531 GstCaps * in_caps, GstCaps * out_caps);
533 static GstStaticPadTemplate gst_vulkan_sink_template =
534 GST_STATIC_PAD_TEMPLATE ("sink",
537 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
538 (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
541 static GstStaticPadTemplate gst_vulkan_src_template =
542 GST_STATIC_PAD_TEMPLATE ("src",
545 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
546 (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
556 PROP_OUTPUT_DOWNMIX_MODE
559 #define DEFAULT_DOWNMIX GST_VULKAN_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
561 #define gst_vulkan_view_convert_parent_class parent_class
562 G_DEFINE_TYPE_WITH_CODE (GstVulkanViewConvert, gst_vulkan_view_convert,
563 GST_TYPE_VULKAN_VIDEO_FILTER,
564 GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_view_convert,
565 "vulkanviewconvert", 0, "Vulkan View Convert"));
568 gst_vulkan_view_convert_class_init (GstVulkanViewConvertClass * klass)
570 GObjectClass *gobject_class;
571 GstElementClass *gstelement_class;
572 GstBaseTransformClass *gstbasetransform_class;
574 gobject_class = (GObjectClass *) klass;
575 gstelement_class = (GstElementClass *) klass;
576 gstbasetransform_class = (GstBaseTransformClass *) klass;
578 gobject_class->set_property = gst_vulkan_view_convert_set_property;
579 gobject_class->get_property = gst_vulkan_view_convert_get_property;
581 g_object_class_install_property (gobject_class, PROP_INPUT_LAYOUT,
582 g_param_spec_enum ("input-mode-override",
583 "Input Multiview Mode Override",
584 "Override any input information about multiview layout",
585 GST_TYPE_VIDEO_MULTIVIEW_MODE,
586 GST_VIDEO_MULTIVIEW_MODE_NONE,
587 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
588 g_object_class_install_property (gobject_class, PROP_INPUT_FLAGS,
589 g_param_spec_flags ("input-flags-override",
590 "Input Multiview Flags Override",
591 "Override any input information about multiview layout flags",
592 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
593 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
594 g_object_class_install_property (gobject_class, PROP_OUTPUT_LAYOUT,
595 g_param_spec_enum ("output-mode-override",
596 "Output Multiview Mode Override",
597 "Override automatic output mode selection for multiview layout",
598 GST_TYPE_VIDEO_MULTIVIEW_MODE, GST_VIDEO_MULTIVIEW_MODE_NONE,
599 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
600 g_object_class_install_property (gobject_class, PROP_OUTPUT_FLAGS,
601 g_param_spec_flags ("output-flags-override",
602 "Output Multiview Flags Override",
603 "Override automatic negotiation for output multiview layout flags",
604 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
605 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
606 g_object_class_install_property (gobject_class, PROP_OUTPUT_DOWNMIX_MODE,
607 g_param_spec_enum ("downmix-mode", "Mode for mono downmixed output",
608 "Output anaglyph type to generate when downmixing to mono",
609 GST_TYPE_VULKAN_STEREO_DOWNMIX, DEFAULT_DOWNMIX,
610 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
612 gst_element_class_set_metadata (gstelement_class, "Vulkan Uploader",
613 "Filter/Video/Convert", "A Vulkan View Convert",
614 "Matthew Waters <matthew@centricular.com>");
616 gst_element_class_add_static_pad_template (gstelement_class,
617 &gst_vulkan_sink_template);
618 gst_element_class_add_static_pad_template (gstelement_class,
619 &gst_vulkan_src_template);
621 gstbasetransform_class->start =
622 GST_DEBUG_FUNCPTR (gst_vulkan_view_convert_start);
623 gstbasetransform_class->stop =
624 GST_DEBUG_FUNCPTR (gst_vulkan_view_convert_stop);
625 gstbasetransform_class->transform_caps =
626 gst_vulkan_view_convert_transform_caps;
627 gstbasetransform_class->fixate_caps = gst_vulkan_view_convert_fixate_caps;
628 gstbasetransform_class->set_caps = gst_vulkan_view_convert_set_caps;
629 gstbasetransform_class->transform = gst_vulkan_view_convert_transform;
633 gst_vulkan_view_convert_init (GstVulkanViewConvert * conv)
635 conv->downmix_mode = DEFAULT_DOWNMIX;
637 conv->input_mode_override = GST_VIDEO_MULTIVIEW_MODE_NONE;
638 conv->input_flags_override = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
639 conv->output_mode_override = GST_VIDEO_MULTIVIEW_MODE_NONE;
640 conv->output_flags_override = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
644 gst_vulkan_view_convert_set_property (GObject * object, guint prop_id,
645 const GValue * value, GParamSpec * pspec)
647 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (object);
650 case PROP_INPUT_LAYOUT:
651 conv->input_mode_override = g_value_get_enum (value);
652 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
654 case PROP_INPUT_FLAGS:
655 conv->input_flags_override = g_value_get_flags (value);
656 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
658 case PROP_OUTPUT_LAYOUT:
659 conv->output_mode_override = g_value_get_enum (value);
660 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
662 case PROP_OUTPUT_FLAGS:
663 conv->output_flags_override = g_value_get_flags (value);
664 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
666 case PROP_OUTPUT_DOWNMIX_MODE:
667 conv->downmix_mode = g_value_get_enum (value);
670 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
676 gst_vulkan_view_convert_get_property (GObject * object, guint prop_id,
677 GValue * value, GParamSpec * pspec)
679 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (object);
682 case PROP_INPUT_LAYOUT:
683 g_value_set_enum (value, conv->input_mode_override);
685 case PROP_INPUT_FLAGS:
686 g_value_set_flags (value, conv->input_flags_override);
688 case PROP_OUTPUT_LAYOUT:
689 g_value_set_enum (value, conv->output_mode_override);
691 case PROP_OUTPUT_FLAGS:
692 g_value_set_flags (value, conv->output_flags_override);
694 case PROP_OUTPUT_DOWNMIX_MODE:
695 g_value_set_enum (value, conv->downmix_mode);
698 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
703 /* Function that can halve the value
704 * of ints, fractions, int/fraction ranges and lists of ints/fractions */
706 _halve_value (GValue * out, const GValue * in_value)
708 /* Fundamental fixed types first */
709 if (G_VALUE_HOLDS_INT (in_value)) {
710 g_value_init (out, G_TYPE_INT);
711 g_value_set_int (out, MAX (g_value_get_int (in_value) / 2, 1));
712 } else if (GST_VALUE_HOLDS_FRACTION (in_value)) {
714 num = gst_value_get_fraction_numerator (in_value);
715 den = gst_value_get_fraction_denominator (in_value);
716 g_value_init (out, GST_TYPE_FRACTION);
717 /* Don't adjust 'infinite' fractions */
718 if ((num != 1 || den != 2147483647) && (num != 2147483647 || den != 1)) {
719 /* FIXME - could do better approximation when den > G_MAXINT/2? */
720 den = den > G_MAXINT / 2 ? G_MAXINT : den * 2;
722 gst_value_set_fraction (out, num, den);
723 } else if (GST_VALUE_HOLDS_INT_RANGE (in_value)) {
724 gint range_min = gst_value_get_int_range_min (in_value);
725 gint range_max = gst_value_get_int_range_max (in_value);
726 gint range_step = gst_value_get_int_range_step (in_value);
727 g_value_init (out, GST_TYPE_INT_RANGE);
729 range_min = MAX (1, range_min / 2);
730 if (range_max != G_MAXINT)
731 range_max = MAX (1, range_max / 2);
732 gst_value_set_int_range_step (out, range_min,
733 range_max, MAX (1, range_step / 2));
734 } else if (GST_VALUE_HOLDS_FRACTION_RANGE (in_value)) {
735 GValue min_out = G_VALUE_INIT;
736 GValue max_out = G_VALUE_INIT;
737 const GValue *range_min = gst_value_get_fraction_range_min (in_value);
738 const GValue *range_max = gst_value_get_fraction_range_max (in_value);
739 _halve_value (&min_out, range_min);
740 _halve_value (&max_out, range_max);
741 g_value_init (out, GST_TYPE_FRACTION_RANGE);
742 gst_value_set_fraction_range (out, &min_out, &max_out);
743 g_value_unset (&min_out);
744 g_value_unset (&max_out);
745 } else if (GST_VALUE_HOLDS_LIST (in_value)) {
747 g_value_init (out, GST_TYPE_LIST);
748 for (i = 0; i < gst_value_list_get_size (in_value); i++) {
750 GValue tmp = G_VALUE_INIT;
752 entry = gst_value_list_get_value (in_value, i);
753 /* Random list values might not be the right type */
754 if (!_halve_value (&tmp, entry))
756 gst_value_list_append_and_take_value (out, &tmp);
768 static GstStructure *
769 _halve_structure_field (const GstStructure * in, const gchar * field_name)
772 const GValue *in_value = gst_structure_get_value (in, field_name);
773 GValue tmp = G_VALUE_INIT;
775 if (G_UNLIKELY (in_value == NULL))
776 return gst_structure_copy (in); /* Field doesn't exist, leave it as is */
778 if (!_halve_value (&tmp, in_value))
781 out = gst_structure_copy (in);
782 gst_structure_set_value (out, field_name, &tmp);
783 g_value_unset (&tmp);
788 /* Function that can double the value
789 * of ints, fractions, int/fraction ranges and lists of ints/fractions */
791 _double_value (GValue * out, const GValue * in_value)
793 /* Fundamental fixed types first */
794 if (G_VALUE_HOLDS_INT (in_value)) {
795 gint n = g_value_get_int (in_value);
796 g_value_init (out, G_TYPE_INT);
797 if (n <= G_MAXINT / 2)
798 g_value_set_int (out, n * 2);
800 g_value_set_int (out, G_MAXINT);
801 } else if (GST_VALUE_HOLDS_FRACTION (in_value)) {
803 num = gst_value_get_fraction_numerator (in_value);
804 den = gst_value_get_fraction_denominator (in_value);
805 g_value_init (out, GST_TYPE_FRACTION);
806 /* Don't adjust 'infinite' fractions */
807 if ((num != 1 || den != 2147483647) && (num != 2147483647 || den != 1)) {
808 /* FIXME - could do better approximation when num > G_MAXINT/2? */
809 num = num > G_MAXINT / 2 ? G_MAXINT : num * 2;
811 gst_value_set_fraction (out, num, den);
812 } else if (GST_VALUE_HOLDS_INT_RANGE (in_value)) {
813 gint range_min = gst_value_get_int_range_min (in_value);
814 gint range_max = gst_value_get_int_range_max (in_value);
815 gint range_step = gst_value_get_int_range_step (in_value);
816 if (range_min != 1) {
817 range_min = MIN (G_MAXINT / 2, range_min);
820 if (range_max != G_MAXINT) {
821 range_max = MIN (G_MAXINT / 2, range_max);
824 range_step = MIN (G_MAXINT / 2, range_step);
825 g_value_init (out, GST_TYPE_INT_RANGE);
826 gst_value_set_int_range_step (out, range_min, range_max, range_step);
827 } else if (GST_VALUE_HOLDS_FRACTION_RANGE (in_value)) {
828 GValue min_out = G_VALUE_INIT;
829 GValue max_out = G_VALUE_INIT;
830 const GValue *range_min = gst_value_get_fraction_range_min (in_value);
831 const GValue *range_max = gst_value_get_fraction_range_max (in_value);
832 _double_value (&min_out, range_min);
833 _double_value (&max_out, range_max);
834 g_value_init (out, GST_TYPE_FRACTION_RANGE);
835 gst_value_set_fraction_range (out, &min_out, &max_out);
836 g_value_unset (&min_out);
837 g_value_unset (&max_out);
838 } else if (GST_VALUE_HOLDS_LIST (in_value)) {
840 g_value_init (out, GST_TYPE_LIST);
841 for (i = 0; i < gst_value_list_get_size (in_value); i++) {
843 GValue tmp = G_VALUE_INIT;
845 entry = gst_value_list_get_value (in_value, i);
846 /* Random list values might not be the right type */
847 if (!_double_value (&tmp, entry))
849 gst_value_list_append_and_take_value (out, &tmp);
861 static GstStructure *
862 _double_structure_field (const GstStructure * in, const gchar * field_name)
865 const GValue *in_value = gst_structure_get_value (in, field_name);
866 GValue tmp = G_VALUE_INIT;
868 if (G_UNLIKELY (in_value == NULL))
869 return gst_structure_copy (in); /* Field doesn't exist, leave it as is */
871 if (!_double_value (&tmp, in_value))
874 out = gst_structure_copy (in);
875 gst_structure_set_value (out, field_name, &tmp);
876 g_value_unset (&tmp);
881 /* Return a copy of the caps with the requested field doubled in value/range */
883 _double_caps_field (const GstCaps * in, const gchar * field_name)
886 GstCaps *out = gst_caps_new_empty ();
888 for (i = 0; i < gst_caps_get_size (in); i++) {
889 const GstStructure *cur = gst_caps_get_structure (in, i);
890 GstCapsFeatures *f = gst_caps_get_features (in, i);
892 GstStructure *res = _double_structure_field (cur, field_name);
894 gst_caps_merge_structure_full (out, res,
895 f ? gst_caps_features_copy (f) : NULL);
901 /* Takes ownership of the input caps */
903 _expand_par_for_half_aspect (GstCaps * in, gboolean vertical_half_aspect)
905 guint mview_flags, mview_flags_mask;
909 out = gst_caps_new_empty ();
911 while (gst_caps_get_size (in) > 0) {
913 GstCapsFeatures *features;
915 features = gst_caps_get_features (in, 0);
917 features = gst_caps_features_copy (features);
919 s = gst_caps_steal_structure (in, 0);
921 if (!gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
922 &mview_flags_mask)) {
923 gst_caps_append_structure_full (out, s, features);
926 /* If the input doesn't care about the half-aspect flag, allow current PAR in either variant */
927 if ((mview_flags_mask & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) == 0) {
928 gst_caps_append_structure_full (out, s, features);
931 if (!gst_structure_has_field (s, "pixel-aspect-ratio")) {
932 /* No par field, dont-care the half-aspect flag */
933 gst_structure_set (s, "multiview-flags",
934 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
935 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
936 mview_flags_mask & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
937 gst_caps_append_structure_full (out, s, features);
941 /* Halve or double PAR base on inputs input specified. */
943 /* Append a copy with the half-aspect flag as-is */
944 tmp = gst_structure_copy (s);
945 out = gst_caps_merge_structure_full (out, tmp,
946 features ? gst_caps_features_copy (features) : NULL);
948 /* and then a copy inverted */
949 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
950 /* Input is half-aspect. Double/halve the PAR, clear the flag */
951 if (vertical_half_aspect)
952 tmp = _halve_structure_field (s, "pixel-aspect-ratio");
954 tmp = _double_structure_field (s, "pixel-aspect-ratio");
956 gst_structure_set (tmp, "multiview-flags",
957 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
958 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
959 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
961 if (vertical_half_aspect)
962 tmp = _double_structure_field (s, "pixel-aspect-ratio");
964 tmp = _halve_structure_field (s, "pixel-aspect-ratio");
966 gst_structure_set (tmp, "multiview-flags",
967 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
968 mview_flags | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
969 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
972 out = gst_caps_merge_structure_full (out, tmp,
973 features ? gst_caps_features_copy (features) : NULL);
975 gst_structure_free (s);
977 gst_caps_features_free (features);
985 /* If input supports top-bottom or row-interleaved, we may halve height to mono frames.
986 * If input supports left-right, checkerboard, quincunx or column-interleaved,
987 * we may halve width to mono frames.
988 * For output of top-bottom or row-interleaved, we may double the mono height
989 * For output of left-right, checkerboard, quincunx or column-interleaved,
990 * we may double the mono width.
991 * In all cases, if input has half-aspect and output does not, we may double the PAR
992 * And if input does *not* have half-aspect flag and output does not, we may halve the PAR
995 _expand_structure (GstVulkanViewConvert * viewconvert,
996 GstCaps * out_caps, GstStructure * structure, GstCapsFeatures * features)
998 GstCaps *expanded_caps, *tmp;
1000 const gchar *default_mview_mode_str = NULL;
1001 guint mview_flags, mview_flags_mask;
1002 const GValue *in_modes;
1005 /* Empty caps to accumulate into */
1006 expanded_caps = gst_caps_new_empty ();
1008 /* First, set defaults if multiview flags are missing */
1009 default_mview_mode_str =
1010 gst_video_multiview_mode_to_caps_string (GST_VIDEO_MULTIVIEW_MODE_MONO);
1012 mview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1013 mview_flags_mask = GST_FLAG_SET_MASK_EXACT;
1015 if (!gst_structure_has_field (structure, "multiview-mode")) {
1016 gst_structure_set (structure,
1017 "multiview-mode", G_TYPE_STRING, default_mview_mode_str, NULL);
1019 if (!gst_structure_has_field (structure, "multiview-flags")) {
1020 gst_structure_set (structure,
1021 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, mview_flags,
1022 mview_flags_mask, NULL);
1024 gst_structure_get_flagset (structure, "multiview-flags",
1025 &mview_flags, &mview_flags_mask);
1028 in_modes = gst_structure_get_value (structure, "multiview-mode");
1029 mono_caps = gst_caps_new_empty ();
1030 if (gst_value_intersect (NULL, in_modes,
1031 gst_video_multiview_get_mono_modes ())) {
1032 GstStructure *new_struct = gst_structure_copy (structure);
1033 gst_structure_set_value (new_struct, "multiview-mode",
1034 gst_video_multiview_get_mono_modes ());
1035 /* Half-aspect makes no sense for mono or unpacked, get rid of it */
1036 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1037 gst_structure_set (new_struct, "multiview-flags",
1038 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1039 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1040 mview_flags_mask & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1042 gst_caps_append_structure_full (mono_caps, new_struct,
1043 features ? gst_caps_features_copy (features) : NULL);
1045 if (gst_value_intersect (NULL, in_modes,
1046 gst_video_multiview_get_unpacked_modes ())) {
1047 GstStructure *new_struct = gst_structure_copy (structure);
1049 gst_structure_set_value (new_struct, "multiview-mode",
1050 gst_video_multiview_get_mono_modes ());
1052 /* Half-aspect makes no sense for mono or unpacked, get rid of it */
1053 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1054 gst_structure_set (new_struct, "multiview-flags",
1055 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1056 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1057 mview_flags_mask & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1059 gst_caps_append_structure_full (mono_caps, new_struct,
1060 features ? gst_caps_features_copy (features) : NULL);
1063 if (gst_value_intersect (NULL, in_modes,
1064 gst_video_multiview_get_doubled_height_modes ())) {
1065 /* Append mono formats with height halved */
1066 GstStructure *new_struct = _halve_structure_field (structure, "height");
1067 gst_structure_set_value (new_struct, "multiview-mode",
1068 gst_video_multiview_get_mono_modes ());
1069 /* Normalise the half-aspect flag away */
1070 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1072 _halve_structure_field (new_struct, "pixel-aspect-ratio");
1073 gst_structure_set (structure, "multiview-flags",
1074 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1075 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1076 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1077 gst_structure_free (new_struct);
1080 mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct,
1081 features ? gst_caps_features_copy (features) : NULL);
1083 if (gst_value_intersect (NULL, in_modes,
1084 gst_video_multiview_get_doubled_width_modes ())) {
1085 /* Append mono formats with width halved */
1086 GstStructure *new_struct = _halve_structure_field (structure, "width");
1087 gst_structure_set_value (new_struct, "multiview-mode",
1088 gst_video_multiview_get_mono_modes ());
1089 /* Normalise the half-aspect flag away */
1090 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1092 _double_structure_field (new_struct, "pixel-aspect-ratio");
1093 gst_structure_set (structure, "multiview-flags",
1094 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1095 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1096 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1097 gst_structure_free (new_struct);
1100 mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct,
1101 features ? gst_caps_features_copy (features) : NULL);
1103 if (gst_value_intersect (NULL, in_modes,
1104 gst_video_multiview_get_doubled_size_modes ())) {
1105 /* Append checkerboard/doubled size formats with width & height halved */
1106 GstStructure *new_struct_w = _halve_structure_field (structure, "width");
1107 GstStructure *new_struct_wh =
1108 _halve_structure_field (new_struct_w, "height");
1109 gst_structure_free (new_struct_w);
1110 gst_structure_set_value (new_struct_wh, "multiview-mode",
1111 gst_video_multiview_get_mono_modes ());
1112 mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct_wh,
1113 features ? gst_caps_features_copy (features) : NULL);
1116 /* Everything is normalised now, unset the flags we can change */
1117 /* Remove the views field, as these are all 'mono' modes
1118 * Need to do this before we expand caps back out to frame packed modes */
1119 for (i = 0; i < gst_caps_get_size (mono_caps); i++) {
1120 GstStructure *s = gst_caps_get_structure (mono_caps, i);
1121 gst_structure_remove_fields (s, "views", NULL);
1122 if (gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
1123 &mview_flags_mask)) {
1124 /* Preserve only the half-aspect and mixed-mono flags, for now.
1125 * The rest we can change */
1127 (GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT |
1128 GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO);
1129 gst_structure_set (s, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1130 mview_flags, mview_flags_mask, NULL);
1134 GST_TRACE_OBJECT (viewconvert,
1135 "Collected single-view caps %" GST_PTR_FORMAT, mono_caps);
1136 /* Put unpacked and mono modes first. We don't care about flags. Clear them */
1137 tmp = gst_caps_copy (mono_caps);
1138 for (i = 0; i < gst_caps_get_size (tmp); i++) {
1139 GstStructure *s = gst_caps_get_structure (tmp, i);
1140 gst_structure_remove_fields (s, "views", NULL);
1141 if (gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
1142 &mview_flags_mask)) {
1143 /* We can change any flags for mono modes - half-aspect and mixed-mono have no meaning */
1144 mview_flags_mask = 0;
1145 gst_structure_set (s, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1146 mview_flags, mview_flags_mask, NULL);
1149 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1151 /* Unpacked output modes have 2 views, for now */
1152 tmp = gst_caps_copy (mono_caps);
1153 gst_caps_set_value (tmp, "multiview-mode",
1154 gst_video_multiview_get_unpacked_modes ());
1155 for (i = 0; i < gst_caps_get_size (tmp); i++) {
1156 GstStructure *s = gst_caps_get_structure (tmp, i);
1157 gst_structure_set (s, "views", G_TYPE_INT, 2, NULL);
1158 if (gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
1159 &mview_flags_mask)) {
1160 /* We can change any flags for unpacked modes - half-aspect and mixed-mono have no meaning */
1161 mview_flags_mask = 0;
1162 gst_structure_set (s, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1163 mview_flags, mview_flags_mask, NULL);
1166 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1168 /* Double height output modes */
1169 tmp = _double_caps_field (mono_caps, "height");
1170 gst_caps_set_value (tmp, "multiview-mode",
1171 gst_video_multiview_get_doubled_height_modes ());
1172 tmp = _expand_par_for_half_aspect (tmp, TRUE);
1174 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1176 /* Double width output modes */
1177 tmp = _double_caps_field (mono_caps, "width");
1178 gst_caps_set_value (tmp, "multiview-mode",
1179 gst_video_multiview_get_doubled_width_modes ());
1180 tmp = _expand_par_for_half_aspect (tmp, FALSE);
1182 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1184 /* Double size output modes */
1186 GstCaps *tmp_w = _double_caps_field (mono_caps, "width");
1187 tmp = _double_caps_field (tmp_w, "height");
1188 gst_caps_unref (tmp_w);
1189 gst_caps_set_value (tmp, "multiview-mode",
1190 gst_video_multiview_get_doubled_size_modes ());
1191 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1194 /* We're done with the mono caps now */
1195 gst_caps_unref (mono_caps);
1197 GST_TRACE_OBJECT (viewconvert,
1198 "expanded transform caps now %" GST_PTR_FORMAT, expanded_caps);
1200 if (gst_caps_is_empty (expanded_caps)) {
1201 gst_caps_unref (expanded_caps);
1204 /* Really, we can rescale - so at this point we can append full-range
1205 * height/width/PAR as an unpreferred final option. */
1206 /* tmp = gst_caps_copy (expanded_caps);
1207 gst_caps_set_simple (tmp, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1208 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
1210 out_caps = gst_caps_merge (out_caps, expanded_caps);
1211 /* out_caps = gst_caps_merge (out_caps, tmp);*/
1216 _intersect_with_mview_mode (GstCaps * caps,
1217 GstVideoMultiviewMode mode, GstVideoMultiviewFlags flags)
1219 GstCaps *filter, *result;
1220 const gchar *caps_str;
1222 caps_str = gst_video_multiview_mode_to_caps_string (mode);
1224 filter = gst_caps_new_simple ("video/x-raw",
1225 "multiview-mode", G_TYPE_STRING,
1226 caps_str, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags,
1227 GST_FLAG_SET_MASK_EXACT, NULL);
1229 if (mode == GST_VIDEO_MULTIVIEW_MODE_SEPARATED ||
1230 mode == GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME)
1231 gst_caps_set_simple (filter, "views", G_TYPE_INT, 2, NULL);
1232 gst_caps_set_features (filter, 0, gst_caps_features_new_any ());
1234 GST_DEBUG ("Intersecting target caps %" GST_PTR_FORMAT
1235 " with caps %" GST_PTR_FORMAT, caps, filter);
1237 result = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1238 gst_caps_unref (filter);
1243 _intersect_with_mview_modes (GstCaps * caps, const GValue * modes)
1245 GstCaps *filter, *result;
1247 filter = gst_caps_new_empty_simple ("video/x-raw");
1249 gst_caps_set_value (filter, "multiview-mode", modes);
1250 gst_caps_set_features (filter, 0, gst_caps_features_new_any ());
1252 GST_DEBUG ("Intersecting target caps %" GST_PTR_FORMAT
1253 " with caps %" GST_PTR_FORMAT, caps, filter);
1255 result = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1256 gst_caps_unref (filter);
1261 gst_vulkan_view_convert_transform_caps (GstBaseTransform * bt,
1262 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1264 GstVulkanViewConvert *viewconvert = GST_VULKAN_VIEW_CONVERT (bt);
1265 GstCaps *base_caps =
1266 gst_static_pad_template_get_caps (&gst_vulkan_sink_template);
1267 GstCaps *out_caps, *tmp_caps;
1270 GST_DEBUG_OBJECT (viewconvert, "Direction %s "
1271 "input caps %" GST_PTR_FORMAT " filter %" GST_PTR_FORMAT,
1272 direction == GST_PAD_SINK ? "sink" : "src", caps, filter);
1274 /* We can only process VulkanImage caps, start from that */
1275 caps = gst_caps_intersect (caps, base_caps);
1276 gst_caps_unref (base_caps);
1278 /* Change input/output to the formats we can convert to/from,
1279 * but keep the original caps at the start - we will always prefer
1281 if (direction == GST_PAD_SINK) {
1282 out_caps = gst_caps_copy (caps);
1283 if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1284 GstVideoMultiviewMode mode = viewconvert->input_mode_override;
1285 GstVideoMultiviewFlags flags = viewconvert->input_flags_override;
1287 const gchar *caps_str = gst_video_multiview_mode_to_caps_string (mode);
1288 /* Coerce the input caps before transforming, so the sizes come out right */
1289 gst_caps_set_simple (out_caps, "multiview-mode", G_TYPE_STRING,
1290 caps_str, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags,
1291 GST_FLAG_SET_MASK_EXACT, NULL);
1294 out_caps = gst_caps_new_empty ();
1297 for (i = 0; i < gst_caps_get_size (caps); i++) {
1298 GstStructure *structure = gst_caps_get_structure (caps, i);
1299 GstCapsFeatures *features = gst_caps_get_features (caps, i);
1300 out_caps = _expand_structure (viewconvert, out_caps, structure, features);
1303 if (gst_caps_is_empty (out_caps))
1306 /* If we have an output mode override, limit things to that */
1307 if (direction == GST_PAD_SINK &&
1308 viewconvert->output_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1310 tmp_caps = _intersect_with_mview_mode (out_caps,
1311 viewconvert->output_mode_override, viewconvert->output_flags_override);
1313 gst_caps_unref (out_caps);
1314 out_caps = tmp_caps;
1315 } else if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1316 /* Prepend a copy of our preferred input caps in case the peer
1317 * can handle them */
1318 tmp_caps = _intersect_with_mview_mode (out_caps,
1319 viewconvert->input_mode_override, viewconvert->input_flags_override);
1320 out_caps = gst_caps_merge (out_caps, tmp_caps);
1322 if (direction == GST_PAD_SRC) {
1324 /* When generating input caps, we also need a copy of the mono caps
1325 * without multiview-mode or flags for backwards compat, at the end */
1326 tmp_caps = _intersect_with_mview_mode (caps,
1327 GST_VIDEO_MULTIVIEW_MODE_MONO, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
1328 if (!gst_caps_is_empty (tmp_caps)) {
1329 s = gst_caps_get_structure (tmp_caps, 0);
1330 gst_structure_remove_fields (s, "multiview-mode", "multiview-flags",
1332 out_caps = gst_caps_merge (out_caps, tmp_caps);
1334 gst_caps_unref (tmp_caps);
1337 gst_caps_unref (caps);
1339 GST_DEBUG_OBJECT (viewconvert, "Have caps %" GST_PTR_FORMAT
1340 " filtering with caps %" GST_PTR_FORMAT, out_caps, filter);
1344 gst_caps_intersect_full (filter, out_caps, GST_CAPS_INTERSECT_FIRST);
1345 gst_caps_unref (out_caps);
1349 GST_DEBUG_OBJECT (viewconvert, "Returning caps %" GST_PTR_FORMAT, out_caps);
1354 fixate_size (GstVulkanViewConvert * viewconvert,
1355 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1357 GstStructure *ins, *outs;
1358 const GValue *from_par, *to_par;
1359 GValue fpar = { 0, }, tpar = {
1362 othercaps = gst_caps_make_writable (othercaps);
1363 othercaps = gst_caps_truncate (othercaps);
1365 GST_DEBUG_OBJECT (viewconvert, "trying to fixate othercaps %" GST_PTR_FORMAT
1366 " based on caps %" GST_PTR_FORMAT, othercaps, caps);
1368 ins = gst_caps_get_structure (caps, 0);
1369 outs = gst_caps_get_structure (othercaps, 0);
1371 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1372 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1374 /* If we're fixating from the sinkpad we always set the PAR and
1375 * assume that missing PAR on the sinkpad means 1/1 and
1376 * missing PAR on the srcpad means undefined
1378 if (direction == GST_PAD_SINK) {
1380 g_value_init (&fpar, GST_TYPE_FRACTION);
1381 gst_value_set_fraction (&fpar, 1, 1);
1385 g_value_init (&tpar, GST_TYPE_FRACTION);
1386 gst_value_set_fraction (&tpar, 1, 1);
1391 g_value_init (&tpar, GST_TYPE_FRACTION);
1392 gst_value_set_fraction (&tpar, 1, 1);
1395 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1399 g_value_init (&fpar, GST_TYPE_FRACTION);
1400 gst_value_set_fraction (&fpar, 1, 1);
1405 /* we have both PAR but they might not be fixated */
1407 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1409 gint from_dar_n, from_dar_d;
1412 /* from_par should be fixed */
1413 g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
1415 from_par_n = gst_value_get_fraction_numerator (from_par);
1416 from_par_d = gst_value_get_fraction_denominator (from_par);
1418 gst_structure_get_int (ins, "width", &from_w);
1419 gst_structure_get_int (ins, "height", &from_h);
1421 gst_structure_get_int (outs, "width", &w);
1422 gst_structure_get_int (outs, "height", &h);
1424 /* if both width and height are already fixed, we can't do anything
1425 * about it anymore */
1427 GST_DEBUG_OBJECT (viewconvert,
1428 "dimensions already set to %dx%d, not fixating", w, h);
1429 if (!gst_value_is_fixed (to_par)) {
1430 GST_DEBUG_OBJECT (viewconvert, "fixating to_par to %dx%d", 1, 1);
1431 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1432 gst_structure_fixate_field_nearest_fraction (outs,
1433 "pixel-aspect-ratio", 1, 1);
1438 /* Calculate input DAR */
1439 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1440 &from_dar_n, &from_dar_d)) {
1441 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1442 ("Error calculating the output scaled size - integer overflow"));
1446 GST_DEBUG_OBJECT (viewconvert, "Input DAR is %d/%d", from_dar_n,
1449 /* If either width or height are fixed there's not much we
1450 * can do either except choosing a height or width and PAR
1451 * that matches the DAR as good as possible
1456 GST_DEBUG_OBJECT (viewconvert, "height is fixed (%d)", h);
1458 if (!gst_value_is_fixed (to_par)) {
1459 /* (shortcut) copy-paste (??) of videoscale seems to aim for 1/1,
1460 * so let's make it so ...
1461 * especially if following code assumes fixed */
1462 GST_DEBUG_OBJECT (viewconvert, "fixating to_par to 1x1");
1463 gst_structure_fixate_field_nearest_fraction (outs,
1464 "pixel-aspect-ratio", 1, 1);
1465 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1468 /* PAR is fixed, choose the height that is nearest to the
1469 * height with the same DAR */
1470 to_par_n = gst_value_get_fraction_numerator (to_par);
1471 to_par_d = gst_value_get_fraction_denominator (to_par);
1473 GST_DEBUG_OBJECT (viewconvert, "PAR is fixed %d/%d", to_par_n, to_par_d);
1475 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1476 to_par_n, &num, &den)) {
1477 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1478 ("Error calculating the output scaled size - integer overflow"));
1482 w = (guint) gst_util_uint64_scale_int (h, num, den);
1483 gst_structure_fixate_field_nearest_int (outs, "width", w);
1489 GST_DEBUG_OBJECT (viewconvert, "width is fixed (%d)", w);
1491 if (!gst_value_is_fixed (to_par)) {
1492 /* (shortcut) copy-paste (??) of videoscale seems to aim for 1/1,
1493 * so let's make it so ...
1494 * especially if following code assumes fixed */
1495 GST_DEBUG_OBJECT (viewconvert, "fixating to_par to 1x1");
1496 gst_structure_fixate_field_nearest_fraction (outs,
1497 "pixel-aspect-ratio", 1, 1);
1498 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1501 /* PAR is fixed, choose the height that is nearest to the
1502 * height with the same DAR */
1503 to_par_n = gst_value_get_fraction_numerator (to_par);
1504 to_par_d = gst_value_get_fraction_denominator (to_par);
1506 GST_DEBUG_OBJECT (viewconvert, "PAR is fixed %d/%d", to_par_n, to_par_d);
1508 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1509 to_par_n, &num, &den)) {
1510 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1511 ("Error calculating the output scaled size - integer overflow"));
1515 h = (guint) gst_util_uint64_scale_int (w, den, num);
1516 gst_structure_fixate_field_nearest_int (outs, "height", h);
1519 } else if (gst_value_is_fixed (to_par)) {
1521 gint set_h, set_w, f_h, f_w;
1523 to_par_n = gst_value_get_fraction_numerator (to_par);
1524 to_par_d = gst_value_get_fraction_denominator (to_par);
1526 /* Calculate scale factor for the PAR change */
1527 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1528 to_par_d, &num, &den)) {
1529 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1530 ("Error calculating the output scaled size - integer overflow"));
1534 /* Try to keep the input height */
1535 tmp = gst_structure_copy (outs);
1536 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1537 gst_structure_get_int (tmp, "height", &set_h);
1539 /* This might have failed but try to scale the width
1540 * to keep the DAR nonetheless */
1541 w = (guint) gst_util_uint64_scale_int (set_h, num, den);
1542 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1543 gst_structure_get_int (tmp, "width", &set_w);
1544 gst_structure_free (tmp);
1546 /* We kept the DAR and the height is nearest to the original height */
1548 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1549 G_TYPE_INT, set_h, NULL);
1556 /* If the former failed, try to keep the input width at least */
1557 tmp = gst_structure_copy (outs);
1558 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1559 gst_structure_get_int (tmp, "width", &set_w);
1561 /* This might have failed but try to scale the width
1562 * to keep the DAR nonetheless */
1563 h = (guint) gst_util_uint64_scale_int (set_w, den, num);
1564 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1565 gst_structure_get_int (tmp, "height", &set_h);
1566 gst_structure_free (tmp);
1568 /* We kept the DAR and the width is nearest to the original width */
1570 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1571 G_TYPE_INT, set_h, NULL);
1575 /* If all this failed, keep the height that was nearest to the original
1576 * height and the nearest possible width. This changes the DAR but
1577 * there's not much else to do here.
1579 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1584 gint set_h, set_w, set_par_n, set_par_d, tmp2;
1586 /* width, height and PAR are not fixed */
1588 /* First try to keep the height and width as good as possible
1590 tmp = gst_structure_copy (outs);
1591 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1592 gst_structure_get_int (tmp, "height", &set_h);
1593 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1594 gst_structure_get_int (tmp, "width", &set_w);
1596 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1597 &to_par_n, &to_par_d)) {
1598 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1599 ("Error calculating the output scaled size - integer overflow"));
1600 gst_structure_free (tmp);
1604 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1605 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1606 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1607 to_par_n, to_par_d);
1608 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1610 gst_structure_free (tmp);
1612 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1613 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1614 G_TYPE_INT, set_h, NULL);
1616 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1617 set_par_n != set_par_d)
1618 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1619 set_par_n, set_par_d, NULL);
1623 /* Otherwise try to scale width to keep the DAR with the set
1625 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1626 set_par_n, &num, &den)) {
1627 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1628 ("Error calculating the output scaled size - integer overflow"));
1632 w = (guint) gst_util_uint64_scale_int (set_h, num, den);
1633 tmp = gst_structure_copy (outs);
1634 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1635 gst_structure_get_int (tmp, "width", &tmp2);
1636 gst_structure_free (tmp);
1639 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1640 G_TYPE_INT, set_h, NULL);
1641 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1642 set_par_n != set_par_d)
1643 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1644 set_par_n, set_par_d, NULL);
1648 /* ... or try the same with the height */
1649 h = (guint) gst_util_uint64_scale_int (set_w, den, num);
1650 tmp = gst_structure_copy (outs);
1651 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1652 gst_structure_get_int (tmp, "height", &tmp2);
1653 gst_structure_free (tmp);
1656 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1657 G_TYPE_INT, tmp2, NULL);
1658 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1659 set_par_n != set_par_d)
1660 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1661 set_par_n, set_par_d, NULL);
1665 /* If all fails we can't keep the DAR and take the nearest values
1666 * for everything from the first try */
1667 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1668 G_TYPE_INT, set_h, NULL);
1669 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1670 set_par_n != set_par_d)
1671 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1672 set_par_n, set_par_d, NULL);
1677 othercaps = gst_caps_fixate (othercaps);
1679 GST_DEBUG_OBJECT (viewconvert, "fixated othercaps to %" GST_PTR_FORMAT,
1682 if (from_par == &fpar)
1683 g_value_unset (&fpar);
1684 if (to_par == &tpar)
1685 g_value_unset (&tpar);
1691 gst_vulkan_view_convert_fixate_caps (GstBaseTransform * bt,
1692 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1694 GstVulkanViewConvert *viewconvert = GST_VULKAN_VIEW_CONVERT (bt);
1695 GstVideoMultiviewMode mode = viewconvert->output_mode_override;
1696 GstVideoMultiviewFlags flags = viewconvert->output_flags_override;
1699 othercaps = gst_caps_make_writable (othercaps);
1700 GST_LOG_OBJECT (viewconvert, "dir %s fixating %" GST_PTR_FORMAT
1701 " against caps %" GST_PTR_FORMAT,
1702 direction == GST_PAD_SINK ? "sink" : "src", othercaps, caps);
1704 if (direction == GST_PAD_SINK) {
1705 if (mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1706 /* We have a requested output mode and are fixating source caps, try and enforce it */
1707 GST_DEBUG_OBJECT (bt, "fixating multiview mode using the configured "
1708 "output override mode 0x%x and flags 0x%x", mode, flags);
1709 tmp = _intersect_with_mview_mode (othercaps, mode, flags);
1710 gst_caps_unref (othercaps);
1713 /* See if we can do passthrough */
1716 if (gst_video_info_from_caps (&info, caps)) {
1717 GstVideoMultiviewMode mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&info);
1718 GstVideoMultiviewFlags flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&info);
1720 if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1721 mode = viewconvert->input_mode_override;
1722 flags = viewconvert->input_flags_override;
1725 tmp = _intersect_with_mview_mode (othercaps, mode, flags);
1726 if (gst_caps_is_empty (tmp)) {
1727 /* Nope, we can't pass our input caps downstream */
1728 gst_caps_unref (tmp);
1730 GST_DEBUG_OBJECT (bt, "can configure a passthrough multiview mode "
1731 "using the input override mode 0x%x and flags 0x%x", mode, flags);
1732 gst_caps_unref (othercaps);
1738 /* Prefer an unpacked mode for output */
1740 _intersect_with_mview_modes (othercaps,
1741 gst_video_multiview_get_unpacked_modes ());
1742 if (!gst_caps_is_empty (tmp)) {
1743 GST_DEBUG_OBJECT (bt, "preferring an unpacked multiview mode");
1744 gst_caps_unref (othercaps);
1747 gst_caps_unref (tmp);
1750 } else if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1751 /* See if we can coerce the caps into matching input mode/flags,
1752 * in case it doesn't care at all, but allow it not to too */
1753 mode = viewconvert->input_mode_override;
1754 flags = viewconvert->input_flags_override;
1755 tmp = _intersect_with_mview_mode (othercaps, mode, flags);
1756 if (gst_caps_is_empty (tmp)) {
1757 /* Nope, we can pass our input caps downstream */
1758 gst_caps_unref (tmp);
1760 GST_DEBUG_OBJECT (bt, "can configure a passthrough multiview mode "
1761 "using the input override mode 0x%x and flags 0x%x", mode, flags);
1762 gst_caps_unref (othercaps);
1768 othercaps = fixate_size (viewconvert, direction, caps, othercaps);
1769 othercaps = gst_caps_fixate (othercaps);
1771 GST_DEBUG_OBJECT (viewconvert, "dir %s fixated to %" GST_PTR_FORMAT
1772 " against caps %" GST_PTR_FORMAT,
1773 direction == GST_PAD_SINK ? "sink" : "src", othercaps, caps);
1778 create_descriptor_set_layout (GstVulkanViewConvert * conv, GError ** error)
1780 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
1781 VkDescriptorSetLayoutBinding bindings[GST_VIDEO_MAX_PLANES * 2 + 1] =
1783 VkDescriptorSetLayoutCreateInfo layout_info;
1784 VkDescriptorSetLayout descriptor_set_layout;
1785 int descriptor_n = 0;
1790 bindings[descriptor_n++] = (VkDescriptorSetLayoutBinding) {
1792 .descriptorCount = 1,
1793 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1794 .pImmutableSamplers = NULL,
1795 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT
1797 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vfilter->in_info) * 2; i++) {
1798 bindings[descriptor_n++] = (VkDescriptorSetLayoutBinding) {
1800 .descriptorCount = 1,
1801 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1802 .pImmutableSamplers = NULL,
1803 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1807 layout_info = (VkDescriptorSetLayoutCreateInfo) {
1808 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1810 .bindingCount = descriptor_n,
1811 .pBindings = bindings
1816 vkCreateDescriptorSetLayout (vfilter->device->device, &layout_info,
1817 NULL, &descriptor_set_layout);
1818 if (gst_vulkan_error_to_g_error (err, error,
1819 "vkCreateDescriptorSetLayout") < 0) {
1823 conv->quad->descriptor_set_layout =
1824 gst_vulkan_handle_new_wrapped (vfilter->device,
1825 GST_VULKAN_HANDLE_TYPE_DESCRIPTOR_SET_LAYOUT,
1826 (GstVulkanHandleTypedef) descriptor_set_layout,
1827 gst_vulkan_handle_free_descriptor_set_layout, NULL);
1833 gst_vulkan_view_convert_start (GstBaseTransform * bt)
1835 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (bt);
1836 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
1837 GstVulkanHandle *vert, *frag;
1839 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->start (bt))
1842 conv->quad = gst_vulkan_full_screen_quad_new (vfilter->queue);
1845 _vk_create_shader (vfilter->device, identity_vert, identity_vert_size,
1850 _vk_create_shader (vfilter->device, view_convert_frag,
1851 view_convert_frag_size, NULL))) {
1852 gst_vulkan_handle_unref (vert);
1856 if (!gst_vulkan_full_screen_quad_set_shaders (conv->quad, vert, frag)) {
1857 gst_vulkan_handle_unref (vert);
1858 gst_vulkan_handle_unref (frag);
1861 gst_vulkan_handle_unref (vert);
1862 gst_vulkan_handle_unref (frag);
1868 gst_vulkan_view_convert_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
1871 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (bt);
1872 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
1874 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->set_caps (bt, in_caps,
1878 if (!gst_vulkan_full_screen_quad_set_info (conv->quad, &vfilter->in_info,
1879 &vfilter->out_info))
1886 gst_vulkan_view_convert_stop (GstBaseTransform * bt)
1888 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
1890 gst_clear_object (&conv->quad);
1892 gst_memory_unref (conv->uniform);
1893 conv->uniform = NULL;
1895 return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
1898 static GstFlowReturn
1899 gst_vulkan_view_convert_transform (GstBaseTransform * bt, GstBuffer * inbuf,
1902 GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (bt);
1903 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
1904 GstVulkanImageView *in_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
1905 GstVulkanImageView *out_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
1906 GstVulkanCommandBuffer *cmd_buf = NULL;
1907 GstVulkanFence *fence = NULL;
1908 GError *error = NULL;
1912 if (!gst_vulkan_full_screen_quad_set_input_buffer (conv->quad, inbuf, &error))
1914 if (!gst_vulkan_full_screen_quad_set_output_buffer (conv->quad, outbuf,
1918 fence = gst_vulkan_device_create_fence (vfilter->device, &error);
1922 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->in_info); i++) {
1923 GstMemory *img_mem = gst_buffer_peek_memory (inbuf, i);
1924 if (!gst_is_vulkan_image_memory (img_mem)) {
1925 g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
1926 "Input memory must be a GstVulkanImageMemory");
1930 get_or_create_image_view ((GstVulkanImageMemory *) img_mem);
1931 gst_vulkan_trash_list_add (conv->quad->trash_list,
1932 gst_vulkan_trash_list_acquire (conv->quad->trash_list, fence,
1933 gst_vulkan_trash_mini_object_unref,
1934 (GstMiniObject *) in_img_views[i]));
1936 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->out_info); i++) {
1937 GstMemory *mem = gst_buffer_peek_memory (outbuf, i);
1938 if (!gst_is_vulkan_image_memory (mem)) {
1939 g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
1940 "Output memory must be a GstVulkanImageMemory");
1943 out_img_views[i] = get_or_create_image_view ((GstVulkanImageMemory *) mem);
1944 gst_vulkan_trash_list_add (conv->quad->trash_list,
1945 gst_vulkan_trash_list_acquire (conv->quad->trash_list, fence,
1946 gst_vulkan_trash_mini_object_unref,
1947 (GstMiniObject *) out_img_views[i]));
1951 GstMemory *uniforms = get_uniforms (conv, in_img_views, out_img_views);
1952 if (!gst_vulkan_full_screen_quad_set_uniform_buffer (conv->quad, uniforms,
1955 gst_memory_unref (uniforms);
1958 if (!conv->quad->descriptor_set_layout)
1959 if (!create_descriptor_set_layout (conv, &error))
1962 if (!gst_vulkan_full_screen_quad_prepare_draw (conv->quad, fence, &error))
1966 gst_vulkan_command_pool_create (conv->quad->cmd_pool, &error)))
1970 VkCommandBufferBeginInfo cmd_buf_info = { 0, };
1973 cmd_buf_info = (VkCommandBufferBeginInfo) {
1974 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
1976 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
1977 .pInheritanceInfo = NULL
1981 gst_vulkan_command_buffer_lock (cmd_buf);
1982 err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
1983 if (gst_vulkan_error_to_g_error (err, &error, "vkBeginCommandBuffer") < 0)
1987 update_descriptor_set (conv, in_img_views);
1988 if (!gst_vulkan_full_screen_quad_fill_command_buffer (conv->quad, cmd_buf,
1992 err = vkEndCommandBuffer (cmd_buf->cmd);
1993 gst_vulkan_command_buffer_unlock (cmd_buf);
1994 if (gst_vulkan_error_to_g_error (err, &error, "vkEndCommandBuffer") < 0)
1997 if (!gst_vulkan_full_screen_quad_submit (conv->quad, cmd_buf, fence, &error))
2000 gst_vulkan_fence_unref (fence);
2006 gst_vulkan_command_buffer_unlock (cmd_buf);
2007 gst_vulkan_command_buffer_unref (cmd_buf);
2011 gst_clear_mini_object ((GstMiniObject **) & fence);
2013 GST_ELEMENT_ERROR (bt, LIBRARY, FAILED, ("%s", error->message), (NULL));
2014 g_clear_error (&error);
2015 return GST_FLOW_ERROR;