2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
5 * Copyright (C) 2012, 2013 Samsung Electronics Co., Ltd.
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.
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.
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.
22 * * Modifications by Samsung Electronics Co., Ltd.
23 * 1. Support samsung extension format
27 * SECTION:element-ffmpegcolorspace
29 * Convert video frames between a great variety of colorspace formats.
32 * <title>Example launch line</title>
34 * gst-launch -v videotestsrc ! video/x-raw-yuv,format=\(fourcc\)YUY2 ! ffmpegcolorspace ! ximagesink
43 #include "gstffmpegcolorspace.h"
44 #include "gstffmpegcodecmap.h"
45 #include <gst/video/video.h>
47 GST_DEBUG_CATEGORY (ffmpegcolorspace_debug);
48 #define GST_CAT_DEFAULT ffmpegcolorspace_debug
49 GST_DEBUG_CATEGORY (ffmpegcolorspace_performance);
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")";"
75 static GstStaticPadTemplate gst_ffmpegcsp_src_template =
76 GST_STATIC_PAD_TEMPLATE ("src",
79 GST_STATIC_CAPS (FFMPEGCSP_VIDEO_CAPS)
82 static GstStaticPadTemplate gst_ffmpegcsp_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
86 GST_STATIC_CAPS (FFMPEGCSP_VIDEO_CAPS)
89 GType gst_ffmpegcsp_get_type (void);
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);
98 static GQuark _QRAWRGB; /* "video/x-raw-rgb" */
99 static GQuark _QRAWYUV; /* "video/x-raw-yuv" */
100 static GQuark _QALPHAMASK; /* "alpha_mask" */
102 /* copies the given caps */
104 gst_ffmpegcsp_caps_remove_format_info (GstCaps * caps)
106 GstStructure *yuvst, *rgbst, *grayst;
108 /* We know there's only one structure since we're given simple caps */
109 caps = gst_caps_copy (caps);
111 yuvst = gst_caps_get_structure (caps, 0);
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);
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);
122 grayst = gst_structure_copy (rgbst);
123 gst_structure_set_name (grayst, "video/x-raw-gray");
125 gst_caps_append_structure (caps, rgbst);
126 gst_caps_append_structure (caps, grayst);
133 gst_ffmpegcsp_structure_is_alpha (GstStructure * s)
137 name = gst_structure_get_name_id (s);
139 if (name == _QRAWRGB) {
140 return gst_structure_id_has_field (s, _QALPHAMASK);
141 } else if (name == _QRAWYUV) {
144 if (!gst_structure_get_fourcc (s, "format", &fourcc))
147 return (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
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. */
157 gst_ffmpegcsp_transform_caps (GstBaseTransform * btrans,
158 GstPadDirection direction, GstCaps * caps)
164 GstCaps *alpha, *non_alpha;
166 template = gst_static_pad_template_get_caps (&gst_ffmpegcsp_src_template);
167 result = gst_caps_copy (caps);
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);
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 ();
180 while ((s = gst_caps_steal_structure (tmp, 0))) {
181 if (gst_ffmpegcsp_structure_is_alpha (s))
182 gst_caps_append_structure (alpha, s);
184 gst_caps_append_structure (non_alpha, s);
187 s = gst_caps_get_structure (caps, 0);
188 gst_caps_unref (tmp);
190 if (gst_ffmpegcsp_structure_is_alpha (s)) {
191 gst_caps_append (alpha, non_alpha);
194 gst_caps_append (non_alpha, alpha);
198 gst_caps_append (result, tmp);
200 GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
201 GST_PTR_FORMAT, caps, result);
207 gst_ffmpegcsp_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
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;
221 space = GST_FFMPEGCSP (btrans);
223 /* parse in and output values */
224 structure = gst_caps_get_structure (incaps, 0);
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);
230 goto no_width_height;
233 in_framerate = gst_structure_get_value (structure, "framerate");
234 if (in_framerate == NULL || !GST_VALUE_HOLDS_FRACTION (in_framerate))
237 /* this is optional */
238 in_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
240 structure = gst_caps_get_structure (outcaps, 0);
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);
246 goto no_width_height;
249 out_framerate = gst_structure_get_value (structure, "framerate");
250 if (out_framerate == NULL || !GST_VALUE_HOLDS_FRACTION (out_framerate))
253 /* this is optional */
254 out_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
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;
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;
266 ctx = avcodec_alloc_context ();
268 space->width = ctx->width = in_width;
269 space->height = ctx->height = in_height;
271 space->interlaced = FALSE;
272 gst_structure_get_boolean (structure, "interlaced", &space->interlaced);
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;
281 /* palette, only for from data */
283 av_free (space->palette);
284 space->palette = ctx->palctrl;
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;
294 GST_DEBUG ("reconfigured %d %d", space->from_pixfmt, space->to_pixfmt);
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;
310 GST_DEBUG_OBJECT (space, "did not specify framerate");
311 space->from_pixfmt = PIX_FMT_NB;
312 space->to_pixfmt = PIX_FMT_NB;
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;
324 GST_DEBUG_OBJECT (space, "could not configure context for input format");
326 space->from_pixfmt = PIX_FMT_NB;
327 space->to_pixfmt = PIX_FMT_NB;
332 GST_DEBUG_OBJECT (space, "could not configure context for output format");
334 space->from_pixfmt = PIX_FMT_NB;
335 space->to_pixfmt = PIX_FMT_NB;
340 GST_BOILERPLATE (GstFFMpegCsp, gst_ffmpegcsp, GstVideoFilter,
341 GST_TYPE_VIDEO_FILTER);
344 gst_ffmpegcsp_base_init (gpointer klass)
346 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
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);
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>");
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");
364 gst_ffmpegcsp_finalize (GObject * obj)
366 GstFFMpegCsp *space = GST_FFMPEGCSP (obj);
369 av_free (space->palette);
371 G_OBJECT_CLASS (parent_class)->finalize (obj);
375 gst_ffmpegcsp_class_init (GstFFMpegCspClass * klass)
377 GObjectClass *gobject_class = (GObjectClass *) klass;
378 GstBaseTransformClass *gstbasetransform_class =
379 (GstBaseTransformClass *) klass;
381 gobject_class->finalize = gst_ffmpegcsp_finalize;
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);
391 gstbasetransform_class->passthrough_on_same_caps = TRUE;
395 gst_ffmpegcsp_init (GstFFMpegCsp * space, GstFFMpegCspClass * klass)
397 space->from_pixfmt = space->to_pixfmt = PIX_FMT_NB;
398 space->palette = NULL;
402 gst_ffmpegcsp_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
405 GstStructure *structure = NULL;
406 AVCodecContext *ctx = NULL;
412 structure = gst_caps_get_structure (caps, 0);
413 gst_structure_get_int (structure, "width", &width);
414 gst_structure_get_int (structure, "height", &height);
416 ctx = avcodec_alloc_context ();
418 g_assert (ctx != NULL);
420 ctx->pix_fmt = PIX_FMT_NB;
422 gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, caps, ctx);
424 if (G_UNLIKELY (ctx->pix_fmt == PIX_FMT_NB)) {
429 *size = avpicture_get_size (ctx->pix_fmt, width, height);
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 */
443 av_free (ctx->palctrl);
450 gst_ffmpegcsp_transform (GstBaseTransform * btrans, GstBuffer * inbuf,
456 space = GST_FFMPEGCSP (btrans);
458 GST_DEBUG ("from %d -> to %d", space->from_pixfmt, space->to_pixfmt);
460 if (G_UNLIKELY (space->from_pixfmt == PIX_FMT_NB ||
461 space->to_pixfmt == PIX_FMT_NB))
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,
469 /* fill optional palette */
471 space->from_frame.data[1] = (uint8_t *) space->palette->palette;
473 /* fill target frame */
474 gst_ffmpegcsp_avpicture_fill (&space->to_frame,
475 GST_BUFFER_DATA (outbuf), space->to_pixfmt, space->width, space->height,
479 result = img_convert (&space->to_frame, space->to_pixfmt,
480 &space->from_frame, space->from_pixfmt, space->width, space->height);
484 /* baseclass copies timestamps */
485 GST_DEBUG ("from %d -> to %d done", space->from_pixfmt, space->to_pixfmt);
492 GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
493 ("attempting to convert colorspaces between unknown formats"));
494 return GST_FLOW_NOT_NEGOTIATED;
498 GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
499 ("cannot convert between formats"));
500 return GST_FLOW_NOT_SUPPORTED;
505 plugin_init (GstPlugin * plugin)
507 GST_DEBUG_CATEGORY_INIT (ffmpegcolorspace_debug, "ffmpegcolorspace", 0,
508 "FFMPEG-based colorspace converter");
509 GST_DEBUG_CATEGORY_GET (ffmpegcolorspace_performance, "GST_PERFORMANCE");
513 return gst_element_register (plugin, "ffmpegcolorspace",
514 GST_RANK_NONE, GST_TYPE_FFMPEGCSP);
517 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
520 "colorspace conversion copied from FFMpeg " FFMPEG_VERSION,
521 plugin_init, VERSION, "LGPL", "FFMpeg", "http://ffmpeg.sourceforge.net/")