Revert "videoconvert: prevent bad interlaced conversions"
[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  *
26  * Convert video frames between a great variety of video formats.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v videotestsrc ! video/x-raw,format=\(string\)YUY2 ! videoconvert ! ximagesink
32  * ]|
33  * </refsect2>
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #  include "config.h"
38 #endif
39
40 #include "gstvideoconvert.h"
41
42 #include <gst/video/video.h>
43 #include <gst/video/gstvideometa.h>
44 #include <gst/video/gstvideopool.h>
45
46 #include <string.h>
47
48 GST_DEBUG_CATEGORY (videoconvert_debug);
49 #define GST_CAT_DEFAULT videoconvert_debug
50 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
51
52 GType gst_video_convert_get_type (void);
53
54 static GQuark _colorspace_quark;
55
56 #define gst_video_convert_parent_class parent_class
57 G_DEFINE_TYPE (GstVideoConvert, gst_video_convert, GST_TYPE_VIDEO_FILTER);
58
59 enum
60 {
61   PROP_0,
62   PROP_DITHER
63 };
64
65 #define CSP_VIDEO_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)
66
67 static GstStaticPadTemplate gst_video_convert_src_template =
68 GST_STATIC_PAD_TEMPLATE ("src",
69     GST_PAD_SRC,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS (CSP_VIDEO_CAPS)
72     );
73
74 static GstStaticPadTemplate gst_video_convert_sink_template =
75 GST_STATIC_PAD_TEMPLATE ("sink",
76     GST_PAD_SINK,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS (CSP_VIDEO_CAPS)
79     );
80
81 static void gst_video_convert_set_property (GObject * object,
82     guint property_id, const GValue * value, GParamSpec * pspec);
83 static void gst_video_convert_get_property (GObject * object,
84     guint property_id, GValue * value, GParamSpec * pspec);
85
86 static gboolean gst_video_convert_set_info (GstVideoFilter * filter,
87     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
88     GstVideoInfo * out_info);
89 static GstFlowReturn gst_video_convert_transform_frame (GstVideoFilter * filter,
90     GstVideoFrame * in_frame, GstVideoFrame * out_frame);
91
92 static GType
93 dither_method_get_type (void)
94 {
95   static GType gtype = 0;
96
97   if (gtype == 0) {
98     static const GEnumValue values[] = {
99       {DITHER_NONE, "No dithering (default)", "none"},
100       {DITHER_VERTERR, "Vertical error propogation", "verterr"},
101       {DITHER_HALFTONE, "Half-tone", "halftone"},
102       {0, NULL, NULL}
103     };
104
105     gtype = g_enum_register_static ("GstVideoConvertDitherMethod", values);
106   }
107   return gtype;
108 }
109
110 /* copies the given caps */
111 static GstCaps *
112 gst_video_convert_caps_remove_format_info (GstCaps * caps)
113 {
114   GstStructure *st;
115   gint i, n;
116   GstCaps *res;
117
118   res = gst_caps_new_empty ();
119
120   n = gst_caps_get_size (caps);
121   for (i = 0; i < n; i++) {
122     st = gst_caps_get_structure (caps, i);
123
124     /* If this is already expressed by the existing caps
125      * skip this structure */
126     if (i > 0 && gst_caps_is_subset_structure (res, st))
127       continue;
128
129     st = gst_structure_copy (st);
130     gst_structure_remove_fields (st, "format",
131         "colorimetry", "chroma-site", NULL);
132
133     gst_caps_append_structure (res, st);
134   }
135
136   return res;
137 }
138
139 static GstCaps *
140 gst_video_convert_fixate_caps (GstBaseTransform * trans,
141     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
142 {
143   GstCaps *result;
144
145   GST_DEBUG_OBJECT (trans, "fixating caps %" GST_PTR_FORMAT, othercaps);
146
147   result = gst_caps_intersect (othercaps, caps);
148   if (gst_caps_is_empty (result)) {
149     gst_caps_unref (result);
150     result = othercaps;
151   } else {
152     gst_caps_unref (othercaps);
153   }
154
155   /* fixate remaining fields */
156   result = gst_caps_fixate (result);
157
158   return result;
159 }
160
161 static gboolean
162 gst_video_convert_filter_meta (GstBaseTransform * trans, GstQuery * query,
163     GType api, const GstStructure * params)
164 {
165   /* propose all metadata upstream */
166   return TRUE;
167 }
168
169 /* The caps can be transformed into any other caps with format info removed.
170  * However, we should prefer passthrough, so if passthrough is possible,
171  * put it first in the list. */
172 static GstCaps *
173 gst_video_convert_transform_caps (GstBaseTransform * btrans,
174     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
175 {
176   GstCaps *tmp, *tmp2;
177   GstCaps *result;
178
179   /* Get all possible caps that we can transform to */
180   tmp = gst_video_convert_caps_remove_format_info (caps);
181
182   if (filter) {
183     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
184     gst_caps_unref (tmp);
185     tmp = tmp2;
186   }
187
188   result = tmp;
189
190   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
191       GST_PTR_FORMAT, caps, result);
192
193   return result;
194 }
195
196 static gboolean
197 gst_video_convert_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
198     GstMeta * meta, GstBuffer * inbuf)
199 {
200   const GstMetaInfo *info = meta->info;
201   gboolean ret;
202
203   if (gst_meta_api_type_has_tag (info->api, _colorspace_quark)) {
204     /* don't copy colorspace specific metadata, FIXME, we need a MetaTransform
205      * for the colorspace metadata. */
206     ret = FALSE;
207   } else {
208     /* copy other metadata */
209     ret = TRUE;
210   }
211   return ret;
212 }
213
214 static gboolean
215 gst_video_convert_set_info (GstVideoFilter * filter,
216     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
217     GstVideoInfo * out_info)
218 {
219   GstVideoConvert *space;
220
221   space = GST_VIDEO_CONVERT_CAST (filter);
222
223   if (space->convert) {
224     videoconvert_convert_free (space->convert);
225   }
226
227   /* these must match */
228   if (in_info->width != out_info->width || in_info->height != out_info->height
229       || in_info->fps_n != out_info->fps_n || in_info->fps_d != out_info->fps_d)
230     goto format_mismatch;
231
232   /* if present, these must match too */
233   if (in_info->par_n != out_info->par_n || in_info->par_d != out_info->par_d)
234     goto format_mismatch;
235
236   /* if present, these must match too */
237   if (in_info->interlace_mode != out_info->interlace_mode)
238     goto format_mismatch;
239
240   space->convert = videoconvert_convert_new (in_info, out_info);
241   if (space->convert == NULL)
242     goto no_convert;
243
244   GST_DEBUG ("reconfigured %d %d", GST_VIDEO_INFO_FORMAT (in_info),
245       GST_VIDEO_INFO_FORMAT (out_info));
246
247   return TRUE;
248
249   /* ERRORS */
250 format_mismatch:
251   {
252     GST_ERROR_OBJECT (space, "input and output formats do not match");
253     return FALSE;
254   }
255 no_convert:
256   {
257     GST_ERROR_OBJECT (space, "could not create converter");
258     return FALSE;
259   }
260 }
261
262 static void
263 gst_video_convert_finalize (GObject * obj)
264 {
265   GstVideoConvert *space = GST_VIDEO_CONVERT (obj);
266
267   if (space->convert) {
268     videoconvert_convert_free (space->convert);
269   }
270
271   G_OBJECT_CLASS (parent_class)->finalize (obj);
272 }
273
274 static void
275 gst_video_convert_class_init (GstVideoConvertClass * klass)
276 {
277   GObjectClass *gobject_class = (GObjectClass *) klass;
278   GstElementClass *gstelement_class = (GstElementClass *) klass;
279   GstBaseTransformClass *gstbasetransform_class =
280       (GstBaseTransformClass *) klass;
281   GstVideoFilterClass *gstvideofilter_class = (GstVideoFilterClass *) klass;
282
283   gobject_class->set_property = gst_video_convert_set_property;
284   gobject_class->get_property = gst_video_convert_get_property;
285   gobject_class->finalize = gst_video_convert_finalize;
286
287   gst_element_class_add_pad_template (gstelement_class,
288       gst_static_pad_template_get (&gst_video_convert_src_template));
289   gst_element_class_add_pad_template (gstelement_class,
290       gst_static_pad_template_get (&gst_video_convert_sink_template));
291
292   gst_element_class_set_static_metadata (gstelement_class,
293       "Colorspace converter", "Filter/Converter/Video",
294       "Converts video from one colorspace to another",
295       "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
296
297   gstbasetransform_class->transform_caps =
298       GST_DEBUG_FUNCPTR (gst_video_convert_transform_caps);
299   gstbasetransform_class->fixate_caps =
300       GST_DEBUG_FUNCPTR (gst_video_convert_fixate_caps);
301   gstbasetransform_class->filter_meta =
302       GST_DEBUG_FUNCPTR (gst_video_convert_filter_meta);
303   gstbasetransform_class->transform_meta =
304       GST_DEBUG_FUNCPTR (gst_video_convert_transform_meta);
305
306   gstbasetransform_class->passthrough_on_same_caps = TRUE;
307
308   gstvideofilter_class->set_info =
309       GST_DEBUG_FUNCPTR (gst_video_convert_set_info);
310   gstvideofilter_class->transform_frame =
311       GST_DEBUG_FUNCPTR (gst_video_convert_transform_frame);
312
313   g_object_class_install_property (gobject_class, PROP_DITHER,
314       g_param_spec_enum ("dither", "Dither", "Apply dithering while converting",
315           dither_method_get_type (), DITHER_NONE,
316           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
317 }
318
319 static void
320 gst_video_convert_init (GstVideoConvert * space)
321 {
322 }
323
324 void
325 gst_video_convert_set_property (GObject * object, guint property_id,
326     const GValue * value, GParamSpec * pspec)
327 {
328   GstVideoConvert *csp;
329
330   csp = GST_VIDEO_CONVERT (object);
331
332   switch (property_id) {
333     case PROP_DITHER:
334       csp->dither = g_value_get_enum (value);
335       break;
336     default:
337       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
338       break;
339   }
340 }
341
342 void
343 gst_video_convert_get_property (GObject * object, guint property_id,
344     GValue * value, GParamSpec * pspec)
345 {
346   GstVideoConvert *csp;
347
348   csp = GST_VIDEO_CONVERT (object);
349
350   switch (property_id) {
351     case PROP_DITHER:
352       g_value_set_enum (value, csp->dither);
353       break;
354     default:
355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
356       break;
357   }
358 }
359
360 static GstFlowReturn
361 gst_video_convert_transform_frame (GstVideoFilter * filter,
362     GstVideoFrame * in_frame, GstVideoFrame * out_frame)
363 {
364   GstVideoConvert *space;
365
366   space = GST_VIDEO_CONVERT_CAST (filter);
367
368   GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, filter,
369       "doing colorspace conversion from %s -> to %s",
370       GST_VIDEO_INFO_NAME (&filter->in_info),
371       GST_VIDEO_INFO_NAME (&filter->out_info));
372
373   videoconvert_convert_set_dither (space->convert, space->dither);
374
375   videoconvert_convert_convert (space->convert, out_frame, in_frame);
376
377   return GST_FLOW_OK;
378 }
379
380 static gboolean
381 plugin_init (GstPlugin * plugin)
382 {
383   GST_DEBUG_CATEGORY_INIT (videoconvert_debug, "videoconvert", 0,
384       "Colorspace Converter");
385
386   _colorspace_quark = g_quark_from_static_string ("colorspace");
387
388   return gst_element_register (plugin, "videoconvert",
389       GST_RANK_NONE, GST_TYPE_VIDEO_CONVERT);
390 }
391
392 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
393     GST_VERSION_MINOR,
394     videoconvert, "Colorspace conversion", plugin_init, VERSION, GST_LICENSE,
395     GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)