1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * This file:
4  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
5  * Copyright (C) 2010 David Schleef <ds@schleef.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:element-videoconvert
25  * @title: videoconvert
26  *
27  * Convert video frames between a great variety of video formats.
28  *
29  * ## Example launch line
30  * |[
31  * gst-launch-1.0 -v videotestsrc ! video/x-raw,format=YUY2 ! videoconvert ! autovideosink
32  * ]|
33  *  This will output a test video (generated in YUY2 format) in a video
34  * window. If the video sink selected does not support YUY2 videoconvert will
35  * automatically convert the video to a format understood by the video sink.
36  *
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42 
43 #include "gstvideoconvert.h"
44 
45 #include <gst/video/video.h>
46 #include <gst/video/gstvideometa.h>
47 #include <gst/video/gstvideopool.h>
48 
49 #include <string.h>
50 
51 GST_DEBUG_CATEGORY (videoconvert_debug);
52 #define GST_CAT_DEFAULT videoconvert_debug
53 GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
54 
55 GType gst_video_convert_get_type (void);
56 
57 static GQuark _colorspace_quark;
58 
59 #define gst_video_convert_parent_class parent_class
60 G_DEFINE_TYPE (GstVideoConvert, gst_video_convert, GST_TYPE_VIDEO_FILTER);
61 
62 #define DEFAULT_PROP_DITHER      GST_VIDEO_DITHER_BAYER
63 #define DEFAULT_PROP_DITHER_QUANTIZATION 1
64 #define DEFAULT_PROP_CHROMA_RESAMPLER	GST_VIDEO_RESAMPLER_METHOD_LINEAR
65 #define DEFAULT_PROP_ALPHA_MODE GST_VIDEO_ALPHA_MODE_COPY
66 #define DEFAULT_PROP_ALPHA_VALUE 1.0
67 #define DEFAULT_PROP_CHROMA_MODE GST_VIDEO_CHROMA_MODE_FULL
68 #define DEFAULT_PROP_MATRIX_MODE GST_VIDEO_MATRIX_MODE_FULL
69 #define DEFAULT_PROP_GAMMA_MODE GST_VIDEO_GAMMA_MODE_NONE
70 #define DEFAULT_PROP_PRIMARIES_MODE GST_VIDEO_PRIMARIES_MODE_NONE
71 #define DEFAULT_PROP_N_THREADS 1
72 
73 enum
74 {
75   PROP_0,
76   PROP_DITHER,
77   PROP_DITHER_QUANTIZATION,
78   PROP_CHROMA_RESAMPLER,
79   PROP_ALPHA_MODE,
80   PROP_ALPHA_VALUE,
81   PROP_CHROMA_MODE,
82   PROP_MATRIX_MODE,
83   PROP_GAMMA_MODE,
84   PROP_PRIMARIES_MODE,
85   PROP_N_THREADS
86 };
87 
88 #define CSP_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ";" \
89     GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
90 
91 static GstStaticPadTemplate gst_video_convert_src_template =
92 GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS (CSP_VIDEO_CAPS)
96     );
97 
98 static GstStaticPadTemplate gst_video_convert_sink_template =
99 GST_STATIC_PAD_TEMPLATE ("sink",
100     GST_PAD_SINK,
101     GST_PAD_ALWAYS,
102     GST_STATIC_CAPS (CSP_VIDEO_CAPS)
103     );
104 
105 static void gst_video_convert_set_property (GObject * object,
106     guint property_id, const GValue * value, GParamSpec * pspec);
107 static void gst_video_convert_get_property (GObject * object,
108     guint property_id, GValue * value, GParamSpec * pspec);
109 
110 static gboolean gst_video_convert_set_info (GstVideoFilter * filter,
111     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
112     GstVideoInfo * out_info);
113 static GstFlowReturn gst_video_convert_transform_frame (GstVideoFilter * filter,
114     GstVideoFrame * in_frame, GstVideoFrame * out_frame);
115 
116 /* copies the given caps */
117 static GstCaps *
gst_video_convert_caps_remove_format_info(GstCaps * caps)118 gst_video_convert_caps_remove_format_info (GstCaps * caps)
119 {
120   GstStructure *st;
121   GstCapsFeatures *f;
122   gint i, n;
123   GstCaps *res;
124 
125   res = gst_caps_new_empty ();
126 
127   n = gst_caps_get_size (caps);
128   for (i = 0; i < n; i++) {
129     st = gst_caps_get_structure (caps, i);
130     f = gst_caps_get_features (caps, i);
131 
132     /* If this is already expressed by the existing caps
133      * skip this structure */
134     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
135       continue;
136 
137     st = gst_structure_copy (st);
138     /* Only remove format info for the cases when we can actually convert */
139     if (!gst_caps_features_is_any (f)
140         && gst_caps_features_is_equal (f,
141             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))
142       gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
143           NULL);
144 
145     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
146   }
147 
148   return res;
149 }
150 
151 /*
152  * This is an incomplete matrix of in formats and a score for the prefered output
153  * format.
154  *
155  *         out: RGB24   RGB16  ARGB  AYUV  YUV444  YUV422 YUV420 YUV411 YUV410  PAL  GRAY
156  *  in
157  * RGB24          0      2       1     2     2       3      4      5      6      7    8
158  * RGB16          1      0       1     2     2       3      4      5      6      7    8
159  * ARGB           2      3       0     1     4       5      6      7      8      9    10
160  * AYUV           3      4       1     0     2       5      6      7      8      9    10
161  * YUV444         2      4       3     1     0       5      6      7      8      9    10
162  * YUV422         3      5       4     2     1       0      6      7      8      9    10
163  * YUV420         4      6       5     3     2       1      0      7      8      9    10
164  * YUV411         4      6       5     3     2       1      7      0      8      9    10
165  * YUV410         6      8       7     5     4       3      2      1      0      9    10
166  * PAL            1      3       2     6     4       6      7      8      9      0    10
167  * GRAY           1      4       3     2     1       5      6      7      8      9    0
168  *
169  * PAL or GRAY are never prefered, if we can we would convert to PAL instead
170  * of GRAY, though
171  * less subsampling is prefered and if any, preferably horizontal
172  * We would like to keep the alpha, even if we would need to to colorspace conversion
173  * or lose depth.
174  */
175 #define SCORE_FORMAT_CHANGE       1
176 #define SCORE_DEPTH_CHANGE        1
177 #define SCORE_ALPHA_CHANGE        1
178 #define SCORE_CHROMA_W_CHANGE     1
179 #define SCORE_CHROMA_H_CHANGE     1
180 #define SCORE_PALETTE_CHANGE      1
181 
182 #define SCORE_COLORSPACE_LOSS     2     /* RGB <-> YUV */
183 #define SCORE_DEPTH_LOSS          4     /* change bit depth */
184 #define SCORE_ALPHA_LOSS          8     /* lose the alpha channel */
185 #define SCORE_CHROMA_W_LOSS      16     /* vertical subsample */
186 #define SCORE_CHROMA_H_LOSS      32     /* horizontal subsample */
187 #define SCORE_PALETTE_LOSS       64     /* convert to palette format */
188 #define SCORE_COLOR_LOSS        128     /* convert to GRAY */
189 
190 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
191                          GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
192 #define ALPHA_MASK      (GST_VIDEO_FORMAT_FLAG_ALPHA)
193 #define PALETTE_MASK    (GST_VIDEO_FORMAT_FLAG_PALETTE)
194 
195 /* calculate how much loss a conversion would be */
196 static void
score_value(GstBaseTransform * base,const GstVideoFormatInfo * in_info,const GValue * val,gint * min_loss,const GstVideoFormatInfo ** out_info)197 score_value (GstBaseTransform * base, const GstVideoFormatInfo * in_info,
198     const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
199 {
200   const gchar *fname;
201   const GstVideoFormatInfo *t_info;
202   GstVideoFormatFlags in_flags, t_flags;
203   gint loss;
204 
205   fname = g_value_get_string (val);
206   t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
207   if (!t_info)
208     return;
209 
210   /* accept input format immediately without loss */
211   if (in_info == t_info) {
212     *min_loss = 0;
213     *out_info = t_info;
214     return;
215   }
216 
217   loss = SCORE_FORMAT_CHANGE;
218 
219   in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
220   in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
221   in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
222   in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
223 
224   t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
225   t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
226   t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
227   t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
228 
229   if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
230     loss += SCORE_PALETTE_CHANGE;
231     if (t_flags & PALETTE_MASK)
232       loss += SCORE_PALETTE_LOSS;
233   }
234 
235   if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
236     loss += SCORE_COLORSPACE_LOSS;
237     if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
238       loss += SCORE_COLOR_LOSS;
239   }
240 
241   if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
242     loss += SCORE_ALPHA_CHANGE;
243     if (in_flags & ALPHA_MASK)
244       loss += SCORE_ALPHA_LOSS;
245   }
246 
247   if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
248     loss += SCORE_CHROMA_H_CHANGE;
249     if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
250       loss += SCORE_CHROMA_H_LOSS;
251   }
252   if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
253     loss += SCORE_CHROMA_W_CHANGE;
254     if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
255       loss += SCORE_CHROMA_W_LOSS;
256   }
257 
258   if ((in_info->bits) != (t_info->bits)) {
259     loss += SCORE_DEPTH_CHANGE;
260     if ((in_info->bits) > (t_info->bits))
261       loss += SCORE_DEPTH_LOSS;
262   }
263 
264   GST_DEBUG_OBJECT (base, "score %s -> %s = %d",
265       GST_VIDEO_FORMAT_INFO_NAME (in_info),
266       GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
267 
268   if (loss < *min_loss) {
269     GST_DEBUG_OBJECT (base, "found new best %d", loss);
270     *out_info = t_info;
271     *min_loss = loss;
272   }
273 }
274 
275 static void
gst_video_convert_fixate_format(GstBaseTransform * base,GstCaps * caps,GstCaps * result)276 gst_video_convert_fixate_format (GstBaseTransform * base, GstCaps * caps,
277     GstCaps * result)
278 {
279   GstStructure *ins, *outs;
280   const gchar *in_format;
281   const GstVideoFormatInfo *in_info, *out_info = NULL;
282   gint min_loss = G_MAXINT;
283   guint i, capslen;
284 
285   ins = gst_caps_get_structure (caps, 0);
286   in_format = gst_structure_get_string (ins, "format");
287   if (!in_format)
288     return;
289 
290   GST_DEBUG_OBJECT (base, "source format %s", in_format);
291 
292   in_info =
293       gst_video_format_get_info (gst_video_format_from_string (in_format));
294   if (!in_info)
295     return;
296 
297   outs = gst_caps_get_structure (result, 0);
298 
299   capslen = gst_caps_get_size (result);
300   GST_DEBUG_OBJECT (base, "iterate %d structures", capslen);
301   for (i = 0; i < capslen; i++) {
302     GstStructure *tests;
303     const GValue *format;
304 
305     tests = gst_caps_get_structure (result, i);
306     format = gst_structure_get_value (tests, "format");
307     /* should not happen */
308     if (format == NULL)
309       continue;
310 
311     if (GST_VALUE_HOLDS_LIST (format)) {
312       gint j, len;
313 
314       len = gst_value_list_get_size (format);
315       GST_DEBUG_OBJECT (base, "have %d formats", len);
316       for (j = 0; j < len; j++) {
317         const GValue *val;
318 
319         val = gst_value_list_get_value (format, j);
320         if (G_VALUE_HOLDS_STRING (val)) {
321           score_value (base, in_info, val, &min_loss, &out_info);
322           if (min_loss == 0)
323             break;
324         }
325       }
326     } else if (G_VALUE_HOLDS_STRING (format)) {
327       score_value (base, in_info, format, &min_loss, &out_info);
328     }
329   }
330   if (out_info)
331     gst_structure_set (outs, "format", G_TYPE_STRING,
332         GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
333 }
334 
335 
336 static GstCaps *
gst_video_convert_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)337 gst_video_convert_fixate_caps (GstBaseTransform * trans,
338     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
339 {
340   GstCaps *result;
341 
342   GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
343       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
344 
345   result = gst_caps_intersect (othercaps, caps);
346   if (gst_caps_is_empty (result)) {
347     gst_caps_unref (result);
348     result = othercaps;
349   } else {
350     gst_caps_unref (othercaps);
351   }
352 
353   GST_DEBUG_OBJECT (trans, "now fixating %" GST_PTR_FORMAT, result);
354 
355   result = gst_caps_make_writable (result);
356   gst_video_convert_fixate_format (trans, caps, result);
357 
358   /* fixate remaining fields */
359   result = gst_caps_fixate (result);
360 
361   if (direction == GST_PAD_SINK) {
362     if (gst_caps_is_subset (caps, result)) {
363       gst_caps_replace (&result, caps);
364     }
365   }
366 
367   return result;
368 }
369 
370 static gboolean
gst_video_convert_filter_meta(GstBaseTransform * trans,GstQuery * query,GType api,const GstStructure * params)371 gst_video_convert_filter_meta (GstBaseTransform * trans, GstQuery * query,
372     GType api, const GstStructure * params)
373 {
374   /* This element cannot passthrough the crop meta, because it would convert the
375    * wrong sub-region of the image, and worst, our output image may not be large
376    * enough for the crop to be applied later */
377   if (api == GST_VIDEO_CROP_META_API_TYPE)
378     return FALSE;
379 
380   /* propose all other metadata upstream */
381   return TRUE;
382 }
383 
384 /* The caps can be transformed into any other caps with format info removed.
385  * However, we should prefer passthrough, so if passthrough is possible,
386  * put it first in the list. */
387 static GstCaps *
gst_video_convert_transform_caps(GstBaseTransform * btrans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)388 gst_video_convert_transform_caps (GstBaseTransform * btrans,
389     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
390 {
391   GstCaps *tmp, *tmp2;
392   GstCaps *result;
393 
394   /* Get all possible caps that we can transform to */
395   tmp = gst_video_convert_caps_remove_format_info (caps);
396 
397   if (filter) {
398     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
399     gst_caps_unref (tmp);
400     tmp = tmp2;
401   }
402 
403   result = tmp;
404 
405   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
406       GST_PTR_FORMAT, caps, result);
407 
408   return result;
409 }
410 
411 static gboolean
gst_video_convert_transform_meta(GstBaseTransform * trans,GstBuffer * outbuf,GstMeta * meta,GstBuffer * inbuf)412 gst_video_convert_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
413     GstMeta * meta, GstBuffer * inbuf)
414 {
415   const GstMetaInfo *info = meta->info;
416   gboolean ret;
417 
418   if (gst_meta_api_type_has_tag (info->api, _colorspace_quark)) {
419     /* don't copy colorspace specific metadata, FIXME, we need a MetaTransform
420      * for the colorspace metadata. */
421     ret = FALSE;
422   } else {
423     /* copy other metadata */
424     ret = TRUE;
425   }
426   return ret;
427 }
428 
429 static gboolean
gst_video_convert_set_info(GstVideoFilter * filter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)430 gst_video_convert_set_info (GstVideoFilter * filter,
431     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
432     GstVideoInfo * out_info)
433 {
434   GstVideoConvert *space;
435 
436   space = GST_VIDEO_CONVERT_CAST (filter);
437 
438   if (space->convert) {
439     gst_video_converter_free (space->convert);
440     space->convert = NULL;
441   }
442 
443   /* these must match */
444   if (in_info->width != out_info->width || in_info->height != out_info->height
445       || in_info->fps_n != out_info->fps_n || in_info->fps_d != out_info->fps_d)
446     goto format_mismatch;
447 
448   /* if present, these must match too */
449   if (in_info->par_n != out_info->par_n || in_info->par_d != out_info->par_d)
450     goto format_mismatch;
451 
452   /* if present, these must match too */
453   if (in_info->interlace_mode != out_info->interlace_mode)
454     goto format_mismatch;
455 
456 
457   space->convert = gst_video_converter_new (in_info, out_info,
458       gst_structure_new ("GstVideoConvertConfig",
459           GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD,
460           space->dither,
461           GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION, G_TYPE_UINT,
462           space->dither_quantization,
463           GST_VIDEO_CONVERTER_OPT_CHROMA_RESAMPLER_METHOD,
464           GST_TYPE_VIDEO_RESAMPLER_METHOD, space->chroma_resampler,
465           GST_VIDEO_CONVERTER_OPT_ALPHA_MODE,
466           GST_TYPE_VIDEO_ALPHA_MODE, space->alpha_mode,
467           GST_VIDEO_CONVERTER_OPT_ALPHA_VALUE,
468           G_TYPE_DOUBLE, space->alpha_value,
469           GST_VIDEO_CONVERTER_OPT_CHROMA_MODE,
470           GST_TYPE_VIDEO_CHROMA_MODE, space->chroma_mode,
471           GST_VIDEO_CONVERTER_OPT_MATRIX_MODE,
472           GST_TYPE_VIDEO_MATRIX_MODE, space->matrix_mode,
473           GST_VIDEO_CONVERTER_OPT_GAMMA_MODE,
474           GST_TYPE_VIDEO_GAMMA_MODE, space->gamma_mode,
475           GST_VIDEO_CONVERTER_OPT_PRIMARIES_MODE,
476           GST_TYPE_VIDEO_PRIMARIES_MODE, space->primaries_mode,
477           GST_VIDEO_CONVERTER_OPT_THREADS, G_TYPE_UINT,
478           space->n_threads, NULL));
479   if (space->convert == NULL)
480     goto no_convert;
481 
482   GST_DEBUG ("reconfigured %d %d", GST_VIDEO_INFO_FORMAT (in_info),
483       GST_VIDEO_INFO_FORMAT (out_info));
484 
485   return TRUE;
486 
487   /* ERRORS */
488 format_mismatch:
489   {
490     GST_ERROR_OBJECT (space, "input and output formats do not match");
491     return FALSE;
492   }
493 no_convert:
494   {
495     GST_ERROR_OBJECT (space, "could not create converter");
496     return FALSE;
497   }
498 }
499 
500 static void
gst_video_convert_finalize(GObject * obj)501 gst_video_convert_finalize (GObject * obj)
502 {
503   GstVideoConvert *space = GST_VIDEO_CONVERT (obj);
504 
505   if (space->convert) {
506     gst_video_converter_free (space->convert);
507   }
508 
509   G_OBJECT_CLASS (parent_class)->finalize (obj);
510 }
511 
512 static void
gst_video_convert_class_init(GstVideoConvertClass * klass)513 gst_video_convert_class_init (GstVideoConvertClass * klass)
514 {
515   GObjectClass *gobject_class = (GObjectClass *) klass;
516   GstElementClass *gstelement_class = (GstElementClass *) klass;
517   GstBaseTransformClass *gstbasetransform_class =
518       (GstBaseTransformClass *) klass;
519   GstVideoFilterClass *gstvideofilter_class = (GstVideoFilterClass *) klass;
520 
521   gobject_class->set_property = gst_video_convert_set_property;
522   gobject_class->get_property = gst_video_convert_get_property;
523   gobject_class->finalize = gst_video_convert_finalize;
524 
525   gst_element_class_add_static_pad_template (gstelement_class,
526       &gst_video_convert_src_template);
527   gst_element_class_add_static_pad_template (gstelement_class,
528       &gst_video_convert_sink_template);
529 
530   gst_element_class_set_static_metadata (gstelement_class,
531       "Colorspace converter", "Filter/Converter/Video",
532       "Converts video from one colorspace to another",
533       "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
534 
535   gstbasetransform_class->transform_caps =
536       GST_DEBUG_FUNCPTR (gst_video_convert_transform_caps);
537   gstbasetransform_class->fixate_caps =
538       GST_DEBUG_FUNCPTR (gst_video_convert_fixate_caps);
539   gstbasetransform_class->filter_meta =
540       GST_DEBUG_FUNCPTR (gst_video_convert_filter_meta);
541   gstbasetransform_class->transform_meta =
542       GST_DEBUG_FUNCPTR (gst_video_convert_transform_meta);
543 
544   gstbasetransform_class->passthrough_on_same_caps = TRUE;
545 
546   gstvideofilter_class->set_info =
547       GST_DEBUG_FUNCPTR (gst_video_convert_set_info);
548   gstvideofilter_class->transform_frame =
549       GST_DEBUG_FUNCPTR (gst_video_convert_transform_frame);
550 
551   g_object_class_install_property (gobject_class, PROP_DITHER,
552       g_param_spec_enum ("dither", "Dither", "Apply dithering while converting",
553           gst_video_dither_method_get_type (), DEFAULT_PROP_DITHER,
554           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
555   g_object_class_install_property (gobject_class, PROP_DITHER_QUANTIZATION,
556       g_param_spec_uint ("dither-quantization", "Dither Quantize",
557           "Quantizer to use", 0, G_MAXUINT, DEFAULT_PROP_DITHER_QUANTIZATION,
558           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
559   g_object_class_install_property (gobject_class, PROP_CHROMA_RESAMPLER,
560       g_param_spec_enum ("chroma-resampler", "Chroma resampler",
561           "Chroma resampler method", gst_video_resampler_method_get_type (),
562           DEFAULT_PROP_CHROMA_RESAMPLER,
563           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
564   g_object_class_install_property (gobject_class, PROP_ALPHA_MODE,
565       g_param_spec_enum ("alpha-mode", "Alpha Mode",
566           "Alpha Mode to use", gst_video_alpha_mode_get_type (),
567           DEFAULT_PROP_ALPHA_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
568   g_object_class_install_property (gobject_class, PROP_ALPHA_VALUE,
569       g_param_spec_double ("alpha-value", "Alpha Value",
570           "Alpha Value to use", 0.0, 1.0,
571           DEFAULT_PROP_ALPHA_VALUE,
572           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
573   g_object_class_install_property (gobject_class, PROP_CHROMA_MODE,
574       g_param_spec_enum ("chroma-mode", "Chroma Mode", "Chroma Resampling Mode",
575           gst_video_chroma_mode_get_type (), DEFAULT_PROP_CHROMA_MODE,
576           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577   g_object_class_install_property (gobject_class, PROP_MATRIX_MODE,
578       g_param_spec_enum ("matrix-mode", "Matrix Mode", "Matrix Conversion Mode",
579           gst_video_matrix_mode_get_type (), DEFAULT_PROP_MATRIX_MODE,
580           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
581   g_object_class_install_property (gobject_class, PROP_GAMMA_MODE,
582       g_param_spec_enum ("gamma-mode", "Gamma Mode", "Gamma Conversion Mode",
583           gst_video_gamma_mode_get_type (), DEFAULT_PROP_GAMMA_MODE,
584           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
585   g_object_class_install_property (gobject_class, PROP_PRIMARIES_MODE,
586       g_param_spec_enum ("primaries-mode", "Primaries Mode",
587           "Primaries Conversion Mode", gst_video_primaries_mode_get_type (),
588           DEFAULT_PROP_PRIMARIES_MODE,
589           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
590   g_object_class_install_property (gobject_class, PROP_N_THREADS,
591       g_param_spec_uint ("n-threads", "Threads",
592           "Maximum number of threads to use", 0, G_MAXUINT,
593           DEFAULT_PROP_N_THREADS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
594 }
595 
596 static void
gst_video_convert_init(GstVideoConvert * space)597 gst_video_convert_init (GstVideoConvert * space)
598 {
599   space->dither = DEFAULT_PROP_DITHER;
600   space->dither_quantization = DEFAULT_PROP_DITHER_QUANTIZATION;
601   space->chroma_resampler = DEFAULT_PROP_CHROMA_RESAMPLER;
602   space->alpha_mode = DEFAULT_PROP_ALPHA_MODE;
603   space->alpha_value = DEFAULT_PROP_ALPHA_VALUE;
604   space->chroma_mode = DEFAULT_PROP_CHROMA_MODE;
605   space->matrix_mode = DEFAULT_PROP_MATRIX_MODE;
606   space->gamma_mode = DEFAULT_PROP_GAMMA_MODE;
607   space->primaries_mode = DEFAULT_PROP_PRIMARIES_MODE;
608   space->n_threads = DEFAULT_PROP_N_THREADS;
609 }
610 
611 void
gst_video_convert_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)612 gst_video_convert_set_property (GObject * object, guint property_id,
613     const GValue * value, GParamSpec * pspec)
614 {
615   GstVideoConvert *csp;
616 
617   csp = GST_VIDEO_CONVERT (object);
618 
619   switch (property_id) {
620     case PROP_DITHER:
621       csp->dither = g_value_get_enum (value);
622       break;
623     case PROP_CHROMA_RESAMPLER:
624       csp->chroma_resampler = g_value_get_enum (value);
625       break;
626     case PROP_ALPHA_MODE:
627       csp->alpha_mode = g_value_get_enum (value);
628       break;
629     case PROP_ALPHA_VALUE:
630       csp->alpha_value = g_value_get_double (value);
631       break;
632     case PROP_CHROMA_MODE:
633       csp->chroma_mode = g_value_get_enum (value);
634       break;
635     case PROP_MATRIX_MODE:
636       csp->matrix_mode = g_value_get_enum (value);
637       break;
638     case PROP_GAMMA_MODE:
639       csp->gamma_mode = g_value_get_enum (value);
640       break;
641     case PROP_PRIMARIES_MODE:
642       csp->primaries_mode = g_value_get_enum (value);
643       break;
644     case PROP_DITHER_QUANTIZATION:
645       csp->dither_quantization = g_value_get_uint (value);
646       break;
647     case PROP_N_THREADS:
648       csp->n_threads = g_value_get_uint (value);
649       break;
650     default:
651       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
652       break;
653   }
654 }
655 
656 void
gst_video_convert_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)657 gst_video_convert_get_property (GObject * object, guint property_id,
658     GValue * value, GParamSpec * pspec)
659 {
660   GstVideoConvert *csp;
661 
662   csp = GST_VIDEO_CONVERT (object);
663 
664   switch (property_id) {
665     case PROP_DITHER:
666       g_value_set_enum (value, csp->dither);
667       break;
668     case PROP_CHROMA_RESAMPLER:
669       g_value_set_enum (value, csp->chroma_resampler);
670       break;
671     case PROP_ALPHA_MODE:
672       g_value_set_enum (value, csp->alpha_mode);
673       break;
674     case PROP_ALPHA_VALUE:
675       g_value_set_double (value, csp->alpha_value);
676       break;
677     case PROP_CHROMA_MODE:
678       g_value_set_enum (value, csp->chroma_mode);
679       break;
680     case PROP_MATRIX_MODE:
681       g_value_set_enum (value, csp->matrix_mode);
682       break;
683     case PROP_GAMMA_MODE:
684       g_value_set_enum (value, csp->gamma_mode);
685       break;
686     case PROP_PRIMARIES_MODE:
687       g_value_set_enum (value, csp->primaries_mode);
688       break;
689     case PROP_DITHER_QUANTIZATION:
690       g_value_set_uint (value, csp->dither_quantization);
691       break;
692     case PROP_N_THREADS:
693       g_value_set_uint (value, csp->n_threads);
694       break;
695     default:
696       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
697       break;
698   }
699 }
700 
701 static GstFlowReturn
gst_video_convert_transform_frame(GstVideoFilter * filter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)702 gst_video_convert_transform_frame (GstVideoFilter * filter,
703     GstVideoFrame * in_frame, GstVideoFrame * out_frame)
704 {
705   GstVideoConvert *space;
706 
707   space = GST_VIDEO_CONVERT_CAST (filter);
708 
709   GST_CAT_DEBUG_OBJECT (CAT_PERFORMANCE, filter,
710       "doing colorspace conversion from %s -> to %s",
711       GST_VIDEO_INFO_NAME (&filter->in_info),
712       GST_VIDEO_INFO_NAME (&filter->out_info));
713 
714   gst_video_converter_frame (space->convert, in_frame, out_frame);
715 
716   return GST_FLOW_OK;
717 }
718 
719 static gboolean
plugin_init(GstPlugin * plugin)720 plugin_init (GstPlugin * plugin)
721 {
722   GST_DEBUG_CATEGORY_INIT (videoconvert_debug, "videoconvert", 0,
723       "Colorspace Converter");
724 
725   GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
726 
727   _colorspace_quark = g_quark_from_static_string ("colorspace");
728 
729   return gst_element_register (plugin, "videoconvert",
730       GST_RANK_NONE, GST_TYPE_VIDEO_CONVERT);
731 }
732 
733 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
734     GST_VERSION_MINOR,
735     videoconvert, "Colorspace conversion", plugin_init, VERSION, GST_LICENSE,
736     GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
737