3 * Copyright (C) 2005 Martin Eikermann <meiker@upb.de>
4 * Copyright (C) 2008-2010 Sebastian Dröge <slomo@collabora.co.uk>
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.
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.
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.
23 * SECTION:element-deinterlace
25 * deinterlace deinterlaces interlaced video frames to progressive video frames.
26 * For this different algorithms can be selected which will be described later.
29 * <title>Example launch line</title>
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.
40 #include "gstdeinterlace.h"
41 #include "tvtime/plugins.h"
49 GST_DEBUG_CATEGORY_STATIC (deinterlace_debug);
50 #define GST_CAT_DEFAULT (deinterlace_debug)
54 #define DEFAULT_MODE GST_DEINTERLACE_MODE_AUTO
55 #define DEFAULT_METHOD GST_DEINTERLACE_GREEDY_H
56 #define DEFAULT_FIELDS GST_DEINTERLACE_ALL
57 #define DEFAULT_FIELD_LAYOUT GST_DEINTERLACE_LAYOUT_AUTO
69 static const GEnumValue methods_types[] = {
70 {GST_DEINTERLACE_TOMSMOCOMP, "Motion Adaptive: Motion Search",
72 {GST_DEINTERLACE_GREEDY_H, "Motion Adaptive: Advanced Detection",
74 {GST_DEINTERLACE_GREEDY_L, "Motion Adaptive: Simple Detection", "greedyl"},
75 {GST_DEINTERLACE_VFIR, "Blur Vertical", "vfir"},
76 {GST_DEINTERLACE_LINEAR, "Television: Full resolution", "linear"},
77 {GST_DEINTERLACE_LINEAR_BLEND, "Blur: Temporal (Do Not Use)",
79 {GST_DEINTERLACE_SCALER_BOB, "Double lines", "scalerbob"},
80 {GST_DEINTERLACE_WEAVE, "Weave (Do Not Use)", "weave"},
81 {GST_DEINTERLACE_WEAVE_TFF, "Progressive: Top Field First (Do Not Use)",
83 {GST_DEINTERLACE_WEAVE_BFF, "Progressive: Bottom Field First (Do Not Use)",
89 #define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ())
91 gst_deinterlace_methods_get_type (void)
93 static GType deinterlace_methods_type = 0;
95 if (!deinterlace_methods_type) {
96 deinterlace_methods_type =
97 g_enum_register_static ("GstDeinterlaceMethods", methods_types);
99 return deinterlace_methods_type;
102 #define GST_TYPE_DEINTERLACE_FIELDS (gst_deinterlace_fields_get_type ())
104 gst_deinterlace_fields_get_type (void)
106 static GType deinterlace_fields_type = 0;
108 static const GEnumValue fields_types[] = {
109 {GST_DEINTERLACE_ALL, "All fields", "all"},
110 {GST_DEINTERLACE_TF, "Top fields only", "top"},
111 {GST_DEINTERLACE_BF, "Bottom fields only", "bottom"},
115 if (!deinterlace_fields_type) {
116 deinterlace_fields_type =
117 g_enum_register_static ("GstDeinterlaceFields", fields_types);
119 return deinterlace_fields_type;
122 #define GST_TYPE_DEINTERLACE_FIELD_LAYOUT (gst_deinterlace_field_layout_get_type ())
124 gst_deinterlace_field_layout_get_type (void)
126 static GType deinterlace_field_layout_type = 0;
128 static const GEnumValue field_layout_types[] = {
129 {GST_DEINTERLACE_LAYOUT_AUTO, "Auto detection", "auto"},
130 {GST_DEINTERLACE_LAYOUT_TFF, "Top field first", "tff"},
131 {GST_DEINTERLACE_LAYOUT_BFF, "Bottom field first", "bff"},
135 if (!deinterlace_field_layout_type) {
136 deinterlace_field_layout_type =
137 g_enum_register_static ("GstDeinterlaceFieldLayout",
140 return deinterlace_field_layout_type;
143 #define GST_TYPE_DEINTERLACE_MODES (gst_deinterlace_modes_get_type ())
145 gst_deinterlace_modes_get_type (void)
147 static GType deinterlace_modes_type = 0;
149 static const GEnumValue modes_types[] = {
150 {GST_DEINTERLACE_MODE_AUTO, "Auto detection", "auto"},
151 {GST_DEINTERLACE_MODE_INTERLACED, "Force deinterlacing", "interlaced"},
152 {GST_DEINTERLACE_MODE_DISABLED, "Run in passthrough mode", "disabled"},
156 if (!deinterlace_modes_type) {
157 deinterlace_modes_type =
158 g_enum_register_static ("GstDeinterlaceModes", modes_types);
160 return deinterlace_modes_type;
163 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
166 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("Y444")
167 ";" GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("YVYU") ";"
168 GST_VIDEO_CAPS_YUV ("UYVY") ";"
169 GST_VIDEO_CAPS_YUV ("Y42B") ";" GST_VIDEO_CAPS_YUV ("I420") ";"
170 GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("Y41B") ";"
171 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";"
172 GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";"
173 GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
174 GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
175 GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR)
178 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
181 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("Y444")
182 ";" GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("YVYU") ";"
183 GST_VIDEO_CAPS_YUV ("UYVY") ";"
184 GST_VIDEO_CAPS_YUV ("Y42B") ";" GST_VIDEO_CAPS_YUV ("I420") ";"
185 GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("Y41B") ";"
186 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_ABGR ";"
187 GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA ";"
188 GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
189 GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
190 GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR)
193 static void gst_deinterlace_finalize (GObject * self);
194 static void gst_deinterlace_set_property (GObject * self, guint prop_id,
195 const GValue * value, GParamSpec * pspec);
196 static void gst_deinterlace_get_property (GObject * self, guint prop_id,
197 GValue * value, GParamSpec * pspec);
199 static GstCaps *gst_deinterlace_getcaps (GstPad * pad);
200 static gboolean gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps);
201 static gboolean gst_deinterlace_sink_event (GstPad * pad, GstEvent * event);
202 static gboolean gst_deinterlace_sink_query (GstPad * pad, GstQuery * query);
203 static GstFlowReturn gst_deinterlace_chain (GstPad * pad, GstBuffer * buffer);
204 static GstFlowReturn gst_deinterlace_alloc_buffer (GstPad * pad, guint64 offset,
205 guint size, GstCaps * caps, GstBuffer ** buf);
206 static GstStateChangeReturn gst_deinterlace_change_state (GstElement * element,
207 GstStateChange transition);
209 static gboolean gst_deinterlace_src_event (GstPad * pad, GstEvent * event);
210 static gboolean gst_deinterlace_src_query (GstPad * pad, GstQuery * query);
211 static const GstQueryType *gst_deinterlace_src_query_types (GstPad * pad);
213 static GstFlowReturn gst_deinterlace_output_frame (GstDeinterlace * self,
215 static void gst_deinterlace_reset (GstDeinterlace * self);
216 static void gst_deinterlace_update_qos (GstDeinterlace * self,
217 gdouble proportion, GstClockTimeDiff diff, GstClockTime time);
218 static void gst_deinterlace_reset_qos (GstDeinterlace * self);
219 static void gst_deinterlace_read_qos (GstDeinterlace * self,
220 gdouble * proportion, GstClockTime * time);
222 static void gst_deinterlace_child_proxy_interface_init (gpointer g_iface,
223 gpointer iface_data);
226 _do_init (GType object_type)
228 const GInterfaceInfo child_proxy_interface_info = {
229 (GInterfaceInitFunc) gst_deinterlace_child_proxy_interface_init,
230 NULL, /* interface_finalize */
231 NULL /* interface_data */
234 g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY,
235 &child_proxy_interface_info);
238 GST_BOILERPLATE_FULL (GstDeinterlace, gst_deinterlace, GstElement,
239 GST_TYPE_ELEMENT, _do_init);
243 GType (*get_type) (void);
244 } _method_types[] = {
246 gst_deinterlace_method_tomsmocomp_get_type}, {
247 gst_deinterlace_method_greedy_h_get_type}, {
248 gst_deinterlace_method_greedy_l_get_type}, {
249 gst_deinterlace_method_vfir_get_type}, {
250 gst_deinterlace_method_linear_get_type}, {
251 gst_deinterlace_method_linear_blend_get_type}, {
252 gst_deinterlace_method_scaler_bob_get_type}, {
253 gst_deinterlace_method_weave_get_type}, {
254 gst_deinterlace_method_weave_tff_get_type}, {
255 gst_deinterlace_method_weave_bff_get_type}
259 gst_deinterlace_set_method (GstDeinterlace * self, GstDeinterlaceMethods method)
263 GST_DEBUG_OBJECT (self, "Setting new method %d", method);
266 if (self->method_id == method &&
267 gst_deinterlace_method_supported (G_TYPE_FROM_INSTANCE (self->method),
268 self->format, self->width, self->height)) {
269 GST_DEBUG_OBJECT (self, "Reusing current method");
273 gst_child_proxy_child_removed (GST_OBJECT (self),
274 GST_OBJECT (self->method));
275 gst_object_unparent (GST_OBJECT (self->method));
280 _method_types[method].get_type !=
281 NULL ? _method_types[method].get_type () : G_TYPE_INVALID;
282 if (method_type == G_TYPE_INVALID
283 || !gst_deinterlace_method_supported (method_type, self->format,
284 self->width, self->height)) {
288 method_type = G_TYPE_INVALID;
290 GST_WARNING_OBJECT (self, "Method doesn't support requested format");
291 for (i = 0; i < G_N_ELEMENTS (_method_types); i++) {
292 if (_method_types[i].get_type == NULL)
294 tmp = _method_types[i].get_type ();
295 if (gst_deinterlace_method_supported (tmp, self->format, self->width,
297 GST_DEBUG_OBJECT (self, "Using method %d", i);
302 /* If we get here we must have invalid caps! */
303 g_assert (method_type != G_TYPE_INVALID);
306 self->method = g_object_new (method_type, NULL);
307 self->method_id = method;
309 gst_object_set_name (GST_OBJECT (self->method), "method");
310 gst_object_set_parent (GST_OBJECT (self->method), GST_OBJECT (self));
311 gst_child_proxy_child_added (GST_OBJECT (self), GST_OBJECT (self->method));
314 gst_deinterlace_method_setup (self->method, self->format, self->width,
319 gst_deinterlace_clip_buffer (GstDeinterlace * self, GstBuffer * buffer)
322 GstClockTime start, stop;
323 gint64 cstart, cstop;
325 GST_DEBUG_OBJECT (self,
326 "Clipping buffer to the current segment: %" GST_TIME_FORMAT " -- %"
327 GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
328 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
329 GST_DEBUG_OBJECT (self, "Current segment: %" GST_SEGMENT_FORMAT,
332 if (G_UNLIKELY (self->segment.format != GST_FORMAT_TIME))
334 if (G_UNLIKELY (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)))
337 start = GST_BUFFER_TIMESTAMP (buffer);
338 stop = start + GST_BUFFER_DURATION (buffer);
340 if (!(ret = gst_segment_clip (&self->segment, GST_FORMAT_TIME,
341 start, stop, &cstart, &cstop)))
344 GST_BUFFER_TIMESTAMP (buffer) = cstart;
345 if (GST_CLOCK_TIME_IS_VALID (cstop))
346 GST_BUFFER_DURATION (buffer) = cstop - cstart;
350 GST_DEBUG_OBJECT (self,
351 "Clipped buffer to the current segment: %" GST_TIME_FORMAT " -- %"
352 GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
353 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
355 GST_DEBUG_OBJECT (self, "Buffer outside the current segment -- dropping");
361 gst_deinterlace_base_init (gpointer klass)
363 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
365 gst_element_class_add_pad_template (element_class,
366 gst_static_pad_template_get (&src_templ));
367 gst_element_class_add_pad_template (element_class,
368 gst_static_pad_template_get (&sink_templ));
370 gst_element_class_set_details_simple (element_class,
373 "Deinterlace Methods ported from DScaler/TvTime",
374 "Martin Eikermann <meiker@upb.de>, "
375 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
379 gst_deinterlace_class_init (GstDeinterlaceClass * klass)
381 GObjectClass *gobject_class = (GObjectClass *) klass;
383 GstElementClass *element_class = (GstElementClass *) klass;
385 gobject_class->set_property = gst_deinterlace_set_property;
386 gobject_class->get_property = gst_deinterlace_get_property;
387 gobject_class->finalize = gst_deinterlace_finalize;
390 * GstDeinterlace:mode
392 * This selects whether the deinterlacing methods should
393 * always be applied or if they should only be applied
394 * on content that has the "interlaced" flag on the caps.
397 g_object_class_install_property (gobject_class, PROP_MODE,
398 g_param_spec_enum ("mode",
401 GST_TYPE_DEINTERLACE_MODES,
402 DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
406 * GstDeinterlace:method
408 * Selects the different deinterlacing algorithms that can be used.
409 * These provide different quality and CPU usage.
411 * Some methods provide parameters which can be set by getting
412 * the "method" child via the #GstChildProxy interface and
413 * setting the appropiate properties on it.
419 * Motion Adaptive: Motion Search
425 * Motion Adaptive: Advanced Detection
431 * Motion Adaptive: Simple Detection
443 * Linear interpolation
449 * Linear interpolation in time domain. Any motion causes significant
450 * ghosting, so this method should not be used.
462 * Weave. Bad quality, do not use.
468 * Progressive: Top Field First. Bad quality, do not use.
474 * Progressive: Bottom Field First. Bad quality, do not use.
479 g_object_class_install_property (gobject_class, PROP_METHOD,
480 g_param_spec_enum ("method",
482 "Deinterlace Method",
483 GST_TYPE_DEINTERLACE_METHODS,
484 DEFAULT_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
488 * GstDeinterlace:fields
490 * This selects which fields should be output. If "all" is selected
491 * the output framerate will be double.
494 g_object_class_install_property (gobject_class, PROP_FIELDS,
495 g_param_spec_enum ("fields",
497 "Fields to use for deinterlacing",
498 GST_TYPE_DEINTERLACE_FIELDS,
499 DEFAULT_FIELDS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
503 * GstDeinterlace:layout
505 * This selects which fields is the first in time.
508 g_object_class_install_property (gobject_class, PROP_FIELD_LAYOUT,
509 g_param_spec_enum ("tff",
511 "Deinterlace top field first",
512 GST_TYPE_DEINTERLACE_FIELD_LAYOUT,
513 DEFAULT_FIELD_LAYOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
516 element_class->change_state =
517 GST_DEBUG_FUNCPTR (gst_deinterlace_change_state);
521 gst_deinterlace_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
524 GstDeinterlace *self = GST_DEINTERLACE (child_proxy);
526 g_return_val_if_fail (index == 0, NULL);
528 return gst_object_ref (self->method);
532 gst_deinterlace_child_proxy_get_children_count (GstChildProxy * child_proxy)
534 GstDeinterlace *self = GST_DEINTERLACE (child_proxy);
536 return ((self->method) ? 1 : 0);
540 gst_deinterlace_child_proxy_interface_init (gpointer g_iface,
543 GstChildProxyInterface *iface = g_iface;
545 iface->get_child_by_index = gst_deinterlace_child_proxy_get_child_by_index;
546 iface->get_children_count = gst_deinterlace_child_proxy_get_children_count;
550 gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass)
552 self->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
553 gst_pad_set_chain_function (self->sinkpad,
554 GST_DEBUG_FUNCPTR (gst_deinterlace_chain));
555 gst_pad_set_event_function (self->sinkpad,
556 GST_DEBUG_FUNCPTR (gst_deinterlace_sink_event));
557 gst_pad_set_setcaps_function (self->sinkpad,
558 GST_DEBUG_FUNCPTR (gst_deinterlace_setcaps));
559 gst_pad_set_getcaps_function (self->sinkpad,
560 GST_DEBUG_FUNCPTR (gst_deinterlace_getcaps));
561 gst_pad_set_query_function (self->sinkpad,
562 GST_DEBUG_FUNCPTR (gst_deinterlace_sink_query));
563 gst_pad_set_bufferalloc_function (self->sinkpad,
564 GST_DEBUG_FUNCPTR (gst_deinterlace_alloc_buffer));
565 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
567 self->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
568 gst_pad_set_event_function (self->srcpad,
569 GST_DEBUG_FUNCPTR (gst_deinterlace_src_event));
570 gst_pad_set_query_type_function (self->srcpad,
571 GST_DEBUG_FUNCPTR (gst_deinterlace_src_query_types));
572 gst_pad_set_query_function (self->srcpad,
573 GST_DEBUG_FUNCPTR (gst_deinterlace_src_query));
574 gst_pad_set_getcaps_function (self->srcpad,
575 GST_DEBUG_FUNCPTR (gst_deinterlace_getcaps));
576 gst_pad_set_setcaps_function (self->srcpad,
577 GST_DEBUG_FUNCPTR (gst_deinterlace_setcaps));
578 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
580 self->mode = DEFAULT_MODE;
581 self->user_set_method_id = DEFAULT_METHOD;
582 gst_deinterlace_set_method (self, self->user_set_method_id);
583 self->fields = DEFAULT_FIELDS;
584 self->field_layout = DEFAULT_FIELD_LAYOUT;
586 self->still_frame_mode = FALSE;
588 gst_deinterlace_reset (self);
592 gst_deinterlace_reset_history (GstDeinterlace * self, gboolean drop_all)
597 GST_DEBUG_OBJECT (self, "Resetting history (count %d)",
598 self->history_count);
600 for (i = 0; i < self->history_count; i++) {
601 if (self->field_history[i].buf) {
602 gst_buffer_unref (self->field_history[i].buf);
603 self->field_history[i].buf = NULL;
607 GST_DEBUG_OBJECT (self, "Flushing history (count %d)", self->history_count);
608 while (self->history_count > 0)
609 gst_deinterlace_output_frame (self, TRUE);
611 memset (self->field_history, 0,
612 GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField));
613 self->history_count = 0;
615 if (!self->still_frame_mode && self->last_buffer) {
616 gst_buffer_unref (self->last_buffer);
617 self->last_buffer = NULL;
622 gst_deinterlace_update_passthrough (GstDeinterlace * self)
624 self->passthrough = (self->mode == GST_DEINTERLACE_MODE_DISABLED
625 || (!self->interlaced && self->mode != GST_DEINTERLACE_MODE_INTERLACED));
626 GST_DEBUG_OBJECT (self, "Passthrough: %d", self->passthrough);
630 gst_deinterlace_reset (GstDeinterlace * self)
632 GST_DEBUG_OBJECT (self, "Resetting internal state");
634 self->format = GST_VIDEO_FORMAT_UNKNOWN;
637 self->frame_size = 0;
638 self->fps_n = self->fps_d = 0;
639 self->passthrough = FALSE;
641 self->reconfigure = FALSE;
642 if (self->new_mode != -1)
643 self->mode = self->new_mode;
644 if (self->new_fields != -1)
645 self->fields = self->new_fields;
647 self->new_fields = -1;
649 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
652 gst_caps_unref (self->sink_caps);
653 self->sink_caps = NULL;
656 gst_caps_unref (self->src_caps);
657 self->src_caps = NULL;
659 if (self->request_caps)
660 gst_caps_unref (self->request_caps);
661 self->request_caps = NULL;
663 gst_deinterlace_reset_history (self, TRUE);
665 gst_deinterlace_reset_qos (self);
669 gst_deinterlace_set_property (GObject * object, guint prop_id,
670 const GValue * value, GParamSpec * pspec)
672 GstDeinterlace *self;
674 g_return_if_fail (GST_IS_DEINTERLACE (object));
675 self = GST_DEINTERLACE (object);
681 GST_OBJECT_LOCK (self);
682 new_mode = g_value_get_enum (value);
683 if (self->mode != new_mode && GST_PAD_CAPS (self->srcpad)) {
684 self->reconfigure = TRUE;
685 self->new_mode = new_mode;
687 self->mode = new_mode;
688 gst_deinterlace_update_passthrough (self);
690 GST_OBJECT_UNLOCK (self);
694 self->user_set_method_id = g_value_get_enum (value);
695 gst_deinterlace_set_method (self, self->user_set_method_id);
700 GST_OBJECT_LOCK (self);
701 new_fields = g_value_get_enum (value);
702 if (self->fields != new_fields && GST_PAD_CAPS (self->srcpad)) {
703 self->reconfigure = TRUE;
704 self->new_fields = new_fields;
706 self->fields = new_fields;
708 GST_OBJECT_UNLOCK (self);
711 case PROP_FIELD_LAYOUT:
712 self->field_layout = g_value_get_enum (value);
715 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
721 gst_deinterlace_get_property (GObject * object, guint prop_id,
722 GValue * value, GParamSpec * pspec)
724 GstDeinterlace *self;
726 g_return_if_fail (GST_IS_DEINTERLACE (object));
727 self = GST_DEINTERLACE (object);
731 g_value_set_enum (value, self->mode);
734 g_value_set_enum (value, self->user_set_method_id);
737 g_value_set_enum (value, self->fields);
739 case PROP_FIELD_LAYOUT:
740 g_value_set_enum (value, self->field_layout);
743 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
748 gst_deinterlace_finalize (GObject * object)
750 GstDeinterlace *self = GST_DEINTERLACE (object);
752 gst_deinterlace_reset (self);
755 gst_object_unparent (GST_OBJECT (self->method));
759 G_OBJECT_CLASS (parent_class)->finalize (object);
763 gst_deinterlace_pop_history (GstDeinterlace * self)
767 g_return_val_if_fail (self->history_count > 0, NULL);
769 GST_DEBUG_OBJECT (self, "Pop last history buffer -- current history size %d",
770 self->history_count);
772 buffer = self->field_history[self->history_count - 1].buf;
774 self->history_count--;
776 GST_DEBUG_OBJECT (self, "Returning buffer: %" GST_TIME_FORMAT
777 " with duration %" GST_TIME_FORMAT " and size %u",
778 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
779 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer));
785 gst_deinterlace_push_history (GstDeinterlace * self, GstBuffer * buffer)
788 GstClockTime timestamp;
789 GstDeinterlaceFieldLayout field_layout = self->field_layout;
790 gboolean repeated = GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_RFF);
791 gboolean tff = GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_TFF);
793 GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_ONEFIELD);
794 GstBuffer *field1, *field2;
795 guint fields_to_push = (onefield) ? 1 : (!repeated) ? 2 : 3;
796 gint field1_flags, field2_flags;
798 g_return_if_fail (self->history_count <
799 GST_DEINTERLACE_MAX_FIELD_HISTORY - fields_to_push);
801 GST_DEBUG_OBJECT (self, "Pushing new buffer to the history: %" GST_TIME_FORMAT
802 " with duration %" GST_TIME_FORMAT " and size %u",
803 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
804 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), GST_BUFFER_SIZE (buffer));
806 for (i = GST_DEINTERLACE_MAX_FIELD_HISTORY - 1; i >= fields_to_push; i--) {
807 self->field_history[i].buf = self->field_history[i - fields_to_push].buf;
808 self->field_history[i].flags =
809 self->field_history[i - fields_to_push].flags;
812 if (field_layout == GST_DEINTERLACE_LAYOUT_AUTO) {
813 if (!self->interlaced) {
814 GST_WARNING_OBJECT (self, "Can't detect field layout -- assuming TFF");
815 field_layout = GST_DEINTERLACE_LAYOUT_TFF;
817 field_layout = GST_DEINTERLACE_LAYOUT_TFF;
819 field_layout = GST_DEINTERLACE_LAYOUT_BFF;
823 if (field_layout == GST_DEINTERLACE_LAYOUT_TFF) {
824 GST_DEBUG_OBJECT (self, "Top field first");
825 field1 = gst_buffer_make_metadata_writable (gst_buffer_ref (buffer));
826 field1_flags = PICTURE_INTERLACED_TOP;
827 field2 = gst_buffer_make_metadata_writable (gst_buffer_ref (buffer));
828 field2_flags = PICTURE_INTERLACED_BOTTOM;
830 GST_DEBUG_OBJECT (self, "Bottom field first");
831 field1 = gst_buffer_make_metadata_writable (gst_buffer_ref (buffer));
832 field1_flags = PICTURE_INTERLACED_BOTTOM;
833 field2 = gst_buffer_make_metadata_writable (gst_buffer_ref (buffer));
834 field2_flags = PICTURE_INTERLACED_TOP;
837 /* Timestamps are assigned to the field buffers under the assumption that
838 the timestamp of the buffer equals the first fields timestamp */
840 timestamp = GST_BUFFER_TIMESTAMP (buffer);
841 GST_BUFFER_TIMESTAMP (field1) = timestamp;
842 GST_BUFFER_TIMESTAMP (field2) = timestamp + self->field_duration;
845 self->field_history[2].buf = field1;
846 self->field_history[2].flags = field1_flags;
848 self->field_history[1].buf = field2;
849 self->field_history[1].flags = field2_flags;
851 self->field_history[0].buf =
852 gst_buffer_make_metadata_writable (gst_buffer_ref (field1));
853 GST_BUFFER_TIMESTAMP (self->field_history[0].buf) +=
854 2 * self->field_duration;
855 self->field_history[0].flags = field1_flags;
856 } else if (!onefield) {
857 self->field_history[1].buf = field1;
858 self->field_history[1].flags = field1_flags;
860 self->field_history[0].buf = field2;
861 self->field_history[0].flags = field2_flags;
862 } else { /* onefield */
863 self->field_history[0].buf = field1;
864 self->field_history[0].flags = field1_flags;
865 gst_buffer_unref (field2);
868 self->history_count += fields_to_push;
870 GST_DEBUG_OBJECT (self, "Pushed buffer -- current history size %d",
871 self->history_count);
873 if (self->last_buffer)
874 gst_buffer_unref (self->last_buffer);
875 self->last_buffer = buffer;
879 gst_deinterlace_update_qos (GstDeinterlace * self, gdouble proportion,
880 GstClockTimeDiff diff, GstClockTime timestamp)
882 GST_DEBUG_OBJECT (self,
883 "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %"
884 GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "",
885 GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp));
887 GST_OBJECT_LOCK (self);
888 self->proportion = proportion;
889 if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
890 if (G_UNLIKELY (diff > 0))
891 self->earliest_time =
892 timestamp + 2 * diff + ((self->fields ==
893 GST_DEINTERLACE_ALL) ? self->field_duration : 2 *
894 self->field_duration);
896 self->earliest_time = timestamp + diff;
898 self->earliest_time = GST_CLOCK_TIME_NONE;
900 GST_OBJECT_UNLOCK (self);
904 gst_deinterlace_reset_qos (GstDeinterlace * self)
906 gst_deinterlace_update_qos (self, 0.5, 0, GST_CLOCK_TIME_NONE);
910 gst_deinterlace_read_qos (GstDeinterlace * self, gdouble * proportion,
913 GST_OBJECT_LOCK (self);
914 *proportion = self->proportion;
915 *time = self->earliest_time;
916 GST_OBJECT_UNLOCK (self);
919 /* Perform qos calculations before processing the next frame. Returns TRUE if
920 * the frame should be processed, FALSE if the frame can be dropped entirely */
922 gst_deinterlace_do_qos (GstDeinterlace * self, GstClockTime timestamp)
924 GstClockTime qostime, earliest_time;
927 /* no timestamp, can't do QoS => process frame */
928 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
929 GST_LOG_OBJECT (self, "invalid timestamp, can't do QoS, process frame");
933 /* get latest QoS observation values */
934 gst_deinterlace_read_qos (self, &proportion, &earliest_time);
936 /* skip qos if we have no observation (yet) => process frame */
937 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
938 GST_LOG_OBJECT (self, "no observation yet, process frame");
942 /* qos is done on running time */
943 qostime = gst_segment_to_running_time (&self->segment, GST_FORMAT_TIME,
946 /* see how our next timestamp relates to the latest qos timestamp */
947 GST_LOG_OBJECT (self, "qostime %" GST_TIME_FORMAT ", earliest %"
948 GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
950 if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) {
951 GST_DEBUG_OBJECT (self, "we are late, drop frame");
955 GST_LOG_OBJECT (self, "process frame");
960 gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing)
962 GstClockTime timestamp;
963 GstFlowReturn ret = GST_FLOW_OK;
964 gint fields_required = 0;
965 gint cur_field_idx = 0;
966 GstBuffer *buf, *outbuf;
968 gst_deinterlace_set_method (self, self->user_set_method_id);
969 fields_required = gst_deinterlace_method_get_fields_required (self->method);
971 if (self->history_count < fields_required) {
973 /* FIXME: if there are any methods implemented that output different
974 * dimensions (e.g. half height) that require more than one field of
975 * history, it is desirable to degrade to something that outputs
976 * half-height also */
977 gst_deinterlace_set_method (self,
978 self->history_count >= 2 ?
979 GST_DEINTERLACE_VFIR : GST_DEINTERLACE_LINEAR);
981 gst_deinterlace_method_get_fields_required (self->method);
982 GST_DEBUG_OBJECT (self, "Flushing field(s) using %s method",
983 methods_types[self->method_id].value_nick);
985 /* Not enough fields in the history */
986 GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
987 self->history_count, fields_required);
992 while (self->history_count >= fields_required) {
993 if (self->fields == GST_DEINTERLACE_ALL)
994 GST_DEBUG_OBJECT (self, "All fields");
995 else if (self->fields == GST_DEINTERLACE_TF)
996 GST_DEBUG_OBJECT (self, "Top fields");
997 else if (self->fields == GST_DEINTERLACE_BF)
998 GST_DEBUG_OBJECT (self, "Bottom fields");
1000 cur_field_idx = self->history_count - fields_required;
1002 if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_TOP
1003 && self->fields == GST_DEINTERLACE_TF) ||
1004 self->fields == GST_DEINTERLACE_ALL) {
1005 GST_DEBUG_OBJECT (self, "deinterlacing top field");
1007 /* create new buffer */
1008 ret = gst_pad_alloc_buffer (self->srcpad,
1009 GST_BUFFER_OFFSET_NONE, self->frame_size, self->src_caps, &outbuf);
1010 if (ret != GST_FLOW_OK)
1013 if (self->src_caps != GST_BUFFER_CAPS (outbuf) &&
1014 !gst_caps_is_equal (self->src_caps, GST_BUFFER_CAPS (outbuf))) {
1015 gst_caps_replace (&self->request_caps, GST_BUFFER_CAPS (outbuf));
1016 GST_DEBUG_OBJECT (self, "Upstream wants new caps %" GST_PTR_FORMAT,
1017 self->request_caps);
1019 gst_buffer_unref (outbuf);
1020 outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
1023 return GST_FLOW_ERROR;
1025 gst_buffer_set_caps (outbuf, self->src_caps);
1028 g_return_val_if_fail (self->history_count - 1 -
1029 gst_deinterlace_method_get_latency (self->method) >= 0,
1033 self->field_history[self->history_count - 1 -
1034 gst_deinterlace_method_get_latency (self->method)].buf;
1035 timestamp = GST_BUFFER_TIMESTAMP (buf);
1037 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
1038 if (self->fields == GST_DEINTERLACE_ALL)
1039 GST_BUFFER_DURATION (outbuf) = self->field_duration;
1041 GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
1043 /* Check if we need to drop the frame because of QoS */
1044 if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
1045 gst_buffer_unref (gst_deinterlace_pop_history (self));
1046 gst_buffer_unref (outbuf);
1050 /* do magic calculus */
1051 gst_deinterlace_method_deinterlace_frame (self->method,
1052 self->field_history, self->history_count, outbuf);
1054 gst_buffer_unref (gst_deinterlace_pop_history (self));
1056 if (gst_deinterlace_clip_buffer (self, outbuf)) {
1057 ret = gst_pad_push (self->srcpad, outbuf);
1060 gst_buffer_unref (outbuf);
1064 if (ret != GST_FLOW_OK)
1068 /* no calculation done: remove excess field */
1069 else if (self->field_history[cur_field_idx].flags ==
1070 PICTURE_INTERLACED_TOP && self->fields == GST_DEINTERLACE_BF) {
1071 GST_DEBUG_OBJECT (self, "Removing unused top field");
1072 gst_buffer_unref (gst_deinterlace_pop_history (self));
1075 cur_field_idx = self->history_count - fields_required;
1076 if (self->history_count < fields_required)
1079 /* deinterlace bottom_field */
1080 if ((self->field_history[cur_field_idx].flags == PICTURE_INTERLACED_BOTTOM
1081 && self->fields == GST_DEINTERLACE_BF) ||
1082 self->fields == GST_DEINTERLACE_ALL) {
1083 GST_DEBUG_OBJECT (self, "deinterlacing bottom field");
1085 /* create new buffer */
1086 ret = gst_pad_alloc_buffer (self->srcpad,
1087 GST_BUFFER_OFFSET_NONE, self->frame_size, self->src_caps, &outbuf);
1088 if (ret != GST_FLOW_OK)
1091 if (self->src_caps != GST_BUFFER_CAPS (outbuf) &&
1092 !gst_caps_is_equal (self->src_caps, GST_BUFFER_CAPS (outbuf))) {
1093 gst_caps_replace (&self->request_caps, GST_BUFFER_CAPS (outbuf));
1094 GST_DEBUG_OBJECT (self, "Upstream wants new caps %" GST_PTR_FORMAT,
1095 self->request_caps);
1097 gst_buffer_unref (outbuf);
1098 outbuf = gst_buffer_try_new_and_alloc (self->frame_size);
1101 return GST_FLOW_ERROR;
1103 gst_buffer_set_caps (outbuf, self->src_caps);
1106 g_return_val_if_fail (self->history_count - 1 -
1107 gst_deinterlace_method_get_latency (self->method) >= 0,
1111 self->field_history[self->history_count - 1 -
1112 gst_deinterlace_method_get_latency (self->method)].buf;
1113 timestamp = GST_BUFFER_TIMESTAMP (buf);
1115 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
1116 if (self->fields == GST_DEINTERLACE_ALL)
1117 GST_BUFFER_DURATION (outbuf) = self->field_duration;
1119 GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
1121 /* Check if we need to drop the frame because of QoS */
1122 if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
1123 gst_buffer_unref (gst_deinterlace_pop_history (self));
1124 gst_buffer_unref (outbuf);
1128 /* do magic calculus */
1129 gst_deinterlace_method_deinterlace_frame (self->method,
1130 self->field_history, self->history_count, outbuf);
1132 gst_buffer_unref (gst_deinterlace_pop_history (self));
1134 if (gst_deinterlace_clip_buffer (self, outbuf)) {
1135 ret = gst_pad_push (self->srcpad, outbuf);
1138 gst_buffer_unref (outbuf);
1142 if (ret != GST_FLOW_OK)
1146 /* no calculation done: remove excess field */
1147 else if (self->field_history[cur_field_idx].flags ==
1148 PICTURE_INTERLACED_BOTTOM && self->fields == GST_DEINTERLACE_TF) {
1149 GST_DEBUG_OBJECT (self, "Removing unused bottom field");
1150 gst_buffer_unref (gst_deinterlace_pop_history (self));
1157 static GstFlowReturn
1158 gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
1160 GstDeinterlace *self = GST_DEINTERLACE (GST_PAD_PARENT (pad));
1162 GST_OBJECT_LOCK (self);
1163 if (self->reconfigure) {
1164 if (self->new_fields != -1)
1165 self->fields = self->new_fields;
1166 if (self->new_mode != -1)
1167 self->mode = self->new_mode;
1168 self->new_mode = self->new_fields = -1;
1170 self->reconfigure = FALSE;
1171 GST_OBJECT_UNLOCK (self);
1172 if (GST_PAD_CAPS (self->srcpad))
1173 gst_deinterlace_setcaps (self->sinkpad, GST_PAD_CAPS (self->sinkpad));
1175 GST_OBJECT_UNLOCK (self);
1178 if (self->still_frame_mode || self->passthrough)
1179 return gst_pad_push (self->srcpad, buf);
1181 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
1182 GST_DEBUG_OBJECT (self, "DISCONT buffer, resetting history");
1183 gst_deinterlace_reset_history (self, FALSE);
1186 gst_deinterlace_push_history (self, buf);
1189 return gst_deinterlace_output_frame (self, FALSE);
1193 gst_greatest_common_divisor (gint a, gint b)
1206 gst_fraction_double (gint * n_out, gint * d_out, gboolean half)
1216 if (n == 0 || (n == G_MAXINT && d == 1))
1219 gcd = gst_greatest_common_divisor (n, d);
1224 if (G_MAXINT / 2 >= ABS (n)) {
1226 } else if (d >= 2) {
1232 if (G_MAXINT / 2 >= ABS (d)) {
1234 } else if (n >= 2) {
1248 gst_deinterlace_getcaps (GstPad * pad)
1251 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1254 const GstCaps *ourcaps;
1257 GST_OBJECT_LOCK (self);
1259 otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad;
1261 ourcaps = gst_pad_get_pad_template_caps (pad);
1262 peercaps = gst_pad_peer_get_caps (otherpad);
1265 GST_DEBUG_OBJECT (pad, "Peer has caps %" GST_PTR_FORMAT, peercaps);
1266 ret = gst_caps_intersect (ourcaps, peercaps);
1267 gst_caps_unref (peercaps);
1269 ret = gst_caps_copy (ourcaps);
1272 GST_OBJECT_UNLOCK (self);
1274 for (len = gst_caps_get_size (ret); len > 0; len--) {
1275 GstStructure *s = gst_caps_get_structure (ret, len - 1);
1277 if (pad == self->sinkpad || self->passthrough)
1278 gst_structure_remove_field (s, "interlaced");
1280 gst_structure_set (s, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
1282 if (!self->passthrough && self->fields == GST_DEINTERLACE_ALL) {
1285 val = gst_structure_get_value (s, "framerate");
1289 if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION) {
1292 n = gst_value_get_fraction_numerator (val);
1293 d = gst_value_get_fraction_denominator (val);
1295 if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1299 gst_structure_set (s, "framerate", GST_TYPE_FRACTION, n, d, NULL);
1300 } else if (G_VALUE_TYPE (val) == GST_TYPE_FRACTION_RANGE) {
1301 const GValue *min, *max;
1302 GValue nrange = { 0, }, nmin = {
1307 g_value_init (&nrange, GST_TYPE_FRACTION_RANGE);
1308 g_value_init (&nmin, GST_TYPE_FRACTION);
1309 g_value_init (&nmax, GST_TYPE_FRACTION);
1311 min = gst_value_get_fraction_range_min (val);
1312 max = gst_value_get_fraction_range_max (val);
1314 n = gst_value_get_fraction_numerator (min);
1315 d = gst_value_get_fraction_denominator (min);
1317 if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1318 g_value_unset (&nrange);
1319 g_value_unset (&nmax);
1320 g_value_unset (&nmin);
1324 gst_value_set_fraction (&nmin, n, d);
1326 n = gst_value_get_fraction_numerator (max);
1327 d = gst_value_get_fraction_denominator (max);
1329 if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1330 g_value_unset (&nrange);
1331 g_value_unset (&nmax);
1332 g_value_unset (&nmin);
1336 gst_value_set_fraction (&nmax, n, d);
1337 gst_value_set_fraction_range (&nrange, &nmin, &nmax);
1339 gst_structure_set_value (s, "framerate", &nrange);
1341 g_value_unset (&nmin);
1342 g_value_unset (&nmax);
1343 g_value_unset (&nrange);
1344 } else if (G_VALUE_TYPE (val) == GST_TYPE_LIST) {
1346 GValue nlist = { 0, };
1347 GValue nval = { 0, };
1350 g_value_init (&nlist, GST_TYPE_LIST);
1351 for (i = gst_value_list_get_size (val); i > 0; i--) {
1354 lval = gst_value_list_get_value (val, i);
1356 if (G_VALUE_TYPE (lval) != GST_TYPE_FRACTION)
1359 n = gst_value_get_fraction_numerator (lval);
1360 d = gst_value_get_fraction_denominator (lval);
1362 /* Double/Half the framerate but if this fails simply
1363 * skip this value from the list */
1364 if (!gst_fraction_double (&n, &d, pad != self->srcpad)) {
1368 g_value_init (&nval, GST_TYPE_FRACTION);
1370 gst_value_set_fraction (&nval, n, d);
1371 gst_value_list_append_value (&nlist, &nval);
1372 g_value_unset (&nval);
1374 gst_structure_set_value (s, "framerate", &nlist);
1375 g_value_unset (&nlist);
1380 GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret);
1382 gst_object_unref (self);
1387 GST_ERROR_OBJECT (pad, "Unable to transform peer caps");
1388 gst_caps_unref (ret);
1393 gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps)
1395 gboolean res = TRUE;
1396 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1400 otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad;
1403 gst_video_format_parse_caps (caps, &self->format, &self->width,
1405 res &= gst_video_parse_caps_framerate (caps, &self->fps_n, &self->fps_d);
1406 if (pad == self->sinkpad)
1407 res &= gst_video_format_parse_caps_interlaced (caps, &self->interlaced);
1411 gst_deinterlace_update_passthrough (self);
1413 if (!self->passthrough && self->fields == GST_DEINTERLACE_ALL) {
1414 gint fps_n = self->fps_n, fps_d = self->fps_d;
1416 if (!gst_fraction_double (&fps_n, &fps_d, otherpad != self->srcpad))
1419 othercaps = gst_caps_copy (caps);
1421 gst_caps_set_simple (othercaps, "framerate", GST_TYPE_FRACTION, fps_n,
1424 othercaps = gst_caps_ref (caps);
1427 if (otherpad == self->srcpad && self->mode != GST_DEINTERLACE_MODE_DISABLED) {
1428 othercaps = gst_caps_make_writable (othercaps);
1429 gst_caps_set_simple (othercaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
1432 gst_deinterlace_reset_history (self, FALSE);
1434 if (!gst_pad_set_caps (otherpad, othercaps))
1435 goto caps_not_accepted;
1438 gst_video_format_get_size (self->format, self->width, self->height);
1440 if (self->fields == GST_DEINTERLACE_ALL && otherpad == self->srcpad)
1441 self->field_duration =
1442 gst_util_uint64_scale (GST_SECOND, self->fps_d, self->fps_n);
1444 self->field_duration =
1445 gst_util_uint64_scale (GST_SECOND, self->fps_d, 2 * self->fps_n);
1447 if (pad == self->sinkpad) {
1448 gst_caps_replace (&self->sink_caps, caps);
1449 gst_caps_replace (&self->src_caps, othercaps);
1451 gst_caps_replace (&self->src_caps, caps);
1452 gst_caps_replace (&self->sink_caps, othercaps);
1455 gst_deinterlace_set_method (self, self->method_id);
1456 gst_deinterlace_method_setup (self->method, self->format, self->width,
1459 GST_DEBUG_OBJECT (pad, "Set caps: %" GST_PTR_FORMAT, caps);
1460 GST_DEBUG_OBJECT (pad, "Other caps: %" GST_PTR_FORMAT, othercaps);
1462 gst_caps_unref (othercaps);
1466 gst_object_unref (self);
1471 GST_ERROR_OBJECT (pad, "Invalid caps: %" GST_PTR_FORMAT, caps);
1476 GST_ERROR_OBJECT (pad, "Caps not accepted: %" GST_PTR_FORMAT, othercaps);
1477 gst_caps_unref (othercaps);
1482 gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
1484 gboolean res = TRUE;
1485 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1487 GST_LOG_OBJECT (pad, "received %s event: %" GST_PTR_FORMAT,
1488 GST_EVENT_TYPE_NAME (event), event);
1490 switch (GST_EVENT_TYPE (event)) {
1491 case GST_EVENT_NEWSEGMENT:
1495 gint64 start, end, base;
1496 gdouble rate, applied_rate;
1498 gst_event_parse_new_segment_full (event, &is_update, &rate, &applied_rate,
1499 &fmt, &start, &end, &base);
1501 gst_deinterlace_reset_qos (self);
1502 gst_deinterlace_reset_history (self, FALSE);
1504 if (fmt == GST_FORMAT_TIME) {
1505 GST_DEBUG_OBJECT (pad,
1506 "Got NEWSEGMENT event in GST_FORMAT_TIME, passing on (%"
1507 GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", GST_TIME_ARGS (start),
1508 GST_TIME_ARGS (end));
1509 gst_segment_set_newsegment_full (&self->segment, is_update, rate,
1510 applied_rate, fmt, start, end, base);
1512 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1515 res = gst_pad_push_event (self->srcpad, event);
1518 case GST_EVENT_CUSTOM_DOWNSTREAM:{
1519 gboolean still_state;
1521 if (gst_video_event_parse_still_frame (event, &still_state)) {
1522 GST_DEBUG_OBJECT (self, "Received still frame event, state %d",
1528 GST_DEBUG_OBJECT (self, "Handling still frame");
1529 self->still_frame_mode = TRUE;
1530 gst_deinterlace_reset_history (self, FALSE);
1531 if (self->last_buffer) {
1533 gst_pad_push (self->srcpad, gst_buffer_ref (self->last_buffer));
1534 GST_DEBUG_OBJECT (self, "Pushed still frame, result: %s",
1535 gst_flow_get_name (ret));
1537 GST_WARNING_OBJECT (self, "No pending buffer!");
1540 GST_DEBUG_OBJECT (self, "Ending still frames");
1541 self->still_frame_mode = FALSE;
1547 gst_deinterlace_reset_history (self, FALSE);
1551 res = gst_pad_push_event (self->srcpad, event);
1554 case GST_EVENT_FLUSH_STOP:
1555 if (self->still_frame_mode) {
1556 GST_DEBUG_OBJECT (self, "Ending still frames");
1557 self->still_frame_mode = FALSE;
1559 gst_deinterlace_reset_qos (self);
1560 res = gst_pad_push_event (self->srcpad, event);
1561 gst_deinterlace_reset_history (self, TRUE);
1565 gst_object_unref (self);
1570 gst_deinterlace_sink_query (GstPad * pad, GstQuery * query)
1572 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1573 gboolean res = FALSE;
1575 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
1577 switch (GST_QUERY_TYPE (query)) {
1579 GstPad *peer = gst_pad_get_peer (self->srcpad);
1582 res = gst_pad_query (peer, query);
1583 gst_object_unref (peer);
1591 gst_object_unref (self);
1595 static GstStateChangeReturn
1596 gst_deinterlace_change_state (GstElement * element, GstStateChange transition)
1598 GstStateChangeReturn ret;
1599 GstDeinterlace *self = GST_DEINTERLACE (element);
1601 switch (transition) {
1602 case GST_STATE_CHANGE_NULL_TO_READY:
1604 case GST_STATE_CHANGE_READY_TO_PAUSED:
1606 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1612 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1613 if (ret != GST_STATE_CHANGE_SUCCESS)
1616 switch (transition) {
1617 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1619 case GST_STATE_CHANGE_PAUSED_TO_READY:
1620 gst_deinterlace_reset (self);
1622 case GST_STATE_CHANGE_READY_TO_NULL:
1631 gst_deinterlace_src_event (GstPad * pad, GstEvent * event)
1633 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1636 GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
1638 switch (GST_EVENT_TYPE (event)) {
1639 case GST_EVENT_QOS:{
1640 GstClockTimeDiff diff;
1641 GstClockTime timestamp;
1644 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
1646 gst_deinterlace_update_qos (self, proportion, diff, timestamp);
1650 res = gst_pad_push_event (self->sinkpad, event);
1654 gst_object_unref (self);
1660 gst_deinterlace_src_query (GstPad * pad, GstQuery * query)
1662 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1663 gboolean res = FALSE;
1665 GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
1667 switch (GST_QUERY_TYPE (query)) {
1668 case GST_QUERY_LATENCY:
1669 if (!self->passthrough) {
1670 GstClockTime min, max;
1674 if ((peer = gst_pad_get_peer (self->sinkpad))) {
1675 if ((res = gst_pad_query (peer, query))) {
1676 GstClockTime latency;
1677 gint fields_required = 0;
1678 gint method_latency = 0;
1682 gst_deinterlace_method_get_fields_required (self->method);
1684 gst_deinterlace_method_get_latency (self->method);
1687 gst_query_parse_latency (query, &live, &min, &max);
1689 GST_DEBUG_OBJECT (self, "Peer latency: min %"
1690 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1691 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1693 /* add our own latency */
1694 latency = (fields_required + method_latency) * self->field_duration;
1696 GST_DEBUG_OBJECT (self, "Our latency: min %" GST_TIME_FORMAT
1697 ", max %" GST_TIME_FORMAT,
1698 GST_TIME_ARGS (latency), GST_TIME_ARGS (latency));
1701 if (max != GST_CLOCK_TIME_NONE)
1704 GST_DEBUG_OBJECT (self, "Calculated total latency : min %"
1705 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1706 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1708 gst_query_set_latency (query, live, min, max);
1710 gst_object_unref (peer);
1717 GstPad *peer = gst_pad_get_peer (self->sinkpad);
1720 res = gst_pad_query (peer, query);
1721 gst_object_unref (peer);
1729 gst_object_unref (self);
1733 static const GstQueryType *
1734 gst_deinterlace_src_query_types (GstPad * pad)
1736 static const GstQueryType types[] = {
1743 static GstFlowReturn
1744 gst_deinterlace_alloc_buffer (GstPad * pad, guint64 offset, guint size,
1745 GstCaps * caps, GstBuffer ** buf)
1747 GstDeinterlace *self = GST_DEINTERLACE (gst_pad_get_parent (pad));
1748 GstFlowReturn ret = GST_FLOW_OK;
1752 GST_DEBUG_OBJECT (pad, "alloc with caps %" GST_PTR_FORMAT ", size %u", caps,
1755 if (self->still_frame_mode || self->passthrough) {
1756 ret = gst_pad_alloc_buffer (self->srcpad, offset, size, caps, buf);
1757 } else if (G_LIKELY (!self->request_caps)) {
1758 *buf = gst_buffer_try_new_and_alloc (size);
1759 if (G_UNLIKELY (!*buf)) {
1760 ret = GST_FLOW_ERROR;
1762 gst_buffer_set_caps (*buf, caps);
1763 GST_BUFFER_OFFSET (*buf) = offset;
1768 guint new_frame_size;
1769 GstCaps *new_caps = gst_caps_copy (self->request_caps);
1771 if (self->fields == GST_DEINTERLACE_ALL) {
1773 GstStructure *s = gst_caps_get_structure (new_caps, 0);
1775 gst_structure_get_fraction (s, "framerate", &n, &d);
1777 if (!gst_fraction_double (&n, &d, TRUE)) {
1778 gst_object_unref (self);
1779 gst_caps_unref (new_caps);
1783 gst_structure_set (s, "framerate", GST_TYPE_FRACTION, n, d, NULL);
1786 if (G_UNLIKELY (!gst_video_format_parse_caps (new_caps, &fmt, &width,
1788 gst_object_unref (self);
1789 gst_caps_unref (new_caps);
1793 new_frame_size = gst_video_format_get_size (fmt, width, height);
1795 *buf = gst_buffer_try_new_and_alloc (new_frame_size);
1796 if (G_UNLIKELY (!*buf)) {
1797 ret = GST_FLOW_ERROR;
1799 gst_buffer_set_caps (*buf, new_caps);
1800 gst_caps_unref (self->request_caps);
1801 self->request_caps = NULL;
1802 gst_caps_unref (new_caps);
1806 gst_object_unref (self);
1812 plugin_init (GstPlugin * plugin)
1814 GST_DEBUG_CATEGORY_INIT (deinterlace_debug, "deinterlace", 0, "Deinterlacer");
1820 if (!gst_element_register (plugin, "deinterlace", GST_RANK_NONE,
1821 GST_TYPE_DEINTERLACE)) {
1828 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1831 "Deinterlacer", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1832 GST_PACKAGE_ORIGIN);