ext/libpostproc/gstpostproc.c: It works better when filing the hash_table BEFORE...
[platform/upstream/gstreamer.git] / ext / libpostproc / gstpostproc.c
1 /*
2     Copyright (C) 2005 Edward Hervey (edward@fluendo.com)
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program 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
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <gst/gst.h>
24 #include <gst/gstcpu.h>
25 #include <gst/video/video.h>
26 #ifdef HAVE_FFMPEG_UNINSTALLED
27 #include <avcodec.h>
28 #include <libpostproc/postprocess.h>
29 #else
30 #include <ffmpeg/avcodec.h>
31 #include <ffmpeg/libpostproc/postprocess.h>
32 #endif
33
34 #include "gstpostproc.h"
35
36 typedef struct _PostProcDetails PostProcDetails;
37
38 struct _PostProcDetails {
39   char  *shortname;
40   char  *longname;
41   char  *description;
42 };
43
44 static PostProcDetails filterdetails[] = {
45   {"hb", "hdeblock",            "horizontal deblocking filter"},
46   {"vb", "vdeblock",            "vertical deblocking filter"},
47   {"h1", "x1hdeblock",          "experimental horizontal deblocking filter 1"},
48   {"v1", "x1vdeblock",          "experimental vertical deblocking filter 1"},
49   {"ha", "ahdeblock",           "another horizontal deblocking filter"},
50   {"va", "avdeblock",           "another vertical deblocking filter"},
51   {"dr", "dering",              "deringing filter"},
52   {"al", "autolevels",          "automatic brightness/contrast filter"},
53   {"lb", "linblenddeint",       "linear blend interpolater"},
54   {"li", "linipoldeint",        "linear interpolation deinterlacer"},
55   {"ci", "cubicipoldeint",      "cubic interpolation deinterlacer"},
56   {"md", "mediandeint",         "median deinterlacer"},
57   {"fd", "ffmpegdeint",         "ffmpeg deinterlacer"},
58   {"l5", "lowpass5",            "FIR lowpass deinterlacer"},
59   {"tn", "tmpnoise",            "temporal noise reducer"},
60   {"fq", "forcequant",          "force quantizer"},
61   {"de", "default",             "default filters"},
62   {NULL, NULL,                  NULL}
63 };
64
65 typedef struct  _GstPostProc GstPostProc;
66
67 struct  _GstPostProc {
68   GstElement    element;
69
70   GstPad        *sinkpad, *srcpad;
71   guint         quality;
72   gint          width, height;
73
74   gint          ystride, ustride, vstride;
75   gint          ysize, usize, vsize;
76
77   pp_mode_t     *mode;
78   pp_context_t  *context;
79 };
80
81 typedef struct  _GstPostProcClass GstPostProcClass;
82
83 struct  _GstPostProcClass {
84   GstElementClass       parent_class;
85
86   gint          filterid;
87 };
88
89 enum {
90   ARG_0,
91   ARG_QUALITY
92 };
93
94 /* hashtable, key = gtype, value = filterdetails index */
95 static GHashTable       *global_plugins;
96
97 /* TODO : add support for the other format supported by libpostproc */
98
99 static GstStaticPadTemplate gst_postproc_src_template = 
100 GST_STATIC_PAD_TEMPLATE ("src",
101                          GST_PAD_SRC,
102                          GST_PAD_ALWAYS,
103                          GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV("I420"))
104                          );
105                          
106 static GstStaticPadTemplate gst_postproc_sink_template =
107 GST_STATIC_PAD_TEMPLATE ("sink",
108                          GST_PAD_SINK,
109                          GST_PAD_ALWAYS,
110                          GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV("I420"))
111                          );
112                          
113 GST_DEBUG_CATEGORY (postproc);
114 #define GST_CAT_DEFAULT postproc
115
116 static void     gst_postproc_class_init (GstPostProcClass * klass);
117 static void     gst_postproc_base_init (GstPostProcClass * klass);
118 static void     gst_postproc_init (GstPostProc * pproc);
119 static void     gst_postproc_dispose (GObject * object);
120
121 static GstPadLinkReturn gst_postproc_link (GstPad  * pad, const GstCaps * caps);
122
123 static void     gst_postproc_chain (GstPad * pad, GstData * data);
124
125 static GstElementStateReturn    gst_postproc_change_state (GstElement * element);
126
127 static void     gst_postproc_set_property ( GObject * object, guint prop_id,
128                                             const GValue * value,
129                                             GParamSpec *pspec );
130 static void     gst_postproc_get_property ( GObject * object, guint prop_id,
131                                             GValue * value, GParamSpec *pspec );
132
133 static GstElementClass  *parent_class = NULL;
134
135 #ifndef GST_DISABLE_GST_DEBUG
136 static void
137 gst_ffmpeg_log_callback (void * ptr, int level, const char * fmt, va_list vl)
138 {
139   GstDebugLevel gst_level;
140
141   switch (level) {
142     case AV_LOG_QUIET:
143       gst_level = GST_LEVEL_NONE;
144       break;
145     case AV_LOG_ERROR:
146       gst_level = GST_LEVEL_ERROR;
147       break;
148     case AV_LOG_INFO:
149       gst_level = GST_LEVEL_INFO;
150       break;
151     case AV_LOG_DEBUG:
152       gst_level = GST_LEVEL_DEBUG;
153       break;
154     default:
155       gst_level = GST_LEVEL_INFO;
156       break;
157   }
158
159   gst_debug_log_valist (postproc, gst_level, "", "", 0, NULL, fmt, vl);
160 }
161 #endif
162
163 #define ROUND_UP_2(x)  (((x)+1)&~1)
164 #define ROUND_UP_4(x)  (((x)+3)&~3)
165 #define ROUND_UP_8(x)  (((x)+7)&~7)
166
167 static void
168 change_context ( GstPostProc * postproc , gint width, gint height )
169 {
170   GstCPUFlags   flags;
171   int           ppflags;
172   /*
173     TODO : We need to find out what CPU flags we have in order to set
174     MMX/MMX2/3DNOW optimizations
175   */
176
177   GST_DEBUG ("change_context, width:%d, height:%d",
178              width, height);
179
180   if ((width != postproc->width) && (height != postproc->height)) {
181     if (postproc->context)
182       pp_free_context (postproc->context);
183     flags = gst_cpu_get_flags();
184     ppflags = (flags & GST_CPU_FLAG_MMX ? PP_CPU_CAPS_MMX : 0)
185       | (flags & GST_CPU_FLAG_MMXEXT ? PP_CPU_CAPS_MMX2 : 0)
186       | (flags & GST_CPU_FLAG_3DNOW ? PP_CPU_CAPS_3DNOW : 0);
187     postproc->context = pp_get_context (width, height, PP_FORMAT_420 | ppflags);
188     postproc->width = width;
189     postproc->height = height;
190     postproc->ystride = ROUND_UP_4 (width);
191     postproc->ustride = ROUND_UP_8 (width) / 2;
192     postproc->vstride = ROUND_UP_8 (postproc->ystride) / 2;
193     postproc->ysize = postproc->ystride * ROUND_UP_2 (height);
194     postproc->usize = postproc->ustride * ROUND_UP_2 (height) / 2;
195     postproc->vsize = postproc->vstride * ROUND_UP_2 (height) / 2;
196     GST_DEBUG ("new strides are (YUV) : %d %d %d",
197                postproc->ystride, postproc->ustride, postproc->vstride);
198   }
199 }
200
201 static void
202 change_mode ( GstPostProc * postproc )
203 {
204   GstPostProcClass * klass = (GstPostProcClass *) G_OBJECT_GET_CLASS (G_OBJECT (postproc));
205
206   if (postproc->mode)
207     pp_free_mode (postproc->mode);
208   postproc->mode = pp_get_mode_by_name_and_quality (filterdetails[klass->filterid].shortname,
209                                                     postproc->quality);
210 }
211
212 static void
213 gst_postproc_base_init ( GstPostProcClass * klass)
214 {
215   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
216   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
217   GstElementDetails details;
218   gint  ppidx;
219   
220   ppidx = GPOINTER_TO_INT (g_hash_table_lookup (global_plugins,
221         GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class))));
222
223   details.longname = g_strdup_printf ("LibPostProc %s filter", filterdetails[ppidx].longname);
224   details.klass = "Filter/Video";
225   details.description = g_strdup_printf ("LibPostProc %s", filterdetails[ppidx].description);
226   details.author = "Edward Hervey <edward@fluendo.com>";
227   gst_element_class_set_details (element_class, &details);
228   g_free(details.longname);
229   g_free(details.description);
230
231   gst_element_class_add_pad_template (element_class, 
232                                       gst_static_pad_template_get (&gst_postproc_src_template));
233   gst_element_class_add_pad_template (element_class, 
234                                       gst_static_pad_template_get (&gst_postproc_sink_template));
235
236   klass->filterid = ppidx;
237 }
238
239 static void
240 gst_postproc_class_init (GstPostProcClass * klass)
241 {
242   GObjectClass  *gobject_class = G_OBJECT_CLASS (klass);
243   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
244
245   parent_class = g_type_class_peek_parent (klass);
246
247   g_object_class_install_property (gobject_class, ARG_QUALITY,
248       g_param_spec_uint ("quality", "Quality",
249                          "Quality level of filter (6:best)",
250                          0, 6, 6, G_PARAM_READWRITE));
251
252   gobject_class->dispose = gst_postproc_dispose;
253   gobject_class->set_property = gst_postproc_set_property;
254   gobject_class->get_property = gst_postproc_get_property;
255   gstelement_class->change_state = gst_postproc_change_state;
256 }
257
258 static void
259 gst_postproc_init (GstPostProc * postproc)
260 {
261   GST_FLAG_SET (postproc, GST_ELEMENT_WORK_IN_PLACE);
262
263   postproc->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get
264                                                  (&gst_postproc_sink_template),
265                                                  "sink");
266   gst_pad_set_link_function (postproc->sinkpad, gst_postproc_link);
267   gst_pad_set_chain_function (postproc->sinkpad, gst_postproc_chain);
268   gst_element_add_pad (GST_ELEMENT (postproc), postproc->sinkpad);
269
270   postproc->srcpad = gst_pad_new_from_template (gst_static_pad_template_get
271                                                 (&gst_postproc_src_template),
272                                                 "src");
273   gst_element_add_pad (GST_ELEMENT (postproc), postproc->srcpad);
274
275   postproc->quality = 6;
276   postproc->mode = NULL;
277   change_mode (postproc);
278   postproc->context = NULL;
279   postproc->width = 0;
280   postproc->height = 0;
281   postproc->ystride = 0;
282   postproc->ustride = 0;
283   postproc->vstride = 0;
284   postproc->ysize = 0;
285   postproc->usize = 0;
286   postproc->vsize = 0;
287 }
288
289 static void
290 gst_postproc_dispose (GObject * object)
291 {
292   GstPostProc * postproc = (GstPostProc *) object;
293   G_OBJECT_CLASS (parent_class)->dispose (object);
294
295   if (postproc->mode)
296     pp_free_mode(postproc->mode);
297   if (postproc->context)
298     pp_free_context(postproc->context);
299 }
300
301 static GstPadLinkReturn
302 gst_postproc_link (GstPad * pad, const GstCaps * caps)
303 {
304   GstPostProc   *postproc;
305   GstStructure  *structure;
306   GstPad        *otherpad;
307   gboolean      res;
308   GstPadLinkReturn      ret;
309   gint          width, height;
310   /* create/replace pp_context here */
311
312   postproc = (GstPostProc *) gst_pad_get_parent (pad);
313   otherpad = (pad == postproc->sinkpad) ? postproc->srcpad : postproc->sinkpad;
314
315   structure = gst_caps_get_structure (caps, 0);
316   res = gst_structure_get_int (structure, "width", &width);
317   res &= gst_structure_get_int (structure, "height", &height);
318
319   if (!res)
320     return GST_PAD_LINK_REFUSED;
321
322   ret = gst_pad_try_set_caps (otherpad, caps);
323
324   if (GST_PAD_LINK_FAILED (ret))
325     return ret;
326
327   change_context (postproc, width, height);
328
329   return GST_PAD_LINK_OK;
330 }
331
332 static void
333 gst_postproc_chain (GstPad * pad, GstData * data)
334 {
335   GstPostProc   *postproc;
336   GstBuffer     *in, *out;
337   int           stride[3];
338   unsigned char * inplane[3];
339   unsigned char * outplane[3];
340   gint          pixdif, i;
341
342   GST_DEBUG("chaining");
343
344   /* postprocess the buffer !*/
345   postproc = (GstPostProc *) gst_pad_get_parent (pad);
346
347   g_return_if_fail(GST_IS_BUFFER (data));
348   g_return_if_fail(postproc->mode != NULL);
349   g_return_if_fail(postproc->context != NULL);
350
351   in = GST_BUFFER (data);
352   out = gst_buffer_copy_on_write (in);
353
354   stride[0] = postproc->ystride;
355   stride[1] = postproc->ustride;
356   stride[2] = postproc->vstride;
357 /*   inplane[0] = GST_BUFFER_DATA(in); */
358 /*   inplane[1] = inplane[0] + postproc->ysize; */
359 /*   inplane[2] = inplane[1] + postproc->usize; */
360   outplane[0] = GST_BUFFER_DATA(out);
361   outplane[1] = outplane[0] + postproc->ysize;
362   outplane[2] = outplane[1] + postproc->usize;
363
364   GST_DEBUG ("calling pp_postprocess, width:%d, height:%d",
365              postproc->width, postproc->height);
366
367   pp_postprocess (outplane, stride,
368                   outplane, stride,
369                   postproc->width,
370                   postproc->height,
371                   "", 0,
372                   postproc->mode, postproc->context, 1);
373
374   gst_buffer_stamp (out, in);
375
376   gst_pad_push (postproc->srcpad, GST_DATA (out));
377   /*
378     void  pp_postprocess(uint8_t * src[3], int srcStride[3],
379         uint8_t * dst[3], int dstStride[3],
380         int horizontalSize, int verticalSize,
381         QP_STORE_T *QP_store,  int QP_stride,
382         pp_mode_t *mode, pp_context_t *ppContext, int pict_type);
383
384         src is the src buffer data
385         srcStride is ->ystride, ->ustride, ->vstride
386         dst same as src
387         dstStride same as srcStride
388         horizontalSize and VerticalsSize are obvious
389         QP_store can be null and qp_stride too
390         mode = mode
391         context = context
392         pict_type = 0
393   */
394
395 }
396
397 static GstElementStateReturn
398 gst_postproc_change_state (GstElement * element)
399 {
400   GstPostProc   *postproc = (GstPostProc *) element;
401   /* don't go to play if we don't have mode and context */
402
403   switch (GST_STATE_TRANSITION (element)) {
404   case GST_STATE_PAUSED_TO_PLAYING:
405     if ((!postproc->mode) && (!postproc->context))
406       return GST_STATE_FAILURE;
407   }
408
409   if (parent_class->change_state)
410     return parent_class->change_state (element);
411
412   return GST_STATE_SUCCESS;
413 }
414
415 static void
416 gst_postproc_set_property ( GObject * object, guint prop_id,
417                             const GValue * value,
418                             GParamSpec *pspec )
419 {
420   GstPostProc   *postproc = (GstPostProc *) object;
421   gint quality;
422
423   switch (prop_id) {
424   case ARG_QUALITY:
425     quality = g_value_get_uint (value);
426     if (quality != postproc->quality) {
427       postproc->quality = quality;
428       change_mode (postproc);
429     }
430     break;
431   default:
432     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
433   }
434 }
435
436 static void
437 gst_postproc_get_property ( GObject * object, guint prop_id,
438                             GValue * value, GParamSpec *pspec )
439 {
440   GstPostProc   *postproc = (GstPostProc *) object;
441
442   switch (prop_id) {
443   case ARG_QUALITY:
444     g_value_set_uint (value, postproc->quality);
445     break;
446   default:
447     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
448     break;
449   }
450 }
451
452 gboolean
453 gst_postproc_register(GstPlugin * plugin)
454 {
455   GTypeInfo     typeinfo = {
456     sizeof (GstPostProcClass),
457     (GBaseInitFunc) gst_postproc_base_init,
458     NULL,
459     (GClassInitFunc) gst_postproc_class_init,
460     NULL,
461     NULL,
462     sizeof (GstPostProc),
463     0,
464     (GInstanceInitFunc) gst_postproc_init,
465   };
466   GType type;
467   int   i;
468
469   global_plugins = g_hash_table_new (NULL, NULL);
470   for (i = 0; filterdetails[i].shortname; i++) {
471     gchar       *type_name;
472
473     g_hash_table_insert (global_plugins,
474                          GINT_TO_POINTER (0),
475                          GINT_TO_POINTER (i));
476
477     /* create type_name */
478     type_name = g_strdup_printf("postproc_%s", filterdetails[i].longname);
479     if (g_type_from_name (type_name)) {
480       g_free (type_name);
481       continue;
482     }
483
484     /* create gtype */
485     type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
486
487     g_hash_table_insert (global_plugins,
488                          GINT_TO_POINTER (type),
489                          GINT_TO_POINTER (i));
490
491     /* register element */
492     if (!gst_element_register (plugin, type_name, GST_RANK_PRIMARY, type)) {
493       g_free(type_name);
494       return FALSE;
495     }
496
497     g_free(type_name);
498   }
499   g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
500   return TRUE;
501 }
502
503 static gboolean
504 plugin_init (GstPlugin * plugin)
505 {
506   GST_DEBUG_CATEGORY_INIT (postproc, "postproc", 0, "video postprocessing elements");
507 #ifndef GST_DISABLE_GST_DEBUG
508   av_log_set_callback (gst_ffmpeg_log_callback);
509 #endif
510
511   /* Register the filters */
512   gst_postproc_register( plugin );
513
514   /* Now we can return the pointer to the newly created Plugin object. */
515   return TRUE;
516 }
517
518 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
519     GST_VERSION_MINOR,
520     "postproc",
521     "postprocessing elements",
522     plugin_init,
523     FFMPEG_VERSION, "GPL", "FFMpeg", "http://ffmpeg.sourceforge.net/")
524
525