compatibility fix for new GST_DEBUG stuff.
[platform/upstream/gstreamer.git] / gst / deinterlace / gstdeinterlace.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /* based on the Area Based Deinterlacer (for RGB frames)        */
20 /* (a VirtualDub filter) from Gunnar Thalin <guth@home.se>      */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <string.h>
26 #include <gst/gst.h>
27 #include "gstdeinterlace.h"
28
29 /* elementfactory information */
30 static GstElementDetails deinterlace_details = {
31   "Deinterlace",
32   "Filter/Video",
33   "LGPL",
34   "Deinterlace video",
35   VERSION,
36   "Wim Taymans <wim.taymans@chello.be>",
37   "(C) 2001",
38 };
39
40
41 /* Filter signals and args */
42 enum {
43   /* FILL ME */
44   LAST_SIGNAL
45 };
46
47 enum {
48   ARG_0,
49   ARG_DI_ONLY,
50   ARG_BLEND,
51   ARG_THRESHOLD,
52   ARG_EDGE_DETECT,
53 };
54
55 GST_PAD_TEMPLATE_FACTORY (deinterlace_src_factory,
56   "src",
57   GST_PAD_SRC,
58   GST_PAD_ALWAYS,
59   GST_CAPS_NEW (
60    "deinterlace_src",
61    "video/raw",
62      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")),
63      "width",    GST_PROPS_INT_POSITIVE,
64      "height",   GST_PROPS_INT_POSITIVE
65   )
66 )
67
68 GST_PAD_TEMPLATE_FACTORY (deinterlace_sink_factory,
69   "sink",
70   GST_PAD_SINK,
71   GST_PAD_ALWAYS,
72   GST_CAPS_NEW (
73    "deinterlace_src",
74    "video/raw",
75      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")),
76      "width",    GST_PROPS_INT_POSITIVE,
77      "height",   GST_PROPS_INT_POSITIVE
78   )
79 )
80
81 static GType            gst_deinterlace_get_type                (void);
82
83 static void             gst_deinterlace_class_init              (GstDeInterlaceClass *klass);
84 static void             gst_deinterlace_init                    (GstDeInterlace *filter);
85
86 static void             gst_deinterlace_set_property            (GObject *object, guint prop_id, 
87                                                                  const GValue *value, GParamSpec *pspec);
88 static void             gst_deinterlace_get_property            (GObject *object, guint prop_id, 
89                                                                  GValue *value, GParamSpec *pspec);
90
91 static void             gst_deinterlace_chain                   (GstPad *pad, GstBuffer *buf);
92
93 static GstElementClass *parent_class = NULL;
94 /*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
95
96 static GType
97 gst_deinterlace_get_type(void) {
98   static GType deinterlace_type = 0;
99
100   if (!deinterlace_type) {
101     static const GTypeInfo deinterlace_info = {
102       sizeof(GstDeInterlaceClass),      NULL,
103       NULL,
104       (GClassInitFunc)gst_deinterlace_class_init,
105       NULL,
106       NULL,
107       sizeof(GstDeInterlace),
108       0,
109       (GInstanceInitFunc)gst_deinterlace_init,
110     };
111     deinterlace_type = g_type_register_static(GST_TYPE_ELEMENT, "GstDeInterlace", &deinterlace_info, 0);
112   }
113   return deinterlace_type;
114 }
115
116 static void
117 gst_deinterlace_class_init (GstDeInterlaceClass *klass)
118 {
119   GObjectClass *gobject_class;
120   GstElementClass *gstelement_class;
121
122   gobject_class = (GObjectClass*)klass;
123   gstelement_class = (GstElementClass*)klass;
124
125   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
126
127   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DI_ONLY,
128     g_param_spec_boolean("di_area_only","di_area_only","di_area_only",
129                          TRUE,G_PARAM_READWRITE)); /* CHECKME */
130   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BLEND,
131     g_param_spec_boolean("blend","blend","blend",
132                          TRUE,G_PARAM_READWRITE)); /* CHECKME */
133   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_THRESHOLD,
134     g_param_spec_int("threshold","threshold","threshold",
135                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
136   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_EDGE_DETECT,
137     g_param_spec_int("edge_detect","edge_detect","edge_detect",
138                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
139
140   gobject_class->set_property = gst_deinterlace_set_property;
141   gobject_class->get_property = gst_deinterlace_get_property;
142 }
143
144 static GstPadLinkReturn
145 gst_deinterlace_sinkconnect (GstPad *pad, GstCaps *caps)
146 {
147   GstDeInterlace *filter;
148
149   filter = GST_DEINTERLACE(gst_pad_get_parent (pad));
150   
151   if (!GST_CAPS_IS_FIXED (caps))
152     return GST_PAD_LINK_DELAYED;
153
154   gst_caps_get_int (caps, "width", &filter->width);
155   gst_caps_get_int (caps, "height", &filter->height);
156
157   if (filter->picsize != (filter->width*filter->height)) {
158     if (filter->src) 
159       g_free(filter->src);
160     filter->picsize = filter->width*filter->height;
161     filter->src = g_malloc(filter->picsize);
162   }
163   return gst_pad_try_set_caps (filter->srcpad, caps);
164 }
165
166 static void
167 gst_deinterlace_init (GstDeInterlace *filter)
168 {
169   filter->sinkpad = gst_pad_new_from_template(deinterlace_sink_factory (),"sink");
170   gst_pad_set_chain_function(filter->sinkpad,gst_deinterlace_chain);
171   gst_pad_set_link_function(filter->sinkpad,gst_deinterlace_sinkconnect);
172   gst_element_add_pad(GST_ELEMENT(filter),filter->sinkpad);
173
174   filter->srcpad = gst_pad_new_from_template(deinterlace_src_factory (),"src");
175   gst_element_add_pad(GST_ELEMENT(filter),filter->srcpad);
176
177   filter->show_deinterlaced_area_only = FALSE;
178   filter->blend = FALSE;
179   /*filter->threshold_blend = 0;  */
180   filter->threshold = 50;
181   filter->edge_detect = 25;
182
183   filter->src = NULL;
184   filter->picsize = 0;
185 }
186
187 static void
188 gst_deinterlace_chain (GstPad *pad, GstBuffer *buf)
189 {
190   GstDeInterlace *filter;
191   gint y0, y1, y2, y3;
192   guchar *psrc1, *psrc2, *psrc3, *pdst1, *yuvptr, *src;
193   gint iInterlaceValue0, iInterlaceValue1, iInterlaceValue2;
194   gint x, y;
195   gint y_line;
196   guchar *y_dst, *y_src;
197   gboolean bBlend;
198   gint iThreshold;
199   gint iEdgeDetect;
200   gint width, height;
201   gboolean bShowDeinterlacedAreaOnly;
202
203   g_return_if_fail (pad != NULL);
204   g_return_if_fail (GST_IS_PAD (pad));
205   g_return_if_fail (buf != NULL);
206
207   filter = GST_DEINTERLACE (gst_pad_get_parent (pad));
208
209   bBlend = filter->blend;
210   iThreshold = filter->threshold;
211   iEdgeDetect = filter->edge_detect;
212   width = filter->width;
213   height = filter->height;
214   src = filter->src;
215   yuvptr = GST_BUFFER_DATA (buf);
216   bShowDeinterlacedAreaOnly = filter->show_deinterlaced_area_only;
217
218   memcpy(filter->src, yuvptr, filter->picsize); 
219
220   y_dst = yuvptr;  /* dst y pointer */
221                    /* we should not change u,v because one u, v value stands for */
222                    /* 2 pixels per 2 lines = 4 pixel and we don't want to change */
223                    /* the color of */
224
225   y_line  = width;
226   y_src = src;
227
228   iThreshold = iThreshold * iThreshold * 4;
229   /* We don't want an integer overflow in the  interlace calculation. */
230   if (iEdgeDetect > 180)
231     iEdgeDetect = 180;
232   iEdgeDetect = iEdgeDetect * iEdgeDetect;
233
234   y1 = 0;               /* Avoid compiler warning. The value is not used. */
235   for (x = 0; x < width; x++) {
236     psrc3 = y_src + x;
237     y3    = *psrc3;
238     psrc2 = psrc3 + y_line;
239     y2 = *psrc2;
240     pdst1 = y_dst + x;
241     iInterlaceValue1 = iInterlaceValue2 = 0;
242     for (y = 0; y <= height; y++) {
243       psrc1 = psrc2;
244       psrc2 = psrc3;
245       psrc3 = psrc3 + y_line;
246       y0 = y1;
247       y1 = y2;
248       y2 = y3;
249       if (y < height - 1) {
250         y3 = *psrc3;
251       } else {
252         y3 = y1;
253       }
254
255       iInterlaceValue0 = iInterlaceValue1;
256       iInterlaceValue1 = iInterlaceValue2;
257
258       if (y < height)
259         iInterlaceValue2 = ((y1 - y2) * (y3 - y2) - 
260                                   ((iEdgeDetect * (y1 - y3) * (y1 - y3)) >> 12))*10;
261       else
262         iInterlaceValue2 = 0;
263
264       if (y > 0) {                      
265         if (iInterlaceValue0 + 2 * iInterlaceValue1 + iInterlaceValue2 > iThreshold) {
266           if (bBlend) { 
267             *pdst1 = (unsigned char)((y0 + 2*y1 + y2) >> 2);
268           } else {
269             /* this method seems to work better than blending if the */
270             /* quality is pretty bad and the half pics don't fit together */
271             if ((y % 2)==1) {  /* if odd simply copy the value */
272               *pdst1 = *psrc1;
273               /**pdst1 = 0; // FIXME this is for adjusting an initial iThreshold */
274             } else {        /* even interpolate the even line (upper + lower)/2 */
275               *pdst1 = (unsigned char)((y0 + y2) >> 1);
276               /**pdst1 = 0; // FIXME this is for adjusting an initial iThreshold */
277             }
278           } 
279         } else {
280           /* so we went below the treshold and therefore we don't have to  */
281           /* change anything */
282           if (bShowDeinterlacedAreaOnly) {
283                 /* this is for testing to see how we should tune the treshhold */
284                 /* and shows as the things that haven't change because the  */
285                 /* threshhold was to low?? (or shows that everything is ok :-) */
286             *pdst1 = 0; /* blank the point and so the interlac area */
287           } else {
288             *pdst1 = *psrc1;
289           }
290         }
291         pdst1 = pdst1 + y_line;
292       }
293     }
294   }
295
296   gst_pad_push (filter->srcpad, buf);
297 }
298
299 static void
300 gst_deinterlace_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
301 {
302   GstDeInterlace *filter;
303
304   /* it's not null if we got it, but it might not be ours */
305   g_return_if_fail(GST_IS_DEINTERLACE(object));
306
307   filter = GST_DEINTERLACE(object);
308
309   switch (prop_id) 
310   {
311     case ARG_DI_ONLY:
312       filter->show_deinterlaced_area_only = g_value_get_boolean (value);
313       break;
314     case ARG_BLEND:
315       filter->blend = g_value_get_boolean (value);
316       break;
317     case ARG_THRESHOLD:
318       filter->threshold = g_value_get_int (value);
319       break;
320     case ARG_EDGE_DETECT:
321       filter->edge_detect = g_value_get_int (value);
322       break;
323     default:
324       break;
325   }
326 }
327
328 static void
329 gst_deinterlace_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
330 {
331   GstDeInterlace *filter;
332
333   /* it's not null if we got it, but it might not be ours */
334   g_return_if_fail(GST_IS_DEINTERLACE(object));
335
336   filter = GST_DEINTERLACE(object);
337
338   switch (prop_id) {
339     case ARG_DI_ONLY:
340       g_value_set_boolean (value, filter->show_deinterlaced_area_only);
341       break;
342     case ARG_BLEND:
343       g_value_set_boolean (value, filter->blend);
344       break;
345     case ARG_THRESHOLD:
346       g_value_set_int (value, filter->threshold);
347       break;
348     case ARG_EDGE_DETECT:
349       g_value_set_int (value, filter->edge_detect);
350       break;
351     default:
352       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
353       break;
354   }
355 }
356
357 static gboolean
358 plugin_init (GModule *module, GstPlugin *plugin)
359 {
360   GstElementFactory *factory;
361
362   factory = gst_element_factory_new("deinterlace",GST_TYPE_DEINTERLACE,
363                                    &deinterlace_details);
364   g_return_val_if_fail(factory != NULL, FALSE);
365   
366   gst_element_factory_add_pad_template (factory, 
367                   GST_PAD_TEMPLATE_GET (deinterlace_src_factory));
368   gst_element_factory_add_pad_template (factory, 
369                   GST_PAD_TEMPLATE_GET (deinterlace_sink_factory));
370
371   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
372
373   return TRUE;
374 }
375
376 GstPluginDesc plugin_desc = {
377   GST_VERSION_MAJOR,
378   GST_VERSION_MINOR,
379   "deinterlace",
380   plugin_init
381 };