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,
130 PROP_SKIN_TONE_ENHANCEMENT,
133 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
134 gst_vaapi_deinterlace_mode_get_type()
137 gst_vaapi_deinterlace_mode_get_type (void)
139 static GType deinterlace_mode_type = 0;
141 static const GEnumValue mode_types[] = {
142 {GST_VAAPI_DEINTERLACE_MODE_AUTO,
143 "Auto detection", "auto"},
144 {GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
145 "Force deinterlacing", "interlaced"},
146 {GST_VAAPI_DEINTERLACE_MODE_DISABLED,
147 "Never deinterlace", "disabled"},
151 if (!deinterlace_mode_type) {
152 deinterlace_mode_type =
153 g_enum_register_static ("GstVaapiDeinterlaceMode", mode_types);
155 return deinterlace_mode_type;
159 ds_reset (GstVaapiDeinterlaceState * ds)
163 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++)
164 gst_buffer_replace (&ds->buffers[i], NULL);
165 ds->buffers_index = 0;
166 ds->num_surfaces = 0;
172 ds_add_buffer (GstVaapiDeinterlaceState * ds, GstBuffer * buf)
174 gst_buffer_replace (&ds->buffers[ds->buffers_index], buf);
175 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS (ds->buffers);
178 static inline GstBuffer *
179 ds_get_buffer (GstVaapiDeinterlaceState * ds, guint index)
181 /* Note: the index increases towards older buffers.
182 i.e. buffer at index 0 means the immediately preceding buffer
183 in the history, buffer at index 1 means the one preceding the
184 surface at index 0, etc. */
185 const guint n = ds->buffers_index + G_N_ELEMENTS (ds->buffers) - index - 1;
186 return ds->buffers[n % G_N_ELEMENTS (ds->buffers)];
190 ds_set_surfaces (GstVaapiDeinterlaceState * ds)
192 GstVaapiVideoMeta *meta;
195 ds->num_surfaces = 0;
196 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++) {
197 GstBuffer *const buf = ds_get_buffer (ds, i);
201 meta = gst_buffer_get_vaapi_video_meta (buf);
202 ds->surfaces[ds->num_surfaces++] = gst_vaapi_video_meta_get_surface (meta);
206 static GstVaapiFilterOpInfo *
207 find_filter_op (GPtrArray * filter_ops, GstVaapiFilterOp op)
212 for (i = 0; i < filter_ops->len; i++) {
213 GstVaapiFilterOpInfo *const filter_op = g_ptr_array_index (filter_ops, i);
214 if (filter_op->op == op)
221 static inline gboolean
222 gst_vaapipostproc_ensure_display (GstVaapiPostproc * postproc)
225 gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (postproc));
229 gst_vaapipostproc_ensure_filter (GstVaapiPostproc * postproc)
231 if (postproc->filter)
234 if (!gst_vaapipostproc_ensure_display (postproc))
237 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
238 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
241 gst_vaapi_filter_new (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
242 if (!postproc->filter)
248 gst_vaapipostproc_ensure_filter_caps (GstVaapiPostproc * postproc)
250 if (!gst_vaapipostproc_ensure_filter (postproc))
253 if (!postproc->filter_ops) {
254 postproc->filter_ops = gst_vaapi_filter_get_operations (postproc->filter);
255 if (!postproc->filter_ops)
259 if (!postproc->filter_formats) {
260 postproc->filter_formats = gst_vaapi_filter_get_formats (postproc->filter);
261 if (!postproc->filter_formats)
268 gst_vaapipostproc_create (GstVaapiPostproc * postproc)
270 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
272 if (!gst_vaapipostproc_ensure_display (postproc))
275 postproc->use_vpp = FALSE;
276 postproc->has_vpp = gst_vaapipostproc_ensure_filter (postproc);
281 gst_vaapipostproc_destroy_filter (GstVaapiPostproc * postproc)
283 if (postproc->filter_formats) {
284 g_array_unref (postproc->filter_formats);
285 postproc->filter_formats = NULL;
288 if (postproc->filter_ops) {
289 g_ptr_array_unref (postproc->filter_ops);
290 postproc->filter_ops = NULL;
292 if (postproc->cb_channels) {
293 g_list_free_full (postproc->cb_channels, g_object_unref);
294 postproc->cb_channels = NULL;
296 gst_vaapi_filter_replace (&postproc->filter, NULL);
297 gst_vaapi_video_pool_replace (&postproc->filter_pool, NULL);
301 gst_vaapipostproc_destroy (GstVaapiPostproc * postproc)
303 ds_reset (&postproc->deinterlace_state);
304 gst_vaapipostproc_destroy_filter (postproc);
306 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
307 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
308 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
312 gst_vaapipostproc_start (GstBaseTransform * trans)
314 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
316 ds_reset (&postproc->deinterlace_state);
317 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
319 g_mutex_lock (&postproc->postproc_lock);
320 gst_vaapipostproc_ensure_filter (postproc);
321 g_mutex_unlock (&postproc->postproc_lock);
327 gst_vaapipostproc_stop (GstBaseTransform * trans)
329 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
331 g_mutex_lock (&postproc->postproc_lock);
332 ds_reset (&postproc->deinterlace_state);
333 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
335 postproc->field_duration = GST_CLOCK_TIME_NONE;
336 gst_video_info_init (&postproc->sinkpad_info);
337 gst_video_info_init (&postproc->srcpad_info);
338 gst_video_info_init (&postproc->filter_pool_info);
339 g_mutex_unlock (&postproc->postproc_lock);
345 should_deinterlace_buffer (GstVaapiPostproc * postproc, GstBuffer * buf)
347 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
348 postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
351 if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
354 g_assert (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
356 switch (GST_VIDEO_INFO_INTERLACE_MODE (&postproc->sinkpad_info)) {
357 case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
359 case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
361 case GST_VIDEO_INTERLACE_MODE_MIXED:
362 if (GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
366 GST_ERROR_OBJECT (postproc,
367 "unhandled \"interlace-mode\", disabling deinterlacing");
374 create_output_buffer (GstVaapiPostproc * postproc)
378 GstBufferPool *const pool =
379 GST_VAAPI_PLUGIN_BASE (postproc)->srcpad_buffer_pool;
382 g_return_val_if_fail (pool != NULL, NULL);
384 if (!gst_buffer_pool_is_active (pool) &&
385 !gst_buffer_pool_set_active (pool, TRUE))
386 goto error_activate_pool;
389 ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
390 if (ret != GST_FLOW_OK || !outbuf)
391 goto error_create_buffer;
397 GST_ERROR_OBJECT (postproc, "failed to activate output video buffer pool");
402 GST_ERROR_OBJECT (postproc, "failed to create output video buffer");
407 static inline GstBuffer *
408 create_output_dump_buffer (GstVaapiPostproc * postproc)
410 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
412 return gst_buffer_new_allocate (plugin->other_srcpad_allocator,
413 GST_VIDEO_INFO_SIZE (&plugin->srcpad_info),
414 &plugin->other_allocator_params);
418 copy_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
421 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (postproc);
422 GstBaseTransform *trans = GST_BASE_TRANSFORM (postproc);
426 if (!bclass->copy_metadata)
428 if (!bclass->copy_metadata (trans, inbuf, outbuf)) {
429 /* something failed, post a warning */
430 GST_ELEMENT_WARNING (trans, STREAM, NOT_IMPLEMENTED,
431 ("could not copy metadata"), (NULL));
436 append_output_buffer_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
437 GstBuffer * inbuf, guint flags)
439 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
440 GstVaapiSurfaceProxy *proxy;
442 gst_buffer_copy_into (outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
444 copy_metadata (postproc, outbuf, inbuf);
446 /* GstVaapiVideoMeta */
447 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
448 g_return_val_if_fail (inbuf_meta != NULL, FALSE);
449 proxy = gst_vaapi_video_meta_get_surface_proxy (inbuf_meta);
451 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
452 g_return_val_if_fail (outbuf_meta != NULL, FALSE);
453 proxy = gst_vaapi_surface_proxy_copy (proxy);
457 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
458 gst_vaapi_surface_proxy_unref (proxy);
463 deint_method_is_advanced (GstVaapiDeinterlaceMethod deint_method)
465 gboolean is_advanced;
467 switch (deint_method) {
468 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
469 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
479 static GstVaapiDeinterlaceMethod
480 get_next_deint_method (GstVaapiDeinterlaceMethod deint_method)
482 switch (deint_method) {
483 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
484 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
487 /* Default to basic "bob" for all others */
488 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
495 set_best_deint_method (GstVaapiPostproc * postproc, guint flags,
496 GstVaapiDeinterlaceMethod * deint_method_ptr)
498 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
502 success = gst_vaapi_filter_set_deinterlacing (postproc->filter,
503 deint_method, flags);
504 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
506 deint_method = get_next_deint_method (deint_method);
508 *deint_method_ptr = deint_method;
513 check_filter_update (GstVaapiPostproc * postproc)
515 guint filter_flag = postproc->flags;
519 if (!postproc->has_vpp)
522 for (i = GST_VAAPI_FILTER_OP_DENOISE; i <= GST_VAAPI_FILTER_OP_SKINTONE; i++) {
523 op_flag = (filter_flag >> i) & 1;
532 update_filter (GstVaapiPostproc * postproc)
534 /* Validate filters */
535 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
536 !gst_vaapi_filter_set_format (postproc->filter, postproc->format))
539 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) {
540 if (!gst_vaapi_filter_set_denoising_level (postproc->filter,
541 postproc->denoise_level))
544 if (gst_vaapi_filter_get_denoising_level_default (postproc->filter) ==
545 postproc->denoise_level)
546 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_DENOISE);
549 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) {
550 if (!gst_vaapi_filter_set_sharpening_level (postproc->filter,
551 postproc->sharpen_level))
554 if (gst_vaapi_filter_get_sharpening_level_default (postproc->filter) ==
555 postproc->sharpen_level)
556 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SHARPEN);
559 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) {
560 if (!gst_vaapi_filter_set_hue (postproc->filter, postproc->hue))
563 if (gst_vaapi_filter_get_hue_default (postproc->filter) == postproc->hue)
564 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_HUE);
567 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) {
568 if (!gst_vaapi_filter_set_saturation (postproc->filter,
569 postproc->saturation))
572 if (gst_vaapi_filter_get_saturation_default (postproc->filter) ==
573 postproc->saturation)
574 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SATURATION);
577 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) {
578 if (!gst_vaapi_filter_set_brightness (postproc->filter,
579 postproc->brightness))
582 if (gst_vaapi_filter_get_brightness_default (postproc->filter) ==
583 postproc->brightness)
584 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS);
587 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) {
588 if (!gst_vaapi_filter_set_contrast (postproc->filter, postproc->contrast))
591 if (gst_vaapi_filter_get_contrast_default (postproc->filter) ==
593 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_CONTRAST);
596 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) {
597 if (!gst_vaapi_filter_set_scaling (postproc->filter,
598 postproc->scale_method))
601 if (gst_vaapi_filter_get_scaling_default (postproc->filter) ==
602 postproc->scale_method)
603 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SCALE);
606 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION) {
607 GstVideoOrientationMethod method = postproc->video_direction;
608 if (method == GST_VIDEO_ORIENTATION_AUTO)
609 method = postproc->tag_video_direction;
611 if (!gst_vaapi_filter_set_video_direction (postproc->filter, method)) {
612 GST_ELEMENT_WARNING (postproc, LIBRARY, SETTINGS,
613 ("Unsupported video direction '%s' by driver.",
614 gst_vaapi_enum_type_get_nick
615 (GST_TYPE_VIDEO_ORIENTATION_METHOD, method)),
616 ("video direction transformation ignored"));
618 /* Don't return FALSE because other filters might be set */
621 if (gst_vaapi_filter_get_video_direction_default (postproc->filter) ==
623 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION);
626 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_CROP)
627 if ((postproc->crop_left | postproc->crop_right | postproc->crop_top
628 | postproc->crop_bottom) == 0)
629 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_CROP);
631 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_SKINTONE) {
632 if (!gst_vaapi_filter_set_skintone (postproc->filter,
633 postproc->skintone_enhance))
636 if (gst_vaapi_filter_get_skintone_default (postproc->filter) ==
637 postproc->skintone_enhance)
638 postproc->flags &= ~(GST_VAAPI_POSTPROC_FLAG_SKINTONE);
645 gst_vaapipostproc_set_passthrough (GstBaseTransform * trans)
647 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
648 gboolean filter_updated = FALSE;
650 if (check_filter_update (postproc) && update_filter (postproc)) {
651 /* check again if changed value is default */
652 filter_updated = check_filter_update (postproc);
655 gst_base_transform_set_passthrough (trans, postproc->same_caps
660 replace_to_dumb_buffer_if_required (GstVaapiPostproc * postproc,
661 GstBuffer ** fieldbuf)
663 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
666 if (!GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (postproc))
669 newbuf = create_output_dump_buffer (postproc);
673 if (!gst_vaapi_plugin_copy_va_buffer (plugin, *fieldbuf, newbuf)) {
674 gst_buffer_unref (newbuf);
678 gst_buffer_replace (fieldbuf, newbuf);
679 gst_buffer_unref (newbuf);
685 use_vpp_crop (GstVaapiPostproc * postproc)
687 return !(postproc->forward_crop
688 && !(postproc->flags & GST_VAAPI_POSTPROC_FLAG_CROP));
692 rotate_crop_meta (GstVaapiPostproc * const postproc, const GstVideoMeta * vmeta,
693 GstVideoCropMeta * crop)
697 /* The video meta is required since the caps width/height are smaller,
698 * which would not result in a usable GstVideoInfo for mapping the
703 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
704 case GST_VIDEO_ORIENTATION_HORIZ:
705 crop->x = vmeta->width - crop->width - crop->x;
707 case GST_VIDEO_ORIENTATION_VERT:
708 crop->y = vmeta->height - crop->height - crop->y;
710 case GST_VIDEO_ORIENTATION_90R:
712 crop->x = vmeta->height - crop->height - crop->y;
714 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
716 case GST_VIDEO_ORIENTATION_180:
717 crop->x = vmeta->width - crop->width - crop->x;
718 crop->y = vmeta->height - crop->height - crop->y;
720 case GST_VIDEO_ORIENTATION_90L:
723 crop->y = vmeta->width - crop->width - tmp;
724 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
726 case GST_VIDEO_ORIENTATION_UR_LL:
728 crop->x = vmeta->height - crop->height - crop->y;
729 crop->y = vmeta->width - crop->width - tmp;
730 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
732 case GST_VIDEO_ORIENTATION_UL_LR:
733 G_PRIMITIVE_SWAP (guint, crop->x, crop->y);
734 G_PRIMITIVE_SWAP (guint, crop->width, crop->height);
742 gst_vaapipostproc_process_vpp (GstBaseTransform * trans, GstBuffer * inbuf,
745 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
746 GstVaapiDeinterlaceState *const ds = &postproc->deinterlace_state;
747 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
748 GstVaapiSurface *inbuf_surface, *outbuf_surface;
749 GstVaapiSurfaceProxy *proxy;
750 GstVaapiFilterStatus status;
751 GstClockTime timestamp;
754 GstVaapiDeinterlaceMethod deint_method;
755 guint flags, deint_flags;
756 gboolean tff, deint, deint_refs, deint_changed, discont;
757 const GstVideoCropMeta *crop_meta;
758 GstVaapiRectangle *crop_rect = NULL;
759 GstVaapiRectangle tmp_rect;
761 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
763 goto error_invalid_buffer;
764 inbuf_surface = gst_vaapi_video_meta_get_surface (inbuf_meta);
766 if (use_vpp_crop (postproc)) {
767 crop_rect = &tmp_rect;
768 crop_rect->x = postproc->crop_left;
769 crop_rect->y = postproc->crop_top;
770 crop_rect->width = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
771 - (postproc->crop_left + postproc->crop_right);
772 crop_rect->height = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info)
773 - (postproc->crop_top + postproc->crop_bottom);
775 crop_meta = gst_buffer_get_video_crop_meta (inbuf);
777 crop_rect->x += crop_meta->x;
778 crop_rect->y += crop_meta->y;
783 crop_rect = (GstVaapiRectangle *)
784 gst_vaapi_video_meta_get_render_rect (inbuf_meta);
786 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
787 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
788 discont = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT);
789 deint = should_deinterlace_buffer (postproc, inbuf);
791 /* Drop references if deinterlacing conditions changed */
792 deint_changed = deint != ds->deint;
793 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
796 deint_method = postproc->deinterlace_method;
797 deint_refs = deint_method_is_advanced (deint_method);
798 if (deint_refs && 0) {
799 GstBuffer *const prev_buf = ds_get_buffer (ds, 0);
800 GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP (inbuf);
801 /* Reset deinterlacing state when there is a discontinuity */
802 if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP (prev_buf)) != pts) {
803 const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF (prev_pts, pts);
804 if (pts_diff < 0 || (postproc->field_duration > 0 &&
805 pts_diff >= postproc->field_duration * 3 - 1))
813 flags = gst_vaapi_video_meta_get_render_flags (inbuf_meta) &
814 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
817 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
818 fieldbuf = create_output_buffer (postproc);
820 goto error_create_buffer;
822 outbuf_meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
824 goto error_create_meta;
826 if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
828 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
829 (postproc->filter_pool));
831 goto error_create_proxy;
832 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
833 gst_vaapi_surface_proxy_unref (proxy);
837 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
839 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
840 if (!set_best_deint_method (postproc, deint_flags, &deint_method))
841 goto error_op_deinterlace;
843 if (deint_method != postproc->deinterlace_method) {
844 GST_DEBUG ("unsupported deinterlace-method %u. Using %u instead",
845 postproc->deinterlace_method, deint_method);
846 postproc->deinterlace_method = deint_method;
847 deint_refs = deint_method_is_advanced (deint_method);
851 ds_set_surfaces (ds);
852 if (!gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
853 ds->surfaces, ds->num_surfaces, NULL, 0))
854 goto error_op_deinterlace;
856 } else if (deint_changed) {
857 // Reset internal filter to non-deinterlacing mode
858 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
859 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
861 goto error_op_deinterlace;
864 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
865 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
866 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
867 outbuf_surface, flags);
868 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
869 goto error_process_vpp;
871 copy_metadata (postproc, fieldbuf, inbuf);
872 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
873 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
875 GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
879 if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
880 goto error_copy_buffer;
882 ret = gst_pad_push (trans->srcpad, fieldbuf);
883 if (ret != GST_FLOW_OK)
884 goto error_push_buffer;
889 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
891 goto error_create_meta;
893 if (!gst_vaapi_video_meta_get_surface_proxy (outbuf_meta)) {
895 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
896 (postproc->filter_pool));
898 goto error_create_proxy;
899 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
900 gst_vaapi_surface_proxy_unref (proxy);
904 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
906 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
907 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
908 deint_method, deint_flags))
909 goto error_op_deinterlace;
912 && !gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
913 ds->surfaces, ds->num_surfaces, NULL, 0))
914 goto error_op_deinterlace;
915 } else if (deint_changed
916 && !gst_vaapi_filter_set_deinterlacing (postproc->filter, deint_method,
918 goto error_op_deinterlace;
920 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
921 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
922 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
923 outbuf_surface, flags);
924 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
925 goto error_process_vpp;
927 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
928 gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
930 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
931 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
933 GST_BUFFER_FLAG_SET (fieldbuf, GST_BUFFER_FLAG_DISCONT);
938 copy_metadata (postproc, outbuf, inbuf);
940 rotate_crop_meta (postproc, gst_buffer_get_video_meta (inbuf),
941 gst_buffer_get_video_crop_meta (outbuf));
943 if (deint && deint_refs)
944 ds_add_buffer (ds, inbuf);
945 postproc->use_vpp = TRUE;
949 error_invalid_buffer:
951 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
952 return GST_FLOW_ERROR;
956 GST_ERROR_OBJECT (postproc, "failed to create output buffer");
957 return GST_FLOW_ERROR;
961 GST_ERROR_OBJECT (postproc, "failed to create new output buffer meta");
962 gst_buffer_replace (&fieldbuf, NULL);
963 return GST_FLOW_ERROR;
967 GST_ERROR_OBJECT (postproc, "failed to create surface proxy from pool");
968 gst_buffer_replace (&fieldbuf, NULL);
969 return GST_FLOW_ERROR;
971 error_op_deinterlace:
973 GST_ERROR_OBJECT (postproc, "failed to apply deinterlacing filter");
974 gst_buffer_replace (&fieldbuf, NULL);
975 return GST_FLOW_NOT_SUPPORTED;
979 GST_ERROR_OBJECT (postproc, "failed to apply VPP filters (error %d)",
981 gst_buffer_replace (&fieldbuf, NULL);
982 return GST_FLOW_ERROR;
986 GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
987 gst_buffer_replace (&fieldbuf, NULL);
988 return GST_FLOW_ERROR;
992 GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
993 gst_flow_get_name (ret));
999 gst_vaapipostproc_process (GstBaseTransform * trans, GstBuffer * inbuf,
1002 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1003 GstVaapiVideoMeta *meta;
1004 GstClockTime timestamp;
1006 GstBuffer *fieldbuf;
1007 guint fieldbuf_flags, outbuf_flags, flags;
1008 gboolean tff, deint;
1010 meta = gst_buffer_get_vaapi_video_meta (inbuf);
1012 goto error_invalid_buffer;
1014 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
1015 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
1016 deint = should_deinterlace_buffer (postproc, inbuf);
1018 flags = gst_vaapi_video_meta_get_render_flags (meta) &
1019 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
1022 fieldbuf = create_output_buffer (postproc);
1024 goto error_create_buffer;
1025 append_output_buffer_metadata (postproc, fieldbuf, inbuf, 0);
1027 meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
1028 fieldbuf_flags = flags;
1029 fieldbuf_flags |= deint ? (tff ?
1030 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
1031 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
1032 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
1033 gst_vaapi_video_meta_set_render_flags (meta, fieldbuf_flags);
1035 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
1036 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
1038 if (!replace_to_dumb_buffer_if_required (postproc, &fieldbuf))
1039 goto error_copy_buffer;
1041 ret = gst_pad_push (trans->srcpad, fieldbuf);
1042 if (ret != GST_FLOW_OK)
1043 goto error_push_buffer;
1046 append_output_buffer_metadata (postproc, outbuf, inbuf, 0);
1048 meta = gst_buffer_get_vaapi_video_meta (outbuf);
1049 outbuf_flags = flags;
1050 outbuf_flags |= deint ? (tff ?
1051 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
1052 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
1053 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
1054 gst_vaapi_video_meta_set_render_flags (meta, outbuf_flags);
1056 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
1057 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
1061 error_invalid_buffer:
1063 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
1064 return GST_FLOW_ERROR;
1066 error_create_buffer:
1068 GST_ERROR_OBJECT (postproc, "failed to create output buffer");
1069 return GST_FLOW_EOS;
1073 GST_ERROR_OBJECT (postproc, "failed to copy field buffer to dumb buffer");
1074 gst_buffer_replace (&fieldbuf, NULL);
1075 return GST_FLOW_ERROR;
1079 GST_DEBUG_OBJECT (postproc, "failed to push output buffer: %s",
1080 gst_flow_get_name (ret));
1085 static GstFlowReturn
1086 gst_vaapipostproc_passthrough (GstBaseTransform * trans, GstBuffer * inbuf,
1089 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1090 GstVaapiVideoMeta *meta;
1092 /* No video processing needed, simply copy buffer metadata */
1093 meta = gst_buffer_get_vaapi_video_meta (inbuf);
1095 goto error_invalid_buffer;
1097 append_output_buffer_metadata (postproc, outbuf, inbuf,
1098 GST_BUFFER_COPY_TIMESTAMPS);
1102 error_invalid_buffer:
1104 GST_ERROR_OBJECT (postproc, "failed to validate source buffer");
1105 return GST_FLOW_ERROR;
1110 video_info_changed (GstVideoInfo * old_vip, GstVideoInfo * new_vip)
1112 if (gst_video_info_changed (old_vip, new_vip))
1114 if (GST_VIDEO_INFO_INTERLACE_MODE (old_vip) !=
1115 GST_VIDEO_INFO_INTERLACE_MODE (new_vip))
1121 video_info_update (GstCaps * caps, GstVideoInfo * info,
1122 gboolean * caps_changed_ptr)
1126 if (!gst_video_info_from_caps (&vi, caps))
1129 *caps_changed_ptr = FALSE;
1130 if (video_info_changed (info, &vi)) {
1131 *caps_changed_ptr = TRUE;
1139 gst_vaapipostproc_update_sink_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1140 gboolean * caps_changed_ptr)
1143 gboolean deinterlace;
1145 GST_INFO_OBJECT (postproc, "new sink caps = %" GST_PTR_FORMAT, caps);
1147 if (!video_info_update (caps, &postproc->sinkpad_info, caps_changed_ptr))
1150 vi = postproc->sinkpad_info;
1151 deinterlace = is_deinterlace_enabled (postproc, &vi);
1153 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
1154 postproc->field_duration = GST_VIDEO_INFO_FPS_N (&vi) > 0 ?
1155 gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&vi),
1156 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N (&vi)) : 0;
1158 postproc->get_va_surfaces = gst_caps_has_vaapi_surface (caps);
1163 gst_vaapipostproc_update_src_caps (GstVaapiPostproc * postproc, GstCaps * caps,
1164 gboolean * caps_changed_ptr)
1166 GST_INFO_OBJECT (postproc, "new src caps = %" GST_PTR_FORMAT, caps);
1168 if (!video_info_update (caps, &postproc->srcpad_info, caps_changed_ptr))
1171 if (postproc->format != GST_VIDEO_INFO_FORMAT (&postproc->sinkpad_info) &&
1172 postproc->format != DEFAULT_FORMAT)
1173 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
1175 if (GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) !=
1176 GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
1177 || GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) !=
1178 GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))
1179 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1185 ensure_allowed_sinkpad_caps (GstVaapiPostproc * postproc)
1187 GstCaps *out_caps, *raw_caps;
1188 guint i, num_structures;
1190 if (postproc->allowed_sinkpad_caps)
1193 if (!GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc))
1196 /* Create VA caps */
1197 out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS ", "
1198 GST_CAPS_INTERLACED_MODES);
1200 GST_WARNING_OBJECT (postproc, "failed to create VA sink caps");
1204 raw_caps = gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps
1205 (GST_VAAPI_PLUGIN_BASE (postproc));
1207 gst_caps_unref (out_caps);
1208 GST_WARNING_OBJECT (postproc, "failed to create YUV sink caps");
1212 out_caps = gst_caps_make_writable (out_caps);
1213 gst_caps_append (out_caps, gst_caps_copy (raw_caps));
1215 num_structures = gst_caps_get_size (out_caps);
1216 for (i = 0; i < num_structures; i++) {
1217 GstStructure *structure;
1219 structure = gst_caps_get_structure (out_caps, i);
1223 if (postproc->filter)
1224 gst_vaapi_filter_append_caps (postproc->filter, structure);
1227 postproc->allowed_sinkpad_caps = out_caps;
1229 /* XXX: append VA/VPP filters */
1233 /* Fixup output caps so that to reflect the supported set of pixel formats */
1235 expand_allowed_srcpad_caps (GstVaapiPostproc * postproc, GstCaps * caps)
1237 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
1238 guint i, num_structures;
1239 gint gl_upload_meta_idx = -1;
1241 if (postproc->filter == NULL)
1243 if (!gst_vaapipostproc_ensure_filter_caps (postproc))
1246 /* Reset "format" field for each structure */
1247 if (!gst_vaapi_value_set_format_list (&value, postproc->filter_formats))
1249 if (gst_vaapi_value_set_format (&v_format, GST_VIDEO_FORMAT_ENCODED)) {
1250 gst_value_list_prepend_value (&value, &v_format);
1251 g_value_unset (&v_format);
1254 num_structures = gst_caps_get_size (caps);
1255 for (i = 0; i < num_structures; i++) {
1256 GstCapsFeatures *const features = gst_caps_get_features (caps, i);
1257 GstStructure *structure;
1259 structure = gst_caps_get_structure (caps, i);
1263 gst_vaapi_filter_append_caps (postproc->filter, structure);
1265 if (gst_caps_features_contains (features,
1266 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META)) {
1267 gl_upload_meta_idx = i;
1271 gst_structure_set_value (structure, "format", &value);
1273 g_value_unset (&value);
1275 if ((GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (postproc)
1276 || !gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY
1278 && gl_upload_meta_idx > -1) {
1279 gst_caps_remove_structure (caps, gl_upload_meta_idx);
1287 ensure_allowed_srcpad_caps (GstVaapiPostproc * postproc)
1291 if (postproc->allowed_srcpad_caps)
1294 /* Create initial caps from pad template */
1295 out_caps = gst_caps_from_string (gst_vaapipostproc_src_caps_str);
1297 GST_ERROR_OBJECT (postproc, "failed to create VA src caps");
1301 postproc->allowed_srcpad_caps =
1302 expand_allowed_srcpad_caps (postproc, out_caps);
1303 return postproc->allowed_srcpad_caps != NULL;
1307 gst_vaapipostproc_transform_caps_impl (GstBaseTransform * trans,
1308 GstPadDirection direction)
1310 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1312 /* Generate the sink pad caps, that could be fixated afterwards */
1313 if (direction == GST_PAD_SRC) {
1314 if (!ensure_allowed_sinkpad_caps (postproc))
1315 return gst_caps_from_string (gst_vaapipostproc_sink_caps_str);
1316 return gst_caps_ref (postproc->allowed_sinkpad_caps);
1319 /* Generate complete set of src pad caps */
1320 if (!ensure_allowed_srcpad_caps (postproc))
1322 return gst_vaapipostproc_transform_srccaps (postproc);
1326 gst_vaapipostproc_transform_caps (GstBaseTransform * trans,
1327 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1329 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1332 GST_DEBUG_OBJECT (trans,
1333 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1334 (direction == GST_PAD_SINK) ? "sink" : "src");
1336 g_mutex_lock (&postproc->postproc_lock);
1337 out_caps = gst_vaapipostproc_transform_caps_impl (trans, direction);
1338 g_mutex_unlock (&postproc->postproc_lock);
1340 if (out_caps && filter) {
1341 GstCaps *intersection;
1343 intersection = gst_caps_intersect_full (out_caps, filter,
1344 GST_CAPS_INTERSECT_FIRST);
1345 gst_caps_unref (out_caps);
1346 out_caps = intersection;
1349 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, out_caps);
1355 gst_vaapipostproc_fixate_caps (GstBaseTransform * trans,
1356 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1358 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1359 GstCaps *outcaps = NULL;
1360 gboolean same_caps, filter_updated = FALSE;
1362 GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
1363 " based on caps %" GST_PTR_FORMAT " in direction %s", othercaps, caps,
1364 (direction == GST_PAD_SINK) ? "sink" : "src");
1366 if (direction == GST_PAD_SRC) {
1367 /* @TODO: we can do better */
1368 outcaps = gst_caps_fixate (othercaps);
1372 g_mutex_lock (&postproc->postproc_lock);
1373 postproc->has_vpp = gst_vaapipostproc_ensure_filter_caps (postproc);
1374 if (check_filter_update (postproc) && update_filter (postproc)) {
1375 /* check again if changed value is default */
1376 filter_updated = check_filter_update (postproc);
1379 outcaps = gst_vaapipostproc_fixate_srccaps (postproc, caps, othercaps);
1380 g_mutex_unlock (&postproc->postproc_lock);
1382 /* set passthrough according to caps changes or filter changes */
1383 same_caps = gst_caps_is_equal (caps, outcaps);
1384 gst_base_transform_set_passthrough (trans, same_caps && !filter_updated);
1387 GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, outcaps);
1388 gst_caps_unref (othercaps);
1394 gst_vaapipostproc_transform_size (GstBaseTransform * trans,
1395 GstPadDirection direction, GstCaps * caps, gsize size,
1396 GstCaps * othercaps, gsize * othersize)
1398 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1400 if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1408 gst_vaapipostproc_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
1409 GstMeta * meta, GstBuffer * inbuf)
1411 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1413 /* don't copy GstVideoCropMeta if we are using vpp crop */
1414 if (meta->info->api == GST_VIDEO_CROP_META_API_TYPE
1415 && use_vpp_crop (postproc))
1418 /* don't copy GstParentBufferMeta if use_vpp */
1419 if (meta->info->api == GST_PARENT_BUFFER_META_API_TYPE && postproc->use_vpp)
1425 static GstFlowReturn
1426 gst_vaapipostproc_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1429 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1430 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (postproc);
1431 GstBuffer *buf, *sys_buf = NULL;
1434 ret = gst_vaapi_plugin_base_get_input_buffer (plugin, inbuf, &buf);
1435 if (ret != GST_FLOW_OK)
1436 return GST_FLOW_ERROR;
1438 if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1439 GstBuffer *va_buf = create_output_buffer (postproc);
1441 ret = GST_FLOW_ERROR;
1448 ret = GST_FLOW_NOT_SUPPORTED;
1449 if (postproc->flags) {
1450 /* Use VA/VPP extensions to process this frame */
1451 if (postproc->has_vpp) {
1452 ret = gst_vaapipostproc_process_vpp (trans, buf, outbuf);
1453 if (ret != GST_FLOW_NOT_SUPPORTED)
1455 GST_WARNING_OBJECT (postproc, "unsupported VPP filters. Disabling");
1458 /* Only append picture structure meta data (top/bottom field) */
1459 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1460 ret = gst_vaapipostproc_process (trans, buf, outbuf);
1461 if (ret != GST_FLOW_NOT_SUPPORTED)
1466 /* Fallback: passthrough to the downstream element as is */
1467 ret = gst_vaapipostproc_passthrough (trans, buf, outbuf);
1470 gst_buffer_unref (buf);
1473 if (!gst_vaapi_plugin_copy_va_buffer (plugin, outbuf, sys_buf))
1474 return GST_FLOW_ERROR;
1476 gst_buffer_unref (outbuf);
1484 ensure_buffer_pool (GstVaapiPostproc * postproc, GstVideoInfo * vi)
1486 GstVaapiVideoPool *pool;
1491 gst_video_info_change_format (vi, postproc->format,
1492 GST_VIDEO_INFO_WIDTH (vi), GST_VIDEO_INFO_HEIGHT (vi));
1494 if (postproc->filter_pool
1495 && !video_info_changed (&postproc->filter_pool_info, vi))
1497 postproc->filter_pool_info = *vi;
1500 gst_vaapi_surface_pool_new_full (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc),
1501 &postproc->filter_pool_info, 0);
1505 gst_vaapi_video_pool_replace (&postproc->filter_pool, pool);
1506 gst_vaapi_video_pool_unref (pool);
1510 static GstFlowReturn
1511 gst_vaapipostproc_prepare_output_buffer (GstBaseTransform * trans,
1512 GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
1514 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1515 const GstVideoMeta *video_meta;
1518 if (gst_base_transform_is_passthrough (trans)) {
1519 *outbuf_ptr = inbuf;
1523 /* If we are not using vpp crop (i.e. forwarding crop meta to downstream)
1524 * then, ensure our output buffer pool is sized and rotated for uncropped
1526 if (gst_buffer_get_video_crop_meta (inbuf) && !use_vpp_crop (postproc)) {
1527 /* The video meta is required since the caps width/height are smaller,
1528 * which would not result in a usable GstVideoInfo for mapping the
1530 video_meta = gst_buffer_get_video_meta (inbuf);
1532 return GST_FLOW_ERROR;
1534 info = postproc->srcpad_info;
1535 info.width = video_meta->width;
1536 info.height = video_meta->height;
1538 /* compensate for rotation if needed */
1539 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1540 case GST_VIDEO_ORIENTATION_90R:
1541 case GST_VIDEO_ORIENTATION_UL_LR:
1542 case GST_VIDEO_ORIENTATION_90L:
1543 case GST_VIDEO_ORIENTATION_UR_LL:
1544 G_PRIMITIVE_SWAP (guint, info.width, info.height);
1549 ensure_buffer_pool (postproc, &info);
1552 if (GST_VAAPI_PLUGIN_BASE_COPY_OUTPUT_FRAME (trans)) {
1553 *outbuf_ptr = create_output_dump_buffer (postproc);
1555 *outbuf_ptr = create_output_buffer (postproc);
1559 return GST_FLOW_ERROR;
1565 ensure_srcpad_buffer_pool (GstVaapiPostproc * postproc, GstCaps * caps)
1569 if (!gst_video_info_from_caps (&vi, caps))
1572 return ensure_buffer_pool (postproc, &vi);
1576 is_native_video_format (GstVideoFormat format)
1579 for (i = 0; i < G_N_ELEMENTS (native_formats); i++)
1580 if (native_formats[i] == format)
1586 gst_vaapipostproc_set_caps (GstBaseTransform * trans, GstCaps * caps,
1589 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1590 gboolean sink_caps_changed = FALSE;
1591 gboolean src_caps_changed = FALSE;
1593 gboolean ret = FALSE;
1595 g_mutex_lock (&postproc->postproc_lock);
1596 if (!gst_vaapipostproc_update_sink_caps (postproc, caps, &sink_caps_changed))
1598 /* HACK: This is a workaround to deal with the va-intel-driver for non-native
1599 * formats while doing advanced deinterlacing. The format of reference surfaces must
1600 * be same as the format used by the driver internally for motion adaptive
1601 * deinterlacing and motion compensated deinterlacing */
1602 if (!gst_video_info_from_caps (&vinfo, caps))
1604 if (deint_method_is_advanced (postproc->deinterlace_method)
1605 && !is_native_video_format (GST_VIDEO_INFO_FORMAT (&vinfo))) {
1606 GST_WARNING_OBJECT (postproc,
1607 "Advanced deinterlacing requires the native video formats used by the driver internally");
1610 if (!gst_vaapipostproc_update_src_caps (postproc, out_caps,
1614 if (sink_caps_changed || src_caps_changed) {
1615 gst_vaapipostproc_destroy (postproc);
1616 if (!gst_vaapipostproc_create (postproc))
1618 if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (trans),
1623 if (!ensure_srcpad_buffer_pool (postproc, out_caps))
1626 postproc->same_caps = gst_caps_is_equal (caps, out_caps);
1628 if (!src_caps_changed) {
1629 /* set passthrough according to caps changes or filter changes */
1630 gst_vaapipostproc_set_passthrough (trans);
1636 g_mutex_unlock (&postproc->postproc_lock);
1638 /* Updates the srcpad caps and send the caps downstream */
1639 if (ret && src_caps_changed)
1640 gst_base_transform_update_src_caps (trans, out_caps);
1646 gst_vaapipostproc_query (GstBaseTransform * trans,
1647 GstPadDirection direction, GstQuery * query)
1649 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1650 GstElement *const element = GST_ELEMENT (trans);
1652 if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
1653 if (gst_vaapi_handle_context_query (element, query)) {
1654 GST_DEBUG_OBJECT (postproc, "sharing display %" GST_PTR_FORMAT,
1655 GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
1661 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->query (trans,
1666 gst_vaapipostproc_propose_allocation (GstBaseTransform * trans,
1667 GstQuery * decide_query, GstQuery * query)
1669 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1670 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (trans);
1671 GstCaps *allocation_caps;
1672 GstStructure *structure;
1673 gint allocation_width, allocation_height;
1674 gint negotiated_width, negotiated_height;
1676 /* advertise to upstream that we can handle crop meta */
1678 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1680 negotiated_width = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info);
1681 negotiated_height = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info);
1683 if (negotiated_width == 0 || negotiated_height == 0)
1686 allocation_caps = NULL;
1687 gst_query_parse_allocation (query, &allocation_caps, NULL);
1688 if (!allocation_caps)
1691 structure = gst_caps_get_structure (allocation_caps, 0);
1692 if (!gst_structure_get_int (structure, "width", &allocation_width))
1694 if (!gst_structure_get_int (structure, "height", &allocation_height))
1697 if (allocation_width != negotiated_width
1698 || allocation_height != negotiated_height) {
1699 g_mutex_lock (&postproc->postproc_lock);
1700 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
1701 g_mutex_unlock (&postproc->postproc_lock);
1705 /* Let vaapidecode allocate the video buffers */
1706 if (postproc->get_va_surfaces)
1708 if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
1714 gst_vaapipostproc_decide_allocation (GstBaseTransform * trans, GstQuery * query)
1716 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1718 g_mutex_lock (&postproc->postproc_lock);
1719 /* Let downstream handle the crop meta if they support it */
1720 postproc->forward_crop = (gst_query_find_allocation_meta (query,
1721 GST_VIDEO_CROP_META_API_TYPE, NULL) &&
1722 gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL));
1723 GST_DEBUG_OBJECT (postproc, "use_vpp_crop=%d", use_vpp_crop (postproc));
1724 g_mutex_unlock (&postproc->postproc_lock);
1726 return gst_vaapi_plugin_base_decide_allocation (GST_VAAPI_PLUGIN_BASE (trans),
1731 get_scale_factor (GstVaapiPostproc * const postproc, gdouble * w_factor,
1734 gdouble wd = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info);
1735 gdouble hd = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info);
1737 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1738 case GST_VIDEO_ORIENTATION_90R:
1739 case GST_VIDEO_ORIENTATION_90L:
1740 case GST_VIDEO_ORIENTATION_UR_LL:
1741 case GST_VIDEO_ORIENTATION_UL_LR:
1742 G_PRIMITIVE_SWAP (gdouble, wd, hd);
1748 *w_factor = GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info)
1749 - (postproc->crop_left + postproc->crop_right);
1752 *h_factor = GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info)
1753 - (postproc->crop_top + postproc->crop_bottom);
1758 gst_vaapipostproc_src_event (GstBaseTransform * trans, GstEvent * event)
1760 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1761 gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
1762 GstStructure *structure;
1765 GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1767 switch (GST_EVENT_TYPE (event)) {
1768 case GST_EVENT_NAVIGATION:
1770 GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1772 structure = (GstStructure *) gst_event_get_structure (event);
1773 if (gst_structure_get_double (structure, "pointer_x", &x) &&
1774 gst_structure_get_double (structure, "pointer_y", &y)) {
1775 GST_DEBUG_OBJECT (postproc, "converting %fx%f", x, y);
1777 /* video-direction compensation */
1778 switch (gst_vaapi_filter_get_video_direction (postproc->filter)) {
1779 case GST_VIDEO_ORIENTATION_90R:
1781 new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1783 case GST_VIDEO_ORIENTATION_90L:
1784 new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1787 case GST_VIDEO_ORIENTATION_UR_LL:
1788 new_x = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1789 new_y = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1791 case GST_VIDEO_ORIENTATION_UL_LR:
1795 case GST_VIDEO_ORIENTATION_180:
1796 new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1797 new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1799 case GST_VIDEO_ORIENTATION_HORIZ:
1800 new_x = GST_VIDEO_INFO_WIDTH (&postproc->srcpad_info) - x;
1803 case GST_VIDEO_ORIENTATION_VERT:
1805 new_y = GST_VIDEO_INFO_HEIGHT (&postproc->srcpad_info) - y;
1813 /* scale compensation */
1814 get_scale_factor (postproc, &w_factor, &h_factor);
1818 /* crop compensation */
1819 new_x += postproc->crop_left;
1820 new_y += postproc->crop_top;
1822 GST_DEBUG_OBJECT (postproc, "to %fx%f", new_x, new_y);
1823 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
1824 "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
1832 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->src_event
1839 gst_vaapipostproc_sink_event (GstBaseTransform * trans, GstEvent * event)
1841 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1842 GstTagList *taglist;
1847 GST_DEBUG_OBJECT (postproc, "handling %s event", GST_EVENT_TYPE_NAME (event));
1849 switch (GST_EVENT_TYPE (event)) {
1851 gst_event_parse_tag (event, &taglist);
1853 if (gst_tag_list_get_string (taglist, "image-orientation", &orientation)) {
1855 if (!g_strcmp0 ("rotate-0", orientation))
1856 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_IDENTITY;
1857 else if (!g_strcmp0 ("rotate-90", orientation))
1858 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90R;
1859 else if (!g_strcmp0 ("rotate-180", orientation))
1860 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_180;
1861 else if (!g_strcmp0 ("rotate-270", orientation))
1862 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_90L;
1863 else if (!g_strcmp0 ("flip-rotate-0", orientation))
1864 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_HORIZ;
1865 else if (!g_strcmp0 ("flip-rotate-90", orientation))
1866 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UL_LR;
1867 else if (!g_strcmp0 ("flip-rotate-180", orientation))
1868 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_VERT;
1869 else if (!g_strcmp0 ("flip-rotate-270", orientation))
1870 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_UR_LL;
1874 g_free (orientation);
1877 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1878 gst_base_transform_reconfigure_src (trans);
1887 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->sink_event
1894 gst_vaapipostproc_finalize (GObject * object)
1896 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1898 gst_vaapipostproc_destroy (postproc);
1900 g_mutex_clear (&postproc->postproc_lock);
1901 gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (postproc));
1902 G_OBJECT_CLASS (gst_vaapipostproc_parent_class)->finalize (object);
1906 gst_vaapipostproc_set_property (GObject * object,
1907 guint prop_id, const GValue * value, GParamSpec * pspec)
1909 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1910 gboolean do_reconf = FALSE;
1912 g_mutex_lock (&postproc->postproc_lock);
1915 postproc->format = g_value_get_enum (value);
1919 guint prev_width = postproc->width;
1920 postproc->width = g_value_get_uint (value);
1921 do_reconf = (prev_width != postproc->width);
1926 guint prev_height = postproc->height;
1927 postproc->height = g_value_get_uint (value);
1928 do_reconf = (prev_height != postproc->height);
1931 case PROP_FORCE_ASPECT_RATIO:
1932 postproc->keep_aspect = g_value_get_boolean (value);
1934 case PROP_DEINTERLACE_MODE:
1935 postproc->deinterlace_mode = g_value_get_enum (value);
1937 case PROP_DEINTERLACE_METHOD:
1938 postproc->deinterlace_method = g_value_get_enum (value);
1941 postproc->denoise_level = g_value_get_float (value);
1942 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1945 postproc->sharpen_level = g_value_get_float (value);
1946 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1949 postproc->hue = g_value_get_float (value);
1950 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1952 case PROP_SATURATION:
1953 postproc->saturation = g_value_get_float (value);
1954 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1956 case PROP_BRIGHTNESS:
1957 postproc->brightness = g_value_get_float (value);
1958 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1961 postproc->contrast = g_value_get_float (value);
1962 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1964 case PROP_SCALE_METHOD:
1965 postproc->scale_method = g_value_get_enum (value);
1966 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1968 case PROP_VIDEO_DIRECTION:
1969 postproc->video_direction = g_value_get_enum (value);
1970 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_VIDEO_DIRECTION;
1972 case PROP_SKIN_TONE_ENHANCEMENT:
1973 postproc->skintone_enhance = g_value_get_boolean (value);
1974 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SKINTONE;
1976 case PROP_CROP_LEFT:
1977 postproc->crop_left = g_value_get_uint (value);
1978 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
1980 case PROP_CROP_RIGHT:
1981 postproc->crop_right = g_value_get_uint (value);
1982 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
1985 postproc->crop_top = g_value_get_uint (value);
1986 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
1988 case PROP_CROP_BOTTOM:
1989 postproc->crop_bottom = g_value_get_uint (value);
1990 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CROP;
1993 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1996 g_mutex_unlock (&postproc->postproc_lock);
1998 if (do_reconf || check_filter_update (postproc))
1999 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
2003 gst_vaapipostproc_get_property (GObject * object,
2004 guint prop_id, GValue * value, GParamSpec * pspec)
2006 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
2008 g_mutex_lock (&postproc->postproc_lock);
2011 g_value_set_enum (value, postproc->format);
2014 g_value_set_uint (value, postproc->width);
2017 g_value_set_uint (value, postproc->height);
2019 case PROP_FORCE_ASPECT_RATIO:
2020 g_value_set_boolean (value, postproc->keep_aspect);
2022 case PROP_DEINTERLACE_MODE:
2023 g_value_set_enum (value, postproc->deinterlace_mode);
2025 case PROP_DEINTERLACE_METHOD:
2026 g_value_set_enum (value, postproc->deinterlace_method);
2029 g_value_set_float (value, postproc->denoise_level);
2032 g_value_set_float (value, postproc->sharpen_level);
2035 g_value_set_float (value, postproc->hue);
2037 case PROP_SATURATION:
2038 g_value_set_float (value, postproc->saturation);
2040 case PROP_BRIGHTNESS:
2041 g_value_set_float (value, postproc->brightness);
2044 g_value_set_float (value, postproc->contrast);
2046 case PROP_SCALE_METHOD:
2047 g_value_set_enum (value, postproc->scale_method);
2049 case PROP_VIDEO_DIRECTION:
2050 g_value_set_enum (value, postproc->video_direction);
2052 case PROP_SKIN_TONE_ENHANCEMENT:
2053 g_value_set_boolean (value, postproc->skintone_enhance);
2055 case PROP_CROP_LEFT:
2056 g_value_set_uint (value, postproc->crop_left);
2058 case PROP_CROP_RIGHT:
2059 g_value_set_uint (value, postproc->crop_right);
2062 g_value_set_uint (value, postproc->crop_top);
2064 case PROP_CROP_BOTTOM:
2065 g_value_set_uint (value, postproc->crop_bottom);
2068 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2071 g_mutex_unlock (&postproc->postproc_lock);
2075 gst_vaapipostproc_class_init (GstVaapiPostprocClass * klass)
2077 GObjectClass *const object_class = G_OBJECT_CLASS (klass);
2078 GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
2079 GstBaseTransformClass *const trans_class = GST_BASE_TRANSFORM_CLASS (klass);
2080 GPtrArray *filter_ops;
2081 GstVaapiFilterOpInfo *filter_op;
2083 GST_DEBUG_CATEGORY_INIT (gst_debug_vaapipostproc,
2084 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
2086 gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
2088 object_class->finalize = gst_vaapipostproc_finalize;
2089 object_class->set_property = gst_vaapipostproc_set_property;
2090 object_class->get_property = gst_vaapipostproc_get_property;
2091 trans_class->start = gst_vaapipostproc_start;
2092 trans_class->stop = gst_vaapipostproc_stop;
2093 trans_class->fixate_caps = gst_vaapipostproc_fixate_caps;
2094 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
2095 trans_class->transform_size = gst_vaapipostproc_transform_size;
2096 trans_class->transform_meta = gst_vaapipostproc_transform_meta;
2097 trans_class->transform = gst_vaapipostproc_transform;
2098 trans_class->set_caps = gst_vaapipostproc_set_caps;
2099 trans_class->query = gst_vaapipostproc_query;
2100 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
2101 trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
2102 trans_class->src_event = gst_vaapipostproc_src_event;
2103 trans_class->sink_event = gst_vaapipostproc_sink_event;
2105 trans_class->prepare_output_buffer = gst_vaapipostproc_prepare_output_buffer;
2107 element_class->set_context = gst_vaapi_base_set_context;
2108 gst_element_class_set_static_metadata (element_class,
2109 "VA-API video postprocessing",
2110 "Filter/Converter/Effect/Video/Scaler/Deinterlace/Hardware",
2111 GST_PLUGIN_DESC, "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
2114 gst_element_class_add_static_pad_template (element_class,
2115 &gst_vaapipostproc_sink_factory);
2118 gst_element_class_add_static_pad_template (element_class,
2119 &gst_vaapipostproc_src_factory);
2122 * GstVaapiPostproc:deinterlace-mode:
2124 * This selects whether the deinterlacing should always be applied
2125 * or if they should only be applied on content that has the
2126 * "interlaced" flag on the caps.
2128 g_object_class_install_property
2130 PROP_DEINTERLACE_MODE,
2131 g_param_spec_enum ("deinterlace-mode",
2133 "Deinterlace mode to use",
2134 GST_VAAPI_TYPE_DEINTERLACE_MODE,
2135 DEFAULT_DEINTERLACE_MODE,
2136 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2139 * GstVaapiPostproc:deinterlace-method:
2141 * This selects the deinterlacing method to apply.
2143 g_object_class_install_property
2145 PROP_DEINTERLACE_METHOD,
2146 g_param_spec_enum ("deinterlace-method",
2147 "Deinterlace method",
2148 "Deinterlace method to use",
2149 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
2150 DEFAULT_DEINTERLACE_METHOD,
2151 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2153 filter_ops = gst_vaapi_filter_get_operations (NULL);
2158 * GstVaapiPostproc:format:
2160 * The forced output pixel format, expressed as a #GstVideoFormat.
2162 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
2164 g_object_class_install_property (object_class,
2165 PROP_FORMAT, filter_op->pspec);
2168 * GstVaapiPostproc:width:
2170 * The forced output width in pixels. If set to zero, the width is
2171 * calculated from the height if aspect ration is preserved, or
2172 * inherited from the sink caps width
2174 g_object_class_install_property
2177 g_param_spec_uint ("width",
2179 "Forced output width",
2180 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2183 * GstVaapiPostproc:height:
2185 * The forced output height in pixels. If set to zero, the height is
2186 * calculated from the width if aspect ration is preserved, or
2187 * inherited from the sink caps height
2189 g_object_class_install_property
2192 g_param_spec_uint ("height",
2194 "Forced output height",
2195 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2198 * GstVaapiPostproc:crop-left:
2200 * The number of pixels to crop at left.
2202 g_object_class_install_property
2205 g_param_spec_uint ("crop-left",
2207 "Pixels to crop at left",
2208 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2211 * GstVaapiPostproc:crop-right:
2213 * The number of pixels to crop at right.
2215 g_object_class_install_property
2218 g_param_spec_uint ("crop-right",
2220 "Pixels to crop at right",
2221 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2224 * GstVaapiPostproc:crop-top:
2226 * The number of pixels to crop at top.
2228 g_object_class_install_property
2231 g_param_spec_uint ("crop-top",
2233 "Pixels to crop at top",
2234 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2237 * GstVaapiPostproc:crop-bottom:
2239 * The number of pixels to crop at bottom.
2241 g_object_class_install_property
2244 g_param_spec_uint ("crop-bottom",
2246 "Pixels to crop at bottom",
2247 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2250 * GstVaapiPostproc:force-aspect-ratio:
2252 * When enabled, scaling respects video aspect ratio; when disabled,
2253 * the video is distorted to fit the width and height properties.
2255 g_object_class_install_property
2257 PROP_FORCE_ASPECT_RATIO,
2258 g_param_spec_boolean ("force-aspect-ratio",
2259 "Force aspect ratio",
2260 "When enabled, scaling will respect original aspect ratio",
2261 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2264 * GstVaapiPostproc:denoise:
2266 * The level of noise reduction to apply.
2268 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
2270 g_object_class_install_property (object_class,
2271 PROP_DENOISE, filter_op->pspec);
2274 * GstVaapiPostproc:sharpen:
2276 * The level of sharpening to apply for positive values, or the
2277 * level of blurring for negative values.
2279 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
2281 g_object_class_install_property (object_class,
2282 PROP_SHARPEN, filter_op->pspec);
2285 * GstVaapiPostproc:hue:
2287 * The color hue, expressed as a float value. Range is -180.0 to
2288 * 180.0. Default value is 0.0 and represents no modification.
2290 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_HUE);
2292 g_object_class_install_property (object_class, PROP_HUE, filter_op->pspec);
2295 * GstVaapiPostproc:saturation:
2297 * The color saturation, expressed as a float value. Range is 0.0 to
2298 * 2.0. Default value is 1.0 and represents no modification.
2300 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
2302 g_object_class_install_property (object_class,
2303 PROP_SATURATION, filter_op->pspec);
2306 * GstVaapiPostproc:brightness:
2308 * The color brightness, expressed as a float value. Range is -1.0
2309 * to 1.0. Default value is 0.0 and represents no modification.
2311 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
2313 g_object_class_install_property (object_class,
2314 PROP_BRIGHTNESS, filter_op->pspec);
2317 * GstVaapiPostproc:contrast:
2319 * The color contrast, expressed as a float value. Range is 0.0 to
2320 * 2.0. Default value is 1.0 and represents no modification.
2322 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
2324 g_object_class_install_property (object_class,
2325 PROP_CONTRAST, filter_op->pspec);
2328 * GstVaapiPostproc:scale-method:
2330 * The scaling method to use, expressed as an enum value. See
2331 * #GstVaapiScaleMethod.
2333 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SCALING);
2335 g_object_class_install_property (object_class,
2336 PROP_SCALE_METHOD, filter_op->pspec);
2339 * GstVaapiPostproc:video-direction:
2341 * The video-direction to use, expressed as an enum value. See
2342 * #GstVideoDirectionMethod.
2344 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_VIDEO_DIRECTION);
2346 g_object_class_install_property (object_class,
2347 PROP_VIDEO_DIRECTION, filter_op->pspec);
2350 * GstVaapiPostproc:skin-tone-enhancement:
2352 * Apply the skin tone enhancement algorithm.
2354 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE);
2356 g_object_class_install_property (object_class,
2357 PROP_SKIN_TONE_ENHANCEMENT, filter_op->pspec);
2359 g_ptr_array_unref (filter_ops);
2363 find_value_ptr (GstVaapiPostproc * postproc, GstVaapiFilterOp op)
2366 case GST_VAAPI_FILTER_OP_HUE:
2367 return &postproc->hue;
2368 case GST_VAAPI_FILTER_OP_SATURATION:
2369 return &postproc->saturation;
2370 case GST_VAAPI_FILTER_OP_BRIGHTNESS:
2371 return &postproc->brightness;
2372 case GST_VAAPI_FILTER_OP_CONTRAST:
2373 return &postproc->contrast;
2380 cb_set_default_value (GstVaapiPostproc * postproc, GPtrArray * filter_ops,
2381 GstVaapiFilterOp op)
2383 GstVaapiFilterOpInfo *filter_op;
2384 GParamSpecFloat *pspec;
2387 filter_op = find_filter_op (filter_ops, op);
2390 var = find_value_ptr (postproc, op);
2393 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2394 *var = pspec->default_value;
2398 gst_vaapipostproc_init (GstVaapiPostproc * postproc)
2400 GPtrArray *filter_ops;
2403 gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (postproc),
2406 g_mutex_init (&postproc->postproc_lock);
2407 postproc->format = DEFAULT_FORMAT;
2408 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
2409 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
2410 postproc->field_duration = GST_CLOCK_TIME_NONE;
2411 postproc->keep_aspect = TRUE;
2412 postproc->get_va_surfaces = TRUE;
2413 postproc->forward_crop = FALSE;
2415 /* AUTO is not valid for tag_video_direction, this is just to
2416 * ensure we setup the method as sink event tag */
2417 postproc->tag_video_direction = GST_VIDEO_ORIENTATION_AUTO;
2419 filter_ops = gst_vaapi_filter_get_operations (NULL);
2421 for (i = GST_VAAPI_FILTER_OP_HUE; i <= GST_VAAPI_FILTER_OP_CONTRAST; i++)
2422 cb_set_default_value (postproc, filter_ops, i);
2423 g_ptr_array_unref (filter_ops);
2426 gst_video_info_init (&postproc->sinkpad_info);
2427 gst_video_info_init (&postproc->srcpad_info);
2428 gst_video_info_init (&postproc->filter_pool_info);
2431 /* ------------------------------------------------------------------------ */
2432 /* --- GstColorBalance interface --- */
2433 /* ------------------------------------------------------------------------ */
2435 #define CB_CHANNEL_FACTOR 1000.0
2439 GstVaapiFilterOp op;
2441 } ColorBalanceChannel;
2443 ColorBalanceChannel cb_channels[] = {
2445 GST_VAAPI_FILTER_OP_HUE, "VA_FILTER_HUE"}, {
2446 GST_VAAPI_FILTER_OP_SATURATION, "VA_FILTER_SATURATION"}, {
2447 GST_VAAPI_FILTER_OP_BRIGHTNESS, "VA_FILTER_BRIGHTNESS"}, {
2448 GST_VAAPI_FILTER_OP_CONTRAST, "VA_FILTER_CONTRAST"},
2452 cb_channels_init (GstVaapiPostproc * postproc)
2454 GPtrArray *filter_ops;
2455 GstVaapiFilterOpInfo *filter_op;
2456 GParamSpecFloat *pspec;
2457 GstColorBalanceChannel *channel;
2460 if (postproc->cb_channels)
2463 g_mutex_lock (&postproc->postproc_lock);
2464 if (!gst_vaapipostproc_ensure_filter (postproc)) {
2465 g_mutex_unlock (&postproc->postproc_lock);
2468 g_mutex_unlock (&postproc->postproc_lock);
2470 filter_ops = postproc->filter_ops ? g_ptr_array_ref (postproc->filter_ops)
2471 : gst_vaapi_filter_get_operations (postproc->filter);
2475 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2476 filter_op = find_filter_op (filter_ops, cb_channels[i].op);
2480 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
2481 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2482 channel->label = g_strdup (cb_channels[i].name);
2483 channel->min_value = pspec->minimum * CB_CHANNEL_FACTOR;
2484 channel->max_value = pspec->maximum * CB_CHANNEL_FACTOR;
2486 postproc->cb_channels = g_list_prepend (postproc->cb_channels, channel);
2489 g_ptr_array_unref (filter_ops);
2492 static const GList *
2493 gst_vaapipostproc_colorbalance_list_channels (GstColorBalance * balance)
2495 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2497 cb_channels_init (postproc);
2498 return postproc->cb_channels;
2502 cb_get_value_ptr (GstVaapiPostproc * postproc,
2503 GstColorBalanceChannel * channel, GstVaapiPostprocFlags * flags)
2508 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
2509 if (g_ascii_strcasecmp (cb_channels[i].name, channel->label) == 0)
2512 if (i >= G_N_ELEMENTS (cb_channels))
2515 ret = find_value_ptr (postproc, cb_channels[i].op);
2517 *flags = 1 << cb_channels[i].op;
2522 gst_vaapipostproc_colorbalance_set_value (GstColorBalance * balance,
2523 GstColorBalanceChannel * channel, gint value)
2525 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2526 GstVaapiPostprocFlags flags;
2527 gfloat new_val, *var;
2529 value = CLAMP (value, channel->min_value, channel->max_value);
2530 new_val = (gfloat) value / CB_CHANNEL_FACTOR;
2532 var = cb_get_value_ptr (postproc, channel, &flags);
2535 g_mutex_lock (&postproc->postproc_lock);
2536 postproc->flags |= flags;
2537 g_mutex_unlock (&postproc->postproc_lock);
2538 gst_color_balance_value_changed (balance, channel, value);
2539 if (check_filter_update (postproc))
2540 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (postproc));
2544 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2548 gst_vaapipostproc_colorbalance_get_value (GstColorBalance * balance,
2549 GstColorBalanceChannel * channel)
2551 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
2555 var = cb_get_value_ptr (postproc, channel, NULL);
2557 new_val = (gint) ((*var) * CB_CHANNEL_FACTOR);
2558 new_val = CLAMP (new_val, channel->min_value, channel->max_value);
2562 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
2566 static GstColorBalanceType
2567 gst_vaapipostproc_colorbalance_get_balance_type (GstColorBalance * balance)
2569 return GST_COLOR_BALANCE_HARDWARE;
2573 gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data)
2575 GstColorBalanceInterface *cbface = iface;
2576 cbface->list_channels = gst_vaapipostproc_colorbalance_list_channels;
2577 cbface->set_value = gst_vaapipostproc_colorbalance_set_value;
2578 cbface->get_value = gst_vaapipostproc_colorbalance_get_value;
2579 cbface->get_balance_type = gst_vaapipostproc_colorbalance_get_balance_type;