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_VIDEO_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_VIDEO_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,
126 PROP_SKIN_TONE_ENHANCEMENT,
129 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
130 gst_vaapi_deinterlace_mode_get_type()
133 gst_vaapi_deinterlace_mode_get_type (void)
135 static GType deinterlace_mode_type = 0;
137 static const GEnumValue mode_types[] = {
138 {GST_VAAPI_DEINTERLACE_MODE_AUTO,
139 "Auto detection", "auto"},
140 {GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
141 "Force deinterlacing", "interlaced"},
142 {GST_VAAPI_DEINTERLACE_MODE_DISABLED,
143 "Never deinterlace", "disabled"},
147 if (!deinterlace_mode_type) {
148 deinterlace_mode_type =
149 g_enum_register_static ("GstVaapiDeinterlaceMode", mode_types);
151 return deinterlace_mode_type;
155 ds_reset (GstVaapiDeinterlaceState * ds)
159 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++)
160 gst_buffer_replace (&ds->buffers[i], NULL);
161 ds->buffers_index = 0;
162 ds->num_surfaces = 0;
168 ds_add_buffer (GstVaapiDeinterlaceState * ds, GstBuffer * buf)
170 gst_buffer_replace (&ds->buffers[ds->buffers_index], buf);
171 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS (ds->buffers);
174 static inline GstBuffer *
175 ds_get_buffer (GstVaapiDeinterlaceState * ds, guint index)
177 /* Note: the index increases towards older buffers.
178 i.e. buffer at index 0 means the immediately preceding buffer
179 in the history, buffer at index 1 means the one preceding the
180 surface at index 0, etc. */
181 const guint n = ds->buffers_index + G_N_ELEMENTS (ds->buffers) - index - 1;
182 return ds->buffers[n % G_N_ELEMENTS (ds->buffers)];
186 ds_set_surfaces (GstVaapiDeinterlaceState * ds)
188 GstVaapiVideoMeta *meta;
191 ds->num_surfaces = 0;
192 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++) {
193 GstBuffer *const buf = ds_get_buffer (ds, i);
197 meta = gst_buffer_get_vaapi_video_meta (buf);
198 ds->surfaces[ds->num_surfaces++] = gst_vaapi_video_meta_get_surface (meta);
202 static GstVaapiFilterOpInfo *
203 find_filter_op (GPtrArray * filter_ops, GstVaapiFilterOp op)
208 for (i = 0; i < filter_ops->len; i++) {
209 GstVaapiFilterOpInfo *const filter_op = g_ptr_array_index (filter_ops, i);
210 if (filter_op->op == op)
217 static inline gboolean
218 gst_vaapipostproc_ensure_display (GstVaapiPostproc * postproc)
221 gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (postproc));
225 gst_vaapipostproc_ensure_filter (GstVaapiPostproc * postproc)
227 if (postproc->filter)
230 if (!gst_vaapipostproc_ensure_display (postproc))
233 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
234 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
237 gst_vaapi_filter_new (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
238 if (!postproc->filter)
244 gst_vaapipostproc_ensure_filter_caps (GstVaapiPostproc * postproc)
246 if (!gst_vaapipostproc_ensure_filter (postproc))
249 if (!postproc->filter_ops) {
250 postproc->filter_ops = gst_vaapi_filter_get_operations (postproc->filter);
251 if (!postproc->filter_ops)
255 if (!postproc->filter_formats) {
256 postproc->filter_formats = gst_vaapi_filter_get_formats (postproc->filter);
257 if (!postproc->filter_formats)
264 gst_vaapipostproc_create (GstVaapiPostproc * postproc)
266 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
268 if (!gst_vaapipostproc_ensure_display (postproc))
271 postproc->use_vpp = FALSE;
272 postproc->has_vpp = gst_vaapipostproc_ensure_filter (postproc);
277 gst_vaapipostproc_destroy_filter (GstVaapiPostproc * postproc)
279 if (postproc->filter_formats) {
280 g_array_unref (postproc->filter_formats);
281 postproc->filter_formats = NULL;
284 if (postproc->filter_ops) {
285 g_ptr_array_unref (postproc->filter_ops);
286 postproc->filter_ops = NULL;
288 if (postproc->cb_channels) {
289 g_list_free_full (postproc->cb_channels, g_object_unref);
290 postproc->cb_channels = NULL;
292 gst_vaapi_filter_replace (&postproc->filter, NULL);
293 gst_vaapi_video_pool_replace (&postproc->filter_pool, NULL);
297 gst_vaapipostproc_destroy (GstVaapiPostproc * postproc)
299 ds_reset (&postproc->deinterlace_state);
300 gst_vaapipostproc_destroy_filter (postproc);
302 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
303 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
304 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
308 gst_vaapipostproc_start (GstBaseTransform * trans)
310 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
312 ds_reset (&postproc->deinterlace_state);
313 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
315 g_mutex_lock (&postproc->postproc_lock);
316 gst_vaapipostproc_ensure_filter (postproc);
317 g_mutex_unlock (&postproc->postproc_lock);
323 gst_vaapipostproc_stop (GstBaseTransform * trans)
325 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
327 g_mutex_lock (&postproc->postproc_lock);
328 ds_reset (&postproc->deinterlace_state);
329 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
331 postproc->field_duration = GST_CLOCK_TIME_NONE;
332 gst_video_info_init (&postproc->sinkpad_info);
333 gst_video_info_init (&postproc->srcpad_info);
334 gst_video_info_init (&postproc->filter_pool_info);
335 g_mutex_unlock (&postproc->postproc_lock);
341 should_deinterlace_buffer (GstVaapiPostproc * postproc, GstBuffer * buf)
343 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
344 postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
347 if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
350 g_assert (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
352 switch (GST_VIDEO_INFO_INTERLACE_MODE (&postproc->sinkpad_info)) {
353 case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
355 case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
357 case GST_VIDEO_INTERLACE_MODE_MIXED:
358 if (GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
362 GST_ERROR_OBJECT (postproc,
363 "unhandled \"interlace-mode\", disabling deinterlacing");
370 create_output_buffer (GstVaapiPostproc * postproc)
374 GstBufferPool *const pool =
375 GST_VAAPI_PLUGIN_BASE (postproc)->srcpad_buffer_pool;
378 g_return_val_if_fail (pool != NULL, NULL);
380 if (!gst_buffer_pool_is_active (pool) &&
381 !gst_buffer_pool_set_active (pool, TRUE))
382 goto error_activate_pool;
385 ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
386 if (ret != GST_FLOW_OK || !outbuf)
387 goto error_create_buffer;
393 GST_ERROR_OBJECT (postproc, "failed to activate output video buffer pool");
398 GST_ERROR_OBJECT (postproc, "failed to create output video buffer");
403 static inline GstBuffer *
404 create_output_dump_buffer (GstVaapiPostproc * postproc)
406 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
408 return gst_buffer_new_allocate (plugin->other_srcpad_allocator,
409 GST_VIDEO_INFO_SIZE (&plugin->srcpad_info),
410 &plugin->other_allocator_params);
414 copy_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
417 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (postproc);
418 GstBaseTransform *trans = GST_BASE_TRANSFORM (postproc);
422 if (!bclass->copy_metadata)
424 if (!bclass->copy_metadata (trans, inbuf, outbuf)) {
425 /* something failed, post a warning */
426 GST_ELEMENT_WARNING (trans, STREAM, NOT_IMPLEMENTED,
427 ("could not copy metadata"), (NULL));
432 append_output_buffer_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
433 GstBuffer * inbuf, guint flags)
435 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
436 GstVaapiSurfaceProxy *proxy;
438 gst_buffer_copy_into (outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
440 copy_metadata (postproc, outbuf, inbuf);
442 /* GstVaapiVideoMeta */
443 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
444 g_return_val_if_fail (inbuf_meta != NULL, FALSE);
445 proxy = gst_vaapi_video_meta_get_surface_proxy (inbuf_meta);
447 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
448 g_return_val_if_fail (outbuf_meta != NULL, FALSE);
449 proxy = gst_vaapi_surface_proxy_copy (proxy);
453 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
454 gst_vaapi_surface_proxy_unref (proxy);
459 deint_method_is_advanced (GstVaapiDeinterlaceMethod deint_method)
461 gboolean is_advanced;
463 switch (deint_method) {
464 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
465 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
475 static GstVaapiDeinterlaceMethod
476 get_next_deint_method (GstVaapiDeinterlaceMethod deint_method)
478 switch (deint_method) {
479 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
480 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
483 /* Default to basic "bob" for all others */
484 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
491 set_best_deint_method (GstVaapiPostproc * postproc, guint flags,
492 GstVaapiDeinterlaceMethod * deint_method_ptr)
494 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
498 success = gst_vaapi_filter_set_deinterlacing (postproc->filter,
499 deint_method, flags);
500 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
502 deint_method = get_next_deint_method (deint_method);
504 *deint_method_ptr = deint_method;
509 check_filter_update (GstVaapiPostproc * postproc)
511 guint filter_flag = postproc->flags;
515 if (!postproc->has_vpp)
518 for (i = GST_VAAPI_FILTER_OP_DENOISE; i <= GST_VAAPI_FILTER_OP_SKINTONE; i++) {
519 op_flag = (filter_flag >> i) & 1;
528 update_filter (GstVaapiPostproc * postproc)
530 /* Validate filters */
531 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
532 !gst_vaapi_filter_set_format (postproc->filter, postproc->format))
535 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) {
536 if (!gst_vaapi_filter_set_denoising_level (postproc->filter,
537 postproc->denoise_level))
540 if (gst_vaapi_filter_get_denoising_level_default (postproc->filter) ==
541 postproc->denoise_level)
542 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_DENOISE);
545 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) {
546 if (!gst_vaapi_filter_set_sharpening_level (postproc->filter,
547 postproc->sharpen_level))
550 if (gst_vaapi_filter_get_sharpening_level_default (postproc->filter) ==
551 postproc->sharpen_level)
552 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SHARPEN);
555 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) {
556 if (!gst_vaapi_filter_set_hue (postproc->filter, postproc->hue))
559 if (gst_vaapi_filter_get_hue_default (postproc->filter) == postproc->hue)
560 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_HUE);
563 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) {
564 if (!gst_vaapi_filter_set_saturation (postproc->filter,
565 postproc->saturation))
568 if (gst_vaapi_filter_get_saturation_default (postproc->filter) ==
569 postproc->saturation)
570 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SATURATION);
573 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) {
574 if (!gst_vaapi_filter_set_brightness (postproc->filter,
575 postproc->brightness))
578 if (gst_vaapi_filter_get_brightness_default (postproc->filter) ==
579 postproc->brightness)
580 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS);
583 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) {
584 if (!gst_vaapi_filter_set_contrast (postproc->filter, postproc->contrast))
587 if (gst_vaapi_filter_get_contrast_default (postproc->filter) ==
589 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_CONTRAST);
592 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) {
593 if (!gst_vaapi_filter_set_scaling (postproc->filter,
594 postproc->scale_method))
597 if (gst_vaapi_filter_get_scaling_default (postproc->filter) ==
598 postproc->scale_method)
599 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SCALE);
602 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION) {
603 GstVideoOrientationMethod method = postproc->video_direction;
604 if (method == GST_VIDEO_ORIENTATION_AUTO)
605 method = postproc->tag_video_direction;
607 if (!gst_vaapi_filter_set_video_direction (postproc->filter, method)) {
608 GST_ELEMENT_WARNING (postproc, LIBRARY, SETTINGS,
609 ("Unsupported video direction '%s' by driver.",
610 gst_vaapi_enum_type_get_nick
611 (GST_TYPE_VIDEO_ORIENTATION_METHOD, method)),
612 ("video direction transformation ignored"));
614 /* Don't return FALSE because other filters might be set */
617 if (gst_vaapi_filter_get_video_direction_default (postproc->filter) ==
619 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION);
622 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SKINTONE) {
623 if (!gst_vaapi_filter_set_skintone (postproc->filter,
624 postproc->skintone_enhance))
627 if (gst_vaapi_filter_get_skintone_default (postproc->filter) ==
628 postproc->skintone_enhance)
629 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SKINTONE);
636 gst_vaapipostproc_set_passthrough (GstBaseTransform * trans)
638 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
639 gboolean filter_updated = FALSE;
641 if (check_filter_update (postproc) && update_filter (postproc)) {
642 /* check again if changed value is default */
643 filter_updated = check_filter_update (postproc);
646 gst_base_transform_set_passthrough (trans, postproc->same_caps
651 replace_to_dumb_buffer_if_required (GstVaapiPostproc * postproc,
652 GstBuffer ** fieldbuf)
654 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
657 if (!GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (postproc))
660 newbuf = create_output_dump_buffer (postproc);
664 if (!gst_vaapi_plugin_copy_va_buffer (plugin, *fieldbuf, newbuf)) {
665 gst_buffer_unref (newbuf);
669 gst_buffer_replace (fieldbuf, newbuf);
670 gst_buffer_unref (newbuf);
676 use_vpp_crop (GstVaapiPostproc * postproc)
678 return !postproc->forward_crop;
682 gst_vaapipostproc_process_vpp (GstBaseTransform * trans, GstBuffer * inbuf,
685 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
686 GstVaapiDeinterlaceState *const ds = &postproc->deinterlace_state;
687 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
688 GstVaapiSurface *inbuf_surface, *outbuf_surface;
689 GstVaapiSurfaceProxy *proxy;
690 GstVaapiFilterStatus status;
691 GstClockTime timestamp;
694 GstVaapiDeinterlaceMethod deint_method;
695 guint flags, deint_flags;
696 gboolean tff, deint, deint_refs, deint_changed, discont;
697 const GstVideoCropMeta *crop_meta;
698 GstVaapiRectangle *crop_rect = NULL;
699 GstVaapiRectangle tmp_rect;
701 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
703 goto error_invalid_buffer;
704 inbuf_surface = gst_vaapi_video_meta_get_surface (inbuf_meta);
706 crop_meta = gst_buffer_get_video_crop_meta (inbuf);
707 if (crop_meta && use_vpp_crop (postproc)) {
708 GST_DEBUG_OBJECT (postproc, "cropping x=%d,y=%d,w=%d,h=%d",
709 crop_meta->x, crop_meta->y, crop_meta->width, crop_meta->height);
710 crop_rect = &tmp_rect;
711 crop_rect->x = crop_meta->x;
712 crop_rect->y = crop_meta->y;
713 crop_rect->width = crop_meta->width;
714 crop_rect->height = crop_meta->height;
717 crop_rect = (GstVaapiRectangle *)
718 gst_vaapi_video_meta_get_render_rect (inbuf_meta);
720 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
721 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
722 discont = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT);
723 deint = should_deinterlace_buffer (postproc, inbuf);
725 /* Drop references if deinterlacing conditions changed */
726 deint_changed = deint != ds->deint;
727 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
730 deint_method = postproc->deinterlace_method;
731 deint_refs = deint_method_is_advanced (deint_method);
732 if (deint_refs && 0) {
733 GstBuffer *const prev_buf = ds_get_buffer (ds, 0);
734 GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP (inbuf);
735 /* Reset deinterlacing state when there is a discontinuity */
736 if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP (prev_buf)) != pts) {
737 const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF (prev_pts, pts);
738 if (pts_diff < 0 || (postproc->field_duration > 0 &&
739 pts_diff >= postproc->field_duration * 3 - 1))
747 flags = gst_vaapi_video_meta_get_render_flags (inbuf_meta) &
748 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
751 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
752 fieldbuf = create_output_buffer (postproc);
754 goto error_create_buffer;
756 outbuf_meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
758 goto error_create_meta;
760 if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
762 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
763 (postproc->filter_pool));
765 goto error_create_proxy;
766 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
767 gst_vaapi_surface_proxy_unref (proxy);
771 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
773 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
774 if (!set_best_deint_method (postproc, deint_flags, &deint_method))
775 goto error_op_deinterlace;
777 if (deint_method != postproc->deinterlace_method) {
778 GST_DEBUG ("unsupported deinterlace-method %u. Using %u instead",
779 postproc->deinterlace_method, deint_method);
780 postproc->deinterlace_method = deint_method;
781 deint_refs = deint_method_is_advanced (deint_method);
785 ds_set_surfaces (ds);
786 if (!gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
787 ds->surfaces, ds->num_surfaces, NULL, 0))
788 goto error_op_deinterlace;
790 } else if (deint_changed) {
791 // Reset internal filter to non-deinterlacing mode
792 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
793 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
795 goto error_op_deinterlace;
798 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
799 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
800 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
801 outbuf_surface, flags);
802 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
803 goto error_process_vpp;
805 copy_metadata (postproc, fieldbuf, inbuf);
806 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
807 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
809 GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
813 if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
814 goto error_copy_buffer;
816 ret = gst_pad_push (trans->srcpad, fieldbuf);
817 if (ret != GST_FLOW_OK)
818 goto error_push_buffer;
823 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
825 goto error_create_meta;
827 if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
829 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
830 (postproc->filter_pool));
832 goto error_create_proxy;
833 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
834 gst_vaapi_surface_proxy_unref (proxy);
838 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
840 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
841 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
842 deint_method, deint_flags))
843 goto error_op_deinterlace;
846 && !gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
847 ds->surfaces, ds->num_surfaces, NULL, 0))
848 goto error_op_deinterlace;
849 } else if (deint_changed
850 && !gst_vaapi_filter_set_deinterlacing (postproc->filter, deint_method,
852 goto error_op_deinterlace;
854 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
855 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
856 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
857 outbuf_surface, flags);
858 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
859 goto error_process_vpp;
861 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
862 gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
864 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
865 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
867 GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
872 copy_metadata (postproc, outbuf, inbuf);
874 if (deint && deint_refs)
875 ds_add_buffer (ds, inbuf);
876 postproc->use_vpp = TRUE;
880 error_invalid_buffer:
882 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
883 return GST_FLOW_ERROR;
887 GST_ERROR_OBJECT (postproc, "failed to create output buffer");
888 return GST_FLOW_ERROR;
892 GST_ERROR_OBJECT (postproc, "failed to create new output buffer meta");
893 gst_buffer_replace (&fieldbuf, NULL);
894 return GST_FLOW_ERROR;
898 GST_ERROR_OBJECT (postproc, "failed to create surface proxy from pool");
899 gst_buffer_replace (&fieldbuf, NULL);
900 return GST_FLOW_ERROR;
902 error_op_deinterlace:
904 GST_ERROR_OBJECT (postproc, "failed to apply deinterlacing filter");
905 gst_buffer_replace (&fieldbuf, NULL);
906 return GST_FLOW_NOT_SUPPORTED;
910 GST_ERROR_OBJECT (postproc, "failed to apply VPP filters (error %d)",
912 gst_buffer_replace (&fieldbuf, NULL);
913 return GST_FLOW_ERROR;
917 GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
918 gst_buffer_replace (&fieldbuf, NULL);
919 return GST_FLOW_ERROR;
923 GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
924 gst_flow_get_name (ret));
930 gst_vaapipostproc_process (GstBaseTransform * trans, GstBuffer * inbuf,
933 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
934 GstVaapiVideoMeta *meta;
935 GstClockTime timestamp;
938 guint fieldbuf_flags, outbuf_flags, flags;
941 meta = gst_buffer_get_vaapi_video_meta (inbuf);
943 goto error_invalid_buffer;
945 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
946 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
947 deint = should_deinterlace_buffer (postproc, inbuf);
949 flags = gst_vaapi_video_meta_get_render_flags (meta) &
950 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
953 fieldbuf = create_output_buffer (postproc);
955 goto error_create_buffer;
956 append_output_buffer_metadata (postproc, fieldbuf, inbuf, 0);
958 meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
959 fieldbuf_flags = flags;
960 fieldbuf_flags |= deint ? (tff ?
961 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
962 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
963 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
964 gst_vaapi_video_meta_set_render_flags (meta, fieldbuf_flags);
966 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
967 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
969 if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
970 goto error_copy_buffer;
972 ret = gst_pad_push (trans->srcpad, fieldbuf);
973 if (ret != GST_FLOW_OK)
974 goto error_push_buffer;
977 append_output_buffer_metadata (postproc, outbuf, inbuf, 0);
979 meta = gst_buffer_get_vaapi_video_meta (outbuf);
980 outbuf_flags = flags;
981 outbuf_flags |= deint ? (tff ?
982 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
983 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
984 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
985 gst_vaapi_video_meta_set_render_flags (meta, outbuf_flags);
987 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
988 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
992 error_invalid_buffer:
994 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
995 return GST_FLOW_ERROR;
999 GST_ERROR_OBJECT (postproc, "failed to create output buffer");
1000 return GST_FLOW_EOS;
1004 GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
1005 gst_buffer_replace (&fieldbuf, NULL);
1006 return GST_FLOW_ERROR;
1010 GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
1011 gst_flow_get_name (ret));
1016 static GstFlowReturn
1017 gst_vaapipostproc_passthrough (GstBaseTransform * trans, GstBuffer * inbuf,
1020 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1021 GstVaapiVideoMeta *meta;
1023 /* No video processing needed, simply copy buffer metadata */
1024 meta = gst_buffer_get_vaapi_video_meta (inbuf);
1026 goto error_invalid_buffer;
1028 append_output_buffer_metadata (postproc, outbuf, inbuf,
1029 GST_BUFFER_COPY_TIMESTAMPS);
1033 error_invalid_buffer:
1035 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
1036 return GST_FLOW_ERROR;
1041 video_info_changed (GstVideoInfo * old_vip, GstVideoInfo * new_vip)
1043 if (gst_video_info_changed (old_vip, new_vip))
1045 if (GST_VIDEO_INFO_INTERLACE_MODE (old_vip) !=
1046 GST_VIDEO_INFO_INTERLACE_MODE (new_vip))
1052 video_info_update (GstCaps * caps, GstVideoInfo * info,
1053 gboolean * caps_changed_ptr)
1057 if (!gst_video_info_from_caps (&vi, caps))
1060 *caps_changed_ptr = FALSE;
1061 if (video_info_changed (info, &vi)) {
1062 *caps_changed_ptr = TRUE;
1070 gst_vaapipostproc_update_sink_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1071 gboolean * caps_changed_ptr)
1074 gboolean deinterlace;
1076 GST_INFO_OBJECT (postproc, "new sink caps = %" GST_PTR_FORMAT, caps);
1078 if (!video_info_update (caps, &postproc->sinkpad_info, caps_changed_ptr))
1081 vi = postproc->sinkpad_info;
1082 deinterlace = is_deinterlace_enabled (postproc, &vi);
1084 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
1085 postproc->field_duration = GST_VIDEO_INFO_FPS_N (&vi) > 0 ?
1086 gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&vi),
1087 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N (&vi)) : 0;
1089 postproc->get_va_surfaces = gst_caps_has_vaapi_surface (caps);
1094 gst_vaapipostproc_update_src_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1095 gboolean * caps_changed_ptr)
1097 GST_INFO_OBJECT (postproc, "new src caps = %" GST_PTR_FORMAT, caps);
1099 if (!video_info_update (caps, &postproc->srcpad_info, caps_changed_ptr))
1102 if (postproc->format != GST_VIDEO_INFO_FORMAT (&postproc->sinkpad_info) &&
1103 postproc->format != DEFAULT_FORMAT)
1104 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
1106 if (GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) !=
1107 GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
1108 || GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) !=
1109 GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))
1110 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1116 ensure_allowed_sinkpad_caps (GstVaapiPostproc * postproc)
1118 GstCaps *out_caps, *raw_caps;
1119 guint i, num_structures;
1121 if (postproc->allowed_sinkpad_caps)
1124 if (!GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc))
1127 /* Create VA caps */
1128 out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS ", "
1129 GST_CAPS_INTERLACED_MODES);
1131 GST_WARNING_OBJECT (postproc, "failed to create VA sink caps");
1135 raw_caps = gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps
1136 (GST_VAAPI_PLUGIN_BASE (postproc));
1138 gst_caps_unref (out_caps);
1139 GST_WARNING_OBJECT (postproc, "failed to create YUV sink caps");
1143 out_caps = gst_caps_make_writable (out_caps);
1144 gst_caps_append (out_caps, gst_caps_copy (raw_caps));
1146 num_structures = gst_caps_get_size (out_caps);
1147 for (i = 0; i < num_structures; i++) {
1148 GstStructure *structure;
1150 structure = gst_caps_get_structure (out_caps, i);
1154 if (postproc->filter)
1155 gst_vaapi_filter_append_caps (postproc->filter, structure);
1158 postproc->allowed_sinkpad_caps = out_caps;
1160 /* XXX: append VA/VPP filters */
1164 /* Fixup output caps so that to reflect the supported set of pixel formats */
1166 expand_allowed_srcpad_caps (GstVaapiPostproc * postproc, GstCaps * caps)
1168 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
1169 guint i, num_structures;
1170 gint gl_upload_meta_idx = -1;
1172 if (postproc->filter == NULL)
1174 if (!gst_vaapipostproc_ensure_filter_caps (postproc))
1177 /* Reset "format" field for each structure */
1178 if (!gst_vaapi_value_set_format_list (&value, postproc->filter_formats))
1180 if (gst_vaapi_value_set_format (&v_format, GST_VIDEO_FORMAT_ENCODED)) {
1181 gst_value_list_prepend_value (&value, &v_format);
1182 g_value_unset (&v_format);
1185 num_structures = gst_caps_get_size (caps);
1186 for (i = 0; i < num_structures; i++) {
1187 GstCapsFeatures *const features = gst_caps_get_features (caps, i);
1188 GstStructure *structure;
1190 structure = gst_caps_get_structure (caps, i);
1194 gst_vaapi_filter_append_caps (postproc->filter, structure);
1196 if (gst_caps_features_contains (features,
1197 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META)) {
1198 gl_upload_meta_idx = i;
1202 gst_structure_set_value (structure, "format", &value);
1204 g_value_unset (&value);
1206 if ((GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (postproc)
1207 || !gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY
1209 && gl_upload_meta_idx > -1) {
1210 gst_caps_remove_structure (caps, gl_upload_meta_idx);
1218 ensure_allowed_srcpad_caps (GstVaapiPostproc * postproc)
1222 if (postproc->allowed_srcpad_caps)
1225 /* Create initial caps from pad template */
1226 out_caps = gst_caps_from_string (gst_vaapipostproc_src_caps_str);
1228 GST_ERROR_OBJECT (postproc, "failed to create VA src caps");
1232 postproc->allowed_srcpad_caps =
1233 expand_allowed_srcpad_caps (postproc, out_caps);
1234 return postproc->allowed_srcpad_caps != NULL;
1238 gst_vaapipostproc_transform_caps_impl (GstBaseTransform * trans,
1239 GstPadDirection direction)
1241 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1243 /* Generate the sink pad caps, that could be fixated afterwards */
1244 if (direction == GST_PAD_SRC) {
1245 if (!ensure_allowed_sinkpad_caps (postproc))
1246 return gst_caps_from_string (gst_vaapipostproc_sink_caps_str);
1247 return gst_caps_ref (postproc->allowed_sinkpad_caps);
1250 /* Generate complete set of src pad caps */
1251 if (!ensure_allowed_srcpad_caps (postproc))
1253 return gst_vaapipostproc_transform_srccaps (postproc);
1257 gst_vaapipostproc_transform_caps (GstBaseTransform * trans,
1258 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1260 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1263 GST_DEBUG_OBJECT (trans,
1264 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1265 (direction == GST_PAD_SINK) ? "sink" : "src");
1267 g_mutex_lock (&postproc->postproc_lock);
1268 out_caps = gst_vaapipostproc_transform_caps_impl (trans, direction);
1269 g_mutex_unlock (&postproc->postproc_lock);
1271 if (out_caps && filter) {
1272 GstCaps *intersection;
1274 intersection = gst_caps_intersect_full (out_caps, filter,
1275 GST_CAPS_INTERSECT_FIRST);
1276 gst_caps_unref (out_caps);
1277 out_caps = intersection;
1280 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, out_caps);
1286 gst_vaapipostproc_fixate_caps (GstBaseTransform * trans,
1287 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1289 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1290 GstCaps *outcaps = NULL;
1291 gboolean same_caps, filter_updated = FALSE;
1293 GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
1294 " based on caps %" GST_PTR_FORMAT " in direction %s", othercaps, caps,
1295 (direction == GST_PAD_SINK) ? "sink" : "src");
1297 if (direction == GST_PAD_SRC) {
1298 /* @TODO: we can do better */
1299 outcaps = gst_caps_fixate (othercaps);
1303 g_mutex_lock (&postproc->postproc_lock);
1304 postproc->has_vpp = gst_vaapipostproc_ensure_filter_caps (postproc);
1305 if (check_filter_update (postproc) && update_filter (postproc)) {
1306 /* check again if changed value is default */
1307 filter_updated = check_filter_update (postproc);
1310 outcaps = gst_vaapipostproc_fixate_srccaps (postproc, caps, othercaps);
1311 g_mutex_unlock (&postproc->postproc_lock);
1313 /* set passthrough according to caps changes or filter changes */
1314 same_caps = gst_caps_is_equal (caps, outcaps);
1315 gst_base_transform_set_passthrough (trans, same_caps && !filter_updated);
1318 GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, outcaps);
1319 gst_caps_unref (othercaps);
1325 gst_vaapipostproc_transform_size (GstBaseTransform * trans,
1326 GstPadDirection direction, GstCaps * caps, gsize size,
1327 GstCaps * othercaps, gsize * othersize)
1329 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1331 if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1339 gst_vaapipostproc_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
1340 GstMeta * meta, GstBuffer * inbuf)
1342 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1344 /* don't copy GstVideoCropMeta if we are using vpp crop */
1345 if (meta->info->api == GST_VIDEO_CROP_META_API_TYPE
1346 && use_vpp_crop (postproc))
1349 /* don't copy GstParentBufferMeta if use_vpp */
1350 if (meta->info->api == GST_PARENT_BUFFER_META_API_TYPE && postproc->use_vpp)
1356 static GstFlowReturn
1357 gst_vaapipostproc_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1360 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1361 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
1362 GstBuffer *buf, *sys_buf = NULL;
1365 ret = gst_vaapi_plugin_base_get_input_buffer (plugin, inbuf, &buf);
1366 if (ret != GST_FLOW_OK)
1367 return GST_FLOW_ERROR;
1369 if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1370 GstBuffer *va_buf = create_output_buffer (postproc);
1372 ret = GST_FLOW_ERROR;
1379 ret = GST_FLOW_NOT_SUPPORTED;
1380 if (postproc->flags) {
1381 /* Use VA/VPP extensions to process this frame */
1382 if (postproc->has_vpp) {
1383 ret = gst_vaapipostproc_process_vpp (trans, buf, outbuf);
1384 if (ret != GST_FLOW_NOT_SUPPORTED)
1386 GST_WARNING_OBJECT (postproc, "unsupported VPP filters. Disabling");
1389 /* Only append picture structure meta data (top/bottom field) */
1390 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1391 ret = gst_vaapipostproc_process (trans, buf, outbuf);
1392 if (ret != GST_FLOW_NOT_SUPPORTED)
1397 /* Fallback: passthrough to the downstream element as is */
1398 ret = gst_vaapipostproc_passthrough (trans, buf, outbuf);
1401 gst_buffer_unref (buf);
1404 if (!gst_vaapi_plugin_copy_va_buffer (plugin, outbuf, sys_buf))
1405 return GST_FLOW_ERROR;
1407 gst_buffer_unref (outbuf);
1415 ensure_buffer_pool (GstVaapiPostproc * postproc, GstVideoInfo * vi)
1417 GstVaapiVideoPool *pool;
1422 gst_video_info_change_format (vi, postproc->format,
1423 GST_VIDEO_INFO_WIDTH (vi), GST_VIDEO_INFO_HEIGHT (vi));
1425 if (postproc->filter_pool
1426 && !video_info_changed (&postproc->filter_pool_info, vi))
1428 postproc->filter_pool_info = *vi;
1431 gst_vaapi_surface_pool_new_full (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc),
1432 &postproc->filter_pool_info, 0);
1436 gst_vaapi_video_pool_replace (&postproc->filter_pool, pool);
1437 gst_vaapi_video_pool_unref (pool);
1441 static GstFlowReturn
1442 gst_vaapipostproc_prepare_output_buffer (GstBaseTransform * trans,
1443 GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
1445 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1446 const GstVideoCropMeta *crop_meta;
1449 if (gst_base_transform_is_passthrough (trans)) {
1450 *outbuf_ptr = inbuf;
1454 /* If we are not using vpp crop (i.e. forwarding crop meta to downstream)
1455 * then, ensure our output buffer pool is sized for uncropped output */
1456 crop_meta = gst_buffer_get_video_crop_meta (inbuf);
1457 if (crop_meta && !use_vpp_crop (postproc)) {
1458 info = postproc->srcpad_info;
1459 info.width += crop_meta->x;
1460 info.height += crop_meta->y;
1461 ensure_buffer_pool (postproc, &info);
1464 if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1465 *outbuf_ptr = create_output_dump_buffer (postproc);
1467 *outbuf_ptr = create_output_buffer (postproc);
1471 return GST_FLOW_ERROR;
1477 ensure_srcpad_buffer_pool (GstVaapiPostproc * postproc, GstCaps * caps)
1481 if (!gst_video_info_from_caps (&vi, caps))
1484 return ensure_buffer_pool (postproc, &vi);
1488 is_native_video_format (GstVideoFormat format)
1491 for (i = 0; i < G_N_ELEMENTS (native_formats); i++)
1492 if (native_formats[i] == format)
1498 gst_vaapipostproc_set_caps (GstBaseTransform * trans, GstCaps * caps,
1501 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1502 gboolean sink_caps_changed = FALSE;
1503 gboolean src_caps_changed = FALSE;
1505 gboolean ret = FALSE;
1507 g_mutex_lock (&postproc->postproc_lock);
1508 if (!gst_vaapipostproc_update_sink_caps (postproc, caps, &sink_caps_changed))
1510 /* HACK: This is a workaround to deal with the va-intel-driver for non-native
1511 * formats while doing advanced deinterlacing. The format of reference surfaces must
1512 * be same as the format used by the driver internally for motion adaptive
1513 * deinterlacing and motion compensated deinterlacing */
1514 if (!gst_video_info_from_caps (&vinfo, caps))
1516 if (deint_method_is_advanced (postproc->deinterlace_method)
1517 && !is_native_video_format (GST_VIDEO_INFO_FORMAT (&vinfo))) {
1518 GST_WARNING_OBJECT (postproc,
1519 "Advanced deinterlacing requires the native video formats used by the driver internally");
1522 if (!gst_vaapipostproc_update_src_caps (postproc, out_caps,
1526 if (sink_caps_changed || src_caps_changed) {
1527 gst_vaapipostproc_destroy (postproc);
1528 if (!gst_vaapipostproc_create (postproc))
1530 if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (trans),
1535 if (!ensure_srcpad_buffer_pool (postproc, out_caps))
1538 postproc->same_caps = gst_caps_is_equal (caps, out_caps);
1540 if (!src_caps_changed) {
1541 /* set passthrough according to caps changes or filter changes */
1542 gst_vaapipostproc_set_passthrough (trans);
1548 g_mutex_unlock (&postproc->postproc_lock);
1550 /* Updates the srcpad caps and send the caps downstream */
1551 if (ret && src_caps_changed)
1552 gst_base_transform_update_src_caps (trans, out_caps);
1558 gst_vaapipostproc_query (GstBaseTransform * trans,
1559 GstPadDirection direction, GstQuery * query)
1561 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1562 GstElement *const element = GST_ELEMENT (trans);
1564 if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
1565 if (gst_vaapi_handle_context_query (element, query)) {
1566 GST_DEBUG_OBJECT (postproc, "sharing display %" GST_PTR_FORMAT,
1567 GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
1573 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->query (trans,
1578 gst_vaapipostproc_propose_allocation (GstBaseTransform * trans,
1579 GstQuery * decide_query, GstQuery * query)
1581 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1582 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (trans);
1583 GstCaps *allocation_caps;
1584 GstStructure *structure;
1585 gint allocation_width, allocation_height;
1586 gint negotiated_width, negotiated_height;
1588 /* advertise to upstream that we can handle crop meta */
1590 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1592 negotiated_width = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info);
1593 negotiated_height = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info);
1595 if (negotiated_width == 0 || negotiated_height == 0)
1598 allocation_caps = NULL;
1599 gst_query_parse_allocation (query, &allocation_caps, NULL);
1600 if (!allocation_caps)
1603 structure = gst_caps_get_structure (allocation_caps, 0);
1604 if (!gst_structure_get_int (structure, "width", &allocation_width))
1606 if (!gst_structure_get_int (structure, "height", &allocation_height))
1609 if (allocation_width != negotiated_width
1610 || allocation_height != negotiated_height) {
1611 g_mutex_lock (&postproc->postproc_lock);
1612 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1613 g_mutex_unlock (&postproc->postproc_lock);
1617 /* Let vaapidecode allocate the video buffers */
1618 if (postproc->get_va_surfaces)
1620 if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
1626 gst_vaapipostproc_decide_allocation (GstBaseTransform * trans, GstQuery * query)
1628 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1630 g_mutex_lock (&postproc->postproc_lock);
1631 /* Let downstream handle the crop meta if they support it */
1632 postproc->forward_crop = (gst_query_find_allocation_meta (query,
1633 GST_VIDEO_CROP_META_API_TYPE, NULL) &&
1634 gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL));
1635 GST_DEBUG_OBJECT (postproc, "use_vpp_crop=%d", use_vpp_crop (postproc));
1636 g_mutex_unlock (&postproc->postproc_lock);
1638 return gst_vaapi_plugin_base_decide_allocation (GST_VAAPI_PLUGIN_BASE (trans),
1643 gst_vaapipostproc_src_event (GstBaseTransform * trans, GstEvent * event)
1645 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1646 gdouble new_x = 0, new_y = 0, x = 0, y = 0;
1647 GstStructure *structure;
1650 GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1652 switch (GST_EVENT_TYPE (event)) {
1653 case GST_EVENT_NAVIGATION:
1655 GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1657 structure = (GstStructure *) gst_event_get_structure (event);
1658 if (gst_structure_get_double (structure, "pointer_x", &x) &&
1659 gst_structure_get_double (structure, "pointer_y", &y)) {
1660 GST_DEBUG_OBJECT (postproc, "converting %fx%f", x, y);
1662 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SIZE) {
1663 if ((GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info)
1664 != GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info))
1665 && (GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info)
1666 != GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))) {
1668 x * GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info) /
1669 GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info);
1671 y * GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info) /
1672 GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info);
1674 } else if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION) {
1675 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1676 case GST_VIDEO_ORIENTATION_90R:
1678 new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1680 case GST_VIDEO_ORIENTATION_90L:
1681 new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1684 case GST_VIDEO_ORIENTATION_UR_LL:
1685 new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1686 new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1688 case GST_VIDEO_ORIENTATION_UL_LR:
1692 case GST_VIDEO_ORIENTATION_180:
1693 new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1694 new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1696 case GST_VIDEO_ORIENTATION_HORIZ:
1697 new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1700 case GST_VIDEO_ORIENTATION_VERT:
1702 new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1711 GST_DEBUG_OBJECT (postproc, "to %fx%f", new_x, new_y);
1712 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
1713 "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
1721 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->src_event
1728 gst_vaapipostproc_sink_event (GstBaseTransform * trans, GstEvent * event)
1730 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1731 GstTagList *taglist;
1736 GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1738 switch (GST_EVENT_TYPE (event)) {
1740 gst_event_parse_tag (event, &taglist);
1742 if (gst_tag_list_get_string (taglist, "image-orientation", &orientation)) {
1744 if (!g_strcmp0 ("rotate-0", orientation))
1745 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_IDENTITY;
1746 else if (!g_strcmp0 ("rotate-90", orientation))
1747 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90R;
1748 else if (!g_strcmp0 ("rotate-180", orientation))
1749 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_180;
1750 else if (!g_strcmp0 ("rotate-270", orientation))
1751 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90L;
1752 else if (!g_strcmp0 ("flip-rotate-0", orientation))
1753 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_HORIZ;
1754 else if (!g_strcmp0 ("flip-rotate-90", orientation))
1755 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UL_LR;
1756 else if (!g_strcmp0 ("flip-rotate-180", orientation))
1757 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_VERT;
1758 else if (!g_strcmp0 ("flip-rotate-270", orientation))
1759 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UR_LL;
1763 g_free (orientation);
1766 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1767 gst_base_transform_reconfigure_src (trans);
1776 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->sink_event
1783 gst_vaapipostproc_finalize (GObject * object)
1785 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1787 gst_vaapipostproc_destroy (postproc);
1789 g_mutex_clear (&postproc->postproc_lock);
1790 gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (postproc));
1791 G_OBJECT_CLASS (gst_vaapipostproc_parent_class)->finalize (object);
1795 gst_vaapipostproc_set_property (GObject * object,
1796 guint prop_id, const GValue * value, GParamSpec * pspec)
1798 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1799 gboolean do_reconf = FALSE;
1801 g_mutex_lock (&postproc->postproc_lock);
1804 postproc->format = g_value_get_enum (value);
1808 guint prev_width = postproc->width;
1809 postproc->width = g_value_get_uint (value);
1810 do_reconf = (prev_width != postproc->width);
1815 guint prev_height = postproc->height;
1816 postproc->height = g_value_get_uint (value);
1817 do_reconf = (prev_height != postproc->height);
1820 case PROP_FORCE_ASPECT_RATIO:
1821 postproc->keep_aspect = g_value_get_boolean (value);
1823 case PROP_DEINTERLACE_MODE:
1824 postproc->deinterlace_mode = g_value_get_enum (value);
1826 case PROP_DEINTERLACE_METHOD:
1827 postproc->deinterlace_method = g_value_get_enum (value);
1830 postproc->denoise_level = g_value_get_float (value);
1831 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1834 postproc->sharpen_level = g_value_get_float (value);
1835 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1838 postproc->hue = g_value_get_float (value);
1839 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1841 case PROP_SATURATION:
1842 postproc->saturation = g_value_get_float (value);
1843 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1845 case PROP_BRIGHTNESS:
1846 postproc->brightness = g_value_get_float (value);
1847 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1850 postproc->contrast = g_value_get_float (value);
1851 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1853 case PROP_SCALE_METHOD:
1854 postproc->scale_method = g_value_get_enum (value);
1855 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1857 case PROP_VIDEO_DIRECTION:
1858 postproc->video_direction = g_value_get_enum (value);
1859 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1861 case PROP_SKIN_TONE_ENHANCEMENT:
1862 postproc->skintone_enhance = g_value_get_boolean (value);
1863 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SKINTONE;
1866 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1869 g_mutex_unlock (&postproc->postproc_lock);
1871 if (do_reconf || check_filter_update (postproc))
1872 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
1876 gst_vaapipostproc_get_property (GObject * object,
1877 guint prop_id, GValue * value, GParamSpec * pspec)
1879 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1881 g_mutex_lock (&postproc->postproc_lock);
1884 g_value_set_enum (value, postproc->format);
1887 g_value_set_uint (value, postproc->width);
1890 g_value_set_uint (value, postproc->height);
1892 case PROP_FORCE_ASPECT_RATIO:
1893 g_value_set_boolean (value, postproc->keep_aspect);
1895 case PROP_DEINTERLACE_MODE:
1896 g_value_set_enum (value, postproc->deinterlace_mode);
1898 case PROP_DEINTERLACE_METHOD:
1899 g_value_set_enum (value, postproc->deinterlace_method);
1902 g_value_set_float (value, postproc->denoise_level);
1905 g_value_set_float (value, postproc->sharpen_level);
1908 g_value_set_float (value, postproc->hue);
1910 case PROP_SATURATION:
1911 g_value_set_float (value, postproc->saturation);
1913 case PROP_BRIGHTNESS:
1914 g_value_set_float (value, postproc->brightness);
1917 g_value_set_float (value, postproc->contrast);
1919 case PROP_SCALE_METHOD:
1920 g_value_set_enum (value, postproc->scale_method);
1922 case PROP_VIDEO_DIRECTION:
1923 g_value_set_enum (value, postproc->video_direction);
1925 case PROP_SKIN_TONE_ENHANCEMENT:
1926 g_value_set_boolean (value, postproc->skintone_enhance);
1929 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1932 g_mutex_unlock (&postproc->postproc_lock);
1936 gst_vaapipostproc_class_init (GstVaapiPostprocClass * klass)
1938 GObjectClass *const object_class = G_OBJECT_CLASS (klass);
1939 GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
1940 GstBaseTransformClass *const trans_class = GST_BASE_TRANSFORM_CLASS (klass);
1941 GPtrArray *filter_ops;
1942 GstVaapiFilterOpInfo *filter_op;
1944 GST_DEBUG_CATEGORY_INIT (gst_debug_vaapipostproc,
1945 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1947 gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
1949 object_class->finalize = gst_vaapipostproc_finalize;
1950 object_class->set_property = gst_vaapipostproc_set_property;
1951 object_class->get_property = gst_vaapipostproc_get_property;
1952 trans_class->start = gst_vaapipostproc_start;
1953 trans_class->stop = gst_vaapipostproc_stop;
1954 trans_class->fixate_caps = gst_vaapipostproc_fixate_caps;
1955 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1956 trans_class->transform_size = gst_vaapipostproc_transform_size;
1957 trans_class->transform_meta = gst_vaapipostproc_transform_meta;
1958 trans_class->transform = gst_vaapipostproc_transform;
1959 trans_class->set_caps = gst_vaapipostproc_set_caps;
1960 trans_class->query = gst_vaapipostproc_query;
1961 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1962 trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
1963 trans_class->src_event = gst_vaapipostproc_src_event;
1964 trans_class->sink_event = gst_vaapipostproc_sink_event;
1966 trans_class->prepare_output_buffer = gst_vaapipostproc_prepare_output_buffer;
1968 element_class->set_context = gst_vaapi_base_set_context;
1969 gst_element_class_set_static_metadata (element_class,
1970 "VA-API video postprocessing",
1971 "Filter/Converter/Effect/Video/Scaler/Deinterlace/Hardware",
1972 GST_PLUGIN_DESC, "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1975 gst_element_class_add_static_pad_template (element_class,
1976 &gst_vaapipostproc_sink_factory);
1979 gst_element_class_add_static_pad_template (element_class,
1980 &gst_vaapipostproc_src_factory);
1983 * GstVaapiPostproc:deinterlace-mode:
1985 * This selects whether the deinterlacing should always be applied
1986 * or if they should only be applied on content that has the
1987 * "interlaced" flag on the caps.
1989 g_object_class_install_property
1991 PROP_DEINTERLACE_MODE,
1992 g_param_spec_enum ("deinterlace-mode",
1994 "Deinterlace mode to use",
1995 GST_VAAPI_TYPE_DEINTERLACE_MODE,
1996 DEFAULT_DEINTERLACE_MODE,
1997 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2000 * GstVaapiPostproc:deinterlace-method:
2002 * This selects the deinterlacing method to apply.
2004 g_object_class_install_property
2006 PROP_DEINTERLACE_METHOD,
2007 g_param_spec_enum ("deinterlace-method",
2008 "Deinterlace method",
2009 "Deinterlace method to use",
2010 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
2011 DEFAULT_DEINTERLACE_METHOD,
2012 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2014 filter_ops = gst_vaapi_filter_get_operations (NULL);
2019 * GstVaapiPostproc:format:
2021 * The forced output pixel format, expressed as a #GstVideoFormat.
2023 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
2025 g_object_class_install_property (object_class,
2026 PROP_FORMAT, filter_op->pspec);
2029 * GstVaapiPostproc:width:
2031 * The forced output width in pixels. If set to zero, the width is
2032 * calculated from the height if aspect ration is preserved, or
2033 * inherited from the sink caps width
2035 g_object_class_install_property
2038 g_param_spec_uint ("width",
2040 "Forced output width",
2041 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2044 * GstVaapiPostproc:height:
2046 * The forced output height in pixels. If set to zero, the height is
2047 * calculated from the width if aspect ration is preserved, or
2048 * inherited from the sink caps height
2050 g_object_class_install_property
2053 g_param_spec_uint ("height",
2055 "Forced output height",
2056 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2059 * GstVaapiPostproc:force-aspect-ratio:
2061 * When enabled, scaling respects video aspect ratio; when disabled,
2062 * the video is distorted to fit the width and height properties.
2064 g_object_class_install_property
2066 PROP_FORCE_ASPECT_RATIO,
2067 g_param_spec_boolean ("force-aspect-ratio",
2068 "Force aspect ratio",
2069 "When enabled, scaling will respect original aspect ratio",
2070 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2073 * GstVaapiPostproc:denoise:
2075 * The level of noise reduction to apply.
2077 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
2079 g_object_class_install_property (object_class,
2080 PROP_DENOISE, filter_op->pspec);
2083 * GstVaapiPostproc:sharpen:
2085 * The level of sharpening to apply for positive values, or the
2086 * level of blurring for negative values.
2088 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
2090 g_object_class_install_property (object_class,
2091 PROP_SHARPEN, filter_op->pspec);
2094 * GstVaapiPostproc:hue:
2096 * The color hue, expressed as a float value. Range is -180.0 to
2097 * 180.0. Default value is 0.0 and represents no modification.
2099 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_HUE);
2101 g_object_class_install_property (object_class, PROP_HUE, filter_op->pspec);
2104 * GstVaapiPostproc:saturation:
2106 * The color saturation, expressed as a float value. Range is 0.0 to
2107 * 2.0. Default value is 1.0 and represents no modification.
2109 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
2111 g_object_class_install_property (object_class,
2112 PROP_SATURATION, filter_op->pspec);
2115 * GstVaapiPostproc:brightness:
2117 * The color brightness, expressed as a float value. Range is -1.0
2118 * to 1.0. Default value is 0.0 and represents no modification.
2120 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
2122 g_object_class_install_property (object_class,
2123 PROP_BRIGHTNESS, filter_op->pspec);
2126 * GstVaapiPostproc:contrast:
2128 * The color contrast, expressed as a float value. Range is 0.0 to
2129 * 2.0. Default value is 1.0 and represents no modification.
2131 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
2133 g_object_class_install_property (object_class,
2134 PROP_CONTRAST, filter_op->pspec);
2137 * GstVaapiPostproc:scale-method:
2139 * The scaling method to use, expressed as an enum value. See
2140 * #GstVaapiScaleMethod.
2142 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SCALING);
2144 g_object_class_install_property (object_class,
2145 PROP_SCALE_METHOD, filter_op->pspec);
2148 * GstVaapiPostproc:video-direction:
2150 * The video-direction to use, expressed as an enum value. See
2151 * #GstVideoDirectionMethod.
2153 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_VIDEO_DIRECTION);
2155 g_object_class_install_property (object_class,
2156 PROP_VIDEO_DIRECTION, filter_op->pspec);
2159 * GstVaapiPostproc:skin-tone-enhancement:
2161 * Apply the skin tone enhancement algorithm.
2163 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE);
2165 g_object_class_install_property (object_class,
2166 PROP_SKIN_TONE_ENHANCEMENT, filter_op->pspec);
2168 g_ptr_array_unref (filter_ops);
2172 find_value_ptr (GstVaapiPostproc * postproc, GstVaapiFilterOp op)
2175 case GST_VAAPI_FILTER_OP_HUE:
2176 return &postproc->hue;
2177 case GST_VAAPI_FILTER_OP_SATURATION:
2178 return &postproc->saturation;
2179 case GST_VAAPI_FILTER_OP_BRIGHTNESS:
2180 return &postproc->brightness;
2181 case GST_VAAPI_FILTER_OP_CONTRAST:
2182 return &postproc->contrast;
2189 cb_set_default_value (GstVaapiPostproc * postproc, GPtrArray * filter_ops,
2190 GstVaapiFilterOp op)
2192 GstVaapiFilterOpInfo *filter_op;
2193 GParamSpecFloat *pspec;
2196 filter_op = find_filter_op (filter_ops, op);
2199 var = find_value_ptr (postproc, op);
2202 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2203 *var = pspec->default_value;
2207 gst_vaapipostproc_init (GstVaapiPostproc * postproc)
2209 GPtrArray *filter_ops;
2212 gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (postproc),
2215 g_mutex_init (&postproc->postproc_lock);
2216 postproc->format = DEFAULT_FORMAT;
2217 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
2218 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
2219 postproc->field_duration = GST_CLOCK_TIME_NONE;
2220 postproc->keep_aspect = TRUE;
2221 postproc->get_va_surfaces = TRUE;
2222 postproc->forward_crop = FALSE;
2224 /* AUTO is not valid for tag_video_direction, this is just to
2225 * ensure we setup the method as sink event tag */
2226 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_AUTO;
2228 filter_ops = gst_vaapi_filter_get_operations (NULL);
2230 for (i = GST_VAAPI_FILTER_OP_HUE; i <= GST_VAAPI_FILTER_OP_CONTRAST; i++)
2231 cb_set_default_value (postproc, filter_ops, i);
2232 g_ptr_array_unref (filter_ops);
2235 gst_video_info_init (&postproc->sinkpad_info);
2236 gst_video_info_init (&postproc->srcpad_info);
2237 gst_video_info_init (&postproc->filter_pool_info);
2240 /* ------------------------------------------------------------------------ */
2241 /* --- GstColorBalance interface --- */
2242 /* ------------------------------------------------------------------------ */
2244 #define CB_CHANNEL_FACTOR 1000.0
2248 GstVaapiFilterOp op;
2250 } ColorBalanceChannel;
2252 ColorBalanceChannel cb_channels[] = {
2254 GST_VAAPI_FILTER_OP_HUE, "VA_FILTER_HUE"}, {
2255 GST_VAAPI_FILTER_OP_SATURATION, "VA_FILTER_SATURATION"}, {
2256 GST_VAAPI_FILTER_OP_BRIGHTNESS, "VA_FILTER_BRIGHTNESS"}, {
2257 GST_VAAPI_FILTER_OP_CONTRAST, "VA_FILTER_CONTRAST"},
2261 cb_channels_init (GstVaapiPostproc * postproc)
2263 GPtrArray *filter_ops;
2264 GstVaapiFilterOpInfo *filter_op;
2265 GParamSpecFloat *pspec;
2266 GstColorBalanceChannel *channel;
2269 if (postproc->cb_channels)
2272 g_mutex_lock (&postproc->postproc_lock);
2273 if (!gst_vaapipostproc_ensure_filter (postproc)) {
2274 g_mutex_unlock (&postproc->postproc_lock);
2277 g_mutex_unlock (&postproc->postproc_lock);
2279 filter_ops = postproc->filter_ops ? g_ptr_array_ref (postproc->filter_ops)
2280 : gst_vaapi_filter_get_operations (postproc->filter);
2284 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2285 filter_op = find_filter_op (filter_ops, cb_channels[i].op);
2289 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2290 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2291 channel->label = g_strdup (cb_channels[i].name);
2292 channel->min_value = pspec->minimum * CB_CHANNEL_FACTOR;
2293 channel->max_value = pspec->maximum * CB_CHANNEL_FACTOR;
2295 postproc->cb_channels = g_list_prepend (postproc->cb_channels, channel);
2298 g_ptr_array_unref (filter_ops);
2301 static const GList *
2302 gst_vaapipostproc_colorbalance_list_channels (GstColorBalance * balance)
2304 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2306 cb_channels_init (postproc);
2307 return postproc->cb_channels;
2311 cb_get_value_ptr (GstVaapiPostproc * postproc,
2312 GstColorBalanceChannel * channel, GstVaapiPostprocFlags * flags)
2317 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2318 if (g_ascii_strcasecmp (cb_channels[i].name, channel->label) == 0)
2321 if (i >= G_N_ELEMENTS (cb_channels))
2324 ret = find_value_ptr (postproc, cb_channels[i].op);
2326 *flags = 1 << cb_channels[i].op;
2331 gst_vaapipostproc_colorbalance_set_value (GstColorBalance * balance,
2332 GstColorBalanceChannel * channel, gint value)
2334 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2335 GstVaapiPostprocFlags flags;
2336 gfloat new_val, *var;
2338 value = CLAMP (value, channel->min_value, channel->max_value);
2339 new_val = (gfloat) value / CB_CHANNEL_FACTOR;
2341 var = cb_get_value_ptr (postproc, channel, &flags);
2344 g_mutex_lock (&postproc->postproc_lock);
2345 postproc->flags |= flags;
2346 g_mutex_unlock (&postproc->postproc_lock);
2347 gst_color_balance_value_changed (balance, channel, value);
2348 if (check_filter_update (postproc))
2349 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
2353 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2357 gst_vaapipostproc_colorbalance_get_value (GstColorBalance * balance,
2358 GstColorBalanceChannel * channel)
2360 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2364 var = cb_get_value_ptr (postproc, channel, NULL);
2366 new_val = (gint) ((*var) * CB_CHANNEL_FACTOR);
2367 new_val = CLAMP (new_val, channel->min_value, channel->max_value);
2371 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2375 static GstColorBalanceType
2376 gst_vaapipostproc_colorbalance_get_balance_type (GstColorBalance * balance)
2378 return GST_COLOR_BALANCE_HARDWARE;
2382 gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data)
2384 GstColorBalanceInterface *cbface = iface;
2385 cbface->list_channels = gst_vaapipostproc_colorbalance_list_channels;
2386 cbface->set_value = gst_vaapipostproc_colorbalance_set_value;
2387 cbface->get_value = gst_vaapipostproc_colorbalance_get_value;
2388 cbface->get_balance_type = gst_vaapipostproc_colorbalance_get_balance_type;