ffdeinterlace: add "mode" property including automatic mode
[gstreamer-omap:gst-ffmpeg.git] / ext / ffmpeg / gstffmpegdeinterlace.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * This file:
4  * Copyright (C) 2005 Luca Ognibene <luogni@tin.it>
5  * Copyright (C) 2006 Martin Zlomek <martin.zlomek@itonis.tv>
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 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #ifdef HAVE_FFMPEG_UNINSTALLED
28 #  include <avcodec.h>
29 #else
30 #  include <libavcodec/avcodec.h>
31 #endif
32
33 #include <gst/gst.h>
34 #include <gst/video/video.h>
35
36 #include "gstffmpeg.h"
37 #include "gstffmpegcodecmap.h"
38 #include "gstffmpegutils.h"
39
40
41 /* Properties */
42
43 #define DEFAULT_MODE            GST_FFMPEGDEINTERLACE_MODE_AUTO
44
45 enum
46 {
47   PROP_0,
48   PROP_MODE,
49   PROP_LAST
50 };
51
52 typedef enum
53 {
54   GST_FFMPEGDEINTERLACE_MODE_AUTO,
55   GST_FFMPEGDEINTERLACE_MODE_INTERLACED,
56   GST_FFMPEGDEINTERLACE_MODE_DISABLED
57 } GstFFMpegDeinterlaceMode;
58
59 #define GST_TYPE_FFMPEGDEINTERLACE_MODES (gst_ffmpegdeinterlace_modes_get_type ())
60 static GType
61 gst_ffmpegdeinterlace_modes_get_type (void)
62 {
63   static GType deinterlace_modes_type = 0;
64
65   static const GEnumValue modes_types[] = {
66     {GST_FFMPEGDEINTERLACE_MODE_AUTO, "Auto detection", "auto"},
67     {GST_FFMPEGDEINTERLACE_MODE_INTERLACED, "Force deinterlacing",
68         "interlaced"},
69     {GST_FFMPEGDEINTERLACE_MODE_DISABLED, "Run in passthrough mode",
70         "disabled"},
71     {0, NULL, NULL},
72   };
73
74   if (!deinterlace_modes_type) {
75     deinterlace_modes_type =
76         g_enum_register_static ("GstFFMpegDeinterlaceModes", modes_types);
77   }
78   return deinterlace_modes_type;
79 }
80
81 typedef struct _GstFFMpegDeinterlace
82 {
83   GstElement element;
84
85   GstPad *sinkpad, *srcpad;
86
87   gint width, height;
88   gint to_size;
89
90   GstFFMpegDeinterlaceMode mode;
91
92   gboolean interlaced;          /* is input interlaced? */
93   gboolean passthrough;
94
95   gboolean reconfigure;
96   GstFFMpegDeinterlaceMode new_mode;
97
98   enum PixelFormat pixfmt;
99   AVPicture from_frame, to_frame;
100 } GstFFMpegDeinterlace;
101
102 typedef struct _GstFFMpegDeinterlaceClass
103 {
104   GstElementClass parent_class;
105 } GstFFMpegDeinterlaceClass;
106
107 #define GST_TYPE_FFMPEGDEINTERLACE \
108   (gst_ffmpegdeinterlace_get_type())
109 #define GST_FFMPEGDEINTERLACE(obj) \
110   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEINTERLACE,GstFFMpegDeinterlace))
111 #define GST_FFMPEGDEINTERLACE_CLASS(klass) \
112   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEINTERLACE,GstFFMpegDeinterlace))
113 #define GST_IS_FFMPEGDEINTERLACE(obj) \
114   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEINTERLACE))
115 #define GST_IS_FFMPEGDEINTERLACE_CLASS(klass) \
116   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEINTERLACE))
117
118 GType gst_ffmpegdeinterlace_get_type (void);
119
120 static void gst_ffmpegdeinterlace_set_property (GObject * self, guint prop_id,
121     const GValue * value, GParamSpec * pspec);
122 static void gst_ffmpegdeinterlace_get_property (GObject * self, guint prop_id,
123     GValue * value, GParamSpec * pspec);
124
125 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
126     GST_PAD_SRC,
127     GST_PAD_ALWAYS,
128     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
129     );
130
131 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
132     GST_PAD_SINK,
133     GST_PAD_ALWAYS,
134     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
135     );
136
137 GST_BOILERPLATE (GstFFMpegDeinterlace, gst_ffmpegdeinterlace, GstElement,
138     GST_TYPE_ELEMENT);
139
140 static GstFlowReturn gst_ffmpegdeinterlace_chain (GstPad * pad,
141     GstBuffer * inbuf);
142
143 static void
144 gst_ffmpegdeinterlace_base_init (gpointer g_class)
145 {
146   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
147
148   gst_element_class_add_pad_template (element_class,
149       gst_static_pad_template_get (&src_factory));
150   gst_element_class_add_pad_template (element_class,
151       gst_static_pad_template_get (&sink_factory));
152   gst_element_class_set_details_simple (element_class,
153       "FFMPEG Deinterlace element", "Filter/Effect/Video/Deinterlace",
154       "Deinterlace video", "Luca Ognibene <luogni@tin.it>");
155 }
156
157 static void
158 gst_ffmpegdeinterlace_class_init (GstFFMpegDeinterlaceClass * klass)
159 {
160   GObjectClass *gobject_class = (GObjectClass *) klass;
161
162   gobject_class->set_property = gst_ffmpegdeinterlace_set_property;
163   gobject_class->get_property = gst_ffmpegdeinterlace_get_property;
164
165   /**
166    * GstFFMpegDeinterlace:mode
167    *
168    * This selects whether the deinterlacing methods should
169    * always be applied or if they should only be applied
170    * on content that has the "interlaced" flag on the caps.
171    *
172    * Since: 0.10.13
173    */
174   g_object_class_install_property (gobject_class, PROP_MODE,
175       g_param_spec_enum ("mode", "Mode", "Deinterlace Mode",
176           GST_TYPE_FFMPEGDEINTERLACE_MODES,
177           DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
178       );
179 }
180
181 static void
182 gst_ffmpegdeinterlace_update_passthrough (GstFFMpegDeinterlace * deinterlace)
183 {
184   deinterlace->passthrough =
185       (deinterlace->mode == GST_FFMPEGDEINTERLACE_MODE_DISABLED
186       || (!deinterlace->interlaced
187           && deinterlace->mode != GST_FFMPEGDEINTERLACE_MODE_INTERLACED));
188   GST_DEBUG_OBJECT (deinterlace, "Passthrough: %d", deinterlace->passthrough);
189 }
190
191 static gboolean
192 gst_ffmpegdeinterlace_sink_setcaps (GstPad * pad, GstCaps * caps)
193 {
194   GstFFMpegDeinterlace *deinterlace =
195       GST_FFMPEGDEINTERLACE (gst_pad_get_parent (pad));
196   GstStructure *structure = gst_caps_get_structure (caps, 0);
197   AVCodecContext *ctx;
198   GstCaps *src_caps;
199   gboolean ret;
200
201   if (!gst_structure_get_int (structure, "width", &deinterlace->width))
202     return FALSE;
203   if (!gst_structure_get_int (structure, "height", &deinterlace->height))
204     return FALSE;
205
206   deinterlace->interlaced = FALSE;
207   gst_structure_get_boolean (structure, "interlaced", &deinterlace->interlaced);
208   gst_ffmpegdeinterlace_update_passthrough (deinterlace);
209
210   ctx = avcodec_alloc_context ();
211   ctx->width = deinterlace->width;
212   ctx->height = deinterlace->height;
213   ctx->pix_fmt = PIX_FMT_NB;
214   gst_ffmpeg_caps_with_codectype (AVMEDIA_TYPE_VIDEO, caps, ctx);
215   if (ctx->pix_fmt == PIX_FMT_NB) {
216     av_free (ctx);
217     return FALSE;
218   }
219
220   deinterlace->pixfmt = ctx->pix_fmt;
221
222   av_free (ctx);
223
224   deinterlace->to_size =
225       avpicture_get_size (deinterlace->pixfmt, deinterlace->width,
226       deinterlace->height);
227
228   src_caps = gst_caps_copy (caps);
229   gst_caps_set_simple (src_caps, "interlaced", G_TYPE_BOOLEAN,
230       deinterlace->interlaced, NULL);
231   ret = gst_pad_set_caps (deinterlace->srcpad, src_caps);
232   gst_caps_unref (src_caps);
233
234   return ret;
235 }
236
237 static void
238 gst_ffmpegdeinterlace_init (GstFFMpegDeinterlace * deinterlace,
239     GstFFMpegDeinterlaceClass * klass)
240 {
241   deinterlace->sinkpad =
242       gst_pad_new_from_static_template (&sink_factory, "sink");
243   gst_pad_set_setcaps_function (deinterlace->sinkpad,
244       gst_ffmpegdeinterlace_sink_setcaps);
245   gst_pad_set_chain_function (deinterlace->sinkpad,
246       gst_ffmpegdeinterlace_chain);
247   gst_element_add_pad (GST_ELEMENT (deinterlace), deinterlace->sinkpad);
248
249   deinterlace->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
250   gst_element_add_pad (GST_ELEMENT (deinterlace), deinterlace->srcpad);
251
252   deinterlace->pixfmt = PIX_FMT_NB;
253
254   deinterlace->interlaced = FALSE;
255   deinterlace->passthrough = FALSE;
256   deinterlace->reconfigure = FALSE;
257   deinterlace->mode = DEFAULT_MODE;
258   deinterlace->new_mode = -1;
259 }
260
261 static GstFlowReturn
262 gst_ffmpegdeinterlace_chain (GstPad * pad, GstBuffer * inbuf)
263 {
264   GstFFMpegDeinterlace *deinterlace =
265       GST_FFMPEGDEINTERLACE (gst_pad_get_parent (pad));
266   GstBuffer *outbuf = NULL;
267   GstFlowReturn result;
268
269   GST_OBJECT_LOCK (deinterlace);
270   if (deinterlace->reconfigure) {
271     if (deinterlace->new_mode != -1)
272       deinterlace->mode = deinterlace->new_mode;
273     deinterlace->new_mode = -1;
274
275     deinterlace->reconfigure = FALSE;
276     GST_OBJECT_UNLOCK (deinterlace);
277     if (GST_PAD_CAPS (deinterlace->srcpad))
278       gst_ffmpegdeinterlace_sink_setcaps (deinterlace->sinkpad,
279           GST_PAD_CAPS (deinterlace->sinkpad));
280   } else {
281     GST_OBJECT_UNLOCK (deinterlace);
282   }
283
284   if (deinterlace->passthrough)
285     return gst_pad_push (deinterlace->srcpad, inbuf);
286
287   result =
288       gst_pad_alloc_buffer (deinterlace->srcpad, GST_BUFFER_OFFSET_NONE,
289       deinterlace->to_size, GST_PAD_CAPS (deinterlace->srcpad), &outbuf);
290   if (result == GST_FLOW_OK) {
291     gst_ffmpeg_avpicture_fill (&deinterlace->from_frame,
292         GST_BUFFER_DATA (inbuf), deinterlace->pixfmt, deinterlace->width,
293         deinterlace->height);
294
295     gst_ffmpeg_avpicture_fill (&deinterlace->to_frame, GST_BUFFER_DATA (outbuf),
296         deinterlace->pixfmt, deinterlace->width, deinterlace->height);
297
298     avpicture_deinterlace (&deinterlace->to_frame, &deinterlace->from_frame,
299         deinterlace->pixfmt, deinterlace->width, deinterlace->height);
300
301     gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
302
303     result = gst_pad_push (deinterlace->srcpad, outbuf);
304   }
305
306   gst_buffer_unref (inbuf);
307
308   return result;
309 }
310
311 gboolean
312 gst_ffmpegdeinterlace_register (GstPlugin * plugin)
313 {
314   return gst_element_register (plugin, "ffdeinterlace",
315       GST_RANK_NONE, GST_TYPE_FFMPEGDEINTERLACE);
316 }
317
318 static void
319 gst_ffmpegdeinterlace_set_property (GObject * object, guint prop_id,
320     const GValue * value, GParamSpec * pspec)
321 {
322   GstFFMpegDeinterlace *self;
323
324   g_return_if_fail (GST_IS_FFMPEGDEINTERLACE (object));
325   self = GST_FFMPEGDEINTERLACE (object);
326
327   switch (prop_id) {
328     case PROP_MODE:{
329       gint new_mode;
330
331       GST_OBJECT_LOCK (self);
332       new_mode = g_value_get_enum (value);
333       if (self->mode != new_mode && GST_PAD_CAPS (self->srcpad)) {
334         self->reconfigure = TRUE;
335         self->new_mode = new_mode;
336       } else {
337         self->mode = new_mode;
338         gst_ffmpegdeinterlace_update_passthrough (self);
339       }
340       GST_OBJECT_UNLOCK (self);
341       break;
342     }
343     default:
344       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
345   }
346
347 }
348
349 static void
350 gst_ffmpegdeinterlace_get_property (GObject * object, guint prop_id,
351     GValue * value, GParamSpec * pspec)
352 {
353   GstFFMpegDeinterlace *self;
354
355   g_return_if_fail (GST_IS_FFMPEGDEINTERLACE (object));
356   self = GST_FFMPEGDEINTERLACE (object);
357
358   switch (prop_id) {
359     case PROP_MODE:
360       g_value_set_enum (value, self->mode);
361       break;
362     default:
363       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
364   }
365 }