2 * gstvaapipostproc.c - VA-API video postprocessing
4 * Copyright (C) 2012-2014 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 #if GST_CHECK_VERSION(1,1,0)
50 # define GST_VAAPIPOSTPROC_SURFACE_CAPS \
51 GST_VIDEO_CAPS_MAKE_WITH_FEATURES( \
52 GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, I420, YV12, NV12 }")
54 # define GST_VAAPIPOSTPROC_SURFACE_CAPS \
55 GST_VAAPI_SURFACE_CAPS
58 /* Default templates */
59 static const char gst_vaapipostproc_sink_caps_str[] =
60 GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
61 GST_CAPS_INTERLACED_MODES "; "
62 #if GST_CHECK_VERSION(1,0,0)
63 GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
66 "width = " GST_VIDEO_SIZE_RANGE ", "
67 "height = " GST_VIDEO_SIZE_RANGE ", "
68 "framerate = " GST_VIDEO_FPS_RANGE ", "
70 GST_CAPS_INTERLACED_MODES;
72 static const char gst_vaapipostproc_src_caps_str[] =
73 GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
74 GST_CAPS_INTERLACED_FALSE "; "
75 #if GST_CHECK_VERSION(1,0,0)
76 GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
79 "width = " GST_VIDEO_SIZE_RANGE ", "
80 "height = " GST_VIDEO_SIZE_RANGE ", "
81 "framerate = " GST_VIDEO_FPS_RANGE ", "
83 GST_CAPS_INTERLACED_FALSE;
85 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
86 GST_STATIC_PAD_TEMPLATE(
90 GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
92 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
93 GST_STATIC_PAD_TEMPLATE(
97 GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
99 G_DEFINE_TYPE_WITH_CODE(
102 GST_TYPE_BASE_TRANSFORM,
103 GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
111 PROP_FORCE_ASPECT_RATIO,
112 PROP_DEINTERLACE_MODE,
113 PROP_DEINTERLACE_METHOD,
122 #define DEFAULT_FORMAT GST_VIDEO_FORMAT_ENCODED
123 #define DEFAULT_DEINTERLACE_MODE GST_VAAPI_DEINTERLACE_MODE_AUTO
124 #define DEFAULT_DEINTERLACE_METHOD GST_VAAPI_DEINTERLACE_METHOD_BOB
126 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
127 gst_vaapi_deinterlace_mode_get_type()
130 gst_vaapi_deinterlace_mode_get_type(void)
132 static GType deinterlace_mode_type = 0;
134 static const GEnumValue mode_types[] = {
135 { GST_VAAPI_DEINTERLACE_MODE_AUTO,
136 "Auto detection", "auto" },
137 { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
138 "Force deinterlacing", "interlaced" },
139 { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
140 "Never deinterlace", "disabled" },
144 if (!deinterlace_mode_type) {
145 deinterlace_mode_type =
146 g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
148 return deinterlace_mode_type;
152 ds_reset(GstVaapiDeinterlaceState *ds)
156 for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++)
157 gst_buffer_replace(&ds->buffers[i], NULL);
158 ds->buffers_index = 0;
159 ds->num_surfaces = 0;
165 ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
167 gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
168 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
171 static inline GstBuffer *
172 ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
174 /* Note: the index increases towards older buffers.
175 i.e. buffer at index 0 means the immediately preceding buffer
176 in the history, buffer at index 1 means the one preceding the
177 surface at index 0, etc. */
178 const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
179 return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
183 ds_set_surfaces(GstVaapiDeinterlaceState *ds)
185 GstVaapiVideoMeta *meta;
188 ds->num_surfaces = 0;
189 for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++) {
190 GstBuffer * const buf = ds_get_buffer(ds, i);
194 meta = gst_buffer_get_vaapi_video_meta(buf);
195 ds->surfaces[ds->num_surfaces++] =
196 gst_vaapi_video_meta_get_surface(meta);
200 static GstVaapiFilterOpInfo *
201 find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
206 for (i = 0; i < filter_ops->len; i++) {
207 GstVaapiFilterOpInfo * const filter_op =
208 g_ptr_array_index(filter_ops, i);
209 if (filter_op->op == op)
216 static inline gboolean
217 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
219 return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(postproc));
223 gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
225 if (!gst_vaapipostproc_ensure_display(postproc))
227 if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(postproc)))
233 gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
235 if (postproc->filter)
238 if (!gst_vaapipostproc_ensure_display(postproc))
241 postproc->filter = gst_vaapi_filter_new(
242 GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
243 if (!postproc->filter)
249 gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
251 if (!gst_vaapipostproc_ensure_filter(postproc))
254 postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
255 if (!postproc->filter_ops)
258 postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
259 if (!postproc->filter_formats)
265 gst_vaapipostproc_create(GstVaapiPostproc *postproc)
267 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
269 if (!gst_vaapipostproc_ensure_display(postproc))
271 if (!gst_vaapipostproc_ensure_uploader(postproc))
274 postproc->use_vpp = FALSE;
275 postproc->has_vpp = gst_vaapipostproc_ensure_filter(postproc);
280 gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
282 if (postproc->filter_formats) {
283 g_array_unref(postproc->filter_formats);
284 postproc->filter_formats = NULL;
287 if (postproc->filter_ops) {
288 g_ptr_array_unref(postproc->filter_ops);
289 postproc->filter_ops = NULL;
291 gst_vaapi_filter_replace(&postproc->filter, NULL);
292 gst_vaapi_video_pool_replace(&postproc->filter_pool, NULL);
293 postproc->filter_pool_active = FALSE;
297 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
299 ds_reset(&postproc->deinterlace_state);
300 gst_vaapipostproc_destroy_filter(postproc);
302 gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
303 gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
304 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
308 gst_vaapipostproc_start(GstBaseTransform *trans)
310 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
312 ds_reset(&postproc->deinterlace_state);
313 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
315 if (!gst_vaapipostproc_ensure_display(postproc))
321 gst_vaapipostproc_stop(GstBaseTransform *trans)
323 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
325 ds_reset(&postproc->deinterlace_state);
326 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
327 postproc->filter_pool_active = FALSE;
332 should_deinterlace_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
334 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
335 postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
338 if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
341 g_assert(postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
343 switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
344 case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
346 case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
348 case GST_VIDEO_INTERLACE_MODE_MIXED:
349 #if GST_CHECK_VERSION(1,0,0)
350 if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
353 if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
358 GST_ERROR("unhandled \"interlace-mode\", disabling deinterlacing" );
365 create_output_buffer(GstVaapiPostproc *postproc)
369 #if GST_CHECK_VERSION(1,0,0)
370 GstBufferPool * const pool =
371 GST_VAAPI_PLUGIN_BASE(postproc)->srcpad_buffer_pool;
374 g_return_val_if_fail(pool != NULL, NULL);
376 if (!postproc->filter_pool_active) {
377 if (!gst_buffer_pool_set_active(pool, TRUE))
378 goto error_activate_pool;
379 postproc->filter_pool_active = TRUE;
383 ret = gst_buffer_pool_acquire_buffer(pool, &outbuf, NULL);
384 if (ret != GST_FLOW_OK || !outbuf)
385 goto error_create_buffer;
387 /* Create a raw VA video buffer without GstVaapiVideoMeta attached
388 to it yet, as this will be done next in the transform() hook */
389 outbuf = gst_vaapi_video_buffer_new_empty();
391 goto error_create_buffer;
393 gst_buffer_set_caps(outbuf, GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAPS(postproc));
398 #if GST_CHECK_VERSION(1,0,0)
401 GST_ERROR("failed to activate output video buffer pool");
407 GST_ERROR("failed to create output video buffer");
413 append_output_buffer_metadata(GstVaapiPostproc *postproc, GstBuffer *outbuf,
414 GstBuffer *inbuf, guint flags)
416 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
417 GstVaapiSurfaceProxy *proxy;
419 gst_buffer_copy_into(outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
421 /* GstVideoCropMeta */
422 #if GST_CHECK_VERSION(1,0,0)
423 if (!postproc->use_vpp) {
424 GstVideoCropMeta * const crop_meta =
425 gst_buffer_get_video_crop_meta(inbuf);
427 GstVideoCropMeta * const out_crop_meta =
428 gst_buffer_add_video_crop_meta(outbuf);
430 *out_crop_meta = *crop_meta;
435 /* GstVaapiVideoMeta */
436 inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
437 g_return_val_if_fail(inbuf_meta != NULL, FALSE);
438 proxy = gst_vaapi_video_meta_get_surface_proxy(inbuf_meta);
440 outbuf_meta = gst_buffer_get_vaapi_video_meta(outbuf);
441 g_return_val_if_fail(outbuf_meta != NULL, FALSE);
442 proxy = gst_vaapi_surface_proxy_copy(proxy);
446 gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
447 gst_vaapi_surface_proxy_unref(proxy);
452 deint_method_is_advanced(GstVaapiDeinterlaceMethod deint_method)
454 gboolean is_advanced;
456 switch (deint_method) {
457 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
458 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
468 static GstVaapiDeinterlaceMethod
469 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
471 switch (deint_method) {
472 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
473 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
476 /* Default to basic "bob" for all others */
477 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
484 set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
485 GstVaapiDeinterlaceMethod *deint_method_ptr)
487 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
491 success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
492 deint_method, flags);
493 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
495 deint_method = get_next_deint_method(deint_method);
497 *deint_method_ptr = deint_method;
502 gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
505 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
506 GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
507 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
508 GstVaapiSurface *inbuf_surface, *outbuf_surface;
509 GstVaapiSurfaceProxy *proxy;
510 GstVaapiFilterStatus status;
511 GstClockTime timestamp;
514 GstVaapiDeinterlaceMethod deint_method;
515 guint flags, deint_flags;
516 gboolean tff, deint, deint_refs, deint_changed;
517 GstVaapiRectangle *crop_rect = NULL;
518 #if GST_CHECK_VERSION(1,0,0)
519 GstVaapiRectangle tmp_rect;
522 /* Validate filters */
523 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
524 !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
525 return GST_FLOW_NOT_SUPPORTED;
527 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
528 !gst_vaapi_filter_set_denoising_level(postproc->filter,
529 postproc->denoise_level))
530 return GST_FLOW_NOT_SUPPORTED;
532 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
533 !gst_vaapi_filter_set_sharpening_level(postproc->filter,
534 postproc->sharpen_level))
535 return GST_FLOW_NOT_SUPPORTED;
537 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) &&
538 !gst_vaapi_filter_set_hue(postproc->filter,
540 return GST_FLOW_NOT_SUPPORTED;
542 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) &&
543 !gst_vaapi_filter_set_saturation(postproc->filter,
544 postproc->saturation))
545 return GST_FLOW_NOT_SUPPORTED;
547 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) &&
548 !gst_vaapi_filter_set_brightness(postproc->filter,
549 postproc->brightness))
550 return GST_FLOW_NOT_SUPPORTED;
552 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) &&
553 !gst_vaapi_filter_set_contrast(postproc->filter,
555 return GST_FLOW_NOT_SUPPORTED;
557 inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
559 goto error_invalid_buffer;
560 inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
562 #if GST_CHECK_VERSION(1,0,0)
563 GstVideoCropMeta * const crop_meta =
564 gst_buffer_get_video_crop_meta(inbuf);
566 crop_rect = &tmp_rect;
567 crop_rect->x = crop_meta->x;
568 crop_rect->y = crop_meta->y;
569 crop_rect->width = crop_meta->width;
570 crop_rect->height = crop_meta->height;
574 crop_rect = (GstVaapiRectangle *)
575 gst_vaapi_video_meta_get_render_rect(inbuf_meta);
577 timestamp = GST_BUFFER_TIMESTAMP(inbuf);
578 tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
579 deint = should_deinterlace_buffer(postproc, inbuf);
581 /* Drop references if deinterlacing conditions changed */
582 deint_changed = deint != ds->deint;
583 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
586 deint_method = postproc->deinterlace_method;
587 deint_refs = deint_method_is_advanced(deint_method);
588 if (deint_refs && 0) {
589 GstBuffer * const prev_buf = ds_get_buffer(ds, 0);
590 GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP(inbuf);
591 /* Reset deinterlacing state when there is a discontinuity */
592 if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP(prev_buf)) != pts) {
593 const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF(prev_pts, pts);
594 if (pts_diff < 0 || (postproc->field_duration > 0 &&
595 pts_diff >= postproc->field_duration * 3 - 1))
603 flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
604 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
607 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
608 fieldbuf = create_output_buffer(postproc);
610 goto error_create_buffer;
612 outbuf_meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
614 goto error_create_meta;
616 proxy = gst_vaapi_surface_proxy_new_from_pool(
617 GST_VAAPI_SURFACE_POOL(postproc->filter_pool));
619 goto error_create_proxy;
620 gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
621 gst_vaapi_surface_proxy_unref(proxy);
624 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
626 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
627 if (!set_best_deint_method(postproc, deint_flags, &deint_method))
628 goto error_op_deinterlace;
630 if (deint_method != postproc->deinterlace_method) {
631 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
632 postproc->deinterlace_method, deint_method);
633 postproc->deinterlace_method = deint_method;
634 deint_refs = deint_method_is_advanced(deint_method);
639 if (!gst_vaapi_filter_set_deinterlacing_references(
640 postproc->filter, ds->surfaces, ds->num_surfaces,
642 goto error_op_deinterlace;
645 else if (deint_changed) {
646 // Reset internal filter to non-deinterlacing mode
647 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
648 if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
650 goto error_op_deinterlace;
653 outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
654 gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
655 status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
656 outbuf_surface, flags);
657 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
658 goto error_process_vpp;
660 GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
661 GST_BUFFER_DURATION(fieldbuf) = postproc->field_duration;
662 ret = gst_pad_push(trans->srcpad, fieldbuf);
663 if (ret != GST_FLOW_OK)
664 goto error_push_buffer;
669 outbuf_meta = gst_buffer_get_vaapi_video_meta(outbuf);
671 goto error_create_meta;
673 proxy = gst_vaapi_surface_proxy_new_from_pool(
674 GST_VAAPI_SURFACE_POOL(postproc->filter_pool));
676 goto error_create_proxy;
677 gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
678 gst_vaapi_surface_proxy_unref(proxy);
681 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
683 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
684 if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
685 deint_method, deint_flags))
686 goto error_op_deinterlace;
688 if (deint_refs && !gst_vaapi_filter_set_deinterlacing_references(
689 postproc->filter, ds->surfaces, ds->num_surfaces, NULL, 0))
690 goto error_op_deinterlace;
692 else if (deint_changed && !gst_vaapi_filter_set_deinterlacing(
693 postproc->filter, deint_method, 0))
694 goto error_op_deinterlace;
696 outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
697 gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
698 status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
699 outbuf_surface, flags);
700 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
701 goto error_process_vpp;
703 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
704 gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
706 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
707 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
710 if (deint && deint_refs)
711 ds_add_buffer(ds, inbuf);
712 postproc->use_vpp = TRUE;
716 error_invalid_buffer:
718 GST_ERROR("failed to validate source buffer");
719 return GST_FLOW_ERROR;
723 GST_ERROR("failed to create output buffer");
724 return GST_FLOW_ERROR;
728 GST_ERROR("failed to create new output buffer meta");
729 gst_buffer_replace(&fieldbuf, NULL);
730 return GST_FLOW_ERROR;
734 GST_ERROR("failed to create surface proxy from pool");
735 gst_buffer_replace(&fieldbuf, NULL);
736 return GST_FLOW_ERROR;
738 error_op_deinterlace:
740 GST_ERROR("failed to apply deinterlacing filter");
741 gst_buffer_replace(&fieldbuf, NULL);
742 return GST_FLOW_NOT_SUPPORTED;
746 GST_ERROR("failed to apply VPP filters (error %d)", status);
747 gst_buffer_replace(&fieldbuf, NULL);
748 return GST_FLOW_ERROR;
752 if (ret != GST_FLOW_FLUSHING)
753 GST_ERROR("failed to push output buffer to video sink");
754 return GST_FLOW_ERROR;
759 gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
762 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
763 GstVaapiVideoMeta *meta;
764 GstClockTime timestamp;
767 guint fieldbuf_flags, outbuf_flags, flags;
770 meta = gst_buffer_get_vaapi_video_meta(inbuf);
772 goto error_invalid_buffer;
774 timestamp = GST_BUFFER_TIMESTAMP(inbuf);
775 tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
776 deint = should_deinterlace_buffer(postproc, inbuf);
778 flags = gst_vaapi_video_meta_get_render_flags(meta) &
779 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
782 fieldbuf = create_output_buffer(postproc);
784 goto error_create_buffer;
785 append_output_buffer_metadata(postproc, fieldbuf, inbuf, 0);
787 meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
788 fieldbuf_flags = flags;
789 fieldbuf_flags |= deint ? (
791 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
792 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
793 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
794 gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
796 GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
797 GST_BUFFER_DURATION(fieldbuf) = postproc->field_duration;
798 ret = gst_pad_push(trans->srcpad, fieldbuf);
799 if (ret != GST_FLOW_OK)
800 goto error_push_buffer;
803 append_output_buffer_metadata(postproc, outbuf, inbuf, 0);
805 meta = gst_buffer_get_vaapi_video_meta(outbuf);
806 outbuf_flags = flags;
807 outbuf_flags |= deint ? (
809 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
810 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
811 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
812 gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
814 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
815 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
819 error_invalid_buffer:
821 GST_ERROR("failed to validate source buffer");
822 return GST_FLOW_ERROR;
826 GST_ERROR("failed to create output buffer");
831 if (ret != GST_FLOW_FLUSHING)
832 GST_ERROR("failed to push output buffer to video sink");
838 gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
841 GstVaapiVideoMeta *meta;
843 /* No video processing needed, simply copy buffer metadata */
844 meta = gst_buffer_get_vaapi_video_meta(inbuf);
846 goto error_invalid_buffer;
848 append_output_buffer_metadata(GST_VAAPIPOSTPROC(trans), outbuf, inbuf,
849 GST_BUFFER_COPY_TIMESTAMPS);
853 error_invalid_buffer:
855 GST_ERROR("failed to validate source buffer");
856 return GST_FLOW_ERROR;
861 is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
863 gboolean deinterlace;
865 switch (postproc->deinterlace_mode) {
866 case GST_VAAPI_DEINTERLACE_MODE_AUTO:
867 deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
869 case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
880 video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
882 if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
884 if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
885 GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
887 if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
889 if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
895 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
896 gboolean *caps_changed_ptr)
899 gboolean deinterlace;
901 if (!gst_video_info_from_caps(&vi, caps))
904 if (video_info_changed(&vi, &postproc->sinkpad_info))
905 postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
907 deinterlace = is_deinterlace_enabled(postproc, &vi);
909 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
910 postproc->field_duration = GST_VIDEO_INFO_FPS_N(&vi) > 0 ?
911 gst_util_uint64_scale(GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
912 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi)) : 0;
914 postproc->get_va_surfaces = gst_caps_has_vaapi_surface(caps);
919 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
920 gboolean *caps_changed_ptr)
924 if (!gst_video_info_from_caps(&vi, caps))
927 if (video_info_changed(&vi, &postproc->srcpad_info))
928 postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
930 if (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info) &&
931 postproc->format != DEFAULT_FORMAT)
932 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
934 if ((postproc->width || postproc->height) &&
935 postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
936 postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
937 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
942 ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
944 GstCaps *out_caps, *raw_caps;
946 if (postproc->allowed_sinkpad_caps)
950 out_caps = gst_caps_from_string(GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
951 GST_CAPS_INTERLACED_MODES);
953 GST_ERROR("failed to create VA sink caps");
957 /* Append raw video caps */
958 if (gst_vaapipostproc_ensure_uploader(postproc)) {
959 raw_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(postproc);
961 out_caps = gst_caps_make_writable(out_caps);
962 gst_caps_append(out_caps, gst_caps_copy(raw_caps));
965 GST_WARNING("failed to create YUV sink caps");
967 postproc->allowed_sinkpad_caps = out_caps;
969 /* XXX: append VA/VPP filters */
973 /* Fixup output caps so that to reflect the supported set of pixel formats */
975 expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
977 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
978 guint i, num_structures;
981 had_filter = postproc->filter != NULL;
982 if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
984 if (!gst_vaapipostproc_ensure_filter_caps(postproc))
987 /* Reset "format" field for each structure */
988 if (!gst_vaapi_value_set_format_list(&value, postproc->filter_formats))
990 if (gst_vaapi_value_set_format(&v_format, GST_VIDEO_FORMAT_ENCODED)) {
991 gst_value_list_prepend_value(&value, &v_format);
992 g_value_unset(&v_format);
995 num_structures = gst_caps_get_size(caps);
996 for (i = 0; i < num_structures; i++) {
997 GstStructure * const structure = gst_caps_get_structure(caps, i);
1000 gst_structure_set_value(structure, "format", &value);
1002 g_value_unset(&value);
1006 gst_vaapipostproc_destroy_filter(postproc);
1011 ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
1015 if (postproc->allowed_srcpad_caps)
1018 /* Create initial caps from pad template */
1019 out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
1021 GST_ERROR("failed to create VA src caps");
1025 postproc->allowed_srcpad_caps =
1026 expand_allowed_srcpad_caps(postproc, out_caps);
1027 return postproc->allowed_srcpad_caps != NULL;
1031 find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
1032 guint *width_ptr, guint *height_ptr)
1034 guint width, height;
1036 width = GST_VIDEO_INFO_WIDTH(vip);
1037 height = GST_VIDEO_INFO_HEIGHT(vip);
1038 if (postproc->width && postproc->height) {
1039 width = postproc->width;
1040 height = postproc->height;
1042 else if (postproc->keep_aspect) {
1043 const gdouble ratio = (gdouble)width / height;
1044 if (postproc->width) {
1045 width = postproc->width;
1046 height = postproc->width / ratio;
1048 else if (postproc->height) {
1049 height = postproc->height;
1050 width = postproc->height * ratio;
1053 else if (postproc->width)
1054 width = postproc->width;
1055 else if (postproc->height)
1056 height = postproc->height;
1059 *height_ptr = height;
1063 gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
1064 GstPadDirection direction, GstCaps *caps)
1066 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1068 GstVideoFormat format, out_format;
1070 #if GST_CHECK_VERSION(1,1,0)
1071 GstVaapiCapsFeature feature;
1072 const gchar *feature_str;
1074 guint width, height;
1076 /* Generate the sink pad caps, that could be fixated afterwards */
1077 if (direction == GST_PAD_SRC) {
1078 if (!ensure_allowed_sinkpad_caps(postproc))
1080 return gst_caps_ref(postproc->allowed_sinkpad_caps);
1083 /* Generate complete set of src pad caps if non-fixated sink pad
1084 caps are provided */
1085 if (!gst_caps_is_fixed(caps)) {
1086 if (!ensure_allowed_srcpad_caps(postproc))
1088 return gst_caps_ref(postproc->allowed_srcpad_caps);
1091 /* Generate the expected src pad caps, from the current fixated
1093 if (!gst_video_info_from_caps(&vi, caps))
1096 // Set double framerate in interlaced mode
1097 if (is_deinterlace_enabled(postproc, &vi)) {
1098 gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
1099 gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
1100 if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
1102 GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
1103 GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
1106 // Signal the other pad that we only generate progressive frames
1107 GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
1109 // Update size from user-specified parameters
1110 format = GST_VIDEO_INFO_FORMAT(&vi);
1111 #if GST_CHECK_VERSION(1,1,0)
1112 out_format = postproc->format;
1114 out_format = GST_VIDEO_FORMAT_ENCODED;
1116 find_best_size(postproc, &vi, &width, &height);
1117 gst_video_info_set_format(&vi, out_format, width, height);
1119 #if GST_CHECK_VERSION(1,1,0)
1120 out_caps = gst_video_info_to_caps(&vi);
1124 feature = gst_vaapi_find_preferred_caps_feature(
1125 GST_BASE_TRANSFORM_SRC_PAD(trans), out_format);
1127 if (out_format == GST_VIDEO_FORMAT_ENCODED &&
1128 feature == GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY) {
1129 GstCaps *sink_caps, *peer_caps =
1130 gst_pad_peer_query_caps(GST_BASE_TRANSFORM_SRC_PAD(trans),
1131 postproc->allowed_srcpad_caps);
1133 gst_video_info_set_format(&vi, format, width, height);
1134 sink_caps = gst_video_info_to_caps(&vi);
1136 if (gst_caps_can_intersect(peer_caps, sink_caps))
1137 gst_caps_set_simple(out_caps, "format", G_TYPE_STRING,
1138 gst_video_format_to_string(format), NULL);
1139 gst_caps_unref(sink_caps);
1141 gst_caps_unref(peer_caps);
1144 feature_str = gst_vaapi_caps_feature_to_string(feature);
1146 gst_caps_set_features(out_caps, 0,
1147 gst_caps_features_new(feature_str, NULL));
1150 /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
1151 reconstruct suitable caps for "encoded" video formats */
1152 out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
1156 gst_caps_set_simple(out_caps,
1157 "type", G_TYPE_STRING, "vaapi",
1158 "opengl", G_TYPE_BOOLEAN, USE_GLX,
1159 "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
1160 "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
1161 "framerate", GST_TYPE_FRACTION, GST_VIDEO_INFO_FPS_N(&vi),
1162 GST_VIDEO_INFO_FPS_D(&vi),
1163 "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
1164 GST_VIDEO_INFO_PAR_D(&vi),
1167 gst_caps_set_interlaced(out_caps, &vi);
1172 #if GST_CHECK_VERSION(1,0,0)
1174 gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
1175 GstPadDirection direction, GstCaps *caps, GstCaps *filter)
1179 caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
1180 if (caps && filter) {
1181 out_caps = gst_caps_intersect_full(caps, filter,
1182 GST_CAPS_INTERSECT_FIRST);
1183 gst_caps_unref(caps);
1189 #define gst_vaapipostproc_transform_caps \
1190 gst_vaapipostproc_transform_caps_impl
1193 #if GST_CHECK_VERSION(1,0,0)
1194 typedef gsize GstBaseTransformSizeType;
1196 typedef guint GstBaseTransformSizeType;
1200 gst_vaapipostproc_transform_size(GstBaseTransform *trans,
1201 GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
1202 GstCaps *othercaps, GstBaseTransformSizeType *othersize)
1204 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1206 if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1213 static GstFlowReturn
1214 gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
1217 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1221 ret = gst_vaapi_plugin_base_get_input_buffer(
1222 GST_VAAPI_PLUGIN_BASE(postproc), inbuf, &buf);
1223 if (ret != GST_FLOW_OK)
1224 return GST_FLOW_ERROR;
1226 ret = GST_FLOW_NOT_SUPPORTED;
1227 if (postproc->flags) {
1228 /* Use VA/VPP extensions to process this frame */
1229 if (postproc->has_vpp &&
1230 (postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE ||
1231 deint_method_is_advanced(postproc->deinterlace_method))) {
1232 ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
1233 if (ret != GST_FLOW_NOT_SUPPORTED)
1235 GST_WARNING("unsupported VPP filters. Disabling");
1238 /* Only append picture structure meta data (top/bottom field) */
1239 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1240 ret = gst_vaapipostproc_process(trans, buf, outbuf);
1241 if (ret != GST_FLOW_NOT_SUPPORTED)
1246 /* Fallback: passthrough to the downstream element as is */
1247 ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
1250 gst_buffer_unref(buf);
1254 static GstFlowReturn
1255 gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
1257 #if !GST_CHECK_VERSION(1,0,0)
1258 gint size, GstCaps *caps,
1260 GstBuffer **outbuf_ptr)
1262 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1264 *outbuf_ptr = create_output_buffer(postproc);
1265 return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1269 ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1272 GstVaapiVideoPool *pool;
1274 gst_video_info_init(&vi);
1275 gst_video_info_from_caps(&vi, caps);
1276 gst_video_info_set_format(&vi, postproc->format,
1277 GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1279 if (postproc->filter_pool && !video_info_changed(&vi, &postproc->filter_pool_info))
1281 postproc->filter_pool_info = vi;
1283 pool = gst_vaapi_surface_pool_new(GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc),
1284 &postproc->filter_pool_info);
1288 gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
1289 gst_vaapi_video_pool_unref(pool);
1294 gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
1297 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1298 gboolean caps_changed = FALSE;
1300 if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
1302 if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
1306 gst_vaapipostproc_destroy(postproc);
1307 if (!gst_vaapipostproc_create(postproc))
1309 if (!gst_vaapi_plugin_base_set_caps(GST_VAAPI_PLUGIN_BASE(trans),
1314 if (!ensure_srcpad_buffer_pool(postproc, out_caps))
1320 gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
1323 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1325 GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
1327 if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc))) {
1328 GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
1332 return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
1333 trans, direction, query);
1336 #if GST_CHECK_VERSION(1,0,0)
1338 gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
1339 GstQuery *decide_query, GstQuery *query)
1341 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1342 GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(trans);
1344 /* Let vaapidecode allocate the video buffers */
1345 if (postproc->get_va_surfaces)
1347 if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1353 gst_vaapipostproc_decide_allocation(GstBaseTransform *trans, GstQuery *query)
1355 return gst_vaapi_plugin_base_decide_allocation(GST_VAAPI_PLUGIN_BASE(trans),
1361 gst_vaapipostproc_finalize(GObject *object)
1363 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1365 gst_vaapipostproc_destroy(postproc);
1367 gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(postproc));
1368 G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
1372 gst_vaapipostproc_set_property(
1375 const GValue *value,
1379 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1383 postproc->format = g_value_get_enum(value);
1386 postproc->width = g_value_get_uint(value);
1389 postproc->height = g_value_get_uint(value);
1391 case PROP_FORCE_ASPECT_RATIO:
1392 postproc->keep_aspect = g_value_get_boolean(value);
1394 case PROP_DEINTERLACE_MODE:
1395 postproc->deinterlace_mode = g_value_get_enum(value);
1397 case PROP_DEINTERLACE_METHOD:
1398 postproc->deinterlace_method = g_value_get_enum(value);
1401 postproc->denoise_level = g_value_get_float(value);
1402 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1405 postproc->sharpen_level = g_value_get_float(value);
1406 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1409 postproc->hue = g_value_get_float(value);
1410 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1412 case PROP_SATURATION:
1413 postproc->saturation = g_value_get_float(value);
1414 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1416 case PROP_BRIGHTNESS:
1417 postproc->brightness = g_value_get_float(value);
1418 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1421 postproc->contrast = g_value_get_float(value);
1422 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1425 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1431 gst_vaapipostproc_get_property(
1438 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1442 g_value_set_enum(value, postproc->format);
1445 g_value_set_uint(value, postproc->width);
1448 g_value_set_uint(value, postproc->height);
1450 case PROP_FORCE_ASPECT_RATIO:
1451 g_value_set_boolean(value, postproc->keep_aspect);
1453 case PROP_DEINTERLACE_MODE:
1454 g_value_set_enum(value, postproc->deinterlace_mode);
1456 case PROP_DEINTERLACE_METHOD:
1457 g_value_set_enum(value, postproc->deinterlace_method);
1460 g_value_set_float(value, postproc->denoise_level);
1463 g_value_set_float(value, postproc->sharpen_level);
1466 g_value_set_float(value, postproc->hue);
1468 case PROP_SATURATION:
1469 g_value_set_float(value, postproc->saturation);
1471 case PROP_BRIGHTNESS:
1472 g_value_set_float(value, postproc->brightness);
1475 g_value_set_float(value, postproc->contrast);
1478 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1484 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
1486 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1487 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1488 GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
1489 GstPadTemplate *pad_template;
1490 GPtrArray *filter_ops;
1491 GstVaapiFilterOpInfo *filter_op;
1493 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
1494 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1496 gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(klass));
1498 object_class->finalize = gst_vaapipostproc_finalize;
1499 object_class->set_property = gst_vaapipostproc_set_property;
1500 object_class->get_property = gst_vaapipostproc_get_property;
1501 trans_class->start = gst_vaapipostproc_start;
1502 trans_class->stop = gst_vaapipostproc_stop;
1503 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1504 trans_class->transform_size = gst_vaapipostproc_transform_size;
1505 trans_class->transform = gst_vaapipostproc_transform;
1506 trans_class->set_caps = gst_vaapipostproc_set_caps;
1507 trans_class->query = gst_vaapipostproc_query;
1509 #if GST_CHECK_VERSION(1,0,0)
1510 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1511 trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
1514 trans_class->prepare_output_buffer =
1515 gst_vaapipostproc_prepare_output_buffer;
1517 gst_element_class_set_static_metadata(element_class,
1518 "VA-API video postprocessing",
1519 "Filter/Converter/Video",
1521 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1524 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
1525 gst_element_class_add_pad_template(element_class, pad_template);
1528 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
1529 gst_element_class_add_pad_template(element_class, pad_template);
1532 * GstVaapiPostproc:deinterlace-mode:
1534 * This selects whether the deinterlacing should always be applied or if
1535 * they should only be applied on content that has the "interlaced" flag
1538 g_object_class_install_property
1540 PROP_DEINTERLACE_MODE,
1541 g_param_spec_enum("deinterlace-mode",
1543 "Deinterlace mode to use",
1544 GST_VAAPI_TYPE_DEINTERLACE_MODE,
1545 DEFAULT_DEINTERLACE_MODE,
1546 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1549 * GstVaapiPostproc:deinterlace-method:
1551 * This selects the deinterlacing method to apply.
1553 g_object_class_install_property
1555 PROP_DEINTERLACE_METHOD,
1556 g_param_spec_enum("deinterlace-method",
1557 "Deinterlace method",
1558 "Deinterlace method to use",
1559 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1560 DEFAULT_DEINTERLACE_METHOD,
1561 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1563 filter_ops = gst_vaapi_filter_get_operations(NULL);
1568 * GstVaapiPostproc:format:
1570 * The forced output pixel format, expressed as a #GstVideoFormat.
1572 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1574 g_object_class_install_property(object_class,
1575 PROP_FORMAT, filter_op->pspec);
1578 * GstVaapiPostproc:width:
1580 * The forced output width in pixels. If set to zero, the width is
1581 * calculated from the height if aspect ration is preserved, or
1582 * inherited from the sink caps width
1584 g_object_class_install_property
1587 g_param_spec_uint("width",
1589 "Forced output width",
1591 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1594 * GstVaapiPostproc:height:
1596 * The forced output height in pixels. If set to zero, the height
1597 * is calculated from the width if aspect ration is preserved, or
1598 * inherited from the sink caps height
1600 g_object_class_install_property
1603 g_param_spec_uint("height",
1605 "Forced output height",
1607 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1610 * GstVaapiPostproc:force-aspect-ratio:
1612 * When enabled, scaling respects video aspect ratio; when
1613 * disabled, the video is distorted to fit the width and height
1616 g_object_class_install_property
1618 PROP_FORCE_ASPECT_RATIO,
1619 g_param_spec_boolean("force-aspect-ratio",
1620 "Force aspect ratio",
1621 "When enabled, scaling will respect original aspect ratio",
1623 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1626 * GstVaapiPostproc:denoise:
1628 * The level of noise reduction to apply.
1630 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
1632 g_object_class_install_property(object_class,
1633 PROP_DENOISE, filter_op->pspec);
1636 * GstVaapiPostproc:sharpen:
1638 * The level of sharpening to apply for positive values, or the
1639 * level of blurring for negative values.
1641 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
1643 g_object_class_install_property(object_class,
1644 PROP_SHARPEN, filter_op->pspec);
1647 * GstVaapiPostproc:hue:
1649 * The color hue, expressed as a float value. Range is -180.0 to
1650 * 180.0. Default value is 0.0 and represents no modification.
1652 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_HUE);
1654 g_object_class_install_property(object_class,
1655 PROP_HUE, filter_op->pspec);
1658 * GstVaapiPostproc:saturation:
1660 * The color saturation, expressed as a float value. Range is 0.0
1661 * to 2.0. Default value is 1.0 and represents no modification.
1663 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
1665 g_object_class_install_property(object_class,
1666 PROP_SATURATION, filter_op->pspec);
1669 * GstVaapiPostproc:brightness:
1671 * The color brightness, expressed as a float value. Range is -1.0
1672 * to 1.0. Default value is 0.0 and represents no modification.
1674 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
1676 g_object_class_install_property(object_class,
1677 PROP_BRIGHTNESS, filter_op->pspec);
1680 * GstVaapiPostproc:contrast:
1682 * The color contrast, expressed as a float value. Range is 0.0 to
1683 * 2.0. Default value is 1.0 and represents no modification.
1685 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
1687 g_object_class_install_property(object_class,
1688 PROP_CONTRAST, filter_op->pspec);
1690 g_ptr_array_unref(filter_ops);
1694 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
1696 gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(postproc), GST_CAT_DEFAULT);
1698 postproc->format = DEFAULT_FORMAT;
1699 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
1700 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
1701 postproc->field_duration = GST_CLOCK_TIME_NONE;
1702 postproc->keep_aspect = TRUE;
1703 postproc->get_va_surfaces = TRUE;
1705 gst_video_info_init(&postproc->sinkpad_info);
1706 gst_video_info_init(&postproc->srcpad_info);
1707 gst_video_info_init(&postproc->filter_pool_info);