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