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