deinterlace: Don't reconfigure the caps when changing properties
[platform/upstream/gstreamer.git] / gst / deinterlace / gstdeinterlace.c
1 /*
2  * GStreamer
3  * Copyright (C) 2005 Martin Eikermann <meiker@upb.de>
4  * Copyright (C) 2008-2010 Sebastian Dröge <slomo@collabora.co.uk>
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-deinterlace
24  *
25  * deinterlace deinterlaces interlaced video frames to progressive video frames.
26  * For this different algorithms can be selected which will be described later.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v filesrc location=/path/to/file ! decodebin2 ! ffmpegcolorspace ! deinterlace ! ffmpegcolorspace ! autovideosink
32  * ]| This pipeline deinterlaces a video file with the default deinterlacing options.
33  * </refsect2>
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "gstdeinterlace.h"
41 #include "tvtime/plugins.h"
42
43 #include <string.h>
44
45 GST_DEBUG_CATEGORY_STATIC (deinterlace_debug);
46 #define GST_CAT_DEFAULT (deinterlace_debug)
47
48 /* Properties */
49
50 #define DEFAULT_MODE            GST_DEINTERLACE_MODE_AUTO
51 #define DEFAULT_METHOD          GST_DEINTERLACE_GREEDY_H
52 #define DEFAULT_FIELDS          GST_DEINTERLACE_ALL
53 #define DEFAULT_FIELD_LAYOUT    GST_DEINTERLACE_LAYOUT_AUTO
54
55 enum
56 {
57   PROP_0,
58   PROP_MODE,
59   PROP_METHOD,
60   PROP_FIELDS,
61   PROP_FIELD_LAYOUT,
62   PROP_LAST
63 };
64
65 #define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ())
66 static GType
67 gst_deinterlace_methods_get_type (void)
68 {
69   static GType deinterlace_methods_type = 0;
70
71   static const GEnumValue methods_types[] = {
72     {GST_DEINTERLACE_TOMSMOCOMP, "Motion Adaptive: Motion Search",
73         "tomsmocomp"},
74     {GST_DEINTERLACE_GREEDY_H, "Motion Adaptive: Advanced Detection",
75         "greedyh"},
76     {GST_DEINTERLACE_GREEDY_L, "Motion Adaptive: Simple Detection", "greedyl"},
77     {GST_DEINTERLACE_VFIR, "Blur Vertical", "vfir"},
78     {GST_DEINTERLACE_LINEAR, "Television: Full resolution", "linear"},
79     {GST_DEINTERLACE_LINEAR_BLEND, "Blur: Temporal", "linearblend"},
80     {GST_DEINTERLACE_SCALER_BOB, "Double lines", "scalerbob"},
81     {GST_DEINTERLACE_WEAVE, "Weave", "weave"},
82     {GST_DEINTERLACE_WEAVE_TFF, "Progressive: Top Field First", "weavetff"},
83     {GST_DEINTERLACE_WEAVE_BFF, "Progressive: Bottom Field First", "weavebff"},
84     {0, NULL, NULL},
85   };
86
87   if (!deinterlace_methods_type) {
88     deinterlace_methods_type =
89         g_enum_register_static ("GstDeinterlaceMethods", methods_types);
90   }
91   return deinterlace_methods_type;
92 }
93
94 #define GST_TYPE_DEINTERLACE_FIELDS (gst_deinterlace_fields_get_type ())
95 static GType
96 gst_deinterlace_fields_get_type (void)
97 {
98   static GType deinterlace_fields_type = 0;
99
100   static const GEnumValue fields_types[] = {
101     {GST_DEINTERLACE_ALL, "All fields", "all"},
102     {GST_DEINTERLACE_TF, "Top fields only", "top"},
103     {GST_DEINTERLACE_BF, "Bottom fields only", "bottom"},
104     {0, NULL, NULL},
105   };
106
107   if (!deinterlace_fields_type) {
108     deinterlace_fields_type =
109         g_enum_register_static ("GstDeinterlaceFields", fields_types);
110   }
111   return deinterlace_fields_type;
112 }
113
114 #define GST_TYPE_DEINTERLACE_FIELD_LAYOUT (gst_deinterlace_field_layout_get_type ())
115 static GType
116 gst_deinterlace_field_layout_get_type (void)
117 {
118   static GType deinterlace_field_layout_type = 0;
119
120   static const GEnumValue field_layout_types[] = {
121     {GST_DEINTERLACE_LAYOUT_AUTO, "Auto detection", "auto"},
122     {GST_DEINTERLACE_LAYOUT_TFF, "Top field first", "tff"},
123     {GST_DEINTERLACE_LAYOUT_BFF, "Bottom field first", "bff"},
124     {0, NULL, NULL},
125   };
126
127   if (!deinterlace_field_layout_type) {
128     deinterlace_field_layout_type =
129         g_enum_register_static ("GstDeinterlaceFieldLayout",
130         field_layout_types);
131   }
132   return deinterlace_field_layout_type;
133 }
134
135 #define GST_TYPE_DEINTERLACE_MODES (gst_deinterlace_modes_get_type ())
136 static GType
137 gst_deinterlace_modes_get_type (void)
138 {
139   static GType deinterlace_modes_type = 0;
140
141   static const GEnumValue modes_types[] = {
142     {GST_DEINTERLACE_MODE_AUTO, "Auto detection", "auto"},
143     {GST_DEINTERLACE_MODE_INTERLACED, "Force deinterlacing", "interlaced"},
144     {GST_DEINTERLACE_MODE_DISABLED, "Run in passthrough mode", "disabled"},
145     {0, NULL, NULL},
146   };
147
148   if (!deinterlace_modes_type) {
149     deinterlace_modes_type =
150         g_enum_register_static ("GstDeinterlaceModes", modes_types);
151   }
152   return deinterlace_modes_type;
153 }
154
155 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
156     GST_PAD_SRC,
157     GST_PAD_ALWAYS,
158     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("Y444")
159         ";" GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("YVYU") ";"
160         GST_VIDEO_CAPS_YUV ("UYVY") ";"
161         GST_VIDEO_CAPS_YUV ("Y42B") ";" GST_VIDEO_CAPS_YUV ("I420") ";"
162         GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("Y41B") ";"
163         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";"
164         GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";"
165         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
166         GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
167         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR)
168     );
169
170 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
171     GST_PAD_SINK,
172     GST_PAD_ALWAYS,
173     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("Y444")
174         ";" GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("YVYU") ";"
175         GST_VIDEO_CAPS_YUV ("UYVY") ";"
176         GST_VIDEO_CAPS_YUV ("Y42B") ";" GST_VIDEO_CAPS_YUV ("I420") ";"
177         GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("Y41B") ";"
178         GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";"
179         GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";"
180         GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
181         GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
182         GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR)
183     );
184
185 static void gst_deinterlace_finalize (GObject * self);
186 static void gst_deinterlace_set_property (GObject * self, guint prop_id,
187     const GValue * value, GParamSpec * pspec);
188 static void gst_deinterlace_get_property (GObject * self, guint prop_id,
189     GValue * value, GParamSpec * pspec);
190
191 static GstCaps *gst_deinterlace_getcaps (GstPad * pad);
192 static gboolean gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps);
193 static gboolean gst_deinterlace_sink_event (GstPad * pad, GstEvent * event);
194 static gboolean gst_deinterlace_sink_query (GstPad * pad, GstQuery * query);
195 static GstFlowReturn gst_deinterlace_chain (GstPad * pad, GstBuffer * buffer);
196 static GstFlowReturn gst_deinterlace_alloc_buffer (GstPad * pad, guint64 offset,
197     guint size, GstCaps * caps, GstBuffer ** buf);
198 static GstStateChangeReturn gst_deinterlace_change_state (GstElement * element,
199     GstStateChange transition);
200
201 static gboolean gst_deinterlace_src_event (GstPad * pad, GstEvent * event);
202 static gboolean gst_deinterlace_src_query (GstPad * pad, GstQuery * query);
203 static const GstQueryType *gst_deinterlace_src_query_types (GstPad * pad);
204
205 static void gst_deinterlace_reset (GstDeinterlace * self);
206 static void gst_deinterlace_update_qos (GstDeinterlace * self,
207     gdouble proportion, GstClockTimeDiff diff, GstClockTime time);
208 static void gst_deinterlace_reset_qos (GstDeinterlace * self);
209 static void gst_deinterlace_read_qos (GstDeinterlace * self,
210     gdouble * proportion, GstClockTime * time);
211
212 static void gst_deinterlace_child_proxy_interface_init (gpointer g_iface,
213     gpointer iface_data);
214
215 static void
216 _do_init (GType object_type)
217 {
218   const GInterfaceInfo child_proxy_interface_info = {
219     (GInterfaceInitFunc) gst_deinterlace_child_proxy_interface_init,
220     NULL,                       /* interface_finalize */
221     NULL                        /* interface_data */
222   };
223
224   g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY,
225       &child_proxy_interface_info);
226 }
227
228 GST_BOILERPLATE_FULL (GstDeinterlace, gst_deinterlace, GstElement,
229     GST_TYPE_ELEMENT, _do_init);
230
231 static const struct
232 {
233   GType (*get_type) (void);
234 } _method_types[] = {
235   {
236   gst_deinterlace_method_tomsmocomp_get_type}, {
237   gst_deinterlace_method_greedy_h_get_type}, {
238   gst_deinterlace_method_greedy_l_get_type}, {
239   gst_deinterlace_method_vfir_get_type}, {
240   gst_deinterlace_method_linear_get_type}, {
241   gst_deinterlace_method_linear_blend_get_type}, {
242   gst_deinterlace_method_scaler_bob_get_type}, {
243   gst_deinterlace_method_weave_get_type}, {
244   gst_deinterlace_method_weave_tff_get_type}, {
245   gst_deinterlace_method_weave_bff_get_type}
246 };
247
248 static void
249 gst_deinterlace_set_method (GstDeinterlace * self, GstDeinterlaceMethods method)
250 {
251   GType method_type;
252
253   GST_DEBUG_OBJECT (self, "Setting new method %d", method);
254
255   if (self->method) {
256     if (self->method_id == method &&
257         gst_deinterlace_method_supported (G_TYPE_FROM_INSTANCE (self->method),
258             self->format, self->width, self->height)) {
259       GST_DEBUG_OBJECT (self, "Reusing current method");
260       return;
261     }
262
263     gst_child_proxy_child_removed (GST_OBJECT (self),
264         GST_OBJECT (self->method));
265     gst_object_unparent (GST_OBJECT (self->method));
266     self->method = NULL;
267   }
268
269   method_type =
270       _method_types[method].get_type !=
271       NULL ? _method_types[method].get_type () : G_TYPE_INVALID;
272   if (method_type == G_TYPE_INVALID
273       || !gst_deinterlace_method_supported (method_type, self->format,
274           self->width, self->height)) {
275     GType tmp;
276     gint i;
277
278     method_type = G_TYPE_INVALID;
279
280     GST_WARNING_OBJECT (self, "Method doesn't support requested format");
281     for (i = 0; i < G_N_ELEMENTS (_method_types); i++) {
282       if (_method_types[i].get_type == NULL)
283         continue;
284       tmp = _method_types[i].get_type ();
285       if (gst_deinterlace_method_supported (tmp, self->format, self->width,
286               self->height)) {
287         GST_DEBUG_OBJECT (self, "Using method %d", i);
288         method_type = tmp;
289         break;
290       }
291     }
292     /* If we get here we must have invalid caps! */
293     g_assert (method_type != G_TYPE_INVALID);
294   }
295
296   self->method = g_object_new (method_type, NULL);
297   self->method_id = method;
298
299   gst_object_set_name (GST_OBJECT (self->method), "method");
300   gst_object_set_parent (GST_OBJECT (self->method), GST_OBJECT (self));
301   gst_child_proxy_child_added (GST_OBJECT (self), GST_OBJECT (self->method));
302
303   if (self->method)
304     gst_deinterlace_method_setup (self->method, self->format, self->width,
305         self->height);
306 }
307
308 static gboolean
309 gst_deinterlace_clip_buffer (GstDeinterlace * self, GstBuffer * buffer)
310 {
311   gboolean ret = TRUE;
312   GstClockTime start, stop;
313   gint64 cstart, cstop;
314
315   GST_DEBUG_OBJECT (self,
316       "Clipping buffer to the current segment: %" GST_TIME_FORMAT " -- %"
317       GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
318       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
319   GST_DEBUG_OBJECT (self, "Current segment: %" GST_SEGMENT_FORMAT,
320       &self->segment);
321
322   if (G_UNLIKELY (self->segment.format != GST_FORMAT_TIME))
323     goto beach;
324   if (G_UNLIKELY (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)))
325     goto beach;
326
327   start = GST_BUFFER_TIMESTAMP (buffer);
328   stop = start + GST_BUFFER_DURATION (buffer);
329
330   if (!(ret = gst_segment_clip (&self->segment, GST_FORMAT_TIME,
331               start, stop, &cstart, &cstop)))
332     goto beach;
333
334   GST_BUFFER_TIMESTAMP (buffer) = cstart;
335   if (GST_CLOCK_TIME_IS_VALID (cstop))
336     GST_BUFFER_DURATION (buffer) = cstop - cstart;
337
338 beach:
339   if (ret)
340     GST_DEBUG_OBJECT (self,
341         "Clipped buffer to the current segment: %" GST_TIME_FORMAT " -- %"
342         GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
343         GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
344   else
345     GST_DEBUG_OBJECT (self, "Buffer outside the current segment -- dropping");
346
347   return ret;
348 }
349
350 static void
351 gst_deinterlace_base_init (gpointer klass)
352 {
353   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
354
355   gst_element_class_add_pad_template (element_class,
356       gst_static_pad_template_get (&src_templ));
357   gst_element_class_add_pad_template (element_class,
358       gst_static_pad_template_get (&sink_templ));
359
360   gst_element_class_set_details_simple (element_class,
361       "Deinterlacer",
362       "Filter/Video",
363       "Deinterlace Methods ported from DScaler/TvTime",
364       "Martin Eikermann <meiker@upb.de>, "
365       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
366 }
367
368 static void
369 gst_deinterlace_class_init (GstDeinterlaceClass * klass)
370 {
371   GObjectClass *gobject_class = (GObjectClass *) klass;
372
373   GstElementClass *element_class = (GstElementClass *) klass;
374
375   gobject_class->set_property = gst_deinterlace_set_property;
376   gobject_class->get_property = gst_deinterlace_get_property;
377   gobject_class->finalize = gst_deinterlace_finalize;
378
379   /**
380    * GstDeinterlace:mode
381    * 
382    * This selects whether the deinterlacing methods should
383    * always be applied or if they should only be applied
384    * on content that has the "interlaced" flag on the caps.
385    *
386    */
387   g_object_class_install_property (gobject_class, PROP_MODE,
388       g_param_spec_enum ("mode",
389           "Mode",
390           "Deinterlace Mode",
391           GST_TYPE_DEINTERLACE_MODES,
392           DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
393       );
394
395   /**
396    * GstDeinterlace:method
397    * 
398    * Selects the different deinterlacing algorithms that can be used.
399    * These provide different quality and CPU usage.
400    *
401    * Some methods provide parameters which can be set by getting
402    * the "method" child via the #GstChildProxy interface and
403    * setting the appropiate properties on it.
404    *
405    * <itemizedlist>
406    * <listitem>
407    * <para>
408    * tomsmocomp
409    * Motion Adaptive: Motion Search
410    * </para>
411    * </listitem>
412    * <listitem>
413    * <para>
414    * greedyh
415    * Motion Adaptive: Advanced Detection
416    * </para>
417    * </listitem>
418    * <listitem>
419    * <para>
420    * greedyl
421    * Motion Adaptive: Simple Detection
422    * </para>
423    * </listitem>
424    * <listitem>
425    * <para>
426    * vfir
427    * Blur vertical
428    * </para>
429    * </listitem>
430    * <listitem>
431    * <para>
432    * linear
433    * Linear interpolation
434    * </para>
435    * </listitem>
436    * <listitem>
437    * <para>
438    * linearblend
439    * Linear interpolation in time domain
440    * </para>
441    * </listitem>
442    * <listitem>
443    * <para>
444    * scalerbob
445    * Double lines
446    * </para>
447    * </listitem>
448    * <listitem>
449    * <para>
450    * weave
451    * Weave
452    * </para>
453    * </listitem>
454    * <listitem>
455    * <para>
456    * weavetff
457    * Progressive: Top Field First
458    * </para>
459    * </listitem>
460    * <listitem>
461    * <para>
462    * weavebff
463    * Progressive: Bottom Field First
464    * </para>
465    * </listitem>
466    * </itemizedlist>
467    */
468   g_object_class_install_property (gobject_class, PROP_METHOD,
469       g_param_spec_enum ("method",
470           "Method",
471           "Deinterlace Method",
472           GST_TYPE_DEINTERLACE_METHODS,
473           DEFAULT_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
474       );
475
476   /**
477    * GstDeinterlace:fields
478    *
479    * This selects which fields should be output. If "all" is selected
480    * the output framerate will be double.
481    *
482    */
483   g_object_class_install_property (gobject_class, PROP_FIELDS,
484       g_param_spec_enum ("fields",
485           "fields",
486           "Fields to use for deinterlacing",
487           GST_TYPE_DEINTERLACE_FIELDS,
488           DEFAULT_FIELDS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
489       );
490
491   /**
492    * GstDeinterlace:layout
493    *
494    * This selects which fields is the first in time.
495    *
496    */
497   g_object_class_install_property (gobject_class, PROP_FIELD_LAYOUT,
498       g_param_spec_enum ("tff",
499           "tff",
500           "Deinterlace top field first",
501           GST_TYPE_DEINTERLACE_FIELD_LAYOUT,
502           DEFAULT_FIELD_LAYOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
503       );
504
505   element_class->change_state =
506       GST_DEBUG_FUNCPTR (gst_deinterlace_change_state);
507 }
508
509 static GstObject *
510 gst_deinterlace_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
511     guint index)
512 {
513   GstDeinterlace *self = GST_DEINTERLACE (child_proxy);
514
515   g_return_val_if_fail (index == 0, NULL);
516
517   return gst_object_ref (self->method);
518 }
519
520 static guint
521 gst_deinterlace_child_proxy_get_children_count (GstChildProxy * child_proxy)
522 {
523   GstDeinterlace *self = GST_DEINTERLACE (child_proxy);
524
525   return ((self->method) ? 1 : 0);
526 }
527
528 static void
529 gst_deinterlace_child_proxy_interface_init (gpointer g_iface,
530     gpointer iface_data)
531 {
532   GstChildProxyInterface *iface = g_iface;
533
534   iface->get_child_by_index = gst_deinterlace_child_proxy_get_child_by_index;
535   iface->get_children_count = gst_deinterlace_child_proxy_get_children_count;
536 }
537
538 static void
539 gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass)
540 {
541   self->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
542   gst_pad_set_chain_function (self->sinkpad,
543       GST_DEBUG_FUNCPTR (gst_deinterlace_chain));
544   gst_pad_set_event_function (self->sinkpad,
545       GST_DEBUG_FUNCPTR (gst_deinterlace_sink_event));
546   gst_pad_set_setcaps_function (self->sinkpad,
547       GST_DEBUG_FUNCPTR (gst_deinterlace_setcaps));
548   gst_pad_set_getcaps_function (self->sinkpad,
549       GST_DEBUG_FUNCPTR (gst_deinterlace_getcaps));
550   gst_pad_set_query_function (self->sinkpad,
551       GST_DEBUG_FUNCPTR (gst_deinterlace_sink_query));
552   gst_pad_set_bufferalloc_function (self->sinkpad,
553       GST_DEBUG_FUNCPTR (gst_deinterlace_alloc_buffer));
554   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
555
556   self->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
557   gst_pad_set_event_function (self->srcpad,
558       GST_DEBUG_FUNCPTR (gst_deinterlace_src_event));
559   gst_pad_set_query_type_function (self->srcpad,
560       GST_DEBUG_FUNCPTR (gst_deinterlace_src_query_types));
561   gst_pad_set_query_function (self->srcpad,
562       GST_DEBUG_FUNCPTR (gst_deinterlace_src_query));
563   gst_pad_set_getcaps_function (self->srcpad,
564       GST_DEBUG_FUNCPTR (gst_deinterlace_getcaps));
565   gst_pad_set_setcaps_function (self->srcpad,
566       GST_DEBUG_FUNCPTR (gst_deinterlace_setcaps));
567   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
568
569   self->mode = DEFAULT_MODE;
570   gst_deinterlace_set_method (self, DEFAULT_METHOD);
571   self->fields = DEFAULT_FIELDS;
572   self->field_layout = DEFAULT_FIELD_LAYOUT;
573
574   self->still_frame_mode = FALSE;
575
576   gst_deinterlace_reset (self);
577 }
578
579 static void
580 gst_deinterlace_reset_history (GstDeinterlace * self)
581 {
582   gint i;
583
584   GST_DEBUG_OBJECT (self, "Resetting history");
585
586   for (i = 0; i < self->history_count; i++) {
587     if (self->field_history[i].buf) {
588       gst_buffer_unref (self->field_history[i].buf);
589       self->field_history[i].buf = NULL;
590     }
591   }
592   memset (self->field_history, 0,
593       GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField));
594   self->history_count = 0;
595
596   if (self->last_buffer)
597     gst_buffer_unref (self->last_buffer);
598   self->last_buffer = NULL;
599 }
600
601 static void
602 gst_deinterlace_update_passthrough (GstDeinterlace * self)
603 {
604   self->passthrough = (self->mode == GST_DEINTERLACE_MODE_DISABLED
605       || (!self->interlaced && self->mode != GST_DEINTERLACE_MODE_INTERLACED));
606   GST_DEBUG_OBJECT (self, "Passthrough: %d", self->passthrough);
607 }
608
609 static void
610 gst_deinterlace_reset (GstDeinterlace * self)
611 {
612   GST_DEBUG_OBJECT (self, "Resetting internal state");
613
614   self->format = GST_VIDEO_FORMAT_UNKNOWN;
615   self->width = 0;
616   self->height = 0;
617   self->frame_size = 0;
618   self->fps_n = self->fps_d = 0;
619   self->passthrough = FALSE;
620
621   self->reconfigure = FALSE;
622   if (self->new_mode != -1)
623     self->mode = self->new_mode;
624   if (self->new_fields != -1)
625     self->fields = self->new_fields;
626   self->new_mode = -1;
627   self->new_fields = -1;
628
629   gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
630
631   if (self->sink_caps)
632     gst_caps_unref (self->sink_caps);
633   self->sink_caps = NULL;
634
635   if (self->src_caps)
636     gst_caps_unref (self->src_caps);
637   self->src_caps = NULL;
638
639   if (self->request_caps)
640     gst_caps_unref (self->request_caps);
641   self->request_caps = NULL;
642
643   gst_deinterlace_reset_history (self);
644
645   gst_deinterlace_reset_qos (self);
646 }
647
648 static void
649 gst_deinterlace_set_property (GObject * object, guint prop_id,
650     const GValue * value, GParamSpec * pspec)
651 {
652   GstDeinterlace *self;
653
654   g_return_if_fail (GST_IS_DEINTERLACE (object));
655   self = GST_DEINTERLACE (object);
656
657   switch (prop_id) {
658     case PROP_MODE:{
659       gint new_mode;
660
661       GST_OBJECT_LOCK (self);
662       new_mode = g_value_get_enum (value);
663       if (self->mode != new_mode && GST_PAD_CAPS (self->srcpad)) {
664         self->reconfigure = TRUE;
665         self->new_mode = new_mode;
666       } else {
667         self->mode = new_mode;
668         gst_deinterlace_update_passthrough (self);
669       }
670       GST_OBJECT_UNLOCK (self);
671       break;
672     }
673     case PROP_METHOD:
674       gst_deinterlace_set_method (self, g_value_get_enum (value));
675       break;
676     case PROP_FIELDS:{
677       gint new_fields;
678
679       GST_OBJECT_LOCK (self);
680       new_fields = g_value_get_enum (value);
681       if (self->fields != new_fields && GST_PAD_CAPS (self->srcpad)) {
682         self->reconfigure = TRUE;
683         self->new_fields = new_fields;
684       } else {
685         self->fields = new_fields;
686       }
687       GST_OBJECT_UNLOCK (self);
688       break;
689     }
690     case PROP_FIELD_LAYOUT:
691       self->field_layout = g_value_get_enum (value);
692       break;
693     default:
694       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
695   }
696
697 }
698
699 static void
700 gst_deinterlace_get_property (GObject * object, guint prop_id,
701     GValue * value, GParamSpec * pspec)
702 {
703   GstDeinterlace *self;
704
705   g_return_if_fail (GST_IS_DEINTERLACE (object));
706   self = GST_DEINTERLACE (object);
707
708   switch (prop_id) {
709     case PROP_MODE:
710       g_value_set_enum (value, self->mode);
711       break;
712     case PROP_METHOD:
713       g_value_set_enum (value, self->method_id);
714       break;
715     case PROP_FIELDS:
716       g_value_set_enum (value, self->fields);
717       break;
718     case PROP_FIELD_LAYOUT:
719       g_value_set_enum (value, self->field_layout);
720       break;
721     default:
722       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
723   }
724 }
725
726 static void
727 gst_deinterlace_finalize (GObject * object)
728 {
729   GstDeinterlace *self = GST_DEINTERLACE (object);
730
731   gst_deinterlace_reset (self);
732
733   if (self->method) {
734     gst_object_unparent (GST_OBJECT (self->method));
735     self->method = NULL;
736   }
737
738   G_OBJECT_CLASS (parent_class)->finalize (object);
739 }
740
741 static GstBuffer *
742 gst_deinterlace_pop_history (GstDeinterlace * self)
743 {
744   GstBuffer *buffer;
745
746   g_return_val_if_fail (self->history_count > 0, NULL);
747
748   GST_DEBUG_OBJECT (self, "Pop last history buffer -- current history size %d",
749       self->history_count);
750
751   buffer = self->field_history[self->history_count - 1].buf;
752
753   self->history_count--;
754
755   GST_DEBUG_OBJECT (self, "Returning buffer: %" GST_TIME_FORMAT
756       " with duration %" GST_TIME_FORMAT " and size %u",
757       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
758       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer));
759
760   return buffer;
761 }
762
763 static void
764 gst_deinterlace_push_history (GstDeinterlace * self, GstBuffer * buffer)
765 {
766   int i = 1;
767   GstClockTime timestamp;
768   GstDeinterlaceFieldLayout field_layout = self->field_layout;
769   gboolean repeated = GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_RFF);
770   gboolean tff = GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_TFF);
771   gboolean onefield =
772       GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_ONEFIELD);
773   GstBuffer *field1, *field2;
774   guint fields_to_push = (onefield) ? 1 : (!repeated) ? 2 : 3;
775   gint field1_flags, field2_flags;
776
777   g_return_if_fail (self->history_count <
778       GST_DEINTERLACE_MAX_FIELD_HISTORY - fields_to_push);
779
780   GST_DEBUG_OBJECT (self, "Pushing new buffer to the history: %" GST_TIME_FORMAT
781       " with duration %" GST_TIME_FORMAT " and size %u",
782       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
783       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer));
784
785   for (i = GST_DEINTERLACE_MAX_FIELD_HISTORY - 1; i >= fields_to_push; i--) {
786     self->field_history[i].buf = self->field_history[i - fields_to_push].buf;
787     self->field_history[i].flags =
788         self->field_history[i - fields_to_push].flags;
789   }
790
791   if (field_layout == GST_DEINTERLACE_LAYOUT_AUTO) {
792     if (!self->interlaced) {
793       GST_WARNING_OBJECT (self, "Can't detect field layout -- assuming TFF");
794       field_layout = GST_DEINTERLACE_LAYOUT_TFF;
795     } else if (tff) {
796       field_layout = GST_DEINTERLACE_LAYOUT_TFF;
797     } else {
798       field_layout = GST_DEINTERLACE_LAYOUT_BFF;
799     }
800   }
801
802   if (field_layout == GST_DEINTERLACE_LAYOUT_TFF) {
803     GST_DEBUG_OBJECT (self, "Top field first");
804     field1 = gst_buffer_ref (buffer);
805     field1_flags = PICTURE_INTERLACED_TOP;
806     field2 = gst_buffer_ref (buffer);
807     field2_flags = PICTURE_INTERLACED_BOTTOM;
808   } else {
809     GST_DEBUG_OBJECT (self, "Bottom field first");
810     field1 = gst_buffer_ref (buffer);
811     field1_flags = PICTURE_INTERLACED_BOTTOM;
812     field2 = gst_buffer_ref (buffer);
813     field2_flags = PICTURE_INTERLACED_TOP;
814   }
815
816   /* Timestamps are assigned to the field buffers under the assumption that
817      the timestamp of the buffer equals the first fields timestamp */
818
819   timestamp = GST_BUFFER_TIMESTAMP (buffer);
820   GST_BUFFER_TIMESTAMP (field1) = timestamp;
821   GST_BUFFER_TIMESTAMP (field2) = timestamp + self->field_duration;
822   if (repeated)
823     GST_BUFFER_TIMESTAMP (field2) += self->field_duration;
824
825   if (repeated) {
826     self->field_history[0].buf = field2;
827     self->field_history[0].flags = field2_flags;
828     self->field_history[1].buf = gst_buffer_ref (field1);
829     GST_BUFFER_TIMESTAMP (self->field_history[1].buf) += self->field_duration;
830     self->field_history[1].flags = field1_flags;
831     self->field_history[2].buf = field1;
832     self->field_history[2].flags = field1_flags;
833   } else if (!onefield) {
834     self->field_history[0].buf = field2;
835     self->field_history[0].flags = field2_flags;
836     self->field_history[1].buf = field1;
837     self->field_history[1].flags = field1_flags;
838   } else {                      /* onefield */
839     self->field_history[0].buf = field1;
840     self->field_history[0].flags = field1_flags;
841     gst_buffer_unref (field2);
842   }
843
844   self->history_count += fields_to_push;
845
846   GST_DEBUG_OBJECT (self, "Pushed buffer -- current history size %d",
847       self->history_count);
848
849   if (self->last_buffer)
850     gst_buffer_unref (self->last_buffer);
851   self->last_buffer = buffer;
852 }
853
854 static void
855 gst_deinterlace_update_qos (GstDeinterlace * self, gdouble proportion,
856     GstClockTimeDiff diff, GstClockTime timestamp)
857 {
858   GST_DEBUG_OBJECT (self,
859       "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %"
860       GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "",
861       GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp));
862
863   GST_OBJECT_LOCK (self);
864   self->proportion = proportion;
865   if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
866     if (G_UNLIKELY (diff > 0))
867       self->earliest_time =
868           timestamp + 2 * diff + ((self->fields ==
869               GST_DEINTERLACE_ALL) ? self->field_duration : 2 *
870           self->field_duration);
871     else
872       self->earliest_time = timestamp + diff;
873   } else {
874     self->earliest_time = GST_CLOCK_TIME_NONE;
875   }
876   GST_OBJECT_UNLOCK (self);
877 }
878
879 static void
880 gst_deinterlace_reset_qos (GstDeinterlace * self)
881 {
882   gst_deinterlace_update_qos (self, 0.5, 0, GST_CLOCK_TIME_NONE);
883 }
884
885 static void
886 gst_deinterlace_read_qos (GstDeinterlace * self, gdouble * proportion,
887     GstClockTime * time)
888 {
889   GST_OBJECT_LOCK (self);
890   *proportion = self->proportion;
891   *time = self->earliest_time;
892   GST_OBJECT_UNLOCK (self);
893 }
894
895 /* Perform qos calculations before processing the next frame. Returns TRUE if
896  * the frame should be processed, FALSE if the frame can be dropped entirely */
897 static gboolean
898 gst_deinterlace_do_qos (GstDeinterlace * self, GstClockTime timestamp)
899 {
900   GstClockTime qostime, earliest_time;
901   gdouble proportion;
902
903   /* no timestamp, can't do QoS => process frame */
904   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
905     GST_LOG_OBJECT (self, "invalid timestamp, can't do QoS, process frame");
906     return TRUE;
907   }
908
909   /* get latest QoS observation values */
910   gst_deinterlace_read_qos (self, &proportion, &earliest_time);
911
912   /* skip qos if we have no observation (yet) => process frame */
913   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
914     GST_LOG_OBJECT (self, "no observation yet, process frame");
915     return TRUE;
916   }
917
918   /* qos is done on running time */
919   qostime = gst_segment_to_running_time (&self->segment, GST_FORMAT_TIME,
920       timestamp);
921
922   /* see how our next timestamp relates to the latest qos timestamp */
923   GST_LOG_OBJECT (self, "qostime %" GST_TIME_FORMAT ", earliest %"
924       GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
925
926   if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) {
927     GST_DEBUG_OBJECT (self, "we are late, drop frame");
928     return FALSE;
929   }
930
931   GST_LOG_OBJECT (self, "process frame");
932   return TRUE;
933 }
934
935 static GstFlowReturn
936 gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
937 {
938   GstDeinterlace *self = GST_DEINTERLACE (GST_PAD_PARENT (pad));
939   GstClockTime timestamp;
940   GstFlowReturn ret = GST_FLOW_OK;
941   gint fields_required = 0;
942   gint cur_field_idx = 0;
943   GstBuffer *outbuf;
944
945   GST_OBJECT_LOCK (self);
946   if (self->reconfigure) {
947     if (self->new_fields != -1)
948       self->fields = self->new_fields;
949     if (self->new_mode != -1)
950       self->mode = self->new_mode;
951     self->new_mode = self->new_fields = -1;
952
953     self->reconfigure = FALSE;
954     GST_OBJECT_UNLOCK (self);
955     if (GST_PAD_CAPS (self->srcpad))
956       gst_deinterlace_setcaps (self->sinkpad, GST_PAD_CAPS (self->sinkpad));
957   } else {
958     GST_OBJECT_UNLOCK (self);
959   }
960
961   if (self->still_frame_mode || self->passthrough)
962     return gst_pad_push (self->srcpad, buf);
963
964   if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
965     GST_DEBUG_OBJECT (self, "DISCONT buffer, resetting history");
966     gst_deinterlace_reset_history (self);
967   }
968
969   gst_deinterlace_push_history (self, buf);
970   buf = NULL;
971
972   fields_required = gst_deinterlace_method_get_fields_required (self->method);
973
974   /* Not enough fields in the history */
975   if (self->history_count < fields_required + 1) {
976     GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
977         self->history_count, fields_required + 1);
978     return GST_FLOW_OK;
979   }
980
981   while (self->history_count >= fields_required) {
982     if (self->fields == GST_DEINTERLACE_ALL)
983       GST_DEBUG_OBJECT (self, "All fields");
984     else if (self->fields == GST_DEINTERLACE_TF)
985       GST_DEBUG_OBJECT (self, "Top fields");
986     else if (self->fields == GST_DEINTERLACE_BF)
987       GST_DEBUG_OBJECT (self, "Bottom fields");
988
989     cur_field_idx = self->history_count - fields_required;
990
991     if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP
992             && self->fields == GST_DEINTERLACE_TF) ||
993         self->fields == GST_DEINTERLACE_ALL) {
994       GST_DEBUG_OBJECT (self, "deinterlacing top field");
995
996       /* create new buffer */
997       ret = gst_pad_alloc_buffer (self->srcpad,
998           GST_BUFFER_OFFSET_NONE, self->frame_size, self->src_caps, &outbuf);
999       if (ret != GST_FLOW_OK)
1000         return ret;
1001
1002       if (self->src_caps != GST_BUFFER_CAPS (outbuf) &&
1003           !gst_caps_is_equal (self->src_caps, GST_BUFFER_CAPS (outbuf))) {
1004         gst_caps_replace (&self->request_caps, GST_BUFFER_CAPS (outbuf));
1005         GST_DEBUG_OBJECT (self, "Upstream wants new caps %" GST_PTR_FORMAT,
1006             self->request_caps);
1007
1008         gst_buffer_unref (outbuf);
1009         outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
1010
1011         if (!outbuf)
1012           return GST_FLOW_ERROR;
1013
1014         gst_buffer_set_caps (outbuf, self->src_caps);
1015       }
1016
1017       g_return_val_if_fail (self->history_count - 1 -
1018           gst_deinterlace_method_get_latency (self->method) >= 0,
1019           GST_FLOW_ERROR);
1020
1021       buf =
1022           self->field_history[self->history_count - 1 -
1023           gst_deinterlace_method_get_latency (self->method)].buf;
1024       timestamp = GST_BUFFER_TIMESTAMP (buf);
1025
1026       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
1027       if (self->fields == GST_DEINTERLACE_ALL)
1028         GST_BUFFER_DURATION (outbuf) = self->field_duration;
1029       else
1030         GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
1031
1032       /* Check if we need to drop the frame because of QoS */
1033       if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
1034         gst_buffer_unref (gst_deinterlace_pop_history (self));
1035         gst_buffer_unref (outbuf);
1036         outbuf = NULL;
1037         ret = GST_FLOW_OK;
1038       } else {
1039         /* do magic calculus */
1040         gst_deinterlace_method_deinterlace_frame (self->method,
1041             self->field_history, self->history_count, outbuf);
1042
1043         gst_buffer_unref (gst_deinterlace_pop_history (self));
1044
1045         if (gst_deinterlace_clip_buffer (self, outbuf)) {
1046           ret = gst_pad_push (self->srcpad, outbuf);
1047         } else {
1048           ret = GST_FLOW_OK;
1049           gst_buffer_unref (outbuf);
1050         }
1051
1052         outbuf = NULL;
1053         if (ret != GST_FLOW_OK)
1054           return ret;
1055       }
1056     }
1057     /* no calculation done: remove excess field */
1058     else if (self->field_history[cur_field_idx].flags ==
1059         PICTURE_INTERLACED_TOP && self->fields == GST_DEINTERLACE_BF) {
1060       GST_DEBUG_OBJECT (self, "Removing unused top field");
1061       gst_buffer_unref (gst_deinterlace_pop_history (self));
1062     }
1063
1064     cur_field_idx = self->history_count - fields_required;
1065     if (self->history_count < fields_required)
1066       break;
1067
1068     /* deinterlace bottom_field */
1069     if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM
1070             && self->fields == GST_DEINTERLACE_BF) ||
1071         self->fields == GST_DEINTERLACE_ALL) {
1072       GST_DEBUG_OBJECT (self, "deinterlacing bottom field");
1073
1074       /* create new buffer */
1075       ret = gst_pad_alloc_buffer (self->srcpad,
1076           GST_BUFFER_OFFSET_NONE, self->frame_size, self->src_caps, &outbuf);
1077       if (ret != GST_FLOW_OK)
1078         return ret;
1079
1080       if (self->src_caps != GST_BUFFER_CAPS (outbuf) &&
1081           !gst_caps_is_equal (self->src_caps, GST_BUFFER_CAPS (outbuf))) {
1082         gst_caps_replace (&self->request_caps, GST_BUFFER_CAPS (outbuf));
1083         GST_DEBUG_OBJECT (self, "Upstream wants new caps %" GST_PTR_FORMAT,
1084             self->request_caps);
1085
1086         gst_buffer_unref (outbuf);
1087         outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
1088
1089         if (!outbuf)
1090           return GST_FLOW_ERROR;
1091
1092         gst_buffer_set_caps (outbuf, self->src_caps);
1093       }
1094
1095       g_return_val_if_fail (self->history_count - 1 -
1096           gst_deinterlace_method_get_latency (self->method) >= 0,
1097           GST_FLOW_ERROR);
1098
1099       buf =
1100           self->field_history[self->history_count - 1 -
1101           gst_deinterlace_method_get_latency (self->method)].buf;
1102       timestamp = GST_BUFFER_TIMESTAMP (buf);
1103
1104       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
1105       if (self->fields == GST_DEINTERLACE_ALL)
1106         GST_BUFFER_DURATION (outbuf) = self->field_duration;
1107       else
1108         GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
1109
1110       /* Check if we need to drop the frame because of QoS */
1111       if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
1112         gst_buffer_unref (gst_deinterlace_pop_history (self));
1113         gst_buffer_unref (outbuf);
1114         outbuf = NULL;
1115         ret = GST_FLOW_OK;
1116       } else {
1117         /* do magic calculus */
1118         gst_deinterlace_method_deinterlace_frame (self->method,
1119             self->field_history, self->history_count, outbuf);
1120
1121         gst_buffer_unref (gst_deinterlace_pop_history (self));
1122
1123         if (gst_deinterlace_clip_buffer (self, outbuf)) {
1124           ret = gst_pad_push (self->srcpad, outbuf);
1125         } else {
1126           ret = GST_FLOW_OK;
1127           gst_buffer_unref (outbuf);
1128         }
1129
1130         outbuf = NULL;
1131         if (ret != GST_FLOW_OK)
1132           return ret;
1133       }
1134     }
1135     /* no calculation done: remove excess field */
1136     else if (self->field_history[cur_field_idx].flags ==
1137         PICTURE_INTERLACED_BOTTOM && self->fields == GST_DEINTERLACE_TF) {
1138       GST_DEBUG_OBJECT (self, "Removing unused bottom field");
1139       gst_buffer_unref (gst_deinterlace_pop_history (self));
1140     }
1141   }
1142
1143   return ret;
1144 }
1145
1146 static gint
1147 gst_greatest_common_divisor (gint a, gint b)
1148 {
1149   while (b != 0) {
1150     int temp = a;
1151
1152     a = b;
1153     b = temp % b;
1154   }
1155
1156   return ABS (a);
1157 }
1158
1159 static gboolean
1160 gst_fraction_double (gint * n_out, gint * d_out, gboolean half)
1161 {
1162   gint n, d, gcd;
1163
1164   n = *n_out;
1165   d = *d_out;
1166
1167   if (d == 0)
1168     return FALSE;
1169
1170   if (n == 0 || (n == G_MAXINT && d == 1))
1171     return TRUE;
1172
1173   gcd = gst_greatest_common_divisor (n, d);
1174   n /= gcd;
1175   d /= gcd;
1176
1177   if (!half) {
1178     if (G_MAXINT / 2 >= ABS (n)) {
1179       n *= 2;
1180     } else if (d >= 2) {
1181       d /= 2;
1182     } else {
1183       return FALSE;
1184     }
1185   } else {
1186     if (G_MAXINT / 2 >= ABS (d)) {
1187       d *= 2;
1188     } else if (n >= 2) {
1189       n /= 2;
1190     } else {
1191       return FALSE;
1192     }
1193   }
1194
1195   *n_out = n;
1196   *d_out = d;
1197
1198   return TRUE;
1199 }
1200
1201 static GstCaps *
1202 gst_deinterlace_getcaps (GstPad * pad)
1203 {
1204   GstCaps *ret;
1205   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1206   GstPad *otherpad;
1207   gint len;
1208   const GstCaps *ourcaps;
1209   GstCaps *peercaps;
1210
1211   GST_OBJECT_LOCK (self);
1212
1213   otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad;
1214
1215   ourcaps = gst_pad_get_pad_template_caps (pad);
1216   peercaps = gst_pad_peer_get_caps (otherpad);
1217
1218   if (peercaps) {
1219     GST_DEBUG_OBJECT (pad, "Peer has caps %" GST_PTR_FORMAT, peercaps);
1220     ret = gst_caps_intersect (ourcaps, peercaps);
1221     gst_caps_unref (peercaps);
1222   } else {
1223     ret = gst_caps_copy (ourcaps);
1224   }
1225
1226   GST_OBJECT_UNLOCK (self);
1227
1228   for (len = gst_caps_get_size (ret); len > 0; len--) {
1229     GstStructure *s = gst_caps_get_structure (ret, len - 1);
1230
1231     if (pad == self->sinkpad || self->passthrough)
1232       gst_structure_remove_field (s, "interlaced");
1233     else
1234       gst_structure_set (s, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
1235
1236     if (!self->passthrough && self->fields == GST_DEINTERLACE_ALL) {
1237       const GValue *val;
1238
1239       val = gst_structure_get_value (s, "framerate");
1240       if (!val)
1241         continue;
1242
1243       if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION) {
1244         gint n, d;
1245
1246         n = gst_value_get_fraction_numerator (val);
1247         d = gst_value_get_fraction_denominator (val);
1248
1249         if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1250           goto error;
1251         }
1252
1253         gst_structure_set (s, "framerate", GST_TYPE_FRACTION, n, d, NULL);
1254       } else if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION_RANGE) {
1255         const GValue *min, *max;
1256         GValue nrange = { 0, }, nmin = {
1257         0,}, nmax = {
1258         0,};
1259         gint n, d;
1260
1261         g_value_init (&nrange, GST_TYPE_FRACTION_RANGE);
1262         g_value_init (&nmin, GST_TYPE_FRACTION);
1263         g_value_init (&nmax, GST_TYPE_FRACTION);
1264
1265         min = gst_value_get_fraction_range_min (val);
1266         max = gst_value_get_fraction_range_max (val);
1267
1268         n = gst_value_get_fraction_numerator (min);
1269         d = gst_value_get_fraction_denominator (min);
1270
1271         if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1272           g_value_unset (&nrange);
1273           g_value_unset (&nmax);
1274           g_value_unset (&nmin);
1275           goto error;
1276         }
1277
1278         gst_value_set_fraction (&nmin, n, d);
1279
1280         n = gst_value_get_fraction_numerator (max);
1281         d = gst_value_get_fraction_denominator (max);
1282
1283         if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1284           g_value_unset (&nrange);
1285           g_value_unset (&nmax);
1286           g_value_unset (&nmin);
1287           goto error;
1288         }
1289
1290         gst_value_set_fraction (&nmax, n, d);
1291         gst_value_set_fraction_range (&nrange, &nmin, &nmax);
1292
1293         gst_structure_set_value (s, "framerate", &nrange);
1294
1295         g_value_unset (&nmin);
1296         g_value_unset (&nmax);
1297         g_value_unset (&nrange);
1298       } else if (G_VALUE_TYPE (val) == GST_TYPE_LIST) {
1299         const GValue *lval;
1300         GValue nlist = { 0, };
1301         GValue nval = { 0, };
1302         gint i;
1303
1304         g_value_init (&nlist, GST_TYPE_LIST);
1305         for (i = gst_value_list_get_size (val); i > 0; i--) {
1306           gint n, d;
1307
1308           lval = gst_value_list_get_value (val, i);
1309
1310           if (G_VALUE_TYPE (lval) != GST_TYPE_FRACTION)
1311             continue;
1312
1313           n = gst_value_get_fraction_numerator (lval);
1314           d = gst_value_get_fraction_denominator (lval);
1315
1316           /* Double/Half the framerate but if this fails simply
1317            * skip this value from the list */
1318           if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1319             continue;
1320           }
1321
1322           g_value_init (&nval, GST_TYPE_FRACTION);
1323
1324           gst_value_set_fraction (&nval, n, d);
1325           gst_value_list_append_value (&nlist, &nval);
1326           g_value_unset (&nval);
1327         }
1328         gst_structure_set_value (s, "framerate", &nlist);
1329         g_value_unset (&nlist);
1330       }
1331     }
1332   }
1333
1334   GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret);
1335
1336   return ret;
1337
1338 error:
1339   GST_ERROR_OBJECT (pad, "Unable to transform peer caps");
1340   gst_caps_unref (ret);
1341   return NULL;
1342 }
1343
1344 static gboolean
1345 gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps)
1346 {
1347   gboolean res = TRUE;
1348   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1349   GstPad *otherpad;
1350   GstCaps *othercaps;
1351
1352   otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad;
1353
1354   res =
1355       gst_video_format_parse_caps (caps, &self->format, &self->width,
1356       &self->height);
1357   res &= gst_video_parse_caps_framerate (caps, &self->fps_n, &self->fps_d);
1358   if (pad == self->sinkpad)
1359     res &= gst_video_format_parse_caps_interlaced (caps, &self->interlaced);
1360   if (!res)
1361     goto invalid_caps;
1362
1363   gst_deinterlace_update_passthrough (self);
1364
1365   if (!self->passthrough && self->fields == GST_DEINTERLACE_ALL) {
1366     gint fps_n = self->fps_n, fps_d = self->fps_d;
1367
1368     if (!gst_fraction_double (&fps_n, &fps_d, otherpad != self->srcpad))
1369       goto invalid_caps;
1370
1371     othercaps = gst_caps_copy (caps);
1372
1373     gst_caps_set_simple (othercaps, "framerate", GST_TYPE_FRACTION, fps_n,
1374         fps_d, NULL);
1375   } else {
1376     othercaps = gst_caps_ref (caps);
1377   }
1378
1379   if (otherpad == self->srcpad && self->mode != GST_DEINTERLACE_MODE_DISABLED) {
1380     othercaps = gst_caps_make_writable (othercaps);
1381     gst_caps_set_simple (othercaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
1382   }
1383
1384   if (!gst_pad_set_caps (otherpad, othercaps))
1385     goto caps_not_accepted;
1386
1387   self->frame_size =
1388       gst_video_format_get_size (self->format, self->width, self->height);
1389
1390   if (self->fields == GST_DEINTERLACE_ALL && otherpad == self->srcpad)
1391     self->field_duration =
1392         gst_util_uint64_scale (GST_SECOND, self->fps_d, self->fps_n);
1393   else
1394     self->field_duration =
1395         gst_util_uint64_scale (GST_SECOND, self->fps_d, 2 * self->fps_n);
1396
1397   if (pad == self->sinkpad) {
1398     gst_caps_replace (&self->sink_caps, caps);
1399     gst_caps_replace (&self->src_caps, othercaps);
1400   } else {
1401     gst_caps_replace (&self->src_caps, caps);
1402     gst_caps_replace (&self->sink_caps, othercaps);
1403   }
1404
1405   gst_deinterlace_set_method (self, self->method_id);
1406   gst_deinterlace_method_setup (self->method, self->format, self->width,
1407       self->height);
1408
1409   GST_DEBUG_OBJECT (pad, "Set caps: %" GST_PTR_FORMAT, caps);
1410   GST_DEBUG_OBJECT (pad, "Other caps: %" GST_PTR_FORMAT, othercaps);
1411
1412   gst_caps_unref (othercaps);
1413
1414 done:
1415
1416   gst_object_unref (self);
1417   return res;
1418
1419 invalid_caps:
1420   res = FALSE;
1421   GST_ERROR_OBJECT (pad, "Invalid caps: %" GST_PTR_FORMAT, caps);
1422   goto done;
1423
1424 caps_not_accepted:
1425   res = FALSE;
1426   GST_ERROR_OBJECT (pad, "Caps not accepted: %" GST_PTR_FORMAT, othercaps);
1427   gst_caps_unref (othercaps);
1428   goto done;
1429 }
1430
1431 static gboolean
1432 gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
1433 {
1434   gboolean res = TRUE;
1435   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1436
1437   GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
1438
1439   switch (GST_EVENT_TYPE (event)) {
1440     case GST_EVENT_NEWSEGMENT:
1441     {
1442       GstFormat fmt;
1443       gboolean is_update;
1444       gint64 start, end, base;
1445       gdouble rate, applied_rate;
1446
1447       gst_event_parse_new_segment_full (event, &is_update, &rate, &applied_rate,
1448           &fmt, &start, &end, &base);
1449       if (fmt == GST_FORMAT_TIME) {
1450         GST_DEBUG_OBJECT (pad,
1451             "Got NEWSEGMENT event in GST_FORMAT_TIME, passing on (%"
1452             GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", GST_TIME_ARGS (start),
1453             GST_TIME_ARGS (end));
1454         gst_segment_set_newsegment_full (&self->segment, is_update, rate,
1455             applied_rate, fmt, start, end, base);
1456       } else {
1457         gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1458       }
1459
1460       gst_deinterlace_reset_qos (self);
1461       gst_deinterlace_reset_history (self);
1462       res = gst_pad_push_event (self->srcpad, event);
1463       break;
1464     }
1465     case GST_EVENT_CUSTOM_DOWNSTREAM:{
1466       gboolean still_state;
1467
1468       if (gst_video_event_parse_still_frame (event, &still_state)) {
1469         GST_DEBUG_OBJECT (self, "Received still frame event, state %d",
1470             still_state);
1471
1472         if (still_state) {
1473           GstFlowReturn ret;
1474
1475           GST_DEBUG_OBJECT (self, "Handling still frame");
1476           self->still_frame_mode = TRUE;
1477           if (self->last_buffer) {
1478             ret =
1479                 gst_pad_push (self->srcpad, gst_buffer_ref (self->last_buffer));
1480             GST_DEBUG_OBJECT (self, "Pushed still frame, result: %s",
1481                 gst_flow_get_name (ret));
1482           } else {
1483             GST_WARNING_OBJECT (self, "No pending buffer!");
1484           }
1485         } else {
1486           GST_DEBUG_OBJECT (self, "Ending still frames");
1487           self->still_frame_mode = FALSE;
1488         }
1489       }
1490     }
1491       /* fall through */
1492     case GST_EVENT_EOS:
1493       gst_deinterlace_reset_history (self);
1494
1495       /* fall through */
1496     default:
1497       res = gst_pad_push_event (self->srcpad, event);
1498       break;
1499
1500     case GST_EVENT_FLUSH_STOP:
1501       if (self->still_frame_mode) {
1502         GST_DEBUG_OBJECT (self, "Ending still frames");
1503         self->still_frame_mode = FALSE;
1504       }
1505       gst_deinterlace_reset_qos (self);
1506       res = gst_pad_push_event (self->srcpad, event);
1507       gst_deinterlace_reset_history (self);
1508       break;
1509   }
1510
1511   gst_object_unref (self);
1512   return res;
1513 }
1514
1515 static gboolean
1516 gst_deinterlace_sink_query (GstPad * pad, GstQuery * query)
1517 {
1518   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1519   gboolean res = FALSE;
1520
1521   GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
1522
1523   switch (GST_QUERY_TYPE (query)) {
1524     default:{
1525       GstPad *peer = gst_pad_get_peer (self->srcpad);
1526
1527       if (peer) {
1528         res = gst_pad_query (peer, query);
1529         gst_object_unref (peer);
1530       } else {
1531         res = FALSE;
1532       }
1533       break;
1534     }
1535   }
1536
1537   gst_object_unref (self);
1538   return res;
1539 }
1540
1541 static GstStateChangeReturn
1542 gst_deinterlace_change_state (GstElement * element, GstStateChange transition)
1543 {
1544   GstStateChangeReturn ret;
1545   GstDeinterlace *self = GST_DEINTERLACE (element);
1546
1547   switch (transition) {
1548     case GST_STATE_CHANGE_NULL_TO_READY:
1549       break;
1550     case GST_STATE_CHANGE_READY_TO_PAUSED:
1551       break;
1552     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1553       break;
1554     default:
1555       break;
1556   }
1557
1558   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1559   if (ret != GST_STATE_CHANGE_SUCCESS)
1560     return ret;
1561
1562   switch (transition) {
1563     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1564       break;
1565     case GST_STATE_CHANGE_PAUSED_TO_READY:
1566       gst_deinterlace_reset (self);
1567       break;
1568     case GST_STATE_CHANGE_READY_TO_NULL:
1569     default:
1570       break;
1571   }
1572
1573   return ret;
1574 }
1575
1576 static gboolean
1577 gst_deinterlace_src_event (GstPad * pad, GstEvent * event)
1578 {
1579   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1580   gboolean res;
1581
1582   GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
1583
1584   switch (GST_EVENT_TYPE (event)) {
1585     case GST_EVENT_QOS:{
1586       GstClockTimeDiff diff;
1587       GstClockTime timestamp;
1588       gdouble proportion;
1589
1590       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
1591
1592       gst_deinterlace_update_qos (self, proportion, diff, timestamp);
1593     }
1594       /* fall through */
1595     default:
1596       res = gst_pad_push_event (self->sinkpad, event);
1597       break;
1598   }
1599
1600   gst_object_unref (self);
1601
1602   return res;
1603 }
1604
1605 static gboolean
1606 gst_deinterlace_src_query (GstPad * pad, GstQuery * query)
1607 {
1608   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1609   gboolean res = FALSE;
1610
1611   GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
1612
1613   switch (GST_QUERY_TYPE (query)) {
1614     case GST_QUERY_LATENCY:
1615       if (!self->passthrough) {
1616         GstClockTime min, max;
1617         gboolean live;
1618         GstPad *peer;
1619
1620         if ((peer = gst_pad_get_peer (self->sinkpad))) {
1621           if ((res = gst_pad_query (peer, query))) {
1622             GstClockTime latency;
1623             gint fields_required = 0;
1624             gint method_latency = 0;
1625
1626             if (self->method) {
1627               fields_required =
1628                   gst_deinterlace_method_get_fields_required (self->method);
1629               method_latency =
1630                   gst_deinterlace_method_get_latency (self->method);
1631             }
1632
1633             gst_query_parse_latency (query, &live, &min, &max);
1634
1635             GST_DEBUG_OBJECT (self, "Peer latency: min %"
1636                 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1637                 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1638
1639             /* add our own latency */
1640             latency = (fields_required + method_latency) * self->field_duration;
1641
1642             GST_DEBUG_OBJECT (self, "Our latency: min %" GST_TIME_FORMAT
1643                 ", max %" GST_TIME_FORMAT,
1644                 GST_TIME_ARGS (latency), GST_TIME_ARGS (latency));
1645
1646             min += latency;
1647             if (max != GST_CLOCK_TIME_NONE)
1648               max += latency;
1649
1650             GST_DEBUG_OBJECT (self, "Calculated total latency : min %"
1651                 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1652                 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1653
1654             gst_query_set_latency (query, live, min, max);
1655           }
1656           gst_object_unref (peer);
1657         } else {
1658           res = FALSE;
1659         }
1660         break;
1661       }
1662     default:{
1663       GstPad *peer = gst_pad_get_peer (self->sinkpad);
1664
1665       if (peer) {
1666         res = gst_pad_query (peer, query);
1667         gst_object_unref (peer);
1668       } else {
1669         res = FALSE;
1670       }
1671       break;
1672     }
1673   }
1674
1675   gst_object_unref (self);
1676   return res;
1677 }
1678
1679 static const GstQueryType *
1680 gst_deinterlace_src_query_types (GstPad * pad)
1681 {
1682   static const GstQueryType types[] = {
1683     GST_QUERY_LATENCY,
1684     GST_QUERY_NONE
1685   };
1686   return types;
1687 }
1688
1689 static GstFlowReturn
1690 gst_deinterlace_alloc_buffer (GstPad * pad, guint64 offset, guint size,
1691     GstCaps * caps, GstBuffer ** buf)
1692 {
1693   GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1694   GstFlowReturn ret = GST_FLOW_OK;
1695
1696   *buf = NULL;
1697
1698   GST_DEBUG_OBJECT (pad, "alloc with caps %" GST_PTR_FORMAT ", size %u", caps,
1699       size);
1700
1701   if (self->still_frame_mode || self->passthrough) {
1702     ret = gst_pad_alloc_buffer (self->srcpad, offset, size, caps, buf);
1703   } else if (G_LIKELY (!self->request_caps)) {
1704     *buf = gst_buffer_try_new_and_alloc (size);
1705     if (G_UNLIKELY (!*buf)) {
1706       ret = GST_FLOW_ERROR;
1707     } else {
1708       gst_buffer_set_caps (*buf, caps);
1709       GST_BUFFER_OFFSET (*buf) = offset;
1710     }
1711   } else {
1712     gint width, height;
1713     GstVideoFormat fmt;
1714     guint new_frame_size;
1715     GstCaps *new_caps = gst_caps_copy (self->request_caps);
1716
1717     if (self->fields == GST_DEINTERLACE_ALL) {
1718       gint n, d;
1719       GstStructure *s = gst_caps_get_structure (new_caps, 0);
1720
1721       gst_structure_get_fraction (s, "framerate", &n, &d);
1722
1723       if (!gst_fraction_double (&n, &d, TRUE)) {
1724         gst_object_unref (self);
1725         gst_caps_unref (new_caps);
1726         return GST_FLOW_OK;
1727       }
1728
1729       gst_structure_set (s, "framerate", GST_TYPE_FRACTION, n, d, NULL);
1730     }
1731
1732     if (G_UNLIKELY (!gst_video_format_parse_caps (new_caps, &fmt, &width,
1733                 &height))) {
1734       gst_object_unref (self);
1735       gst_caps_unref (new_caps);
1736       return GST_FLOW_OK;
1737     }
1738
1739     new_frame_size = gst_video_format_get_size (fmt, width, height);
1740
1741     *buf = gst_buffer_try_new_and_alloc (new_frame_size);
1742     if (G_UNLIKELY (!*buf)) {
1743       ret = GST_FLOW_ERROR;
1744     } else {
1745       gst_buffer_set_caps (*buf, new_caps);
1746       gst_caps_unref (self->request_caps);
1747       self->request_caps = NULL;
1748       gst_caps_unref (new_caps);
1749     }
1750   }
1751
1752   gst_object_unref (self);
1753
1754   return ret;
1755 }
1756
1757 static gboolean
1758 plugin_init (GstPlugin * plugin)
1759 {
1760   GST_DEBUG_CATEGORY_INIT (deinterlace_debug, "deinterlace", 0, "Deinterlacer");
1761
1762   oil_init ();
1763
1764   if (!gst_element_register (plugin, "deinterlace", GST_RANK_NONE,
1765           GST_TYPE_DEINTERLACE)) {
1766     return FALSE;
1767   }
1768
1769   return TRUE;
1770 }
1771
1772 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1773     GST_VERSION_MINOR,
1774     "deinterlace",
1775     "Deinterlacer", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1776     GST_PACKAGE_ORIGIN);