2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2005-2012 David Schleef <ds@schleef.org>
4 * Copyright (C) <2019> Seungha Yang <seungha.yang@navercorp.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * SECTION:element-cudaconvert
26 * Convert video frames between supported video formats.
28 * ## Example launch line
30 * gst-launch-1.0 -v videotestsrc ! video/x-raw,format=Y444_16LE ! cudaupload ! cudaconvert ! cudadownload ! autovideosink
32 * This will output a test video (generated in Y444_16LE format) in a video
33 * window. If the video sink selected does not support Y444_16LE
34 * cudaconvert will automatically convert the video to a format understood
44 #include <gst/cuda/gstcudautils.h>
46 #include "gstcudaconvert.h"
48 GST_DEBUG_CATEGORY_STATIC (gst_cuda_convert_debug);
49 #define GST_CAT_DEFAULT gst_cuda_convert_debug
51 #define gst_cuda_convert_parent_class parent_class
52 G_DEFINE_TYPE (GstCudaConvert, gst_cuda_convert, GST_TYPE_CUDA_BASE_FILTER);
54 static GstCaps *gst_cuda_convert_transform_caps (GstBaseTransform * trans,
55 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
56 static GstCaps *gst_cuda_convert_fixate_caps (GstBaseTransform * base,
57 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
58 static gboolean gst_cuda_convert_filter_meta (GstBaseTransform * trans,
59 GstQuery * query, GType api, const GstStructure * params);
61 gst_cuda_convert_set_info (GstCudaBaseTransform * btrans, GstCaps * incaps,
62 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info);
64 /* copies the given caps */
66 gst_cuda_convert_caps_remove_format_info (GstCaps * caps)
72 GstCapsFeatures *feature =
73 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY);
75 res = gst_caps_new_empty ();
77 n = gst_caps_get_size (caps);
78 for (i = 0; i < n; i++) {
79 st = gst_caps_get_structure (caps, i);
80 f = gst_caps_get_features (caps, i);
82 /* If this is already expressed by the existing caps
83 * skip this structure */
84 if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
87 st = gst_structure_copy (st);
88 /* Only remove format info for the cases when we can actually convert */
89 if (!gst_caps_features_is_any (f)
90 && gst_caps_features_is_equal (f, feature))
91 gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
94 gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
96 gst_caps_features_free (feature);
102 * This is an incomplete matrix of in formats and a score for the prefered output
105 * out: RGB24 RGB16 ARGB AYUV YUV444 YUV422 YUV420 YUV411 YUV410 PAL GRAY
107 * RGB24 0 2 1 2 2 3 4 5 6 7 8
108 * RGB16 1 0 1 2 2 3 4 5 6 7 8
109 * ARGB 2 3 0 1 4 5 6 7 8 9 10
110 * AYUV 3 4 1 0 2 5 6 7 8 9 10
111 * YUV444 2 4 3 1 0 5 6 7 8 9 10
112 * YUV422 3 5 4 2 1 0 6 7 8 9 10
113 * YUV420 4 6 5 3 2 1 0 7 8 9 10
114 * YUV411 4 6 5 3 2 1 7 0 8 9 10
115 * YUV410 6 8 7 5 4 3 2 1 0 9 10
116 * PAL 1 3 2 6 4 6 7 8 9 0 10
117 * GRAY 1 4 3 2 1 5 6 7 8 9 0
119 * PAL or GRAY are never preferred, if we can we would convert to PAL instead
121 * less subsampling is preferred and if any, preferably horizontal
122 * We would like to keep the alpha, even if we would need to to colorspace conversion
125 #define SCORE_FORMAT_CHANGE 1
126 #define SCORE_DEPTH_CHANGE 1
127 #define SCORE_ALPHA_CHANGE 1
128 #define SCORE_CHROMA_W_CHANGE 1
129 #define SCORE_CHROMA_H_CHANGE 1
130 #define SCORE_PALETTE_CHANGE 1
132 #define SCORE_COLORSPACE_LOSS 2 /* RGB <-> YUV */
133 #define SCORE_DEPTH_LOSS 4 /* change bit depth */
134 #define SCORE_ALPHA_LOSS 8 /* lose the alpha channel */
135 #define SCORE_CHROMA_W_LOSS 16 /* vertical subsample */
136 #define SCORE_CHROMA_H_LOSS 32 /* horizontal subsample */
137 #define SCORE_PALETTE_LOSS 64 /* convert to palette format */
138 #define SCORE_COLOR_LOSS 128 /* convert to GRAY */
140 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
141 GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
142 #define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
143 #define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
145 /* calculate how much loss a conversion would be */
147 score_value (GstBaseTransform * base, const GstVideoFormatInfo * in_info,
148 const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
151 const GstVideoFormatInfo *t_info;
152 GstVideoFormatFlags in_flags, t_flags;
155 fname = g_value_get_string (val);
156 t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
160 /* accept input format immediately without loss */
161 if (in_info == t_info) {
167 loss = SCORE_FORMAT_CHANGE;
169 in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
170 in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
171 in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
172 in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
174 t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
175 t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
176 t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
177 t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
179 if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
180 loss += SCORE_PALETTE_CHANGE;
181 if (t_flags & PALETTE_MASK)
182 loss += SCORE_PALETTE_LOSS;
185 if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
186 loss += SCORE_COLORSPACE_LOSS;
187 if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
188 loss += SCORE_COLOR_LOSS;
191 if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
192 loss += SCORE_ALPHA_CHANGE;
193 if (in_flags & ALPHA_MASK)
194 loss += SCORE_ALPHA_LOSS;
197 if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
198 loss += SCORE_CHROMA_H_CHANGE;
199 if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
200 loss += SCORE_CHROMA_H_LOSS;
202 if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
203 loss += SCORE_CHROMA_W_CHANGE;
204 if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
205 loss += SCORE_CHROMA_W_LOSS;
208 if ((in_info->bits) != (t_info->bits)) {
209 loss += SCORE_DEPTH_CHANGE;
210 if ((in_info->bits) > (t_info->bits))
211 loss += SCORE_DEPTH_LOSS;
214 GST_DEBUG_OBJECT (base, "score %s -> %s = %d",
215 GST_VIDEO_FORMAT_INFO_NAME (in_info),
216 GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
218 if (loss < *min_loss) {
219 GST_DEBUG_OBJECT (base, "found new best %d", loss);
226 gst_cuda_convert_class_init (GstCudaConvertClass * klass)
228 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
229 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
230 GstCudaBaseTransformClass *btrans_class =
231 GST_CUDA_BASE_TRANSFORM_CLASS (klass);
233 gst_element_class_set_static_metadata (element_class,
234 "CUDA Colorspace converter",
235 "Filter/Converter/Video/Hardware",
236 "Converts video from one colorspace to another using CUDA",
237 "Seungha Yang <seungha.yang@navercorp.com>");
239 trans_class->passthrough_on_same_caps = TRUE;
241 trans_class->transform_caps =
242 GST_DEBUG_FUNCPTR (gst_cuda_convert_transform_caps);
243 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_cuda_convert_fixate_caps);
244 trans_class->filter_meta = GST_DEBUG_FUNCPTR (gst_cuda_convert_filter_meta);
246 btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_cuda_convert_set_info);
248 GST_DEBUG_CATEGORY_INIT (gst_cuda_convert_debug,
249 "cudaconvert", 0, "Video ColorSpace convert using CUDA");
253 gst_cuda_convert_init (GstCudaConvert * convert)
258 gst_cuda_convert_transform_caps (GstBaseTransform * trans,
259 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
264 /* Get all possible caps that we can transform to */
265 tmp = gst_cuda_convert_caps_remove_format_info (caps);
268 tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
269 gst_caps_unref (tmp);
275 GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
276 GST_PTR_FORMAT, caps, result);
281 /* fork of gstvideoconvert */
283 gst_cuda_convert_fixate_format (GstBaseTransform * base, GstCaps * caps,
286 GstStructure *ins, *outs;
287 const gchar *in_format;
288 const GstVideoFormatInfo *in_info, *out_info = NULL;
289 gint min_loss = G_MAXINT;
292 ins = gst_caps_get_structure (caps, 0);
293 in_format = gst_structure_get_string (ins, "format");
297 GST_DEBUG_OBJECT (base, "source format %s", in_format);
300 gst_video_format_get_info (gst_video_format_from_string (in_format));
304 outs = gst_caps_get_structure (result, 0);
306 capslen = gst_caps_get_size (result);
307 GST_DEBUG_OBJECT (base, "iterate %d structures", capslen);
308 for (i = 0; i < capslen; i++) {
310 const GValue *format;
312 tests = gst_caps_get_structure (result, i);
313 format = gst_structure_get_value (tests, "format");
314 /* should not happen */
318 if (GST_VALUE_HOLDS_LIST (format)) {
321 len = gst_value_list_get_size (format);
322 GST_DEBUG_OBJECT (base, "have %d formats", len);
323 for (j = 0; j < len; j++) {
326 val = gst_value_list_get_value (format, j);
327 if (G_VALUE_HOLDS_STRING (val)) {
328 score_value (base, in_info, val, &min_loss, &out_info);
333 } else if (G_VALUE_HOLDS_STRING (format)) {
334 score_value (base, in_info, format, &min_loss, &out_info);
338 gst_structure_set (outs, "format", G_TYPE_STRING,
339 GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
343 gst_cuda_convert_fixate_caps (GstBaseTransform * trans,
344 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
348 GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
349 " based on caps %" GST_PTR_FORMAT, othercaps, caps);
351 result = gst_caps_intersect (othercaps, caps);
352 if (gst_caps_is_empty (result)) {
353 gst_caps_unref (result);
356 gst_caps_unref (othercaps);
359 GST_DEBUG_OBJECT (trans, "now fixating %" GST_PTR_FORMAT, result);
361 result = gst_caps_make_writable (result);
362 gst_cuda_convert_fixate_format (trans, caps, result);
364 /* fixate remaining fields */
365 result = gst_caps_fixate (result);
367 if (direction == GST_PAD_SINK) {
368 if (gst_caps_is_subset (caps, result)) {
369 gst_caps_replace (&result, caps);
377 gst_cuda_convert_filter_meta (GstBaseTransform * trans, GstQuery * query,
378 GType api, const GstStructure * params)
380 /* This element cannot passthrough the crop meta, because it would convert the
381 * wrong sub-region of the image, and worst, our output image may not be large
382 * enough for the crop to be applied later */
383 if (api == GST_VIDEO_CROP_META_API_TYPE)
386 /* propose all other metadata upstream */
390 #define CHECK_INFO_FIELDS_MATCHES(field) { \
391 if (in_info->field != in_info->field) { \
392 GST_ERROR_OBJECT (btrans, "%s do not match %d != %d", G_STRINGIFY(field), \
393 in_info->field, out_info->field); \
399 gst_cuda_convert_set_info (GstCudaBaseTransform * btrans, GstCaps * incaps,
400 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
402 CHECK_INFO_FIELDS_MATCHES (width);
403 CHECK_INFO_FIELDS_MATCHES (height);
404 CHECK_INFO_FIELDS_MATCHES (fps_n);
405 CHECK_INFO_FIELDS_MATCHES (fps_d);
406 CHECK_INFO_FIELDS_MATCHES (par_n);
408 /* if present, these must match too */
409 CHECK_INFO_FIELDS_MATCHES (par_d);
410 CHECK_INFO_FIELDS_MATCHES (interlace_mode);
413 return GST_CUDA_BASE_TRANSFORM_CLASS (parent_class)->set_info (btrans, incaps,
414 in_info, outcaps, out_info);
417 #undef CHECK_INFO_FIELDS_MATCHES