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:element-vaapipostproc
25 * @short_description: A VA-API base video postprocessing filter
27 * vaapipostproc consists in various postprocessing algorithms to be
28 * applied to VA surfaces.
30 * ## Example launch line
33 * gst-launch-1.0 videotestsrc ! vaapipostproc ! video/x-raw, width=1920, height=1080 ! vaapisink
37 #include "gstcompat.h"
38 #include <gst/video/video.h>
40 #include <gst/vaapi/gstvaapivalue.h>
42 #include "gstvaapipostproc.h"
43 #include "gstvaapipostprocutil.h"
44 #include "gstvaapipluginutil.h"
45 #include "gstvaapivideobuffer.h"
46 #include "gstvaapivideobufferpool.h"
47 #include "gstvaapivideomemory.h"
49 #define GST_PLUGIN_NAME "vaapipostproc"
50 #define GST_PLUGIN_DESC "A VA-API video postprocessing filter"
52 GST_DEBUG_CATEGORY_STATIC (gst_debug_vaapipostproc);
53 #ifndef GST_DISABLE_GST_DEBUG
54 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
56 #define GST_CAT_DEFAULT NULL
59 /* Default templates */
61 static const char gst_vaapipostproc_sink_caps_str[] =
62 GST_VAAPI_MAKE_SURFACE_CAPS ", "
63 GST_CAPS_INTERLACED_MODES "; "
64 GST_VIDEO_CAPS_MAKE (GST_VAAPI_FORMATS_ALL) ", "
65 GST_CAPS_INTERLACED_MODES;
69 static const char gst_vaapipostproc_src_caps_str[] =
70 GST_VAAPI_MAKE_SURFACE_CAPS ", "
71 GST_CAPS_INTERLACED_FALSE "; "
72 #if (USE_GLX || USE_EGL)
73 GST_VAAPI_MAKE_GLTEXUPLOAD_CAPS "; "
75 GST_VIDEO_CAPS_MAKE (GST_VAAPI_FORMATS_ALL) ", "
76 GST_CAPS_INTERLACED_MODES "; "
77 GST_VAAPI_MAKE_DMABUF_CAPS;
81 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
82 GST_STATIC_PAD_TEMPLATE ("sink",
85 GST_STATIC_CAPS (gst_vaapipostproc_sink_caps_str));
89 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
90 GST_STATIC_PAD_TEMPLATE ("src",
93 GST_STATIC_CAPS (gst_vaapipostproc_src_caps_str));
96 static void gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data);
98 G_DEFINE_TYPE_WITH_CODE (GstVaapiPostproc, gst_vaapipostproc,
99 GST_TYPE_BASE_TRANSFORM, GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
100 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
101 gst_vaapipostproc_colorbalance_init));
103 GST_VAAPI_PLUGIN_BASE_DEFINE_SET_CONTEXT (gst_vaapipostproc_parent_class);
105 static GstVideoFormat native_formats[] =
106 { GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_I420 };
115 PROP_FORCE_ASPECT_RATIO,
116 PROP_DEINTERLACE_MODE,
117 PROP_DEINTERLACE_METHOD,
125 PROP_VIDEO_DIRECTION,
130 #ifndef GST_REMOVE_DEPRECATED
131 PROP_SKIN_TONE_ENHANCEMENT,
133 PROP_SKIN_TONE_ENHANCEMENT_LEVEL,
136 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
137 gst_vaapi_deinterlace_mode_get_type()
140 gst_vaapi_deinterlace_mode_get_type (void)
142 static GType deinterlace_mode_type = 0;
144 static const GEnumValue mode_types[] = {
145 {GST_VAAPI_DEINTERLACE_MODE_AUTO,
146 "Auto detection", "auto"},
147 {GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
148 "Force deinterlacing", "interlaced"},
149 {GST_VAAPI_DEINTERLACE_MODE_DISABLED,
150 "Never deinterlace", "disabled"},
154 if (!deinterlace_mode_type) {
155 deinterlace_mode_type =
156 g_enum_register_static ("GstVaapiDeinterlaceMode", mode_types);
158 return deinterlace_mode_type;
162 ds_reset (GstVaapiDeinterlaceState * ds)
166 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++)
167 gst_buffer_replace (&ds->buffers[i], NULL);
168 ds->buffers_index = 0;
169 ds->num_surfaces = 0;
175 ds_add_buffer (GstVaapiDeinterlaceState * ds, GstBuffer * buf)
177 gst_buffer_replace (&ds->buffers[ds->buffers_index], buf);
178 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS (ds->buffers);
181 static inline GstBuffer *
182 ds_get_buffer (GstVaapiDeinterlaceState * ds, guint index)
184 /* Note: the index increases towards older buffers.
185 i.e. buffer at index 0 means the immediately preceding buffer
186 in the history, buffer at index 1 means the one preceding the
187 surface at index 0, etc. */
188 const guint n = ds->buffers_index + G_N_ELEMENTS (ds->buffers) - index - 1;
189 return ds->buffers[n % G_N_ELEMENTS (ds->buffers)];
193 ds_set_surfaces (GstVaapiDeinterlaceState * ds)
195 GstVaapiVideoMeta *meta;
198 ds->num_surfaces = 0;
199 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++) {
200 GstBuffer *const buf = ds_get_buffer (ds, i);
204 meta = gst_buffer_get_vaapi_video_meta (buf);
205 ds->surfaces[ds->num_surfaces++] = gst_vaapi_video_meta_get_surface (meta);
209 static GstVaapiFilterOpInfo *
210 find_filter_op (GPtrArray * filter_ops, GstVaapiFilterOp op)
215 for (i = 0; i < filter_ops->len; i++) {
216 GstVaapiFilterOpInfo *const filter_op = g_ptr_array_index (filter_ops, i);
217 if (filter_op->op == op)
224 static inline gboolean
225 gst_vaapipostproc_ensure_display (GstVaapiPostproc * postproc)
228 gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (postproc));
232 gst_vaapipostproc_ensure_filter (GstVaapiPostproc * postproc)
234 if (postproc->filter)
237 if (!gst_vaapipostproc_ensure_display (postproc))
240 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
241 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
244 gst_vaapi_filter_new (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
245 if (!postproc->filter)
251 gst_vaapipostproc_ensure_filter_caps (GstVaapiPostproc * postproc)
253 if (!gst_vaapipostproc_ensure_filter (postproc))
256 if (!postproc->filter_ops) {
257 postproc->filter_ops = gst_vaapi_filter_get_operations (postproc->filter);
258 if (!postproc->filter_ops)
262 if (!postproc->filter_formats) {
263 postproc->filter_formats = gst_vaapi_filter_get_formats (postproc->filter);
264 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))
278 postproc->use_vpp = FALSE;
279 postproc->has_vpp = gst_vaapipostproc_ensure_filter (postproc);
284 gst_vaapipostproc_destroy_filter (GstVaapiPostproc * postproc)
286 if (postproc->filter_formats) {
287 g_array_unref (postproc->filter_formats);
288 postproc->filter_formats = NULL;
291 if (postproc->filter_ops) {
292 g_ptr_array_unref (postproc->filter_ops);
293 postproc->filter_ops = NULL;
295 if (postproc->cb_channels) {
296 g_list_free_full (postproc->cb_channels, g_object_unref);
297 postproc->cb_channels = NULL;
299 gst_vaapi_filter_replace (&postproc->filter, NULL);
300 gst_vaapi_video_pool_replace (&postproc->filter_pool, NULL);
304 gst_vaapipostproc_destroy (GstVaapiPostproc * postproc)
306 ds_reset (&postproc->deinterlace_state);
307 gst_vaapipostproc_destroy_filter (postproc);
309 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
310 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
311 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
315 gst_vaapipostproc_start (GstBaseTransform * trans)
317 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
319 ds_reset (&postproc->deinterlace_state);
320 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
322 g_mutex_lock (&postproc->postproc_lock);
323 gst_vaapipostproc_ensure_filter (postproc);
324 g_mutex_unlock (&postproc->postproc_lock);
330 gst_vaapipostproc_stop (GstBaseTransform * trans)
332 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
334 g_mutex_lock (&postproc->postproc_lock);
335 ds_reset (&postproc->deinterlace_state);
336 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
338 postproc->field_duration = GST_CLOCK_TIME_NONE;
339 gst_video_info_init (&postproc->sinkpad_info);
340 gst_video_info_init (&postproc->srcpad_info);
341 gst_video_info_init (&postproc->filter_pool_info);
342 g_mutex_unlock (&postproc->postproc_lock);
348 should_deinterlace_buffer (GstVaapiPostproc * postproc, GstBuffer * buf)
350 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
351 postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
354 if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
357 g_assert (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
359 switch (GST_VIDEO_INFO_INTERLACE_MODE (&postproc->sinkpad_info)) {
360 case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
362 case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
364 case GST_VIDEO_INTERLACE_MODE_MIXED:
365 if (GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
369 GST_ERROR_OBJECT (postproc,
370 "unhandled \"interlace-mode\", disabling deinterlacing");
377 create_output_buffer (GstVaapiPostproc * postproc)
381 GstBufferPool *const pool =
382 GST_VAAPI_PLUGIN_BASE_SRC_PAD_BUFFER_POOL (postproc);
385 g_return_val_if_fail (pool != NULL, NULL);
387 if (!gst_buffer_pool_is_active (pool) &&
388 !gst_buffer_pool_set_active (pool, TRUE))
389 goto error_activate_pool;
392 ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
393 if (ret != GST_FLOW_OK || !outbuf)
394 goto error_create_buffer;
400 GST_ERROR_OBJECT (postproc, "failed to activate output video buffer pool");
405 GST_ERROR_OBJECT (postproc, "failed to create output video buffer");
410 static inline GstBuffer *
411 create_output_dump_buffer (GstVaapiPostproc * postproc)
413 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
416 gst_buffer_new_allocate (GST_VAAPI_PLUGIN_BASE_OTHER_ALLOCATOR (plugin),
417 GST_VIDEO_INFO_SIZE (GST_VAAPI_PLUGIN_BASE_SRC_PAD_INFO (plugin)),
418 &GST_VAAPI_PLUGIN_BASE_OTHER_ALLOCATOR_PARAMS (plugin));
422 copy_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
425 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (postproc);
426 GstBaseTransform *trans = GST_BASE_TRANSFORM (postproc);
430 if (!bclass->copy_metadata)
432 if (!bclass->copy_metadata (trans, inbuf, outbuf)) {
433 /* something failed, post a warning */
434 GST_ELEMENT_WARNING (trans, STREAM, NOT_IMPLEMENTED,
435 ("could not copy metadata"), (NULL));
440 append_output_buffer_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
441 GstBuffer * inbuf, guint flags)
443 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
444 GstVaapiSurfaceProxy *proxy;
446 gst_buffer_copy_into (outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
448 copy_metadata (postproc, outbuf, inbuf);
450 /* GstVaapiVideoMeta */
451 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
452 g_return_val_if_fail (inbuf_meta != NULL, FALSE);
453 proxy = gst_vaapi_video_meta_get_surface_proxy (inbuf_meta);
455 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
456 g_return_val_if_fail (outbuf_meta != NULL, FALSE);
457 proxy = gst_vaapi_surface_proxy_copy (proxy);
461 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
462 gst_vaapi_surface_proxy_unref (proxy);
467 deint_method_is_advanced (GstVaapiDeinterlaceMethod deint_method)
469 gboolean is_advanced;
471 switch (deint_method) {
472 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
473 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
483 static GstVaapiDeinterlaceMethod
484 get_next_deint_method (GstVaapiDeinterlaceMethod deint_method)
486 switch (deint_method) {
487 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
488 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
491 /* Default to basic "bob" for all others */
492 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
499 set_best_deint_method (GstVaapiPostproc * postproc, guint flags,
500 GstVaapiDeinterlaceMethod * deint_method_ptr)
502 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
506 success = gst_vaapi_filter_set_deinterlacing (postproc->filter,
507 deint_method, flags);
508 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
510 deint_method = get_next_deint_method (deint_method);
512 *deint_method_ptr = deint_method;
517 check_filter_update (GstVaapiPostproc * postproc)
519 guint filter_flag = postproc->flags;
523 if (!postproc->has_vpp)
526 for (i = GST_VAAPI_FILTER_OP_DENOISE;
527 i <= GST_VAAPI_FILTER_OP_SKINTONE_LEVEL; i++) {
528 op_flag = (filter_flag >> i) & 1;
537 update_filter (GstVaapiPostproc * postproc)
539 /* Validate filters */
540 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
541 !gst_vaapi_filter_set_format (postproc->filter, postproc->format))
544 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) {
545 if (!gst_vaapi_filter_set_denoising_level (postproc->filter,
546 postproc->denoise_level))
549 if (gst_vaapi_filter_get_denoising_level_default (postproc->filter) ==
550 postproc->denoise_level)
551 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_DENOISE);
554 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) {
555 if (!gst_vaapi_filter_set_sharpening_level (postproc->filter,
556 postproc->sharpen_level))
559 if (gst_vaapi_filter_get_sharpening_level_default (postproc->filter) ==
560 postproc->sharpen_level)
561 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SHARPEN);
564 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) {
565 if (!gst_vaapi_filter_set_hue (postproc->filter, postproc->hue))
568 if (gst_vaapi_filter_get_hue_default (postproc->filter) == postproc->hue)
569 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_HUE);
572 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) {
573 if (!gst_vaapi_filter_set_saturation (postproc->filter,
574 postproc->saturation))
577 if (gst_vaapi_filter_get_saturation_default (postproc->filter) ==
578 postproc->saturation)
579 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SATURATION);
582 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) {
583 if (!gst_vaapi_filter_set_brightness (postproc->filter,
584 postproc->brightness))
587 if (gst_vaapi_filter_get_brightness_default (postproc->filter) ==
588 postproc->brightness)
589 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS);
592 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) {
593 if (!gst_vaapi_filter_set_contrast (postproc->filter, postproc->contrast))
596 if (gst_vaapi_filter_get_contrast_default (postproc->filter) ==
598 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_CONTRAST);
601 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) {
602 if (!gst_vaapi_filter_set_scaling (postproc->filter,
603 postproc->scale_method))
606 if (gst_vaapi_filter_get_scaling_default (postproc->filter) ==
607 postproc->scale_method)
608 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SCALE);
611 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION) {
612 GstVideoOrientationMethod method = postproc->video_direction;
613 if (method == GST_VIDEO_ORIENTATION_AUTO)
614 method = postproc->tag_video_direction;
616 if (!gst_vaapi_filter_set_video_direction (postproc->filter, method)) {
617 GST_ELEMENT_WARNING (postproc, LIBRARY, SETTINGS,
618 ("Unsupported video direction '%s' by driver.",
619 gst_vaapi_enum_type_get_nick
620 (GST_TYPE_VIDEO_ORIENTATION_METHOD, method)),
621 ("video direction transformation ignored"));
623 /* Don't return FALSE because other filters might be set */
626 if (gst_vaapi_filter_get_video_direction_default (postproc->filter) ==
628 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION);
631 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_CROP)
632 if ((postproc->crop_left | postproc->crop_right | postproc->crop_top
633 | postproc->crop_bottom) == 0)
634 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_CROP);
636 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SKINTONE_LEVEL) {
637 if (!gst_vaapi_filter_set_skintone_level (postproc->filter,
638 postproc->skintone_value))
641 if (gst_vaapi_filter_get_skintone_level_default (postproc->filter) ==
642 postproc->skintone_value)
643 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SKINTONE_LEVEL);
645 #ifndef GST_REMOVE_DEPRECATED
647 * When use skin tone level property, disable old skin tone property always
649 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SKINTONE);
652 #ifndef GST_REMOVE_DEPRECATED
653 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SKINTONE) {
654 if (!gst_vaapi_filter_set_skintone (postproc->filter,
655 postproc->skintone_enhance))
658 if (gst_vaapi_filter_get_skintone_default (postproc->filter) ==
659 postproc->skintone_enhance)
660 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SKINTONE);
669 gst_vaapipostproc_set_passthrough (GstBaseTransform * trans)
671 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
672 gboolean filter_updated = FALSE;
674 if (check_filter_update (postproc) && update_filter (postproc)) {
675 /* check again if changed value is default */
676 filter_updated = check_filter_update (postproc);
679 gst_base_transform_set_passthrough (trans, postproc->same_caps
684 replace_to_dumb_buffer_if_required (GstVaapiPostproc * postproc,
685 GstBuffer ** fieldbuf)
687 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
690 if (!GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (postproc))
693 newbuf = create_output_dump_buffer (postproc);
697 if (!gst_vaapi_plugin_copy_va_buffer (plugin, *fieldbuf, newbuf)) {
698 gst_buffer_unref (newbuf);
702 gst_buffer_replace (fieldbuf, newbuf);
703 gst_buffer_unref (newbuf);
709 use_vpp_crop (GstVaapiPostproc * postproc)
711 return !(postproc->forward_crop
712 && !(postproc->flags & GST_VAAPI_POSTPROC_FLAG_CROP));
716 rotate_crop_meta (GstVaapiPostproc * const postproc, const GstVideoMeta * vmeta,
717 GstVideoCropMeta * crop)
721 /* The video meta is required since the caps width/height are smaller,
722 * which would not result in a usable GstVideoInfo for mapping the
727 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
728 case GST_VIDEO_ORIENTATION_HORIZ:
729 crop->x = vmeta->width - crop->width - crop->x;
731 case GST_VIDEO_ORIENTATION_VERT:
732 crop->y = vmeta->height - crop->height - crop->y;
734 case GST_VIDEO_ORIENTATION_90R:
736 crop->x = vmeta->height - crop->height - crop->y;
738 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
740 case GST_VIDEO_ORIENTATION_180:
741 crop->x = vmeta->width - crop->width - crop->x;
742 crop->y = vmeta->height - crop->height - crop->y;
744 case GST_VIDEO_ORIENTATION_90L:
747 crop->y = vmeta->width - crop->width - tmp;
748 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
750 case GST_VIDEO_ORIENTATION_UR_LL:
752 crop->x = vmeta->height - crop->height - crop->y;
753 crop->y = vmeta->width - crop->width - tmp;
754 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
756 case GST_VIDEO_ORIENTATION_UL_LR:
757 G_PRIMITIVE_SWAP (guint, crop->x, crop->y);
758 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
766 gst_vaapipostproc_process_vpp (GstBaseTransform * trans, GstBuffer * inbuf,
769 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
770 GstVaapiDeinterlaceState *const ds = &postproc->deinterlace_state;
771 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
772 GstVaapiSurface *inbuf_surface, *outbuf_surface;
773 GstVaapiSurfaceProxy *proxy;
774 GstVaapiFilterStatus status;
775 GstClockTime timestamp;
778 GstVaapiDeinterlaceMethod deint_method;
779 guint flags, deint_flags;
780 gboolean tff, deint, deint_refs, deint_changed, discont;
781 const GstVideoCropMeta *crop_meta;
782 GstVaapiRectangle *crop_rect = NULL;
783 GstVaapiRectangle tmp_rect;
785 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
787 goto error_invalid_buffer;
788 inbuf_surface = gst_vaapi_video_meta_get_surface (inbuf_meta);
790 if (use_vpp_crop (postproc)) {
791 crop_rect = &tmp_rect;
792 crop_rect->x = postproc->crop_left;
793 crop_rect->y = postproc->crop_top;
794 crop_rect->width = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
795 - (postproc->crop_left + postproc->crop_right);
796 crop_rect->height = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info)
797 - (postproc->crop_top + postproc->crop_bottom);
799 crop_meta = gst_buffer_get_video_crop_meta (inbuf);
801 crop_rect->x += crop_meta->x;
802 crop_rect->y += crop_meta->y;
807 crop_rect = (GstVaapiRectangle *)
808 gst_vaapi_video_meta_get_render_rect (inbuf_meta);
810 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
811 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
812 discont = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT);
813 deint = should_deinterlace_buffer (postproc, inbuf);
815 /* Drop references if deinterlacing conditions changed */
816 deint_changed = deint != ds->deint;
817 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
820 deint_method = postproc->deinterlace_method;
821 deint_refs = deint_method_is_advanced (deint_method);
822 if (deint_refs && 0) {
823 GstBuffer *const prev_buf = ds_get_buffer (ds, 0);
824 GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP (inbuf);
825 /* Reset deinterlacing state when there is a discontinuity */
826 if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP (prev_buf)) != pts) {
827 const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF (prev_pts, pts);
828 if (pts_diff < 0 || (postproc->field_duration > 0 &&
829 pts_diff >= postproc->field_duration * 3 - 1))
837 flags = gst_vaapi_video_meta_get_render_flags (inbuf_meta) &
838 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
841 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
842 fieldbuf = create_output_buffer (postproc);
844 goto error_create_buffer;
846 outbuf_meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
848 goto error_create_meta;
850 if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
852 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
853 (postproc->filter_pool));
855 goto error_create_proxy;
856 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
857 gst_vaapi_surface_proxy_unref (proxy);
861 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
863 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
864 if (!set_best_deint_method (postproc, deint_flags, &deint_method))
865 goto error_op_deinterlace;
867 if (deint_method != postproc->deinterlace_method) {
868 GST_DEBUG ("unsupported deinterlace-method %u. Using %u instead",
869 postproc->deinterlace_method, deint_method);
870 postproc->deinterlace_method = deint_method;
871 deint_refs = deint_method_is_advanced (deint_method);
875 ds_set_surfaces (ds);
876 if (!gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
877 ds->surfaces, ds->num_surfaces, NULL, 0))
878 goto error_op_deinterlace;
880 } else if (deint_changed) {
881 // Reset internal filter to non-deinterlacing mode
882 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
883 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
885 goto error_op_deinterlace;
888 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
889 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
890 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
891 outbuf_surface, flags);
892 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
893 goto error_process_vpp;
895 copy_metadata (postproc, fieldbuf, inbuf);
896 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
897 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
899 GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
903 if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
904 goto error_copy_buffer;
906 ret = gst_pad_push (trans->srcpad, fieldbuf);
907 if (ret != GST_FLOW_OK)
908 goto error_push_buffer;
913 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
915 goto error_create_meta;
917 if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
919 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
920 (postproc->filter_pool));
922 goto error_create_proxy;
923 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
924 gst_vaapi_surface_proxy_unref (proxy);
928 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
930 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
931 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
932 deint_method, deint_flags))
933 goto error_op_deinterlace;
936 && !gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
937 ds->surfaces, ds->num_surfaces, NULL, 0))
938 goto error_op_deinterlace;
939 } else if (deint_changed
940 && !gst_vaapi_filter_set_deinterlacing (postproc->filter, deint_method,
942 goto error_op_deinterlace;
944 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
945 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
946 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
947 outbuf_surface, flags);
948 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
949 goto error_process_vpp;
951 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
952 gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
954 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
955 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
957 GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
962 copy_metadata (postproc, outbuf, inbuf);
964 rotate_crop_meta (postproc, gst_buffer_get_video_meta (inbuf),
965 gst_buffer_get_video_crop_meta (outbuf));
967 if (deint && deint_refs)
968 ds_add_buffer (ds, inbuf);
969 postproc->use_vpp = TRUE;
973 error_invalid_buffer:
975 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
976 return GST_FLOW_ERROR;
980 GST_ERROR_OBJECT (postproc, "failed to create output buffer");
981 return GST_FLOW_ERROR;
985 GST_ERROR_OBJECT (postproc, "failed to create new output buffer meta");
986 gst_buffer_replace (&fieldbuf, NULL);
987 return GST_FLOW_ERROR;
991 GST_ERROR_OBJECT (postproc, "failed to create surface proxy from pool");
992 gst_buffer_replace (&fieldbuf, NULL);
993 return GST_FLOW_ERROR;
995 error_op_deinterlace:
997 GST_ERROR_OBJECT (postproc, "failed to apply deinterlacing filter");
998 gst_buffer_replace (&fieldbuf, NULL);
999 return GST_FLOW_NOT_SUPPORTED;
1003 GST_ERROR_OBJECT (postproc, "failed to apply VPP filters (error %d)",
1005 gst_buffer_replace (&fieldbuf, NULL);
1006 return GST_FLOW_ERROR;
1010 GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
1011 gst_buffer_replace (&fieldbuf, NULL);
1012 return GST_FLOW_ERROR;
1016 GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
1017 gst_flow_get_name (ret));
1022 static GstFlowReturn
1023 gst_vaapipostproc_process (GstBaseTransform * trans, GstBuffer * inbuf,
1026 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1027 GstVaapiVideoMeta *meta;
1028 GstClockTime timestamp;
1030 GstBuffer *fieldbuf;
1031 guint fieldbuf_flags, outbuf_flags, flags;
1032 gboolean tff, deint;
1034 meta = gst_buffer_get_vaapi_video_meta (inbuf);
1036 goto error_invalid_buffer;
1038 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
1039 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
1040 deint = should_deinterlace_buffer (postproc, inbuf);
1042 flags = gst_vaapi_video_meta_get_render_flags (meta) &
1043 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
1046 fieldbuf = create_output_buffer (postproc);
1048 goto error_create_buffer;
1049 append_output_buffer_metadata (postproc, fieldbuf, inbuf, 0);
1051 meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
1052 fieldbuf_flags = flags;
1053 fieldbuf_flags |= deint ? (tff ?
1054 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
1055 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
1056 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
1057 gst_vaapi_video_meta_set_render_flags (meta, fieldbuf_flags);
1059 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
1060 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
1062 if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
1063 goto error_copy_buffer;
1065 ret = gst_pad_push (trans->srcpad, fieldbuf);
1066 if (ret != GST_FLOW_OK)
1067 goto error_push_buffer;
1070 append_output_buffer_metadata (postproc, outbuf, inbuf, 0);
1072 meta = gst_buffer_get_vaapi_video_meta (outbuf);
1073 outbuf_flags = flags;
1074 outbuf_flags |= deint ? (tff ?
1075 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
1076 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
1077 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
1078 gst_vaapi_video_meta_set_render_flags (meta, outbuf_flags);
1080 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
1081 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
1085 error_invalid_buffer:
1087 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
1088 return GST_FLOW_ERROR;
1090 error_create_buffer:
1092 GST_ERROR_OBJECT (postproc, "failed to create output buffer");
1093 return GST_FLOW_EOS;
1097 GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
1098 gst_buffer_replace (&fieldbuf, NULL);
1099 return GST_FLOW_ERROR;
1103 GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
1104 gst_flow_get_name (ret));
1109 static GstFlowReturn
1110 gst_vaapipostproc_passthrough (GstBaseTransform * trans, GstBuffer * inbuf,
1113 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1114 GstVaapiVideoMeta *meta;
1116 /* No video processing needed, simply copy buffer metadata */
1117 meta = gst_buffer_get_vaapi_video_meta (inbuf);
1119 goto error_invalid_buffer;
1121 append_output_buffer_metadata (postproc, outbuf, inbuf,
1122 GST_BUFFER_COPY_TIMESTAMPS);
1126 error_invalid_buffer:
1128 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
1129 return GST_FLOW_ERROR;
1134 video_info_changed (GstVideoInfo * old_vip, GstVideoInfo * new_vip)
1136 if (gst_video_info_changed (old_vip, new_vip))
1138 if (GST_VIDEO_INFO_INTERLACE_MODE (old_vip) !=
1139 GST_VIDEO_INFO_INTERLACE_MODE (new_vip))
1145 video_info_update (GstCaps * caps, GstVideoInfo * info,
1146 gboolean * caps_changed_ptr)
1150 if (!gst_video_info_from_caps (&vi, caps))
1153 *caps_changed_ptr = FALSE;
1154 if (video_info_changed (info, &vi)) {
1155 *caps_changed_ptr = TRUE;
1163 gst_vaapipostproc_update_sink_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1164 gboolean * caps_changed_ptr)
1167 gboolean deinterlace;
1169 GST_INFO_OBJECT (postproc, "new sink caps = %" GST_PTR_FORMAT, caps);
1171 if (!video_info_update (caps, &postproc->sinkpad_info, caps_changed_ptr))
1174 vi = postproc->sinkpad_info;
1175 deinterlace = is_deinterlace_enabled (postproc, &vi);
1177 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
1178 postproc->field_duration = GST_VIDEO_INFO_FPS_N (&vi) > 0 ?
1179 gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&vi),
1180 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N (&vi)) : 0;
1182 postproc->get_va_surfaces = gst_caps_has_vaapi_surface (caps);
1187 gst_vaapipostproc_update_src_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1188 gboolean * caps_changed_ptr)
1190 GST_INFO_OBJECT (postproc, "new src caps = %" GST_PTR_FORMAT, caps);
1192 if (!video_info_update (caps, &postproc->srcpad_info, caps_changed_ptr))
1195 if (postproc->format != GST_VIDEO_INFO_FORMAT (&postproc->sinkpad_info) &&
1196 postproc->format != DEFAULT_FORMAT)
1197 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
1199 if (GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) !=
1200 GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
1201 || GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) !=
1202 GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))
1203 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1209 ensure_allowed_sinkpad_caps (GstVaapiPostproc * postproc)
1211 GstCaps *out_caps, *raw_caps;
1212 guint i, num_structures;
1214 if (postproc->allowed_sinkpad_caps)
1217 if (!GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc))
1220 /* Create VA caps */
1221 out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS ", "
1222 GST_CAPS_INTERLACED_MODES);
1224 GST_WARNING_OBJECT (postproc, "failed to create VA sink caps");
1228 raw_caps = gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps
1229 (GST_VAAPI_PLUGIN_BASE (postproc));
1231 gst_caps_unref (out_caps);
1232 GST_WARNING_OBJECT (postproc, "failed to create YUV sink caps");
1236 out_caps = gst_caps_make_writable (out_caps);
1237 gst_caps_append (out_caps, gst_caps_copy (raw_caps));
1239 num_structures = gst_caps_get_size (out_caps);
1240 for (i = 0; i < num_structures; i++) {
1241 GstStructure *structure;
1243 structure = gst_caps_get_structure (out_caps, i);
1247 if (postproc->filter)
1248 gst_vaapi_filter_append_caps (postproc->filter, structure);
1251 postproc->allowed_sinkpad_caps = out_caps;
1253 /* XXX: append VA/VPP filters */
1257 /* Fixup output caps so that to reflect the supported set of pixel formats */
1259 expand_allowed_srcpad_caps (GstVaapiPostproc * postproc, GstCaps * caps)
1261 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
1262 guint i, num_structures;
1263 gint gl_upload_meta_idx = -1;
1265 if (postproc->filter == NULL)
1267 if (!gst_vaapipostproc_ensure_filter_caps (postproc))
1270 /* Reset "format" field for each structure */
1271 if (!gst_vaapi_value_set_format_list (&value, postproc->filter_formats))
1273 if (gst_vaapi_value_set_format (&v_format, GST_VIDEO_FORMAT_ENCODED)) {
1274 gst_value_list_prepend_value (&value, &v_format);
1275 g_value_unset (&v_format);
1278 num_structures = gst_caps_get_size (caps);
1279 for (i = 0; i < num_structures; i++) {
1280 GstCapsFeatures *const features = gst_caps_get_features (caps, i);
1281 GstStructure *structure;
1283 structure = gst_caps_get_structure (caps, i);
1287 gst_vaapi_filter_append_caps (postproc->filter, structure);
1289 if (gst_caps_features_contains (features,
1290 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META)) {
1291 gl_upload_meta_idx = i;
1295 gst_structure_set_value (structure, "format", &value);
1297 g_value_unset (&value);
1299 if ((GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (postproc)
1300 || !gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY
1302 && gl_upload_meta_idx > -1) {
1303 gst_caps_remove_structure (caps, gl_upload_meta_idx);
1311 ensure_allowed_srcpad_caps (GstVaapiPostproc * postproc)
1315 if (postproc->allowed_srcpad_caps)
1318 /* Create initial caps from pad template */
1319 out_caps = gst_caps_from_string (gst_vaapipostproc_src_caps_str);
1321 GST_ERROR_OBJECT (postproc, "failed to create VA src caps");
1325 postproc->allowed_srcpad_caps =
1326 expand_allowed_srcpad_caps (postproc, out_caps);
1327 return postproc->allowed_srcpad_caps != NULL;
1331 gst_vaapipostproc_transform_caps_impl (GstBaseTransform * trans,
1332 GstPadDirection direction)
1334 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1336 /* Generate the sink pad caps, that could be fixated afterwards */
1337 if (direction == GST_PAD_SRC) {
1338 if (!ensure_allowed_sinkpad_caps (postproc))
1339 return gst_caps_from_string (gst_vaapipostproc_sink_caps_str);
1340 return gst_caps_ref (postproc->allowed_sinkpad_caps);
1343 /* Generate complete set of src pad caps */
1344 if (!ensure_allowed_srcpad_caps (postproc))
1346 return gst_vaapipostproc_transform_srccaps (postproc);
1350 gst_vaapipostproc_transform_caps (GstBaseTransform * trans,
1351 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1353 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1356 GST_DEBUG_OBJECT (trans,
1357 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1358 (direction == GST_PAD_SINK) ? "sink" : "src");
1360 g_mutex_lock (&postproc->postproc_lock);
1361 out_caps = gst_vaapipostproc_transform_caps_impl (trans, direction);
1362 g_mutex_unlock (&postproc->postproc_lock);
1364 if (out_caps && filter) {
1365 GstCaps *intersection;
1367 intersection = gst_caps_intersect_full (out_caps, filter,
1368 GST_CAPS_INTERSECT_FIRST);
1369 gst_caps_unref (out_caps);
1370 out_caps = intersection;
1373 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, out_caps);
1379 gst_vaapipostproc_fixate_caps (GstBaseTransform * trans,
1380 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1382 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1383 GstCaps *outcaps = NULL;
1384 gboolean same_caps, filter_updated = FALSE;
1386 GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
1387 " based on caps %" GST_PTR_FORMAT " in direction %s", othercaps, caps,
1388 (direction == GST_PAD_SINK) ? "sink" : "src");
1390 if (direction == GST_PAD_SRC) {
1391 /* @TODO: we can do better */
1392 outcaps = gst_caps_fixate (othercaps);
1396 g_mutex_lock (&postproc->postproc_lock);
1397 postproc->has_vpp = gst_vaapipostproc_ensure_filter_caps (postproc);
1398 if (check_filter_update (postproc) && update_filter (postproc)) {
1399 /* check again if changed value is default */
1400 filter_updated = check_filter_update (postproc);
1403 outcaps = gst_vaapipostproc_fixate_srccaps (postproc, caps, othercaps);
1404 g_mutex_unlock (&postproc->postproc_lock);
1406 /* set passthrough according to caps changes or filter changes */
1407 same_caps = gst_caps_is_equal (caps, outcaps);
1408 gst_base_transform_set_passthrough (trans, same_caps && !filter_updated);
1411 GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, outcaps);
1412 gst_caps_unref (othercaps);
1418 gst_vaapipostproc_transform_size (GstBaseTransform * trans,
1419 GstPadDirection direction, GstCaps * caps, gsize size,
1420 GstCaps * othercaps, gsize * othersize)
1422 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1424 if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1432 gst_vaapipostproc_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
1433 GstMeta * meta, GstBuffer * inbuf)
1435 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1437 /* don't copy GstVideoCropMeta if we are using vpp crop */
1438 if (meta->info->api == GST_VIDEO_CROP_META_API_TYPE
1439 && use_vpp_crop (postproc))
1442 /* don't copy GstParentBufferMeta if use_vpp */
1443 if (meta->info->api == GST_PARENT_BUFFER_META_API_TYPE && postproc->use_vpp)
1449 static GstFlowReturn
1450 gst_vaapipostproc_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1453 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1454 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
1455 GstBuffer *buf, *sys_buf = NULL;
1458 ret = gst_vaapi_plugin_base_get_input_buffer (plugin, inbuf, &buf);
1459 if (ret != GST_FLOW_OK)
1460 return GST_FLOW_ERROR;
1462 if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1463 GstBuffer *va_buf = create_output_buffer (postproc);
1465 ret = GST_FLOW_ERROR;
1472 ret = GST_FLOW_NOT_SUPPORTED;
1473 if (postproc->flags) {
1474 /* Use VA/VPP extensions to process this frame */
1475 if (postproc->has_vpp) {
1476 ret = gst_vaapipostproc_process_vpp (trans, buf, outbuf);
1477 if (ret != GST_FLOW_NOT_SUPPORTED)
1479 GST_WARNING_OBJECT (postproc, "unsupported VPP filters. Disabling");
1482 /* Only append picture structure meta data (top/bottom field) */
1483 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1484 ret = gst_vaapipostproc_process (trans, buf, outbuf);
1485 if (ret != GST_FLOW_NOT_SUPPORTED)
1490 /* Fallback: passthrough to the downstream element as is */
1491 ret = gst_vaapipostproc_passthrough (trans, buf, outbuf);
1494 gst_buffer_unref (buf);
1497 if (!gst_vaapi_plugin_copy_va_buffer (plugin, outbuf, sys_buf))
1498 return GST_FLOW_ERROR;
1500 gst_buffer_unref (outbuf);
1508 ensure_buffer_pool (GstVaapiPostproc * postproc, GstVideoInfo * vi)
1510 GstVaapiVideoPool *pool;
1515 gst_video_info_change_format (vi, postproc->format,
1516 GST_VIDEO_INFO_WIDTH (vi), GST_VIDEO_INFO_HEIGHT (vi));
1518 if (postproc->filter_pool
1519 && !video_info_changed (&postproc->filter_pool_info, vi))
1521 postproc->filter_pool_info = *vi;
1524 gst_vaapi_surface_pool_new_full (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc),
1525 &postproc->filter_pool_info, 0);
1529 gst_vaapi_video_pool_replace (&postproc->filter_pool, pool);
1530 gst_vaapi_video_pool_unref (pool);
1534 static GstFlowReturn
1535 gst_vaapipostproc_prepare_output_buffer (GstBaseTransform * trans,
1536 GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
1538 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1539 const GstVideoMeta *video_meta;
1542 if (gst_base_transform_is_passthrough (trans)) {
1543 *outbuf_ptr = inbuf;
1547 /* If we are not using vpp crop (i.e. forwarding crop meta to downstream)
1548 * then, ensure our output buffer pool is sized and rotated for uncropped
1550 if (gst_buffer_get_video_crop_meta (inbuf) && !use_vpp_crop (postproc)) {
1551 /* The video meta is required since the caps width/height are smaller,
1552 * which would not result in a usable GstVideoInfo for mapping the
1554 video_meta = gst_buffer_get_video_meta (inbuf);
1556 return GST_FLOW_ERROR;
1558 info = postproc->srcpad_info;
1559 info.width = video_meta->width;
1560 info.height = video_meta->height;
1562 /* compensate for rotation if needed */
1563 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1564 case GST_VIDEO_ORIENTATION_90R:
1565 case GST_VIDEO_ORIENTATION_UL_LR:
1566 case GST_VIDEO_ORIENTATION_90L:
1567 case GST_VIDEO_ORIENTATION_UR_LL:
1568 G_PRIMITIVE_SWAP (guint, info.width, info.height);
1573 ensure_buffer_pool (postproc, &info);
1576 if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1577 *outbuf_ptr = create_output_dump_buffer (postproc);
1579 *outbuf_ptr = create_output_buffer (postproc);
1583 return GST_FLOW_ERROR;
1589 ensure_srcpad_buffer_pool (GstVaapiPostproc * postproc, GstCaps * caps)
1593 if (!gst_video_info_from_caps (&vi, caps))
1596 return ensure_buffer_pool (postproc, &vi);
1600 is_native_video_format (GstVideoFormat format)
1603 for (i = 0; i < G_N_ELEMENTS (native_formats); i++)
1604 if (native_formats[i] == format)
1610 gst_vaapipostproc_set_caps (GstBaseTransform * trans, GstCaps * caps,
1613 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1614 gboolean sink_caps_changed = FALSE;
1615 gboolean src_caps_changed = FALSE;
1617 gboolean ret = FALSE;
1619 g_mutex_lock (&postproc->postproc_lock);
1620 if (!gst_vaapipostproc_update_sink_caps (postproc, caps, &sink_caps_changed))
1622 /* HACK: This is a workaround to deal with the va-intel-driver for non-native
1623 * formats while doing advanced deinterlacing. The format of reference surfaces must
1624 * be same as the format used by the driver internally for motion adaptive
1625 * deinterlacing and motion compensated deinterlacing */
1626 if (!gst_video_info_from_caps (&vinfo, caps))
1628 if (deint_method_is_advanced (postproc->deinterlace_method)
1629 && !is_native_video_format (GST_VIDEO_INFO_FORMAT (&vinfo))) {
1630 GST_WARNING_OBJECT (postproc,
1631 "Advanced deinterlacing requires the native video formats used by the driver internally");
1634 if (!gst_vaapipostproc_update_src_caps (postproc, out_caps,
1638 if (sink_caps_changed || src_caps_changed) {
1639 gst_vaapipostproc_destroy (postproc);
1640 if (!gst_vaapipostproc_create (postproc))
1642 if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (trans),
1647 if (!ensure_srcpad_buffer_pool (postproc, out_caps))
1650 postproc->same_caps = gst_caps_is_equal (caps, out_caps);
1652 if (!src_caps_changed) {
1653 /* set passthrough according to caps changes or filter changes */
1654 gst_vaapipostproc_set_passthrough (trans);
1657 ret = gst_vaapi_filter_set_colorimetry (postproc->filter,
1658 &GST_VIDEO_INFO_COLORIMETRY (GST_VAAPI_PLUGIN_BASE_SINK_PAD_INFO
1660 &GST_VIDEO_INFO_COLORIMETRY (GST_VAAPI_PLUGIN_BASE_SRC_PAD_INFO
1664 g_mutex_unlock (&postproc->postproc_lock);
1666 /* Updates the srcpad caps and send the caps downstream */
1667 if (ret && src_caps_changed)
1668 gst_base_transform_update_src_caps (trans, out_caps);
1674 gst_vaapipostproc_query (GstBaseTransform * trans,
1675 GstPadDirection direction, GstQuery * query)
1677 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1678 GstElement *const element = GST_ELEMENT (trans);
1680 if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
1681 if (gst_vaapi_handle_context_query (element, query)) {
1682 GST_DEBUG_OBJECT (postproc, "sharing display %" GST_PTR_FORMAT,
1683 GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
1689 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->query (trans,
1694 gst_vaapipostproc_propose_allocation (GstBaseTransform * trans,
1695 GstQuery * decide_query, GstQuery * query)
1697 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1698 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (trans);
1699 GstCaps *allocation_caps;
1700 GstStructure *structure;
1701 gint allocation_width, allocation_height;
1702 gint negotiated_width, negotiated_height;
1704 /* advertise to upstream that we can handle crop meta */
1706 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1708 negotiated_width = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info);
1709 negotiated_height = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info);
1711 if (negotiated_width == 0 || negotiated_height == 0)
1714 allocation_caps = NULL;
1715 gst_query_parse_allocation (query, &allocation_caps, NULL);
1716 if (!allocation_caps)
1719 structure = gst_caps_get_structure (allocation_caps, 0);
1720 if (!gst_structure_get_int (structure, "width", &allocation_width))
1722 if (!gst_structure_get_int (structure, "height", &allocation_height))
1725 if (allocation_width != negotiated_width
1726 || allocation_height != negotiated_height) {
1727 g_mutex_lock (&postproc->postproc_lock);
1728 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1729 g_mutex_unlock (&postproc->postproc_lock);
1733 /* Let vaapidecode allocate the video buffers */
1734 if (postproc->get_va_surfaces)
1736 if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
1742 gst_vaapipostproc_decide_allocation (GstBaseTransform * trans, GstQuery * query)
1744 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1746 g_mutex_lock (&postproc->postproc_lock);
1747 /* Let downstream handle the crop meta if they support it */
1748 postproc->forward_crop = (gst_query_find_allocation_meta (query,
1749 GST_VIDEO_CROP_META_API_TYPE, NULL) &&
1750 gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL));
1751 GST_DEBUG_OBJECT (postproc, "use_vpp_crop=%d", use_vpp_crop (postproc));
1752 g_mutex_unlock (&postproc->postproc_lock);
1754 return gst_vaapi_plugin_base_decide_allocation (GST_VAAPI_PLUGIN_BASE (trans),
1759 get_scale_factor (GstVaapiPostproc * const postproc, gdouble * w_factor,
1762 gdouble wd = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info);
1763 gdouble hd = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info);
1765 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1766 case GST_VIDEO_ORIENTATION_90R:
1767 case GST_VIDEO_ORIENTATION_90L:
1768 case GST_VIDEO_ORIENTATION_UR_LL:
1769 case GST_VIDEO_ORIENTATION_UL_LR:
1770 G_PRIMITIVE_SWAP (gdouble, wd, hd);
1776 *w_factor = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
1777 - (postproc->crop_left + postproc->crop_right);
1780 *h_factor = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info)
1781 - (postproc->crop_top + postproc->crop_bottom);
1786 gst_vaapipostproc_src_event (GstBaseTransform * trans, GstEvent * event)
1788 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1789 gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
1790 GstStructure *structure;
1793 GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1795 switch (GST_EVENT_TYPE (event)) {
1796 case GST_EVENT_NAVIGATION:
1798 GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1800 structure = (GstStructure *) gst_event_get_structure (event);
1801 if (gst_structure_get_double (structure, "pointer_x", &x) &&
1802 gst_structure_get_double (structure, "pointer_y", &y)) {
1803 GST_DEBUG_OBJECT (postproc, "converting %fx%f", x, y);
1805 /* video-direction compensation */
1806 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1807 case GST_VIDEO_ORIENTATION_90R:
1809 new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - 1 - x;
1811 case GST_VIDEO_ORIENTATION_90L:
1812 new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - 1 - y;
1815 case GST_VIDEO_ORIENTATION_UR_LL:
1816 new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - 1 - y;
1817 new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - 1 - x;
1819 case GST_VIDEO_ORIENTATION_UL_LR:
1823 case GST_VIDEO_ORIENTATION_180:
1824 new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - 1 - x;
1825 new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - 1 - y;
1827 case GST_VIDEO_ORIENTATION_HORIZ:
1828 new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - 1 - x;
1831 case GST_VIDEO_ORIENTATION_VERT:
1833 new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - 1 - y;
1841 /* scale compensation */
1842 get_scale_factor (postproc, &w_factor, &h_factor);
1846 /* crop compensation */
1847 new_x += postproc->crop_left;
1848 new_y += postproc->crop_top;
1850 GST_DEBUG_OBJECT (postproc, "to %fx%f", new_x, new_y);
1851 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
1852 "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
1860 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->src_event
1867 gst_vaapipostproc_sink_event (GstBaseTransform * trans, GstEvent * event)
1869 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1870 GstTagList *taglist;
1875 GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1877 switch (GST_EVENT_TYPE (event)) {
1879 gst_event_parse_tag (event, &taglist);
1881 if (gst_tag_list_get_string (taglist, "image-orientation", &orientation)) {
1883 if (!g_strcmp0 ("rotate-0", orientation))
1884 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_IDENTITY;
1885 else if (!g_strcmp0 ("rotate-90", orientation))
1886 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90R;
1887 else if (!g_strcmp0 ("rotate-180", orientation))
1888 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_180;
1889 else if (!g_strcmp0 ("rotate-270", orientation))
1890 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90L;
1891 else if (!g_strcmp0 ("flip-rotate-0", orientation))
1892 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_HORIZ;
1893 else if (!g_strcmp0 ("flip-rotate-90", orientation))
1894 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UL_LR;
1895 else if (!g_strcmp0 ("flip-rotate-180", orientation))
1896 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_VERT;
1897 else if (!g_strcmp0 ("flip-rotate-270", orientation))
1898 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UR_LL;
1902 g_free (orientation);
1905 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1906 gst_base_transform_reconfigure_src (trans);
1915 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->sink_event
1922 gst_vaapipostproc_finalize (GObject * object)
1924 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1926 gst_vaapipostproc_destroy (postproc);
1928 g_mutex_clear (&postproc->postproc_lock);
1929 gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (postproc));
1930 G_OBJECT_CLASS (gst_vaapipostproc_parent_class)->finalize (object);
1934 gst_vaapipostproc_set_property (GObject * object,
1935 guint prop_id, const GValue * value, GParamSpec * pspec)
1937 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1938 gboolean do_reconf = FALSE;
1940 g_mutex_lock (&postproc->postproc_lock);
1943 postproc->format = g_value_get_enum (value);
1947 guint prev_width = postproc->width;
1948 postproc->width = g_value_get_uint (value);
1949 do_reconf = (prev_width != postproc->width);
1954 guint prev_height = postproc->height;
1955 postproc->height = g_value_get_uint (value);
1956 do_reconf = (prev_height != postproc->height);
1959 case PROP_FORCE_ASPECT_RATIO:
1960 postproc->keep_aspect = g_value_get_boolean (value);
1962 case PROP_DEINTERLACE_MODE:
1963 postproc->deinterlace_mode = g_value_get_enum (value);
1965 case PROP_DEINTERLACE_METHOD:
1966 postproc->deinterlace_method = g_value_get_enum (value);
1969 postproc->denoise_level = g_value_get_float (value);
1970 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1973 postproc->sharpen_level = g_value_get_float (value);
1974 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1977 postproc->hue = g_value_get_float (value);
1978 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1980 case PROP_SATURATION:
1981 postproc->saturation = g_value_get_float (value);
1982 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1984 case PROP_BRIGHTNESS:
1985 postproc->brightness = g_value_get_float (value);
1986 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1989 postproc->contrast = g_value_get_float (value);
1990 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1992 case PROP_SCALE_METHOD:
1993 postproc->scale_method = g_value_get_enum (value);
1994 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1996 case PROP_VIDEO_DIRECTION:
1997 postproc->video_direction = g_value_get_enum (value);
1998 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
2000 #ifndef GST_REMOVE_DEPRECATED
2001 case PROP_SKIN_TONE_ENHANCEMENT:
2002 postproc->skintone_enhance = g_value_get_boolean (value);
2003 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SKINTONE;
2006 case PROP_SKIN_TONE_ENHANCEMENT_LEVEL:
2007 postproc->skintone_value = g_value_get_uint (value);
2008 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SKINTONE_LEVEL;
2010 case PROP_CROP_LEFT:
2011 postproc->crop_left = g_value_get_uint (value);
2012 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
2014 case PROP_CROP_RIGHT:
2015 postproc->crop_right = g_value_get_uint (value);
2016 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
2019 postproc->crop_top = g_value_get_uint (value);
2020 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
2022 case PROP_CROP_BOTTOM:
2023 postproc->crop_bottom = g_value_get_uint (value);
2024 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
2027 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2030 g_mutex_unlock (&postproc->postproc_lock);
2032 if (do_reconf || check_filter_update (postproc))
2033 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
2037 gst_vaapipostproc_get_property (GObject * object,
2038 guint prop_id, GValue * value, GParamSpec * pspec)
2040 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
2042 g_mutex_lock (&postproc->postproc_lock);
2045 g_value_set_enum (value, postproc->format);
2048 g_value_set_uint (value, postproc->width);
2051 g_value_set_uint (value, postproc->height);
2053 case PROP_FORCE_ASPECT_RATIO:
2054 g_value_set_boolean (value, postproc->keep_aspect);
2056 case PROP_DEINTERLACE_MODE:
2057 g_value_set_enum (value, postproc->deinterlace_mode);
2059 case PROP_DEINTERLACE_METHOD:
2060 g_value_set_enum (value, postproc->deinterlace_method);
2063 g_value_set_float (value, postproc->denoise_level);
2066 g_value_set_float (value, postproc->sharpen_level);
2069 g_value_set_float (value, postproc->hue);
2071 case PROP_SATURATION:
2072 g_value_set_float (value, postproc->saturation);
2074 case PROP_BRIGHTNESS:
2075 g_value_set_float (value, postproc->brightness);
2078 g_value_set_float (value, postproc->contrast);
2080 case PROP_SCALE_METHOD:
2081 g_value_set_enum (value, postproc->scale_method);
2083 case PROP_VIDEO_DIRECTION:
2084 g_value_set_enum (value, postproc->video_direction);
2086 #ifndef GST_REMOVE_DEPRECATED
2087 case PROP_SKIN_TONE_ENHANCEMENT:
2088 g_value_set_boolean (value, postproc->skintone_enhance);
2091 case PROP_SKIN_TONE_ENHANCEMENT_LEVEL:
2092 g_value_set_uint (value, postproc->skintone_value);
2094 case PROP_CROP_LEFT:
2095 g_value_set_uint (value, postproc->crop_left);
2097 case PROP_CROP_RIGHT:
2098 g_value_set_uint (value, postproc->crop_right);
2101 g_value_set_uint (value, postproc->crop_top);
2103 case PROP_CROP_BOTTOM:
2104 g_value_set_uint (value, postproc->crop_bottom);
2107 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2110 g_mutex_unlock (&postproc->postproc_lock);
2114 gst_vaapipostproc_class_init (GstVaapiPostprocClass * klass)
2116 GObjectClass *const object_class = G_OBJECT_CLASS (klass);
2117 GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
2118 GstBaseTransformClass *const trans_class = GST_BASE_TRANSFORM_CLASS (klass);
2119 GPtrArray *filter_ops;
2120 GstVaapiFilterOpInfo *filter_op;
2122 GST_DEBUG_CATEGORY_INIT (gst_debug_vaapipostproc,
2123 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
2125 gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
2127 object_class->finalize = gst_vaapipostproc_finalize;
2128 object_class->set_property = gst_vaapipostproc_set_property;
2129 object_class->get_property = gst_vaapipostproc_get_property;
2130 trans_class->start = gst_vaapipostproc_start;
2131 trans_class->stop = gst_vaapipostproc_stop;
2132 trans_class->fixate_caps = gst_vaapipostproc_fixate_caps;
2133 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
2134 trans_class->transform_size = gst_vaapipostproc_transform_size;
2135 trans_class->transform_meta = gst_vaapipostproc_transform_meta;
2136 trans_class->transform = gst_vaapipostproc_transform;
2137 trans_class->set_caps = gst_vaapipostproc_set_caps;
2138 trans_class->query = gst_vaapipostproc_query;
2139 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
2140 trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
2141 trans_class->src_event = gst_vaapipostproc_src_event;
2142 trans_class->sink_event = gst_vaapipostproc_sink_event;
2144 trans_class->prepare_output_buffer = gst_vaapipostproc_prepare_output_buffer;
2146 element_class->set_context = gst_vaapi_base_set_context;
2147 gst_element_class_set_static_metadata (element_class,
2148 "VA-API video postprocessing",
2149 "Filter/Converter/Effect/Video/Scaler/Deinterlace/Hardware",
2150 GST_PLUGIN_DESC, "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
2153 gst_element_class_add_static_pad_template (element_class,
2154 &gst_vaapipostproc_sink_factory);
2157 gst_element_class_add_static_pad_template (element_class,
2158 &gst_vaapipostproc_src_factory);
2161 * GstVaapiPostproc:deinterlace-mode:
2163 * This selects whether the deinterlacing should always be applied
2164 * or if they should only be applied on content that has the
2165 * "interlaced" flag on the caps.
2167 g_object_class_install_property
2169 PROP_DEINTERLACE_MODE,
2170 g_param_spec_enum ("deinterlace-mode",
2172 "Deinterlace mode to use",
2173 GST_VAAPI_TYPE_DEINTERLACE_MODE,
2174 DEFAULT_DEINTERLACE_MODE,
2175 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2178 * GstVaapiPostproc:deinterlace-method:
2180 * This selects the deinterlacing method to apply.
2182 g_object_class_install_property
2184 PROP_DEINTERLACE_METHOD,
2185 g_param_spec_enum ("deinterlace-method",
2186 "Deinterlace method",
2187 "Deinterlace method to use",
2188 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
2189 DEFAULT_DEINTERLACE_METHOD,
2190 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2192 filter_ops = gst_vaapi_filter_get_operations (NULL);
2197 * GstVaapiPostproc:format:
2199 * The forced output pixel format, expressed as a #GstVideoFormat.
2201 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
2203 g_object_class_install_property (object_class,
2204 PROP_FORMAT, filter_op->pspec);
2207 * GstVaapiPostproc:width:
2209 * The forced output width in pixels. If set to zero, the width is
2210 * calculated from the height if aspect ration is preserved, or
2211 * inherited from the sink caps width
2213 g_object_class_install_property
2216 g_param_spec_uint ("width",
2218 "Forced output width",
2219 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2222 * GstVaapiPostproc:height:
2224 * The forced output height in pixels. If set to zero, the height is
2225 * calculated from the width if aspect ration is preserved, or
2226 * inherited from the sink caps height
2228 g_object_class_install_property
2231 g_param_spec_uint ("height",
2233 "Forced output height",
2234 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2237 * GstVaapiPostproc:crop-left:
2239 * The number of pixels to crop at left.
2241 g_object_class_install_property
2244 g_param_spec_uint ("crop-left",
2246 "Pixels to crop at left",
2247 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2250 * GstVaapiPostproc:crop-right:
2252 * The number of pixels to crop at right.
2254 g_object_class_install_property
2257 g_param_spec_uint ("crop-right",
2259 "Pixels to crop at right",
2260 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2263 * GstVaapiPostproc:crop-top:
2265 * The number of pixels to crop at top.
2267 g_object_class_install_property
2270 g_param_spec_uint ("crop-top",
2272 "Pixels to crop at top",
2273 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2276 * GstVaapiPostproc:crop-bottom:
2278 * The number of pixels to crop at bottom.
2280 g_object_class_install_property
2283 g_param_spec_uint ("crop-bottom",
2285 "Pixels to crop at bottom",
2286 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2289 * GstVaapiPostproc:force-aspect-ratio:
2291 * When enabled, scaling respects video aspect ratio; when disabled,
2292 * the video is distorted to fit the width and height properties.
2294 g_object_class_install_property
2296 PROP_FORCE_ASPECT_RATIO,
2297 g_param_spec_boolean ("force-aspect-ratio",
2298 "Force aspect ratio",
2299 "When enabled, scaling will respect original aspect ratio",
2300 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2303 * GstVaapiPostproc:denoise:
2305 * The level of noise reduction to apply.
2307 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
2309 g_object_class_install_property (object_class,
2310 PROP_DENOISE, filter_op->pspec);
2313 * GstVaapiPostproc:sharpen:
2315 * The level of sharpening to apply for positive values, or the
2316 * level of blurring for negative values.
2318 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
2320 g_object_class_install_property (object_class,
2321 PROP_SHARPEN, filter_op->pspec);
2324 * GstVaapiPostproc:hue:
2326 * The color hue, expressed as a float value. Range is -180.0 to
2327 * 180.0. Default value is 0.0 and represents no modification.
2329 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_HUE);
2331 g_object_class_install_property (object_class, PROP_HUE, filter_op->pspec);
2334 * GstVaapiPostproc:saturation:
2336 * The color saturation, expressed as a float value. Range is 0.0 to
2337 * 2.0. Default value is 1.0 and represents no modification.
2339 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
2341 g_object_class_install_property (object_class,
2342 PROP_SATURATION, filter_op->pspec);
2345 * GstVaapiPostproc:brightness:
2347 * The color brightness, expressed as a float value. Range is -1.0
2348 * to 1.0. Default value is 0.0 and represents no modification.
2350 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
2352 g_object_class_install_property (object_class,
2353 PROP_BRIGHTNESS, filter_op->pspec);
2356 * GstVaapiPostproc:contrast:
2358 * The color contrast, expressed as a float value. Range is 0.0 to
2359 * 2.0. Default value is 1.0 and represents no modification.
2361 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
2363 g_object_class_install_property (object_class,
2364 PROP_CONTRAST, filter_op->pspec);
2367 * GstVaapiPostproc:scale-method:
2369 * The scaling method to use, expressed as an enum value. See
2370 * #GstVaapiScaleMethod.
2372 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SCALING);
2374 g_object_class_install_property (object_class,
2375 PROP_SCALE_METHOD, filter_op->pspec);
2378 * GstVaapiPostproc:video-direction:
2380 * The video-direction to use, expressed as an enum value. See
2381 * #GstVideoDirectionMethod.
2383 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_VIDEO_DIRECTION);
2385 g_object_class_install_property (object_class,
2386 PROP_VIDEO_DIRECTION, filter_op->pspec);
2388 #ifndef GST_REMOVE_DEPRECATED
2390 * GstVaapiPostproc:skin-tone-enhancement:
2392 * Apply the skin tone enhancement algorithm.
2394 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE);
2396 g_object_class_install_property (object_class,
2397 PROP_SKIN_TONE_ENHANCEMENT, filter_op->pspec);
2401 * GstVaapiPostproc:skin-tone-enhancement-setting:
2403 * Apply the skin tone enhancement algorithm with specified value.
2405 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE_LEVEL);
2407 g_object_class_install_property (object_class,
2408 PROP_SKIN_TONE_ENHANCEMENT_LEVEL, filter_op->pspec);
2410 g_ptr_array_unref (filter_ops);
2414 find_value_ptr (GstVaapiPostproc * postproc, GstVaapiFilterOp op)
2417 case GST_VAAPI_FILTER_OP_HUE:
2418 return &postproc->hue;
2419 case GST_VAAPI_FILTER_OP_SATURATION:
2420 return &postproc->saturation;
2421 case GST_VAAPI_FILTER_OP_BRIGHTNESS:
2422 return &postproc->brightness;
2423 case GST_VAAPI_FILTER_OP_CONTRAST:
2424 return &postproc->contrast;
2431 cb_set_default_value (GstVaapiPostproc * postproc, GPtrArray * filter_ops,
2432 GstVaapiFilterOp op)
2434 GstVaapiFilterOpInfo *filter_op;
2435 GParamSpecFloat *pspec;
2438 filter_op = find_filter_op (filter_ops, op);
2441 var = find_value_ptr (postproc, op);
2444 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2445 *var = pspec->default_value;
2449 skintone_set_default_value (GstVaapiPostproc * postproc, GPtrArray * filter_ops)
2451 GstVaapiFilterOpInfo *filter_op;
2452 GParamSpecUInt *pspec;
2454 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE_LEVEL);
2457 pspec = G_PARAM_SPEC_UINT (filter_op->pspec);
2458 postproc->skintone_value = pspec->default_value;
2462 gst_vaapipostproc_init (GstVaapiPostproc * postproc)
2464 GPtrArray *filter_ops;
2467 gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (postproc),
2470 g_mutex_init (&postproc->postproc_lock);
2471 postproc->format = DEFAULT_FORMAT;
2472 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
2473 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
2474 postproc->field_duration = GST_CLOCK_TIME_NONE;
2475 postproc->keep_aspect = TRUE;
2476 postproc->get_va_surfaces = TRUE;
2477 postproc->forward_crop = FALSE;
2479 /* AUTO is not valid for tag_video_direction, this is just to
2480 * ensure we setup the method as sink event tag */
2481 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_AUTO;
2483 filter_ops = gst_vaapi_filter_get_operations (NULL);
2485 for (i = GST_VAAPI_FILTER_OP_HUE; i <= GST_VAAPI_FILTER_OP_CONTRAST; i++)
2486 cb_set_default_value (postproc, filter_ops, i);
2488 skintone_set_default_value (postproc, filter_ops);
2489 g_ptr_array_unref (filter_ops);
2492 gst_video_info_init (&postproc->sinkpad_info);
2493 gst_video_info_init (&postproc->srcpad_info);
2494 gst_video_info_init (&postproc->filter_pool_info);
2497 /* ------------------------------------------------------------------------ */
2498 /* --- GstColorBalance interface --- */
2499 /* ------------------------------------------------------------------------ */
2501 #define CB_CHANNEL_FACTOR 1000.0
2505 GstVaapiFilterOp op;
2507 } ColorBalanceChannel;
2509 ColorBalanceChannel cb_channels[] = {
2511 GST_VAAPI_FILTER_OP_HUE, "VA_FILTER_HUE"}, {
2512 GST_VAAPI_FILTER_OP_SATURATION, "VA_FILTER_SATURATION"}, {
2513 GST_VAAPI_FILTER_OP_BRIGHTNESS, "VA_FILTER_BRIGHTNESS"}, {
2514 GST_VAAPI_FILTER_OP_CONTRAST, "VA_FILTER_CONTRAST"},
2518 cb_channels_init (GstVaapiPostproc * postproc)
2520 GPtrArray *filter_ops;
2521 GstVaapiFilterOpInfo *filter_op;
2522 GParamSpecFloat *pspec;
2523 GstColorBalanceChannel *channel;
2526 if (postproc->cb_channels)
2529 g_mutex_lock (&postproc->postproc_lock);
2530 if (!gst_vaapipostproc_ensure_filter (postproc)) {
2531 g_mutex_unlock (&postproc->postproc_lock);
2534 g_mutex_unlock (&postproc->postproc_lock);
2536 filter_ops = postproc->filter_ops ? g_ptr_array_ref (postproc->filter_ops)
2537 : gst_vaapi_filter_get_operations (postproc->filter);
2541 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2542 filter_op = find_filter_op (filter_ops, cb_channels[i].op);
2546 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2547 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2548 channel->label = g_strdup (cb_channels[i].name);
2549 channel->min_value = pspec->minimum * CB_CHANNEL_FACTOR;
2550 channel->max_value = pspec->maximum * CB_CHANNEL_FACTOR;
2552 postproc->cb_channels = g_list_prepend (postproc->cb_channels, channel);
2555 g_ptr_array_unref (filter_ops);
2558 static const GList *
2559 gst_vaapipostproc_colorbalance_list_channels (GstColorBalance * balance)
2561 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2563 cb_channels_init (postproc);
2564 return postproc->cb_channels;
2568 cb_get_value_ptr (GstVaapiPostproc * postproc,
2569 GstColorBalanceChannel * channel, GstVaapiPostprocFlags * flags)
2574 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2575 if (g_ascii_strcasecmp (cb_channels[i].name, channel->label) == 0)
2578 if (i >= G_N_ELEMENTS (cb_channels))
2581 ret = find_value_ptr (postproc, cb_channels[i].op);
2583 *flags = 1 << cb_channels[i].op;
2588 gst_vaapipostproc_colorbalance_set_value (GstColorBalance * balance,
2589 GstColorBalanceChannel * channel, gint value)
2591 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2592 GstVaapiPostprocFlags flags;
2593 gfloat new_val, *var;
2595 value = CLAMP (value, channel->min_value, channel->max_value);
2596 new_val = (gfloat) value / CB_CHANNEL_FACTOR;
2598 var = cb_get_value_ptr (postproc, channel, &flags);
2601 g_mutex_lock (&postproc->postproc_lock);
2602 postproc->flags |= flags;
2603 g_mutex_unlock (&postproc->postproc_lock);
2604 gst_color_balance_value_changed (balance, channel, value);
2605 if (check_filter_update (postproc))
2606 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
2610 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2614 gst_vaapipostproc_colorbalance_get_value (GstColorBalance * balance,
2615 GstColorBalanceChannel * channel)
2617 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2621 var = cb_get_value_ptr (postproc, channel, NULL);
2623 new_val = (gint) ((*var) * CB_CHANNEL_FACTOR);
2624 new_val = CLAMP (new_val, channel->min_value, channel->max_value);
2628 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2632 static GstColorBalanceType
2633 gst_vaapipostproc_colorbalance_get_balance_type (GstColorBalance * balance)
2635 return GST_COLOR_BALANCE_HARDWARE;
2639 gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data)
2641 GstColorBalanceInterface *cbface = iface;
2642 cbface->list_channels = gst_vaapipostproc_colorbalance_list_channels;
2643 cbface->set_value = gst_vaapipostproc_colorbalance_set_value;
2644 cbface->get_value = gst_vaapipostproc_colorbalance_get_value;
2645 cbface->get_balance_type = gst_vaapipostproc_colorbalance_get_balance_type;