2 * gstvaapipostproc.c - VA-API video postprocessing
4 * Copyright (C) 2012 Intel Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1
9 * 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 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
23 * SECTION:gstvaapipostproc
24 * @short_description: A video postprocessing filter
26 * vaapipostproc consists in various postprocessing algorithms to be
27 * applied to VA surfaces. So far, only basic bob deinterlacing is
32 #include <gst/video/video.h>
33 #include <gst/video/videocontext.h>
34 #include <gst/vaapi/gstvaapivideosink.h>
35 #include <gst/vaapi/gstvaapivideobuffer.h>
37 #include "gstvaapipostproc.h"
38 #include "gstvaapipluginutil.h"
39 #include "gstvaapipluginbuffer.h"
41 #define GST_PLUGIN_NAME "vaapipostproc"
42 #define GST_PLUGIN_DESC "A video postprocessing filter"
44 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
45 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
47 /* ElementFactory information */
48 static const GstElementDetails gst_vaapipostproc_details =
50 "VA-API video postprocessing",
51 "Filter/Converter/Video",
53 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
55 /* Default templates */
56 static const char gst_vaapipostproc_sink_caps_str[] =
57 GST_VAAPI_SURFACE_CAPS ", "
58 "interlaced = (boolean) { true, false }";
60 static const char gst_vaapipostproc_src_caps_str[] =
61 GST_VAAPI_SURFACE_CAPS ", "
62 "interlaced = (boolean) false";
64 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
65 GST_STATIC_PAD_TEMPLATE(
69 GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
71 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
72 GST_STATIC_PAD_TEMPLATE(
76 GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
79 gst_vaapipostproc_implements_iface_init(GstImplementsInterfaceClass *iface);
82 gst_video_context_interface_init(GstVideoContextInterface *iface);
84 #define GstVideoContextClass GstVideoContextInterface
85 G_DEFINE_TYPE_WITH_CODE(
89 G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
90 gst_vaapipostproc_implements_iface_init);
91 G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
92 gst_video_context_interface_init));
97 PROP_DEINTERLACE_MODE,
98 PROP_DEINTERLACE_METHOD,
101 #define DEFAULT_DEINTERLACE_MODE GST_VAAPI_DEINTERLACE_MODE_AUTO
102 #define DEFAULT_DEINTERLACE_METHOD GST_VAAPI_DEINTERLACE_METHOD_BOB
104 #define GST_TYPE_VAAPI_DEINTERLACE_MODES \
105 gst_vaapi_deinterlace_modes_get_type()
108 gst_vaapi_deinterlace_modes_get_type(void)
110 static GType deinterlace_modes_type = 0;
112 static const GEnumValue modes_types[] = {
113 { GST_VAAPI_DEINTERLACE_MODE_AUTO,
114 "Auto detection", "auto" },
115 { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
116 "Force deinterlacing", "interlaced" },
117 { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
118 "Never deinterlace", "disabled" },
122 if (!deinterlace_modes_type) {
123 deinterlace_modes_type =
124 g_enum_register_static("GstVaapiDeinterlaceModes", modes_types);
126 return deinterlace_modes_type;
129 #define GST_TYPE_VAAPI_DEINTERLACE_METHODS \
130 gst_vaapi_deinterlace_methods_get_type()
133 gst_vaapi_deinterlace_methods_get_type(void)
135 static GType deinterlace_methods_type = 0;
137 static const GEnumValue methods_types[] = {
138 { GST_VAAPI_DEINTERLACE_METHOD_BOB,
139 "Bob deinterlacing", "bob" },
142 { GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
143 "Weave deinterlacing", "weave" },
144 { GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
145 "Motion adaptive deinterlacing", "motion-adaptive" },
146 { GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
147 "Motion compensated deinterlacing", "motion-compensated" },
152 if (!deinterlace_methods_type) {
153 deinterlace_methods_type =
154 g_enum_register_static("GstVaapiDeinterlaceMethods", methods_types);
156 return deinterlace_methods_type;
159 static inline GstVaapiPostproc *
160 get_vaapipostproc_from_pad(GstPad *pad)
162 return GST_VAAPIPOSTPROC(gst_pad_get_parent_element(pad));
165 /* GstImplementsInterface interface */
168 gst_vaapipostproc_implements_interface_supported(
169 GstImplementsInterface *iface,
173 return (type == GST_TYPE_VIDEO_CONTEXT);
177 gst_vaapipostproc_implements_iface_init(GstImplementsInterfaceClass *iface)
179 iface->supported = gst_vaapipostproc_implements_interface_supported;
182 /* GstVideoContext interface */
185 gst_vaapipostproc_set_video_context(
186 GstVideoContext *context,
191 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(context);
193 gst_vaapi_set_display(type, value, &postproc->display);
197 gst_video_context_interface_init(GstVideoContextInterface *iface)
199 iface->set_context = gst_vaapipostproc_set_video_context;
203 gst_vaapipostproc_create(GstVaapiPostproc *postproc, GstCaps *caps)
205 if (!gst_vaapi_ensure_display(postproc, &postproc->display, NULL))
208 gst_caps_replace(&postproc->postproc_caps, caps);
213 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
215 gst_caps_replace(&postproc->postproc_caps, NULL);
217 g_clear_object(&postproc->display);
221 gst_vaapipostproc_reset(GstVaapiPostproc *postproc, GstCaps *caps)
223 if (postproc->postproc_caps &&
224 gst_caps_is_always_compatible(caps, postproc->postproc_caps))
227 gst_vaapipostproc_destroy(postproc);
228 return gst_vaapipostproc_create(postproc, caps);
232 gst_vaapipostproc_start(GstVaapiPostproc *postproc)
234 if (!gst_vaapi_ensure_display(postproc, &postproc->display, NULL))
240 gst_vaapipostproc_stop(GstVaapiPostproc *postproc)
242 if (postproc->display) {
243 g_object_unref(postproc->display);
244 postproc->display = NULL;
250 gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf)
252 GstVaapiVideoBuffer *vbuf = GST_VAAPI_VIDEO_BUFFER(buf);
253 GstVaapiSurfaceProxy *proxy;
254 GstClockTime timestamp;
256 GstBuffer *outbuf = NULL;
257 guint outbuf_flags, flags;
258 gboolean interlaced, tff;
260 flags = gst_vaapi_video_buffer_get_render_flags(vbuf);
262 /* Deinterlacing disabled, push frame */
263 if (!postproc->deinterlace) {
264 gst_vaapi_video_buffer_set_render_flags(vbuf, flags);
265 ret = gst_pad_push(postproc->srcpad, buf);
266 if (ret != GST_FLOW_OK)
267 goto error_push_buffer;
271 timestamp = GST_BUFFER_TIMESTAMP(buf);
272 proxy = gst_vaapi_video_buffer_get_surface_proxy(vbuf);
273 interlaced = gst_vaapi_surface_proxy_get_interlaced(proxy);
274 tff = gst_vaapi_surface_proxy_get_tff(proxy);
276 flags &= ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
277 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
280 outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
282 goto error_create_buffer;
284 vbuf = GST_VAAPI_VIDEO_BUFFER(outbuf);
285 outbuf_flags = flags;
286 outbuf_flags |= interlaced ? (
288 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
289 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
290 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
291 gst_vaapi_video_buffer_set_render_flags(vbuf, outbuf_flags);
293 GST_BUFFER_TIMESTAMP(outbuf) = timestamp;
294 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
295 gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
296 ret = gst_pad_push(postproc->srcpad, outbuf);
297 if (ret != GST_FLOW_OK)
298 goto error_push_buffer;
301 outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
303 goto error_create_buffer;
305 vbuf = GST_VAAPI_VIDEO_BUFFER(outbuf);
306 outbuf_flags = flags;
307 outbuf_flags |= interlaced ? (
309 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
310 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
311 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
312 gst_vaapi_video_buffer_set_render_flags(vbuf, outbuf_flags);
314 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
315 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
316 gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
317 ret = gst_pad_push(postproc->srcpad, outbuf);
318 if (ret != GST_FLOW_OK)
319 goto error_push_buffer;
321 gst_buffer_unref(buf);
327 GST_ERROR("failed to create output buffer");
328 gst_buffer_unref(buf);
329 return GST_FLOW_UNEXPECTED;
333 if (ret != GST_FLOW_WRONG_STATE)
334 GST_ERROR("failed to push output buffer to video sink");
335 gst_buffer_unref(buf);
336 return GST_FLOW_UNEXPECTED;
341 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps)
346 if (!gst_video_parse_caps_framerate(caps, &fps_n, &fps_d))
348 postproc->fps_n = fps_n;
349 postproc->fps_d = fps_d;
351 switch (postproc->deinterlace_mode) {
352 case GST_VAAPI_DEINTERLACE_MODE_AUTO:
353 if (!gst_video_format_parse_caps_interlaced(caps, &interlaced))
355 postproc->deinterlace = interlaced;
357 case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
358 postproc->deinterlace = TRUE;
360 case GST_VAAPI_DEINTERLACE_MODE_DISABLED:
361 postproc->deinterlace = FALSE;
365 postproc->field_duration = gst_util_uint64_scale(
368 (1 + postproc->deinterlace) * postproc->fps_n
371 gst_caps_replace(&postproc->sinkpad_caps, caps);
376 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps)
379 GstStructure *structure;
380 const GValue *v_width, *v_height, *v_par;
383 if (postproc->srcpad_caps)
384 src_caps = gst_caps_make_writable(postproc->srcpad_caps);
386 src_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
389 postproc->srcpad_caps = src_caps;
391 structure = gst_caps_get_structure(caps, 0);
392 v_width = gst_structure_get_value(structure, "width");
393 v_height = gst_structure_get_value(structure, "height");
394 v_par = gst_structure_get_value(structure, "pixel-aspect-ratio");
396 structure = gst_caps_get_structure(src_caps, 0);
397 if (v_width && v_height) {
398 gst_structure_set_value(structure, "width", v_width);
399 gst_structure_set_value(structure, "height", v_height);
402 gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
404 gst_structure_set(structure, "type", G_TYPE_STRING, "vaapi", NULL);
405 gst_structure_set(structure, "opengl", G_TYPE_BOOLEAN, USE_GLX, NULL);
407 if (!postproc->deinterlace)
408 gst_structure_remove_field(structure, "interlaced");
410 /* Set double framerate in interlaced mode */
411 if (!gst_util_fraction_multiply(postproc->fps_n, postproc->fps_d,
418 "interlaced", G_TYPE_BOOLEAN, FALSE,
419 "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
423 return gst_pad_set_caps(postproc->srcpad, src_caps);
427 gst_vaapipostproc_ensure_allowed_caps(GstVaapiPostproc *postproc)
429 if (postproc->allowed_caps)
432 postproc->allowed_caps =
433 gst_caps_from_string(gst_vaapipostproc_sink_caps_str);
434 if (!postproc->allowed_caps)
437 /* XXX: append VA/VPP filters */
442 gst_vaapipostproc_get_caps(GstPad *pad)
444 GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
447 if (gst_vaapipostproc_ensure_allowed_caps(postproc))
448 out_caps = gst_caps_ref(postproc->allowed_caps);
450 out_caps = gst_caps_new_empty();
452 gst_object_unref(postproc);
457 gst_vaapipostproc_set_caps(GstPad *pad, GstCaps *caps)
459 GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
460 gboolean success = FALSE;
462 g_return_val_if_fail(pad == postproc->sinkpad, FALSE);
465 if (!gst_vaapipostproc_update_sink_caps(postproc, caps))
467 if (!gst_vaapipostproc_update_src_caps(postproc, caps))
469 if (!gst_vaapipostproc_reset(postproc, postproc->sinkpad_caps))
473 gst_object_unref(postproc);
478 gst_vaapipostproc_chain(GstPad *pad, GstBuffer *buf)
480 GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
483 ret = gst_vaapipostproc_process(postproc, buf);
484 gst_object_unref(postproc);
489 gst_vaapipostproc_sink_event(GstPad *pad, GstEvent *event)
491 GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
494 GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
496 /* Propagate event downstream */
497 success = gst_pad_push_event(postproc->srcpad, event);
498 gst_object_unref(postproc);
503 gst_vaapipostproc_src_event(GstPad *pad, GstEvent *event)
505 GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
508 GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
510 /* Propagate event upstream */
511 success = gst_pad_push_event(postproc->sinkpad, event);
512 gst_object_unref(postproc);
517 gst_vaapipostproc_query(GstPad *pad, GstQuery *query)
519 GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
522 GST_DEBUG("sharing display %p", postproc->display);
524 if (gst_vaapi_reply_to_query(query, postproc->display))
527 success = gst_pad_query_default(pad, query);
529 gst_object_unref(postproc);
534 gst_vaapipostproc_finalize(GObject *object)
536 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
538 gst_vaapipostproc_destroy(postproc);
540 gst_caps_replace(&postproc->sinkpad_caps, NULL);
541 gst_caps_replace(&postproc->srcpad_caps, NULL);
542 gst_caps_replace(&postproc->allowed_caps, NULL);
544 G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
548 gst_vaapipostproc_set_property(
555 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
558 case PROP_DEINTERLACE_MODE:
559 postproc->deinterlace_mode = g_value_get_enum(value);
561 case PROP_DEINTERLACE_METHOD:
562 postproc->deinterlace_method = g_value_get_enum(value);
565 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
571 gst_vaapipostproc_get_property(
578 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
581 case PROP_DEINTERLACE_MODE:
582 g_value_set_enum(value, postproc->deinterlace_mode);
584 case PROP_DEINTERLACE_METHOD:
585 g_value_set_enum(value, postproc->deinterlace_method);
588 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
593 static GstStateChangeReturn
594 gst_vaapipostproc_change_state(GstElement *element, GstStateChange transition)
596 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(element);
597 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
599 switch (transition) {
600 case GST_STATE_CHANGE_NULL_TO_READY:
601 if (!gst_vaapipostproc_start(postproc))
602 return GST_STATE_CHANGE_FAILURE;
604 case GST_STATE_CHANGE_READY_TO_PAUSED:
606 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
612 ret = GST_ELEMENT_CLASS(gst_vaapipostproc_parent_class)->change_state(element, transition);
613 if (ret != GST_STATE_CHANGE_SUCCESS)
616 switch (transition) {
617 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
619 case GST_STATE_CHANGE_PAUSED_TO_READY:
621 case GST_STATE_CHANGE_READY_TO_NULL:
622 if (!gst_vaapipostproc_stop(postproc))
623 return GST_STATE_CHANGE_FAILURE;
628 return GST_STATE_CHANGE_SUCCESS;
632 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
634 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
635 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
636 GstPadTemplate *pad_template;
638 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
639 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
641 object_class->finalize = gst_vaapipostproc_finalize;
642 object_class->set_property = gst_vaapipostproc_set_property;
643 object_class->get_property = gst_vaapipostproc_get_property;
645 element_class->change_state = gst_vaapipostproc_change_state;
647 gst_element_class_set_details_simple(
649 gst_vaapipostproc_details.longname,
650 gst_vaapipostproc_details.klass,
651 gst_vaapipostproc_details.description,
652 gst_vaapipostproc_details.author
656 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
657 gst_element_class_add_pad_template(element_class, pad_template);
658 gst_object_unref(pad_template);
661 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
662 gst_element_class_add_pad_template(element_class, pad_template);
663 gst_object_unref(pad_template);
666 * GstVaapiPostproc:deinterlace-mode:
668 * This selects whether the deinterlacing should always be applied or if
669 * they should only be applied on content that has the "interlaced" flag
672 g_object_class_install_property
674 PROP_DEINTERLACE_MODE,
675 g_param_spec_enum("deinterlace",
677 "Deinterlace mode to use",
678 GST_TYPE_VAAPI_DEINTERLACE_MODES,
679 DEFAULT_DEINTERLACE_MODE,
680 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
683 * GstVaapiPostproc:deinterlace-method:
685 * This selects the deinterlacing method to apply.
687 g_object_class_install_property
689 PROP_DEINTERLACE_METHOD,
690 g_param_spec_enum("deinterlace-method",
691 "Deinterlace method",
692 "Deinterlace method to use",
693 GST_TYPE_VAAPI_DEINTERLACE_METHODS,
694 DEFAULT_DEINTERLACE_METHOD,
695 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
699 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
701 GstVaapiPostprocClass *klass = GST_VAAPIPOSTPROC_GET_CLASS(postproc);
702 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
704 postproc->allowed_caps = NULL;
705 postproc->postproc_caps = NULL;
706 postproc->display = NULL;
707 postproc->surface_width = 0;
708 postproc->surface_height = 0;
709 postproc->deinterlace = FALSE;
710 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
711 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
712 postproc->field_duration = GST_CLOCK_TIME_NONE;
716 /* Pad through which data comes in to the element */
717 postproc->sinkpad = gst_pad_new_from_template(
718 gst_element_class_get_pad_template(element_class, "sink"),
721 postproc->sinkpad_caps = NULL;
723 gst_pad_set_getcaps_function(postproc->sinkpad, gst_vaapipostproc_get_caps);
724 gst_pad_set_setcaps_function(postproc->sinkpad, gst_vaapipostproc_set_caps);
725 gst_pad_set_chain_function(postproc->sinkpad, gst_vaapipostproc_chain);
726 gst_pad_set_event_function(postproc->sinkpad, gst_vaapipostproc_sink_event);
727 gst_pad_set_query_function(postproc->sinkpad, gst_vaapipostproc_query);
728 gst_element_add_pad(GST_ELEMENT(postproc), postproc->sinkpad);
730 /* Pad through which data goes out of the element */
731 postproc->srcpad = gst_pad_new_from_template(
732 gst_element_class_get_pad_template(element_class, "src"),
735 postproc->srcpad_caps = NULL;
737 gst_pad_set_event_function(postproc->srcpad, gst_vaapipostproc_src_event);
738 gst_pad_set_query_function(postproc->srcpad, gst_vaapipostproc_query);
739 gst_element_add_pad(GST_ELEMENT(postproc), postproc->srcpad);