[MOVED FROM BAD 59/68] colorspace: fix a few formats
[platform/upstream/gstreamer.git] / gst / colorspace / gstcolorspace.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., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:element-colorspace
25  *
26  * Convert video frames between a great variety of colorspace formats.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v videotestsrc ! video/x-raw-yuv,format=\(fourcc\)YUY2 ! colorspace ! ximagesink
32  * ]|
33  * </refsect2>
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #  include "config.h"
38 #endif
39
40 #include "gstcolorspace.h"
41 #include <gst/video/video.h>
42
43 #include <string.h>
44
45 GST_DEBUG_CATEGORY (colorspace_debug);
46 #define GST_CAT_DEFAULT colorspace_debug
47 GST_DEBUG_CATEGORY (colorspace_performance);
48
49 #define CSP_VIDEO_CAPS                                          \
50   "video/x-raw-yuv, width = "GST_VIDEO_SIZE_RANGE" , "                  \
51   "height="GST_VIDEO_SIZE_RANGE",framerate="GST_VIDEO_FPS_RANGE","      \
52   "format= (fourcc) { I420 , NV12 , NV21 , YV12 , YUY2 , Y42B , Y444 , YUV9 , YVU9 , Y41B , Y800 , Y8 , GREY , Y16 , UYVY , YVYU , IYU1 , v308 , AYUV, v210, v216, A420, AY64 } ;" \
53   GST_VIDEO_CAPS_RGB";"                                                 \
54   GST_VIDEO_CAPS_BGR";"                                                 \
55   GST_VIDEO_CAPS_RGBx";"                                                \
56   GST_VIDEO_CAPS_xRGB";"                                                \
57   GST_VIDEO_CAPS_BGRx";"                                                \
58   GST_VIDEO_CAPS_xBGR";"                                                \
59   GST_VIDEO_CAPS_RGBA";"                                                \
60   GST_VIDEO_CAPS_ARGB";"                                                \
61   GST_VIDEO_CAPS_BGRA";"                                                \
62   GST_VIDEO_CAPS_ABGR";"                                                \
63   GST_VIDEO_CAPS_RGB_16";"                                              \
64   GST_VIDEO_CAPS_BGR_16";"                                              \
65   GST_VIDEO_CAPS_RGB_15";"                                              \
66   GST_VIDEO_CAPS_BGR_15";"                                              \
67   GST_VIDEO_CAPS_RGB8_PALETTED "; "                                     \
68   GST_VIDEO_CAPS_GRAY8";"                                               \
69   GST_VIDEO_CAPS_GRAY16("BIG_ENDIAN")";"                                \
70   GST_VIDEO_CAPS_GRAY16("LITTLE_ENDIAN")";"                             \
71   GST_VIDEO_CAPS_ARGB_64
72
73 static GstStaticPadTemplate gst_csp_src_template =
74 GST_STATIC_PAD_TEMPLATE ("src",
75     GST_PAD_SRC,
76     GST_PAD_ALWAYS,
77     GST_STATIC_CAPS (CSP_VIDEO_CAPS)
78     );
79
80 static GstStaticPadTemplate gst_csp_sink_template =
81 GST_STATIC_PAD_TEMPLATE ("sink",
82     GST_PAD_SINK,
83     GST_PAD_ALWAYS,
84     GST_STATIC_CAPS (CSP_VIDEO_CAPS)
85     );
86
87 GType gst_csp_get_type (void);
88
89 static gboolean gst_csp_set_caps (GstBaseTransform * btrans,
90     GstCaps * incaps, GstCaps * outcaps);
91 static gboolean gst_csp_get_unit_size (GstBaseTransform * btrans,
92     GstCaps * caps, guint * size);
93 static GstFlowReturn gst_csp_transform (GstBaseTransform * btrans,
94     GstBuffer * inbuf, GstBuffer * outbuf);
95
96 static GQuark _QRAWRGB;         /* "video/x-raw-rgb" */
97 static GQuark _QRAWYUV;         /* "video/x-raw-yuv" */
98 static GQuark _QALPHAMASK;      /* "alpha_mask" */
99
100 /* copies the given caps */
101 static GstCaps *
102 gst_csp_caps_remove_format_info (GstCaps * caps)
103 {
104   GstStructure *yuvst, *rgbst, *grayst;
105
106   /* We know there's only one structure since we're given simple caps */
107   caps = gst_caps_copy (caps);
108
109   yuvst = gst_caps_get_structure (caps, 0);
110
111   gst_structure_set_name (yuvst, "video/x-raw-yuv");
112   gst_structure_remove_fields (yuvst, "format", "endianness", "depth",
113       "bpp", "red_mask", "green_mask", "blue_mask", "alpha_mask",
114       "palette_data", "color-matrix", NULL);
115
116   rgbst = gst_structure_copy (yuvst);
117   gst_structure_set_name (rgbst, "video/x-raw-rgb");
118   gst_structure_remove_fields (rgbst, "color-matrix", "chroma-site", NULL);
119
120   grayst = gst_structure_copy (rgbst);
121   gst_structure_set_name (grayst, "video/x-raw-gray");
122
123   gst_caps_append_structure (caps, rgbst);
124   gst_caps_append_structure (caps, grayst);
125
126   return caps;
127 }
128
129
130 static gboolean
131 gst_csp_structure_is_alpha (GstStructure * s)
132 {
133   GQuark name;
134
135   name = gst_structure_get_name_id (s);
136
137   if (name == _QRAWRGB) {
138     return gst_structure_id_has_field (s, _QALPHAMASK);
139   } else if (name == _QRAWYUV) {
140     guint32 fourcc;
141
142     if (!gst_structure_get_fourcc (s, "format", &fourcc))
143       return FALSE;
144
145     return (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
146   }
147
148   return FALSE;
149 }
150
151 /* The caps can be transformed into any other caps with format info removed.
152  * However, we should prefer passthrough, so if passthrough is possible,
153  * put it first in the list. */
154 static GstCaps *
155 gst_csp_transform_caps (GstBaseTransform * btrans,
156     GstPadDirection direction, GstCaps * caps)
157 {
158   GstCaps *template;
159   GstCaps *tmp, *tmp2;
160   GstCaps *result;
161   GstStructure *s;
162   GstCaps *alpha, *non_alpha;
163
164   template = gst_static_pad_template_get_caps (&gst_csp_src_template);
165   result = gst_caps_copy (caps);
166
167   /* Get all possible caps that we can transform to */
168   tmp = gst_csp_caps_remove_format_info (caps);
169   tmp2 = gst_caps_intersect (tmp, template);
170   gst_caps_unref (tmp);
171   tmp = tmp2;
172
173   /* Now move alpha formats to the beginning if caps is an alpha format
174    * or at the end if caps is no alpha format */
175   alpha = gst_caps_new_empty ();
176   non_alpha = gst_caps_new_empty ();
177
178   while ((s = gst_caps_steal_structure (tmp, 0))) {
179     if (gst_csp_structure_is_alpha (s))
180       gst_caps_append_structure (alpha, s);
181     else
182       gst_caps_append_structure (non_alpha, s);
183   }
184
185   s = gst_caps_get_structure (caps, 0);
186   gst_caps_unref (tmp);
187
188   if (gst_csp_structure_is_alpha (s)) {
189     gst_caps_append (alpha, non_alpha);
190     tmp = alpha;
191   } else {
192     gst_caps_append (non_alpha, alpha);
193     tmp = non_alpha;
194   }
195
196   gst_caps_append (result, tmp);
197
198   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
199       GST_PTR_FORMAT, caps, result);
200
201   return result;
202 }
203
204 static gboolean
205 gst_csp_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
206     GstCaps * outcaps)
207 {
208   GstCsp *space;
209   GstVideoFormat in_format;
210   GstVideoFormat out_format;
211   gint in_height, in_width;
212   gint out_height, out_width;
213   gint in_fps_n, in_fps_d, in_par_n, in_par_d;
214   gint out_fps_n, out_fps_d, out_par_n, out_par_d;
215   gboolean have_in_par, have_out_par;
216   gboolean have_in_interlaced, have_out_interlaced;
217   gboolean in_interlaced, out_interlaced;
218   gboolean ret;
219   ColorSpaceColorSpec in_spec, out_spec;
220
221   space = GST_CSP (btrans);
222
223   if (space->convert) {
224     colorspace_convert_free (space->convert);
225   }
226
227   /* input caps */
228
229   ret = gst_video_format_parse_caps (incaps, &in_format, &in_width, &in_height);
230   if (!ret)
231     goto no_width_height;
232
233   ret = gst_video_parse_caps_framerate (incaps, &in_fps_n, &in_fps_d);
234   if (!ret)
235     goto no_framerate;
236
237   have_in_par = gst_video_parse_caps_pixel_aspect_ratio (incaps,
238       &in_par_n, &in_par_d);
239   have_in_interlaced = gst_video_format_parse_caps_interlaced (incaps,
240       &in_interlaced);
241
242   if (gst_video_format_is_rgb (in_format)) {
243     in_spec = COLOR_SPEC_RGB;
244   } else if (gst_video_format_is_yuv (in_format)) {
245     const gchar *matrix = gst_video_parse_caps_color_matrix (incaps);
246
247     if (matrix && g_str_equal (matrix, "hdtv"))
248       in_spec = COLOR_SPEC_YUV_BT709;
249     else
250       in_spec = COLOR_SPEC_YUV_BT470_6;
251   } else {
252     in_spec = COLOR_SPEC_GRAY;
253   }
254
255   /* output caps */
256
257   ret =
258       gst_video_format_parse_caps (outcaps, &out_format, &out_width,
259       &out_height);
260   if (!ret)
261     goto no_width_height;
262
263   ret = gst_video_parse_caps_framerate (outcaps, &out_fps_n, &out_fps_d);
264   if (!ret)
265     goto no_framerate;
266
267   have_out_par = gst_video_parse_caps_pixel_aspect_ratio (outcaps,
268       &out_par_n, &out_par_d);
269   have_out_interlaced = gst_video_format_parse_caps_interlaced (incaps,
270       &out_interlaced);
271
272   if (gst_video_format_is_rgb (out_format)) {
273     out_spec = COLOR_SPEC_RGB;
274   } else if (gst_video_format_is_yuv (out_format)) {
275     const gchar *matrix = gst_video_parse_caps_color_matrix (outcaps);
276
277     if (matrix && g_str_equal (matrix, "hdtv"))
278       out_spec = COLOR_SPEC_YUV_BT709;
279     else
280       out_spec = COLOR_SPEC_YUV_BT470_6;
281   } else {
282     out_spec = COLOR_SPEC_GRAY;
283   }
284
285   /* these must match */
286   if (in_width != out_width || in_height != out_height ||
287       in_fps_n != out_fps_n || in_fps_d != out_fps_d)
288     goto format_mismatch;
289
290   /* if present, these must match too */
291   if (have_in_par && have_out_par &&
292       (in_par_n != out_par_n || in_par_d != out_par_d))
293     goto format_mismatch;
294
295   /* if present, these must match too */
296   if (have_in_interlaced && have_out_interlaced &&
297       in_interlaced != out_interlaced)
298     goto format_mismatch;
299
300   space->from_format = in_format;
301   space->from_spec = in_spec;
302   space->to_format = out_format;
303   space->to_spec = out_spec;
304   space->width = in_width;
305   space->height = in_height;
306   space->interlaced = in_interlaced;
307
308   space->convert = colorspace_convert_new (out_format, out_spec, in_format,
309       in_spec, in_width, in_height);
310   if (space->convert) {
311     colorspace_convert_set_interlaced (space->convert, in_interlaced);
312   }
313   /* palette, only for from data */
314   if (space->from_format == GST_VIDEO_FORMAT_RGB8_PALETTED &&
315       space->to_format == GST_VIDEO_FORMAT_RGB8_PALETTED) {
316     goto format_mismatch;
317   } else if (space->from_format == GST_VIDEO_FORMAT_RGB8_PALETTED) {
318     GstBuffer *palette;
319
320     palette = gst_video_parse_caps_palette (incaps);
321
322     if (!palette || GST_BUFFER_SIZE (palette) < 256 * 4) {
323       if (palette)
324         gst_buffer_unref (palette);
325       goto invalid_palette;
326     }
327     colorspace_convert_set_palette (space->convert,
328         (const guint32 *) GST_BUFFER_DATA (palette));
329     gst_buffer_unref (palette);
330   } else if (space->to_format == GST_VIDEO_FORMAT_RGB8_PALETTED) {
331     const guint32 *palette;
332     GstBuffer *p_buf;
333
334     palette = colorspace_convert_get_palette (space->convert);
335     p_buf = gst_buffer_new_and_alloc (256 * 4);
336     memcpy (GST_BUFFER_DATA (p_buf), palette, 256 * 4);
337     gst_caps_set_simple (outcaps, "palette_data", GST_TYPE_BUFFER, p_buf, NULL);
338     gst_buffer_unref (p_buf);
339   }
340
341   GST_DEBUG ("reconfigured %d %d", space->from_format, space->to_format);
342
343   return TRUE;
344
345   /* ERRORS */
346 no_width_height:
347   {
348     GST_ERROR_OBJECT (space, "did not specify width or height");
349     space->from_format = GST_VIDEO_FORMAT_UNKNOWN;
350     space->to_format = GST_VIDEO_FORMAT_UNKNOWN;
351     return FALSE;
352   }
353 no_framerate:
354   {
355     GST_ERROR_OBJECT (space, "did not specify framerate");
356     space->from_format = GST_VIDEO_FORMAT_UNKNOWN;
357     space->to_format = GST_VIDEO_FORMAT_UNKNOWN;
358     return FALSE;
359   }
360 format_mismatch:
361   {
362     GST_ERROR_OBJECT (space, "input and output formats do not match");
363     space->from_format = GST_VIDEO_FORMAT_UNKNOWN;
364     space->to_format = GST_VIDEO_FORMAT_UNKNOWN;
365     return FALSE;
366   }
367 invalid_palette:
368   {
369     GST_ERROR_OBJECT (space, "invalid palette");
370     space->from_format = GST_VIDEO_FORMAT_UNKNOWN;
371     space->to_format = GST_VIDEO_FORMAT_UNKNOWN;
372     return FALSE;
373   }
374 }
375
376 GST_BOILERPLATE (GstCsp, gst_csp, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
377
378 static void
379 gst_csp_base_init (gpointer klass)
380 {
381   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
382
383   gst_element_class_add_pad_template (element_class,
384       gst_static_pad_template_get (&gst_csp_src_template));
385   gst_element_class_add_pad_template (element_class,
386       gst_static_pad_template_get (&gst_csp_sink_template));
387
388   gst_element_class_set_details_simple (element_class,
389       " Colorspace converter", "Filter/Converter/Video",
390       "Converts video from one colorspace to another",
391       "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
392
393   _QRAWRGB = g_quark_from_string ("video/x-raw-rgb");
394   _QRAWYUV = g_quark_from_string ("video/x-raw-yuv");
395   _QALPHAMASK = g_quark_from_string ("alpha_mask");
396 }
397
398 static void
399 gst_csp_finalize (GObject * obj)
400 {
401   GstCsp *space = GST_CSP (obj);
402
403   if (space->convert) {
404     colorspace_convert_free (space->convert);
405   }
406
407   G_OBJECT_CLASS (parent_class)->finalize (obj);
408
409 }
410
411 static void
412 gst_csp_class_init (GstCspClass * klass)
413 {
414   GObjectClass *gobject_class = (GObjectClass *) klass;
415   GstBaseTransformClass *gstbasetransform_class =
416       (GstBaseTransformClass *) klass;
417
418   gobject_class->finalize = gst_csp_finalize;
419
420   gstbasetransform_class->transform_caps =
421       GST_DEBUG_FUNCPTR (gst_csp_transform_caps);
422   gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_csp_set_caps);
423   gstbasetransform_class->get_unit_size =
424       GST_DEBUG_FUNCPTR (gst_csp_get_unit_size);
425   gstbasetransform_class->transform = GST_DEBUG_FUNCPTR (gst_csp_transform);
426
427   gstbasetransform_class->passthrough_on_same_caps = TRUE;
428 }
429
430 static void
431 gst_csp_init (GstCsp * space, GstCspClass * klass)
432 {
433   space->from_format = GST_VIDEO_FORMAT_UNKNOWN;
434   space->to_format = GST_VIDEO_FORMAT_UNKNOWN;
435 }
436
437 static gboolean
438 gst_csp_get_unit_size (GstBaseTransform * btrans, GstCaps * caps, guint * size)
439 {
440   gboolean ret = TRUE;
441   GstVideoFormat format;
442   gint width, height;
443
444   g_assert (size);
445
446   ret = gst_video_format_parse_caps (caps, &format, &width, &height);
447   if (ret) {
448     *size = gst_video_format_get_size (format, width, height);
449   }
450
451   return ret;
452 }
453
454 static GstFlowReturn
455 gst_csp_transform (GstBaseTransform * btrans, GstBuffer * inbuf,
456     GstBuffer * outbuf)
457 {
458   GstCsp *space;
459
460   space = GST_CSP (btrans);
461
462   GST_DEBUG ("from %d -> to %d", space->from_format, space->to_format);
463
464   if (G_UNLIKELY (space->from_format == GST_VIDEO_FORMAT_UNKNOWN ||
465           space->to_format == GST_VIDEO_FORMAT_UNKNOWN))
466     goto unknown_format;
467
468   colorspace_convert_convert (space->convert, GST_BUFFER_DATA (outbuf),
469       GST_BUFFER_DATA (inbuf));
470
471   /* baseclass copies timestamps */
472   GST_DEBUG ("from %d -> to %d done", space->from_format, space->to_format);
473
474   return GST_FLOW_OK;
475
476   /* ERRORS */
477 unknown_format:
478   {
479     GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
480         ("attempting to convert colorspaces between unknown formats"));
481     return GST_FLOW_NOT_NEGOTIATED;
482   }
483 #if 0
484 not_supported:
485   {
486     GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
487         ("cannot convert between formats"));
488     return GST_FLOW_NOT_SUPPORTED;
489   }
490 #endif
491 }
492
493 static gboolean
494 plugin_init (GstPlugin * plugin)
495 {
496   GST_DEBUG_CATEGORY_INIT (colorspace_debug, "colorspace", 0,
497       "Colorspace Converter");
498   GST_DEBUG_CATEGORY_GET (colorspace_performance, "GST_PERFORMANCE");
499
500   return gst_element_register (plugin, "colorspace",
501       GST_RANK_NONE, GST_TYPE_CSP);
502 }
503
504 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
505     GST_VERSION_MINOR,
506     "colorspace", "Colorspace conversion", plugin_init, VERSION, "LGPL", "", "")