tizen 2.3 release
[framework/multimedia/gst-plugins-base0.10.git] / gst / ffmpegcolorspace / gstffmpegcolorspace.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) 2012, 2013 Samsung Electronics Co., Ltd.
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  * * Modifications by Samsung Electronics Co., Ltd.
23  * 1. Support samsung extension format
24  */
25
26 /**
27  * SECTION:element-ffmpegcolorspace
28  *
29  * Convert video frames between a great variety of colorspace formats.
30  *
31  * <refsect2>
32  * <title>Example launch line</title>
33  * |[
34  * gst-launch -v videotestsrc ! video/x-raw-yuv,format=\(fourcc\)YUY2 ! ffmpegcolorspace ! ximagesink
35  * ]|
36  * </refsect2>
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42
43 #include "gstffmpegcolorspace.h"
44 #include "gstffmpegcodecmap.h"
45 #include <gst/video/video.h>
46
47 GST_DEBUG_CATEGORY (ffmpegcolorspace_debug);
48 #define GST_CAT_DEFAULT ffmpegcolorspace_debug
49 GST_DEBUG_CATEGORY (ffmpegcolorspace_performance);
50
51 #define FFMPEGCSP_VIDEO_CAPS                                            \
52   "video/x-raw-yuv, width = "GST_VIDEO_SIZE_RANGE" , "                  \
53   "height="GST_VIDEO_SIZE_RANGE",framerate="GST_VIDEO_FPS_RANGE","      \
54   "format= (fourcc) { I420 , NV12 , NV21 , YV12 , YUY2 , Y42B , Y444 , YUV9 , YVU9 , Y41B , Y800 , Y8 , GREY , Y16 , UYVY , YVYU , IYU1 , v308 , AYUV , A420 , SUYV , SYVY , S420 , ITLV } ;" \
55   GST_VIDEO_CAPS_RGB";"                                                 \
56   GST_VIDEO_CAPS_BGR";"                                                 \
57   GST_VIDEO_CAPS_RGBx";"                                                \
58   GST_VIDEO_CAPS_xRGB";"                                                \
59   GST_VIDEO_CAPS_BGRx";"                                                \
60   GST_VIDEO_CAPS_xBGR";"                                                \
61   GST_VIDEO_CAPS_RGBA";"                                                \
62   GST_VIDEO_CAPS_ARGB";"                                                \
63   GST_VIDEO_CAPS_BGRA";"                                                \
64   GST_VIDEO_CAPS_ABGR";"                                                \
65   GST_VIDEO_CAPS_RGB_16";"                                              \
66   GST_VIDEO_CAPS_RGB_15";"                                              \
67   "video/x-raw-rgb, bpp = (int)8, depth = (int)8, "                     \
68       "width = "GST_VIDEO_SIZE_RANGE" , "                               \
69       "height = " GST_VIDEO_SIZE_RANGE ", "                             \
70       "framerate = "GST_VIDEO_FPS_RANGE ";"                             \
71   GST_VIDEO_CAPS_GRAY8";"                                               \
72   GST_VIDEO_CAPS_GRAY16("BIG_ENDIAN")";"                                \
73   GST_VIDEO_CAPS_GRAY16("LITTLE_ENDIAN")";"
74
75 static GstStaticPadTemplate gst_ffmpegcsp_src_template =
76 GST_STATIC_PAD_TEMPLATE ("src",
77     GST_PAD_SRC,
78     GST_PAD_ALWAYS,
79     GST_STATIC_CAPS (FFMPEGCSP_VIDEO_CAPS)
80     );
81
82 static GstStaticPadTemplate gst_ffmpegcsp_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS (FFMPEGCSP_VIDEO_CAPS)
87     );
88
89 GType gst_ffmpegcsp_get_type (void);
90
91 static gboolean gst_ffmpegcsp_set_caps (GstBaseTransform * btrans,
92     GstCaps * incaps, GstCaps * outcaps);
93 static gboolean gst_ffmpegcsp_get_unit_size (GstBaseTransform * btrans,
94     GstCaps * caps, guint * size);
95 static GstFlowReturn gst_ffmpegcsp_transform (GstBaseTransform * btrans,
96     GstBuffer * inbuf, GstBuffer * outbuf);
97
98 static GQuark _QRAWRGB;         /* "video/x-raw-rgb" */
99 static GQuark _QRAWYUV;         /* "video/x-raw-yuv" */
100 static GQuark _QALPHAMASK;      /* "alpha_mask" */
101
102 /* copies the given caps */
103 static GstCaps *
104 gst_ffmpegcsp_caps_remove_format_info (GstCaps * caps)
105 {
106   GstStructure *yuvst, *rgbst, *grayst;
107
108   /* We know there's only one structure since we're given simple caps */
109   caps = gst_caps_copy (caps);
110
111   yuvst = gst_caps_get_structure (caps, 0);
112
113   gst_structure_set_name (yuvst, "video/x-raw-yuv");
114   gst_structure_remove_fields (yuvst, "format", "endianness", "depth",
115       "bpp", "red_mask", "green_mask", "blue_mask", "alpha_mask",
116       "palette_data", NULL);
117
118   rgbst = gst_structure_copy (yuvst);
119   gst_structure_set_name (rgbst, "video/x-raw-rgb");
120   gst_structure_remove_fields (rgbst, "color-matrix", "chroma-site", NULL);
121
122   grayst = gst_structure_copy (rgbst);
123   gst_structure_set_name (grayst, "video/x-raw-gray");
124
125   gst_caps_append_structure (caps, rgbst);
126   gst_caps_append_structure (caps, grayst);
127
128   return caps;
129 }
130
131
132 static gboolean
133 gst_ffmpegcsp_structure_is_alpha (GstStructure * s)
134 {
135   GQuark name;
136
137   name = gst_structure_get_name_id (s);
138
139   if (name == _QRAWRGB) {
140     return gst_structure_id_has_field (s, _QALPHAMASK);
141   } else if (name == _QRAWYUV) {
142     guint32 fourcc;
143
144     if (!gst_structure_get_fourcc (s, "format", &fourcc))
145       return FALSE;
146
147     return (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
148   }
149
150   return FALSE;
151 }
152
153 /* The caps can be transformed into any other caps with format info removed.
154  * However, we should prefer passthrough, so if passthrough is possible,
155  * put it first in the list. */
156 static GstCaps *
157 gst_ffmpegcsp_transform_caps (GstBaseTransform * btrans,
158     GstPadDirection direction, GstCaps * caps)
159 {
160   GstCaps *template;
161   GstCaps *tmp, *tmp2;
162   GstCaps *result;
163   GstStructure *s;
164   GstCaps *alpha, *non_alpha;
165
166   template = gst_static_pad_template_get_caps (&gst_ffmpegcsp_src_template);
167   result = gst_caps_copy (caps);
168
169   /* Get all possible caps that we can transform to */
170   tmp = gst_ffmpegcsp_caps_remove_format_info (caps);
171   tmp2 = gst_caps_intersect (tmp, template);
172   gst_caps_unref (tmp);
173   tmp = tmp2;
174
175   /* Now move alpha formats to the beginning if caps is an alpha format
176    * or at the end if caps is no alpha format */
177   alpha = gst_caps_new_empty ();
178   non_alpha = gst_caps_new_empty ();
179
180   while ((s = gst_caps_steal_structure (tmp, 0))) {
181     if (gst_ffmpegcsp_structure_is_alpha (s))
182       gst_caps_append_structure (alpha, s);
183     else
184       gst_caps_append_structure (non_alpha, s);
185   }
186
187   s = gst_caps_get_structure (caps, 0);
188   gst_caps_unref (tmp);
189
190   if (gst_ffmpegcsp_structure_is_alpha (s)) {
191     gst_caps_append (alpha, non_alpha);
192     tmp = alpha;
193   } else {
194     gst_caps_append (non_alpha, alpha);
195     tmp = non_alpha;
196   }
197
198   gst_caps_append (result, tmp);
199
200   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
201       GST_PTR_FORMAT, caps, result);
202
203   return result;
204 }
205
206 static gboolean
207 gst_ffmpegcsp_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
208     GstCaps * outcaps)
209 {
210   GstFFMpegCsp *space;
211   GstStructure *structure;
212   gint in_height, in_width;
213   gint out_height, out_width;
214   const GValue *in_framerate = NULL;
215   const GValue *out_framerate = NULL;
216   const GValue *in_par = NULL;
217   const GValue *out_par = NULL;
218   AVCodecContext *ctx;
219   gboolean res;
220
221   space = GST_FFMPEGCSP (btrans);
222
223   /* parse in and output values */
224   structure = gst_caps_get_structure (incaps, 0);
225
226   /* we have to have width and height */
227   res = gst_structure_get_int (structure, "width", &in_width);
228   res &= gst_structure_get_int (structure, "height", &in_height);
229   if (!res)
230     goto no_width_height;
231
232   /* and framerate */
233   in_framerate = gst_structure_get_value (structure, "framerate");
234   if (in_framerate == NULL || !GST_VALUE_HOLDS_FRACTION (in_framerate))
235     goto no_framerate;
236
237   /* this is optional */
238   in_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
239
240   structure = gst_caps_get_structure (outcaps, 0);
241
242   /* we have to have width and height */
243   res = gst_structure_get_int (structure, "width", &out_width);
244   res &= gst_structure_get_int (structure, "height", &out_height);
245   if (!res)
246     goto no_width_height;
247
248   /* and framerate */
249   out_framerate = gst_structure_get_value (structure, "framerate");
250   if (out_framerate == NULL || !GST_VALUE_HOLDS_FRACTION (out_framerate))
251     goto no_framerate;
252
253   /* this is optional */
254   out_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
255
256   /* these must match */
257   if (in_width != out_width || in_height != out_height ||
258       gst_value_compare (in_framerate, out_framerate) != GST_VALUE_EQUAL)
259     goto format_mismatch;
260
261   /* if present, these must match too */
262   if (in_par && out_par
263       && gst_value_compare (in_par, out_par) != GST_VALUE_EQUAL)
264     goto format_mismatch;
265
266   ctx = avcodec_alloc_context ();
267
268   space->width = ctx->width = in_width;
269   space->height = ctx->height = in_height;
270
271   space->interlaced = FALSE;
272   gst_structure_get_boolean (structure, "interlaced", &space->interlaced);
273
274   /* get from format */
275   ctx->pix_fmt = PIX_FMT_NB;
276   gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, incaps, ctx);
277   if (ctx->pix_fmt == PIX_FMT_NB)
278     goto invalid_in_caps;
279   space->from_pixfmt = ctx->pix_fmt;
280
281   /* palette, only for from data */
282   if (space->palette)
283     av_free (space->palette);
284   space->palette = ctx->palctrl;
285   ctx->palctrl = NULL;
286
287   /* get to format */
288   ctx->pix_fmt = PIX_FMT_NB;
289   gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, outcaps, ctx);
290   if (ctx->pix_fmt == PIX_FMT_NB)
291     goto invalid_out_caps;
292   space->to_pixfmt = ctx->pix_fmt;
293
294   GST_DEBUG ("reconfigured %d %d", space->from_pixfmt, space->to_pixfmt);
295
296   av_free (ctx);
297
298   return TRUE;
299
300   /* ERRORS */
301 no_width_height:
302   {
303     GST_DEBUG_OBJECT (space, "did not specify width or height");
304     space->from_pixfmt = PIX_FMT_NB;
305     space->to_pixfmt = PIX_FMT_NB;
306     return FALSE;
307   }
308 no_framerate:
309   {
310     GST_DEBUG_OBJECT (space, "did not specify framerate");
311     space->from_pixfmt = PIX_FMT_NB;
312     space->to_pixfmt = PIX_FMT_NB;
313     return FALSE;
314   }
315 format_mismatch:
316   {
317     GST_DEBUG_OBJECT (space, "input and output formats do not match");
318     space->from_pixfmt = PIX_FMT_NB;
319     space->to_pixfmt = PIX_FMT_NB;
320     return FALSE;
321   }
322 invalid_in_caps:
323   {
324     GST_DEBUG_OBJECT (space, "could not configure context for input format");
325     av_free (ctx);
326     space->from_pixfmt = PIX_FMT_NB;
327     space->to_pixfmt = PIX_FMT_NB;
328     return FALSE;
329   }
330 invalid_out_caps:
331   {
332     GST_DEBUG_OBJECT (space, "could not configure context for output format");
333     av_free (ctx);
334     space->from_pixfmt = PIX_FMT_NB;
335     space->to_pixfmt = PIX_FMT_NB;
336     return FALSE;
337   }
338 }
339
340 GST_BOILERPLATE (GstFFMpegCsp, gst_ffmpegcsp, GstVideoFilter,
341     GST_TYPE_VIDEO_FILTER);
342
343 static void
344 gst_ffmpegcsp_base_init (gpointer klass)
345 {
346   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
347
348   gst_element_class_add_static_pad_template (element_class,
349       &gst_ffmpegcsp_src_template);
350   gst_element_class_add_static_pad_template (element_class,
351       &gst_ffmpegcsp_sink_template);
352
353   gst_element_class_set_details_simple (element_class,
354       "FFMPEG Colorspace converter", "Filter/Converter/Video",
355       "Converts video from one colorspace to another",
356       "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
357
358   _QRAWRGB = g_quark_from_string ("video/x-raw-rgb");
359   _QRAWYUV = g_quark_from_string ("video/x-raw-yuv");
360   _QALPHAMASK = g_quark_from_string ("alpha_mask");
361 }
362
363 static void
364 gst_ffmpegcsp_finalize (GObject * obj)
365 {
366   GstFFMpegCsp *space = GST_FFMPEGCSP (obj);
367
368   if (space->palette)
369     av_free (space->palette);
370
371   G_OBJECT_CLASS (parent_class)->finalize (obj);
372 }
373
374 static void
375 gst_ffmpegcsp_class_init (GstFFMpegCspClass * klass)
376 {
377   GObjectClass *gobject_class = (GObjectClass *) klass;
378   GstBaseTransformClass *gstbasetransform_class =
379       (GstBaseTransformClass *) klass;
380
381   gobject_class->finalize = gst_ffmpegcsp_finalize;
382
383   gstbasetransform_class->transform_caps =
384       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_transform_caps);
385   gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_ffmpegcsp_set_caps);
386   gstbasetransform_class->get_unit_size =
387       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_get_unit_size);
388   gstbasetransform_class->transform =
389       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_transform);
390
391   gstbasetransform_class->passthrough_on_same_caps = TRUE;
392 }
393
394 static void
395 gst_ffmpegcsp_init (GstFFMpegCsp * space, GstFFMpegCspClass * klass)
396 {
397   space->from_pixfmt = space->to_pixfmt = PIX_FMT_NB;
398   space->palette = NULL;
399 }
400
401 static gboolean
402 gst_ffmpegcsp_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
403     guint * size)
404 {
405   GstStructure *structure = NULL;
406   AVCodecContext *ctx = NULL;
407   gboolean ret = TRUE;
408   gint width, height;
409
410   g_assert (size);
411
412   structure = gst_caps_get_structure (caps, 0);
413   gst_structure_get_int (structure, "width", &width);
414   gst_structure_get_int (structure, "height", &height);
415
416   ctx = avcodec_alloc_context ();
417
418   g_assert (ctx != NULL);
419
420   ctx->pix_fmt = PIX_FMT_NB;
421
422   gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, caps, ctx);
423
424   if (G_UNLIKELY (ctx->pix_fmt == PIX_FMT_NB)) {
425     ret = FALSE;
426     goto beach;
427   }
428
429   *size = avpicture_get_size (ctx->pix_fmt, width, height);
430
431   /* ffmpeg frames have the palette after the frame data, whereas
432    * GStreamer currently puts it into the caps as 'palette_data' field,
433    * so for paletted data the frame size avpicture_get_size() returns is
434    * 1024 bytes larger than what GStreamer expects. */
435   if (gst_structure_has_field (structure, "palette_data") &&
436       ctx->pix_fmt == PIX_FMT_PAL8) {
437     *size -= 4 * 256;           /* = AVPALETTE_SIZE */
438   }
439
440 beach:
441
442   if (ctx->palctrl)
443     av_free (ctx->palctrl);
444   av_free (ctx);
445
446   return ret;
447 }
448
449 static GstFlowReturn
450 gst_ffmpegcsp_transform (GstBaseTransform * btrans, GstBuffer * inbuf,
451     GstBuffer * outbuf)
452 {
453   GstFFMpegCsp *space;
454   gint result;
455
456   space = GST_FFMPEGCSP (btrans);
457
458   GST_DEBUG ("from %d -> to %d", space->from_pixfmt, space->to_pixfmt);
459
460   if (G_UNLIKELY (space->from_pixfmt == PIX_FMT_NB ||
461           space->to_pixfmt == PIX_FMT_NB))
462     goto unknown_format;
463
464   /* fill from with source data */
465   gst_ffmpegcsp_avpicture_fill (&space->from_frame,
466       GST_BUFFER_DATA (inbuf), space->from_pixfmt, space->width, space->height,
467       space->interlaced);
468
469   /* fill optional palette */
470   if (space->palette)
471     space->from_frame.data[1] = (uint8_t *) space->palette->palette;
472
473   /* fill target frame */
474   gst_ffmpegcsp_avpicture_fill (&space->to_frame,
475       GST_BUFFER_DATA (outbuf), space->to_pixfmt, space->width, space->height,
476       space->interlaced);
477
478   /* and convert */
479   result = img_convert (&space->to_frame, space->to_pixfmt,
480       &space->from_frame, space->from_pixfmt, space->width, space->height);
481   if (result == -1)
482     goto not_supported;
483
484   /* baseclass copies timestamps */
485   GST_DEBUG ("from %d -> to %d done", space->from_pixfmt, space->to_pixfmt);
486
487   return GST_FLOW_OK;
488
489   /* ERRORS */
490 unknown_format:
491   {
492     GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
493         ("attempting to convert colorspaces between unknown formats"));
494     return GST_FLOW_NOT_NEGOTIATED;
495   }
496 not_supported:
497   {
498     GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
499         ("cannot convert between formats"));
500     return GST_FLOW_NOT_SUPPORTED;
501   }
502 }
503
504 static gboolean
505 plugin_init (GstPlugin * plugin)
506 {
507   GST_DEBUG_CATEGORY_INIT (ffmpegcolorspace_debug, "ffmpegcolorspace", 0,
508       "FFMPEG-based colorspace converter");
509   GST_DEBUG_CATEGORY_GET (ffmpegcolorspace_performance, "GST_PERFORMANCE");
510
511   avcodec_init ();
512
513   return gst_element_register (plugin, "ffmpegcolorspace",
514       GST_RANK_NONE, GST_TYPE_FFMPEGCSP);
515 }
516
517 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
518     GST_VERSION_MINOR,
519     "ffmpegcolorspace",
520     "colorspace conversion copied from FFMpeg " FFMPEG_VERSION,
521     plugin_init, VERSION, "LGPL", "FFMpeg", "http://ffmpeg.sourceforge.net/")