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
43 #include "gstcudaconvert.h"
44 #include "gstcudautils.h"
46 GST_DEBUG_CATEGORY_STATIC (gst_cuda_convert_debug);
47 #define GST_CAT_DEFAULT gst_cuda_convert_debug
49 #define gst_cuda_convert_parent_class parent_class
50 G_DEFINE_TYPE (GstCudaConvert, gst_cuda_convert, GST_TYPE_CUDA_BASE_FILTER);
52 static GstCaps *gst_cuda_convert_transform_caps (GstBaseTransform * trans,
53 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
54 static GstCaps *gst_cuda_convert_fixate_caps (GstBaseTransform * base,
55 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
56 static gboolean gst_cuda_convert_filter_meta (GstBaseTransform * trans,
57 GstQuery * query, GType api, const GstStructure * params);
59 gst_cuda_convert_set_info (GstCudaBaseTransform * btrans, GstCaps * incaps,
60 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info);
62 /* copies the given caps */
64 gst_cuda_convert_caps_remove_format_info (GstCaps * caps)
70 GstCapsFeatures *feature =
71 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY);
73 res = gst_caps_new_empty ();
75 n = gst_caps_get_size (caps);
76 for (i = 0; i < n; i++) {
77 st = gst_caps_get_structure (caps, i);
78 f = gst_caps_get_features (caps, i);
80 /* If this is already expressed by the existing caps
81 * skip this structure */
82 if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
85 st = gst_structure_copy (st);
86 /* Only remove format info for the cases when we can actually convert */
87 if (!gst_caps_features_is_any (f)
88 && gst_caps_features_is_equal (f, feature))
89 gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
92 gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
94 gst_caps_features_free (feature);
100 * This is an incomplete matrix of in formats and a score for the prefered output
103 * out: RGB24 RGB16 ARGB AYUV YUV444 YUV422 YUV420 YUV411 YUV410 PAL GRAY
105 * RGB24 0 2 1 2 2 3 4 5 6 7 8
106 * RGB16 1 0 1 2 2 3 4 5 6 7 8
107 * ARGB 2 3 0 1 4 5 6 7 8 9 10
108 * AYUV 3 4 1 0 2 5 6 7 8 9 10
109 * YUV444 2 4 3 1 0 5 6 7 8 9 10
110 * YUV422 3 5 4 2 1 0 6 7 8 9 10
111 * YUV420 4 6 5 3 2 1 0 7 8 9 10
112 * YUV411 4 6 5 3 2 1 7 0 8 9 10
113 * YUV410 6 8 7 5 4 3 2 1 0 9 10
114 * PAL 1 3 2 6 4 6 7 8 9 0 10
115 * GRAY 1 4 3 2 1 5 6 7 8 9 0
117 * PAL or GRAY are never preferred, if we can we would convert to PAL instead
119 * less subsampling is preferred and if any, preferably horizontal
120 * We would like to keep the alpha, even if we would need to to colorspace conversion
123 #define SCORE_FORMAT_CHANGE 1
124 #define SCORE_DEPTH_CHANGE 1
125 #define SCORE_ALPHA_CHANGE 1
126 #define SCORE_CHROMA_W_CHANGE 1
127 #define SCORE_CHROMA_H_CHANGE 1
128 #define SCORE_PALETTE_CHANGE 1
130 #define SCORE_COLORSPACE_LOSS 2 /* RGB <-> YUV */
131 #define SCORE_DEPTH_LOSS 4 /* change bit depth */
132 #define SCORE_ALPHA_LOSS 8 /* lose the alpha channel */
133 #define SCORE_CHROMA_W_LOSS 16 /* vertical subsample */
134 #define SCORE_CHROMA_H_LOSS 32 /* horizontal subsample */
135 #define SCORE_PALETTE_LOSS 64 /* convert to palette format */
136 #define SCORE_COLOR_LOSS 128 /* convert to GRAY */
138 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
139 GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
140 #define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
141 #define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
143 /* calculate how much loss a conversion would be */
145 score_value (GstBaseTransform * base, const GstVideoFormatInfo * in_info,
146 const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
149 const GstVideoFormatInfo *t_info;
150 GstVideoFormatFlags in_flags, t_flags;
153 fname = g_value_get_string (val);
154 t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
158 /* accept input format immediately without loss */
159 if (in_info == t_info) {
165 loss = SCORE_FORMAT_CHANGE;
167 in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
168 in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
169 in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
170 in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
172 t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
173 t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
174 t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
175 t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
177 if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
178 loss += SCORE_PALETTE_CHANGE;
179 if (t_flags & PALETTE_MASK)
180 loss += SCORE_PALETTE_LOSS;
183 if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
184 loss += SCORE_COLORSPACE_LOSS;
185 if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
186 loss += SCORE_COLOR_LOSS;
189 if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
190 loss += SCORE_ALPHA_CHANGE;
191 if (in_flags & ALPHA_MASK)
192 loss += SCORE_ALPHA_LOSS;
195 if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
196 loss += SCORE_CHROMA_H_CHANGE;
197 if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
198 loss += SCORE_CHROMA_H_LOSS;
200 if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
201 loss += SCORE_CHROMA_W_CHANGE;
202 if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
203 loss += SCORE_CHROMA_W_LOSS;
206 if ((in_info->bits) != (t_info->bits)) {
207 loss += SCORE_DEPTH_CHANGE;
208 if ((in_info->bits) > (t_info->bits))
209 loss += SCORE_DEPTH_LOSS;
212 GST_DEBUG_OBJECT (base, "score %s -> %s = %d",
213 GST_VIDEO_FORMAT_INFO_NAME (in_info),
214 GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
216 if (loss < *min_loss) {
217 GST_DEBUG_OBJECT (base, "found new best %d", loss);
224 gst_cuda_convert_class_init (GstCudaConvertClass * klass)
226 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
227 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
228 GstCudaBaseTransformClass *btrans_class =
229 GST_CUDA_BASE_TRANSFORM_CLASS (klass);
231 gst_element_class_set_static_metadata (element_class,
232 "CUDA Colorspace converter",
233 "Filter/Converter/Video/Hardware",
234 "Converts video from one colorspace to another using CUDA",
235 "Seungha Yang <seungha.yang@navercorp.com>");
237 trans_class->passthrough_on_same_caps = TRUE;
239 trans_class->transform_caps =
240 GST_DEBUG_FUNCPTR (gst_cuda_convert_transform_caps);
241 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_cuda_convert_fixate_caps);
242 trans_class->filter_meta = GST_DEBUG_FUNCPTR (gst_cuda_convert_filter_meta);
244 btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_cuda_convert_set_info);
246 GST_DEBUG_CATEGORY_INIT (gst_cuda_convert_debug,
247 "cudaconvert", 0, "Video ColorSpace convert using CUDA");
251 gst_cuda_convert_init (GstCudaConvert * convert)
256 gst_cuda_convert_transform_caps (GstBaseTransform * trans,
257 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
262 /* Get all possible caps that we can transform to */
263 tmp = gst_cuda_convert_caps_remove_format_info (caps);
266 tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
267 gst_caps_unref (tmp);
273 GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
274 GST_PTR_FORMAT, caps, result);
279 /* fork of gstvideoconvert */
281 gst_cuda_convert_fixate_format (GstBaseTransform * base, GstCaps * caps,
284 GstStructure *ins, *outs;
285 const gchar *in_format;
286 const GstVideoFormatInfo *in_info, *out_info = NULL;
287 gint min_loss = G_MAXINT;
290 ins = gst_caps_get_structure (caps, 0);
291 in_format = gst_structure_get_string (ins, "format");
295 GST_DEBUG_OBJECT (base, "source format %s", in_format);
298 gst_video_format_get_info (gst_video_format_from_string (in_format));
302 outs = gst_caps_get_structure (result, 0);
304 capslen = gst_caps_get_size (result);
305 GST_DEBUG_OBJECT (base, "iterate %d structures", capslen);
306 for (i = 0; i < capslen; i++) {
308 const GValue *format;
310 tests = gst_caps_get_structure (result, i);
311 format = gst_structure_get_value (tests, "format");
312 /* should not happen */
316 if (GST_VALUE_HOLDS_LIST (format)) {
319 len = gst_value_list_get_size (format);
320 GST_DEBUG_OBJECT (base, "have %d formats", len);
321 for (j = 0; j < len; j++) {
324 val = gst_value_list_get_value (format, j);
325 if (G_VALUE_HOLDS_STRING (val)) {
326 score_value (base, in_info, val, &min_loss, &out_info);
331 } else if (G_VALUE_HOLDS_STRING (format)) {
332 score_value (base, in_info, format, &min_loss, &out_info);
336 gst_structure_set (outs, "format", G_TYPE_STRING,
337 GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
341 gst_cuda_convert_fixate_caps (GstBaseTransform * trans,
342 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
346 GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
347 " based on caps %" GST_PTR_FORMAT, othercaps, caps);
349 result = gst_caps_intersect (othercaps, caps);
350 if (gst_caps_is_empty (result)) {
351 gst_caps_unref (result);
354 gst_caps_unref (othercaps);
357 GST_DEBUG_OBJECT (trans, "now fixating %" GST_PTR_FORMAT, result);
359 result = gst_caps_make_writable (result);
360 gst_cuda_convert_fixate_format (trans, caps, result);
362 /* fixate remaining fields */
363 result = gst_caps_fixate (result);
365 if (direction == GST_PAD_SINK) {
366 if (gst_caps_is_subset (caps, result)) {
367 gst_caps_replace (&result, caps);
375 gst_cuda_convert_filter_meta (GstBaseTransform * trans, GstQuery * query,
376 GType api, const GstStructure * params)
378 /* This element cannot passthrough the crop meta, because it would convert the
379 * wrong sub-region of the image, and worst, our output image may not be large
380 * enough for the crop to be applied later */
381 if (api == GST_VIDEO_CROP_META_API_TYPE)
384 /* propose all other metadata upstream */
389 gst_cuda_convert_set_info (GstCudaBaseTransform * btrans, GstCaps * incaps,
390 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
392 /* these must match */
393 if (in_info->width != out_info->width || in_info->height != out_info->height
394 || in_info->fps_n != out_info->fps_n || in_info->fps_d != out_info->fps_d)
395 goto format_mismatch;
397 /* if present, these must match too */
398 if (in_info->par_n != out_info->par_n || in_info->par_d != out_info->par_d)
399 goto format_mismatch;
401 /* if present, these must match too */
402 if (in_info->interlace_mode != out_info->interlace_mode)
403 goto format_mismatch;
405 return GST_CUDA_BASE_TRANSFORM_CLASS (parent_class)->set_info (btrans, incaps,
406 in_info, outcaps, out_info);
411 GST_ERROR_OBJECT (btrans, "input and output formats do not match");