gio: Remove unused function
[platform/upstream/gstreamer.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  *
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
42 GST_DEBUG_CATEGORY (ffmpegcolorspace_debug);
43 #define GST_CAT_DEFAULT ffmpegcolorspace_debug
44 GST_DEBUG_CATEGORY (ffmpegcolorspace_performance);
45
46 /* elementfactory information */
47 static const GstElementDetails ffmpegcsp_details =
48 GST_ELEMENT_DETAILS ("FFMPEG Colorspace converter",
49     "Filter/Converter/Video",
50     "Converts video from one colorspace to another",
51     "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
52
53
54 /* Stereo signals and args */
55 enum
56 {
57   /* FILL ME */
58   LAST_SIGNAL
59 };
60
61 enum
62 {
63   ARG_0,
64 };
65
66 static GType gst_ffmpegcsp_get_type (void);
67
68 static void gst_ffmpegcsp_base_init (GstFFMpegCspClass * klass);
69 static void gst_ffmpegcsp_class_init (GstFFMpegCspClass * klass);
70 static void gst_ffmpegcsp_init (GstFFMpegCsp * space);
71
72 static gboolean gst_ffmpegcsp_set_caps (GstBaseTransform * btrans,
73     GstCaps * incaps, GstCaps * outcaps);
74 static gboolean gst_ffmpegcsp_get_unit_size (GstBaseTransform * btrans,
75     GstCaps * caps, guint * size);
76 static GstFlowReturn gst_ffmpegcsp_transform (GstBaseTransform * btrans,
77     GstBuffer * inbuf, GstBuffer * outbuf);
78 #if 0
79 static GstFlowReturn gst_ffmpegcsp_transform_ip (GstBaseTransform * btrans,
80     GstBuffer * inbuf);
81 #endif
82
83 static GstPadTemplate *sinktempl, *srctempl;
84 static GstElementClass *parent_class = NULL;
85
86 /*static guint gst_ffmpegcsp_signals[LAST_SIGNAL] = { 0 }; */
87
88 /* copies the given caps */
89 static GstCaps *
90 gst_ffmpegcsp_caps_remove_format_info (GstCaps * caps)
91 {
92   int i;
93   GstStructure *structure;
94   GstCaps *rgbcaps;
95   GstCaps *graycaps;
96
97   caps = gst_caps_copy (caps);
98
99   for (i = 0; i < gst_caps_get_size (caps); i++) {
100     structure = gst_caps_get_structure (caps, i);
101
102     gst_structure_set_name (structure, "video/x-raw-yuv");
103     gst_structure_remove_field (structure, "format");
104     gst_structure_remove_field (structure, "endianness");
105     gst_structure_remove_field (structure, "depth");
106     gst_structure_remove_field (structure, "bpp");
107     gst_structure_remove_field (structure, "red_mask");
108     gst_structure_remove_field (structure, "green_mask");
109     gst_structure_remove_field (structure, "blue_mask");
110     gst_structure_remove_field (structure, "alpha_mask");
111     gst_structure_remove_field (structure, "palette_data");
112   }
113
114   gst_caps_do_simplify (caps);
115   rgbcaps = gst_caps_copy (caps);
116
117   for (i = 0; i < gst_caps_get_size (rgbcaps); i++) {
118     structure = gst_caps_get_structure (rgbcaps, i);
119
120     gst_structure_set_name (structure, "video/x-raw-rgb");
121   }
122   graycaps = gst_caps_copy (caps);
123
124   for (i = 0; i < gst_caps_get_size (graycaps); i++) {
125     structure = gst_caps_get_structure (graycaps, i);
126
127     gst_structure_set_name (structure, "video/x-raw-gray");
128   }
129
130   gst_caps_append (caps, graycaps);
131   gst_caps_append (caps, rgbcaps);
132
133   return caps;
134 }
135
136
137 static gboolean
138 gst_ffmpegcsp_structure_is_alpha (GstStructure * s)
139 {
140   const gchar *name;
141
142   name = gst_structure_get_name (s);
143
144   if (g_str_equal (name, "video/x-raw-rgb")) {
145     return gst_structure_has_field (s, "alpha_mask");
146   } else if (g_str_equal (name, "video/x-raw-yuv")) {
147     guint32 fourcc;
148
149     if (!gst_structure_get_fourcc (s, "format", &fourcc))
150       return FALSE;
151
152     return (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
153   }
154
155   return FALSE;
156 }
157
158 /* The caps can be transformed into any other caps with format info removed.
159  * However, we should prefer passthrough, so if passthrough is possible,
160  * put it first in the list. */
161 static GstCaps *
162 gst_ffmpegcsp_transform_caps (GstBaseTransform * btrans,
163     GstPadDirection direction, GstCaps * caps)
164 {
165   GstCaps *template;
166   GstCaps *tmp, *tmp2;
167   GstCaps *result;
168   guint i, n;
169   gboolean is_alpha;
170   GstStructure *s;
171   GstCaps *alpha, *non_alpha;
172
173   s = gst_caps_get_structure (caps, 0);
174   is_alpha = gst_ffmpegcsp_structure_is_alpha (s);
175
176   template = gst_ffmpegcsp_codectype_to_caps (CODEC_TYPE_VIDEO, NULL);
177   result = gst_caps_intersect (caps, template);
178
179   /* Get all possible caps that we can transform to */
180   tmp = gst_ffmpegcsp_caps_remove_format_info (caps);
181   tmp2 = gst_caps_intersect (tmp, template);
182   gst_caps_unref (template);
183   gst_caps_unref (tmp);
184   tmp = tmp2;
185
186   /* Now move alpha formats to the beginning if caps is an alpha format
187    * or at the end if caps is no alpha format */
188   n = gst_caps_get_size (tmp);
189
190   alpha = gst_caps_new_empty ();
191   non_alpha = gst_caps_new_empty ();
192
193   for (i = 0; i < n; i++) {
194     s = gst_caps_get_structure (tmp, i);
195
196     if (gst_ffmpegcsp_structure_is_alpha (s))
197       gst_caps_append_structure (alpha, gst_structure_copy (s));
198     else
199       gst_caps_append_structure (non_alpha, gst_structure_copy (s));
200   }
201
202   s = gst_caps_get_structure (caps, 0);
203   gst_caps_unref (tmp);
204
205   if (gst_ffmpegcsp_structure_is_alpha (s)) {
206     gst_caps_append (alpha, non_alpha);
207     tmp = alpha;
208   } else {
209     gst_caps_append (non_alpha, alpha);
210     tmp = non_alpha;
211   }
212
213   gst_caps_append (result, tmp);
214
215   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
216       GST_PTR_FORMAT, caps, result);
217
218   return result;
219 }
220
221 static gboolean
222 gst_ffmpegcsp_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
223     GstCaps * outcaps)
224 {
225   GstFFMpegCsp *space;
226   GstStructure *structure;
227   gint in_height, in_width;
228   gint out_height, out_width;
229   const GValue *in_framerate = NULL;
230   const GValue *out_framerate = NULL;
231   const GValue *in_par = NULL;
232   const GValue *out_par = NULL;
233   AVCodecContext *ctx;
234   gboolean res;
235
236   space = GST_FFMPEGCSP (btrans);
237
238   /* parse in and output values */
239   structure = gst_caps_get_structure (incaps, 0);
240
241   /* we have to have width and height */
242   res = gst_structure_get_int (structure, "width", &in_width);
243   res &= gst_structure_get_int (structure, "height", &in_height);
244   if (!res)
245     goto no_width_height;
246
247   /* and framerate */
248   in_framerate = gst_structure_get_value (structure, "framerate");
249   if (in_framerate == NULL || !GST_VALUE_HOLDS_FRACTION (in_framerate))
250     goto no_framerate;
251
252   /* this is optional */
253   in_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
254
255   structure = gst_caps_get_structure (outcaps, 0);
256
257   /* we have to have width and height */
258   res = gst_structure_get_int (structure, "width", &out_width);
259   res &= gst_structure_get_int (structure, "height", &out_height);
260   if (!res)
261     goto no_width_height;
262
263   /* and framerate */
264   out_framerate = gst_structure_get_value (structure, "framerate");
265   if (out_framerate == NULL || !GST_VALUE_HOLDS_FRACTION (out_framerate))
266     goto no_framerate;
267
268   /* this is optional */
269   out_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
270
271   /* these must match */
272   if (in_width != out_width || in_height != out_height ||
273       gst_value_compare (in_framerate, out_framerate) != GST_VALUE_EQUAL)
274     goto format_mismatch;
275
276   /* if present, these must match too */
277   if (in_par && out_par
278       && gst_value_compare (in_par, out_par) != GST_VALUE_EQUAL)
279     goto format_mismatch;
280
281   ctx = avcodec_alloc_context ();
282
283   space->width = ctx->width = in_width;
284   space->height = ctx->height = in_height;
285
286   space->interlaced = FALSE;
287   gst_structure_get_boolean (structure, "interlaced", &space->interlaced);
288
289   /* get from format */
290   ctx->pix_fmt = PIX_FMT_NB;
291   gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, incaps, ctx);
292   if (ctx->pix_fmt == PIX_FMT_NB)
293     goto invalid_in_caps;
294   space->from_pixfmt = ctx->pix_fmt;
295
296   /* palette, only for from data */
297   if (space->palette)
298     av_free (space->palette);
299   space->palette = ctx->palctrl;
300   ctx->palctrl = NULL;
301
302   /* get to format */
303   ctx->pix_fmt = PIX_FMT_NB;
304   gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, outcaps, ctx);
305   if (ctx->pix_fmt == PIX_FMT_NB)
306     goto invalid_out_caps;
307   space->to_pixfmt = ctx->pix_fmt;
308
309   GST_DEBUG ("reconfigured %d %d", space->from_pixfmt, space->to_pixfmt);
310
311   av_free (ctx);
312
313   return TRUE;
314
315   /* ERRORS */
316 no_width_height:
317   {
318     GST_DEBUG_OBJECT (space, "did not specify width or height");
319     space->from_pixfmt = PIX_FMT_NB;
320     space->to_pixfmt = PIX_FMT_NB;
321     return FALSE;
322   }
323 no_framerate:
324   {
325     GST_DEBUG_OBJECT (space, "did not specify framerate");
326     space->from_pixfmt = PIX_FMT_NB;
327     space->to_pixfmt = PIX_FMT_NB;
328     return FALSE;
329   }
330 format_mismatch:
331   {
332     GST_DEBUG_OBJECT (space, "input and output formats do not match");
333     space->from_pixfmt = PIX_FMT_NB;
334     space->to_pixfmt = PIX_FMT_NB;
335     return FALSE;
336   }
337 invalid_in_caps:
338   {
339     GST_DEBUG_OBJECT (space, "could not configure context for input format");
340     av_free (ctx);
341     space->from_pixfmt = PIX_FMT_NB;
342     space->to_pixfmt = PIX_FMT_NB;
343     return FALSE;
344   }
345 invalid_out_caps:
346   {
347     GST_DEBUG_OBJECT (space, "could not configure context for output format");
348     av_free (ctx);
349     space->from_pixfmt = PIX_FMT_NB;
350     space->to_pixfmt = PIX_FMT_NB;
351     return FALSE;
352   }
353 }
354
355 static GType
356 gst_ffmpegcsp_get_type (void)
357 {
358   static GType ffmpegcsp_type = 0;
359
360   if (!ffmpegcsp_type) {
361     static const GTypeInfo ffmpegcsp_info = {
362       sizeof (GstFFMpegCspClass),
363       (GBaseInitFunc) gst_ffmpegcsp_base_init,
364       NULL,
365       (GClassInitFunc) gst_ffmpegcsp_class_init,
366       NULL,
367       NULL,
368       sizeof (GstFFMpegCsp),
369       0,
370       (GInstanceInitFunc) gst_ffmpegcsp_init,
371     };
372
373     ffmpegcsp_type = g_type_register_static (GST_TYPE_BASE_TRANSFORM,
374         "GstFFMpegCsp", &ffmpegcsp_info, 0);
375   }
376
377   return ffmpegcsp_type;
378 }
379
380 static void
381 gst_ffmpegcsp_base_init (GstFFMpegCspClass * klass)
382 {
383   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
384
385   gst_element_class_add_pad_template (element_class, srctempl);
386   gst_element_class_add_pad_template (element_class, sinktempl);
387   gst_element_class_set_details (element_class, &ffmpegcsp_details);
388 }
389
390 static void
391 gst_ffmpegcsp_finalize (GObject * obj)
392 {
393   GstFFMpegCsp *space = GST_FFMPEGCSP (obj);
394
395   if (space->palette)
396     av_free (space->palette);
397
398   G_OBJECT_CLASS (parent_class)->finalize (obj);
399 }
400
401 static void
402 gst_ffmpegcsp_class_init (GstFFMpegCspClass * klass)
403 {
404   GObjectClass *gobject_class;
405   GstBaseTransformClass *gstbasetransform_class;
406
407   gobject_class = (GObjectClass *) klass;
408   gstbasetransform_class = (GstBaseTransformClass *) klass;
409
410   parent_class = g_type_class_peek_parent (klass);
411
412   gobject_class->finalize = gst_ffmpegcsp_finalize;
413
414   gstbasetransform_class->transform_caps =
415       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_transform_caps);
416   gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_ffmpegcsp_set_caps);
417   gstbasetransform_class->get_unit_size =
418       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_get_unit_size);
419   gstbasetransform_class->transform =
420       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_transform);
421 #if 0
422   gstbasetransform_class->transform_ip =
423       GST_DEBUG_FUNCPTR (gst_ffmpegcsp_transform_ip);
424 #endif
425
426   gstbasetransform_class->passthrough_on_same_caps = TRUE;
427 }
428
429 static void
430 gst_ffmpegcsp_init (GstFFMpegCsp * space)
431 {
432   gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (space), TRUE);
433   space->from_pixfmt = space->to_pixfmt = PIX_FMT_NB;
434   space->palette = NULL;
435 }
436
437 static gboolean
438 gst_ffmpegcsp_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
439     guint * size)
440 {
441   GstStructure *structure = NULL;
442   AVCodecContext *ctx = NULL;
443   gboolean ret = TRUE;
444   gint width, height;
445
446   g_assert (size);
447
448   structure = gst_caps_get_structure (caps, 0);
449   gst_structure_get_int (structure, "width", &width);
450   gst_structure_get_int (structure, "height", &height);
451
452   ctx = avcodec_alloc_context ();
453
454   g_assert (ctx != NULL);
455
456   ctx->pix_fmt = PIX_FMT_NB;
457
458   gst_ffmpegcsp_caps_with_codectype (CODEC_TYPE_VIDEO, caps, ctx);
459
460   if (G_UNLIKELY (ctx->pix_fmt == PIX_FMT_NB)) {
461     ret = FALSE;
462     goto beach;
463   }
464
465   *size = avpicture_get_size (ctx->pix_fmt, width, height);
466
467   /* ffmpeg frames have the palette after the frame data, whereas
468    * GStreamer currently puts it into the caps as 'palette_data' field,
469    * so for paletted data the frame size avpicture_get_size() returns is
470    * 1024 bytes larger than what GStreamer expects. */
471   if (gst_structure_has_field (structure, "palette_data") &&
472       ctx->pix_fmt == PIX_FMT_PAL8) {
473     *size -= 4 * 256;           /* = AVPALETTE_SIZE */
474   }
475
476 beach:
477
478   if (ctx->palctrl)
479     av_free (ctx->palctrl);
480   av_free (ctx);
481
482   return ret;
483 }
484
485 #if 0
486 /* FIXME: Could use transform_ip to implement endianness swap type operations */
487 static GstFlowReturn
488 gst_ffmpegcsp_transform_ip (GstBaseTransform * btrans, GstBuffer * inbuf)
489 {
490   /* do nothing */
491   return GST_FLOW_OK;
492 }
493 #endif
494
495 static GstFlowReturn
496 gst_ffmpegcsp_transform (GstBaseTransform * btrans, GstBuffer * inbuf,
497     GstBuffer * outbuf)
498 {
499   GstFFMpegCsp *space;
500   gint result;
501
502   space = GST_FFMPEGCSP (btrans);
503
504   GST_DEBUG ("from %d -> to %d", space->from_pixfmt, space->to_pixfmt);
505
506   if (G_UNLIKELY (space->from_pixfmt == PIX_FMT_NB ||
507           space->to_pixfmt == PIX_FMT_NB))
508     goto unknown_format;
509
510   /* fill from with source data */
511   gst_ffmpegcsp_avpicture_fill (&space->from_frame,
512       GST_BUFFER_DATA (inbuf), space->from_pixfmt, space->width, space->height,
513       space->interlaced);
514
515   /* fill optional palette */
516   if (space->palette)
517     space->from_frame.data[1] = (uint8_t *) space->palette->palette;
518
519   /* fill target frame */
520   gst_ffmpegcsp_avpicture_fill (&space->to_frame,
521       GST_BUFFER_DATA (outbuf), space->to_pixfmt, space->width, space->height,
522       space->interlaced);
523
524   /* and convert */
525   result = img_convert (&space->to_frame, space->to_pixfmt,
526       &space->from_frame, space->from_pixfmt, space->width, space->height);
527   if (result == -1)
528     goto not_supported;
529
530   /* baseclass copies timestamps */
531   GST_DEBUG ("from %d -> to %d done", space->from_pixfmt, space->to_pixfmt);
532
533   return GST_FLOW_OK;
534
535   /* ERRORS */
536 unknown_format:
537   {
538     GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
539         ("attempting to convert colorspaces between unknown formats"));
540     return GST_FLOW_NOT_NEGOTIATED;
541   }
542 not_supported:
543   {
544     GST_ELEMENT_ERROR (space, CORE, NOT_IMPLEMENTED, (NULL),
545         ("cannot convert between formats"));
546     return GST_FLOW_NOT_SUPPORTED;
547   }
548 }
549
550 gboolean
551 gst_ffmpegcolorspace_register (GstPlugin * plugin)
552 {
553   GstCaps *caps;
554
555   GST_DEBUG_CATEGORY_INIT (ffmpegcolorspace_debug, "ffmpegcolorspace", 0,
556       "FFMPEG-based colorspace converter");
557   GST_DEBUG_CATEGORY_GET (ffmpegcolorspace_performance, "GST_PERFORMANCE");
558
559   /* template caps */
560   caps = gst_ffmpegcsp_codectype_to_caps (CODEC_TYPE_VIDEO, NULL);
561
562   /* build templates */
563   srctempl = gst_pad_template_new ("src",
564       GST_PAD_SRC, GST_PAD_ALWAYS, gst_caps_copy (caps));
565
566   /* the sink template will do palette handling as well... */
567   sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
568
569   return gst_element_register (plugin, "ffmpegcolorspace",
570       GST_RANK_NONE, GST_TYPE_FFMPEGCSP);
571 }