2 * gstvaapipostproc.c - VA-API video postprocessing
4 * Copyright (C) 2012-2014 Intel Corporation
5 * Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1
10 * of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301 USA
24 * SECTION:gstvaapipostproc
25 * @short_description: A video postprocessing filter
27 * vaapipostproc consists in various postprocessing algorithms to be
28 * applied to VA surfaces. So far, only basic bob deinterlacing is
32 #include "gstcompat.h"
33 #include <gst/video/video.h>
35 #include "gstvaapipostproc.h"
36 #include "gstvaapipluginutil.h"
37 #include "gstvaapivideobuffer.h"
38 #include "gstvaapivideobufferpool.h"
39 #include "gstvaapivideomemory.h"
41 #define GST_PLUGIN_NAME "vaapipostproc"
42 #define GST_PLUGIN_DESC "A video postprocessing filter"
44 GST_DEBUG_CATEGORY_STATIC (gst_debug_vaapipostproc);
45 #define GST_CAT_DEFAULT gst_debug_vaapipostproc
47 /* Default templates */
49 static const char gst_vaapipostproc_sink_caps_str[] =
50 GST_VAAPI_MAKE_SURFACE_CAPS ", "
51 GST_CAPS_INTERLACED_MODES "; "
52 GST_VAAPI_MAKE_GLTEXUPLOAD_CAPS "; "
53 GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ", "
54 GST_CAPS_INTERLACED_MODES;
58 static const char gst_vaapipostproc_src_caps_str[] =
59 GST_VAAPI_MAKE_SURFACE_CAPS ", "
60 GST_CAPS_INTERLACED_FALSE "; "
61 GST_VAAPI_MAKE_GLTEXUPLOAD_CAPS "; "
62 GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ", "
63 GST_CAPS_INTERLACED_FALSE;
67 static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
68 GST_STATIC_PAD_TEMPLATE ("sink",
71 GST_STATIC_CAPS (gst_vaapipostproc_sink_caps_str));
75 static GstStaticPadTemplate gst_vaapipostproc_src_factory =
76 GST_STATIC_PAD_TEMPLATE ("src",
79 GST_STATIC_CAPS (gst_vaapipostproc_src_caps_str));
82 static void gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data);
84 G_DEFINE_TYPE_WITH_CODE (GstVaapiPostproc, gst_vaapipostproc,
85 GST_TYPE_BASE_TRANSFORM, GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
86 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
87 gst_vaapipostproc_colorbalance_init));
89 static GstVideoFormat native_formats[] =
90 { GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_I420 };
99 PROP_FORCE_ASPECT_RATIO,
100 PROP_DEINTERLACE_MODE,
101 PROP_DEINTERLACE_METHOD,
109 PROP_SKIN_TONE_ENHANCEMENT,
112 #define DEFAULT_FORMAT GST_VIDEO_FORMAT_ENCODED
113 #define DEFAULT_DEINTERLACE_MODE GST_VAAPI_DEINTERLACE_MODE_AUTO
114 #define DEFAULT_DEINTERLACE_METHOD GST_VAAPI_DEINTERLACE_METHOD_BOB
116 #define GST_VAAPI_TYPE_DEINTERLACE_MODE \
117 gst_vaapi_deinterlace_mode_get_type()
120 gst_vaapi_deinterlace_mode_get_type (void)
122 static GType deinterlace_mode_type = 0;
124 static const GEnumValue mode_types[] = {
125 {GST_VAAPI_DEINTERLACE_MODE_AUTO,
126 "Auto detection", "auto"},
127 {GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
128 "Force deinterlacing", "interlaced"},
129 {GST_VAAPI_DEINTERLACE_MODE_DISABLED,
130 "Never deinterlace", "disabled"},
134 if (!deinterlace_mode_type) {
135 deinterlace_mode_type =
136 g_enum_register_static ("GstVaapiDeinterlaceMode", mode_types);
138 return deinterlace_mode_type;
142 ds_reset (GstVaapiDeinterlaceState * ds)
146 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++)
147 gst_buffer_replace (&ds->buffers[i], NULL);
148 ds->buffers_index = 0;
149 ds->num_surfaces = 0;
155 ds_add_buffer (GstVaapiDeinterlaceState * ds, GstBuffer * buf)
157 gst_buffer_replace (&ds->buffers[ds->buffers_index], buf);
158 ds->buffers_index = (ds->buffers_index + 1) % G_N_ELEMENTS (ds->buffers);
161 static inline GstBuffer *
162 ds_get_buffer (GstVaapiDeinterlaceState * ds, guint index)
164 /* Note: the index increases towards older buffers.
165 i.e. buffer at index 0 means the immediately preceding buffer
166 in the history, buffer at index 1 means the one preceding the
167 surface at index 0, etc. */
168 const guint n = ds->buffers_index + G_N_ELEMENTS (ds->buffers) - index - 1;
169 return ds->buffers[n % G_N_ELEMENTS (ds->buffers)];
173 ds_set_surfaces (GstVaapiDeinterlaceState * ds)
175 GstVaapiVideoMeta *meta;
178 ds->num_surfaces = 0;
179 for (i = 0; i < G_N_ELEMENTS (ds->buffers); i++) {
180 GstBuffer *const buf = ds_get_buffer (ds, i);
184 meta = gst_buffer_get_vaapi_video_meta (buf);
185 ds->surfaces[ds->num_surfaces++] = gst_vaapi_video_meta_get_surface (meta);
189 static GstVaapiFilterOpInfo *
190 find_filter_op (GPtrArray * filter_ops, GstVaapiFilterOp op)
195 for (i = 0; i < filter_ops->len; i++) {
196 GstVaapiFilterOpInfo *const filter_op = g_ptr_array_index (filter_ops, i);
197 if (filter_op->op == op)
204 static inline gboolean
205 gst_vaapipostproc_ensure_display (GstVaapiPostproc * postproc)
208 gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (postproc));
212 gst_vaapipostproc_ensure_filter (GstVaapiPostproc * postproc)
214 if (postproc->filter)
217 if (!gst_vaapipostproc_ensure_display (postproc))
220 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
221 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
224 gst_vaapi_filter_new (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
225 if (!postproc->filter)
231 gst_vaapipostproc_ensure_filter_caps (GstVaapiPostproc * postproc)
233 if (!gst_vaapipostproc_ensure_filter (postproc))
236 postproc->filter_ops = gst_vaapi_filter_get_operations (postproc->filter);
237 if (!postproc->filter_ops)
240 postproc->filter_formats = gst_vaapi_filter_get_formats (postproc->filter);
241 if (!postproc->filter_formats)
247 gst_vaapipostproc_create (GstVaapiPostproc * postproc)
249 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
251 if (!gst_vaapipostproc_ensure_display (postproc))
254 postproc->use_vpp = FALSE;
255 postproc->has_vpp = gst_vaapipostproc_ensure_filter (postproc);
260 gst_vaapipostproc_destroy_filter (GstVaapiPostproc * postproc)
262 if (postproc->filter_formats) {
263 g_array_unref (postproc->filter_formats);
264 postproc->filter_formats = NULL;
267 if (postproc->filter_ops) {
268 g_ptr_array_unref (postproc->filter_ops);
269 postproc->filter_ops = NULL;
271 if (postproc->cb_channels) {
272 g_list_free_full (postproc->cb_channels, g_object_unref);
273 postproc->cb_channels = NULL;
275 gst_vaapi_filter_replace (&postproc->filter, NULL);
276 gst_vaapi_video_pool_replace (&postproc->filter_pool, NULL);
280 gst_vaapipostproc_destroy (GstVaapiPostproc * postproc)
282 ds_reset (&postproc->deinterlace_state);
283 gst_vaapipostproc_destroy_filter (postproc);
285 gst_caps_replace (&postproc->allowed_sinkpad_caps, NULL);
286 gst_caps_replace (&postproc->allowed_srcpad_caps, NULL);
287 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
291 gst_vaapipostproc_start (GstBaseTransform * trans)
293 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
295 ds_reset (&postproc->deinterlace_state);
296 if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (postproc)))
298 if (!gst_vaapipostproc_ensure_filter (postproc))
304 gst_vaapipostproc_stop (GstBaseTransform * trans)
306 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
308 ds_reset (&postproc->deinterlace_state);
309 gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (postproc));
311 postproc->field_duration = GST_CLOCK_TIME_NONE;
312 gst_video_info_init (&postproc->sinkpad_info);
313 gst_video_info_init (&postproc->srcpad_info);
314 gst_video_info_init (&postproc->filter_pool_info);
320 should_deinterlace_buffer (GstVaapiPostproc * postproc, GstBuffer * buf)
322 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) ||
323 postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_DISABLED)
326 if (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_INTERLACED)
329 g_assert (postproc->deinterlace_mode == GST_VAAPI_DEINTERLACE_MODE_AUTO);
331 switch (GST_VIDEO_INFO_INTERLACE_MODE (&postproc->sinkpad_info)) {
332 case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
334 case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
336 case GST_VIDEO_INTERLACE_MODE_MIXED:
337 if (GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_INTERLACED))
341 GST_ERROR ("unhandled \"interlace-mode\", disabling deinterlacing");
348 create_output_buffer (GstVaapiPostproc * postproc)
352 GstBufferPool *const pool =
353 GST_VAAPI_PLUGIN_BASE (postproc)->srcpad_buffer_pool;
356 g_return_val_if_fail (pool != NULL, NULL);
358 if (!gst_buffer_pool_set_active (pool, TRUE))
359 goto error_activate_pool;
362 ret = gst_buffer_pool_acquire_buffer (pool, &outbuf, NULL);
363 if (ret != GST_FLOW_OK || !outbuf)
364 goto error_create_buffer;
370 GST_ERROR ("failed to activate output video buffer pool");
375 GST_ERROR ("failed to create output video buffer");
381 append_output_buffer_metadata (GstVaapiPostproc * postproc, GstBuffer * outbuf,
382 GstBuffer * inbuf, guint flags)
384 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
385 GstVaapiSurfaceProxy *proxy;
387 gst_buffer_copy_into (outbuf, inbuf, flags | GST_BUFFER_COPY_FLAGS, 0, -1);
389 /* GstVideoCropMeta */
390 if (!postproc->use_vpp) {
391 GstVideoCropMeta *const crop_meta = gst_buffer_get_video_crop_meta (inbuf);
393 GstVideoCropMeta *const out_crop_meta =
394 gst_buffer_add_video_crop_meta (outbuf);
396 *out_crop_meta = *crop_meta;
400 /* GstVaapiVideoMeta */
401 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
402 g_return_val_if_fail (inbuf_meta != NULL, FALSE);
403 proxy = gst_vaapi_video_meta_get_surface_proxy (inbuf_meta);
405 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
406 g_return_val_if_fail (outbuf_meta != NULL, FALSE);
407 proxy = gst_vaapi_surface_proxy_copy (proxy);
411 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
412 gst_vaapi_surface_proxy_unref (proxy);
417 deint_method_is_advanced (GstVaapiDeinterlaceMethod deint_method)
419 gboolean is_advanced;
421 switch (deint_method) {
422 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE:
423 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
433 static GstVaapiDeinterlaceMethod
434 get_next_deint_method (GstVaapiDeinterlaceMethod deint_method)
436 switch (deint_method) {
437 case GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED:
438 deint_method = GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE;
441 /* Default to basic "bob" for all others */
442 deint_method = GST_VAAPI_DEINTERLACE_METHOD_BOB;
449 set_best_deint_method (GstVaapiPostproc * postproc, guint flags,
450 GstVaapiDeinterlaceMethod * deint_method_ptr)
452 GstVaapiDeinterlaceMethod deint_method = postproc->deinterlace_method;
456 success = gst_vaapi_filter_set_deinterlacing (postproc->filter,
457 deint_method, flags);
458 if (success || deint_method == GST_VAAPI_DEINTERLACE_METHOD_BOB)
460 deint_method = get_next_deint_method (deint_method);
462 *deint_method_ptr = deint_method;
467 gst_vaapipostproc_process_vpp (GstBaseTransform * trans, GstBuffer * inbuf,
470 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
471 GstVaapiDeinterlaceState *const ds = &postproc->deinterlace_state;
472 GstVaapiVideoMeta *inbuf_meta, *outbuf_meta;
473 GstVaapiSurface *inbuf_surface, *outbuf_surface;
474 GstVaapiSurfaceProxy *proxy;
475 GstVaapiFilterStatus status;
476 GstClockTime timestamp;
479 GstVaapiDeinterlaceMethod deint_method;
480 guint flags, deint_flags;
481 gboolean tff, deint, deint_refs, deint_changed;
482 GstVaapiRectangle *crop_rect = NULL;
483 GstVaapiRectangle tmp_rect;
485 /* Validate filters */
486 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_FORMAT) &&
487 !gst_vaapi_filter_set_format (postproc->filter, postproc->format))
488 return GST_FLOW_NOT_SUPPORTED;
490 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_DENOISE) &&
491 !gst_vaapi_filter_set_denoising_level (postproc->filter,
492 postproc->denoise_level))
493 return GST_FLOW_NOT_SUPPORTED;
495 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SHARPEN) &&
496 !gst_vaapi_filter_set_sharpening_level (postproc->filter,
497 postproc->sharpen_level))
498 return GST_FLOW_NOT_SUPPORTED;
500 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_HUE) &&
501 !gst_vaapi_filter_set_hue (postproc->filter, postproc->hue))
502 return GST_FLOW_NOT_SUPPORTED;
504 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SATURATION) &&
505 !gst_vaapi_filter_set_saturation (postproc->filter, postproc->saturation))
506 return GST_FLOW_NOT_SUPPORTED;
508 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS) &&
509 !gst_vaapi_filter_set_brightness (postproc->filter, postproc->brightness))
510 return GST_FLOW_NOT_SUPPORTED;
512 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_CONTRAST) &&
513 !gst_vaapi_filter_set_contrast (postproc->filter, postproc->contrast))
514 return GST_FLOW_NOT_SUPPORTED;
516 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SCALE) &&
517 !gst_vaapi_filter_set_scaling (postproc->filter, postproc->scale_method))
518 return GST_FLOW_NOT_SUPPORTED;
520 if ((postproc->flags & GST_VAAPI_POSTPROC_FLAG_SKINTONE) &&
521 !gst_vaapi_filter_set_skintone (postproc->filter,
522 postproc->skintone_enhance))
523 return GST_FLOW_NOT_SUPPORTED;
525 inbuf_meta = gst_buffer_get_vaapi_video_meta (inbuf);
527 goto error_invalid_buffer;
528 inbuf_surface = gst_vaapi_video_meta_get_surface (inbuf_meta);
530 GstVideoCropMeta *const crop_meta = gst_buffer_get_video_crop_meta (inbuf);
532 crop_rect = &tmp_rect;
533 crop_rect->x = crop_meta->x;
534 crop_rect->y = crop_meta->y;
535 crop_rect->width = crop_meta->width;
536 crop_rect->height = crop_meta->height;
539 crop_rect = (GstVaapiRectangle *)
540 gst_vaapi_video_meta_get_render_rect (inbuf_meta);
542 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
543 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
544 deint = should_deinterlace_buffer (postproc, inbuf);
546 /* Drop references if deinterlacing conditions changed */
547 deint_changed = deint != ds->deint;
548 if (deint_changed || (ds->num_surfaces > 0 && tff != ds->tff))
551 deint_method = postproc->deinterlace_method;
552 deint_refs = deint_method_is_advanced (deint_method);
553 if (deint_refs && 0) {
554 GstBuffer *const prev_buf = ds_get_buffer (ds, 0);
555 GstClockTime prev_pts, pts = GST_BUFFER_TIMESTAMP (inbuf);
556 /* Reset deinterlacing state when there is a discontinuity */
557 if (prev_buf && (prev_pts = GST_BUFFER_TIMESTAMP (prev_buf)) != pts) {
558 const GstClockTimeDiff pts_diff = GST_CLOCK_DIFF (prev_pts, pts);
559 if (pts_diff < 0 || (postproc->field_duration > 0 &&
560 pts_diff >= postproc->field_duration * 3 - 1))
568 flags = gst_vaapi_video_meta_get_render_flags (inbuf_meta) &
569 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
572 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
573 fieldbuf = create_output_buffer (postproc);
575 goto error_create_buffer;
577 outbuf_meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
579 goto error_create_meta;
582 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
583 (postproc->filter_pool));
585 goto error_create_proxy;
586 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
587 gst_vaapi_surface_proxy_unref (proxy);
590 deint_flags = (tff ? GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD : 0);
592 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
593 if (!set_best_deint_method (postproc, deint_flags, &deint_method))
594 goto error_op_deinterlace;
596 if (deint_method != postproc->deinterlace_method) {
597 GST_DEBUG ("unsupported deinterlace-method %u. Using %u instead",
598 postproc->deinterlace_method, deint_method);
599 postproc->deinterlace_method = deint_method;
600 deint_refs = deint_method_is_advanced (deint_method);
604 ds_set_surfaces (ds);
605 if (!gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
606 ds->surfaces, ds->num_surfaces, NULL, 0))
607 goto error_op_deinterlace;
609 } else if (deint_changed) {
610 // Reset internal filter to non-deinterlacing mode
611 deint_method = GST_VAAPI_DEINTERLACE_METHOD_NONE;
612 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
614 goto error_op_deinterlace;
617 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
618 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
619 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
620 outbuf_surface, flags);
621 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
622 goto error_process_vpp;
624 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
625 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
626 ret = gst_pad_push (trans->srcpad, fieldbuf);
627 if (ret != GST_FLOW_OK)
628 goto error_push_buffer;
633 outbuf_meta = gst_buffer_get_vaapi_video_meta (outbuf);
635 goto error_create_meta;
638 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
639 (postproc->filter_pool));
641 goto error_create_proxy;
642 gst_vaapi_video_meta_set_surface_proxy (outbuf_meta, proxy);
643 gst_vaapi_surface_proxy_unref (proxy);
646 deint_flags = (tff ? 0 : GST_VAAPI_DEINTERLACE_FLAG_TOPFIELD);
648 deint_flags |= GST_VAAPI_DEINTERLACE_FLAG_TFF;
649 if (!gst_vaapi_filter_set_deinterlacing (postproc->filter,
650 deint_method, deint_flags))
651 goto error_op_deinterlace;
654 && !gst_vaapi_filter_set_deinterlacing_references (postproc->filter,
655 ds->surfaces, ds->num_surfaces, NULL, 0))
656 goto error_op_deinterlace;
657 } else if (deint_changed
658 && !gst_vaapi_filter_set_deinterlacing (postproc->filter, deint_method,
660 goto error_op_deinterlace;
662 outbuf_surface = gst_vaapi_video_meta_get_surface (outbuf_meta);
663 gst_vaapi_filter_set_cropping_rectangle (postproc->filter, crop_rect);
664 status = gst_vaapi_filter_process (postproc->filter, inbuf_surface,
665 outbuf_surface, flags);
666 if (status != GST_VAAPI_FILTER_STATUS_SUCCESS)
667 goto error_process_vpp;
669 if (!(postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE))
670 gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
672 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
673 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
676 if (deint && deint_refs)
677 ds_add_buffer (ds, inbuf);
678 postproc->use_vpp = TRUE;
682 error_invalid_buffer:
684 GST_ERROR ("failed to validate source buffer");
685 return GST_FLOW_ERROR;
689 GST_ERROR ("failed to create output buffer");
690 return GST_FLOW_ERROR;
694 GST_ERROR ("failed to create new output buffer meta");
695 gst_buffer_replace (&fieldbuf, NULL);
696 return GST_FLOW_ERROR;
700 GST_ERROR ("failed to create surface proxy from pool");
701 gst_buffer_replace (&fieldbuf, NULL);
702 return GST_FLOW_ERROR;
704 error_op_deinterlace:
706 GST_ERROR ("failed to apply deinterlacing filter");
707 gst_buffer_replace (&fieldbuf, NULL);
708 return GST_FLOW_NOT_SUPPORTED;
712 GST_ERROR ("failed to apply VPP filters (error %d)", status);
713 gst_buffer_replace (&fieldbuf, NULL);
714 return GST_FLOW_ERROR;
718 if (ret != GST_FLOW_FLUSHING)
719 GST_ERROR ("failed to push output buffer to video sink");
720 return GST_FLOW_ERROR;
725 gst_vaapipostproc_process (GstBaseTransform * trans, GstBuffer * inbuf,
728 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
729 GstVaapiVideoMeta *meta;
730 GstClockTime timestamp;
733 guint fieldbuf_flags, outbuf_flags, flags;
736 meta = gst_buffer_get_vaapi_video_meta (inbuf);
738 goto error_invalid_buffer;
740 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
741 tff = GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF);
742 deint = should_deinterlace_buffer (postproc, inbuf);
744 flags = gst_vaapi_video_meta_get_render_flags (meta) &
745 ~GST_VAAPI_PICTURE_STRUCTURE_MASK;
748 fieldbuf = create_output_buffer (postproc);
750 goto error_create_buffer;
751 append_output_buffer_metadata (postproc, fieldbuf, inbuf, 0);
753 meta = gst_buffer_get_vaapi_video_meta (fieldbuf);
754 fieldbuf_flags = flags;
755 fieldbuf_flags |= deint ? (tff ?
756 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
757 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD) :
758 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
759 gst_vaapi_video_meta_set_render_flags (meta, fieldbuf_flags);
761 GST_BUFFER_TIMESTAMP (fieldbuf) = timestamp;
762 GST_BUFFER_DURATION (fieldbuf) = postproc->field_duration;
763 ret = gst_pad_push (trans->srcpad, fieldbuf);
764 if (ret != GST_FLOW_OK)
765 goto error_push_buffer;
768 append_output_buffer_metadata (postproc, outbuf, inbuf, 0);
770 meta = gst_buffer_get_vaapi_video_meta (outbuf);
771 outbuf_flags = flags;
772 outbuf_flags |= deint ? (tff ?
773 GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
774 GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD) :
775 GST_VAAPI_PICTURE_STRUCTURE_FRAME;
776 gst_vaapi_video_meta_set_render_flags (meta, outbuf_flags);
778 GST_BUFFER_TIMESTAMP (outbuf) = timestamp + postproc->field_duration;
779 GST_BUFFER_DURATION (outbuf) = postproc->field_duration;
783 error_invalid_buffer:
785 GST_ERROR ("failed to validate source buffer");
786 return GST_FLOW_ERROR;
790 GST_ERROR ("failed to create output buffer");
795 if (ret != GST_FLOW_FLUSHING)
796 GST_ERROR ("failed to push output buffer to video sink");
802 gst_vaapipostproc_passthrough (GstBaseTransform * trans, GstBuffer * inbuf,
805 GstVaapiVideoMeta *meta;
807 /* No video processing needed, simply copy buffer metadata */
808 meta = gst_buffer_get_vaapi_video_meta (inbuf);
810 goto error_invalid_buffer;
812 append_output_buffer_metadata (GST_VAAPIPOSTPROC (trans), outbuf, inbuf,
813 GST_BUFFER_COPY_TIMESTAMPS);
817 error_invalid_buffer:
819 GST_ERROR ("failed to validate source buffer");
820 return GST_FLOW_ERROR;
825 is_deinterlace_enabled (GstVaapiPostproc * postproc, GstVideoInfo * vip)
827 gboolean deinterlace;
829 switch (postproc->deinterlace_mode) {
830 case GST_VAAPI_DEINTERLACE_MODE_AUTO:
831 deinterlace = GST_VIDEO_INFO_IS_INTERLACED (vip);
833 case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
844 video_info_changed (GstVideoInfo * old_vip, GstVideoInfo * new_vip)
846 if (GST_VIDEO_INFO_FORMAT (old_vip) != GST_VIDEO_INFO_FORMAT (new_vip))
848 if (GST_VIDEO_INFO_INTERLACE_MODE (old_vip) !=
849 GST_VIDEO_INFO_INTERLACE_MODE (new_vip))
851 if (GST_VIDEO_INFO_WIDTH (old_vip) != GST_VIDEO_INFO_WIDTH (new_vip))
853 if (GST_VIDEO_INFO_HEIGHT (old_vip) != GST_VIDEO_INFO_HEIGHT (new_vip))
859 gst_vaapipostproc_update_sink_caps (GstVaapiPostproc * postproc, GstCaps * caps,
860 gboolean * caps_changed_ptr)
863 gboolean deinterlace;
865 GST_INFO_OBJECT (postproc, "new sink caps = %" GST_PTR_FORMAT, caps);
867 if (!gst_video_info_from_caps (&vi, caps))
870 if (video_info_changed (&vi, &postproc->sinkpad_info))
871 postproc->sinkpad_info = vi, *caps_changed_ptr = TRUE;
873 deinterlace = is_deinterlace_enabled (postproc, &vi);
875 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DEINTERLACE;
876 postproc->field_duration = GST_VIDEO_INFO_FPS_N (&vi) > 0 ?
877 gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&vi),
878 (1 + deinterlace) * GST_VIDEO_INFO_FPS_N (&vi)) : 0;
880 postproc->get_va_surfaces = gst_caps_has_vaapi_surface (caps);
885 gst_vaapipostproc_update_src_caps (GstVaapiPostproc * postproc, GstCaps * caps,
886 gboolean * caps_changed_ptr)
890 GST_INFO_OBJECT (postproc, "new src caps = %" GST_PTR_FORMAT, caps);
892 if (!gst_video_info_from_caps (&vi, caps))
895 if (video_info_changed (&vi, &postproc->srcpad_info))
896 postproc->srcpad_info = vi, *caps_changed_ptr = TRUE;
898 if (postproc->format != GST_VIDEO_INFO_FORMAT (&postproc->sinkpad_info) &&
899 postproc->format != DEFAULT_FORMAT)
900 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_FORMAT;
902 if ((postproc->width || postproc->height) &&
903 postproc->width != GST_VIDEO_INFO_WIDTH (&postproc->sinkpad_info) &&
904 postproc->height != GST_VIDEO_INFO_HEIGHT (&postproc->sinkpad_info))
905 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SIZE;
910 ensure_allowed_sinkpad_caps (GstVaapiPostproc * postproc)
912 GstCaps *out_caps, *raw_caps;
914 if (postproc->allowed_sinkpad_caps)
918 out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS ", "
919 GST_CAPS_INTERLACED_MODES);
921 GST_ERROR_OBJECT (postproc, "failed to create VA sink caps");
925 /* Append raw video caps */
926 if (gst_vaapipostproc_ensure_display (postproc)) {
928 gst_vaapi_plugin_base_get_allowed_raw_caps (GST_VAAPI_PLUGIN_BASE
931 out_caps = gst_caps_make_writable (out_caps);
932 gst_caps_append (out_caps, gst_caps_copy (raw_caps));
934 GST_WARNING_OBJECT (postproc, "failed to create YUV sink caps");
937 postproc->allowed_sinkpad_caps = out_caps;
939 /* XXX: append VA/VPP filters */
943 /* Fixup output caps so that to reflect the supported set of pixel formats */
945 expand_allowed_srcpad_caps (GstVaapiPostproc * postproc, GstCaps * caps)
947 GValue value = G_VALUE_INIT, v_format = G_VALUE_INIT;
948 guint i, num_structures;
950 if (postproc->filter == NULL)
952 if (!gst_vaapipostproc_ensure_filter_caps (postproc))
955 /* Reset "format" field for each structure */
956 if (!gst_vaapi_value_set_format_list (&value, postproc->filter_formats))
958 if (gst_vaapi_value_set_format (&v_format, GST_VIDEO_FORMAT_ENCODED)) {
959 gst_value_list_prepend_value (&value, &v_format);
960 g_value_unset (&v_format);
963 num_structures = gst_caps_get_size (caps);
964 for (i = 0; i < num_structures; i++) {
965 GstCapsFeatures *const features = gst_caps_get_features (caps, i);
966 if (gst_caps_features_contains (features,
967 GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META))
970 GstStructure *const structure = gst_caps_get_structure (caps, i);
973 gst_structure_set_value (structure, "format", &value);
975 g_value_unset (&value);
982 ensure_allowed_srcpad_caps (GstVaapiPostproc * postproc)
986 if (postproc->allowed_srcpad_caps)
989 /* Create initial caps from pad template */
990 out_caps = gst_caps_from_string (gst_vaapipostproc_src_caps_str);
992 GST_ERROR ("failed to create VA src caps");
996 postproc->allowed_srcpad_caps =
997 expand_allowed_srcpad_caps (postproc, out_caps);
998 return postproc->allowed_srcpad_caps != NULL;
1002 find_best_size (GstVaapiPostproc * postproc, GstVideoInfo * vip,
1003 guint * width_ptr, guint * height_ptr)
1005 guint width, height;
1007 width = GST_VIDEO_INFO_WIDTH (vip);
1008 height = GST_VIDEO_INFO_HEIGHT (vip);
1009 if (postproc->width && postproc->height) {
1010 width = postproc->width;
1011 height = postproc->height;
1012 } else if (postproc->keep_aspect) {
1013 const gdouble ratio = (gdouble) width / height;
1014 if (postproc->width) {
1015 width = postproc->width;
1016 height = postproc->width / ratio;
1017 } else if (postproc->height) {
1018 height = postproc->height;
1019 width = postproc->height * ratio;
1021 } else if (postproc->width)
1022 width = postproc->width;
1023 else if (postproc->height)
1024 height = postproc->height;
1027 *height_ptr = height;
1031 gst_vaapipostproc_transform_caps_impl (GstBaseTransform * trans,
1032 GstPadDirection direction, GstCaps * caps)
1034 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1036 GstVideoFormat out_format;
1038 GstVaapiCapsFeature feature;
1039 const gchar *feature_str;
1040 guint width, height;
1042 /* Generate the sink pad caps, that could be fixated afterwards */
1043 if (direction == GST_PAD_SRC) {
1044 if (!ensure_allowed_sinkpad_caps (postproc))
1046 return gst_caps_ref (postproc->allowed_sinkpad_caps);
1049 /* Generate complete set of src pad caps if non-fixated sink pad
1050 caps are provided */
1051 if (!gst_caps_is_fixed (caps)) {
1052 if (!ensure_allowed_srcpad_caps (postproc))
1054 return gst_caps_ref (postproc->allowed_srcpad_caps);
1057 /* Generate the expected src pad caps, from the current fixated
1059 if (!gst_video_info_from_caps (&vi, caps))
1062 // Set double framerate in interlaced mode
1063 if (is_deinterlace_enabled (postproc, &vi)) {
1064 gint fps_n = GST_VIDEO_INFO_FPS_N (&vi);
1065 gint fps_d = GST_VIDEO_INFO_FPS_D (&vi);
1066 if (!gst_util_fraction_multiply (fps_n, fps_d, 2, 1, &fps_n, &fps_d))
1068 GST_VIDEO_INFO_FPS_N (&vi) = fps_n;
1069 GST_VIDEO_INFO_FPS_D (&vi) = fps_d;
1071 // Signal the other pad that we only generate progressive frames
1072 GST_VIDEO_INFO_INTERLACE_MODE (&vi) = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
1074 // Update size from user-specified parameters
1075 find_best_size (postproc, &vi, &width, &height);
1077 // Update format from user-specified parameters
1078 /* XXX: this is a workaround until auto-plugging is fixed when
1079 * format=ENCODED + memory:VASurface caps feature are provided.
1080 * use the downstream negotiated video format as the output format
1081 * if the user didn't explicitly ask for colorspace conversion.
1082 * Use a filter caps which contain all raw video formats, (excluding
1083 * GST_VIDEO_FORMAT_ENCODED) */
1084 if (postproc->format != DEFAULT_FORMAT)
1085 out_format = postproc->format;
1088 GstVideoInfo peer_vi;
1090 gst_pad_peer_query_caps (GST_BASE_TRANSFORM_SRC_PAD (trans),
1091 postproc->allowed_srcpad_caps);
1092 if (gst_caps_is_empty (peer_caps))
1094 if (!gst_caps_is_fixed (peer_caps))
1095 peer_caps = gst_caps_fixate (peer_caps);
1096 gst_video_info_from_caps (&peer_vi, peer_caps);
1097 out_format = GST_VIDEO_INFO_FORMAT (&peer_vi);
1099 gst_caps_unref (peer_caps);
1103 gst_vaapi_find_preferred_caps_feature (GST_BASE_TRANSFORM_SRC_PAD (trans),
1104 out_format, &out_format);
1105 gst_video_info_change_format (&vi, out_format, width, height);
1107 out_caps = gst_video_info_to_caps (&vi);
1112 feature_str = gst_vaapi_caps_feature_to_string (feature);
1114 gst_caps_set_features (out_caps, 0,
1115 gst_caps_features_new (feature_str, NULL));
1118 /* we don't need to do format conversion if GL_TEXTURE_UPLOAD_META
1120 if (feature != GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META &&
1121 postproc->format != out_format) {
1122 postproc->format = out_format;
1128 gst_vaapipostproc_transform_caps (GstBaseTransform * trans,
1129 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1133 caps = gst_vaapipostproc_transform_caps_impl (trans, direction, caps);
1134 if (caps && filter) {
1135 out_caps = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1136 gst_caps_unref (caps);
1143 gst_vaapipostproc_transform_size (GstBaseTransform * trans,
1144 GstPadDirection direction, GstCaps * caps, gsize size,
1145 GstCaps * othercaps, gsize * othersize)
1147 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1149 if (direction == GST_PAD_SINK || postproc->get_va_surfaces)
1156 static GstFlowReturn
1157 gst_vaapipostproc_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1160 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1165 gst_vaapi_plugin_base_get_input_buffer (GST_VAAPI_PLUGIN_BASE (postproc),
1167 if (ret != GST_FLOW_OK)
1168 return GST_FLOW_ERROR;
1170 ret = GST_FLOW_NOT_SUPPORTED;
1171 if (postproc->flags) {
1172 /* Use VA/VPP extensions to process this frame */
1173 if (postproc->has_vpp &&
1174 (postproc->flags != GST_VAAPI_POSTPROC_FLAG_DEINTERLACE ||
1175 deint_method_is_advanced (postproc->deinterlace_method))) {
1176 ret = gst_vaapipostproc_process_vpp (trans, buf, outbuf);
1177 if (ret != GST_FLOW_NOT_SUPPORTED)
1179 GST_WARNING ("unsupported VPP filters. Disabling");
1182 /* Only append picture structure meta data (top/bottom field) */
1183 if (postproc->flags & GST_VAAPI_POSTPROC_FLAG_DEINTERLACE) {
1184 ret = gst_vaapipostproc_process (trans, buf, outbuf);
1185 if (ret != GST_FLOW_NOT_SUPPORTED)
1190 /* Fallback: passthrough to the downstream element as is */
1191 ret = gst_vaapipostproc_passthrough (trans, buf, outbuf);
1194 gst_buffer_unref (buf);
1198 static GstFlowReturn
1199 gst_vaapipostproc_prepare_output_buffer (GstBaseTransform * trans,
1200 GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
1202 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1204 *outbuf_ptr = create_output_buffer (postproc);
1205 return *outbuf_ptr ? GST_FLOW_OK : GST_FLOW_ERROR;
1209 ensure_srcpad_buffer_pool (GstVaapiPostproc * postproc, GstCaps * caps)
1212 GstVaapiVideoPool *pool;
1214 gst_video_info_init (&vi);
1215 gst_video_info_from_caps (&vi, caps);
1216 gst_video_info_change_format (&vi, postproc->format,
1217 GST_VIDEO_INFO_WIDTH (&vi), GST_VIDEO_INFO_HEIGHT (&vi));
1219 if (postproc->filter_pool
1220 && !video_info_changed (&vi, &postproc->filter_pool_info))
1222 postproc->filter_pool_info = vi;
1225 gst_vaapi_surface_pool_new_full (GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc),
1226 &postproc->filter_pool_info, 0);
1230 gst_vaapi_video_pool_replace (&postproc->filter_pool, pool);
1231 gst_vaapi_video_pool_unref (pool);
1236 is_native_video_format (GstVideoFormat format)
1239 for (i = 0; i < G_N_ELEMENTS (native_formats); i++)
1240 if (native_formats[i] == format)
1246 gst_vaapipostproc_set_caps (GstBaseTransform * trans, GstCaps * caps,
1249 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1250 gboolean caps_changed = FALSE;
1253 if (!gst_vaapipostproc_update_sink_caps (postproc, caps, &caps_changed))
1255 /* HACK: This is a workaround to deal with the va-intel-driver for non-native
1256 * formats while doing advanced deinterlacing. The format of reference surfaces must
1257 * be same as the format used by the driver internally for motion adaptive
1258 * deinterlacing and motion compensated deinterlacing */
1259 gst_video_info_from_caps (&vinfo, caps);
1260 if (deint_method_is_advanced (postproc->deinterlace_method)
1261 && !is_native_video_format (GST_VIDEO_INFO_FORMAT (&vinfo))) {
1262 GST_WARNING_OBJECT (postproc,
1263 "Advanced deinterlacing requires the native video formats used by the driver internally");
1266 if (!gst_vaapipostproc_update_src_caps (postproc, out_caps, &caps_changed))
1270 gst_vaapipostproc_destroy (postproc);
1271 if (!gst_vaapipostproc_create (postproc))
1273 if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (trans),
1278 if (!ensure_srcpad_buffer_pool (postproc, out_caps))
1284 gst_vaapipostproc_query (GstBaseTransform * trans, GstPadDirection direction,
1287 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1289 if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
1290 if (gst_vaapi_handle_context_query (query,
1291 GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc))) {
1292 GST_DEBUG_OBJECT (postproc, "sharing display %p",
1293 GST_VAAPI_PLUGIN_BASE_DISPLAY (postproc));
1299 GST_BASE_TRANSFORM_CLASS (gst_vaapipostproc_parent_class)->query (trans,
1304 gst_vaapipostproc_propose_allocation (GstBaseTransform * trans,
1305 GstQuery * decide_query, GstQuery * query)
1307 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (trans);
1308 GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (trans);
1310 /* Let vaapidecode allocate the video buffers */
1311 if (postproc->get_va_surfaces)
1313 if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
1319 gst_vaapipostproc_decide_allocation (GstBaseTransform * trans, GstQuery * query)
1321 return gst_vaapi_plugin_base_decide_allocation (GST_VAAPI_PLUGIN_BASE (trans),
1326 gst_vaapipostproc_finalize (GObject * object)
1328 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1330 gst_vaapipostproc_destroy (postproc);
1332 gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (postproc));
1333 G_OBJECT_CLASS (gst_vaapipostproc_parent_class)->finalize (object);
1337 gst_vaapipostproc_set_property (GObject * object,
1338 guint prop_id, const GValue * value, GParamSpec * pspec)
1340 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1344 postproc->format = g_value_get_enum (value);
1347 postproc->width = g_value_get_uint (value);
1350 postproc->height = g_value_get_uint (value);
1352 case PROP_FORCE_ASPECT_RATIO:
1353 postproc->keep_aspect = g_value_get_boolean (value);
1355 case PROP_DEINTERLACE_MODE:
1356 postproc->deinterlace_mode = g_value_get_enum (value);
1358 case PROP_DEINTERLACE_METHOD:
1359 postproc->deinterlace_method = g_value_get_enum (value);
1362 postproc->denoise_level = g_value_get_float (value);
1363 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_DENOISE;
1366 postproc->sharpen_level = g_value_get_float (value);
1367 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SHARPEN;
1370 postproc->hue = g_value_get_float (value);
1371 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_HUE;
1373 case PROP_SATURATION:
1374 postproc->saturation = g_value_get_float (value);
1375 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SATURATION;
1377 case PROP_BRIGHTNESS:
1378 postproc->brightness = g_value_get_float (value);
1379 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_BRIGHTNESS;
1382 postproc->contrast = g_value_get_float (value);
1383 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_CONTRAST;
1385 case PROP_SCALE_METHOD:
1386 postproc->scale_method = g_value_get_enum (value);
1387 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SCALE;
1389 case PROP_SKIN_TONE_ENHANCEMENT:
1390 postproc->skintone_enhance = g_value_get_boolean (value);
1391 postproc->flags |= GST_VAAPI_POSTPROC_FLAG_SKINTONE;
1394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1400 gst_vaapipostproc_get_property (GObject * object,
1401 guint prop_id, GValue * value, GParamSpec * pspec)
1403 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (object);
1407 g_value_set_enum (value, postproc->format);
1410 g_value_set_uint (value, postproc->width);
1413 g_value_set_uint (value, postproc->height);
1415 case PROP_FORCE_ASPECT_RATIO:
1416 g_value_set_boolean (value, postproc->keep_aspect);
1418 case PROP_DEINTERLACE_MODE:
1419 g_value_set_enum (value, postproc->deinterlace_mode);
1421 case PROP_DEINTERLACE_METHOD:
1422 g_value_set_enum (value, postproc->deinterlace_method);
1425 g_value_set_float (value, postproc->denoise_level);
1428 g_value_set_float (value, postproc->sharpen_level);
1431 g_value_set_float (value, postproc->hue);
1433 case PROP_SATURATION:
1434 g_value_set_float (value, postproc->saturation);
1436 case PROP_BRIGHTNESS:
1437 g_value_set_float (value, postproc->brightness);
1440 g_value_set_float (value, postproc->contrast);
1442 case PROP_SCALE_METHOD:
1443 g_value_set_enum (value, postproc->scale_method);
1445 case PROP_SKIN_TONE_ENHANCEMENT:
1446 g_value_set_boolean (value, postproc->skintone_enhance);
1449 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1455 gst_vaapipostproc_class_init (GstVaapiPostprocClass * klass)
1457 GObjectClass *const object_class = G_OBJECT_CLASS (klass);
1458 GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
1459 GstBaseTransformClass *const trans_class = GST_BASE_TRANSFORM_CLASS (klass);
1460 GstPadTemplate *pad_template;
1461 GPtrArray *filter_ops;
1462 GstVaapiFilterOpInfo *filter_op;
1464 GST_DEBUG_CATEGORY_INIT (gst_debug_vaapipostproc,
1465 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1467 gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
1469 object_class->finalize = gst_vaapipostproc_finalize;
1470 object_class->set_property = gst_vaapipostproc_set_property;
1471 object_class->get_property = gst_vaapipostproc_get_property;
1472 trans_class->start = gst_vaapipostproc_start;
1473 trans_class->stop = gst_vaapipostproc_stop;
1474 trans_class->transform_caps = gst_vaapipostproc_transform_caps;
1475 trans_class->transform_size = gst_vaapipostproc_transform_size;
1476 trans_class->transform = gst_vaapipostproc_transform;
1477 trans_class->set_caps = gst_vaapipostproc_set_caps;
1478 trans_class->query = gst_vaapipostproc_query;
1479 trans_class->propose_allocation = gst_vaapipostproc_propose_allocation;
1480 trans_class->decide_allocation = gst_vaapipostproc_decide_allocation;
1482 trans_class->prepare_output_buffer = gst_vaapipostproc_prepare_output_buffer;
1484 gst_element_class_set_static_metadata (element_class,
1485 "VA-API video postprocessing",
1486 "Filter/Converter/Video;Filter/Converter/Video/Scaler;"
1487 "Filter/Effect/Video;Filter/Effect/Video/Deinterlace",
1488 GST_PLUGIN_DESC, "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1491 pad_template = gst_static_pad_template_get (&gst_vaapipostproc_sink_factory);
1492 gst_element_class_add_pad_template (element_class, pad_template);
1495 pad_template = gst_static_pad_template_get (&gst_vaapipostproc_src_factory);
1496 gst_element_class_add_pad_template (element_class, pad_template);
1499 * GstVaapiPostproc:deinterlace-mode:
1501 * This selects whether the deinterlacing should always be applied
1502 * or if they should only be applied on content that has the
1503 * "interlaced" flag on the caps.
1505 g_object_class_install_property
1507 PROP_DEINTERLACE_MODE,
1508 g_param_spec_enum ("deinterlace-mode",
1510 "Deinterlace mode to use",
1511 GST_VAAPI_TYPE_DEINTERLACE_MODE,
1512 DEFAULT_DEINTERLACE_MODE,
1513 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1516 * GstVaapiPostproc:deinterlace-method:
1518 * This selects the deinterlacing method to apply.
1520 g_object_class_install_property
1522 PROP_DEINTERLACE_METHOD,
1523 g_param_spec_enum ("deinterlace-method",
1524 "Deinterlace method",
1525 "Deinterlace method to use",
1526 GST_VAAPI_TYPE_DEINTERLACE_METHOD,
1527 DEFAULT_DEINTERLACE_METHOD,
1528 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1530 filter_ops = gst_vaapi_filter_get_operations (NULL);
1535 * GstVaapiPostproc:format:
1537 * The forced output pixel format, expressed as a #GstVideoFormat.
1539 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_FORMAT);
1541 g_object_class_install_property (object_class,
1542 PROP_FORMAT, filter_op->pspec);
1545 * GstVaapiPostproc:width:
1547 * The forced output width in pixels. If set to zero, the width is
1548 * calculated from the height if aspect ration is preserved, or
1549 * inherited from the sink caps width
1551 g_object_class_install_property
1554 g_param_spec_uint ("width",
1556 "Forced output width",
1557 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1560 * GstVaapiPostproc:height:
1562 * The forced output height in pixels. If set to zero, the height is
1563 * calculated from the width if aspect ration is preserved, or
1564 * inherited from the sink caps height
1566 g_object_class_install_property
1569 g_param_spec_uint ("height",
1571 "Forced output height",
1572 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1575 * GstVaapiPostproc:force-aspect-ratio:
1577 * When enabled, scaling respects video aspect ratio; when disabled,
1578 * the video is distorted to fit the width and height properties.
1580 g_object_class_install_property
1582 PROP_FORCE_ASPECT_RATIO,
1583 g_param_spec_boolean ("force-aspect-ratio",
1584 "Force aspect ratio",
1585 "When enabled, scaling will respect original aspect ratio",
1586 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1589 * GstVaapiPostproc:denoise:
1591 * The level of noise reduction to apply.
1593 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_DENOISE);
1595 g_object_class_install_property (object_class,
1596 PROP_DENOISE, filter_op->pspec);
1599 * GstVaapiPostproc:sharpen:
1601 * The level of sharpening to apply for positive values, or the
1602 * level of blurring for negative values.
1604 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SHARPEN);
1606 g_object_class_install_property (object_class,
1607 PROP_SHARPEN, filter_op->pspec);
1610 * GstVaapiPostproc:hue:
1612 * The color hue, expressed as a float value. Range is -180.0 to
1613 * 180.0. Default value is 0.0 and represents no modification.
1615 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_HUE);
1617 g_object_class_install_property (object_class, PROP_HUE, filter_op->pspec);
1620 * GstVaapiPostproc:saturation:
1622 * The color saturation, expressed as a float value. Range is 0.0 to
1623 * 2.0. Default value is 1.0 and represents no modification.
1625 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SATURATION);
1627 g_object_class_install_property (object_class,
1628 PROP_SATURATION, filter_op->pspec);
1631 * GstVaapiPostproc:brightness:
1633 * The color brightness, expressed as a float value. Range is -1.0
1634 * to 1.0. Default value is 0.0 and represents no modification.
1636 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_BRIGHTNESS);
1638 g_object_class_install_property (object_class,
1639 PROP_BRIGHTNESS, filter_op->pspec);
1642 * GstVaapiPostproc:contrast:
1644 * The color contrast, expressed as a float value. Range is 0.0 to
1645 * 2.0. Default value is 1.0 and represents no modification.
1647 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_CONTRAST);
1649 g_object_class_install_property (object_class,
1650 PROP_CONTRAST, filter_op->pspec);
1653 * GstVaapiPostproc:scale-method:
1655 * The scaling method to use, expressed as an enum value. See
1656 * #GstVaapiScaleMethod.
1658 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SCALING);
1660 g_object_class_install_property (object_class,
1661 PROP_SCALE_METHOD, filter_op->pspec);
1664 * GstVaapiPostproc:skin-tone-enhancement:
1666 * Apply the skin tone enhancement algorithm.
1668 filter_op = find_filter_op (filter_ops, GST_VAAPI_FILTER_OP_SKINTONE);
1670 g_object_class_install_property (object_class,
1671 PROP_SKIN_TONE_ENHANCEMENT, filter_op->pspec);
1673 g_ptr_array_unref (filter_ops);
1677 find_value_ptr (GstVaapiPostproc * postproc, GstVaapiFilterOp op)
1680 case GST_VAAPI_FILTER_OP_HUE:
1681 return &postproc->hue;
1682 case GST_VAAPI_FILTER_OP_SATURATION:
1683 return &postproc->saturation;
1684 case GST_VAAPI_FILTER_OP_BRIGHTNESS:
1685 return &postproc->brightness;
1686 case GST_VAAPI_FILTER_OP_CONTRAST:
1687 return &postproc->contrast;
1694 cb_set_default_value (GstVaapiPostproc * postproc, GPtrArray * filter_ops,
1695 GstVaapiFilterOp op)
1697 GstVaapiFilterOpInfo *filter_op;
1698 GParamSpecFloat *pspec;
1701 filter_op = find_filter_op (filter_ops, op);
1704 var = find_value_ptr (postproc, op);
1707 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
1708 *var = pspec->default_value;
1712 gst_vaapipostproc_init (GstVaapiPostproc * postproc)
1714 GPtrArray *filter_ops;
1717 gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (postproc),
1720 postproc->format = DEFAULT_FORMAT;
1721 postproc->deinterlace_mode = DEFAULT_DEINTERLACE_MODE;
1722 postproc->deinterlace_method = DEFAULT_DEINTERLACE_METHOD;
1723 postproc->field_duration = GST_CLOCK_TIME_NONE;
1724 postproc->keep_aspect = TRUE;
1725 postproc->get_va_surfaces = TRUE;
1727 filter_ops = gst_vaapi_filter_get_operations (NULL);
1729 for (i = GST_VAAPI_FILTER_OP_HUE; i <= GST_VAAPI_FILTER_OP_CONTRAST; i++)
1730 cb_set_default_value (postproc, filter_ops, i);
1731 g_ptr_array_unref (filter_ops);
1734 gst_video_info_init (&postproc->sinkpad_info);
1735 gst_video_info_init (&postproc->srcpad_info);
1736 gst_video_info_init (&postproc->filter_pool_info);
1739 /* ------------------------------------------------------------------------ */
1740 /* --- GstColorBalance interface --- */
1741 /* ------------------------------------------------------------------------ */
1743 #define CB_CHANNEL_FACTOR 1000.0
1747 GstVaapiFilterOp op;
1749 } ColorBalanceChannel;
1751 ColorBalanceChannel cb_channels[] = {
1752 {GST_VAAPI_FILTER_OP_HUE, "VA_FILTER_HUE"},
1753 {GST_VAAPI_FILTER_OP_SATURATION, "VA_FILTER_SATURATION"},
1754 {GST_VAAPI_FILTER_OP_BRIGHTNESS, "VA_FILTER_BRIGHTNESS"},
1755 {GST_VAAPI_FILTER_OP_CONTRAST, "VA_FILTER_CONTRAST"},
1759 cb_channels_init (GstVaapiPostproc * postproc)
1761 GPtrArray *filter_ops;
1762 GstVaapiFilterOpInfo *filter_op;
1763 GParamSpecFloat *pspec;
1764 GstColorBalanceChannel *channel;
1767 if (postproc->cb_channels)
1770 if (!gst_vaapipostproc_ensure_filter (postproc))
1773 filter_ops = postproc->filter_ops ? g_ptr_array_ref (postproc->filter_ops)
1774 : gst_vaapi_filter_get_operations (postproc->filter);
1778 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
1779 filter_op = find_filter_op (filter_ops, cb_channels[i].op);
1783 pspec = G_PARAM_SPEC_FLOAT (filter_op->pspec);
1784 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1785 channel->label = g_strdup (cb_channels[i].name);
1786 channel->min_value = pspec->minimum * CB_CHANNEL_FACTOR;
1787 channel->max_value = pspec->maximum * CB_CHANNEL_FACTOR;
1789 postproc->cb_channels = g_list_prepend (postproc->cb_channels, channel);
1792 g_ptr_array_unref (filter_ops);
1795 static const GList *
1796 gst_vaapipostproc_colorbalance_list_channels (GstColorBalance * balance)
1798 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
1800 cb_channels_init (postproc);
1801 return postproc->cb_channels;
1805 cb_get_value_ptr (GstVaapiPostproc * postproc, GstColorBalanceChannel * channel,
1806 GstVaapiPostprocFlags * flags)
1811 for (i = 0; i < G_N_ELEMENTS (cb_channels); i++) {
1812 if (g_ascii_strcasecmp (cb_channels[i].name, channel->label) == 0)
1815 if (i >= G_N_ELEMENTS (cb_channels))
1818 ret = find_value_ptr (postproc, cb_channels[i].op);
1820 *flags = 1 << cb_channels[i].op;
1825 gst_vaapipostproc_colorbalance_set_value (GstColorBalance * balance,
1826 GstColorBalanceChannel * channel, gint value)
1828 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
1829 GstVaapiPostprocFlags flags;
1830 gfloat new_val, *var;
1832 value = CLAMP (value, channel->min_value, channel->max_value);
1833 new_val = (gfloat) value / CB_CHANNEL_FACTOR;
1835 var = cb_get_value_ptr (postproc, channel, &flags);
1838 postproc->flags |= flags;
1839 gst_color_balance_value_changed (balance, channel, value);
1843 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
1847 gst_vaapipostproc_colorbalance_get_value (GstColorBalance * balance,
1848 GstColorBalanceChannel * channel)
1850 GstVaapiPostproc *const postproc = GST_VAAPIPOSTPROC (balance);
1854 var = cb_get_value_ptr (postproc, channel, NULL);
1856 new_val = (gint) ((*var) * CB_CHANNEL_FACTOR);
1857 new_val = CLAMP (new_val, channel->min_value, channel->max_value);
1861 GST_WARNING_OBJECT (postproc, "unknown channel %s", channel->label);
1865 static GstColorBalanceType
1866 gst_vaapipostproc_colorbalance_get_balance_type (GstColorBalance * balance)
1868 return GST_COLOR_BALANCE_HARDWARE;
1872 gst_vaapipostproc_colorbalance_init (gpointer iface, gpointer data)
1874 GstColorBalanceInterface *cbface = iface;
1875 cbface->list_channels = gst_vaapipostproc_colorbalance_list_channels;
1876 cbface->set_value = gst_vaapipostproc_colorbalance_set_value;
1877 cbface->get_value = gst_vaapipostproc_colorbalance_get_value;
1878 cbface->get_balance_type = gst_vaapipostproc_colorbalance_get_balance_type;