2 * Copyright (C) 2020 Igalia, S.L.
3 * Author: Víctor Jáquez <vjaquez@igalia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the0
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-vapostproc
24 * @short_description: A VA-API base video postprocessing filter
26 * vapostproc applies different video filters to VA surfaces. These
27 * filters vary depending on the installed and chosen
28 * [VA-API](https://01.org/linuxmedia/vaapi) driver, but usually
29 * resizing and color conversion are available.
31 * The generated surfaces can be mapped onto main memory as video
34 * Use gst-inspect-1.0 to introspect the available capabilities of the
35 * driver's post-processor entry point.
37 * ## Example launch line
39 * gst-launch-1.0 videotestsrc ! "video/x-raw,format=(string)NV12" ! vapostproc ! autovideosink
42 * Cropping is supported via buffers' crop meta. It's only done if the
43 * postproccessor is not in passthrough mode or if downstream doesn't
44 * support the crop meta API.
46 * ### Cropping example
48 * gst-launch-1.0 videotestsrc ! "video/x-raw,format=(string)NV12" ! videocrop bottom=50 left=100 ! vapostproc ! autovideosink
51 * If the VA driver support color balance filter, with controls such
52 * as hue, brightness, contrast, etc., those controls are exposed both
53 * as element properties and through the #GstColorBalance interface.
65 #include <gst/va/gstva.h>
66 #include <gst/video/video.h>
67 #include <va/va_drmcommon.h>
69 #include "gstvabasetransform.h"
70 #include "gstvacaps.h"
71 #include "gstvadisplay_priv.h"
72 #include "gstvafilter.h"
74 GST_DEBUG_CATEGORY_STATIC (gst_va_vpp_debug);
75 #define GST_CAT_DEFAULT gst_va_vpp_debug
77 #define GST_VA_VPP(obj) ((GstVaVpp *) obj)
78 #define GST_VA_VPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaVppClass))
79 #define GST_VA_VPP_CLASS(klass) ((GstVaVppClass *) klass)
81 #define SWAP_INT(a, b) G_STMT_START { \
87 typedef struct _GstVaVpp GstVaVpp;
88 typedef struct _GstVaVppClass GstVaVppClass;
92 /* GstVideoFilter overlaps functionality */
93 GstVaBaseTransformClass parent_class;
98 GstVaBaseTransform parent;
100 gboolean rebuild_filters;
111 gboolean auto_contrast;
112 gboolean auto_brightness;
113 gboolean auto_saturation;
114 GstVideoOrientationMethod direction;
115 GstVideoOrientationMethod prev_direction;
116 GstVideoOrientationMethod tag_direction;
117 gboolean add_borders;
120 guint32 scale_method;
122 gboolean hdr_mapping;
123 gboolean has_hdr_meta;
124 VAHdrMetaDataHDR10 hdr_meta;
129 static GstElementClass *parent_class = NULL;
133 gchar *render_device_path;
139 PROP_DISABLE_PASSTHROUGH = GST_VA_FILTER_PROP_LAST + 1,
145 static GParamSpec *properties[N_PROPERTIES - GST_VA_FILTER_PROP_LAST];
147 #define PROPERTIES(idx) properties[idx - GST_VA_FILTER_PROP_LAST]
149 /* convertions that disable passthrough */
152 VPP_CONVERT_SIZE = 1 << 0,
153 VPP_CONVERT_FORMAT = 1 << 1,
154 VPP_CONVERT_FILTERS = 1 << 2,
155 VPP_CONVERT_DIRECTION = 1 << 3,
156 VPP_CONVERT_FEATURE = 1 << 4,
157 VPP_CONVERT_CROP = 1 << 5,
158 VPP_CONVERT_DUMMY = 1 << 6,
162 static const gchar *caps_str =
163 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
164 "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
165 GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
166 "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR }");
169 #define META_TAG_COLORSPACE meta_tag_colorspace_quark
170 static GQuark meta_tag_colorspace_quark;
171 #define META_TAG_SIZE meta_tag_size_quark
172 static GQuark meta_tag_size_quark;
173 #define META_TAG_ORIENTATION meta_tag_orientation_quark
174 static GQuark meta_tag_orientation_quark;
175 #define META_TAG_VIDEO meta_tag_video_quark
176 static GQuark meta_tag_video_quark;
178 static void gst_va_vpp_colorbalance_init (gpointer iface, gpointer data);
179 static void gst_va_vpp_rebuild_filters (GstVaVpp * self);
182 gst_va_vpp_dispose (GObject * object)
184 GstVaVpp *self = GST_VA_VPP (object);
187 g_list_free_full (g_steal_pointer (&self->channels), g_object_unref);
189 G_OBJECT_CLASS (parent_class)->dispose (object);
193 gst_va_vpp_update_passthrough (GstVaVpp * self, gboolean reconf)
195 GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
198 old = gst_base_transform_is_passthrough (trans);
200 GST_OBJECT_LOCK (self);
201 new = (self->op_flags == 0);
202 GST_OBJECT_UNLOCK (self);
205 GST_INFO_OBJECT (self, "%s passthrough", new ? "enabling" : "disabling");
207 gst_base_transform_reconfigure_src (trans);
208 gst_base_transform_set_passthrough (trans, new);
213 _update_properties_unlocked (GstVaVpp * self)
215 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
220 if ((self->direction != GST_VIDEO_ORIENTATION_AUTO
221 && self->direction != self->prev_direction)
222 || (self->direction == GST_VIDEO_ORIENTATION_AUTO
223 && self->tag_direction != self->prev_direction)) {
225 GstVideoOrientationMethod direction =
226 (self->direction == GST_VIDEO_ORIENTATION_AUTO) ?
227 self->tag_direction : self->direction;
229 if (!gst_va_filter_set_orientation (btrans->filter, direction)) {
230 if (self->direction == GST_VIDEO_ORIENTATION_AUTO)
231 self->tag_direction = self->prev_direction;
233 self->direction = self->prev_direction;
235 self->op_flags &= ~VPP_CONVERT_DIRECTION;
237 /* FIXME: unlocked bus warning message */
238 GST_WARNING_OBJECT (self,
239 "Driver cannot set resquested orientation. Setting it back.");
241 self->prev_direction = direction;
243 self->op_flags |= VPP_CONVERT_DIRECTION;
245 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (self));
248 self->op_flags &= ~VPP_CONVERT_DIRECTION;
251 if (!gst_va_filter_set_scale_method (btrans->filter, self->scale_method))
252 GST_WARNING_OBJECT (self, "could not set the filter scale method.");
256 gst_va_vpp_set_property (GObject * object, guint prop_id,
257 const GValue * value, GParamSpec * pspec)
259 GstVaVpp *self = GST_VA_VPP (object);
261 GST_OBJECT_LOCK (object);
263 case GST_VA_FILTER_PROP_DENOISE:
264 self->denoise = g_value_get_float (value);
265 g_atomic_int_set (&self->rebuild_filters, TRUE);
267 case GST_VA_FILTER_PROP_SHARPEN:
268 self->sharpen = g_value_get_float (value);
269 g_atomic_int_set (&self->rebuild_filters, TRUE);
271 case GST_VA_FILTER_PROP_SKINTONE:
272 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
273 self->skintone = (float) g_value_get_boolean (value);
275 self->skintone = g_value_get_float (value);
276 g_atomic_int_set (&self->rebuild_filters, TRUE);
278 case GST_VA_FILTER_PROP_VIDEO_DIR:{
279 GstVideoOrientationMethod direction = g_value_get_enum (value);
280 self->prev_direction = (direction == GST_VIDEO_ORIENTATION_AUTO) ?
281 self->tag_direction : self->direction;
282 self->direction = direction;
285 case GST_VA_FILTER_PROP_HUE:
286 self->hue = g_value_get_float (value);
287 g_atomic_int_set (&self->rebuild_filters, TRUE);
289 case GST_VA_FILTER_PROP_SATURATION:
290 self->saturation = g_value_get_float (value);
291 g_atomic_int_set (&self->rebuild_filters, TRUE);
293 case GST_VA_FILTER_PROP_BRIGHTNESS:
294 self->brightness = g_value_get_float (value);
295 g_atomic_int_set (&self->rebuild_filters, TRUE);
297 case GST_VA_FILTER_PROP_CONTRAST:
298 self->contrast = g_value_get_float (value);
299 g_atomic_int_set (&self->rebuild_filters, TRUE);
301 case GST_VA_FILTER_PROP_AUTO_SATURATION:
302 self->auto_saturation = g_value_get_boolean (value);
303 g_atomic_int_set (&self->rebuild_filters, TRUE);
305 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
306 self->auto_brightness = g_value_get_boolean (value);
307 g_atomic_int_set (&self->rebuild_filters, TRUE);
309 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
310 self->auto_contrast = g_value_get_boolean (value);
311 g_atomic_int_set (&self->rebuild_filters, TRUE);
313 case GST_VA_FILTER_PROP_HDR:
314 self->hdr_mapping = g_value_get_boolean (value);
315 g_atomic_int_set (&self->rebuild_filters, TRUE);
317 case PROP_DISABLE_PASSTHROUGH:{
318 gboolean disable_passthrough = g_value_get_boolean (value);
319 if (disable_passthrough)
320 self->op_flags |= VPP_CONVERT_DUMMY;
322 self->op_flags &= ~VPP_CONVERT_DUMMY;
325 case PROP_ADD_BORDERS:
326 self->add_borders = g_value_get_boolean (value);
328 case PROP_SCALE_METHOD:
329 self->scale_method = g_value_get_enum (value);
332 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336 _update_properties_unlocked (self);
337 GST_OBJECT_UNLOCK (object);
339 gst_va_vpp_update_passthrough (self, FALSE);
343 gst_va_vpp_get_property (GObject * object, guint prop_id, GValue * value,
346 GstVaVpp *self = GST_VA_VPP (object);
348 GST_OBJECT_LOCK (object);
350 case GST_VA_FILTER_PROP_DENOISE:
351 g_value_set_float (value, self->denoise);
353 case GST_VA_FILTER_PROP_SHARPEN:
354 g_value_set_float (value, self->sharpen);
356 case GST_VA_FILTER_PROP_SKINTONE:
357 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
358 g_value_set_boolean (value, self->skintone > 0);
360 g_value_set_float (value, self->skintone);
362 case GST_VA_FILTER_PROP_VIDEO_DIR:
363 g_value_set_enum (value, self->direction);
365 case GST_VA_FILTER_PROP_HUE:
366 g_value_set_float (value, self->hue);
368 case GST_VA_FILTER_PROP_SATURATION:
369 g_value_set_float (value, self->saturation);
371 case GST_VA_FILTER_PROP_BRIGHTNESS:
372 g_value_set_float (value, self->brightness);
374 case GST_VA_FILTER_PROP_CONTRAST:
375 g_value_set_float (value, self->contrast);
377 case GST_VA_FILTER_PROP_AUTO_SATURATION:
378 g_value_set_boolean (value, self->auto_saturation);
380 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
381 g_value_set_boolean (value, self->auto_brightness);
383 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
384 g_value_set_boolean (value, self->auto_contrast);
386 case GST_VA_FILTER_PROP_HDR:
387 g_value_set_boolean (value, self->hdr_mapping);
389 case PROP_DISABLE_PASSTHROUGH:
390 g_value_set_boolean (value, (self->op_flags & VPP_CONVERT_DUMMY));
392 case PROP_ADD_BORDERS:
393 g_value_set_boolean (value, self->add_borders);
395 case PROP_SCALE_METHOD:
396 g_value_set_enum (value, self->scale_method);
399 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
402 GST_OBJECT_UNLOCK (object);
406 gst_va_vpp_propose_allocation (GstBaseTransform * trans,
407 GstQuery * decide_query, GstQuery * query)
409 /* if we are not passthrough, we can handle crop meta */
411 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
413 return GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
414 decide_query, query);
418 gst_va_vpp_update_properties (GstVaBaseTransform * btrans)
420 GstVaVpp *self = GST_VA_VPP (btrans);
422 gst_va_vpp_rebuild_filters (self);
423 _update_properties_unlocked (self);
427 _set_hdr_metadata (GstVaVpp * self, GstCaps * caps)
429 GstVideoMasteringDisplayInfo mdinfo;
430 GstVideoContentLightLevel llevel;
432 self->has_hdr_meta = FALSE;
434 if (gst_video_mastering_display_info_from_caps (&mdinfo, caps)) {
435 self->hdr_meta.display_primaries_x[0] = mdinfo.display_primaries[1].x;
436 self->hdr_meta.display_primaries_x[1] = mdinfo.display_primaries[2].x;
437 self->hdr_meta.display_primaries_x[2] = mdinfo.display_primaries[0].x;
439 self->hdr_meta.display_primaries_y[0] = mdinfo.display_primaries[1].y;
440 self->hdr_meta.display_primaries_y[1] = mdinfo.display_primaries[2].y;
441 self->hdr_meta.display_primaries_y[2] = mdinfo.display_primaries[0].y;
443 self->hdr_meta.white_point_x = mdinfo.white_point.x;
444 self->hdr_meta.white_point_y = mdinfo.white_point.y;
446 self->hdr_meta.max_display_mastering_luminance =
447 mdinfo.max_display_mastering_luminance;
448 self->hdr_meta.min_display_mastering_luminance =
449 mdinfo.min_display_mastering_luminance;
451 self->has_hdr_meta = TRUE;
455 if (gst_video_content_light_level_from_caps (&llevel, caps)) {
456 self->hdr_meta.max_content_light_level = llevel.max_content_light_level;
457 self->hdr_meta.max_pic_average_light_level =
458 llevel.max_frame_average_light_level;
460 self->has_hdr_meta = TRUE;
463 /* rebuild filters only if hdr mapping is enabled */
464 g_atomic_int_set (&self->rebuild_filters, self->hdr_mapping);
468 gst_va_vpp_set_info (GstVaBaseTransform * btrans, GstCaps * incaps,
469 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
471 GstVaVpp *self = GST_VA_VPP (btrans);
472 GstCapsFeatures *infeat, *outfeat;
473 gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
475 if (GST_VIDEO_INFO_INTERLACE_MODE (in_info) !=
476 GST_VIDEO_INFO_INTERLACE_MODE (out_info)) {
477 GST_ERROR_OBJECT (self, "input and output formats do not match");
481 /* calculate possible borders if display-aspect-ratio change */
483 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (in_info),
484 GST_VIDEO_INFO_HEIGHT (in_info), GST_VIDEO_INFO_PAR_N (in_info),
485 GST_VIDEO_INFO_PAR_D (in_info), &from_dar_n, &from_dar_d)) {
486 from_dar_n = from_dar_d = -1;
489 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (out_info),
490 GST_VIDEO_INFO_HEIGHT (out_info), GST_VIDEO_INFO_PAR_N (out_info),
491 GST_VIDEO_INFO_PAR_D (out_info), &to_dar_n, &to_dar_d)) {
492 to_dar_n = to_dar_d = -1;
495 /* if video-orientation changes consider it for borders */
496 switch (gst_va_filter_get_orientation (btrans->filter)) {
497 case GST_VIDEO_ORIENTATION_90R:
498 case GST_VIDEO_ORIENTATION_90L:
499 case GST_VIDEO_ORIENTATION_UL_LR:
500 case GST_VIDEO_ORIENTATION_UR_LL:
501 SWAP_INT (from_dar_n, from_dar_d);
507 self->borders_h = self->borders_w = 0;
508 if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
509 if (self->add_borders) {
510 gint n, d, to_h, to_w;
512 if (from_dar_n != -1 && from_dar_d != -1
513 && gst_util_fraction_multiply (from_dar_n, from_dar_d,
514 out_info->par_d, out_info->par_n, &n, &d)) {
515 to_h = gst_util_uint64_scale_int (out_info->width, d, n);
516 if (to_h <= out_info->height) {
517 self->borders_h = out_info->height - to_h;
520 to_w = gst_util_uint64_scale_int (out_info->height, n, d);
521 g_assert (to_w <= out_info->width);
523 self->borders_w = out_info->width - to_w;
526 GST_WARNING_OBJECT (self, "Can't calculate borders");
529 GST_WARNING_OBJECT (self, "Can't keep DAR!");
534 if (gst_video_info_is_equal (in_info, out_info)) {
535 self->op_flags &= ~VPP_CONVERT_FORMAT & ~VPP_CONVERT_SIZE;
537 if ((GST_VIDEO_INFO_FORMAT (in_info) != GST_VIDEO_INFO_FORMAT (out_info))
538 || !gst_video_colorimetry_is_equivalent (&GST_VIDEO_INFO_COLORIMETRY
539 (in_info), GST_VIDEO_INFO_COMP_DEPTH (in_info, 0),
540 &GST_VIDEO_INFO_COLORIMETRY (out_info),
541 GST_VIDEO_INFO_COMP_DEPTH (out_info, 0)))
542 self->op_flags |= VPP_CONVERT_FORMAT;
544 self->op_flags &= ~VPP_CONVERT_FORMAT;
546 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
547 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
548 || self->borders_h > 0 || self->borders_w > 0)
549 self->op_flags |= VPP_CONVERT_SIZE;
551 self->op_flags &= ~VPP_CONVERT_SIZE;
554 infeat = gst_caps_get_features (incaps, 0);
555 outfeat = gst_caps_get_features (outcaps, 0);
556 if (!gst_caps_features_is_equal (infeat, outfeat))
557 self->op_flags |= VPP_CONVERT_FEATURE;
559 self->op_flags &= ~VPP_CONVERT_FEATURE;
561 if (gst_va_filter_set_video_info (btrans->filter, in_info, out_info)) {
562 _set_hdr_metadata (self, incaps);
563 gst_va_vpp_update_passthrough (self, FALSE);
570 static inline gboolean
571 _get_filter_value (GstVaVpp * self, VAProcFilterType type, gfloat * value)
575 GST_OBJECT_LOCK (self);
577 case VAProcFilterNoiseReduction:
578 *value = self->denoise;
580 case VAProcFilterSharpening:
581 *value = self->sharpen;
583 case VAProcFilterSkinToneEnhancement:
584 *value = self->skintone;
590 GST_OBJECT_UNLOCK (self);
595 static inline gboolean
596 _add_filter_buffer (GstVaVpp * self, VAProcFilterType type,
597 const VAProcFilterCap * cap)
599 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
600 VAProcFilterParameterBuffer param;
603 if (!_get_filter_value (self, type, &value))
605 if (value == cap->range.default_value)
609 param = (VAProcFilterParameterBuffer) {
615 return gst_va_filter_add_filter_buffer (btrans->filter, ¶m,
619 static inline gboolean
620 _get_filter_cb_value (GstVaVpp * self, VAProcColorBalanceType type,
625 GST_OBJECT_LOCK (self);
627 case VAProcColorBalanceHue:
630 case VAProcColorBalanceSaturation:
631 *value = self->saturation;
633 case VAProcColorBalanceBrightness:
634 *value = self->brightness;
636 case VAProcColorBalanceContrast:
637 *value = self->contrast;
639 case VAProcColorBalanceAutoSaturation:
640 *value = self->auto_saturation;
642 case VAProcColorBalanceAutoBrightness:
643 *value = self->auto_brightness;
645 case VAProcColorBalanceAutoContrast:
646 *value = self->auto_contrast;
652 GST_OBJECT_UNLOCK (self);
657 static inline gboolean
658 _add_filter_cb_buffer (GstVaVpp * self,
659 const VAProcFilterCapColorBalance * caps, guint num_caps)
661 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
662 VAProcFilterParameterBufferColorBalance param[VAProcColorBalanceCount] =
668 for (i = 0; i < num_caps && i < VAProcColorBalanceCount; i++) {
669 if (!_get_filter_cb_value (self, caps[i].type, &value))
671 if (value == caps[i].range.default_value)
675 param[c++] = (VAProcFilterParameterBufferColorBalance) {
676 .type = VAProcFilterColorBalance,
677 .attrib = caps[i].type,
684 return gst_va_filter_add_filter_buffer (btrans->filter, param,
690 static inline gboolean
691 _add_filter_hdr_buffer (GstVaVpp * self,
692 const VAProcFilterCapHighDynamicRange * caps)
694 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
696 VAProcFilterParameterBufferHDRToneMapping params = {
697 .type = VAProcFilterHighDynamicRangeToneMapping,
699 .metadata_type = VAProcHighDynamicRangeMetadataHDR10,
700 .metadata = &self->hdr_meta,
701 .metadata_size = sizeof (self->hdr_meta),
706 /* if not has hdr meta, it may try later again */
707 if (!(self->has_hdr_meta && self->hdr_mapping))
710 if (!(caps && caps->metadata_type == VAProcHighDynamicRangeMetadataHDR10
711 && (caps->caps_flag & VA_TONE_MAPPING_HDR_TO_SDR)))
714 if (self->op_flags & VPP_CONVERT_FORMAT) {
715 GST_WARNING_OBJECT (self, "Cannot apply HDR with color conversion");
719 return gst_va_filter_add_filter_buffer (btrans->filter, ¶ms,
723 self->hdr_mapping = FALSE;
724 g_object_notify (G_OBJECT (self), "hdr-tone-mapping");
729 _build_filters (GstVaVpp * self)
731 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
732 static const VAProcFilterType filter_types[] = { VAProcFilterNoiseReduction,
733 VAProcFilterSharpening, VAProcFilterSkinToneEnhancement,
734 VAProcFilterColorBalance, VAProcFilterHighDynamicRangeToneMapping,
737 gboolean apply = FALSE;
739 for (i = 0; i < G_N_ELEMENTS (filter_types); i++) {
740 const gpointer caps = gst_va_filter_get_filter_caps (btrans->filter,
741 filter_types[i], &num_caps);
745 switch (filter_types[i]) {
746 case VAProcFilterNoiseReduction:
747 apply |= _add_filter_buffer (self, filter_types[i], caps);
749 case VAProcFilterSharpening:
750 apply |= _add_filter_buffer (self, filter_types[i], caps);
752 case VAProcFilterSkinToneEnhancement:
753 apply |= _add_filter_buffer (self, filter_types[i], caps);
755 case VAProcFilterColorBalance:
756 apply |= _add_filter_cb_buffer (self, caps, num_caps);
758 case VAProcFilterHighDynamicRangeToneMapping:
759 apply |= _add_filter_hdr_buffer (self, caps);
765 GST_OBJECT_LOCK (self);
767 self->op_flags |= VPP_CONVERT_FILTERS;
769 self->op_flags &= ~VPP_CONVERT_FILTERS;
770 GST_OBJECT_UNLOCK (self);
774 gst_va_vpp_rebuild_filters (GstVaVpp * self)
776 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
778 if (!g_atomic_int_get (&self->rebuild_filters))
781 gst_va_filter_drop_filter_buffers (btrans->filter);
782 _build_filters (self);
783 g_atomic_int_set (&self->rebuild_filters, FALSE);
787 gst_va_vpp_before_transform (GstBaseTransform * trans, GstBuffer * inbuf)
789 GstVaVpp *self = GST_VA_VPP (trans);
790 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
791 GstClockTime ts, stream_time;
792 gboolean is_passthrough;
794 ts = GST_BUFFER_TIMESTAMP (inbuf);
796 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, ts);
798 GST_TRACE_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
800 if (GST_CLOCK_TIME_IS_VALID (stream_time))
801 gst_object_sync_values (GST_OBJECT (self), stream_time);
803 gst_va_vpp_rebuild_filters (self);
804 gst_va_vpp_update_passthrough (self, TRUE);
806 /* cropping is only enabled if vapostproc is not in passthrough */
807 is_passthrough = gst_base_transform_is_passthrough (trans);
808 GST_OBJECT_LOCK (self);
809 if (!is_passthrough && gst_buffer_get_video_crop_meta (inbuf)) {
810 self->op_flags |= VPP_CONVERT_CROP;
812 self->op_flags &= ~VPP_CONVERT_CROP;
814 gst_va_filter_enable_cropping (btrans->filter,
815 (self->op_flags & VPP_CONVERT_CROP));
816 GST_OBJECT_UNLOCK (self);
820 gst_va_vpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
823 GstVaVpp *self = GST_VA_VPP (trans);
824 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
825 GstBuffer *buf = NULL;
826 GstFlowReturn res = GST_FLOW_OK;
827 GstVaSample src, dst;
829 if (G_UNLIKELY (!btrans->negotiated))
832 res = gst_va_base_transform_import_buffer (btrans, inbuf, &buf);
833 if (res != GST_FLOW_OK)
837 src = (GstVaSample) {
839 .flags = gst_va_buffer_get_surface_flags (buf, &btrans->in_info),
842 dst = (GstVaSample) {
844 .borders_h = self->borders_h,
845 .borders_w = self->borders_w,
846 .flags = gst_va_buffer_get_surface_flags (outbuf, &btrans->out_info),
850 if (!gst_va_filter_process (btrans->filter, &src, &dst)) {
851 gst_buffer_set_flags (outbuf, GST_BUFFER_FLAG_CORRUPTED);
852 res = GST_BASE_TRANSFORM_FLOW_DROPPED;
855 gst_buffer_unref (buf);
862 GST_ELEMENT_ERROR (self, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
863 return GST_FLOW_NOT_NEGOTIATED;
868 gst_va_vpp_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
869 GstMeta * meta, GstBuffer * outbuf)
871 GstVaVpp *self = GST_VA_VPP (trans);
872 const GstMetaInfo *info = meta->info;
873 const gchar *const *tags;
875 tags = gst_meta_api_type_get_tags (info->api);
880 /* don't copy colorspace/size/orientation specific metadata */
881 if ((self->op_flags & VPP_CONVERT_FORMAT)
882 && gst_meta_api_type_has_tag (info->api, META_TAG_COLORSPACE))
884 else if ((self->op_flags & (VPP_CONVERT_SIZE | VPP_CONVERT_CROP))
885 && gst_meta_api_type_has_tag (info->api, META_TAG_SIZE))
887 else if ((self->op_flags & VPP_CONVERT_DIRECTION)
888 && gst_meta_api_type_has_tag (info->api, META_TAG_ORIENTATION))
890 else if (gst_meta_api_type_has_tag (info->api, META_TAG_VIDEO))
896 /* In structures with supported caps features it's:
897 * + Rangified resolution size.
898 * + Rangified "pixel-aspect-ratio" if present.
899 * + Removed "format", "colorimetry", "chroma-site"
901 * Structures with unsupported caps features are copied as-is.
904 gst_va_vpp_caps_remove_fields (GstCaps * caps)
907 GstStructure *structure;
908 GstCapsFeatures *features;
911 ret = gst_caps_new_empty ();
913 n = gst_caps_get_size (caps);
914 for (i = 0; i < n; i++) {
915 structure = gst_caps_get_structure (caps, i);
916 features = gst_caps_get_features (caps, i);
918 /* If this is already expressed by the existing caps
919 * skip this structure */
920 if (i > 0 && gst_caps_is_subset_structure_full (ret, structure, features))
923 structure = gst_structure_copy (structure);
925 m = gst_caps_features_get_size (features);
926 for (j = 0; j < m; j++) {
927 const gchar *feature = gst_caps_features_get_nth (features, j);
929 if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0
930 || g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_DMABUF) == 0
931 || g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_VA) == 0) {
933 /* rangify frame size */
934 gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
935 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
937 /* if pixel aspect ratio, make a range of it */
938 if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
939 gst_structure_set (structure, "pixel-aspect-ratio",
940 GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
943 /* remove format-related fields */
944 gst_structure_remove_fields (structure, "format", "colorimetry",
945 "chroma-site", NULL);
951 gst_caps_append_structure_full (ret, structure,
952 gst_caps_features_copy (features));
958 /* Returns all structures in @caps without @feature_name but now with
961 gst_va_vpp_complete_caps_features (const GstCaps * caps,
962 const gchar * feature_name)
967 tmp = gst_caps_new_empty ();
969 n = gst_caps_get_size (caps);
970 for (i = 0; i < n; i++) {
971 GstCapsFeatures *features, *orig_features;
972 GstStructure *s = gst_caps_get_structure (caps, i);
973 gboolean contained = FALSE;
975 orig_features = gst_caps_get_features (caps, i);
976 features = gst_caps_features_new (feature_name, NULL);
978 m = gst_caps_features_get_size (orig_features);
979 for (j = 0; j < m; j++) {
980 const gchar *feature = gst_caps_features_get_nth (orig_features, j);
982 /* if we already have the features */
983 if (gst_caps_features_contains (features, feature)) {
989 if (!contained && !gst_caps_is_subset_structure_full (tmp, s, features))
990 gst_caps_append_structure_full (tmp, gst_structure_copy (s), features);
992 gst_caps_features_free (features);
999 gst_va_vpp_transform_caps (GstBaseTransform * trans, GstPadDirection direction,
1000 GstCaps * caps, GstCaps * filter)
1002 GstVaVpp *self = GST_VA_VPP (trans);
1003 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
1004 GstCaps *ret, *tmp, *filter_caps;
1006 GST_DEBUG_OBJECT (self,
1007 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1008 (direction == GST_PAD_SINK) ? "sink" : "src");
1010 filter_caps = gst_va_base_transform_get_filter_caps (btrans);
1011 if (filter_caps && !gst_caps_can_intersect (caps, filter_caps)) {
1012 ret = gst_caps_ref (caps);
1016 ret = gst_va_vpp_caps_remove_fields (caps);
1018 tmp = gst_va_vpp_complete_caps_features (ret, GST_CAPS_FEATURE_MEMORY_VA);
1019 if (!gst_caps_is_subset (tmp, ret)) {
1020 gst_caps_append (ret, tmp);
1022 gst_caps_unref (tmp);
1025 tmp = gst_va_vpp_complete_caps_features (ret, GST_CAPS_FEATURE_MEMORY_DMABUF);
1026 if (!gst_caps_is_subset (tmp, ret)) {
1027 gst_caps_append (ret, tmp);
1029 gst_caps_unref (tmp);
1032 tmp = gst_va_vpp_complete_caps_features (ret,
1033 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
1034 if (!gst_caps_is_subset (tmp, ret)) {
1035 gst_caps_append (ret, tmp);
1037 gst_caps_unref (tmp);
1042 GstCaps *intersection;
1045 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1046 gst_caps_unref (ret);
1050 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
1056 * This is an incomplete matrix of in formats and a score for the preferred output
1059 * out: RGB24 RGB16 ARGB AYUV YUV444 YUV422 YUV420 YUV411 YUV410 PAL GRAY
1061 * RGB24 0 2 1 2 2 3 4 5 6 7 8
1062 * RGB16 1 0 1 2 2 3 4 5 6 7 8
1063 * ARGB 2 3 0 1 4 5 6 7 8 9 10
1064 * AYUV 3 4 1 0 2 5 6 7 8 9 10
1065 * YUV444 2 4 3 1 0 5 6 7 8 9 10
1066 * YUV422 3 5 4 2 1 0 6 7 8 9 10
1067 * YUV420 4 6 5 3 2 1 0 7 8 9 10
1068 * YUV411 4 6 5 3 2 1 7 0 8 9 10
1069 * YUV410 6 8 7 5 4 3 2 1 0 9 10
1070 * PAL 1 3 2 6 4 6 7 8 9 0 10
1071 * GRAY 1 4 3 2 1 5 6 7 8 9 0
1073 * PAL or GRAY are never preferred, if we can we would convert to PAL instead
1075 * less subsampling is preferred and if any, preferably horizontal
1076 * We would like to keep the alpha, even if we would need to to colorspace conversion
1079 #define SCORE_FORMAT_CHANGE 1
1080 #define SCORE_DEPTH_CHANGE 1
1081 #define SCORE_ALPHA_CHANGE 1
1082 #define SCORE_CHROMA_W_CHANGE 1
1083 #define SCORE_CHROMA_H_CHANGE 1
1084 #define SCORE_PALETTE_CHANGE 1
1086 #define SCORE_COLORSPACE_LOSS 2 /* RGB <-> YUV */
1087 #define SCORE_DEPTH_LOSS 4 /* change bit depth */
1088 #define SCORE_ALPHA_LOSS 8 /* lose the alpha channel */
1089 #define SCORE_CHROMA_W_LOSS 16 /* vertical subsample */
1090 #define SCORE_CHROMA_H_LOSS 32 /* horizontal subsample */
1091 #define SCORE_PALETTE_LOSS 64 /* convert to palette format */
1092 #define SCORE_COLOR_LOSS 128 /* convert to GRAY */
1094 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
1095 GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
1096 #define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
1097 #define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
1099 /* calculate how much loss a conversion would be */
1101 score_value (GstVaVpp * self, const GstVideoFormatInfo * in_info,
1102 GstVideoFormat format, gint * min_loss,
1103 const GstVideoFormatInfo ** out_info)
1105 const GstVideoFormatInfo *t_info;
1106 GstVideoFormatFlags in_flags, t_flags;
1109 t_info = gst_video_format_get_info (format);
1110 if (!t_info || t_info->format == GST_VIDEO_FORMAT_UNKNOWN)
1113 /* accept input format immediately without loss */
1114 if (in_info == t_info) {
1120 loss = SCORE_FORMAT_CHANGE;
1122 in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
1123 in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1124 in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1125 in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1127 t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
1128 t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1129 t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1130 t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1132 if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
1133 loss += SCORE_PALETTE_CHANGE;
1134 if (t_flags & PALETTE_MASK)
1135 loss += SCORE_PALETTE_LOSS;
1138 if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
1139 loss += SCORE_COLORSPACE_LOSS;
1140 if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
1141 loss += SCORE_COLOR_LOSS;
1144 if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
1145 loss += SCORE_ALPHA_CHANGE;
1146 if (in_flags & ALPHA_MASK)
1147 loss += SCORE_ALPHA_LOSS;
1150 if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
1151 loss += SCORE_CHROMA_H_CHANGE;
1152 if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
1153 loss += SCORE_CHROMA_H_LOSS;
1155 if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
1156 loss += SCORE_CHROMA_W_CHANGE;
1157 if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
1158 loss += SCORE_CHROMA_W_LOSS;
1161 if ((in_info->bits) != (t_info->bits)) {
1162 loss += SCORE_DEPTH_CHANGE;
1163 if ((in_info->bits) > (t_info->bits))
1164 loss += SCORE_DEPTH_LOSS;
1167 GST_DEBUG_OBJECT (self, "score %s -> %s = %d",
1168 GST_VIDEO_FORMAT_INFO_NAME (in_info),
1169 GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
1171 if (loss < *min_loss) {
1172 GST_DEBUG_OBJECT (self, "found new best %d", loss);
1182 gst_va_vpp_fixate_format (GstVaVpp * self, GstCaps * caps, GstCaps * result)
1184 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1186 const gchar *in_format;
1187 const GstVideoFormatInfo *in_info, *out_info = NULL;
1188 GstCapsFeatures *features;
1190 gint min_loss = G_MAXINT;
1191 guint i, best_i, capslen;
1193 ins = gst_caps_get_structure (caps, 0);
1194 in_format = gst_structure_get_string (ins, "format");
1198 GST_DEBUG_OBJECT (self, "source format %s", in_format);
1201 gst_video_format_get_info (gst_video_format_from_string (in_format));
1206 capslen = gst_caps_get_size (result);
1207 GST_DEBUG_OBJECT (self, "iterate %d structures", capslen);
1208 for (i = 0; i < capslen; i++) {
1209 GstStructure *tests;
1210 const GValue *format;
1212 tests = gst_caps_get_structure (result, i);
1213 format = gst_structure_get_value (tests, "format");
1214 /* should not happen */
1218 features = gst_caps_get_features (result, i);
1220 if (GST_VALUE_HOLDS_LIST (format)) {
1223 len = gst_value_list_get_size (format);
1224 GST_DEBUG_OBJECT (self, "have %d formats", len);
1225 for (j = 0; j < len; j++) {
1228 val = gst_value_list_get_value (format, j);
1229 if (G_VALUE_HOLDS_STRING (val)) {
1230 fmt = gst_video_format_from_string (g_value_get_string (val));
1231 if (!gst_va_filter_has_video_format (btrans->filter, fmt, features))
1233 if (score_value (self, in_info, fmt, &min_loss, &out_info))
1239 } else if (G_VALUE_HOLDS_STRING (format)) {
1240 fmt = gst_video_format_from_string (g_value_get_string (format));
1241 if (!gst_va_filter_has_video_format (btrans->filter, fmt, features))
1243 if (score_value (self, in_info, fmt, &min_loss, &out_info))
1255 features = gst_caps_features_copy (gst_caps_get_features (result, best_i));
1256 out = gst_structure_copy (gst_caps_get_structure (result, best_i));
1257 gst_structure_set (out, "format", G_TYPE_STRING,
1258 GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
1259 ret = gst_caps_new_full (out, NULL);
1260 gst_caps_set_features_simple (ret, features);
1268 gst_va_vpp_fixate_size (GstVaVpp * self, GstPadDirection direction,
1269 GstCaps * caps, GstCaps * othercaps)
1271 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1272 GstStructure *ins, *outs;
1273 const GValue *from_par, *to_par;
1274 GValue fpar = { 0, };
1275 GValue tpar = { 0, };
1277 ins = gst_caps_get_structure (caps, 0);
1278 outs = gst_caps_get_structure (othercaps, 0);
1280 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1281 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1283 /* If we're fixating from the sinkpad we always set the PAR and
1284 * assume that missing PAR on the sinkpad means 1/1 and
1285 * missing PAR on the srcpad means undefined
1287 if (direction == GST_PAD_SINK) {
1289 g_value_init (&fpar, GST_TYPE_FRACTION);
1290 gst_value_set_fraction (&fpar, 1, 1);
1294 g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
1295 gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
1300 g_value_init (&tpar, GST_TYPE_FRACTION);
1301 gst_value_set_fraction (&tpar, 1, 1);
1304 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1308 g_value_init (&fpar, GST_TYPE_FRACTION);
1309 gst_value_set_fraction (&fpar, 1, 1);
1314 /* we have both PAR but they might not be fixated */
1316 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1318 gint from_dar_n, from_dar_d;
1321 /* from_par should be fixed */
1322 g_return_if_fail (gst_value_is_fixed (from_par));
1324 from_par_n = gst_value_get_fraction_numerator (from_par);
1325 from_par_d = gst_value_get_fraction_denominator (from_par);
1327 gst_structure_get_int (ins, "width", &from_w);
1328 gst_structure_get_int (ins, "height", &from_h);
1330 gst_structure_get_int (outs, "width", &w);
1331 gst_structure_get_int (outs, "height", &h);
1333 /* if video-orientation changes */
1334 switch (gst_va_filter_get_orientation (btrans->filter)) {
1335 case GST_VIDEO_ORIENTATION_90R:
1336 case GST_VIDEO_ORIENTATION_90L:
1337 case GST_VIDEO_ORIENTATION_UL_LR:
1338 case GST_VIDEO_ORIENTATION_UR_LL:
1339 SWAP_INT (from_w, from_h);
1340 SWAP_INT (from_par_n, from_par_d);
1346 /* if both width and height are already fixed, we can't do anything
1347 * about it anymore */
1351 GST_DEBUG_OBJECT (self, "dimensions already set to %dx%d, not fixating",
1353 if (!gst_value_is_fixed (to_par)) {
1354 if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
1355 from_par_n, from_par_d, w, h)) {
1356 GST_DEBUG_OBJECT (self, "fixating to_par to %dx%d", n, d);
1357 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1358 gst_structure_fixate_field_nearest_fraction (outs,
1359 "pixel-aspect-ratio", n, d);
1361 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1368 /* Calculate input DAR */
1369 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1370 &from_dar_n, &from_dar_d)) {
1371 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1372 ("Error calculating the output scaled size - integer overflow"));
1376 GST_DEBUG_OBJECT (self, "Input DAR is %d/%d", from_dar_n, from_dar_d);
1378 /* If either width or height are fixed there's not much we
1379 * can do either except choosing a height or width and PAR
1380 * that matches the DAR as good as possible
1384 gint set_w, set_par_n, set_par_d;
1386 GST_DEBUG_OBJECT (self, "height is fixed (%d)", h);
1388 /* If the PAR is fixed too, there's not much to do
1389 * except choosing the width that is nearest to the
1390 * width with the same DAR */
1391 if (gst_value_is_fixed (to_par)) {
1392 to_par_n = gst_value_get_fraction_numerator (to_par);
1393 to_par_d = gst_value_get_fraction_denominator (to_par);
1395 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1397 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1398 to_par_n, &num, &den)) {
1399 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1400 ("Error calculating the output scaled size - integer overflow"));
1404 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1405 gst_structure_fixate_field_nearest_int (outs, "width", w);
1410 /* The PAR is not fixed and it's quite likely that we can set
1411 * an arbitrary PAR. */
1413 /* Check if we can keep the input width */
1414 tmp = gst_structure_copy (outs);
1415 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1416 gst_structure_get_int (tmp, "width", &set_w);
1418 /* Might have failed but try to keep the DAR nonetheless by
1419 * adjusting the PAR */
1420 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
1421 &to_par_n, &to_par_d)) {
1422 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1423 ("Error calculating the output scaled size - integer overflow"));
1424 gst_structure_free (tmp);
1428 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1429 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1430 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1431 to_par_n, to_par_d);
1432 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1434 gst_structure_free (tmp);
1436 /* Check if the adjusted PAR is accepted */
1437 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1438 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1439 set_par_n != set_par_d)
1440 gst_structure_set (outs, "width", G_TYPE_INT, set_w,
1441 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1446 /* Otherwise scale the width to the new PAR and check if the
1447 * adjusted with is accepted. If all that fails we can't keep
1449 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1450 set_par_n, &num, &den)) {
1451 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1452 ("Error calculating the output scaled size - integer overflow"));
1456 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1457 gst_structure_fixate_field_nearest_int (outs, "width", w);
1458 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1459 set_par_n != set_par_d)
1460 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1461 set_par_n, set_par_d, NULL);
1466 gint set_h, set_par_n, set_par_d;
1468 GST_DEBUG_OBJECT (self, "width is fixed (%d)", w);
1470 /* If the PAR is fixed too, there's not much to do
1471 * except choosing the height that is nearest to the
1472 * height with the same DAR */
1473 if (gst_value_is_fixed (to_par)) {
1474 to_par_n = gst_value_get_fraction_numerator (to_par);
1475 to_par_d = gst_value_get_fraction_denominator (to_par);
1477 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1479 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1480 to_par_n, &num, &den)) {
1481 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1482 ("Error calculating the output scaled size - integer overflow"));
1486 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1487 gst_structure_fixate_field_nearest_int (outs, "height", h);
1492 /* The PAR is not fixed and it's quite likely that we can set
1493 * an arbitrary PAR. */
1495 /* Check if we can keep the input height */
1496 tmp = gst_structure_copy (outs);
1497 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1498 gst_structure_get_int (tmp, "height", &set_h);
1500 /* Might have failed but try to keep the DAR nonetheless by
1501 * adjusting the PAR */
1502 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
1503 &to_par_n, &to_par_d)) {
1504 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1505 ("Error calculating the output scaled size - integer overflow"));
1506 gst_structure_free (tmp);
1509 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1510 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1511 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1512 to_par_n, to_par_d);
1513 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1515 gst_structure_free (tmp);
1517 /* Check if the adjusted PAR is accepted */
1518 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1519 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1520 set_par_n != set_par_d)
1521 gst_structure_set (outs, "height", G_TYPE_INT, set_h,
1522 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1527 /* Otherwise scale the height to the new PAR and check if the
1528 * adjusted with is accepted. If all that fails we can't keep
1530 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1531 set_par_n, &num, &den)) {
1532 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1533 ("Error calculating the output scale sized - integer overflow"));
1537 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1538 gst_structure_fixate_field_nearest_int (outs, "height", h);
1539 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1540 set_par_n != set_par_d)
1541 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1542 set_par_n, set_par_d, NULL);
1545 } else if (gst_value_is_fixed (to_par)) {
1547 gint set_h, set_w, f_h, f_w;
1549 to_par_n = gst_value_get_fraction_numerator (to_par);
1550 to_par_d = gst_value_get_fraction_denominator (to_par);
1552 /* Calculate scale factor for the PAR change */
1553 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1554 to_par_d, &num, &den)) {
1555 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1556 ("Error calculating the output scaled size - integer overflow"));
1560 /* Try to keep the input height (because of interlacing) */
1561 tmp = gst_structure_copy (outs);
1562 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1563 gst_structure_get_int (tmp, "height", &set_h);
1565 /* This might have failed but try to scale the width
1566 * to keep the DAR nonetheless */
1567 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1568 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1569 gst_structure_get_int (tmp, "width", &set_w);
1570 gst_structure_free (tmp);
1572 /* We kept the DAR and the height is nearest to the original height */
1574 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1575 G_TYPE_INT, set_h, NULL);
1582 /* If the former failed, try to keep the input width at least */
1583 tmp = gst_structure_copy (outs);
1584 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1585 gst_structure_get_int (tmp, "width", &set_w);
1587 /* This might have failed but try to scale the width
1588 * to keep the DAR nonetheless */
1589 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1590 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1591 gst_structure_get_int (tmp, "height", &set_h);
1592 gst_structure_free (tmp);
1594 /* We kept the DAR and the width is nearest to the original width */
1596 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1597 G_TYPE_INT, set_h, NULL);
1601 /* If all this failed, keep the dimensions with the DAR that was closest
1602 * to the correct DAR. This changes the DAR but there's not much else to
1605 if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
1609 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1614 gint set_h, set_w, set_par_n, set_par_d, tmp2;
1616 /* width, height and PAR are not fixed but passthrough is not possible */
1618 /* First try to keep the height and width as good as possible
1620 tmp = gst_structure_copy (outs);
1621 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1622 gst_structure_get_int (tmp, "height", &set_h);
1623 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1624 gst_structure_get_int (tmp, "width", &set_w);
1626 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1627 &to_par_n, &to_par_d)) {
1628 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1629 ("Error calculating the output scaled size - integer overflow"));
1630 gst_structure_free (tmp);
1634 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1635 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1636 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1637 to_par_n, to_par_d);
1638 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1640 gst_structure_free (tmp);
1642 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1643 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1644 G_TYPE_INT, set_h, NULL);
1646 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1647 set_par_n != set_par_d)
1648 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1649 set_par_n, set_par_d, NULL);
1653 /* Otherwise try to scale width to keep the DAR with the set
1655 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1656 set_par_n, &num, &den)) {
1657 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1658 ("Error calculating the output scaled size - integer overflow"));
1662 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1663 tmp = gst_structure_copy (outs);
1664 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1665 gst_structure_get_int (tmp, "width", &tmp2);
1666 gst_structure_free (tmp);
1669 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1670 G_TYPE_INT, set_h, NULL);
1671 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1672 set_par_n != set_par_d)
1673 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1674 set_par_n, set_par_d, NULL);
1678 /* ... or try the same with the height */
1679 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1680 tmp = gst_structure_copy (outs);
1681 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1682 gst_structure_get_int (tmp, "height", &tmp2);
1683 gst_structure_free (tmp);
1686 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1687 G_TYPE_INT, tmp2, NULL);
1688 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1689 set_par_n != set_par_d)
1690 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1691 set_par_n, set_par_d, NULL);
1695 /* If all fails we can't keep the DAR and take the nearest values
1696 * for everything from the first try */
1697 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1698 G_TYPE_INT, set_h, NULL);
1699 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1700 set_par_n != set_par_d)
1701 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1702 set_par_n, set_par_d, NULL);
1707 if (from_par == &fpar)
1708 g_value_unset (&fpar);
1709 if (to_par == &tpar)
1710 g_value_unset (&tpar);
1714 subsampling_unchanged (GstVideoInfo * in_info, GstVideoInfo * out_info)
1717 const GstVideoFormatInfo *in_format, *out_format;
1719 if (GST_VIDEO_INFO_N_COMPONENTS (in_info) !=
1720 GST_VIDEO_INFO_N_COMPONENTS (out_info))
1723 in_format = in_info->finfo;
1724 out_format = out_info->finfo;
1726 for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (in_info); i++) {
1727 if (GST_VIDEO_FORMAT_INFO_W_SUB (in_format,
1728 i) != GST_VIDEO_FORMAT_INFO_W_SUB (out_format, i))
1730 if (GST_VIDEO_FORMAT_INFO_H_SUB (in_format,
1731 i) != GST_VIDEO_FORMAT_INFO_H_SUB (out_format, i))
1739 transfer_colorimetry_from_input (GstVaVpp * self, GstCaps * in_caps,
1742 GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
1743 GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
1744 gboolean have_colorimetry =
1745 gst_structure_has_field (out_caps_s, "colorimetry");
1746 gboolean have_chroma_site =
1747 gst_structure_has_field (out_caps_s, "chroma-site");
1749 /* If the output already has colorimetry and chroma-site, stop,
1750 * otherwise try and transfer what we can from the input caps */
1751 if (have_colorimetry && have_chroma_site)
1755 GstVideoInfo in_info, out_info;
1756 const GValue *in_colorimetry =
1757 gst_structure_get_value (in_caps_s, "colorimetry");
1759 if (!gst_video_info_from_caps (&in_info, in_caps)) {
1760 GST_WARNING_OBJECT (self,
1761 "Failed to convert sink pad caps to video info");
1764 if (!gst_video_info_from_caps (&out_info, out_caps)) {
1765 GST_WARNING_OBJECT (self, "Failed to convert src pad caps to video info");
1769 if (!have_colorimetry && in_colorimetry != NULL) {
1770 if ((GST_VIDEO_INFO_IS_YUV (&out_info)
1771 && GST_VIDEO_INFO_IS_YUV (&in_info))
1772 || (GST_VIDEO_INFO_IS_RGB (&out_info)
1773 && GST_VIDEO_INFO_IS_RGB (&in_info))
1774 || (GST_VIDEO_INFO_IS_GRAY (&out_info)
1775 && GST_VIDEO_INFO_IS_GRAY (&in_info))) {
1776 /* Can transfer the colorimetry intact from the input if it has it */
1777 gst_structure_set_value (out_caps_s, "colorimetry", in_colorimetry);
1779 gchar *colorimetry_str;
1781 /* Changing between YUV/RGB - forward primaries and transfer function, but use
1782 * default range and matrix.
1783 * the primaries is used for conversion between RGB and XYZ (CIE 1931 coordinate).
1784 * the transfer function could be another reference (e.g., HDR)
1786 out_info.colorimetry.primaries = in_info.colorimetry.primaries;
1787 out_info.colorimetry.transfer = in_info.colorimetry.transfer;
1790 gst_video_colorimetry_to_string (&out_info.colorimetry);
1791 gst_caps_set_simple (out_caps, "colorimetry", G_TYPE_STRING,
1792 colorimetry_str, NULL);
1793 g_free (colorimetry_str);
1797 /* Only YUV output needs chroma-site. If the input was also YUV and had the same chroma
1798 * subsampling, transfer the siting. If the sub-sampling is changing, then the planes get
1799 * scaled anyway so there's no real reason to prefer the input siting. */
1800 if (!have_chroma_site && GST_VIDEO_INFO_IS_YUV (&out_info)) {
1801 if (GST_VIDEO_INFO_IS_YUV (&in_info)) {
1802 const GValue *in_chroma_site =
1803 gst_structure_get_value (in_caps_s, "chroma-site");
1804 if (in_chroma_site != NULL
1805 && subsampling_unchanged (&in_info, &out_info))
1806 gst_structure_set_value (out_caps_s, "chroma-site", in_chroma_site);
1813 copy_misc_fields_from_input (GstCaps * in_caps, GstCaps * out_caps)
1815 const gchar *fields[] = { "interlace-mode", "field-order", "multiview-mode",
1816 "multiview-flags", "framerate"
1818 GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
1819 GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
1822 for (i = 0; i < G_N_ELEMENTS (fields); i++) {
1823 const GValue *in_field = gst_structure_get_value (in_caps_s, fields[i]);
1824 const GValue *out_field = gst_structure_get_value (out_caps_s, fields[i]);
1826 if (out_field && gst_value_is_fixed (out_field))
1830 gst_structure_set_value (out_caps_s, fields[i], in_field);
1835 update_hdr_fields (GstVaVpp * self, GstCaps * result)
1837 GstStructure *s = gst_caps_get_structure (result, 0);
1838 GstVideoInfo out_info;
1839 gboolean have_colorimetry;
1841 gst_structure_remove_fields (s, "mastering-display-info",
1842 "content-light-level", "hdr-format", NULL);
1844 have_colorimetry = gst_structure_has_field (s, "colorimetry");
1845 if (!have_colorimetry) {
1846 if (gst_video_info_from_caps (&out_info, result)) {
1847 gchar *colorimetry_str =
1848 gst_video_colorimetry_to_string (&out_info.colorimetry);
1849 gst_caps_set_simple (result, "colorimetry", G_TYPE_STRING,
1850 colorimetry_str, NULL);
1851 g_free (colorimetry_str);
1853 GST_WARNING_OBJECT (self, "Failed to convert src pad caps to video info");
1859 gst_va_vpp_fixate_caps (GstBaseTransform * trans, GstPadDirection direction,
1860 GstCaps * caps, GstCaps * othercaps)
1862 GstVaVpp *self = GST_VA_VPP (trans);
1865 GST_DEBUG_OBJECT (self,
1866 "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
1867 GST_PTR_FORMAT, othercaps, caps);
1869 /* will iterate in all structures to find one with "best color" */
1870 result = gst_va_vpp_fixate_format (self, caps, othercaps);
1874 gst_clear_caps (&othercaps);
1876 gst_va_vpp_fixate_size (self, direction, caps, result);
1878 /* some fields might be lost while feature caps conversion */
1879 copy_misc_fields_from_input (caps, result);
1881 /* fixate remaining fields */
1882 result = gst_caps_fixate (result);
1884 if (direction == GST_PAD_SINK) {
1885 if (self->hdr_mapping)
1886 update_hdr_fields (self, result);
1888 /* Try and preserve input colorimetry / chroma information */
1889 transfer_colorimetry_from_input (self, caps, result);
1891 if (gst_caps_is_subset (caps, result))
1892 gst_caps_replace (&result, caps);
1895 GST_DEBUG_OBJECT (self, "fixated othercaps to %" GST_PTR_FORMAT, result);
1901 _get_scale_factor (GstVaVpp * self, gdouble * w_factor, gdouble * h_factor)
1903 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1904 gdouble w = GST_VIDEO_INFO_WIDTH (&btrans->out_info);
1905 gdouble h = GST_VIDEO_INFO_HEIGHT (&btrans->out_info);
1907 switch (gst_va_filter_get_orientation (btrans->filter)) {
1908 case GST_VIDEO_ORIENTATION_90R:
1909 case GST_VIDEO_ORIENTATION_90L:
1910 case GST_VIDEO_ORIENTATION_UR_LL:
1911 case GST_VIDEO_ORIENTATION_UL_LR:{
1921 *w_factor = GST_VIDEO_INFO_WIDTH (&btrans->in_info);
1924 *h_factor = GST_VIDEO_INFO_HEIGHT (&btrans->in_info);
1929 gst_va_vpp_src_event (GstBaseTransform * trans, GstEvent * event)
1931 GstVaVpp *self = GST_VA_VPP (trans);
1932 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
1933 const GstVideoInfo *in_info = &btrans->in_info, *out_info = &btrans->out_info;
1934 gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
1937 GST_TRACE_OBJECT (self, "handling %s event", GST_EVENT_TYPE_NAME (event));
1939 switch (GST_EVENT_TYPE (event)) {
1940 case GST_EVENT_NAVIGATION:
1941 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
1942 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
1943 || gst_va_filter_get_orientation (btrans->filter) !=
1944 GST_VIDEO_ORIENTATION_IDENTITY) {
1946 if (!gst_navigation_event_get_coordinates (event, &x, &y))
1949 event = gst_event_make_writable (event);
1951 /* video-direction compensation */
1952 switch (gst_va_filter_get_orientation (btrans->filter)) {
1953 case GST_VIDEO_ORIENTATION_90R:
1955 new_y = GST_VIDEO_INFO_WIDTH (out_info) - 1 - x;
1957 case GST_VIDEO_ORIENTATION_90L:
1958 new_x = GST_VIDEO_INFO_HEIGHT (out_info) - 1 - y;
1961 case GST_VIDEO_ORIENTATION_UL_LR:
1965 case GST_VIDEO_ORIENTATION_UR_LL:
1966 new_x = GST_VIDEO_INFO_HEIGHT (out_info) - 1 - y;
1967 new_y = GST_VIDEO_INFO_WIDTH (out_info) - 1 - x;
1969 case GST_VIDEO_ORIENTATION_180:
1970 /* FIXME: is this correct? */
1971 new_x = GST_VIDEO_INFO_WIDTH (out_info) - 1 - x;
1972 new_y = GST_VIDEO_INFO_HEIGHT (out_info) - 1 - y;
1974 case GST_VIDEO_ORIENTATION_HORIZ:
1975 new_x = GST_VIDEO_INFO_WIDTH (out_info) - 1 - x;
1978 case GST_VIDEO_ORIENTATION_VERT:
1980 new_y = GST_VIDEO_INFO_HEIGHT (out_info) - 1 - y;
1988 /* scale compensation */
1989 _get_scale_factor (self, &w_factor, &h_factor);
1993 /* crop compensation is done by videocrop */
1995 GST_TRACE_OBJECT (self, "from %fx%f to %fx%f", x, y, new_x, new_y);
1996 gst_navigation_event_set_coordinates (event, new_x, new_y);
2003 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
2009 gst_va_vpp_sink_event (GstBaseTransform * trans, GstEvent * event)
2011 GstVaVpp *self = GST_VA_VPP (trans);
2012 GstTagList *taglist;
2013 GstVideoOrientationMethod method;
2015 switch (GST_EVENT_TYPE (event)) {
2017 gst_event_parse_tag (event, &taglist);
2019 if (self->direction != GST_VIDEO_ORIENTATION_AUTO)
2022 if (!gst_video_orientation_from_tag (taglist, &method))
2025 GST_OBJECT_LOCK (self);
2026 self->tag_direction = method;
2027 _update_properties_unlocked (self);
2028 GST_OBJECT_UNLOCK (self);
2030 gst_va_vpp_update_passthrough (self, FALSE);
2037 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2041 _install_static_properties (GObjectClass * klass)
2044 * GstVaPostProc:disable-passthrough:
2046 * If set to %TRUE the filter will not enable passthrough mode, thus
2047 * each frame will be processed. It's useful for cropping, for
2052 PROPERTIES (PROP_DISABLE_PASSTHROUGH) =
2053 g_param_spec_boolean ("disable-passthrough", "Disable Passthrough",
2054 "Forces passing buffers through the postprocessor", FALSE,
2055 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY);
2056 g_object_class_install_property (klass, PROP_DISABLE_PASSTHROUGH,
2057 PROPERTIES (PROP_DISABLE_PASSTHROUGH));
2060 * GstVaPostProc:add-borders:
2062 * If set to %TRUE the filter will add black borders if necessary to
2063 * keep the display aspect ratio.
2067 PROPERTIES (PROP_ADD_BORDERS) = g_param_spec_boolean ("add-borders",
2069 "Add black borders if necessary to keep the display aspect ratio", FALSE,
2070 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING);
2071 g_object_class_install_property (klass, PROP_ADD_BORDERS,
2072 PROPERTIES (PROP_ADD_BORDERS));
2075 * GstVaPostProc:scale-method
2077 * Sets the scale method algorithm to use when resizing.
2081 PROPERTIES (PROP_SCALE_METHOD) = g_param_spec_enum ("scale-method",
2082 "Scale Method", "Scale method to use", GST_TYPE_VA_SCALE_METHOD,
2083 VA_FILTER_SCALING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
2084 | GST_PARAM_MUTABLE_PLAYING);
2085 g_object_class_install_property (klass, PROP_SCALE_METHOD,
2086 PROPERTIES (PROP_SCALE_METHOD));
2090 gst_va_vpp_class_init (gpointer g_class, gpointer class_data)
2092 GstCaps *doc_caps, *caps = NULL;
2093 GstPadTemplate *sink_pad_templ, *src_pad_templ;
2094 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
2095 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
2096 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2097 GstVaBaseTransformClass *btrans_class = GST_VA_BASE_TRANSFORM_CLASS (g_class);
2098 GstVaDisplay *display;
2099 GstVaFilter *filter;
2100 struct CData *cdata = class_data;
2104 parent_class = g_type_class_peek_parent (g_class);
2106 btrans_class->render_device_path = g_strdup (cdata->render_device_path);
2108 if (cdata->description) {
2109 long_name = g_strdup_printf ("VA-API Video Postprocessor in %s",
2110 cdata->description);
2112 long_name = g_strdup ("VA-API Video Postprocessor");
2115 klass = g_string_new ("Converter/Filter/Colorspace/Scaler/Video/Hardware");
2117 display = gst_va_display_drm_new_from_path (btrans_class->render_device_path);
2118 filter = gst_va_filter_new (display);
2120 if (gst_va_filter_open (filter)) {
2121 caps = gst_va_filter_get_caps (filter);
2123 /* adds any to enable passthrough */
2125 GstCaps *any_caps = gst_caps_new_empty_simple ("video/x-raw");
2126 gst_caps_set_features_simple (any_caps, gst_caps_features_new_any ());
2127 caps = gst_caps_merge (caps, any_caps);
2130 /* add converter klass */
2133 VAProcFilterType types[] = { VAProcFilterColorBalance,
2134 VAProcFilterSkinToneEnhancement, VAProcFilterSharpening,
2135 VAProcFilterNoiseReduction
2138 for (i = 0; i < G_N_ELEMENTS (types); i++) {
2139 if (gst_va_filter_has_filter (filter, types[i])) {
2140 g_string_prepend (klass, "Effect/");
2146 caps = gst_caps_from_string (caps_str);
2149 gst_element_class_set_metadata (element_class, long_name, klass->str,
2150 "VA-API based video postprocessor",
2151 "Víctor Jáquez <vjaquez@igalia.com>");
2153 g_string_free (klass, TRUE);
2155 doc_caps = gst_caps_from_string (caps_str);
2157 sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
2159 gst_element_class_add_pad_template (element_class, sink_pad_templ);
2160 gst_pad_template_set_documentation_caps (sink_pad_templ,
2161 gst_caps_ref (doc_caps));
2163 src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
2165 gst_element_class_add_pad_template (element_class, src_pad_templ);
2166 gst_pad_template_set_documentation_caps (src_pad_templ,
2167 gst_caps_ref (doc_caps));
2168 gst_caps_unref (doc_caps);
2170 gst_caps_unref (caps);
2172 object_class->dispose = gst_va_vpp_dispose;
2173 object_class->set_property = gst_va_vpp_set_property;
2174 object_class->get_property = gst_va_vpp_get_property;
2176 trans_class->propose_allocation =
2177 GST_DEBUG_FUNCPTR (gst_va_vpp_propose_allocation);
2178 trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_caps);
2179 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_fixate_caps);
2180 trans_class->before_transform =
2181 GST_DEBUG_FUNCPTR (gst_va_vpp_before_transform);
2182 trans_class->transform = GST_DEBUG_FUNCPTR (gst_va_vpp_transform);
2183 trans_class->transform_meta = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_meta);
2184 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_va_vpp_src_event);
2185 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_va_vpp_sink_event);
2187 trans_class->transform_ip_on_passthrough = FALSE;
2189 btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_va_vpp_set_info);
2190 btrans_class->update_properties =
2191 GST_DEBUG_FUNCPTR (gst_va_vpp_update_properties);
2193 gst_va_filter_install_properties (filter, object_class);
2195 _install_static_properties (object_class);
2198 g_free (cdata->description);
2199 g_free (cdata->render_device_path);
2201 gst_object_unref (filter);
2202 gst_object_unref (display);
2206 _create_colorbalance_channel (GstVaVpp * self, const gchar * label)
2208 GstColorBalanceChannel *channel;
2210 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2211 channel->label = g_strdup_printf ("VA-%s", label);
2212 channel->min_value = -1000;
2213 channel->max_value = 1000;
2215 self->channels = g_list_append (self->channels, channel);
2219 gst_va_vpp_init (GTypeInstance * instance, gpointer g_class)
2221 GstVaVpp *self = GST_VA_VPP (instance);
2224 self->direction = GST_VIDEO_ORIENTATION_IDENTITY;
2225 self->prev_direction = self->direction;
2226 self->tag_direction = GST_VIDEO_ORIENTATION_AUTO;
2228 pspec = g_object_class_find_property (g_class, "denoise");
2230 self->denoise = g_value_get_float (g_param_spec_get_default_value (pspec));
2232 pspec = g_object_class_find_property (g_class, "sharpen");
2234 self->sharpen = g_value_get_float (g_param_spec_get_default_value (pspec));
2236 pspec = g_object_class_find_property (g_class, "skin-tone");
2238 const GValue *value = g_param_spec_get_default_value (pspec);
2239 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
2240 self->skintone = g_value_get_boolean (value);
2242 self->skintone = g_value_get_float (value);
2246 pspec = g_object_class_find_property (g_class, "brightness");
2249 g_value_get_float (g_param_spec_get_default_value (pspec));
2250 _create_colorbalance_channel (self, "BRIGHTNESS");
2252 pspec = g_object_class_find_property (g_class, "contrast");
2254 self->contrast = g_value_get_float (g_param_spec_get_default_value (pspec));
2255 _create_colorbalance_channel (self, "CONTRAST");
2257 pspec = g_object_class_find_property (g_class, "hue");
2259 self->hue = g_value_get_float (g_param_spec_get_default_value (pspec));
2260 _create_colorbalance_channel (self, "HUE");
2262 pspec = g_object_class_find_property (g_class, "saturation");
2265 g_value_get_float (g_param_spec_get_default_value (pspec));
2266 _create_colorbalance_channel (self, "SATURATION");
2269 /* HDR tone mapping */
2270 pspec = g_object_class_find_property (g_class, "hdr-tone-mapping");
2273 g_value_get_boolean (g_param_spec_get_default_value (pspec));
2277 gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (instance), TRUE);
2281 _register_debug_category (gpointer data)
2283 GST_DEBUG_CATEGORY_INIT (gst_va_vpp_debug, "vapostproc", 0,
2284 "VA Video Postprocessor");
2287 G_PASTE (META_TAG_, type) = \
2288 g_quark_from_static_string (G_PASTE (G_PASTE (GST_META_TAG_VIDEO_, type), _STR))
2293 META_TAG_VIDEO = g_quark_from_static_string (GST_META_TAG_VIDEO_STR);
2299 gst_va_vpp_register (GstPlugin * plugin, GstVaDevice * device,
2300 gboolean has_colorbalance, guint rank)
2302 static GOnce debug_once = G_ONCE_INIT;
2304 GTypeInfo type_info = {
2305 .class_size = sizeof (GstVaVppClass),
2306 .class_init = gst_va_vpp_class_init,
2307 .instance_size = sizeof (GstVaVpp),
2308 .instance_init = gst_va_vpp_init,
2310 struct CData *cdata;
2312 gchar *type_name, *feature_name;
2314 g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
2315 g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
2317 cdata = g_new (struct CData, 1);
2318 cdata->description = NULL;
2319 cdata->render_device_path = g_strdup (device->render_device_path);
2321 type_info.class_data = cdata;
2323 type_name = g_strdup ("GstVaPostProc");
2324 feature_name = g_strdup ("vapostproc");
2326 /* The first postprocessor to be registered should use a constant
2327 * name, like vapostproc, for any additional postprocessors, we
2328 * create unique names, using inserting the render device name. */
2329 if (g_type_from_name (type_name)) {
2330 gchar *basename = g_path_get_basename (device->render_device_path);
2332 g_free (feature_name);
2333 type_name = g_strdup_printf ("GstVa%sPostProc", basename);
2334 feature_name = g_strdup_printf ("va%spostproc", basename);
2335 cdata->description = basename;
2337 /* lower rank for non-first device */
2342 g_once (&debug_once, _register_debug_category, NULL);
2344 type = g_type_register_static (GST_TYPE_VA_BASE_TRANSFORM, type_name,
2347 if (has_colorbalance) {
2348 const GInterfaceInfo info = { gst_va_vpp_colorbalance_init, NULL, NULL };
2349 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &info);
2352 ret = gst_element_register (plugin, feature_name, rank, type);
2355 g_free (feature_name);
2360 /* Color Balance interface */
2361 static const GList *
2362 gst_va_vpp_colorbalance_list_channels (GstColorBalance * balance)
2364 GstVaVpp *self = GST_VA_VPP (balance);
2366 return self->channels;
2369 /* This assumes --as happens with intel drivers-- that max values are
2370 * bigger than the simmetrical values of min values */
2372 make_max_simmetrical (GParamSpecFloat * fpspec)
2376 if (fpspec->default_value == 0)
2377 max = -fpspec->minimum;
2379 max = fpspec->default_value + ABS (fpspec->minimum - fpspec->default_value);
2381 return MIN (max, fpspec->maximum);
2385 _set_cb_val (GstVaVpp * self, const gchar * name,
2386 GstColorBalanceChannel * channel, gint value, gfloat * cb)
2388 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2390 GParamSpecFloat *fpspec;
2391 gfloat new_value, max;
2394 pspec = g_object_class_find_property (klass, name);
2398 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2399 max = make_max_simmetrical (fpspec);
2401 new_value = (value - channel->min_value) * (max - fpspec->minimum)
2402 / (channel->max_value - channel->min_value) + fpspec->minimum;
2404 GST_OBJECT_LOCK (self);
2405 changed = new_value != *cb;
2407 value = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2408 / (max - fpspec->minimum) + channel->min_value;
2409 GST_OBJECT_UNLOCK (self);
2412 GST_INFO_OBJECT (self, "%s: %d / %f", channel->label, value, new_value);
2413 gst_color_balance_value_changed (GST_COLOR_BALANCE (self), channel, value);
2414 g_atomic_int_set (&self->rebuild_filters, TRUE);
2421 gst_va_vpp_colorbalance_set_value (GstColorBalance * balance,
2422 GstColorBalanceChannel * channel, gint value)
2424 GstVaVpp *self = GST_VA_VPP (balance);
2426 if (g_str_has_suffix (channel->label, "HUE"))
2427 _set_cb_val (self, "hue", channel, value, &self->hue);
2428 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2429 _set_cb_val (self, "brightness", channel, value, &self->brightness);
2430 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2431 _set_cb_val (self, "contrast", channel, value, &self->contrast);
2432 else if (g_str_has_suffix (channel->label, "SATURATION"))
2433 _set_cb_val (self, "saturation", channel, value, &self->saturation);
2437 _get_cb_val (GstVaVpp * self, const gchar * name,
2438 GstColorBalanceChannel * channel, gfloat * cb, gint * val)
2440 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2442 GParamSpecFloat *fpspec;
2445 pspec = g_object_class_find_property (klass, name);
2449 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2450 max = make_max_simmetrical (fpspec);
2452 GST_OBJECT_LOCK (self);
2453 *val = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2454 / (max - fpspec->minimum) + channel->min_value;
2455 GST_OBJECT_UNLOCK (self);
2461 gst_va_vpp_colorbalance_get_value (GstColorBalance * balance,
2462 GstColorBalanceChannel * channel)
2464 GstVaVpp *self = GST_VA_VPP (balance);
2467 if (g_str_has_suffix (channel->label, "HUE"))
2468 _get_cb_val (self, "hue", channel, &self->hue, &value);
2469 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2470 _get_cb_val (self, "brightness", channel, &self->brightness, &value);
2471 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2472 _get_cb_val (self, "contrast", channel, &self->contrast, &value);
2473 else if (g_str_has_suffix (channel->label, "SATURATION"))
2474 _get_cb_val (self, "saturation", channel, &self->saturation, &value);
2479 static GstColorBalanceType
2480 gst_va_vpp_colorbalance_get_balance_type (GstColorBalance * balance)
2482 return GST_COLOR_BALANCE_HARDWARE;
2486 gst_va_vpp_colorbalance_init (gpointer iface, gpointer data)
2488 GstColorBalanceInterface *cbiface = iface;
2490 cbiface->list_channels = gst_va_vpp_colorbalance_list_channels;
2491 cbiface->set_value = gst_va_vpp_colorbalance_set_value;
2492 cbiface->get_value = gst_va_vpp_colorbalance_get_value;
2493 cbiface->get_balance_type = gst_va_vpp_colorbalance_get_balance_type;