2 Copyright (C) 2005 Edward Hervey (edward@fluendo.com)
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.
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.
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
24 #include <gst/gstcpu.h>
25 #include <gst/video/video.h>
26 #ifdef HAVE_FFMPEG_UNINSTALLED
28 #include <libpostproc/postprocess.h>
30 #include <ffmpeg/avcodec.h>
31 #include <ffmpeg/libpostproc/postprocess.h>
34 #include "gstpostproc.h"
36 typedef struct _PostProcDetails PostProcDetails;
38 struct _PostProcDetails {
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"},
65 typedef struct _GstPostProc GstPostProc;
70 GstPad *sinkpad, *srcpad;
74 gint ystride, ustride, vstride;
75 gint ysize, usize, vsize;
78 pp_context_t *context;
81 typedef struct _GstPostProcClass GstPostProcClass;
83 struct _GstPostProcClass {
84 GstElementClass parent_class;
94 /* hashtable, key = gtype, value = filterdetails index */
95 static GHashTable *global_plugins;
97 /* TODO : add support for the other format supported by libpostproc */
99 static GstStaticPadTemplate gst_postproc_src_template =
100 GST_STATIC_PAD_TEMPLATE ("src",
103 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV("I420"))
106 static GstStaticPadTemplate gst_postproc_sink_template =
107 GST_STATIC_PAD_TEMPLATE ("sink",
110 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV("I420"))
113 GST_DEBUG_CATEGORY (postproc);
114 #define GST_CAT_DEFAULT postproc
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);
121 static GstPadLinkReturn gst_postproc_link (GstPad * pad, const GstCaps * caps);
123 static void gst_postproc_chain (GstPad * pad, GstData * data);
125 static GstElementStateReturn gst_postproc_change_state (GstElement * element);
127 static void gst_postproc_set_property ( GObject * object, guint prop_id,
128 const GValue * value,
130 static void gst_postproc_get_property ( GObject * object, guint prop_id,
131 GValue * value, GParamSpec *pspec );
133 static GstElementClass *parent_class = NULL;
135 #ifndef GST_DISABLE_GST_DEBUG
137 gst_ffmpeg_log_callback (void * ptr, int level, const char * fmt, va_list vl)
139 GstDebugLevel gst_level;
143 gst_level = GST_LEVEL_NONE;
146 gst_level = GST_LEVEL_ERROR;
149 gst_level = GST_LEVEL_INFO;
152 gst_level = GST_LEVEL_DEBUG;
155 gst_level = GST_LEVEL_INFO;
159 gst_debug_log_valist (postproc, gst_level, "", "", 0, NULL, fmt, vl);
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)
168 change_context ( GstPostProc * postproc , gint width, gint height )
173 TODO : We need to find out what CPU flags we have in order to set
174 MMX/MMX2/3DNOW optimizations
177 GST_DEBUG ("change_context, width:%d, height:%d",
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);
202 change_mode ( GstPostProc * postproc )
204 GstPostProcClass * klass = (GstPostProcClass *) G_OBJECT_GET_CLASS (G_OBJECT (postproc));
207 pp_free_mode (postproc->mode);
208 postproc->mode = pp_get_mode_by_name_and_quality (filterdetails[klass->filterid].shortname,
213 gst_postproc_base_init ( GstPostProcClass * klass)
215 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
216 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
217 GstElementDetails details;
220 ppidx = GPOINTER_TO_INT (g_hash_table_lookup (global_plugins,
221 GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class))));
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);
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));
236 klass->filterid = ppidx;
240 gst_postproc_class_init (GstPostProcClass * klass)
242 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
243 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
245 parent_class = g_type_class_peek_parent (klass);
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));
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;
259 gst_postproc_init (GstPostProc * postproc)
261 GST_FLAG_SET (postproc, GST_ELEMENT_WORK_IN_PLACE);
263 postproc->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get
264 (&gst_postproc_sink_template),
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);
270 postproc->srcpad = gst_pad_new_from_template (gst_static_pad_template_get
271 (&gst_postproc_src_template),
273 gst_element_add_pad (GST_ELEMENT (postproc), postproc->srcpad);
275 postproc->quality = 6;
276 postproc->mode = NULL;
277 change_mode (postproc);
278 postproc->context = NULL;
280 postproc->height = 0;
281 postproc->ystride = 0;
282 postproc->ustride = 0;
283 postproc->vstride = 0;
290 gst_postproc_dispose (GObject * object)
292 GstPostProc * postproc = (GstPostProc *) object;
293 G_OBJECT_CLASS (parent_class)->dispose (object);
296 pp_free_mode(postproc->mode);
297 if (postproc->context)
298 pp_free_context(postproc->context);
301 static GstPadLinkReturn
302 gst_postproc_link (GstPad * pad, const GstCaps * caps)
304 GstPostProc *postproc;
305 GstStructure *structure;
308 GstPadLinkReturn ret;
310 /* create/replace pp_context here */
312 postproc = (GstPostProc *) gst_pad_get_parent (pad);
313 otherpad = (pad == postproc->sinkpad) ? postproc->srcpad : postproc->sinkpad;
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);
320 return GST_PAD_LINK_REFUSED;
322 ret = gst_pad_try_set_caps (otherpad, caps);
324 if (GST_PAD_LINK_FAILED (ret))
327 change_context (postproc, width, height);
329 return GST_PAD_LINK_OK;
333 gst_postproc_chain (GstPad * pad, GstData * data)
335 GstPostProc *postproc;
338 unsigned char * inplane[3];
339 unsigned char * outplane[3];
342 GST_DEBUG("chaining");
344 /* postprocess the buffer !*/
345 postproc = (GstPostProc *) gst_pad_get_parent (pad);
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);
351 in = GST_BUFFER (data);
352 out = gst_buffer_copy_on_write (in);
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;
364 GST_DEBUG ("calling pp_postprocess, width:%d, height:%d",
365 postproc->width, postproc->height);
367 pp_postprocess (outplane, stride,
372 postproc->mode, postproc->context, 1);
374 gst_buffer_stamp (out, in);
376 gst_pad_push (postproc->srcpad, GST_DATA (out));
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);
384 src is the src buffer data
385 srcStride is ->ystride, ->ustride, ->vstride
387 dstStride same as srcStride
388 horizontalSize and VerticalsSize are obvious
389 QP_store can be null and qp_stride too
397 static GstElementStateReturn
398 gst_postproc_change_state (GstElement * element)
400 GstPostProc *postproc = (GstPostProc *) element;
401 /* don't go to play if we don't have mode and context */
403 switch (GST_STATE_TRANSITION (element)) {
404 case GST_STATE_PAUSED_TO_PLAYING:
405 if ((!postproc->mode) && (!postproc->context))
406 return GST_STATE_FAILURE;
409 if (parent_class->change_state)
410 return parent_class->change_state (element);
412 return GST_STATE_SUCCESS;
416 gst_postproc_set_property ( GObject * object, guint prop_id,
417 const GValue * value,
420 GstPostProc *postproc = (GstPostProc *) object;
425 quality = g_value_get_uint (value);
426 if (quality != postproc->quality) {
427 postproc->quality = quality;
428 change_mode (postproc);
432 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
437 gst_postproc_get_property ( GObject * object, guint prop_id,
438 GValue * value, GParamSpec *pspec )
440 GstPostProc *postproc = (GstPostProc *) object;
444 g_value_set_uint (value, postproc->quality);
447 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
453 gst_postproc_register(GstPlugin * plugin)
455 GTypeInfo typeinfo = {
456 sizeof (GstPostProcClass),
457 (GBaseInitFunc) gst_postproc_base_init,
459 (GClassInitFunc) gst_postproc_class_init,
462 sizeof (GstPostProc),
464 (GInstanceInitFunc) gst_postproc_init,
469 global_plugins = g_hash_table_new (NULL, NULL);
470 for (i = 0; filterdetails[i].shortname; i++) {
473 g_hash_table_insert (global_plugins,
475 GINT_TO_POINTER (i));
477 /* create type_name */
478 type_name = g_strdup_printf("postproc_%s", filterdetails[i].longname);
479 if (g_type_from_name (type_name)) {
485 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
487 g_hash_table_insert (global_plugins,
488 GINT_TO_POINTER (type),
489 GINT_TO_POINTER (i));
491 /* register element */
492 if (!gst_element_register (plugin, type_name, GST_RANK_PRIMARY, type)) {
499 g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
504 plugin_init (GstPlugin * plugin)
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);
511 /* Register the filters */
512 gst_postproc_register( plugin );
514 /* Now we can return the pointer to the newly created Plugin object. */
518 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
521 "postprocessing elements",
523 FFMPEG_VERSION, "GPL", "FFMpeg", "http://ffmpeg.sourceforge.net/")