cudaconvert, cudascale: Port to GstCudaBaseCovert baseclass
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / nvcodec / gstcudaconvertscale.c
1 /* GStreamer
2  * Copyright (C) 2020 Thibault Saunier <tsaunier@igalia.com>
3  * Copyright (C) 2022 Seungha Yang <seungha@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 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <gst/cuda/gstcudautils.h>
26 #include "gstcudaconvertscale.h"
27 #include "cuda-converter.h"
28
29 GST_DEBUG_CATEGORY_STATIC (gst_cuda_base_convert_debug);
30 #define GST_CAT_DEFAULT gst_cuda_base_convert_debug
31
32 #define GST_CUDA_CONVET_FORMATS \
33     "{ I420, YV12, NV12, NV21, P010_10LE, P016_LE, I420_10LE, Y444, Y444_16LE, " \
34     "BGRA, RGBA, RGBx, BGRx, ARGB, ABGR, RGB, BGR, BGR10A2_LE, RGB10A2_LE, " \
35     "Y42B, I422_10LE, I422_12LE }"
36
37 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
38     GST_PAD_SINK,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
41         (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_CONVET_FORMATS))
42     );
43
44 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
45     GST_PAD_SRC,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
48         (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_CONVET_FORMATS))
49     );
50
51 struct _GstCudaBaseConvert
52 {
53   GstCudaBaseTransform parent;
54
55   GstCudaConverter *converter;
56
57   gint borders_h;
58   gint borders_w;
59   gboolean add_borders;
60 };
61
62 static void gst_cuda_base_convert_dispose (GObject * object);
63 static GstCaps *gst_cuda_base_convert_transform_caps (GstBaseTransform * trans,
64     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
65 static GstCaps *gst_cuda_base_convert_fixate_caps (GstBaseTransform * trans,
66     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
67 static gboolean
68 gst_cuda_base_convert_propose_allocation (GstBaseTransform * trans,
69     GstQuery * decide_query, GstQuery * query);
70 static gboolean gst_cuda_base_convert_decide_allocation (GstBaseTransform *
71     trans, GstQuery * query);
72 static gboolean gst_cuda_base_convert_filter_meta (GstBaseTransform * trans,
73     GstQuery * query, GType api, const GstStructure * params);
74 static GstFlowReturn gst_cuda_base_convert_transform (GstBaseTransform * trans,
75     GstBuffer * inbuf, GstBuffer * outbuf);
76 static gboolean gst_cuda_base_convert_set_info (GstCudaBaseTransform * btrans,
77     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
78     GstVideoInfo * out_info);
79
80 /**
81  * GstCudaBaseConvert:
82  *
83  * A baseclass implementation for cuda convert elements
84  *
85  * Since: 1.22
86  */
87 #define gst_cuda_base_convert_parent_class parent_class
88 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstCudaBaseConvert,
89     gst_cuda_base_convert, GST_TYPE_CUDA_BASE_TRANSFORM,
90     GST_DEBUG_CATEGORY_INIT (gst_cuda_base_convert_debug,
91         "cudaconvertscale", 0, "CUDA Base Filter"));
92
93 static void
94 gst_cuda_base_convert_class_init (GstCudaBaseConvertClass * klass)
95 {
96   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
98   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
99   GstCudaBaseTransformClass *btrans_class =
100       GST_CUDA_BASE_TRANSFORM_CLASS (klass);
101
102   gobject_class->dispose = gst_cuda_base_convert_dispose;
103
104   gst_element_class_add_static_pad_template (element_class, &sink_template);
105   gst_element_class_add_static_pad_template (element_class, &src_template);
106
107   trans_class->passthrough_on_same_caps = TRUE;
108
109   trans_class->transform_caps =
110       GST_DEBUG_FUNCPTR (gst_cuda_base_convert_transform_caps);
111   trans_class->fixate_caps =
112       GST_DEBUG_FUNCPTR (gst_cuda_base_convert_fixate_caps);
113   trans_class->propose_allocation =
114       GST_DEBUG_FUNCPTR (gst_cuda_base_convert_propose_allocation);
115   trans_class->decide_allocation =
116       GST_DEBUG_FUNCPTR (gst_cuda_base_convert_decide_allocation);
117   trans_class->filter_meta =
118       GST_DEBUG_FUNCPTR (gst_cuda_base_convert_filter_meta);
119   trans_class->transform = GST_DEBUG_FUNCPTR (gst_cuda_base_convert_transform);
120
121   btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_cuda_base_convert_set_info);
122
123   gst_type_mark_as_plugin_api (GST_TYPE_CUDA_BASE_CONVERT, 0);
124 }
125
126 static void
127 gst_cuda_base_convert_init (GstCudaBaseConvert * self)
128 {
129 }
130
131 static void
132 gst_cuda_base_convert_dispose (GObject * object)
133 {
134   GstCudaBaseConvert *self = GST_CUDA_BASE_CONVERT (object);
135
136   if (self->converter) {
137     gst_cuda_converter_free (self->converter);
138     self->converter = NULL;
139   }
140
141   G_OBJECT_CLASS (parent_class)->dispose (object);
142 }
143
144 static GstCaps *
145 gst_cuda_base_convert_caps_remove_format_info (GstCaps * caps)
146 {
147   GstStructure *st;
148   GstCapsFeatures *f;
149   gint i, n;
150   GstCaps *res;
151   GstCapsFeatures *feature =
152       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY);
153
154   res = gst_caps_new_empty ();
155
156   n = gst_caps_get_size (caps);
157   for (i = 0; i < n; i++) {
158     st = gst_caps_get_structure (caps, i);
159     f = gst_caps_get_features (caps, i);
160
161     /* If this is already expressed by the existing caps
162      * skip this structure */
163     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
164       continue;
165
166     st = gst_structure_copy (st);
167     /* Only remove format info for the cases when we can actually convert */
168     if (!gst_caps_features_is_any (f)
169         && gst_caps_features_is_equal (f, feature)) {
170       gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
171           NULL);
172     }
173
174     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
175   }
176   gst_caps_features_free (feature);
177
178   return res;
179 }
180
181 static GstCaps *
182 gst_cuda_base_convert_caps_rangify_size_info (GstCaps * caps)
183 {
184   GstStructure *st;
185   GstCapsFeatures *f;
186   gint i, n;
187   GstCaps *res;
188   GstCapsFeatures *feature =
189       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY);
190
191   res = gst_caps_new_empty ();
192
193   n = gst_caps_get_size (caps);
194   for (i = 0; i < n; i++) {
195     st = gst_caps_get_structure (caps, i);
196     f = gst_caps_get_features (caps, i);
197
198     /* If this is already expressed by the existing caps
199      * skip this structure */
200     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
201       continue;
202
203     st = gst_structure_copy (st);
204     /* Only remove format info for the cases when we can actually convert */
205     if (!gst_caps_features_is_any (f)
206         && gst_caps_features_is_equal (f, feature)) {
207       gst_structure_set (st, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
208           "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
209
210       /* if pixel aspect ratio, make a range of it */
211       if (gst_structure_has_field (st, "pixel-aspect-ratio")) {
212         gst_structure_set (st, "pixel-aspect-ratio",
213             GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
214       }
215     }
216
217     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
218   }
219   gst_caps_features_free (feature);
220
221   return res;
222 }
223
224 static GstCaps *
225 gst_cuda_base_convert_caps_remove_format_and_rangify_size_info (GstCaps * caps)
226 {
227   GstStructure *st;
228   GstCapsFeatures *f;
229   gint i, n;
230   GstCaps *res;
231   GstCapsFeatures *feature =
232       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY);
233
234   res = gst_caps_new_empty ();
235
236   n = gst_caps_get_size (caps);
237   for (i = 0; i < n; i++) {
238     st = gst_caps_get_structure (caps, i);
239     f = gst_caps_get_features (caps, i);
240
241     /* If this is already expressed by the existing caps
242      * skip this structure */
243     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
244       continue;
245
246     st = gst_structure_copy (st);
247     /* Only remove format info for the cases when we can actually convert */
248     if (!gst_caps_features_is_any (f)
249         && gst_caps_features_is_equal (f, feature)) {
250       gst_structure_set (st, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
251           "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
252       /* if pixel aspect ratio, make a range of it */
253       if (gst_structure_has_field (st, "pixel-aspect-ratio")) {
254         gst_structure_set (st, "pixel-aspect-ratio",
255             GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
256       }
257       gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
258           NULL);
259     }
260
261     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
262   }
263   gst_caps_features_free (feature);
264
265   return res;
266 }
267
268 static GstCaps *
269 gst_cuda_base_convert_transform_caps (GstBaseTransform *
270     trans, GstPadDirection direction, GstCaps * caps, GstCaps * filter)
271 {
272   GstCaps *tmp, *tmp2;
273   GstCaps *result;
274
275   /* Get all possible caps that we can transform to */
276   tmp = gst_cuda_base_convert_caps_remove_format_and_rangify_size_info (caps);
277
278   if (filter) {
279     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
280     gst_caps_unref (tmp);
281     tmp = tmp2;
282   }
283
284   result = tmp;
285
286   GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
287       GST_PTR_FORMAT, caps, result);
288
289   return result;
290 }
291
292 /*
293  * This is an incomplete matrix of in formats and a score for the prefered output
294  * format.
295  *
296  *         out: RGB24   RGB16  ARGB  AYUV  YUV444  YUV422 YUV420 YUV411 YUV410  PAL  GRAY
297  *  in
298  * RGB24          0      2       1     2     2       3      4      5      6      7    8
299  * RGB16          1      0       1     2     2       3      4      5      6      7    8
300  * ARGB           2      3       0     1     4       5      6      7      8      9    10
301  * AYUV           3      4       1     0     2       5      6      7      8      9    10
302  * YUV444         2      4       3     1     0       5      6      7      8      9    10
303  * YUV422         3      5       4     2     1       0      6      7      8      9    10
304  * YUV420         4      6       5     3     2       1      0      7      8      9    10
305  * YUV411         4      6       5     3     2       1      7      0      8      9    10
306  * YUV410         6      8       7     5     4       3      2      1      0      9    10
307  * PAL            1      3       2     6     4       6      7      8      9      0    10
308  * GRAY           1      4       3     2     1       5      6      7      8      9    0
309  *
310  * PAL or GRAY are never prefered, if we can we would convert to PAL instead
311  * of GRAY, though
312  * less subsampling is prefered and if any, preferably horizontal
313  * We would like to keep the alpha, even if we would need to to colorspace conversion
314  * or lose depth.
315  */
316 #define SCORE_FORMAT_CHANGE       1
317 #define SCORE_DEPTH_CHANGE        1
318 #define SCORE_ALPHA_CHANGE        1
319 #define SCORE_CHROMA_W_CHANGE     1
320 #define SCORE_CHROMA_H_CHANGE     1
321 #define SCORE_PALETTE_CHANGE      1
322
323 #define SCORE_COLORSPACE_LOSS     2     /* RGB <-> YUV */
324 #define SCORE_DEPTH_LOSS          4     /* change bit depth */
325 #define SCORE_ALPHA_LOSS          8     /* lose the alpha channel */
326 #define SCORE_CHROMA_W_LOSS      16     /* vertical subsample */
327 #define SCORE_CHROMA_H_LOSS      32     /* horizontal subsample */
328 #define SCORE_PALETTE_LOSS       64     /* convert to palette format */
329 #define SCORE_COLOR_LOSS        128     /* convert to GRAY */
330
331 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
332                          GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
333 #define ALPHA_MASK      (GST_VIDEO_FORMAT_FLAG_ALPHA)
334 #define PALETTE_MASK    (GST_VIDEO_FORMAT_FLAG_PALETTE)
335
336 /* calculate how much loss a conversion would be */
337 static void
338 score_value (GstBaseTransform * base, const GstVideoFormatInfo * in_info,
339     const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
340 {
341   const gchar *fname;
342   const GstVideoFormatInfo *t_info;
343   guint in_flags, t_flags;
344   gint loss;
345
346   fname = g_value_get_string (val);
347   t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
348   if (!t_info || t_info->format == GST_VIDEO_FORMAT_UNKNOWN)
349     return;
350
351   /* accept input format immediately without loss */
352   if (in_info == t_info) {
353     *min_loss = 0;
354     *out_info = t_info;
355     return;
356   }
357
358   loss = SCORE_FORMAT_CHANGE;
359
360   in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
361   in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
362   in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
363   in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
364
365   t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
366   t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
367   t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
368   t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
369
370   if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
371     loss += SCORE_PALETTE_CHANGE;
372     if (t_flags & PALETTE_MASK)
373       loss += SCORE_PALETTE_LOSS;
374   }
375
376   if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
377     loss += SCORE_COLORSPACE_LOSS;
378     if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
379       loss += SCORE_COLOR_LOSS;
380   }
381
382   if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
383     loss += SCORE_ALPHA_CHANGE;
384     if (in_flags & ALPHA_MASK)
385       loss += SCORE_ALPHA_LOSS;
386   }
387
388   if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
389     loss += SCORE_CHROMA_H_CHANGE;
390     if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
391       loss += SCORE_CHROMA_H_LOSS;
392   }
393   if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
394     loss += SCORE_CHROMA_W_CHANGE;
395     if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
396       loss += SCORE_CHROMA_W_LOSS;
397   }
398
399   if ((in_info->bits) != (t_info->bits)) {
400     loss += SCORE_DEPTH_CHANGE;
401     if ((in_info->bits) > (t_info->bits))
402       loss += SCORE_DEPTH_LOSS + (in_info->bits - t_info->bits);
403   }
404
405   GST_DEBUG_OBJECT (base, "score %s -> %s = %d",
406       GST_VIDEO_FORMAT_INFO_NAME (in_info),
407       GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
408
409   if (loss < *min_loss) {
410     GST_DEBUG_OBJECT (base, "found new best %d", loss);
411     *out_info = t_info;
412     *min_loss = loss;
413   }
414 }
415
416 static void
417 gst_cuda_base_convert_fixate_format (GstBaseTransform * trans,
418     GstCaps * caps, GstCaps * result)
419 {
420   GstStructure *ins, *outs;
421   const gchar *in_format;
422   const GstVideoFormatInfo *in_info, *out_info = NULL;
423   gint min_loss = G_MAXINT;
424   guint i, capslen;
425
426   ins = gst_caps_get_structure (caps, 0);
427   in_format = gst_structure_get_string (ins, "format");
428   if (!in_format) {
429     return;
430   }
431
432   GST_DEBUG_OBJECT (trans, "source format %s", in_format);
433
434   in_info =
435       gst_video_format_get_info (gst_video_format_from_string (in_format));
436   if (!in_info)
437     return;
438
439   outs = gst_caps_get_structure (result, 0);
440
441   capslen = gst_caps_get_size (result);
442   GST_DEBUG ("iterate %d structures", capslen);
443   for (i = 0; i < capslen; i++) {
444     GstStructure *tests;
445     const GValue *format;
446
447     tests = gst_caps_get_structure (result, i);
448     format = gst_structure_get_value (tests, "format");
449
450     /* should not happen */
451     if (format == NULL)
452       continue;
453
454     if (GST_VALUE_HOLDS_LIST (format)) {
455       gint j, len;
456
457       len = gst_value_list_get_size (format);
458       GST_DEBUG_OBJECT (trans, "have %d formats", len);
459       for (j = 0; j < len; j++) {
460         const GValue *val;
461
462         val = gst_value_list_get_value (format, j);
463         if (G_VALUE_HOLDS_STRING (val)) {
464           score_value (trans, in_info, val, &min_loss, &out_info);
465           if (min_loss == 0)
466             break;
467         }
468       }
469     } else if (G_VALUE_HOLDS_STRING (format)) {
470       score_value (trans, in_info, format, &min_loss, &out_info);
471     }
472   }
473   if (out_info)
474     gst_structure_set (outs, "format", G_TYPE_STRING,
475         GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
476 }
477
478 static gboolean
479 subsampling_unchanged (GstVideoInfo * in_info, GstVideoInfo * out_info)
480 {
481   guint i;
482   const GstVideoFormatInfo *in_format, *out_format;
483
484   if (GST_VIDEO_INFO_N_COMPONENTS (in_info) !=
485       GST_VIDEO_INFO_N_COMPONENTS (out_info))
486     return FALSE;
487
488   in_format = in_info->finfo;
489   out_format = out_info->finfo;
490
491   for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (in_info); i++) {
492     if (GST_VIDEO_FORMAT_INFO_W_SUB (in_format,
493             i) != GST_VIDEO_FORMAT_INFO_W_SUB (out_format, i))
494       return FALSE;
495     if (GST_VIDEO_FORMAT_INFO_H_SUB (in_format,
496             i) != GST_VIDEO_FORMAT_INFO_H_SUB (out_format, i))
497       return FALSE;
498   }
499
500   return TRUE;
501 }
502
503 static void
504 transfer_colorimetry_from_input (GstBaseTransform * trans, GstCaps * in_caps,
505     GstCaps * out_caps)
506 {
507   GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
508   GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
509   gboolean have_colorimetry =
510       gst_structure_has_field (out_caps_s, "colorimetry");
511   gboolean have_chroma_site =
512       gst_structure_has_field (out_caps_s, "chroma-site");
513
514   /* If the output already has colorimetry and chroma-site, stop,
515    * otherwise try and transfer what we can from the input caps */
516   if (have_colorimetry && have_chroma_site)
517     return;
518
519   {
520     GstVideoInfo in_info, out_info;
521     const GValue *in_colorimetry =
522         gst_structure_get_value (in_caps_s, "colorimetry");
523
524     if (!gst_video_info_from_caps (&in_info, in_caps)) {
525       GST_WARNING_OBJECT (trans,
526           "Failed to convert sink pad caps to video info");
527       return;
528     }
529     if (!gst_video_info_from_caps (&out_info, out_caps)) {
530       GST_WARNING_OBJECT (trans,
531           "Failed to convert src pad caps to video info");
532       return;
533     }
534
535     if (!have_colorimetry && in_colorimetry != NULL) {
536       if ((GST_VIDEO_INFO_IS_YUV (&out_info)
537               && GST_VIDEO_INFO_IS_YUV (&in_info))
538           || (GST_VIDEO_INFO_IS_RGB (&out_info)
539               && GST_VIDEO_INFO_IS_RGB (&in_info))
540           || (GST_VIDEO_INFO_IS_GRAY (&out_info)
541               && GST_VIDEO_INFO_IS_GRAY (&in_info))) {
542         /* Can transfer the colorimetry intact from the input if it has it */
543         gst_structure_set_value (out_caps_s, "colorimetry", in_colorimetry);
544       } else {
545         gchar *colorimetry_str;
546
547         /* Changing between YUV/RGB - forward primaries and transfer function, but use
548          * default range and matrix.
549          * the primaries is used for conversion between RGB and XYZ (CIE 1931 coordinate).
550          * the transfer function could be another reference (e.g., HDR)
551          */
552         out_info.colorimetry.primaries = in_info.colorimetry.primaries;
553         out_info.colorimetry.transfer = in_info.colorimetry.transfer;
554
555         colorimetry_str =
556             gst_video_colorimetry_to_string (&out_info.colorimetry);
557         gst_caps_set_simple (out_caps, "colorimetry", G_TYPE_STRING,
558             colorimetry_str, NULL);
559         g_free (colorimetry_str);
560       }
561     }
562
563     /* Only YUV output needs chroma-site. If the input was also YUV and had the same chroma
564      * subsampling, transfer the siting. If the sub-sampling is changing, then the planes get
565      * scaled anyway so there's no real reason to prefer the input siting. */
566     if (!have_chroma_site && GST_VIDEO_INFO_IS_YUV (&out_info)) {
567       if (GST_VIDEO_INFO_IS_YUV (&in_info)) {
568         const GValue *in_chroma_site =
569             gst_structure_get_value (in_caps_s, "chroma-site");
570         if (in_chroma_site != NULL
571             && subsampling_unchanged (&in_info, &out_info))
572           gst_structure_set_value (out_caps_s, "chroma-site", in_chroma_site);
573       }
574     }
575   }
576 }
577
578 static GstCaps *
579 gst_cuda_base_convert_get_fixed_format (GstBaseTransform * trans,
580     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
581 {
582   GstCaps *result;
583
584   result = gst_caps_intersect (othercaps, caps);
585   if (gst_caps_is_empty (result)) {
586     gst_caps_unref (result);
587     result = gst_caps_copy (othercaps);
588   }
589
590   gst_cuda_base_convert_fixate_format (trans, caps, result);
591
592   /* fixate remaining fields */
593   result = gst_caps_fixate (result);
594
595   if (direction == GST_PAD_SINK) {
596     if (gst_caps_is_subset (caps, result)) {
597       gst_caps_replace (&result, caps);
598     } else {
599       /* Try and preserve input colorimetry / chroma information */
600       transfer_colorimetry_from_input (trans, caps, result);
601     }
602   }
603
604   return result;
605 }
606
607 static GstCaps *
608 gst_cuda_base_convert_fixate_size (GstBaseTransform * base,
609     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
610 {
611   GstStructure *ins, *outs;
612   const GValue *from_par, *to_par;
613   GValue fpar = G_VALUE_INIT, tpar = G_VALUE_INIT;
614
615   othercaps = gst_caps_truncate (othercaps);
616   othercaps = gst_caps_make_writable (othercaps);
617   ins = gst_caps_get_structure (caps, 0);
618   outs = gst_caps_get_structure (othercaps, 0);
619
620   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
621   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
622
623   if (direction == GST_PAD_SINK) {
624     if (!from_par) {
625       g_value_init (&fpar, GST_TYPE_FRACTION);
626       gst_value_set_fraction (&fpar, 1, 1);
627       from_par = &fpar;
628     }
629     if (!to_par) {
630       g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
631       gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
632       to_par = &tpar;
633     }
634   } else {
635     gint from_par_n, from_par_d;
636
637     if (!from_par) {
638       g_value_init (&fpar, GST_TYPE_FRACTION);
639       gst_value_set_fraction (&fpar, 1, 1);
640       from_par = &fpar;
641
642       from_par_n = from_par_d = 1;
643     } else {
644       from_par_n = gst_value_get_fraction_numerator (from_par);
645       from_par_d = gst_value_get_fraction_denominator (from_par);
646     }
647
648     if (!to_par) {
649       gint to_par_n, to_par_d;
650
651       to_par_n = from_par_n;
652       to_par_d = from_par_d;
653
654       g_value_init (&tpar, GST_TYPE_FRACTION);
655       gst_value_set_fraction (&tpar, to_par_n, to_par_d);
656       to_par = &tpar;
657
658       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
659           to_par_n, to_par_d, NULL);
660     }
661   }
662
663   /* we have both PAR but they might not be fixated */
664   {
665     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
666     gint w = 0, h = 0;
667     gint from_dar_n, from_dar_d;
668     gint num, den;
669
670     /* from_par should be fixed */
671     g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
672
673     from_par_n = gst_value_get_fraction_numerator (from_par);
674     from_par_d = gst_value_get_fraction_denominator (from_par);
675
676     gst_structure_get_int (ins, "width", &from_w);
677     gst_structure_get_int (ins, "height", &from_h);
678
679     gst_structure_get_int (outs, "width", &w);
680     gst_structure_get_int (outs, "height", &h);
681
682     /* if both width and height are already fixed, we can't do anything
683      * about it anymore */
684     if (w && h) {
685       guint n, d;
686
687       GST_DEBUG_OBJECT (base, "dimensions already set to %dx%d, not fixating",
688           w, h);
689       if (!gst_value_is_fixed (to_par)) {
690         if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
691                 from_par_n, from_par_d, w, h)) {
692           GST_DEBUG_OBJECT (base, "fixating to_par to %dx%d", n, d);
693           if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
694             gst_structure_fixate_field_nearest_fraction (outs,
695                 "pixel-aspect-ratio", n, d);
696           else if (n != d)
697             gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
698                 n, d, NULL);
699         }
700       }
701       goto done;
702     }
703
704     /* Calculate input DAR */
705     if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
706             &from_dar_n, &from_dar_d)) {
707       GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
708           ("Error calculating the output scaled size - integer overflow"));
709       goto done;
710     }
711
712     GST_DEBUG_OBJECT (base, "Input DAR is %d/%d", from_dar_n, from_dar_d);
713
714     /* If either width or height are fixed there's not much we
715      * can do either except choosing a height or width and PAR
716      * that matches the DAR as good as possible
717      */
718     if (h) {
719       GstStructure *tmp;
720       gint set_w, set_par_n, set_par_d;
721
722       GST_DEBUG_OBJECT (base, "height is fixed (%d)", h);
723
724       /* If the PAR is fixed too, there's not much to do
725        * except choosing the width that is nearest to the
726        * width with the same DAR */
727       if (gst_value_is_fixed (to_par)) {
728         to_par_n = gst_value_get_fraction_numerator (to_par);
729         to_par_d = gst_value_get_fraction_denominator (to_par);
730
731         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
732
733         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
734                 to_par_n, &num, &den)) {
735           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
736               ("Error calculating the output scaled size - integer overflow"));
737           goto done;
738         }
739
740         w = (guint) gst_util_uint64_scale_int_round (h, num, den);
741         gst_structure_fixate_field_nearest_int (outs, "width", w);
742
743         goto done;
744       }
745
746       /* The PAR is not fixed and it's quite likely that we can set
747        * an arbitrary PAR. */
748
749       /* Check if we can keep the input width */
750       tmp = gst_structure_copy (outs);
751       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
752       gst_structure_get_int (tmp, "width", &set_w);
753
754       /* Might have failed but try to keep the DAR nonetheless by
755        * adjusting the PAR */
756       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
757               &to_par_n, &to_par_d)) {
758         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
759             ("Error calculating the output scaled size - integer overflow"));
760         gst_structure_free (tmp);
761         goto done;
762       }
763
764       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
765         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
766       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
767           to_par_n, to_par_d);
768       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
769           &set_par_d);
770       gst_structure_free (tmp);
771
772       /* Check if the adjusted PAR is accepted */
773       if (set_par_n == to_par_n && set_par_d == to_par_d) {
774         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
775             set_par_n != set_par_d)
776           gst_structure_set (outs, "width", G_TYPE_INT, set_w,
777               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
778               NULL);
779         goto done;
780       }
781
782       /* Otherwise scale the width to the new PAR and check if the
783        * adjusted with is accepted. If all that fails we can't keep
784        * the DAR */
785       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
786               set_par_n, &num, &den)) {
787         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
788             ("Error calculating the output scaled size - integer overflow"));
789         goto done;
790       }
791
792       w = (guint) gst_util_uint64_scale_int_round (h, num, den);
793       gst_structure_fixate_field_nearest_int (outs, "width", w);
794       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
795           set_par_n != set_par_d)
796         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
797             set_par_n, set_par_d, NULL);
798
799       goto done;
800     } else if (w) {
801       GstStructure *tmp;
802       gint set_h, set_par_n, set_par_d;
803
804       GST_DEBUG_OBJECT (base, "width is fixed (%d)", w);
805
806       /* If the PAR is fixed too, there's not much to do
807        * except choosing the height that is nearest to the
808        * height with the same DAR */
809       if (gst_value_is_fixed (to_par)) {
810         to_par_n = gst_value_get_fraction_numerator (to_par);
811         to_par_d = gst_value_get_fraction_denominator (to_par);
812
813         GST_DEBUG_OBJECT (base, "PAR is fixed %d/%d", to_par_n, to_par_d);
814
815         if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
816                 to_par_n, &num, &den)) {
817           GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
818               ("Error calculating the output scaled size - integer overflow"));
819           goto done;
820         }
821
822         h = (guint) gst_util_uint64_scale_int_round (w, den, num);
823         gst_structure_fixate_field_nearest_int (outs, "height", h);
824
825         goto done;
826       }
827
828       /* The PAR is not fixed and it's quite likely that we can set
829        * an arbitrary PAR. */
830
831       /* Check if we can keep the input height */
832       tmp = gst_structure_copy (outs);
833       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
834       gst_structure_get_int (tmp, "height", &set_h);
835
836       /* Might have failed but try to keep the DAR nonetheless by
837        * adjusting the PAR */
838       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
839               &to_par_n, &to_par_d)) {
840         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
841             ("Error calculating the output scaled size - integer overflow"));
842         gst_structure_free (tmp);
843         goto done;
844       }
845       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
846         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
847       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
848           to_par_n, to_par_d);
849       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
850           &set_par_d);
851       gst_structure_free (tmp);
852
853       /* Check if the adjusted PAR is accepted */
854       if (set_par_n == to_par_n && set_par_d == to_par_d) {
855         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
856             set_par_n != set_par_d)
857           gst_structure_set (outs, "height", G_TYPE_INT, set_h,
858               "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
859               NULL);
860         goto done;
861       }
862
863       /* Otherwise scale the height to the new PAR and check if the
864        * adjusted with is accepted. If all that fails we can't keep
865        * the DAR */
866       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
867               set_par_n, &num, &den)) {
868         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
869             ("Error calculating the output scale sized - integer overflow"));
870         goto done;
871       }
872
873       h = (guint) gst_util_uint64_scale_int_round (w, den, num);
874       gst_structure_fixate_field_nearest_int (outs, "height", h);
875       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
876           set_par_n != set_par_d)
877         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
878             set_par_n, set_par_d, NULL);
879
880       goto done;
881     } else if (gst_value_is_fixed (to_par)) {
882       GstStructure *tmp;
883       gint set_h, set_w, f_h, f_w;
884
885       to_par_n = gst_value_get_fraction_numerator (to_par);
886       to_par_d = gst_value_get_fraction_denominator (to_par);
887
888       /* Calculate scale factor for the PAR change */
889       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
890               to_par_d, &num, &den)) {
891         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
892             ("Error calculating the output scaled size - integer overflow"));
893         goto done;
894       }
895
896       /* Try to keep the input height (because of interlacing) */
897       tmp = gst_structure_copy (outs);
898       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
899       gst_structure_get_int (tmp, "height", &set_h);
900
901       /* This might have failed but try to scale the width
902        * to keep the DAR nonetheless */
903       w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
904       gst_structure_fixate_field_nearest_int (tmp, "width", w);
905       gst_structure_get_int (tmp, "width", &set_w);
906       gst_structure_free (tmp);
907
908       /* We kept the DAR and the height is nearest to the original height */
909       if (set_w == w) {
910         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
911             G_TYPE_INT, set_h, NULL);
912         goto done;
913       }
914
915       f_h = set_h;
916       f_w = set_w;
917
918       /* If the former failed, try to keep the input width at least */
919       tmp = gst_structure_copy (outs);
920       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
921       gst_structure_get_int (tmp, "width", &set_w);
922
923       /* This might have failed but try to scale the width
924        * to keep the DAR nonetheless */
925       h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
926       gst_structure_fixate_field_nearest_int (tmp, "height", h);
927       gst_structure_get_int (tmp, "height", &set_h);
928       gst_structure_free (tmp);
929
930       /* We kept the DAR and the width is nearest to the original width */
931       if (set_h == h) {
932         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
933             G_TYPE_INT, set_h, NULL);
934         goto done;
935       }
936
937       /* If all this failed, keep the dimensions with the DAR that was closest
938        * to the correct DAR. This changes the DAR but there's not much else to
939        * do here.
940        */
941       if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
942         f_h = set_h;
943         f_w = set_w;
944       }
945       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
946           f_h, NULL);
947       goto done;
948     } else {
949       GstStructure *tmp;
950       gint set_h, set_w, set_par_n, set_par_d, tmp2;
951
952       /* width, height and PAR are not fixed but passthrough is not possible */
953
954       /* First try to keep the height and width as good as possible
955        * and scale PAR */
956       tmp = gst_structure_copy (outs);
957       gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
958       gst_structure_get_int (tmp, "height", &set_h);
959       gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
960       gst_structure_get_int (tmp, "width", &set_w);
961
962       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
963               &to_par_n, &to_par_d)) {
964         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
965             ("Error calculating the output scaled size - integer overflow"));
966         gst_structure_free (tmp);
967         goto done;
968       }
969
970       if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
971         gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
972       gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
973           to_par_n, to_par_d);
974       gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
975           &set_par_d);
976       gst_structure_free (tmp);
977
978       if (set_par_n == to_par_n && set_par_d == to_par_d) {
979         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
980             G_TYPE_INT, set_h, NULL);
981
982         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
983             set_par_n != set_par_d)
984           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
985               set_par_n, set_par_d, NULL);
986         goto done;
987       }
988
989       /* Otherwise try to scale width to keep the DAR with the set
990        * PAR and height */
991       if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
992               set_par_n, &num, &den)) {
993         GST_ELEMENT_ERROR (base, CORE, NEGOTIATION, (NULL),
994             ("Error calculating the output scaled size - integer overflow"));
995         goto done;
996       }
997
998       w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
999       tmp = gst_structure_copy (outs);
1000       gst_structure_fixate_field_nearest_int (tmp, "width", w);
1001       gst_structure_get_int (tmp, "width", &tmp2);
1002       gst_structure_free (tmp);
1003
1004       if (tmp2 == w) {
1005         gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1006             G_TYPE_INT, set_h, NULL);
1007         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1008             set_par_n != set_par_d)
1009           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1010               set_par_n, set_par_d, NULL);
1011         goto done;
1012       }
1013
1014       /* ... or try the same with the height */
1015       h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1016       tmp = gst_structure_copy (outs);
1017       gst_structure_fixate_field_nearest_int (tmp, "height", h);
1018       gst_structure_get_int (tmp, "height", &tmp2);
1019       gst_structure_free (tmp);
1020
1021       if (tmp2 == h) {
1022         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1023             G_TYPE_INT, tmp2, NULL);
1024         if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1025             set_par_n != set_par_d)
1026           gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1027               set_par_n, set_par_d, NULL);
1028         goto done;
1029       }
1030
1031       /* If all fails we can't keep the DAR and take the nearest values
1032        * for everything from the first try */
1033       gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1034           G_TYPE_INT, set_h, NULL);
1035       if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1036           set_par_n != set_par_d)
1037         gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1038             set_par_n, set_par_d, NULL);
1039     }
1040   }
1041
1042 done:
1043   if (from_par == &fpar)
1044     g_value_unset (&fpar);
1045   if (to_par == &tpar)
1046     g_value_unset (&tpar);
1047
1048   return othercaps;
1049 }
1050
1051 static GstCaps *
1052 gst_cuda_base_convert_fixate_caps (GstBaseTransform * trans,
1053     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1054 {
1055   GstCaps *format = NULL;
1056
1057   GST_DEBUG_OBJECT (trans,
1058       "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
1059       GST_PTR_FORMAT, othercaps, caps);
1060
1061   format = gst_cuda_base_convert_get_fixed_format (trans, direction, caps,
1062       othercaps);
1063
1064   if (gst_caps_is_empty (format)) {
1065     GST_ERROR_OBJECT (trans, "Could not convert formats");
1066     return format;
1067   }
1068
1069   /* convert mode is "all" or "size" here */
1070   othercaps =
1071       gst_cuda_base_convert_fixate_size (trans, direction, caps, othercaps);
1072
1073   if (gst_caps_get_size (othercaps) == 1) {
1074     guint i;
1075     const gchar *format_fields[] = { "format", "colorimetry", "chroma-site" };
1076     GstStructure *format_struct = gst_caps_get_structure (format, 0);
1077     GstStructure *fixated_struct;
1078
1079     othercaps = gst_caps_make_writable (othercaps);
1080     fixated_struct = gst_caps_get_structure (othercaps, 0);
1081
1082     for (i = 0; i < G_N_ELEMENTS (format_fields); i++) {
1083       if (gst_structure_has_field (format_struct, format_fields[i])) {
1084         gst_structure_set (fixated_struct, format_fields[i], G_TYPE_STRING,
1085             gst_structure_get_string (format_struct, format_fields[i]), NULL);
1086       } else {
1087         gst_structure_remove_field (fixated_struct, format_fields[i]);
1088       }
1089     }
1090   }
1091   gst_caps_unref (format);
1092
1093   GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
1094
1095   return othercaps;
1096 }
1097
1098 static gboolean
1099 gst_cuda_base_convert_propose_allocation (GstBaseTransform * trans,
1100     GstQuery * decide_query, GstQuery * query)
1101 {
1102   GstCudaBaseTransform *ctrans = GST_CUDA_BASE_TRANSFORM (trans);
1103   GstVideoInfo info;
1104   GstBufferPool *pool;
1105   GstCaps *caps;
1106   guint size;
1107
1108   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
1109           decide_query, query))
1110     return FALSE;
1111
1112   /* passthrough, we're done */
1113   if (decide_query == NULL)
1114     return TRUE;
1115
1116   gst_query_parse_allocation (query, &caps, NULL);
1117
1118   if (caps == NULL)
1119     return FALSE;
1120
1121   if (!gst_video_info_from_caps (&info, caps))
1122     return FALSE;
1123
1124   if (gst_query_get_n_allocation_pools (query) == 0) {
1125     GstStructure *config;
1126
1127     pool = gst_cuda_buffer_pool_new (ctrans->context);
1128
1129     config = gst_buffer_pool_get_config (pool);
1130
1131     gst_buffer_pool_config_add_option (config,
1132         GST_BUFFER_POOL_OPTION_VIDEO_META);
1133
1134     size = GST_VIDEO_INFO_SIZE (&info);
1135     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1136
1137     if (!gst_buffer_pool_set_config (pool, config)) {
1138       GST_ERROR_OBJECT (ctrans, "failed to set config");
1139       gst_object_unref (pool);
1140       return FALSE;
1141     }
1142
1143     /* Get updated size by cuda buffer pool */
1144     config = gst_buffer_pool_get_config (pool);
1145     gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
1146     gst_structure_free (config);
1147
1148     gst_query_add_allocation_pool (query, pool, size, 0, 0);
1149
1150     gst_object_unref (pool);
1151   }
1152
1153   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1154
1155   return TRUE;
1156 }
1157
1158 static gboolean
1159 gst_cuda_base_convert_decide_allocation (GstBaseTransform * trans,
1160     GstQuery * query)
1161 {
1162   GstCudaBaseTransform *ctrans = GST_CUDA_BASE_TRANSFORM (trans);
1163   GstCaps *outcaps = NULL;
1164   GstBufferPool *pool = NULL;
1165   guint size, min, max;
1166   GstStructure *config;
1167   gboolean update_pool = FALSE;
1168
1169   gst_query_parse_allocation (query, &outcaps, NULL);
1170
1171   if (!outcaps)
1172     return FALSE;
1173
1174   if (gst_query_get_n_allocation_pools (query) > 0) {
1175     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1176     if (pool) {
1177       if (!GST_IS_CUDA_BUFFER_POOL (pool)) {
1178         gst_clear_object (&pool);
1179       } else {
1180         GstCudaBufferPool *cpool = GST_CUDA_BUFFER_POOL (pool);
1181
1182         if (cpool->context != ctrans->context) {
1183           gst_clear_object (&pool);
1184         }
1185       }
1186     }
1187
1188     update_pool = TRUE;
1189   } else {
1190     GstVideoInfo vinfo;
1191     gst_video_info_from_caps (&vinfo, outcaps);
1192     size = GST_VIDEO_INFO_SIZE (&vinfo);
1193     min = max = 0;
1194   }
1195
1196   if (!pool) {
1197     GST_DEBUG_OBJECT (ctrans, "create our pool");
1198
1199     pool = gst_cuda_buffer_pool_new (ctrans->context);
1200   }
1201
1202   config = gst_buffer_pool_get_config (pool);
1203   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1204   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
1205   gst_buffer_pool_set_config (pool, config);
1206
1207   /* Get updated size by cuda buffer pool */
1208   config = gst_buffer_pool_get_config (pool);
1209   gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
1210   gst_structure_free (config);
1211
1212   if (update_pool)
1213     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1214   else
1215     gst_query_add_allocation_pool (query, pool, size, min, max);
1216
1217   gst_object_unref (pool);
1218
1219   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
1220       query);
1221 }
1222
1223 static gboolean
1224 gst_cuda_base_convert_set_info (GstCudaBaseTransform * btrans,
1225     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
1226     GstVideoInfo * out_info)
1227 {
1228   GstCudaBaseConvert *self = GST_CUDA_BASE_CONVERT (btrans);
1229   gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
1230   GstVideoInfo tmp_info;
1231
1232   g_clear_pointer (&self->converter, gst_cuda_converter_free);
1233
1234   if (!gst_util_fraction_multiply (in_info->width,
1235           in_info->height, in_info->par_n, in_info->par_d, &from_dar_n,
1236           &from_dar_d)) {
1237     from_dar_n = from_dar_d = -1;
1238   }
1239
1240   if (!gst_util_fraction_multiply (out_info->width,
1241           out_info->height, out_info->par_n, out_info->par_d, &to_dar_n,
1242           &to_dar_d)) {
1243     to_dar_n = to_dar_d = -1;
1244   }
1245
1246   self->borders_w = self->borders_h = 0;
1247   if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
1248     if (self->add_borders) {
1249       gint n, d, to_h, to_w;
1250
1251       if (from_dar_n != -1 && from_dar_d != -1
1252           && gst_util_fraction_multiply (from_dar_n, from_dar_d,
1253               out_info->par_d, out_info->par_n, &n, &d)) {
1254         to_h = gst_util_uint64_scale_int (out_info->width, d, n);
1255         if (to_h <= out_info->height) {
1256           self->borders_h = out_info->height - to_h;
1257           self->borders_w = 0;
1258         } else {
1259           to_w = gst_util_uint64_scale_int (out_info->height, n, d);
1260           g_assert (to_w <= out_info->width);
1261           self->borders_h = 0;
1262           self->borders_w = out_info->width - to_w;
1263         }
1264       } else {
1265         GST_WARNING_OBJECT (self, "Can't calculate borders");
1266       }
1267     } else {
1268       GST_WARNING_OBJECT (self, "Can't keep DAR!");
1269     }
1270   }
1271
1272   /* if present, these must match */
1273   if (in_info->interlace_mode != out_info->interlace_mode) {
1274     GST_ERROR_OBJECT (self, "input and output formats do not match");
1275     return FALSE;
1276   }
1277
1278   /* if the only thing different in the caps is the transfer function, and
1279    * we're converting between equivalent transfer functions, do passthrough */
1280   tmp_info = *in_info;
1281   tmp_info.colorimetry.transfer = out_info->colorimetry.transfer;
1282   if (gst_video_info_is_equal (&tmp_info, out_info) &&
1283       gst_video_transfer_function_is_equivalent (in_info->colorimetry.transfer,
1284           in_info->finfo->bits, out_info->colorimetry.transfer,
1285           out_info->finfo->bits)) {
1286     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (self), TRUE);
1287   } else {
1288     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (self), FALSE);
1289
1290     self->converter = gst_cuda_converter_new (in_info,
1291         out_info, btrans->context);
1292     if (!self->converter) {
1293       GST_ERROR_OBJECT (self, "Couldn't create converter");
1294       return FALSE;
1295     }
1296   }
1297
1298   GST_DEBUG_OBJECT (self, "%s from=%dx%d (par=%d/%d dar=%d/%d), size %"
1299       G_GSIZE_FORMAT " -> %s to=%dx%d (par=%d/%d dar=%d/%d borders=%d:%d), "
1300       "size %" G_GSIZE_FORMAT,
1301       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)),
1302       in_info->width, in_info->height, in_info->par_n, in_info->par_d,
1303       from_dar_n, from_dar_d, in_info->size,
1304       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info)),
1305       out_info->width,
1306       out_info->height, out_info->par_n, out_info->par_d, to_dar_n, to_dar_d,
1307       self->borders_w, self->borders_h, out_info->size);
1308
1309   return TRUE;
1310 }
1311
1312 static gboolean
1313 gst_cuda_base_convert_filter_meta (GstBaseTransform * trans, GstQuery * query,
1314     GType api, const GstStructure * params)
1315 {
1316   /* This element cannot passthrough the crop meta, because it would convert the
1317    * wrong sub-region of the image, and worst, our output image may not be large
1318    * enough for the crop to be applied later */
1319   if (api == GST_VIDEO_CROP_META_API_TYPE)
1320     return FALSE;
1321
1322   /* propose all other metadata upstream */
1323   return TRUE;
1324 }
1325
1326 static GstFlowReturn
1327 gst_cuda_base_convert_transform (GstBaseTransform * trans,
1328     GstBuffer * inbuf, GstBuffer * outbuf)
1329 {
1330   GstCudaBaseConvert *self = GST_CUDA_BASE_CONVERT (trans);
1331   GstCudaBaseTransform *btrans = GST_CUDA_BASE_TRANSFORM (trans);
1332   GstVideoFrame in_frame, out_frame;
1333   GstFlowReturn ret = GST_FLOW_OK;
1334   GstMemory *mem;
1335
1336   if (gst_buffer_n_memory (inbuf) != 1) {
1337     GST_ERROR_OBJECT (self, "Invalid input buffer");
1338     return GST_FLOW_ERROR;
1339   }
1340
1341   mem = gst_buffer_peek_memory (inbuf, 0);
1342   if (!gst_is_cuda_memory (mem)) {
1343     GST_ERROR_OBJECT (self, "Input buffer is not CUDA");
1344     return GST_FLOW_ERROR;
1345   }
1346
1347   if (gst_buffer_n_memory (outbuf) != 1) {
1348     GST_ERROR_OBJECT (self, "Invalid output buffer");
1349     return GST_FLOW_ERROR;
1350   }
1351
1352   mem = gst_buffer_peek_memory (outbuf, 0);
1353   if (!gst_is_cuda_memory (mem)) {
1354     GST_ERROR_OBJECT (self, "Input buffer is not CUDA");
1355     return GST_FLOW_ERROR;
1356   }
1357
1358   if (!gst_video_frame_map (&in_frame, &btrans->in_info, inbuf,
1359           GST_MAP_READ | GST_MAP_CUDA)) {
1360     GST_ERROR_OBJECT (self, "Failed to map input buffer");
1361     return GST_FLOW_ERROR;
1362   }
1363
1364   if (!gst_video_frame_map (&out_frame, &btrans->out_info, outbuf,
1365           GST_MAP_WRITE | GST_MAP_CUDA)) {
1366     gst_video_frame_unmap (&in_frame);
1367     GST_ERROR_OBJECT (self, "Failed to map output buffer");
1368     return GST_FLOW_ERROR;
1369   }
1370
1371   if (!gst_cuda_converter_convert_frame (self->converter, &in_frame, &out_frame,
1372           btrans->cuda_stream)) {
1373     GST_ERROR_OBJECT (self, "Failed to convert frame");
1374     ret = GST_FLOW_ERROR;
1375   }
1376
1377   gst_video_frame_unmap (&out_frame);
1378   gst_video_frame_unmap (&in_frame);
1379
1380   return ret;
1381 }
1382
1383 /**
1384  * SECTION:element-cudaconvertscale
1385  * @title: cudaconvertscale
1386  * @short_description: A CUDA based color conversion and video resizing element
1387  *
1388  * This element resizes video frames and change color space.
1389  * By default the element will try to negotiate to the same size on the source
1390  * and sinkpad so that no scaling is needed.
1391  * It is therefore safe to insert this element in a pipeline to
1392  * get more robust behaviour without any cost if no scaling is needed.
1393  *
1394  * ## Example launch line
1395  * ```
1396  * gst-launch-1.0 videotestsrc ! cudaupload ! cudaconvertscale ! cudadownload ! autovideosink
1397  * ```
1398  *
1399  * Since: 1.22
1400  */
1401
1402 struct _GstCudaConvertScale
1403 {
1404   GstCudaBaseConvert parent;
1405 };
1406
1407 G_DEFINE_TYPE (GstCudaConvertScale, gst_cuda_convert_scale,
1408     GST_TYPE_CUDA_BASE_CONVERT);
1409
1410 static void
1411 gst_cuda_convert_scale_class_init (GstCudaConvertScaleClass * klass)
1412 {
1413   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
1414
1415   gst_element_class_set_static_metadata (element_class,
1416       "CUDA colorspace converter and scaler",
1417       "Filter/Converter/Video/Scaler/Colorspace/Hardware",
1418       "Resizes video and allow color conversion using CUDA",
1419       "Seungha Yang <seungha@centricular.com>");
1420 }
1421
1422 static void
1423 gst_cuda_convert_scale_init (GstCudaConvertScale * self)
1424 {
1425 }
1426
1427 /**
1428  * SECTION:element-cudaconvert
1429  * @title: cudaconvert
1430  *
1431  * Convert video frames between supported video formats.
1432  *
1433  * ## Example launch line
1434  * ```
1435  * gst-launch-1.0 videotestsrc ! cudaupload ! cudaconvert ! cudadownload ! autovideosink
1436  * ```
1437  *
1438  * Since: 1.20
1439  */
1440
1441 struct _GstCudaConvert
1442 {
1443   GstCudaBaseConvert parent;
1444 };
1445
1446 static GstCaps *gst_cuda_convert_transform_caps (GstBaseTransform *
1447     trans, GstPadDirection direction, GstCaps * caps, GstCaps * filter);
1448 static GstCaps *gst_cuda_convert_fixate_caps (GstBaseTransform * base,
1449     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
1450
1451 G_DEFINE_TYPE (GstCudaConvert, gst_cuda_convert, GST_TYPE_CUDA_BASE_CONVERT);
1452
1453 static void
1454 gst_cuda_convert_class_init (GstCudaConvertClass * klass)
1455 {
1456   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
1457   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
1458
1459   gst_element_class_set_static_metadata (element_class,
1460       "CUDA colorspace converter",
1461       "Filter/Converter/Video/Hardware",
1462       "Converts video from one colorspace to another using CUDA",
1463       "Seungha Yang <seungha.yang@navercorp.com>");
1464
1465   trans_class->transform_caps =
1466       GST_DEBUG_FUNCPTR (gst_cuda_convert_transform_caps);
1467   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_cuda_convert_fixate_caps);
1468 }
1469
1470 static void
1471 gst_cuda_convert_init (GstCudaConvert * self)
1472 {
1473 }
1474
1475 static GstCaps *
1476 gst_cuda_convert_transform_caps (GstBaseTransform *
1477     trans, GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1478 {
1479   GstCaps *tmp, *tmp2;
1480   GstCaps *result;
1481
1482   /* Get all possible caps that we can transform to */
1483   tmp = gst_cuda_base_convert_caps_remove_format_info (caps);
1484
1485   if (filter) {
1486     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
1487     gst_caps_unref (tmp);
1488     tmp = tmp2;
1489   }
1490
1491   result = tmp;
1492
1493   GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
1494       GST_PTR_FORMAT, caps, result);
1495
1496   return result;
1497 }
1498
1499 static GstCaps *
1500 gst_cuda_convert_fixate_caps (GstBaseTransform * base,
1501     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1502 {
1503   GstCaps *format = NULL;
1504
1505   GST_DEBUG_OBJECT (base,
1506       "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
1507       GST_PTR_FORMAT, othercaps, caps);
1508
1509   format = gst_cuda_base_convert_get_fixed_format (base, direction, caps,
1510       othercaps);
1511   gst_caps_unref (othercaps);
1512
1513   if (gst_caps_is_empty (format)) {
1514     GST_ERROR_OBJECT (base, "Could not convert formats");
1515   } else {
1516     GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, format);
1517   }
1518
1519   return format;
1520 }
1521
1522 /**
1523  * SECTION:element-cudascale
1524  * @title: cudascale
1525  *
1526  * A CUDA based video resizing element
1527  *
1528  * ## Example launch line
1529  * ```
1530  * gst-launch-1.0 videotestsrc ! video/x-raw,width=640,height=480 ! cudaupload ! cudascale ! cudadownload ! video/x-raw,width=1280,height=720 ! fakesink
1531  * ```
1532  *  This will upload a 640x480 resolution test video to CUDA
1533  * memory space and resize it to 1280x720 resolution. Then a resized CUDA
1534  * frame will be downloaded to system memory space.
1535  *
1536  * Since: 1.20
1537  */
1538
1539 struct _GstCudaScale
1540 {
1541   GstCudaBaseConvert parent;
1542 };
1543
1544 static GstCaps *gst_cuda_scale_transform_caps (GstBaseTransform *
1545     trans, GstPadDirection direction, GstCaps * caps, GstCaps * filter);
1546 static GstCaps *gst_cuda_scale_fixate_caps (GstBaseTransform * base,
1547     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
1548
1549 G_DEFINE_TYPE (GstCudaScale, gst_cuda_scale, GST_TYPE_CUDA_BASE_CONVERT);
1550
1551 static void
1552 gst_cuda_scale_class_init (GstCudaScaleClass * klass)
1553 {
1554   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
1555   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
1556
1557   gst_element_class_set_static_metadata (element_class,
1558       "CUDA video scaler",
1559       "Filter/Converter/Video/Scaler/Hardware",
1560       "Resize video using CUDA", "Seungha Yang <seungha.yang@navercorp.com>");
1561
1562   trans_class->transform_caps =
1563       GST_DEBUG_FUNCPTR (gst_cuda_scale_transform_caps);
1564   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_cuda_scale_fixate_caps);
1565 }
1566
1567 static void
1568 gst_cuda_scale_init (GstCudaScale * self)
1569 {
1570 }
1571
1572 static GstCaps *
1573 gst_cuda_scale_transform_caps (GstBaseTransform * trans,
1574     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1575 {
1576   GstCaps *tmp, *tmp2;
1577   GstCaps *result;
1578
1579   /* Get all possible caps that we can transform to */
1580   tmp = gst_cuda_base_convert_caps_rangify_size_info (caps);
1581
1582   if (filter) {
1583     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
1584     gst_caps_unref (tmp);
1585     tmp = tmp2;
1586   }
1587
1588   result = tmp;
1589
1590   GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
1591       GST_PTR_FORMAT, caps, result);
1592
1593   return result;
1594 }
1595
1596 static GstCaps *
1597 gst_cuda_scale_fixate_caps (GstBaseTransform * base,
1598     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1599 {
1600   GST_DEBUG_OBJECT (base,
1601       "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
1602       GST_PTR_FORMAT, othercaps, caps);
1603
1604   othercaps =
1605       gst_cuda_base_convert_fixate_size (base, direction, caps, othercaps);
1606
1607   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
1608
1609   return othercaps;
1610 }