2 * gstvaapipostproc.c - VA-API video postprocessing
4 * Copyright (C) 2012-2013 Intel Corporation
5 * Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1
10 * of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301 USA
24 * SECTION:gstvaapipostproc
25 * @short_description: A video postprocessing filter
27 * vaapipostproc consists in various postprocessing algorithms to be
28 * applied to VA surfaces. So far, only basic bob deinterlacing is
32 #include "gst/vaapi/sysdeps.h"
33 #include <gst/video/video.h>
35 #include "gstvaapipostproc.h"
36 #include "gstvaapipluginutil.h"
37 #include "gstvaapivideobuffer.h"
38 #if GST_CHECK_VERSION(1,0,0)
39 #include "gstvaapivideobufferpool.h"
40 #include "gstvaapivideomemory.h"
43 #define GST_PLUGIN_NAME "vaapipostproc"
44 #define GST_PLUGIN_DESC "A video postprocessing filter"
46 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
47 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
49 /* Default templates */
50 static const char gst_vaapipostproc_sink_caps_str[] =
51 #if GST_CHECK_VERSION(1,1,0)
52 GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
53 GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ", "
55 GST_VAAPI_SURFACE_CAPS ", "
57 GST_CAPS_INTERLACED_MODES "; "
58 #if GST_CHECK_VERSION(1,0,0)
59 GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
62 "width = " GST_VIDEO_SIZE_RANGE ", "
63 "height = " GST_VIDEO_SIZE_RANGE ", "
64 "framerate = " GST_VIDEO_FPS_RANGE ", "
66 GST_CAPS_INTERLACED_MODES;
68 static const char gst_vaapipostproc_src_caps_str[] =
69 #if GST_CHECK_VERSION(1,1,0)
70 GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
71 GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ", "
73 GST_VAAPI_SURFACE_CAPS ", "
75 GST_CAPS_INTERLACED_FALSE;
77 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
78 GST_STATIC_PAD_TEMPLATE(
82 GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
84 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
85 GST_STATIC_PAD_TEMPLATE(
89 GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
91 G_DEFINE_TYPE_WITH_CODE(
94 GST_TYPE_BASE_TRANSFORM,
95 GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
103 PROP_FORCE_ASPECT_RATIO,
104 PROP_DEINTERLACE_MODE,
105 PROP_DEINTERLACE_METHOD,
110 #define DEFAULT_FORMAT GST_VIDEO_FORMAT_ENCODED
111 #define DEFAULT_DEINTERLACE_MODE GST_VAAPI_DEINTERLACE_MODE_AUTO
112 #define DEFAULT_DEINTERLACE_METHOD GST_VAAPI_DEINTERLACE_METHOD_BOB
114 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
115 gst_vaapi_deinterlace_mode_get_type()
118 gst_vaapi_deinterlace_mode_get_type(void)
120 static GType deinterlace_mode_type = 0;
122 static const GEnumValue mode_types[] = {
123 { GST_VAAPI_DEINTERLACE_MODE_AUTO,
124 "Auto detection", "auto" },
125 { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
126 "Force deinterlacing", "interlaced" },
127 { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
128 "Never deinterlace", "disabled" },
132 if (!deinterlace_mode_type) {
133 deinterlace_mode_type =
134 g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
136 return deinterlace_mode_type;
140 ds_reset(GstVaapiDeinterlaceState *ds)
144 for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++)
145 gst_buffer_replace(&ds->buffers[i], NULL);
146 ds->buffers_index = 0;
147 ds->num_surfaces = 0;
153 ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
155 gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
156 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
159 static inline GstBuffer *
160 ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
162 /* Note: the index increases towards older buffers.
163 i.e. buffer at index 0 means the immediately preceding buffer
164 in the history, buffer at index 1 means the one preceding the
165 surface at index 0, etc. */
166 const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
167 return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
171 ds_set_surfaces(GstVaapiDeinterlaceState *ds)
173 GstVaapiVideoMeta *meta;
176 ds->num_surfaces = 0;
177 for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++) {
178 GstBuffer * const buf = ds_get_buffer(ds, i);
182 meta = gst_buffer_get_vaapi_video_meta(buf);
183 ds->surfaces[ds->num_surfaces++] =
184 gst_vaapi_video_meta_get_surface(meta);
188 static GstVaapiFilterOpInfo *
189 find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
194 for (i = 0; i < filter_ops->len; i++) {
195 GstVaapiFilterOpInfo * const filter_op =
196 g_ptr_array_index(filter_ops, i);
197 if (filter_op->op == op)
204 static inline gboolean
205 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
207 return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(postproc));
211 gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
213 if (!gst_vaapipostproc_ensure_display(postproc))
215 if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(postproc)))
221 gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
223 if (postproc->filter)
226 if (!gst_vaapipostproc_ensure_display(postproc))
229 postproc->filter = gst_vaapi_filter_new(
230 GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
231 if (!postproc->filter)
237 gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
239 if (!gst_vaapipostproc_ensure_filter(postproc))
242 postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
243 if (!postproc->filter_ops)
246 postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
247 if (!postproc->filter_formats)
253 gst_vaapipostproc_create(GstVaapiPostproc *postproc)
255 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
257 if (!gst_vaapipostproc_ensure_display(postproc))
259 if (!gst_vaapipostproc_ensure_uploader(postproc))
261 if (gst_vaapipostproc_ensure_filter(postproc))
262 postproc->use_vpp = TRUE;
267 gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
269 if (postproc->filter_formats) {
270 g_array_unref(postproc->filter_formats);
271 postproc->filter_formats = NULL;
274 if (postproc->filter_ops) {
275 g_ptr_array_unref(postproc->filter_ops);
276 postproc->filter_ops = NULL;
278 gst_vaapi_filter_replace(&postproc->filter, NULL);
279 gst_vaapi_video_pool_replace(&postproc->filter_pool, NULL);
283 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
285 ds_reset(&postproc->deinterlace_state);
286 gst_vaapipostproc_destroy_filter(postproc);
288 gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
289 gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
290 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
294 gst_vaapipostproc_start(GstBaseTransform *trans)
296 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
298 ds_reset(&postproc->deinterlace_state);
299 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
301 if (!gst_vaapipostproc_ensure_display(postproc))
307 gst_vaapipostproc_stop(GstBaseTransform *trans)
309 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
311 ds_reset(&postproc->deinterlace_state);
312 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
317 is_interlaced_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
319 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
322 switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
323 case GST_VIDEO_INTERLACE_MODE_MIXED:
324 #if GST_CHECK_VERSION(1,0,0)
325 if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
328 if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
339 create_output_buffer(GstVaapiPostproc *postproc)
343 /* Create a raw VA video buffer without GstVaapiVideoMeta attached
344 to it yet, as this will be done next in the transform() hook */
345 outbuf = gst_vaapi_video_buffer_new_empty();
347 goto error_create_buffer;
349 #if !GST_CHECK_VERSION(1,0,0)
350 gst_buffer_set_caps(outbuf, GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAPS(postproc));
357 GST_ERROR("failed to create output video buffer");
363 append_output_buffer_metadata(GstBuffer *outbuf, GstBuffer *inbuf, guint flags)
365 gst_buffer_copy_into(outbuf, inbuf, flags |
366 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY,
371 deint_method_is_advanced(GstVaapiDeinterlaceMethod deint_method)
373 gboolean is_advanced;
375 switch (deint_method) {
376 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
377 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
387 static GstVaapiDeinterlaceMethod
388 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
390 switch (deint_method) {
391 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
392 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
395 /* Default to basic "bob" for all others */
396 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
403 set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
404 GstVaapiDeinterlaceMethod *deint_method_ptr)
406 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
410 success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
411 deint_method, flags);
412 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
414 deint_method = get_next_deint_method(deint_method);
416 *deint_method_ptr = deint_method;
421 gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
424 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
425 GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
426 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
427 GstVaapiSurface *inbuf_surface, *outbuf_surface;
428 GstVaapiFilterStatus status;
429 GstClockTime timestamp;
432 GstVaapiDeinterlaceMethod deint_method;
433 guint flags, deint_flags;
434 gboolean tff, deint, deint_refs, deint_changed;
436 /* Validate filters */
437 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
438 !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
439 return GST_FLOW_NOT_SUPPORTED;
441 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
442 !gst_vaapi_filter_set_denoising_level(postproc->filter,
443 postproc->denoise_level))
444 return GST_FLOW_NOT_SUPPORTED;
446 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
447 !gst_vaapi_filter_set_denoising_level(postproc->filter,
448 postproc->sharpen_level))
449 return GST_FLOW_NOT_SUPPORTED;
451 inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
453 goto error_invalid_buffer;
454 inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
456 timestamp = GST_BUFFER_TIMESTAMP(inbuf);
457 tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
458 deint = is_interlaced_buffer(postproc, inbuf);
460 /* Drop references if deinterlacing conditions changed */
461 deint_changed = deint != ds->deint;
462 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
467 flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
468 ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
469 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
472 deint_method = postproc->deinterlace_method;
473 deint_refs = deint_method_is_advanced(deint_method);
474 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
475 fieldbuf = create_output_buffer(postproc);
477 goto error_create_buffer;
479 outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
481 goto error_create_meta;
482 outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
485 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
487 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
488 if (!set_best_deint_method(postproc, deint_flags, &deint_method))
489 goto error_op_deinterlace;
491 if (deint_method != postproc->deinterlace_method) {
492 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
493 postproc->deinterlace_method, deint_method);
494 postproc->deinterlace_method = deint_method;
495 deint_refs = deint_method_is_advanced(deint_method);
500 if (!gst_vaapi_filter_set_deinterlacing_references(
501 postproc->filter, ds->surfaces, ds->num_surfaces,
503 goto error_op_deinterlace;
506 else if (deint_changed) {
507 // Reset internal filter to non-deinterlacing mode
508 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
509 if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
511 goto error_op_deinterlace;
514 status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
515 outbuf_surface, flags);
516 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
517 goto error_process_vpp;
519 gst_buffer_set_vaapi_video_meta(fieldbuf, outbuf_meta);
520 gst_vaapi_video_meta_unref(outbuf_meta);
522 GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
523 GST_BUFFER_DURATION(fieldbuf) = postproc->field_duration;
524 ret = gst_pad_push(trans->srcpad, fieldbuf);
525 if (ret != GST_FLOW_OK)
526 goto error_push_buffer;
531 outbuf_meta = gst_vaapi_video_meta_new_from_pool(postproc->filter_pool);
533 goto error_create_meta;
534 outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
537 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
539 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
540 if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
541 deint_method, deint_flags))
542 goto error_op_deinterlace;
544 if (deint_refs && !gst_vaapi_filter_set_deinterlacing_references(
545 postproc->filter, ds->surfaces, ds->num_surfaces, NULL, 0))
546 goto error_op_deinterlace;
548 else if (deint_changed && !gst_vaapi_filter_set_deinterlacing(
549 postproc->filter, deint_method, 0))
550 goto error_op_deinterlace;
552 status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
553 outbuf_surface, flags);
554 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
555 goto error_process_vpp;
557 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
558 gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
560 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
561 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
563 gst_buffer_set_vaapi_video_meta(outbuf, outbuf_meta);
564 gst_vaapi_video_meta_unref(outbuf_meta);
566 if (deint && deint_refs)
567 ds_add_buffer(ds, inbuf);
571 error_invalid_buffer:
573 GST_ERROR("failed to validate source buffer");
574 return GST_FLOW_ERROR;
578 GST_ERROR("failed to create output buffer");
579 return GST_FLOW_ERROR;
583 GST_ERROR("failed to create new output buffer meta");
584 gst_buffer_replace(&fieldbuf, NULL);
585 gst_vaapi_video_meta_unref(outbuf_meta);
586 return GST_FLOW_ERROR;
588 error_op_deinterlace:
590 GST_ERROR("failed to apply deinterlacing filter");
591 gst_buffer_replace(&fieldbuf, NULL);
592 gst_vaapi_video_meta_unref(outbuf_meta);
593 return GST_FLOW_NOT_SUPPORTED;
597 GST_ERROR("failed to apply VPP filters (error %d)", status);
598 gst_buffer_replace(&fieldbuf, NULL);
599 gst_vaapi_video_meta_unref(outbuf_meta);
600 return GST_FLOW_ERROR;
604 if (ret != GST_FLOW_FLUSHING)
605 GST_ERROR("failed to push output buffer to video sink");
606 return GST_FLOW_ERROR;
611 gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
614 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
615 GstVaapiVideoMeta *meta;
616 GstClockTime timestamp;
619 guint fieldbuf_flags, outbuf_flags, flags;
622 meta = gst_buffer_get_vaapi_video_meta(inbuf);
624 goto error_invalid_buffer;
626 timestamp = GST_BUFFER_TIMESTAMP(inbuf);
627 tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
628 deint = is_interlaced_buffer(postproc, inbuf);
630 flags = gst_vaapi_video_meta_get_render_flags(meta) &
631 ~(GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD|
632 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD);
635 fieldbuf = create_output_buffer(postproc);
637 goto error_create_buffer;
638 append_output_buffer_metadata(fieldbuf, inbuf, 0);
640 meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
641 fieldbuf_flags = flags;
642 fieldbuf_flags |= deint ? (
644 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
645 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
646 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
647 gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
649 GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
650 GST_BUFFER_DURATION(fieldbuf) = postproc->field_duration;
651 ret = gst_pad_push(trans->srcpad, fieldbuf);
652 if (ret != GST_FLOW_OK)
653 goto error_push_buffer;
656 append_output_buffer_metadata(outbuf, inbuf, 0);
658 meta = gst_buffer_get_vaapi_video_meta(outbuf);
659 outbuf_flags = flags;
660 outbuf_flags |= deint ? (
662 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
663 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
664 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
665 gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
667 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
668 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
672 error_invalid_buffer:
674 GST_ERROR("failed to validate source buffer");
675 return GST_FLOW_ERROR;
679 GST_ERROR("failed to create output buffer");
684 if (ret != GST_FLOW_FLUSHING)
685 GST_ERROR("failed to push output buffer to video sink");
691 gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
694 GstVaapiVideoMeta *meta;
696 /* No video processing needed, simply copy buffer metadata */
697 meta = gst_buffer_get_vaapi_video_meta(inbuf);
699 goto error_invalid_buffer;
701 append_output_buffer_metadata(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
705 error_invalid_buffer:
707 GST_ERROR("failed to validate source buffer");
708 return GST_FLOW_ERROR;
713 is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
715 gboolean deinterlace;
717 switch (postproc->deinterlace_mode) {
718 case GST_VAAPI_DEINTERLACE_MODE_AUTO:
719 deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
721 case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
732 video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
734 if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
736 if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
737 GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
739 if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
741 if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
747 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
748 gboolean *caps_changed_ptr)
751 gboolean deinterlace;
753 if (!gst_video_info_from_caps(&vi, caps))
756 if (video_info_changed(&vi, &postproc->sinkpad_info))
757 postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
759 deinterlace = is_deinterlace_enabled(postproc, &vi);
761 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
762 postproc->field_duration = gst_util_uint64_scale(
763 GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
764 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi));
766 postproc->is_raw_yuv = GST_VIDEO_INFO_IS_YUV(&vi);
771 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
772 gboolean *caps_changed_ptr)
776 if (!gst_video_info_from_caps(&vi, caps))
779 if (video_info_changed(&vi, &postproc->srcpad_info))
780 postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
782 if (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info))
783 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
785 if ((postproc->width || postproc->height) &&
786 postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
787 postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
788 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
793 ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
795 GstCaps *out_caps, *yuv_caps;
797 if (postproc->allowed_sinkpad_caps)
801 #if GST_CHECK_VERSION(1,1,0)
802 out_caps = gst_static_pad_template_get_caps(
803 &gst_vaapipostproc_sink_factory);
805 out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS ", "
806 GST_CAPS_INTERLACED_MODES);
809 GST_ERROR("failed to create VA sink caps");
813 /* Append YUV caps */
814 if (gst_vaapipostproc_ensure_uploader(postproc)) {
815 yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(postproc);
817 out_caps = gst_caps_make_writable(out_caps);
818 gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
821 GST_WARNING("failed to create YUV sink caps");
823 postproc->allowed_sinkpad_caps = out_caps;
825 /* XXX: append VA/VPP filters */
829 /* Fixup output caps so that to reflect the supported set of pixel formats */
831 expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
833 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
834 guint i, num_structures;
837 had_filter = postproc->filter != NULL;
838 if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
840 if (!gst_vaapipostproc_ensure_filter_caps(postproc))
843 /* Reset "format" field for each structure */
844 if (!gst_vaapi_value_set_format_list(&value, postproc->filter_formats))
846 if (gst_vaapi_value_set_format(&v_format, GST_VIDEO_FORMAT_ENCODED)) {
847 gst_value_list_prepend_value(&value, &v_format);
848 g_value_unset(&v_format);
851 num_structures = gst_caps_get_size(caps);
852 for (i = 0; i < num_structures; i++) {
853 GstStructure * const structure = gst_caps_get_structure(caps, i);
856 gst_structure_set_value(structure, "format", &value);
858 g_value_unset(&value);
862 gst_vaapipostproc_destroy_filter(postproc);
867 ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
871 if (postproc->allowed_srcpad_caps)
874 /* Create initial caps from pad template */
875 out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
877 GST_ERROR("failed to create VA src caps");
881 postproc->allowed_srcpad_caps =
882 expand_allowed_srcpad_caps(postproc, out_caps);
883 return postproc->allowed_srcpad_caps != NULL;
887 find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
888 guint *width_ptr, guint *height_ptr)
892 width = GST_VIDEO_INFO_WIDTH(vip);
893 height = GST_VIDEO_INFO_HEIGHT(vip);
894 if (postproc->width && postproc->height) {
895 width = postproc->width;
896 height = postproc->height;
898 else if (postproc->keep_aspect) {
899 const gdouble ratio = (gdouble)width / height;
900 if (postproc->width) {
901 width = postproc->width;
902 height = postproc->width / ratio;
904 else if (postproc->height) {
905 height = postproc->height;
906 width = postproc->height * ratio;
909 else if (postproc->width)
910 width = postproc->width;
911 else if (postproc->height)
912 height = postproc->height;
915 *height_ptr = height;
919 gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
920 GstPadDirection direction, GstCaps *caps)
922 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
924 GstVideoFormat format;
928 /* Generate the sink pad caps, that could be fixated afterwards */
929 if (direction == GST_PAD_SRC) {
930 if (!ensure_allowed_sinkpad_caps(postproc))
932 return gst_caps_ref(postproc->allowed_sinkpad_caps);
935 /* Generate complete set of src pad caps if non-fixated sink pad
937 if (!gst_caps_is_fixed(caps)) {
938 if (!ensure_allowed_srcpad_caps(postproc))
940 return gst_caps_ref(postproc->allowed_srcpad_caps);
943 /* Generate the expected src pad caps, from the current fixated
945 if (!gst_video_info_from_caps(&vi, caps))
948 // Set double framerate in interlaced mode
949 if (is_deinterlace_enabled(postproc, &vi)) {
950 gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
951 gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
952 if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
954 GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
955 GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
958 // Signal the other pad that we only generate progressive frames
959 GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
961 // Update size from user-specified parameters
962 #if GST_CHECK_VERSION(1,1,0)
963 format = postproc->format;
965 format = GST_VIDEO_FORMAT_ENCODED;
967 find_best_size(postproc, &vi, &width, &height);
968 gst_video_info_set_format(&vi, format, width, height);
970 #if GST_CHECK_VERSION(1,1,0)
971 out_caps = gst_video_info_to_caps(&vi);
975 gst_caps_set_features(out_caps, 0,
976 gst_caps_features_new(GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, NULL));
978 /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
979 reconstruct suitable caps for "encoded" video formats */
980 out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
984 gst_caps_set_simple(out_caps,
985 "type", G_TYPE_STRING, "vaapi",
986 "opengl", G_TYPE_BOOLEAN, USE_GLX,
987 "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
988 "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
989 "framerate", GST_TYPE_FRACTION, GST_VIDEO_INFO_FPS_N(&vi),
990 GST_VIDEO_INFO_FPS_D(&vi),
991 "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
992 GST_VIDEO_INFO_PAR_D(&vi),
995 gst_caps_set_interlaced(out_caps, &vi);
1000 #if GST_CHECK_VERSION(1,0,0)
1002 gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
1003 GstPadDirection direction, GstCaps *caps, GstCaps *filter)
1007 caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
1008 if (caps && filter) {
1009 out_caps = gst_caps_intersect_full(caps, filter,
1010 GST_CAPS_INTERSECT_FIRST);
1011 gst_caps_unref(caps);
1017 #define gst_vaapipostproc_transform_caps \
1018 gst_vaapipostproc_transform_caps_impl
1021 #if GST_CHECK_VERSION(1,0,0)
1022 typedef gsize GstBaseTransformSizeType;
1024 typedef guint GstBaseTransformSizeType;
1028 gst_vaapipostproc_transform_size(GstBaseTransform *trans,
1029 GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
1030 GstCaps *othercaps, GstBaseTransformSizeType *othersize)
1032 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1034 if (direction == GST_PAD_SINK || !postproc->is_raw_yuv)
1041 static GstFlowReturn
1042 gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
1045 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1049 ret = gst_vaapi_plugin_base_get_input_buffer(
1050 GST_VAAPI_PLUGIN_BASE(postproc), inbuf, &buf);
1051 if (ret != GST_FLOW_OK)
1052 return GST_FLOW_ERROR;
1054 ret = GST_FLOW_NOT_SUPPORTED;
1055 if (postproc->flags) {
1056 /* Use VA/VPP extensions to process this frame */
1057 if (postproc->use_vpp &&
1058 postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1059 ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
1060 if (ret != GST_FLOW_NOT_SUPPORTED)
1062 GST_WARNING("unsupported VPP filters. Disabling");
1063 postproc->use_vpp = FALSE;
1066 /* Only append picture structure meta data (top/bottom field) */
1067 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1068 ret = gst_vaapipostproc_process(trans, buf, outbuf);
1069 if (ret != GST_FLOW_NOT_SUPPORTED)
1074 /* Fallback: passthrough to the downstream element as is */
1075 ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
1078 gst_buffer_unref(buf);
1082 static GstFlowReturn
1083 gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
1085 #if !GST_CHECK_VERSION(1,0,0)
1086 gint size, GstCaps *caps,
1088 GstBuffer **outbuf_ptr)
1090 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1092 *outbuf_ptr = create_output_buffer(postproc);
1093 return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1097 ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1100 GstVaapiVideoPool *pool;
1102 gst_video_info_init(&vi);
1103 gst_video_info_from_caps(&vi, caps);
1104 gst_video_info_set_format(&vi, postproc->format,
1105 GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1107 if (!video_info_changed(&vi, &postproc->filter_pool_info))
1109 postproc->filter_pool_info = vi;
1111 pool = gst_vaapi_surface_pool_new(GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc),
1112 &postproc->filter_pool_info);
1116 gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
1117 gst_vaapi_video_pool_unref(pool);
1122 gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
1125 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1126 gboolean caps_changed = FALSE;
1128 if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
1130 if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
1134 gst_vaapipostproc_destroy(postproc);
1135 if (!gst_vaapipostproc_create(postproc))
1137 if (!gst_vaapi_plugin_base_set_caps(GST_VAAPI_PLUGIN_BASE(trans),
1142 if (!ensure_srcpad_buffer_pool(postproc, out_caps))
1148 gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
1151 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1153 GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
1155 if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc))) {
1156 GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
1160 return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
1161 trans, direction, query);
1164 #if GST_CHECK_VERSION(1,0,0)
1166 gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
1167 GstQuery *decide_query, GstQuery *query)
1169 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1170 GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(trans);
1172 /* Let vaapidecode allocate the video buffers */
1173 if (!postproc->is_raw_yuv)
1175 if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1182 gst_vaapipostproc_finalize(GObject *object)
1184 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1186 gst_vaapipostproc_destroy(postproc);
1188 gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(postproc));
1189 G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
1193 gst_vaapipostproc_set_property(
1196 const GValue *value,
1200 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1204 postproc->format = g_value_get_enum(value);
1207 postproc->width = g_value_get_uint(value);
1210 postproc->height = g_value_get_uint(value);
1212 case PROP_FORCE_ASPECT_RATIO:
1213 postproc->keep_aspect = g_value_get_boolean(value);
1215 case PROP_DEINTERLACE_MODE:
1216 postproc->deinterlace_mode = g_value_get_enum(value);
1218 case PROP_DEINTERLACE_METHOD:
1219 postproc->deinterlace_method = g_value_get_enum(value);
1222 postproc->denoise_level = g_value_get_float(value);
1223 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1226 postproc->sharpen_level = g_value_get_float(value);
1227 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1230 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1236 gst_vaapipostproc_get_property(
1243 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1247 g_value_set_enum(value, postproc->format);
1250 g_value_set_uint(value, postproc->width);
1253 g_value_set_uint(value, postproc->height);
1255 case PROP_FORCE_ASPECT_RATIO:
1256 g_value_set_boolean(value, postproc->keep_aspect);
1258 case PROP_DEINTERLACE_MODE:
1259 g_value_set_enum(value, postproc->deinterlace_mode);
1261 case PROP_DEINTERLACE_METHOD:
1262 g_value_set_enum(value, postproc->deinterlace_method);
1265 g_value_set_float(value, postproc->denoise_level);
1268 g_value_set_float(value, postproc->sharpen_level);
1271 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1277 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
1279 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1280 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1281 GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
1282 GstPadTemplate *pad_template;
1283 GPtrArray *filter_ops;
1284 GstVaapiFilterOpInfo *filter_op;
1286 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
1287 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1289 gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(klass));
1291 object_class->finalize = gst_vaapipostproc_finalize;
1292 object_class->set_property = gst_vaapipostproc_set_property;
1293 object_class->get_property = gst_vaapipostproc_get_property;
1294 trans_class->start = gst_vaapipostproc_start;
1295 trans_class->stop = gst_vaapipostproc_stop;
1296 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1297 trans_class->transform_size = gst_vaapipostproc_transform_size;
1298 trans_class->transform = gst_vaapipostproc_transform;
1299 trans_class->set_caps = gst_vaapipostproc_set_caps;
1300 trans_class->query = gst_vaapipostproc_query;
1302 #if GST_CHECK_VERSION(1,0,0)
1303 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1306 trans_class->prepare_output_buffer =
1307 gst_vaapipostproc_prepare_output_buffer;
1309 gst_element_class_set_static_metadata(element_class,
1310 "VA-API video postprocessing",
1311 "Filter/Converter/Video",
1313 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1316 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
1317 gst_element_class_add_pad_template(element_class, pad_template);
1320 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
1321 gst_element_class_add_pad_template(element_class, pad_template);
1324 * GstVaapiPostproc:deinterlace-mode:
1326 * This selects whether the deinterlacing should always be applied or if
1327 * they should only be applied on content that has the "interlaced" flag
1330 g_object_class_install_property
1332 PROP_DEINTERLACE_MODE,
1333 g_param_spec_enum("deinterlace-mode",
1335 "Deinterlace mode to use",
1336 GST_VAAPI_TYPE_DEINTERLACE_MODE,
1337 DEFAULT_DEINTERLACE_MODE,
1338 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1341 * GstVaapiPostproc:deinterlace-method:
1343 * This selects the deinterlacing method to apply.
1345 g_object_class_install_property
1347 PROP_DEINTERLACE_METHOD,
1348 g_param_spec_enum("deinterlace-method",
1349 "Deinterlace method",
1350 "Deinterlace method to use",
1351 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1352 DEFAULT_DEINTERLACE_METHOD,
1353 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1355 filter_ops = gst_vaapi_filter_get_operations(NULL);
1360 * GstVaapiPostproc:format:
1362 * The forced output pixel format, expressed as a #GstVideoFormat.
1364 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1366 g_object_class_install_property(object_class,
1367 PROP_FORMAT, filter_op->pspec);
1370 * GstVaapiPostproc:width:
1372 * The forced output width in pixels. If set to zero, the width is
1373 * calculated from the height if aspect ration is preserved, or
1374 * inherited from the sink caps width
1376 g_object_class_install_property
1379 g_param_spec_uint("width",
1381 "Forced output width",
1383 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1386 * GstVaapiPostproc:height:
1388 * The forced output height in pixels. If set to zero, the height
1389 * is calculated from the width if aspect ration is preserved, or
1390 * inherited from the sink caps height
1392 g_object_class_install_property
1395 g_param_spec_uint("height",
1397 "Forced output height",
1399 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1402 * GstVaapiPostproc:force-aspect-ratio:
1404 * When enabled, scaling respects video aspect ratio; when
1405 * disabled, the video is distorted to fit the width and height
1408 g_object_class_install_property
1410 PROP_FORCE_ASPECT_RATIO,
1411 g_param_spec_boolean("force-aspect-ratio",
1412 "Force aspect ratio",
1413 "When enabled, scaling will respect original aspect ratio",
1415 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1418 * GstVaapiPostproc:denoise:
1420 * The level of noise reduction to apply.
1422 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
1424 g_object_class_install_property(object_class,
1425 PROP_DENOISE, filter_op->pspec);
1428 * GstVaapiPostproc:sharpen:
1430 * The level of sharpening to apply for positive values, or the
1431 * level of blurring for negative values.
1433 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
1435 g_object_class_install_property(object_class,
1436 PROP_SHARPEN, filter_op->pspec);
1438 g_ptr_array_unref(filter_ops);
1442 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
1444 gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(postproc), GST_CAT_DEFAULT);
1446 postproc->format = DEFAULT_FORMAT;
1447 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
1448 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
1449 postproc->field_duration = GST_CLOCK_TIME_NONE;
1450 postproc->keep_aspect = TRUE;
1452 gst_video_info_init(&postproc->sinkpad_info);
1453 gst_video_info_init(&postproc->srcpad_info);
1454 gst_video_info_init(&postproc->filter_pool_info);