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,1,0)
76 GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
77 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA") ", "
78 GST_CAPS_INTERLACED_FALSE "; "
80 #if GST_CHECK_VERSION(1,0,0)
81 GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ", "
84 "width = " GST_VIDEO_SIZE_RANGE ", "
85 "height = " GST_VIDEO_SIZE_RANGE ", "
86 "framerate = " GST_VIDEO_FPS_RANGE ", "
88 GST_CAPS_INTERLACED_FALSE;
90 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
91 GST_STATIC_PAD_TEMPLATE(
95 GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
97 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
98 GST_STATIC_PAD_TEMPLATE(
102 GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
104 G_DEFINE_TYPE_WITH_CODE(
107 GST_TYPE_BASE_TRANSFORM,
108 GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES)
116 PROP_FORCE_ASPECT_RATIO,
117 PROP_DEINTERLACE_MODE,
118 PROP_DEINTERLACE_METHOD,
128 #define DEFAULT_FORMAT GST_VIDEO_FORMAT_ENCODED
129 #define DEFAULT_DEINTERLACE_MODE GST_VAAPI_DEINTERLACE_MODE_AUTO
130 #define DEFAULT_DEINTERLACE_METHOD GST_VAAPI_DEINTERLACE_METHOD_BOB
132 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
133 gst_vaapi_deinterlace_mode_get_type()
136 gst_vaapi_deinterlace_mode_get_type(void)
138 static GType deinterlace_mode_type = 0;
140 static const GEnumValue mode_types[] = {
141 { GST_VAAPI_DEINTERLACE_MODE_AUTO,
142 "Auto detection", "auto" },
143 { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
144 "Force deinterlacing", "interlaced" },
145 { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
146 "Never deinterlace", "disabled" },
150 if (!deinterlace_mode_type) {
151 deinterlace_mode_type =
152 g_enum_register_static("GstVaapiDeinterlaceMode", mode_types);
154 return deinterlace_mode_type;
158 ds_reset(GstVaapiDeinterlaceState *ds)
162 for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++)
163 gst_buffer_replace(&ds->buffers[i], NULL);
164 ds->buffers_index = 0;
165 ds->num_surfaces = 0;
171 ds_add_buffer(GstVaapiDeinterlaceState *ds, GstBuffer *buf)
173 gst_buffer_replace(&ds->buffers[ds->buffers_index], buf);
174 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS(ds->buffers);
177 static inline GstBuffer *
178 ds_get_buffer(GstVaapiDeinterlaceState *ds, guint index)
180 /* Note: the index increases towards older buffers.
181 i.e. buffer at index 0 means the immediately preceding buffer
182 in the history, buffer at index 1 means the one preceding the
183 surface at index 0, etc. */
184 const guint n = ds->buffers_index + G_N_ELEMENTS(ds->buffers) - index - 1;
185 return ds->buffers[n % G_N_ELEMENTS(ds->buffers)];
189 ds_set_surfaces(GstVaapiDeinterlaceState *ds)
191 GstVaapiVideoMeta *meta;
194 ds->num_surfaces = 0;
195 for (i = 0; i < G_N_ELEMENTS(ds->buffers); i++) {
196 GstBuffer * const buf = ds_get_buffer(ds, i);
200 meta = gst_buffer_get_vaapi_video_meta(buf);
201 ds->surfaces[ds->num_surfaces++] =
202 gst_vaapi_video_meta_get_surface(meta);
206 static GstVaapiFilterOpInfo *
207 find_filter_op(GPtrArray *filter_ops, GstVaapiFilterOp op)
212 for (i = 0; i < filter_ops->len; i++) {
213 GstVaapiFilterOpInfo * const filter_op =
214 g_ptr_array_index(filter_ops, i);
215 if (filter_op->op == op)
222 static inline gboolean
223 gst_vaapipostproc_ensure_display(GstVaapiPostproc *postproc)
225 return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(postproc));
229 gst_vaapipostproc_ensure_uploader(GstVaapiPostproc *postproc)
231 if (!gst_vaapipostproc_ensure_display(postproc))
233 if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(postproc)))
239 gst_vaapipostproc_ensure_filter(GstVaapiPostproc *postproc)
241 if (postproc->filter)
244 if (!gst_vaapipostproc_ensure_display(postproc))
247 postproc->filter = gst_vaapi_filter_new(
248 GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
249 if (!postproc->filter)
255 gst_vaapipostproc_ensure_filter_caps(GstVaapiPostproc *postproc)
257 if (!gst_vaapipostproc_ensure_filter(postproc))
260 postproc->filter_ops = gst_vaapi_filter_get_operations(postproc->filter);
261 if (!postproc->filter_ops)
264 postproc->filter_formats = gst_vaapi_filter_get_formats(postproc->filter);
265 if (!postproc->filter_formats)
271 gst_vaapipostproc_create(GstVaapiPostproc *postproc)
273 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
275 if (!gst_vaapipostproc_ensure_display(postproc))
277 if (!gst_vaapipostproc_ensure_uploader(postproc))
280 postproc->use_vpp = FALSE;
281 postproc->has_vpp = gst_vaapipostproc_ensure_filter(postproc);
286 gst_vaapipostproc_destroy_filter(GstVaapiPostproc *postproc)
288 if (postproc->filter_formats) {
289 g_array_unref(postproc->filter_formats);
290 postproc->filter_formats = NULL;
293 if (postproc->filter_ops) {
294 g_ptr_array_unref(postproc->filter_ops);
295 postproc->filter_ops = NULL;
297 gst_vaapi_filter_replace(&postproc->filter, NULL);
298 gst_vaapi_video_pool_replace(&postproc->filter_pool, NULL);
299 postproc->filter_pool_active = FALSE;
303 gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
305 ds_reset(&postproc->deinterlace_state);
306 gst_vaapipostproc_destroy_filter(postproc);
308 gst_caps_replace(&postproc->allowed_sinkpad_caps, NULL);
309 gst_caps_replace(&postproc->allowed_srcpad_caps, NULL);
310 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
314 gst_vaapipostproc_start(GstBaseTransform *trans)
316 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
318 ds_reset(&postproc->deinterlace_state);
319 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(postproc)))
321 if (!gst_vaapipostproc_ensure_display(postproc))
327 gst_vaapipostproc_stop(GstBaseTransform *trans)
329 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
331 ds_reset(&postproc->deinterlace_state);
332 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(postproc));
333 postproc->filter_pool_active = FALSE;
338 should_deinterlace_buffer(GstVaapiPostproc *postproc, GstBuffer *buf)
340 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
341 postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
344 if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
347 g_assert(postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
349 switch (GST_VIDEO_INFO_INTERLACE_MODE(&postproc->sinkpad_info)) {
350 case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
352 case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
354 case GST_VIDEO_INTERLACE_MODE_MIXED:
355 #if GST_CHECK_VERSION(1,0,0)
356 if (GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
359 if (!GST_BUFFER_FLAG_IS_SET(buf, GST_VIDEO_BUFFER_PROGRESSIVE))
364 GST_ERROR("unhandled \"interlace-mode\", disabling deinterlacing" );
371 create_output_buffer(GstVaapiPostproc *postproc)
375 #if GST_CHECK_VERSION(1,0,0)
376 GstBufferPool * const pool =
377 GST_VAAPI_PLUGIN_BASE(postproc)->srcpad_buffer_pool;
380 g_return_val_if_fail(pool != NULL, NULL);
382 if (!postproc->filter_pool_active) {
383 if (!gst_buffer_pool_set_active(pool, TRUE))
384 goto error_activate_pool;
385 postproc->filter_pool_active = TRUE;
389 ret = gst_buffer_pool_acquire_buffer(pool, &outbuf, NULL);
390 if (ret != GST_FLOW_OK || !outbuf)
391 goto error_create_buffer;
393 /* Create a raw VA video buffer without GstVaapiVideoMeta attached
394 to it yet, as this will be done next in the transform() hook */
395 outbuf = gst_vaapi_video_buffer_new_empty();
397 goto error_create_buffer;
399 gst_buffer_set_caps(outbuf, GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAPS(postproc));
404 #if GST_CHECK_VERSION(1,0,0)
407 GST_ERROR("failed to activate output video buffer pool");
413 GST_ERROR("failed to create output video buffer");
419 append_output_buffer_metadata(GstVaapiPostproc *postproc, GstBuffer *outbuf,
420 GstBuffer *inbuf, guint flags)
422 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
423 GstVaapiSurfaceProxy *proxy;
425 gst_buffer_copy_into(outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
427 /* GstVideoCropMeta */
428 #if GST_CHECK_VERSION(1,0,0)
429 if (!postproc->use_vpp) {
430 GstVideoCropMeta * const crop_meta =
431 gst_buffer_get_video_crop_meta(inbuf);
433 GstVideoCropMeta * const out_crop_meta =
434 gst_buffer_add_video_crop_meta(outbuf);
436 *out_crop_meta = *crop_meta;
441 /* GstVaapiVideoMeta */
442 inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
443 g_return_val_if_fail(inbuf_meta != NULL, FALSE);
444 proxy = gst_vaapi_video_meta_get_surface_proxy(inbuf_meta);
446 outbuf_meta = gst_buffer_get_vaapi_video_meta(outbuf);
447 g_return_val_if_fail(outbuf_meta != NULL, FALSE);
448 proxy = gst_vaapi_surface_proxy_copy(proxy);
452 gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
453 gst_vaapi_surface_proxy_unref(proxy);
458 deint_method_is_advanced(GstVaapiDeinterlaceMethod deint_method)
460 gboolean is_advanced;
462 switch (deint_method) {
463 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
464 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
474 static GstVaapiDeinterlaceMethod
475 get_next_deint_method(GstVaapiDeinterlaceMethod deint_method)
477 switch (deint_method) {
478 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
479 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
482 /* Default to basic "bob" for all others */
483 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
490 set_best_deint_method(GstVaapiPostproc *postproc, guint flags,
491 GstVaapiDeinterlaceMethod *deint_method_ptr)
493 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
497 success = gst_vaapi_filter_set_deinterlacing(postproc->filter,
498 deint_method, flags);
499 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
501 deint_method = get_next_deint_method(deint_method);
503 *deint_method_ptr = deint_method;
508 gst_vaapipostproc_process_vpp(GstBaseTransform *trans, GstBuffer *inbuf,
511 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
512 GstVaapiDeinterlaceState * const ds = &postproc->deinterlace_state;
513 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
514 GstVaapiSurface *inbuf_surface, *outbuf_surface;
515 GstVaapiSurfaceProxy *proxy;
516 GstVaapiFilterStatus status;
517 GstClockTime timestamp;
520 GstVaapiDeinterlaceMethod deint_method;
521 guint flags, deint_flags;
522 gboolean tff, deint, deint_refs, deint_changed;
523 GstVaapiRectangle *crop_rect = NULL;
524 #if GST_CHECK_VERSION(1,0,0)
525 GstVaapiRectangle tmp_rect;
528 /* Validate filters */
529 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
530 !gst_vaapi_filter_set_format(postproc->filter, postproc->format))
531 return GST_FLOW_NOT_SUPPORTED;
533 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
534 !gst_vaapi_filter_set_denoising_level(postproc->filter,
535 postproc->denoise_level))
536 return GST_FLOW_NOT_SUPPORTED;
538 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
539 !gst_vaapi_filter_set_sharpening_level(postproc->filter,
540 postproc->sharpen_level))
541 return GST_FLOW_NOT_SUPPORTED;
543 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) &&
544 !gst_vaapi_filter_set_hue(postproc->filter,
546 return GST_FLOW_NOT_SUPPORTED;
548 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) &&
549 !gst_vaapi_filter_set_saturation(postproc->filter,
550 postproc->saturation))
551 return GST_FLOW_NOT_SUPPORTED;
553 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) &&
554 !gst_vaapi_filter_set_brightness(postproc->filter,
555 postproc->brightness))
556 return GST_FLOW_NOT_SUPPORTED;
558 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) &&
559 !gst_vaapi_filter_set_contrast(postproc->filter,
561 return GST_FLOW_NOT_SUPPORTED;
563 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) &&
564 !gst_vaapi_filter_set_scaling(postproc->filter,
565 postproc->scale_method))
566 return GST_FLOW_NOT_SUPPORTED;
568 inbuf_meta = gst_buffer_get_vaapi_video_meta(inbuf);
570 goto error_invalid_buffer;
571 inbuf_surface = gst_vaapi_video_meta_get_surface(inbuf_meta);
573 #if GST_CHECK_VERSION(1,0,0)
574 GstVideoCropMeta * const crop_meta =
575 gst_buffer_get_video_crop_meta(inbuf);
577 crop_rect = &tmp_rect;
578 crop_rect->x = crop_meta->x;
579 crop_rect->y = crop_meta->y;
580 crop_rect->width = crop_meta->width;
581 crop_rect->height = crop_meta->height;
585 crop_rect = (GstVaapiRectangle *)
586 gst_vaapi_video_meta_get_render_rect(inbuf_meta);
588 timestamp = GST_BUFFER_TIMESTAMP(inbuf);
589 tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
590 deint = should_deinterlace_buffer(postproc, inbuf);
592 /* Drop references if deinterlacing conditions changed */
593 deint_changed = deint != ds->deint;
594 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
597 deint_method = postproc->deinterlace_method;
598 deint_refs = deint_method_is_advanced(deint_method);
599 if (deint_refs && 0) {
600 GstBuffer * const prev_buf = ds_get_buffer(ds, 0);
601 GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP(inbuf);
602 /* Reset deinterlacing state when there is a discontinuity */
603 if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP(prev_buf)) != pts) {
604 const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF(prev_pts, pts);
605 if (pts_diff < 0 || (postproc->field_duration > 0 &&
606 pts_diff >= postproc->field_duration * 3 - 1))
614 flags = gst_vaapi_video_meta_get_render_flags(inbuf_meta) &
615 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
618 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
619 fieldbuf = create_output_buffer(postproc);
621 goto error_create_buffer;
623 outbuf_meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
625 goto error_create_meta;
627 proxy = gst_vaapi_surface_proxy_new_from_pool(
628 GST_VAAPI_SURFACE_POOL(postproc->filter_pool));
630 goto error_create_proxy;
631 gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
632 gst_vaapi_surface_proxy_unref(proxy);
635 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
637 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
638 if (!set_best_deint_method(postproc, deint_flags, &deint_method))
639 goto error_op_deinterlace;
641 if (deint_method != postproc->deinterlace_method) {
642 GST_DEBUG("unsupported deinterlace-method %u. Using %u instead",
643 postproc->deinterlace_method, deint_method);
644 postproc->deinterlace_method = deint_method;
645 deint_refs = deint_method_is_advanced(deint_method);
650 if (!gst_vaapi_filter_set_deinterlacing_references(
651 postproc->filter, ds->surfaces, ds->num_surfaces,
653 goto error_op_deinterlace;
656 else if (deint_changed) {
657 // Reset internal filter to non-deinterlacing mode
658 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
659 if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
661 goto error_op_deinterlace;
664 outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
665 gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
666 status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
667 outbuf_surface, flags);
668 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
669 goto error_process_vpp;
671 GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
672 GST_BUFFER_DURATION(fieldbuf) = postproc->field_duration;
673 ret = gst_pad_push(trans->srcpad, fieldbuf);
674 if (ret != GST_FLOW_OK)
675 goto error_push_buffer;
680 outbuf_meta = gst_buffer_get_vaapi_video_meta(outbuf);
682 goto error_create_meta;
684 proxy = gst_vaapi_surface_proxy_new_from_pool(
685 GST_VAAPI_SURFACE_POOL(postproc->filter_pool));
687 goto error_create_proxy;
688 gst_vaapi_video_meta_set_surface_proxy(outbuf_meta, proxy);
689 gst_vaapi_surface_proxy_unref(proxy);
692 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
694 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
695 if (!gst_vaapi_filter_set_deinterlacing(postproc->filter,
696 deint_method, deint_flags))
697 goto error_op_deinterlace;
699 if (deint_refs && !gst_vaapi_filter_set_deinterlacing_references(
700 postproc->filter, ds->surfaces, ds->num_surfaces, NULL, 0))
701 goto error_op_deinterlace;
703 else if (deint_changed && !gst_vaapi_filter_set_deinterlacing(
704 postproc->filter, deint_method, 0))
705 goto error_op_deinterlace;
707 outbuf_surface = gst_vaapi_video_meta_get_surface(outbuf_meta);
708 gst_vaapi_filter_set_cropping_rectangle(postproc->filter, crop_rect);
709 status = gst_vaapi_filter_process(postproc->filter, inbuf_surface,
710 outbuf_surface, flags);
711 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
712 goto error_process_vpp;
714 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
715 gst_buffer_copy_into(outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
717 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
718 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
721 if (deint && deint_refs)
722 ds_add_buffer(ds, inbuf);
723 postproc->use_vpp = TRUE;
727 error_invalid_buffer:
729 GST_ERROR("failed to validate source buffer");
730 return GST_FLOW_ERROR;
734 GST_ERROR("failed to create output buffer");
735 return GST_FLOW_ERROR;
739 GST_ERROR("failed to create new output buffer meta");
740 gst_buffer_replace(&fieldbuf, NULL);
741 return GST_FLOW_ERROR;
745 GST_ERROR("failed to create surface proxy from pool");
746 gst_buffer_replace(&fieldbuf, NULL);
747 return GST_FLOW_ERROR;
749 error_op_deinterlace:
751 GST_ERROR("failed to apply deinterlacing filter");
752 gst_buffer_replace(&fieldbuf, NULL);
753 return GST_FLOW_NOT_SUPPORTED;
757 GST_ERROR("failed to apply VPP filters (error %d)", status);
758 gst_buffer_replace(&fieldbuf, NULL);
759 return GST_FLOW_ERROR;
763 if (ret != GST_FLOW_FLUSHING)
764 GST_ERROR("failed to push output buffer to video sink");
765 return GST_FLOW_ERROR;
770 gst_vaapipostproc_process(GstBaseTransform *trans, GstBuffer *inbuf,
773 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
774 GstVaapiVideoMeta *meta;
775 GstClockTime timestamp;
778 guint fieldbuf_flags, outbuf_flags, flags;
781 meta = gst_buffer_get_vaapi_video_meta(inbuf);
783 goto error_invalid_buffer;
785 timestamp = GST_BUFFER_TIMESTAMP(inbuf);
786 tff = GST_BUFFER_FLAG_IS_SET(inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
787 deint = should_deinterlace_buffer(postproc, inbuf);
789 flags = gst_vaapi_video_meta_get_render_flags(meta) &
790 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
793 fieldbuf = create_output_buffer(postproc);
795 goto error_create_buffer;
796 append_output_buffer_metadata(postproc, fieldbuf, inbuf, 0);
798 meta = gst_buffer_get_vaapi_video_meta(fieldbuf);
799 fieldbuf_flags = flags;
800 fieldbuf_flags |= deint ? (
802 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
803 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
804 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
805 gst_vaapi_video_meta_set_render_flags(meta, fieldbuf_flags);
807 GST_BUFFER_TIMESTAMP(fieldbuf) = timestamp;
808 GST_BUFFER_DURATION(fieldbuf) = postproc->field_duration;
809 ret = gst_pad_push(trans->srcpad, fieldbuf);
810 if (ret != GST_FLOW_OK)
811 goto error_push_buffer;
814 append_output_buffer_metadata(postproc, outbuf, inbuf, 0);
816 meta = gst_buffer_get_vaapi_video_meta(outbuf);
817 outbuf_flags = flags;
818 outbuf_flags |= deint ? (
820 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
821 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
822 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
823 gst_vaapi_video_meta_set_render_flags(meta, outbuf_flags);
825 GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
826 GST_BUFFER_DURATION(outbuf) = postproc->field_duration;
830 error_invalid_buffer:
832 GST_ERROR("failed to validate source buffer");
833 return GST_FLOW_ERROR;
837 GST_ERROR("failed to create output buffer");
842 if (ret != GST_FLOW_FLUSHING)
843 GST_ERROR("failed to push output buffer to video sink");
849 gst_vaapipostproc_passthrough(GstBaseTransform *trans, GstBuffer *inbuf,
852 GstVaapiVideoMeta *meta;
854 /* No video processing needed, simply copy buffer metadata */
855 meta = gst_buffer_get_vaapi_video_meta(inbuf);
857 goto error_invalid_buffer;
859 append_output_buffer_metadata(GST_VAAPIPOSTPROC(trans), outbuf, inbuf,
860 GST_BUFFER_COPY_TIMESTAMPS);
864 error_invalid_buffer:
866 GST_ERROR("failed to validate source buffer");
867 return GST_FLOW_ERROR;
872 is_deinterlace_enabled(GstVaapiPostproc *postproc, GstVideoInfo *vip)
874 gboolean deinterlace;
876 switch (postproc->deinterlace_mode) {
877 case GST_VAAPI_DEINTERLACE_MODE_AUTO:
878 deinterlace = GST_VIDEO_INFO_IS_INTERLACED(vip);
880 case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
891 video_info_changed(GstVideoInfo *old_vip, GstVideoInfo *new_vip)
893 if (GST_VIDEO_INFO_FORMAT(old_vip) != GST_VIDEO_INFO_FORMAT(new_vip))
895 if (GST_VIDEO_INFO_INTERLACE_MODE(old_vip) !=
896 GST_VIDEO_INFO_INTERLACE_MODE(new_vip))
898 if (GST_VIDEO_INFO_WIDTH(old_vip) != GST_VIDEO_INFO_WIDTH(new_vip))
900 if (GST_VIDEO_INFO_HEIGHT(old_vip) != GST_VIDEO_INFO_HEIGHT(new_vip))
906 gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps,
907 gboolean *caps_changed_ptr)
910 gboolean deinterlace;
912 if (!gst_video_info_from_caps(&vi, caps))
915 if (video_info_changed(&vi, &postproc->sinkpad_info))
916 postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
918 deinterlace = is_deinterlace_enabled(postproc, &vi);
920 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
921 postproc->field_duration = GST_VIDEO_INFO_FPS_N(&vi) > 0 ?
922 gst_util_uint64_scale(GST_SECOND, GST_VIDEO_INFO_FPS_D(&vi),
923 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N(&vi)) : 0;
925 postproc->get_va_surfaces = gst_caps_has_vaapi_surface(caps);
930 gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps,
931 gboolean *caps_changed_ptr)
935 if (!gst_video_info_from_caps(&vi, caps))
938 if (video_info_changed(&vi, &postproc->srcpad_info))
939 postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
941 if (postproc->format != GST_VIDEO_INFO_FORMAT(&postproc->sinkpad_info) &&
942 postproc->format != DEFAULT_FORMAT)
943 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
945 if ((postproc->width || postproc->height) &&
946 postproc->width != GST_VIDEO_INFO_WIDTH(&postproc->sinkpad_info) &&
947 postproc->height != GST_VIDEO_INFO_HEIGHT(&postproc->sinkpad_info))
948 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
953 ensure_allowed_sinkpad_caps(GstVaapiPostproc *postproc)
955 GstCaps *out_caps, *raw_caps;
957 if (postproc->allowed_sinkpad_caps)
961 out_caps = gst_caps_from_string(GST_VAAPIPOSTPROC_SURFACE_CAPS ", "
962 GST_CAPS_INTERLACED_MODES);
964 GST_ERROR("failed to create VA sink caps");
968 /* Append raw video caps */
969 if (gst_vaapipostproc_ensure_uploader(postproc)) {
970 raw_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(postproc);
972 out_caps = gst_caps_make_writable(out_caps);
973 gst_caps_append(out_caps, gst_caps_copy(raw_caps));
976 GST_WARNING("failed to create YUV sink caps");
978 postproc->allowed_sinkpad_caps = out_caps;
980 /* XXX: append VA/VPP filters */
984 /* Fixup output caps so that to reflect the supported set of pixel formats */
986 expand_allowed_srcpad_caps(GstVaapiPostproc *postproc, GstCaps *caps)
988 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
989 guint i, num_structures;
992 had_filter = postproc->filter != NULL;
993 if (!had_filter && !gst_vaapipostproc_ensure_filter(postproc))
995 if (!gst_vaapipostproc_ensure_filter_caps(postproc))
998 /* Reset "format" field for each structure */
999 if (!gst_vaapi_value_set_format_list(&value, postproc->filter_formats))
1001 if (gst_vaapi_value_set_format(&v_format, GST_VIDEO_FORMAT_ENCODED)) {
1002 gst_value_list_prepend_value(&value, &v_format);
1003 g_value_unset(&v_format);
1006 num_structures = gst_caps_get_size(caps);
1007 for (i = 0; i < num_structures; i++) {
1008 #if GST_CHECK_VERSION(1,1,0)
1009 GstCapsFeatures * const features = gst_caps_get_features (caps, i);
1010 if (gst_caps_features_contains(features,
1011 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META))
1015 GstStructure * const structure = gst_caps_get_structure(caps, i);
1018 gst_structure_set_value(structure, "format", &value);
1020 g_value_unset(&value);
1024 gst_vaapipostproc_destroy_filter(postproc);
1029 ensure_allowed_srcpad_caps(GstVaapiPostproc *postproc)
1033 if (postproc->allowed_srcpad_caps)
1036 /* Create initial caps from pad template */
1037 out_caps = gst_caps_from_string(gst_vaapipostproc_src_caps_str);
1039 GST_ERROR("failed to create VA src caps");
1043 postproc->allowed_srcpad_caps =
1044 expand_allowed_srcpad_caps(postproc, out_caps);
1045 return postproc->allowed_srcpad_caps != NULL;
1049 find_best_size(GstVaapiPostproc *postproc, GstVideoInfo *vip,
1050 guint *width_ptr, guint *height_ptr)
1052 guint width, height;
1054 width = GST_VIDEO_INFO_WIDTH(vip);
1055 height = GST_VIDEO_INFO_HEIGHT(vip);
1056 if (postproc->width && postproc->height) {
1057 width = postproc->width;
1058 height = postproc->height;
1060 else if (postproc->keep_aspect) {
1061 const gdouble ratio = (gdouble)width / height;
1062 if (postproc->width) {
1063 width = postproc->width;
1064 height = postproc->width / ratio;
1066 else if (postproc->height) {
1067 height = postproc->height;
1068 width = postproc->height * ratio;
1071 else if (postproc->width)
1072 width = postproc->width;
1073 else if (postproc->height)
1074 height = postproc->height;
1077 *height_ptr = height;
1081 gst_vaapipostproc_transform_caps_impl(GstBaseTransform *trans,
1082 GstPadDirection direction, GstCaps *caps)
1084 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1086 GstVideoFormat format, out_format;
1088 #if GST_CHECK_VERSION(1,1,0)
1089 GstVaapiCapsFeature feature;
1090 const gchar *feature_str;
1092 guint width, height;
1094 /* Generate the sink pad caps, that could be fixated afterwards */
1095 if (direction == GST_PAD_SRC) {
1096 if (!ensure_allowed_sinkpad_caps(postproc))
1098 return gst_caps_ref(postproc->allowed_sinkpad_caps);
1101 /* Generate complete set of src pad caps if non-fixated sink pad
1102 caps are provided */
1103 if (!gst_caps_is_fixed(caps)) {
1104 if (!ensure_allowed_srcpad_caps(postproc))
1106 return gst_caps_ref(postproc->allowed_srcpad_caps);
1109 /* Generate the expected src pad caps, from the current fixated
1111 if (!gst_video_info_from_caps(&vi, caps))
1114 // Set double framerate in interlaced mode
1115 if (is_deinterlace_enabled(postproc, &vi)) {
1116 gint fps_n = GST_VIDEO_INFO_FPS_N(&vi);
1117 gint fps_d = GST_VIDEO_INFO_FPS_D(&vi);
1118 if (!gst_util_fraction_multiply(fps_n, fps_d, 2, 1, &fps_n, &fps_d))
1120 GST_VIDEO_INFO_FPS_N(&vi) = fps_n;
1121 GST_VIDEO_INFO_FPS_D(&vi) = fps_d;
1124 // Signal the other pad that we only generate progressive frames
1125 GST_VIDEO_INFO_INTERLACE_MODE(&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
1127 // Update size from user-specified parameters
1128 format = GST_VIDEO_INFO_FORMAT(&vi);
1129 #if GST_CHECK_VERSION(1,1,0)
1130 out_format = postproc->format;
1132 out_format = GST_VIDEO_FORMAT_ENCODED;
1134 find_best_size(postproc, &vi, &width, &height);
1135 gst_video_info_set_format(&vi, out_format, width, height);
1137 #if GST_CHECK_VERSION(1,1,0)
1138 out_caps = gst_video_info_to_caps(&vi);
1142 feature = gst_vaapi_find_preferred_caps_feature(
1143 GST_BASE_TRANSFORM_SRC_PAD(trans), out_format);
1145 feature_str = gst_vaapi_caps_feature_to_string(feature);
1147 gst_caps_set_features(out_caps, 0,
1148 gst_caps_features_new(feature_str, NULL));
1150 if (out_format == GST_VIDEO_FORMAT_ENCODED &&
1151 feature != GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE) {
1152 GstCaps *sink_caps, *peer_caps =
1153 gst_pad_peer_query_caps(GST_BASE_TRANSFORM_SRC_PAD(trans),
1154 postproc->allowed_srcpad_caps);
1156 if (feature == GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META)
1157 format = GST_VIDEO_FORMAT_RGBA;
1159 gst_video_info_set_format(&vi, format, width, height);
1160 sink_caps = gst_video_info_to_caps(&vi);
1163 gst_caps_set_features(sink_caps, 0,
1164 gst_caps_features_new(feature_str, NULL));
1165 if (gst_caps_can_intersect(peer_caps, sink_caps))
1166 gst_caps_set_simple(out_caps, "format", G_TYPE_STRING,
1167 gst_video_format_to_string(format), NULL);
1168 gst_caps_unref(sink_caps);
1170 gst_caps_unref(peer_caps);
1174 /* XXX: gst_video_info_to_caps() from GStreamer 0.10 does not
1175 reconstruct suitable caps for "encoded" video formats */
1176 out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
1180 gst_caps_set_simple(out_caps,
1181 "type", G_TYPE_STRING, "vaapi",
1182 "opengl", G_TYPE_BOOLEAN, USE_GLX,
1183 "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH(&vi),
1184 "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT(&vi),
1185 "framerate", GST_TYPE_FRACTION, GST_VIDEO_INFO_FPS_N(&vi),
1186 GST_VIDEO_INFO_FPS_D(&vi),
1187 "pixel-aspect-ratio", GST_TYPE_FRACTION, GST_VIDEO_INFO_PAR_N(&vi),
1188 GST_VIDEO_INFO_PAR_D(&vi),
1191 gst_caps_set_interlaced(out_caps, &vi);
1196 #if GST_CHECK_VERSION(1,0,0)
1198 gst_vaapipostproc_transform_caps(GstBaseTransform *trans,
1199 GstPadDirection direction, GstCaps *caps, GstCaps *filter)
1203 caps = gst_vaapipostproc_transform_caps_impl(trans, direction, caps);
1204 if (caps && filter) {
1205 out_caps = gst_caps_intersect_full(caps, filter,
1206 GST_CAPS_INTERSECT_FIRST);
1207 gst_caps_unref(caps);
1213 #define gst_vaapipostproc_transform_caps \
1214 gst_vaapipostproc_transform_caps_impl
1217 #if GST_CHECK_VERSION(1,0,0)
1218 typedef gsize GstBaseTransformSizeType;
1220 typedef guint GstBaseTransformSizeType;
1224 gst_vaapipostproc_transform_size(GstBaseTransform *trans,
1225 GstPadDirection direction, GstCaps *caps, GstBaseTransformSizeType size,
1226 GstCaps *othercaps, GstBaseTransformSizeType *othersize)
1228 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1230 if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1237 static GstFlowReturn
1238 gst_vaapipostproc_transform(GstBaseTransform *trans, GstBuffer *inbuf,
1241 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1245 ret = gst_vaapi_plugin_base_get_input_buffer(
1246 GST_VAAPI_PLUGIN_BASE(postproc), inbuf, &buf);
1247 if (ret != GST_FLOW_OK)
1248 return GST_FLOW_ERROR;
1250 ret = GST_FLOW_NOT_SUPPORTED;
1251 if (postproc->flags) {
1252 /* Use VA/VPP extensions to process this frame */
1253 if (postproc->has_vpp &&
1254 (postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE ||
1255 deint_method_is_advanced(postproc->deinterlace_method))) {
1256 ret = gst_vaapipostproc_process_vpp(trans, buf, outbuf);
1257 if (ret != GST_FLOW_NOT_SUPPORTED)
1259 GST_WARNING("unsupported VPP filters. Disabling");
1262 /* Only append picture structure meta data (top/bottom field) */
1263 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1264 ret = gst_vaapipostproc_process(trans, buf, outbuf);
1265 if (ret != GST_FLOW_NOT_SUPPORTED)
1270 /* Fallback: passthrough to the downstream element as is */
1271 ret = gst_vaapipostproc_passthrough(trans, buf, outbuf);
1274 gst_buffer_unref(buf);
1278 static GstFlowReturn
1279 gst_vaapipostproc_prepare_output_buffer(GstBaseTransform *trans,
1281 #if !GST_CHECK_VERSION(1,0,0)
1282 gint size, GstCaps *caps,
1284 GstBuffer **outbuf_ptr)
1286 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1288 *outbuf_ptr = create_output_buffer(postproc);
1289 return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1293 ensure_srcpad_buffer_pool(GstVaapiPostproc *postproc, GstCaps *caps)
1296 GstVaapiVideoPool *pool;
1298 gst_video_info_init(&vi);
1299 gst_video_info_from_caps(&vi, caps);
1300 gst_video_info_set_format(&vi, postproc->format,
1301 GST_VIDEO_INFO_WIDTH(&vi), GST_VIDEO_INFO_HEIGHT(&vi));
1303 if (postproc->filter_pool && !video_info_changed(&vi, &postproc->filter_pool_info))
1305 postproc->filter_pool_info = vi;
1307 pool = gst_vaapi_surface_pool_new(GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc),
1308 &postproc->filter_pool_info);
1312 gst_vaapi_video_pool_replace(&postproc->filter_pool, pool);
1313 gst_vaapi_video_pool_unref(pool);
1318 gst_vaapipostproc_set_caps(GstBaseTransform *trans, GstCaps *caps,
1321 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1322 gboolean caps_changed = FALSE;
1324 if (!gst_vaapipostproc_update_sink_caps(postproc, caps, &caps_changed))
1326 if (!gst_vaapipostproc_update_src_caps(postproc, out_caps, &caps_changed))
1330 gst_vaapipostproc_destroy(postproc);
1331 if (!gst_vaapipostproc_create(postproc))
1333 if (!gst_vaapi_plugin_base_set_caps(GST_VAAPI_PLUGIN_BASE(trans),
1338 if (!ensure_srcpad_buffer_pool(postproc, out_caps))
1344 gst_vaapipostproc_query(GstBaseTransform *trans, GstPadDirection direction,
1347 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1349 GST_INFO_OBJECT(trans, "query type `%s'", GST_QUERY_TYPE_NAME(query));
1351 if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc))) {
1352 GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(postproc));
1356 return GST_BASE_TRANSFORM_CLASS(gst_vaapipostproc_parent_class)->query(
1357 trans, direction, query);
1360 #if GST_CHECK_VERSION(1,0,0)
1362 gst_vaapipostproc_propose_allocation(GstBaseTransform *trans,
1363 GstQuery *decide_query, GstQuery *query)
1365 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(trans);
1366 GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(trans);
1368 /* Let vaapidecode allocate the video buffers */
1369 if (postproc->get_va_surfaces)
1371 if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1377 gst_vaapipostproc_decide_allocation(GstBaseTransform *trans, GstQuery *query)
1379 return gst_vaapi_plugin_base_decide_allocation(GST_VAAPI_PLUGIN_BASE(trans),
1385 gst_vaapipostproc_finalize(GObject *object)
1387 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1389 gst_vaapipostproc_destroy(postproc);
1391 gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(postproc));
1392 G_OBJECT_CLASS(gst_vaapipostproc_parent_class)->finalize(object);
1396 gst_vaapipostproc_set_property(
1399 const GValue *value,
1403 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1407 postproc->format = g_value_get_enum(value);
1410 postproc->width = g_value_get_uint(value);
1413 postproc->height = g_value_get_uint(value);
1415 case PROP_FORCE_ASPECT_RATIO:
1416 postproc->keep_aspect = g_value_get_boolean(value);
1418 case PROP_DEINTERLACE_MODE:
1419 postproc->deinterlace_mode = g_value_get_enum(value);
1421 case PROP_DEINTERLACE_METHOD:
1422 postproc->deinterlace_method = g_value_get_enum(value);
1425 postproc->denoise_level = g_value_get_float(value);
1426 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1429 postproc->sharpen_level = g_value_get_float(value);
1430 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1433 postproc->hue = g_value_get_float(value);
1434 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1436 case PROP_SATURATION:
1437 postproc->saturation = g_value_get_float(value);
1438 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1440 case PROP_BRIGHTNESS:
1441 postproc->brightness = g_value_get_float(value);
1442 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1445 postproc->contrast = g_value_get_float(value);
1446 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1448 case PROP_SCALE_METHOD:
1449 postproc->scale_method = g_value_get_enum(value);
1450 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1453 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1459 gst_vaapipostproc_get_property(
1466 GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
1470 g_value_set_enum(value, postproc->format);
1473 g_value_set_uint(value, postproc->width);
1476 g_value_set_uint(value, postproc->height);
1478 case PROP_FORCE_ASPECT_RATIO:
1479 g_value_set_boolean(value, postproc->keep_aspect);
1481 case PROP_DEINTERLACE_MODE:
1482 g_value_set_enum(value, postproc->deinterlace_mode);
1484 case PROP_DEINTERLACE_METHOD:
1485 g_value_set_enum(value, postproc->deinterlace_method);
1488 g_value_set_float(value, postproc->denoise_level);
1491 g_value_set_float(value, postproc->sharpen_level);
1494 g_value_set_float(value, postproc->hue);
1496 case PROP_SATURATION:
1497 g_value_set_float(value, postproc->saturation);
1499 case PROP_BRIGHTNESS:
1500 g_value_set_float(value, postproc->brightness);
1503 g_value_set_float(value, postproc->contrast);
1505 case PROP_SCALE_METHOD:
1506 g_value_set_enum(value, postproc->scale_method);
1509 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1515 gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
1517 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1518 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1519 GstBaseTransformClass * const trans_class = GST_BASE_TRANSFORM_CLASS(klass);
1520 GstPadTemplate *pad_template;
1521 GPtrArray *filter_ops;
1522 GstVaapiFilterOpInfo *filter_op;
1524 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
1525 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1527 gst_vaapi_plugin_base_class_init(GST_VAAPI_PLUGIN_BASE_CLASS(klass));
1529 object_class->finalize = gst_vaapipostproc_finalize;
1530 object_class->set_property = gst_vaapipostproc_set_property;
1531 object_class->get_property = gst_vaapipostproc_get_property;
1532 trans_class->start = gst_vaapipostproc_start;
1533 trans_class->stop = gst_vaapipostproc_stop;
1534 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1535 trans_class->transform_size = gst_vaapipostproc_transform_size;
1536 trans_class->transform = gst_vaapipostproc_transform;
1537 trans_class->set_caps = gst_vaapipostproc_set_caps;
1538 trans_class->query = gst_vaapipostproc_query;
1540 #if GST_CHECK_VERSION(1,0,0)
1541 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1542 trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
1545 trans_class->prepare_output_buffer =
1546 gst_vaapipostproc_prepare_output_buffer;
1548 gst_element_class_set_static_metadata(element_class,
1549 "VA-API video postprocessing",
1550 "Filter/Converter/Video",
1552 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1555 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
1556 gst_element_class_add_pad_template(element_class, pad_template);
1559 pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
1560 gst_element_class_add_pad_template(element_class, pad_template);
1563 * GstVaapiPostproc:deinterlace-mode:
1565 * This selects whether the deinterlacing should always be applied or if
1566 * they should only be applied on content that has the "interlaced" flag
1569 g_object_class_install_property
1571 PROP_DEINTERLACE_MODE,
1572 g_param_spec_enum("deinterlace-mode",
1574 "Deinterlace mode to use",
1575 GST_VAAPI_TYPE_DEINTERLACE_MODE,
1576 DEFAULT_DEINTERLACE_MODE,
1577 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1580 * GstVaapiPostproc:deinterlace-method:
1582 * This selects the deinterlacing method to apply.
1584 g_object_class_install_property
1586 PROP_DEINTERLACE_METHOD,
1587 g_param_spec_enum("deinterlace-method",
1588 "Deinterlace method",
1589 "Deinterlace method to use",
1590 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1591 DEFAULT_DEINTERLACE_METHOD,
1592 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1594 filter_ops = gst_vaapi_filter_get_operations(NULL);
1599 * GstVaapiPostproc:format:
1601 * The forced output pixel format, expressed as a #GstVideoFormat.
1603 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1605 g_object_class_install_property(object_class,
1606 PROP_FORMAT, filter_op->pspec);
1609 * GstVaapiPostproc:width:
1611 * The forced output width in pixels. If set to zero, the width is
1612 * calculated from the height if aspect ration is preserved, or
1613 * inherited from the sink caps width
1615 g_object_class_install_property
1618 g_param_spec_uint("width",
1620 "Forced output width",
1622 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1625 * GstVaapiPostproc:height:
1627 * The forced output height in pixels. If set to zero, the height
1628 * is calculated from the width if aspect ration is preserved, or
1629 * inherited from the sink caps height
1631 g_object_class_install_property
1634 g_param_spec_uint("height",
1636 "Forced output height",
1638 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1641 * GstVaapiPostproc:force-aspect-ratio:
1643 * When enabled, scaling respects video aspect ratio; when
1644 * disabled, the video is distorted to fit the width and height
1647 g_object_class_install_property
1649 PROP_FORCE_ASPECT_RATIO,
1650 g_param_spec_boolean("force-aspect-ratio",
1651 "Force aspect ratio",
1652 "When enabled, scaling will respect original aspect ratio",
1654 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1657 * GstVaapiPostproc:denoise:
1659 * The level of noise reduction to apply.
1661 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
1663 g_object_class_install_property(object_class,
1664 PROP_DENOISE, filter_op->pspec);
1667 * GstVaapiPostproc:sharpen:
1669 * The level of sharpening to apply for positive values, or the
1670 * level of blurring for negative values.
1672 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
1674 g_object_class_install_property(object_class,
1675 PROP_SHARPEN, filter_op->pspec);
1678 * GstVaapiPostproc:hue:
1680 * The color hue, expressed as a float value. Range is -180.0 to
1681 * 180.0. Default value is 0.0 and represents no modification.
1683 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_HUE);
1685 g_object_class_install_property(object_class,
1686 PROP_HUE, filter_op->pspec);
1689 * GstVaapiPostproc:saturation:
1691 * The color saturation, expressed as a float value. Range is 0.0
1692 * to 2.0. Default value is 1.0 and represents no modification.
1694 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
1696 g_object_class_install_property(object_class,
1697 PROP_SATURATION, filter_op->pspec);
1700 * GstVaapiPostproc:brightness:
1702 * The color brightness, expressed as a float value. Range is -1.0
1703 * to 1.0. Default value is 0.0 and represents no modification.
1705 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
1707 g_object_class_install_property(object_class,
1708 PROP_BRIGHTNESS, filter_op->pspec);
1711 * GstVaapiPostproc:contrast:
1713 * The color contrast, expressed as a float value. Range is 0.0 to
1714 * 2.0. Default value is 1.0 and represents no modification.
1716 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
1718 g_object_class_install_property(object_class,
1719 PROP_CONTRAST, filter_op->pspec);
1722 * GstVaapiPostproc:scale-method:
1724 * The scaling method to use, expressed as an enum value. See
1725 * #GstVaapiScaleMethod.
1727 filter_op = find_filter_op(filter_ops, GST_VAAPI_FILTER_OP_SCALING);
1729 g_object_class_install_property(object_class,
1730 PROP_SCALE_METHOD, filter_op->pspec);
1732 g_ptr_array_unref(filter_ops);
1736 gst_vaapipostproc_init(GstVaapiPostproc *postproc)
1738 gst_vaapi_plugin_base_init(GST_VAAPI_PLUGIN_BASE(postproc), GST_CAT_DEFAULT);
1740 postproc->format = DEFAULT_FORMAT;
1741 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
1742 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
1743 postproc->field_duration = GST_CLOCK_TIME_NONE;
1744 postproc->keep_aspect = TRUE;
1745 postproc->get_va_surfaces = TRUE;
1747 gst_video_info_init(&postproc->sinkpad_info);
1748 gst_video_info_init(&postproc->srcpad_info);
1749 gst_video_info_init(&postproc->filter_pool_info);