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