1 /* GStreamer
2 * Copyright (C) 2013 David Schleef <ds@schleef.org>
3 * Copyright (C) 2013 Rdio, Inc. <ingestions@rd.io>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
18 * Boston, MA 02110-1335, USA.
19 */
20 /**
21 * SECTION:element-yadif
22 * @title: yadif
23 *
24 * The yadif element deinterlaces video, using the YADIF deinterlacing
25 * filter copied from Libav. This element only handles the simple case
26 * of interlace-mode=interleaved video instead of the more complex
27 * inverse telecine and deinterlace cases that are handled by the
28 * deinterlace element.
29 *
30 * ## Example launch line
31 * |[
32 * gst-launch-1.0 -v videotestsrc pattern=ball ! interlace ! yadif ! xvimagesink
33 * ]|
34 * This pipeline creates an interlaced test pattern, and then deinterlaces
35 * it using the yadif filter.
36 *
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include <gst/gst.h>
44 #include <gst/base/gstbasetransform.h>
45 #include <gst/video/video.h>
46 #include "gstyadif.h"
47
48 GST_DEBUG_CATEGORY_STATIC (gst_yadif_debug_category);
49 #define GST_CAT_DEFAULT gst_yadif_debug_category
50
51 /* prototypes */
52
53
54 static void gst_yadif_set_property (GObject * object,
55 guint property_id, const GValue * value, GParamSpec * pspec);
56 static void gst_yadif_get_property (GObject * object,
57 guint property_id, GValue * value, GParamSpec * pspec);
58 static void gst_yadif_dispose (GObject * object);
59 static void gst_yadif_finalize (GObject * object);
60
61 static GstCaps *gst_yadif_transform_caps (GstBaseTransform * trans,
62 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
63 static gboolean gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps,
64 GstCaps * outcaps);
65 static gboolean gst_yadif_get_unit_size (GstBaseTransform * trans,
66 GstCaps * caps, gsize * size);
67 static gboolean gst_yadif_start (GstBaseTransform * trans);
68 static gboolean gst_yadif_stop (GstBaseTransform * trans);
69 static GstFlowReturn gst_yadif_transform (GstBaseTransform * trans,
70 GstBuffer * inbuf, GstBuffer * outbuf);
71
72 enum
73 {
74 PROP_0,
75 PROP_MODE
76 };
77
78 #define DEFAULT_MODE GST_DEINTERLACE_MODE_AUTO
79
80 /* pad templates */
81
82 static GstStaticPadTemplate gst_yadif_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
84 GST_PAD_SINK,
85 GST_PAD_ALWAYS,
86 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{Y42B,I420,Y444}")
87 ",interlace-mode=(string){interleaved,mixed,progressive}")
88 );
89
90 static GstStaticPadTemplate gst_yadif_src_template =
91 GST_STATIC_PAD_TEMPLATE ("src",
92 GST_PAD_SRC,
93 GST_PAD_ALWAYS,
94 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{Y42B,I420,Y444}")
95 ",interlace-mode=(string)progressive")
96 );
97
98 #define GST_TYPE_DEINTERLACE_MODES (gst_deinterlace_modes_get_type ())
99 static GType
gst_deinterlace_modes_get_type(void)100 gst_deinterlace_modes_get_type (void)
101 {
102 static GType deinterlace_modes_type = 0;
103
104 static const GEnumValue modes_types[] = {
105 {GST_DEINTERLACE_MODE_AUTO, "Auto detection", "auto"},
106 {GST_DEINTERLACE_MODE_INTERLACED, "Force deinterlacing", "interlaced"},
107 {GST_DEINTERLACE_MODE_DISABLED, "Run in passthrough mode", "disabled"},
108 {0, NULL, NULL},
109 };
110
111 if (!deinterlace_modes_type) {
112 deinterlace_modes_type =
113 g_enum_register_static ("GstYadifModes", modes_types);
114 }
115 return deinterlace_modes_type;
116 }
117
118
119 /* class initialization */
120
121 G_DEFINE_TYPE_WITH_CODE (GstYadif, gst_yadif, GST_TYPE_BASE_TRANSFORM,
122 GST_DEBUG_CATEGORY_INIT (gst_yadif_debug_category, "yadif", 0,
123 "debug category for yadif element"));
124
125 static void
gst_yadif_class_init(GstYadifClass * klass)126 gst_yadif_class_init (GstYadifClass * klass)
127 {
128 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
129 GstBaseTransformClass *base_transform_class =
130 GST_BASE_TRANSFORM_CLASS (klass);
131
132 /* Setting up pads and setting metadata should be moved to
133 base_class_init if you intend to subclass this class. */
134 gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
135 &gst_yadif_sink_template);
136 gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
137 &gst_yadif_src_template);
138
139 gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
140 "YADIF deinterlacer", "Video/Filter",
141 "Deinterlace video using YADIF filter", "David Schleef <ds@schleef.org>");
142
143 gobject_class->set_property = gst_yadif_set_property;
144 gobject_class->get_property = gst_yadif_get_property;
145 gobject_class->dispose = gst_yadif_dispose;
146 gobject_class->finalize = gst_yadif_finalize;
147 base_transform_class->transform_caps =
148 GST_DEBUG_FUNCPTR (gst_yadif_transform_caps);
149 base_transform_class->set_caps = GST_DEBUG_FUNCPTR (gst_yadif_set_caps);
150 base_transform_class->get_unit_size =
151 GST_DEBUG_FUNCPTR (gst_yadif_get_unit_size);
152 base_transform_class->start = GST_DEBUG_FUNCPTR (gst_yadif_start);
153 base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_yadif_stop);
154 base_transform_class->transform = GST_DEBUG_FUNCPTR (gst_yadif_transform);
155
156 g_object_class_install_property (gobject_class, PROP_MODE,
157 g_param_spec_enum ("mode", "Deinterlace Mode",
158 "Deinterlace mode",
159 GST_TYPE_DEINTERLACE_MODES,
160 DEFAULT_MODE,
161 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
162
163 }
164
165 static void
gst_yadif_init(GstYadif * yadif)166 gst_yadif_init (GstYadif * yadif)
167 {
168 }
169
170 void
gst_yadif_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)171 gst_yadif_set_property (GObject * object, guint property_id,
172 const GValue * value, GParamSpec * pspec)
173 {
174 GstYadif *yadif = GST_YADIF (object);
175
176 switch (property_id) {
177 case PROP_MODE:
178 yadif->mode = g_value_get_enum (value);
179 break;
180 default:
181 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
182 break;
183 }
184 }
185
186 void
gst_yadif_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)187 gst_yadif_get_property (GObject * object, guint property_id,
188 GValue * value, GParamSpec * pspec)
189 {
190 GstYadif *yadif = GST_YADIF (object);
191
192 switch (property_id) {
193 case PROP_MODE:
194 g_value_set_enum (value, yadif->mode);
195 break;
196 default:
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
198 break;
199 }
200 }
201
202 void
gst_yadif_dispose(GObject * object)203 gst_yadif_dispose (GObject * object)
204 {
205 /* GstYadif *yadif = GST_YADIF (object); */
206
207 /* clean up as possible. may be called multiple times */
208
209 G_OBJECT_CLASS (gst_yadif_parent_class)->dispose (object);
210 }
211
212 void
gst_yadif_finalize(GObject * object)213 gst_yadif_finalize (GObject * object)
214 {
215 /* GstYadif *yadif = GST_YADIF (object); */
216
217 /* clean up object here */
218
219 G_OBJECT_CLASS (gst_yadif_parent_class)->finalize (object);
220 }
221
222
223 static GstCaps *
gst_yadif_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)224 gst_yadif_transform_caps (GstBaseTransform * trans,
225 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
226 {
227 GstCaps *othercaps;
228
229 othercaps = gst_caps_copy (caps);
230
231 if (direction == GST_PAD_SRC) {
232 GValue value = G_VALUE_INIT;
233 GValue v = G_VALUE_INIT;
234
235 g_value_init (&value, GST_TYPE_LIST);
236 g_value_init (&v, G_TYPE_STRING);
237
238 g_value_set_string (&v, "interleaved");
239 gst_value_list_append_value (&value, &v);
240 g_value_set_string (&v, "mixed");
241 gst_value_list_append_value (&value, &v);
242 g_value_set_string (&v, "progressive");
243 gst_value_list_append_value (&value, &v);
244
245 gst_caps_set_value (othercaps, "interlace-mode", &value);
246 g_value_unset (&value);
247 g_value_unset (&v);
248 } else {
249 gst_caps_set_simple (othercaps, "interlace-mode", G_TYPE_STRING,
250 "progressive", NULL);
251 }
252
253 return othercaps;
254 }
255
256 static gboolean
gst_yadif_set_caps(GstBaseTransform * trans,GstCaps * incaps,GstCaps * outcaps)257 gst_yadif_set_caps (GstBaseTransform * trans, GstCaps * incaps,
258 GstCaps * outcaps)
259 {
260 GstYadif *yadif = GST_YADIF (trans);
261
262 gst_video_info_from_caps (&yadif->video_info, incaps);
263
264 return TRUE;
265 }
266
267 static gboolean
gst_yadif_get_unit_size(GstBaseTransform * trans,GstCaps * caps,gsize * size)268 gst_yadif_get_unit_size (GstBaseTransform * trans, GstCaps * caps, gsize * size)
269 {
270 GstVideoInfo info;
271
272 if (gst_video_info_from_caps (&info, caps)) {
273 *size = GST_VIDEO_INFO_SIZE (&info);
274
275 return TRUE;
276 }
277 return FALSE;
278 }
279
280 static gboolean
gst_yadif_start(GstBaseTransform * trans)281 gst_yadif_start (GstBaseTransform * trans)
282 {
283
284 return TRUE;
285 }
286
287 static gboolean
gst_yadif_stop(GstBaseTransform * trans)288 gst_yadif_stop (GstBaseTransform * trans)
289 {
290
291 return TRUE;
292 }
293
294 void yadif_filter (GstYadif * yadif, int parity, int tff);
295
296 static GstFlowReturn
gst_yadif_transform(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer * outbuf)297 gst_yadif_transform (GstBaseTransform * trans, GstBuffer * inbuf,
298 GstBuffer * outbuf)
299 {
300 GstYadif *yadif = GST_YADIF (trans);
301 int parity;
302 int tff;
303
304 parity = 0;
305 tff = 0;
306
307 if (!gst_video_frame_map (&yadif->dest_frame, &yadif->video_info, outbuf,
308 GST_MAP_WRITE))
309 goto dest_map_failed;
310
311 if (!gst_video_frame_map (&yadif->cur_frame, &yadif->video_info, inbuf,
312 GST_MAP_READ))
313 goto src_map_failed;
314
315 yadif->next_frame = yadif->cur_frame;
316 yadif->prev_frame = yadif->cur_frame;
317
318 yadif_filter (yadif, parity, tff);
319
320 gst_video_frame_unmap (&yadif->dest_frame);
321 gst_video_frame_unmap (&yadif->cur_frame);
322 return GST_FLOW_OK;
323
324 dest_map_failed:
325 {
326 GST_ERROR_OBJECT (yadif, "failed to map dest");
327 return GST_FLOW_ERROR;
328 }
329 src_map_failed:
330 {
331 GST_ERROR_OBJECT (yadif, "failed to map src");
332 gst_video_frame_unmap (&yadif->dest_frame);
333 return GST_FLOW_ERROR;
334 }
335 }
336
337
338 static gboolean
plugin_init(GstPlugin * plugin)339 plugin_init (GstPlugin * plugin)
340 {
341
342 return gst_element_register (plugin, "yadif", GST_RANK_NONE, GST_TYPE_YADIF);
343 }
344
345 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
346 GST_VERSION_MINOR,
347 yadif,
348 "YADIF deinterlacing filter",
349 plugin_init, VERSION, "GPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)
350