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(a, b) do { const __typeof__ (a) t = a; a = b; b = t; } while (0)
83 typedef struct _GstVaVpp GstVaVpp;
84 typedef struct _GstVaVppClass GstVaVppClass;
88 /* GstVideoFilter overlaps functionality */
89 GstVaBaseTransformClass parent_class;
94 GstVaBaseTransform parent;
96 gboolean rebuild_filters;
107 gboolean auto_contrast;
108 gboolean auto_brightness;
109 gboolean auto_saturation;
110 GstVideoOrientationMethod direction;
111 GstVideoOrientationMethod prev_direction;
112 GstVideoOrientationMethod tag_direction;
113 gboolean add_borders;
116 guint32 scale_method;
118 gboolean hdr_mapping;
119 gboolean has_hdr_meta;
120 VAHdrMetaDataHDR10 hdr_meta;
125 static GstElementClass *parent_class = NULL;
129 gchar *render_device_path;
135 PROP_DISABLE_PASSTHROUGH = GST_VA_FILTER_PROP_LAST + 1,
141 static GParamSpec *properties[N_PROPERTIES - GST_VA_FILTER_PROP_LAST];
143 #define PROPERTIES(idx) properties[idx - GST_VA_FILTER_PROP_LAST]
145 /* convertions that disable passthrough */
148 VPP_CONVERT_SIZE = 1 << 0,
149 VPP_CONVERT_FORMAT = 1 << 1,
150 VPP_CONVERT_FILTERS = 1 << 2,
151 VPP_CONVERT_DIRECTION = 1 << 3,
152 VPP_CONVERT_FEATURE = 1 << 4,
153 VPP_CONVERT_CROP = 1 << 5,
154 VPP_CONVERT_DUMMY = 1 << 6,
158 static const gchar *caps_str =
159 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
160 "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
161 GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
162 "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR }");
165 #define META_TAG_COLORSPACE meta_tag_colorspace_quark
166 static GQuark meta_tag_colorspace_quark;
167 #define META_TAG_SIZE meta_tag_size_quark
168 static GQuark meta_tag_size_quark;
169 #define META_TAG_ORIENTATION meta_tag_orientation_quark
170 static GQuark meta_tag_orientation_quark;
171 #define META_TAG_VIDEO meta_tag_video_quark
172 static GQuark meta_tag_video_quark;
174 static void gst_va_vpp_colorbalance_init (gpointer iface, gpointer data);
175 static void gst_va_vpp_rebuild_filters (GstVaVpp * self);
178 gst_va_vpp_dispose (GObject * object)
180 GstVaVpp *self = GST_VA_VPP (object);
183 g_list_free_full (g_steal_pointer (&self->channels), g_object_unref);
185 G_OBJECT_CLASS (parent_class)->dispose (object);
189 gst_va_vpp_update_passthrough (GstVaVpp * self, gboolean reconf)
191 GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
194 old = gst_base_transform_is_passthrough (trans);
196 GST_OBJECT_LOCK (self);
197 new = (self->op_flags == 0);
198 GST_OBJECT_UNLOCK (self);
201 GST_INFO_OBJECT (self, "%s passthrough", new ? "enabling" : "disabling");
203 gst_base_transform_reconfigure_src (trans);
204 gst_base_transform_set_passthrough (trans, new);
209 _update_properties_unlocked (GstVaVpp * self)
211 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
216 if ((self->direction != GST_VIDEO_ORIENTATION_AUTO
217 && self->direction != self->prev_direction)
218 || (self->direction == GST_VIDEO_ORIENTATION_AUTO
219 && self->tag_direction != self->prev_direction)) {
221 GstVideoOrientationMethod direction =
222 (self->direction == GST_VIDEO_ORIENTATION_AUTO) ?
223 self->tag_direction : self->direction;
225 if (!gst_va_filter_set_orientation (btrans->filter, direction)) {
226 if (self->direction == GST_VIDEO_ORIENTATION_AUTO)
227 self->tag_direction = self->prev_direction;
229 self->direction = self->prev_direction;
231 self->op_flags &= ~VPP_CONVERT_DIRECTION;
233 /* FIXME: unlocked bus warning message */
234 GST_WARNING_OBJECT (self,
235 "Driver cannot set resquested orientation. Setting it back.");
237 self->prev_direction = direction;
239 self->op_flags |= VPP_CONVERT_DIRECTION;
241 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (self));
244 self->op_flags &= ~VPP_CONVERT_DIRECTION;
247 if (!gst_va_filter_set_scale_method (btrans->filter, self->scale_method))
248 GST_WARNING_OBJECT (self, "could not set the filter scale method.");
252 gst_va_vpp_set_property (GObject * object, guint prop_id,
253 const GValue * value, GParamSpec * pspec)
255 GstVaVpp *self = GST_VA_VPP (object);
257 GST_OBJECT_LOCK (object);
259 case GST_VA_FILTER_PROP_DENOISE:
260 self->denoise = g_value_get_float (value);
261 g_atomic_int_set (&self->rebuild_filters, TRUE);
263 case GST_VA_FILTER_PROP_SHARPEN:
264 self->sharpen = g_value_get_float (value);
265 g_atomic_int_set (&self->rebuild_filters, TRUE);
267 case GST_VA_FILTER_PROP_SKINTONE:
268 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
269 self->skintone = (float) g_value_get_boolean (value);
271 self->skintone = g_value_get_float (value);
272 g_atomic_int_set (&self->rebuild_filters, TRUE);
274 case GST_VA_FILTER_PROP_VIDEO_DIR:{
275 GstVideoOrientationMethod direction = g_value_get_enum (value);
276 self->prev_direction = (direction == GST_VIDEO_ORIENTATION_AUTO) ?
277 self->tag_direction : self->direction;
278 self->direction = direction;
281 case GST_VA_FILTER_PROP_HUE:
282 self->hue = g_value_get_float (value);
283 g_atomic_int_set (&self->rebuild_filters, TRUE);
285 case GST_VA_FILTER_PROP_SATURATION:
286 self->saturation = g_value_get_float (value);
287 g_atomic_int_set (&self->rebuild_filters, TRUE);
289 case GST_VA_FILTER_PROP_BRIGHTNESS:
290 self->brightness = g_value_get_float (value);
291 g_atomic_int_set (&self->rebuild_filters, TRUE);
293 case GST_VA_FILTER_PROP_CONTRAST:
294 self->contrast = g_value_get_float (value);
295 g_atomic_int_set (&self->rebuild_filters, TRUE);
297 case GST_VA_FILTER_PROP_AUTO_SATURATION:
298 self->auto_saturation = g_value_get_boolean (value);
299 g_atomic_int_set (&self->rebuild_filters, TRUE);
301 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
302 self->auto_brightness = g_value_get_boolean (value);
303 g_atomic_int_set (&self->rebuild_filters, TRUE);
305 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
306 self->auto_contrast = g_value_get_boolean (value);
307 g_atomic_int_set (&self->rebuild_filters, TRUE);
309 case GST_VA_FILTER_PROP_HDR:
310 self->hdr_mapping = g_value_get_boolean (value);
311 g_atomic_int_set (&self->rebuild_filters, TRUE);
313 case PROP_DISABLE_PASSTHROUGH:{
314 gboolean disable_passthrough = g_value_get_boolean (value);
315 if (disable_passthrough)
316 self->op_flags |= VPP_CONVERT_DUMMY;
318 self->op_flags &= ~VPP_CONVERT_DUMMY;
321 case PROP_ADD_BORDERS:
322 self->add_borders = g_value_get_boolean (value);
324 case PROP_SCALE_METHOD:
325 self->scale_method = g_value_get_enum (value);
328 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332 _update_properties_unlocked (self);
333 GST_OBJECT_UNLOCK (object);
335 gst_va_vpp_update_passthrough (self, FALSE);
339 gst_va_vpp_get_property (GObject * object, guint prop_id, GValue * value,
342 GstVaVpp *self = GST_VA_VPP (object);
344 GST_OBJECT_LOCK (object);
346 case GST_VA_FILTER_PROP_DENOISE:
347 g_value_set_float (value, self->denoise);
349 case GST_VA_FILTER_PROP_SHARPEN:
350 g_value_set_float (value, self->sharpen);
352 case GST_VA_FILTER_PROP_SKINTONE:
353 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
354 g_value_set_boolean (value, self->skintone > 0);
356 g_value_set_float (value, self->skintone);
358 case GST_VA_FILTER_PROP_VIDEO_DIR:
359 g_value_set_enum (value, self->direction);
361 case GST_VA_FILTER_PROP_HUE:
362 g_value_set_float (value, self->hue);
364 case GST_VA_FILTER_PROP_SATURATION:
365 g_value_set_float (value, self->saturation);
367 case GST_VA_FILTER_PROP_BRIGHTNESS:
368 g_value_set_float (value, self->brightness);
370 case GST_VA_FILTER_PROP_CONTRAST:
371 g_value_set_float (value, self->contrast);
373 case GST_VA_FILTER_PROP_AUTO_SATURATION:
374 g_value_set_boolean (value, self->auto_saturation);
376 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
377 g_value_set_boolean (value, self->auto_brightness);
379 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
380 g_value_set_boolean (value, self->auto_contrast);
382 case GST_VA_FILTER_PROP_HDR:
383 g_value_set_boolean (value, self->hdr_mapping);
385 case PROP_DISABLE_PASSTHROUGH:
386 g_value_set_boolean (value, (self->op_flags & VPP_CONVERT_DUMMY));
388 case PROP_ADD_BORDERS:
389 g_value_set_boolean (value, self->add_borders);
391 case PROP_SCALE_METHOD:
392 g_value_set_enum (value, self->scale_method);
395 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
398 GST_OBJECT_UNLOCK (object);
402 gst_va_vpp_propose_allocation (GstBaseTransform * trans,
403 GstQuery * decide_query, GstQuery * query)
405 /* if we are not passthrough, we can handle crop meta */
407 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
409 return GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
410 decide_query, query);
414 gst_va_vpp_update_properties (GstVaBaseTransform * btrans)
416 GstVaVpp *self = GST_VA_VPP (btrans);
418 gst_va_vpp_rebuild_filters (self);
419 _update_properties_unlocked (self);
423 _set_hdr_metadata (GstVaVpp * self, GstCaps * caps)
425 GstVideoMasteringDisplayInfo mdinfo;
426 GstVideoContentLightLevel llevel;
428 self->has_hdr_meta = FALSE;
430 if (gst_video_mastering_display_info_from_caps (&mdinfo, caps)) {
431 self->hdr_meta.display_primaries_x[0] = mdinfo.display_primaries[1].x;
432 self->hdr_meta.display_primaries_x[1] = mdinfo.display_primaries[2].x;
433 self->hdr_meta.display_primaries_x[2] = mdinfo.display_primaries[0].x;
435 self->hdr_meta.display_primaries_y[0] = mdinfo.display_primaries[1].y;
436 self->hdr_meta.display_primaries_y[1] = mdinfo.display_primaries[2].y;
437 self->hdr_meta.display_primaries_y[2] = mdinfo.display_primaries[0].y;
439 self->hdr_meta.white_point_x = mdinfo.white_point.x;
440 self->hdr_meta.white_point_y = mdinfo.white_point.y;
442 self->hdr_meta.max_display_mastering_luminance =
443 mdinfo.max_display_mastering_luminance;
444 self->hdr_meta.min_display_mastering_luminance =
445 mdinfo.min_display_mastering_luminance;
447 self->has_hdr_meta = TRUE;
451 if (gst_video_content_light_level_from_caps (&llevel, caps)) {
452 self->hdr_meta.max_content_light_level = llevel.max_content_light_level;
453 self->hdr_meta.max_pic_average_light_level =
454 llevel.max_frame_average_light_level;
456 self->has_hdr_meta = TRUE;
459 /* rebuild filters only if hdr mapping is enabled */
460 g_atomic_int_set (&self->rebuild_filters, self->hdr_mapping);
464 gst_va_vpp_set_info (GstVaBaseTransform * btrans, GstCaps * incaps,
465 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
467 GstVaVpp *self = GST_VA_VPP (btrans);
468 GstCapsFeatures *infeat, *outfeat;
469 gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
471 if (GST_VIDEO_INFO_INTERLACE_MODE (in_info) !=
472 GST_VIDEO_INFO_INTERLACE_MODE (out_info)) {
473 GST_ERROR_OBJECT (self, "input and output formats do not match");
477 /* calculate possible borders if display-aspect-ratio change */
479 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (in_info),
480 GST_VIDEO_INFO_HEIGHT (in_info), GST_VIDEO_INFO_PAR_N (in_info),
481 GST_VIDEO_INFO_PAR_D (in_info), &from_dar_n, &from_dar_d)) {
482 from_dar_n = from_dar_d = -1;
485 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (out_info),
486 GST_VIDEO_INFO_HEIGHT (out_info), GST_VIDEO_INFO_PAR_N (out_info),
487 GST_VIDEO_INFO_PAR_D (out_info), &to_dar_n, &to_dar_d)) {
488 to_dar_n = to_dar_d = -1;
491 /* if video-orientation changes consider it for borders */
492 switch (gst_va_filter_get_orientation (btrans->filter)) {
493 case GST_VIDEO_ORIENTATION_90R:
494 case GST_VIDEO_ORIENTATION_90L:
495 case GST_VIDEO_ORIENTATION_UL_LR:
496 case GST_VIDEO_ORIENTATION_UR_LL:
497 SWAP (from_dar_n, from_dar_d);
503 self->borders_h = self->borders_w = 0;
504 if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
505 if (self->add_borders) {
506 gint n, d, to_h, to_w;
508 if (from_dar_n != -1 && from_dar_d != -1
509 && gst_util_fraction_multiply (from_dar_n, from_dar_d,
510 out_info->par_d, out_info->par_n, &n, &d)) {
511 to_h = gst_util_uint64_scale_int (out_info->width, d, n);
512 if (to_h <= out_info->height) {
513 self->borders_h = out_info->height - to_h;
516 to_w = gst_util_uint64_scale_int (out_info->height, n, d);
517 g_assert (to_w <= out_info->width);
519 self->borders_w = out_info->width - to_w;
522 GST_WARNING_OBJECT (self, "Can't calculate borders");
525 GST_WARNING_OBJECT (self, "Can't keep DAR!");
530 if (gst_video_info_is_equal (in_info, out_info)) {
531 self->op_flags &= ~VPP_CONVERT_FORMAT & ~VPP_CONVERT_SIZE;
533 if ((GST_VIDEO_INFO_FORMAT (in_info) != GST_VIDEO_INFO_FORMAT (out_info))
534 || !gst_video_colorimetry_is_equivalent (&GST_VIDEO_INFO_COLORIMETRY
535 (in_info), GST_VIDEO_INFO_COMP_DEPTH (in_info, 0),
536 &GST_VIDEO_INFO_COLORIMETRY (out_info),
537 GST_VIDEO_INFO_COMP_DEPTH (out_info, 0)))
538 self->op_flags |= VPP_CONVERT_FORMAT;
540 self->op_flags &= ~VPP_CONVERT_FORMAT;
542 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
543 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
544 || self->borders_h > 0 || self->borders_w > 0)
545 self->op_flags |= VPP_CONVERT_SIZE;
547 self->op_flags &= ~VPP_CONVERT_SIZE;
550 infeat = gst_caps_get_features (incaps, 0);
551 outfeat = gst_caps_get_features (outcaps, 0);
552 if (!gst_caps_features_is_equal (infeat, outfeat))
553 self->op_flags |= VPP_CONVERT_FEATURE;
555 self->op_flags &= ~VPP_CONVERT_FEATURE;
557 if (gst_va_filter_set_video_info (btrans->filter, in_info, out_info)) {
558 _set_hdr_metadata (self, incaps);
559 gst_va_vpp_update_passthrough (self, FALSE);
566 static inline gboolean
567 _get_filter_value (GstVaVpp * self, VAProcFilterType type, gfloat * value)
571 GST_OBJECT_LOCK (self);
573 case VAProcFilterNoiseReduction:
574 *value = self->denoise;
576 case VAProcFilterSharpening:
577 *value = self->sharpen;
579 case VAProcFilterSkinToneEnhancement:
580 *value = self->skintone;
586 GST_OBJECT_UNLOCK (self);
591 static inline gboolean
592 _add_filter_buffer (GstVaVpp * self, VAProcFilterType type,
593 const VAProcFilterCap * cap)
595 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
596 VAProcFilterParameterBuffer param;
599 if (!_get_filter_value (self, type, &value))
601 if (value == cap->range.default_value)
605 param = (VAProcFilterParameterBuffer) {
611 return gst_va_filter_add_filter_buffer (btrans->filter, ¶m,
615 static inline gboolean
616 _get_filter_cb_value (GstVaVpp * self, VAProcColorBalanceType type,
621 GST_OBJECT_LOCK (self);
623 case VAProcColorBalanceHue:
626 case VAProcColorBalanceSaturation:
627 *value = self->saturation;
629 case VAProcColorBalanceBrightness:
630 *value = self->brightness;
632 case VAProcColorBalanceContrast:
633 *value = self->contrast;
635 case VAProcColorBalanceAutoSaturation:
636 *value = self->auto_saturation;
638 case VAProcColorBalanceAutoBrightness:
639 *value = self->auto_brightness;
641 case VAProcColorBalanceAutoContrast:
642 *value = self->auto_contrast;
648 GST_OBJECT_UNLOCK (self);
653 static inline gboolean
654 _add_filter_cb_buffer (GstVaVpp * self,
655 const VAProcFilterCapColorBalance * caps, guint num_caps)
657 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
658 VAProcFilterParameterBufferColorBalance param[VAProcColorBalanceCount] =
664 for (i = 0; i < num_caps && i < VAProcColorBalanceCount; i++) {
665 if (!_get_filter_cb_value (self, caps[i].type, &value))
667 if (value == caps[i].range.default_value)
671 param[c++] = (VAProcFilterParameterBufferColorBalance) {
672 .type = VAProcFilterColorBalance,
673 .attrib = caps[i].type,
680 return gst_va_filter_add_filter_buffer (btrans->filter, param,
686 static inline gboolean
687 _add_filter_hdr_buffer (GstVaVpp * self,
688 const VAProcFilterCapHighDynamicRange * caps)
690 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
692 VAProcFilterParameterBufferHDRToneMapping params = {
693 .type = VAProcFilterHighDynamicRangeToneMapping,
695 .metadata_type = VAProcHighDynamicRangeMetadataHDR10,
696 .metadata = &self->hdr_meta,
697 .metadata_size = sizeof (self->hdr_meta),
702 /* if not has hdr meta, it may try later again */
703 if (!(self->has_hdr_meta && self->hdr_mapping))
706 if (!(caps && caps->metadata_type == VAProcHighDynamicRangeMetadataHDR10
707 && (caps->caps_flag & VA_TONE_MAPPING_HDR_TO_SDR)))
710 if (self->op_flags & VPP_CONVERT_FORMAT) {
711 GST_WARNING_OBJECT (self, "Cannot apply HDR with color conversion");
715 return gst_va_filter_add_filter_buffer (btrans->filter, ¶ms,
719 self->hdr_mapping = FALSE;
720 g_object_notify (G_OBJECT (self), "hdr-tone-mapping");
725 _build_filters (GstVaVpp * self)
727 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
728 static const VAProcFilterType filter_types[] = { VAProcFilterNoiseReduction,
729 VAProcFilterSharpening, VAProcFilterSkinToneEnhancement,
730 VAProcFilterColorBalance, VAProcFilterHighDynamicRangeToneMapping,
733 gboolean apply = FALSE;
735 for (i = 0; i < G_N_ELEMENTS (filter_types); i++) {
736 const gpointer caps = gst_va_filter_get_filter_caps (btrans->filter,
737 filter_types[i], &num_caps);
741 switch (filter_types[i]) {
742 case VAProcFilterNoiseReduction:
743 apply |= _add_filter_buffer (self, filter_types[i], caps);
745 case VAProcFilterSharpening:
746 apply |= _add_filter_buffer (self, filter_types[i], caps);
748 case VAProcFilterSkinToneEnhancement:
749 apply |= _add_filter_buffer (self, filter_types[i], caps);
751 case VAProcFilterColorBalance:
752 apply |= _add_filter_cb_buffer (self, caps, num_caps);
754 case VAProcFilterHighDynamicRangeToneMapping:
755 apply |= _add_filter_hdr_buffer (self, caps);
761 GST_OBJECT_LOCK (self);
763 self->op_flags |= VPP_CONVERT_FILTERS;
765 self->op_flags &= ~VPP_CONVERT_FILTERS;
766 GST_OBJECT_UNLOCK (self);
770 gst_va_vpp_rebuild_filters (GstVaVpp * self)
772 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
774 if (!g_atomic_int_get (&self->rebuild_filters))
777 gst_va_filter_drop_filter_buffers (btrans->filter);
778 _build_filters (self);
779 g_atomic_int_set (&self->rebuild_filters, FALSE);
783 gst_va_vpp_before_transform (GstBaseTransform * trans, GstBuffer * inbuf)
785 GstVaVpp *self = GST_VA_VPP (trans);
786 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
787 GstClockTime ts, stream_time;
788 gboolean is_passthrough;
790 ts = GST_BUFFER_TIMESTAMP (inbuf);
792 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, ts);
794 GST_TRACE_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
796 if (GST_CLOCK_TIME_IS_VALID (stream_time))
797 gst_object_sync_values (GST_OBJECT (self), stream_time);
799 gst_va_vpp_rebuild_filters (self);
800 gst_va_vpp_update_passthrough (self, TRUE);
802 /* cropping is only enabled if vapostproc is not in passthrough */
803 is_passthrough = gst_base_transform_is_passthrough (trans);
804 GST_OBJECT_LOCK (self);
805 if (!is_passthrough && gst_buffer_get_video_crop_meta (inbuf)) {
806 self->op_flags |= VPP_CONVERT_CROP;
808 self->op_flags &= ~VPP_CONVERT_CROP;
810 gst_va_filter_enable_cropping (btrans->filter,
811 (self->op_flags & VPP_CONVERT_CROP));
812 GST_OBJECT_UNLOCK (self);
816 gst_va_vpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
819 GstVaVpp *self = GST_VA_VPP (trans);
820 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
821 GstBuffer *buf = NULL;
822 GstFlowReturn res = GST_FLOW_OK;
823 GstVaSample src, dst;
825 if (G_UNLIKELY (!btrans->negotiated))
828 res = gst_va_base_transform_import_buffer (btrans, inbuf, &buf);
829 if (res != GST_FLOW_OK)
833 src = (GstVaSample) {
835 .flags = gst_va_buffer_get_surface_flags (buf, &btrans->in_info),
838 dst = (GstVaSample) {
840 .borders_h = self->borders_h,
841 .borders_w = self->borders_w,
842 .flags = gst_va_buffer_get_surface_flags (outbuf, &btrans->out_info),
846 if (!gst_va_filter_process (btrans->filter, &src, &dst)) {
847 gst_buffer_set_flags (outbuf, GST_BUFFER_FLAG_CORRUPTED);
850 gst_buffer_unref (buf);
857 GST_ELEMENT_ERROR (self, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
858 return GST_FLOW_NOT_NEGOTIATED;
863 gst_va_vpp_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
864 GstMeta * meta, GstBuffer * outbuf)
866 GstVaVpp *self = GST_VA_VPP (trans);
867 const GstMetaInfo *info = meta->info;
868 const gchar *const *tags;
870 tags = gst_meta_api_type_get_tags (info->api);
875 /* don't copy colorspace/size/orientation specific metadata */
876 if ((self->op_flags & VPP_CONVERT_FORMAT)
877 && gst_meta_api_type_has_tag (info->api, META_TAG_COLORSPACE))
879 else if ((self->op_flags & (VPP_CONVERT_SIZE | VPP_CONVERT_CROP))
880 && gst_meta_api_type_has_tag (info->api, META_TAG_SIZE))
882 else if ((self->op_flags & VPP_CONVERT_DIRECTION)
883 && gst_meta_api_type_has_tag (info->api, META_TAG_ORIENTATION))
885 else if (gst_meta_api_type_has_tag (info->api, META_TAG_VIDEO))
891 /* In structures with supported caps features it's:
892 * + Rangified resolution size.
893 * + Rangified "pixel-aspect-ratio" if present.
894 * + Removed "format", "colorimetry", "chroma-site"
896 * Structures with unsupported caps features are copied as-is.
899 gst_va_vpp_caps_remove_fields (GstCaps * caps)
902 GstStructure *structure;
903 GstCapsFeatures *features;
906 ret = gst_caps_new_empty ();
908 n = gst_caps_get_size (caps);
909 for (i = 0; i < n; i++) {
910 structure = gst_caps_get_structure (caps, i);
911 features = gst_caps_get_features (caps, i);
913 /* If this is already expressed by the existing caps
914 * skip this structure */
915 if (i > 0 && gst_caps_is_subset_structure_full (ret, structure, features))
918 structure = gst_structure_copy (structure);
920 m = gst_caps_features_get_size (features);
921 for (j = 0; j < m; j++) {
922 const gchar *feature = gst_caps_features_get_nth (features, j);
924 if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0
925 || g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_DMABUF) == 0
926 || g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_VA) == 0) {
928 /* rangify frame size */
929 gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
930 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
932 /* if pixel aspect ratio, make a range of it */
933 if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
934 gst_structure_set (structure, "pixel-aspect-ratio",
935 GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
938 /* remove format-related fields */
939 gst_structure_remove_fields (structure, "format", "colorimetry",
940 "chroma-site", NULL);
946 gst_caps_append_structure_full (ret, structure,
947 gst_caps_features_copy (features));
953 /* Returns all structures in @caps without @feature_name but now with
956 gst_va_vpp_complete_caps_features (const GstCaps * caps,
957 const gchar * feature_name)
962 tmp = gst_caps_new_empty ();
964 n = gst_caps_get_size (caps);
965 for (i = 0; i < n; i++) {
966 GstCapsFeatures *features, *orig_features;
967 GstStructure *s = gst_caps_get_structure (caps, i);
968 gboolean contained = FALSE;
970 orig_features = gst_caps_get_features (caps, i);
971 features = gst_caps_features_new (feature_name, NULL);
973 m = gst_caps_features_get_size (orig_features);
974 for (j = 0; j < m; j++) {
975 const gchar *feature = gst_caps_features_get_nth (orig_features, j);
977 /* if we already have the features */
978 if (gst_caps_features_contains (features, feature)) {
984 if (!contained && !gst_caps_is_subset_structure_full (tmp, s, features))
985 gst_caps_append_structure_full (tmp, gst_structure_copy (s), features);
987 gst_caps_features_free (features);
994 gst_va_vpp_transform_caps (GstBaseTransform * trans, GstPadDirection direction,
995 GstCaps * caps, GstCaps * filter)
997 GstVaVpp *self = GST_VA_VPP (trans);
998 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
999 GstCaps *ret, *tmp, *filter_caps;
1001 GST_DEBUG_OBJECT (self,
1002 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1003 (direction == GST_PAD_SINK) ? "sink" : "src");
1005 filter_caps = gst_va_base_transform_get_filter_caps (btrans);
1006 if (filter_caps && !gst_caps_can_intersect (caps, filter_caps)) {
1007 ret = gst_caps_ref (caps);
1011 ret = gst_va_vpp_caps_remove_fields (caps);
1013 tmp = gst_va_vpp_complete_caps_features (ret, GST_CAPS_FEATURE_MEMORY_VA);
1014 if (!gst_caps_is_subset (tmp, ret)) {
1015 gst_caps_append (ret, tmp);
1017 gst_caps_unref (tmp);
1020 tmp = gst_va_vpp_complete_caps_features (ret, GST_CAPS_FEATURE_MEMORY_DMABUF);
1021 if (!gst_caps_is_subset (tmp, ret)) {
1022 gst_caps_append (ret, tmp);
1024 gst_caps_unref (tmp);
1027 tmp = gst_va_vpp_complete_caps_features (ret,
1028 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
1029 if (!gst_caps_is_subset (tmp, ret)) {
1030 gst_caps_append (ret, tmp);
1032 gst_caps_unref (tmp);
1037 GstCaps *intersection;
1040 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1041 gst_caps_unref (ret);
1045 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
1051 * This is an incomplete matrix of in formats and a score for the preferred output
1054 * out: RGB24 RGB16 ARGB AYUV YUV444 YUV422 YUV420 YUV411 YUV410 PAL GRAY
1056 * RGB24 0 2 1 2 2 3 4 5 6 7 8
1057 * RGB16 1 0 1 2 2 3 4 5 6 7 8
1058 * ARGB 2 3 0 1 4 5 6 7 8 9 10
1059 * AYUV 3 4 1 0 2 5 6 7 8 9 10
1060 * YUV444 2 4 3 1 0 5 6 7 8 9 10
1061 * YUV422 3 5 4 2 1 0 6 7 8 9 10
1062 * YUV420 4 6 5 3 2 1 0 7 8 9 10
1063 * YUV411 4 6 5 3 2 1 7 0 8 9 10
1064 * YUV410 6 8 7 5 4 3 2 1 0 9 10
1065 * PAL 1 3 2 6 4 6 7 8 9 0 10
1066 * GRAY 1 4 3 2 1 5 6 7 8 9 0
1068 * PAL or GRAY are never preferred, if we can we would convert to PAL instead
1070 * less subsampling is preferred and if any, preferably horizontal
1071 * We would like to keep the alpha, even if we would need to to colorspace conversion
1074 #define SCORE_FORMAT_CHANGE 1
1075 #define SCORE_DEPTH_CHANGE 1
1076 #define SCORE_ALPHA_CHANGE 1
1077 #define SCORE_CHROMA_W_CHANGE 1
1078 #define SCORE_CHROMA_H_CHANGE 1
1079 #define SCORE_PALETTE_CHANGE 1
1081 #define SCORE_COLORSPACE_LOSS 2 /* RGB <-> YUV */
1082 #define SCORE_DEPTH_LOSS 4 /* change bit depth */
1083 #define SCORE_ALPHA_LOSS 8 /* lose the alpha channel */
1084 #define SCORE_CHROMA_W_LOSS 16 /* vertical subsample */
1085 #define SCORE_CHROMA_H_LOSS 32 /* horizontal subsample */
1086 #define SCORE_PALETTE_LOSS 64 /* convert to palette format */
1087 #define SCORE_COLOR_LOSS 128 /* convert to GRAY */
1089 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
1090 GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
1091 #define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
1092 #define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
1094 /* calculate how much loss a conversion would be */
1096 score_value (GstVaVpp * self, const GstVideoFormatInfo * in_info,
1097 GstVideoFormat format, gint * min_loss,
1098 const GstVideoFormatInfo ** out_info)
1100 const GstVideoFormatInfo *t_info;
1101 GstVideoFormatFlags in_flags, t_flags;
1104 t_info = gst_video_format_get_info (format);
1105 if (!t_info || t_info->format == GST_VIDEO_FORMAT_UNKNOWN)
1108 /* accept input format immediately without loss */
1109 if (in_info == t_info) {
1115 loss = SCORE_FORMAT_CHANGE;
1117 in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
1118 in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1119 in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1120 in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1122 t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
1123 t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1124 t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1125 t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1127 if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
1128 loss += SCORE_PALETTE_CHANGE;
1129 if (t_flags & PALETTE_MASK)
1130 loss += SCORE_PALETTE_LOSS;
1133 if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
1134 loss += SCORE_COLORSPACE_LOSS;
1135 if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
1136 loss += SCORE_COLOR_LOSS;
1139 if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
1140 loss += SCORE_ALPHA_CHANGE;
1141 if (in_flags & ALPHA_MASK)
1142 loss += SCORE_ALPHA_LOSS;
1145 if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
1146 loss += SCORE_CHROMA_H_CHANGE;
1147 if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
1148 loss += SCORE_CHROMA_H_LOSS;
1150 if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
1151 loss += SCORE_CHROMA_W_CHANGE;
1152 if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
1153 loss += SCORE_CHROMA_W_LOSS;
1156 if ((in_info->bits) != (t_info->bits)) {
1157 loss += SCORE_DEPTH_CHANGE;
1158 if ((in_info->bits) > (t_info->bits))
1159 loss += SCORE_DEPTH_LOSS;
1162 GST_DEBUG_OBJECT (self, "score %s -> %s = %d",
1163 GST_VIDEO_FORMAT_INFO_NAME (in_info),
1164 GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
1166 if (loss < *min_loss) {
1167 GST_DEBUG_OBJECT (self, "found new best %d", loss);
1177 gst_va_vpp_fixate_format (GstVaVpp * self, GstCaps * caps, GstCaps * result)
1179 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1181 const gchar *in_format;
1182 const GstVideoFormatInfo *in_info, *out_info = NULL;
1183 GstCapsFeatures *features;
1185 gint min_loss = G_MAXINT;
1186 guint i, best_i, capslen;
1188 ins = gst_caps_get_structure (caps, 0);
1189 in_format = gst_structure_get_string (ins, "format");
1193 GST_DEBUG_OBJECT (self, "source format %s", in_format);
1196 gst_video_format_get_info (gst_video_format_from_string (in_format));
1201 capslen = gst_caps_get_size (result);
1202 GST_DEBUG_OBJECT (self, "iterate %d structures", capslen);
1203 for (i = 0; i < capslen; i++) {
1204 GstStructure *tests;
1205 const GValue *format;
1207 tests = gst_caps_get_structure (result, i);
1208 format = gst_structure_get_value (tests, "format");
1209 /* should not happen */
1213 features = gst_caps_get_features (result, i);
1215 if (GST_VALUE_HOLDS_LIST (format)) {
1218 len = gst_value_list_get_size (format);
1219 GST_DEBUG_OBJECT (self, "have %d formats", len);
1220 for (j = 0; j < len; j++) {
1223 val = gst_value_list_get_value (format, j);
1224 if (G_VALUE_HOLDS_STRING (val)) {
1225 fmt = gst_video_format_from_string (g_value_get_string (val));
1226 if (!gst_va_filter_has_video_format (btrans->filter, fmt, features))
1228 if (score_value (self, in_info, fmt, &min_loss, &out_info))
1234 } else if (G_VALUE_HOLDS_STRING (format)) {
1235 fmt = gst_video_format_from_string (g_value_get_string (format));
1236 if (!gst_va_filter_has_video_format (btrans->filter, fmt, features))
1238 if (score_value (self, in_info, fmt, &min_loss, &out_info))
1250 features = gst_caps_features_copy (gst_caps_get_features (result, best_i));
1251 out = gst_structure_copy (gst_caps_get_structure (result, best_i));
1252 gst_structure_set (out, "format", G_TYPE_STRING,
1253 GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
1254 ret = gst_caps_new_full (out, NULL);
1255 gst_caps_set_features_simple (ret, features);
1263 gst_va_vpp_fixate_size (GstVaVpp * self, GstPadDirection direction,
1264 GstCaps * caps, GstCaps * othercaps)
1266 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1267 GstStructure *ins, *outs;
1268 const GValue *from_par, *to_par;
1269 GValue fpar = { 0, };
1270 GValue tpar = { 0, };
1272 ins = gst_caps_get_structure (caps, 0);
1273 outs = gst_caps_get_structure (othercaps, 0);
1275 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1276 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1278 /* If we're fixating from the sinkpad we always set the PAR and
1279 * assume that missing PAR on the sinkpad means 1/1 and
1280 * missing PAR on the srcpad means undefined
1282 if (direction == GST_PAD_SINK) {
1284 g_value_init (&fpar, GST_TYPE_FRACTION);
1285 gst_value_set_fraction (&fpar, 1, 1);
1289 g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
1290 gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
1295 g_value_init (&tpar, GST_TYPE_FRACTION);
1296 gst_value_set_fraction (&tpar, 1, 1);
1299 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1303 g_value_init (&fpar, GST_TYPE_FRACTION);
1304 gst_value_set_fraction (&fpar, 1, 1);
1309 /* we have both PAR but they might not be fixated */
1311 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1313 gint from_dar_n, from_dar_d;
1316 /* from_par should be fixed */
1317 g_return_if_fail (gst_value_is_fixed (from_par));
1319 from_par_n = gst_value_get_fraction_numerator (from_par);
1320 from_par_d = gst_value_get_fraction_denominator (from_par);
1322 gst_structure_get_int (ins, "width", &from_w);
1323 gst_structure_get_int (ins, "height", &from_h);
1325 gst_structure_get_int (outs, "width", &w);
1326 gst_structure_get_int (outs, "height", &h);
1328 /* if video-orientation changes */
1329 switch (gst_va_filter_get_orientation (btrans->filter)) {
1330 case GST_VIDEO_ORIENTATION_90R:
1331 case GST_VIDEO_ORIENTATION_90L:
1332 case GST_VIDEO_ORIENTATION_UL_LR:
1333 case GST_VIDEO_ORIENTATION_UR_LL:
1334 SWAP (from_w, from_h);
1335 SWAP (from_par_n, from_par_d);
1341 /* if both width and height are already fixed, we can't do anything
1342 * about it anymore */
1346 GST_DEBUG_OBJECT (self, "dimensions already set to %dx%d, not fixating",
1348 if (!gst_value_is_fixed (to_par)) {
1349 if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
1350 from_par_n, from_par_d, w, h)) {
1351 GST_DEBUG_OBJECT (self, "fixating to_par to %dx%d", n, d);
1352 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1353 gst_structure_fixate_field_nearest_fraction (outs,
1354 "pixel-aspect-ratio", n, d);
1356 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1363 /* Calculate input DAR */
1364 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1365 &from_dar_n, &from_dar_d)) {
1366 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1367 ("Error calculating the output scaled size - integer overflow"));
1371 GST_DEBUG_OBJECT (self, "Input DAR is %d/%d", from_dar_n, from_dar_d);
1373 /* If either width or height are fixed there's not much we
1374 * can do either except choosing a height or width and PAR
1375 * that matches the DAR as good as possible
1379 gint set_w, set_par_n, set_par_d;
1381 GST_DEBUG_OBJECT (self, "height is fixed (%d)", h);
1383 /* If the PAR is fixed too, there's not much to do
1384 * except choosing the width that is nearest to the
1385 * width with the same DAR */
1386 if (gst_value_is_fixed (to_par)) {
1387 to_par_n = gst_value_get_fraction_numerator (to_par);
1388 to_par_d = gst_value_get_fraction_denominator (to_par);
1390 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1392 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1393 to_par_n, &num, &den)) {
1394 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1395 ("Error calculating the output scaled size - integer overflow"));
1399 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1400 gst_structure_fixate_field_nearest_int (outs, "width", w);
1405 /* The PAR is not fixed and it's quite likely that we can set
1406 * an arbitrary PAR. */
1408 /* Check if we can keep the input width */
1409 tmp = gst_structure_copy (outs);
1410 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1411 gst_structure_get_int (tmp, "width", &set_w);
1413 /* Might have failed but try to keep the DAR nonetheless by
1414 * adjusting the PAR */
1415 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
1416 &to_par_n, &to_par_d)) {
1417 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1418 ("Error calculating the output scaled size - integer overflow"));
1419 gst_structure_free (tmp);
1423 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1424 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1425 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1426 to_par_n, to_par_d);
1427 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1429 gst_structure_free (tmp);
1431 /* Check if the adjusted PAR is accepted */
1432 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1433 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1434 set_par_n != set_par_d)
1435 gst_structure_set (outs, "width", G_TYPE_INT, set_w,
1436 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1441 /* Otherwise scale the width to the new PAR and check if the
1442 * adjusted with is accepted. If all that fails we can't keep
1444 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1445 set_par_n, &num, &den)) {
1446 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1447 ("Error calculating the output scaled size - integer overflow"));
1451 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1452 gst_structure_fixate_field_nearest_int (outs, "width", w);
1453 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1454 set_par_n != set_par_d)
1455 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1456 set_par_n, set_par_d, NULL);
1461 gint set_h, set_par_n, set_par_d;
1463 GST_DEBUG_OBJECT (self, "width is fixed (%d)", w);
1465 /* If the PAR is fixed too, there's not much to do
1466 * except choosing the height that is nearest to the
1467 * height with the same DAR */
1468 if (gst_value_is_fixed (to_par)) {
1469 to_par_n = gst_value_get_fraction_numerator (to_par);
1470 to_par_d = gst_value_get_fraction_denominator (to_par);
1472 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1474 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1475 to_par_n, &num, &den)) {
1476 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1477 ("Error calculating the output scaled size - integer overflow"));
1481 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1482 gst_structure_fixate_field_nearest_int (outs, "height", h);
1487 /* The PAR is not fixed and it's quite likely that we can set
1488 * an arbitrary PAR. */
1490 /* Check if we can keep the input height */
1491 tmp = gst_structure_copy (outs);
1492 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1493 gst_structure_get_int (tmp, "height", &set_h);
1495 /* Might have failed but try to keep the DAR nonetheless by
1496 * adjusting the PAR */
1497 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
1498 &to_par_n, &to_par_d)) {
1499 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1500 ("Error calculating the output scaled size - integer overflow"));
1501 gst_structure_free (tmp);
1504 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1505 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1506 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1507 to_par_n, to_par_d);
1508 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1510 gst_structure_free (tmp);
1512 /* Check if the adjusted PAR is accepted */
1513 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1514 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1515 set_par_n != set_par_d)
1516 gst_structure_set (outs, "height", G_TYPE_INT, set_h,
1517 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1522 /* Otherwise scale the height to the new PAR and check if the
1523 * adjusted with is accepted. If all that fails we can't keep
1525 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1526 set_par_n, &num, &den)) {
1527 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1528 ("Error calculating the output scale sized - integer overflow"));
1532 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1533 gst_structure_fixate_field_nearest_int (outs, "height", h);
1534 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1535 set_par_n != set_par_d)
1536 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1537 set_par_n, set_par_d, NULL);
1540 } else if (gst_value_is_fixed (to_par)) {
1542 gint set_h, set_w, f_h, f_w;
1544 to_par_n = gst_value_get_fraction_numerator (to_par);
1545 to_par_d = gst_value_get_fraction_denominator (to_par);
1547 /* Calculate scale factor for the PAR change */
1548 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1549 to_par_d, &num, &den)) {
1550 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1551 ("Error calculating the output scaled size - integer overflow"));
1555 /* Try to keep the input height (because of interlacing) */
1556 tmp = gst_structure_copy (outs);
1557 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1558 gst_structure_get_int (tmp, "height", &set_h);
1560 /* This might have failed but try to scale the width
1561 * to keep the DAR nonetheless */
1562 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1563 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1564 gst_structure_get_int (tmp, "width", &set_w);
1565 gst_structure_free (tmp);
1567 /* We kept the DAR and the height is nearest to the original height */
1569 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1570 G_TYPE_INT, set_h, NULL);
1577 /* If the former failed, try to keep the input width at least */
1578 tmp = gst_structure_copy (outs);
1579 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1580 gst_structure_get_int (tmp, "width", &set_w);
1582 /* This might have failed but try to scale the width
1583 * to keep the DAR nonetheless */
1584 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1585 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1586 gst_structure_get_int (tmp, "height", &set_h);
1587 gst_structure_free (tmp);
1589 /* We kept the DAR and the width is nearest to the original width */
1591 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1592 G_TYPE_INT, set_h, NULL);
1596 /* If all this failed, keep the dimensions with the DAR that was closest
1597 * to the correct DAR. This changes the DAR but there's not much else to
1600 if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
1604 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1609 gint set_h, set_w, set_par_n, set_par_d, tmp2;
1611 /* width, height and PAR are not fixed but passthrough is not possible */
1613 /* First try to keep the height and width as good as possible
1615 tmp = gst_structure_copy (outs);
1616 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1617 gst_structure_get_int (tmp, "height", &set_h);
1618 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1619 gst_structure_get_int (tmp, "width", &set_w);
1621 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1622 &to_par_n, &to_par_d)) {
1623 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1624 ("Error calculating the output scaled size - integer overflow"));
1625 gst_structure_free (tmp);
1629 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1630 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1631 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1632 to_par_n, to_par_d);
1633 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1635 gst_structure_free (tmp);
1637 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1638 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1639 G_TYPE_INT, set_h, NULL);
1641 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1642 set_par_n != set_par_d)
1643 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1644 set_par_n, set_par_d, NULL);
1648 /* Otherwise try to scale width to keep the DAR with the set
1650 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1651 set_par_n, &num, &den)) {
1652 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1653 ("Error calculating the output scaled size - integer overflow"));
1657 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1658 tmp = gst_structure_copy (outs);
1659 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1660 gst_structure_get_int (tmp, "width", &tmp2);
1661 gst_structure_free (tmp);
1664 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1665 G_TYPE_INT, set_h, NULL);
1666 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1667 set_par_n != set_par_d)
1668 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1669 set_par_n, set_par_d, NULL);
1673 /* ... or try the same with the height */
1674 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1675 tmp = gst_structure_copy (outs);
1676 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1677 gst_structure_get_int (tmp, "height", &tmp2);
1678 gst_structure_free (tmp);
1681 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1682 G_TYPE_INT, tmp2, NULL);
1683 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1684 set_par_n != set_par_d)
1685 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1686 set_par_n, set_par_d, NULL);
1690 /* If all fails we can't keep the DAR and take the nearest values
1691 * for everything from the first try */
1692 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1693 G_TYPE_INT, set_h, NULL);
1694 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1695 set_par_n != set_par_d)
1696 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1697 set_par_n, set_par_d, NULL);
1702 if (from_par == &fpar)
1703 g_value_unset (&fpar);
1704 if (to_par == &tpar)
1705 g_value_unset (&tpar);
1709 subsampling_unchanged (GstVideoInfo * in_info, GstVideoInfo * out_info)
1712 const GstVideoFormatInfo *in_format, *out_format;
1714 if (GST_VIDEO_INFO_N_COMPONENTS (in_info) !=
1715 GST_VIDEO_INFO_N_COMPONENTS (out_info))
1718 in_format = in_info->finfo;
1719 out_format = out_info->finfo;
1721 for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (in_info); i++) {
1722 if (GST_VIDEO_FORMAT_INFO_W_SUB (in_format,
1723 i) != GST_VIDEO_FORMAT_INFO_W_SUB (out_format, i))
1725 if (GST_VIDEO_FORMAT_INFO_H_SUB (in_format,
1726 i) != GST_VIDEO_FORMAT_INFO_H_SUB (out_format, i))
1734 transfer_colorimetry_from_input (GstVaVpp * self, GstCaps * in_caps,
1737 GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
1738 GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
1739 gboolean have_colorimetry =
1740 gst_structure_has_field (out_caps_s, "colorimetry");
1741 gboolean have_chroma_site =
1742 gst_structure_has_field (out_caps_s, "chroma-site");
1744 /* If the output already has colorimetry and chroma-site, stop,
1745 * otherwise try and transfer what we can from the input caps */
1746 if (have_colorimetry && have_chroma_site)
1750 GstVideoInfo in_info, out_info;
1751 const GValue *in_colorimetry =
1752 gst_structure_get_value (in_caps_s, "colorimetry");
1754 if (!gst_video_info_from_caps (&in_info, in_caps)) {
1755 GST_WARNING_OBJECT (self,
1756 "Failed to convert sink pad caps to video info");
1759 if (!gst_video_info_from_caps (&out_info, out_caps)) {
1760 GST_WARNING_OBJECT (self, "Failed to convert src pad caps to video info");
1764 if (!have_colorimetry && in_colorimetry != NULL) {
1765 if ((GST_VIDEO_INFO_IS_YUV (&out_info)
1766 && GST_VIDEO_INFO_IS_YUV (&in_info))
1767 || (GST_VIDEO_INFO_IS_RGB (&out_info)
1768 && GST_VIDEO_INFO_IS_RGB (&in_info))
1769 || (GST_VIDEO_INFO_IS_GRAY (&out_info)
1770 && GST_VIDEO_INFO_IS_GRAY (&in_info))) {
1771 /* Can transfer the colorimetry intact from the input if it has it */
1772 gst_structure_set_value (out_caps_s, "colorimetry", in_colorimetry);
1774 gchar *colorimetry_str;
1776 /* Changing between YUV/RGB - forward primaries and transfer function, but use
1777 * default range and matrix.
1778 * the primaries is used for conversion between RGB and XYZ (CIE 1931 coordinate).
1779 * the transfer function could be another reference (e.g., HDR)
1781 out_info.colorimetry.primaries = in_info.colorimetry.primaries;
1782 out_info.colorimetry.transfer = in_info.colorimetry.transfer;
1785 gst_video_colorimetry_to_string (&out_info.colorimetry);
1786 gst_caps_set_simple (out_caps, "colorimetry", G_TYPE_STRING,
1787 colorimetry_str, NULL);
1788 g_free (colorimetry_str);
1792 /* Only YUV output needs chroma-site. If the input was also YUV and had the same chroma
1793 * subsampling, transfer the siting. If the sub-sampling is changing, then the planes get
1794 * scaled anyway so there's no real reason to prefer the input siting. */
1795 if (!have_chroma_site && GST_VIDEO_INFO_IS_YUV (&out_info)) {
1796 if (GST_VIDEO_INFO_IS_YUV (&in_info)) {
1797 const GValue *in_chroma_site =
1798 gst_structure_get_value (in_caps_s, "chroma-site");
1799 if (in_chroma_site != NULL
1800 && subsampling_unchanged (&in_info, &out_info))
1801 gst_structure_set_value (out_caps_s, "chroma-site", in_chroma_site);
1808 copy_misc_fields_from_input (GstCaps * in_caps, GstCaps * out_caps)
1810 const gchar *fields[] = { "interlace-mode", "field-order", "multiview-mode",
1811 "multiview-flags", "framerate"
1813 GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
1814 GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
1817 for (i = 0; i < G_N_ELEMENTS (fields); i++) {
1818 const GValue *in_field = gst_structure_get_value (in_caps_s, fields[i]);
1819 const GValue *out_field = gst_structure_get_value (out_caps_s, fields[i]);
1821 if (out_field && gst_value_is_fixed (out_field))
1825 gst_structure_set_value (out_caps_s, fields[i], in_field);
1830 update_hdr_fields (GstVaVpp * self, GstCaps * result)
1832 GstStructure *s = gst_caps_get_structure (result, 0);
1833 GstVideoInfo out_info;
1834 gboolean have_colorimetry;
1836 gst_structure_remove_fields (s, "mastering-display-info",
1837 "content-light-level", "hdr-format", NULL);
1839 have_colorimetry = gst_structure_has_field (s, "colorimetry");
1840 if (!have_colorimetry) {
1841 if (gst_video_info_from_caps (&out_info, result)) {
1842 gchar *colorimetry_str =
1843 gst_video_colorimetry_to_string (&out_info.colorimetry);
1844 gst_caps_set_simple (result, "colorimetry", G_TYPE_STRING,
1845 colorimetry_str, NULL);
1846 g_free (colorimetry_str);
1848 GST_WARNING_OBJECT (self, "Failed to convert src pad caps to video info");
1854 gst_va_vpp_fixate_caps (GstBaseTransform * trans, GstPadDirection direction,
1855 GstCaps * caps, GstCaps * othercaps)
1857 GstVaVpp *self = GST_VA_VPP (trans);
1860 GST_DEBUG_OBJECT (self,
1861 "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
1862 GST_PTR_FORMAT, othercaps, caps);
1864 /* will iterate in all structures to find one with "best color" */
1865 result = gst_va_vpp_fixate_format (self, caps, othercaps);
1869 gst_clear_caps (&othercaps);
1871 gst_va_vpp_fixate_size (self, direction, caps, result);
1873 /* some fields might be lost while feature caps conversion */
1874 copy_misc_fields_from_input (caps, result);
1876 /* fixate remaining fields */
1877 result = gst_caps_fixate (result);
1879 if (direction == GST_PAD_SINK) {
1880 if (self->hdr_mapping)
1881 update_hdr_fields (self, result);
1883 /* Try and preserve input colorimetry / chroma information */
1884 transfer_colorimetry_from_input (self, caps, result);
1886 if (gst_caps_is_subset (caps, result))
1887 gst_caps_replace (&result, caps);
1890 GST_DEBUG_OBJECT (self, "fixated othercaps to %" GST_PTR_FORMAT, result);
1896 _get_scale_factor (GstVaVpp * self, gdouble * w_factor, gdouble * h_factor)
1898 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1899 gdouble w = GST_VIDEO_INFO_WIDTH (&btrans->in_info);
1900 gdouble h = GST_VIDEO_INFO_HEIGHT (&btrans->in_info);
1902 switch (self->direction) {
1903 case GST_VIDEO_ORIENTATION_90R:
1904 case GST_VIDEO_ORIENTATION_90L:
1905 case GST_VIDEO_ORIENTATION_UR_LL:
1906 case GST_VIDEO_ORIENTATION_UL_LR:{
1916 *w_factor = GST_VIDEO_INFO_WIDTH (&btrans->out_info);
1919 *h_factor = GST_VIDEO_INFO_HEIGHT (&btrans->out_info);
1924 gst_va_vpp_src_event (GstBaseTransform * trans, GstEvent * event)
1926 GstVaVpp *self = GST_VA_VPP (trans);
1927 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
1928 const GstVideoInfo *in_info = &btrans->in_info, *out_info = &btrans->out_info;
1929 gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
1932 GST_TRACE_OBJECT (self, "handling %s event", GST_EVENT_TYPE_NAME (event));
1934 switch (GST_EVENT_TYPE (event)) {
1935 case GST_EVENT_NAVIGATION:
1936 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
1937 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
1938 || gst_va_filter_get_orientation (btrans->filter) !=
1939 GST_VIDEO_ORIENTATION_IDENTITY) {
1941 event = gst_event_make_writable (event);
1943 if (!gst_navigation_event_get_coordinates (event, &x, &y))
1946 /* video-direction compensation */
1947 switch (self->direction) {
1948 case GST_VIDEO_ORIENTATION_90R:
1950 new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1952 case GST_VIDEO_ORIENTATION_90L:
1953 new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1956 case GST_VIDEO_ORIENTATION_UR_LL:
1957 new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1958 new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1960 case GST_VIDEO_ORIENTATION_UL_LR:
1964 case GST_VIDEO_ORIENTATION_180:
1965 /* FIXME: is this correct? */
1966 new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1967 new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1969 case GST_VIDEO_ORIENTATION_HORIZ:
1970 new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1973 case GST_VIDEO_ORIENTATION_VERT:
1975 new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1983 /* scale compensation */
1984 _get_scale_factor (self, &w_factor, &h_factor);
1988 /* crop compensation is done by videocrop */
1990 GST_TRACE_OBJECT (self, "from %fx%f to %fx%f", x, y, new_x, new_y);
1991 gst_navigation_event_set_coordinates (event, new_x, new_y);
1998 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
2004 gst_va_vpp_sink_event (GstBaseTransform * trans, GstEvent * event)
2006 GstVaVpp *self = GST_VA_VPP (trans);
2007 GstTagList *taglist;
2010 switch (GST_EVENT_TYPE (event)) {
2012 gst_event_parse_tag (event, &taglist);
2014 if (!gst_tag_list_get_string (taglist, "image-orientation", &orientation))
2017 if (self->direction != GST_VIDEO_ORIENTATION_AUTO)
2020 GST_DEBUG_OBJECT (self, "tag orientation %s", orientation);
2022 GST_OBJECT_LOCK (self);
2023 if (!g_strcmp0 ("rotate-0", orientation))
2024 self->tag_direction = GST_VIDEO_ORIENTATION_IDENTITY;
2025 else if (!g_strcmp0 ("rotate-90", orientation))
2026 self->tag_direction = GST_VIDEO_ORIENTATION_90R;
2027 else if (!g_strcmp0 ("rotate-180", orientation))
2028 self->tag_direction = GST_VIDEO_ORIENTATION_180;
2029 else if (!g_strcmp0 ("rotate-270", orientation))
2030 self->tag_direction = GST_VIDEO_ORIENTATION_90L;
2031 else if (!g_strcmp0 ("flip-rotate-0", orientation))
2032 self->tag_direction = GST_VIDEO_ORIENTATION_HORIZ;
2033 else if (!g_strcmp0 ("flip-rotate-90", orientation))
2034 self->tag_direction = GST_VIDEO_ORIENTATION_UL_LR;
2035 else if (!g_strcmp0 ("flip-rotate-180", orientation))
2036 self->tag_direction = GST_VIDEO_ORIENTATION_VERT;
2037 else if (!g_strcmp0 ("flip-rotate-270", orientation))
2038 self->tag_direction = GST_VIDEO_ORIENTATION_UR_LL;
2040 _update_properties_unlocked (self);
2041 GST_OBJECT_UNLOCK (self);
2043 gst_va_vpp_update_passthrough (self, FALSE);
2050 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2054 _install_static_properties (GObjectClass * klass)
2057 * GstVaPostProc:disable-passthrough:
2059 * If set to %TRUE the filter will not enable passthrough mode, thus
2060 * each frame will be processed. It's useful for cropping, for
2065 PROPERTIES (PROP_DISABLE_PASSTHROUGH) =
2066 g_param_spec_boolean ("disable-passthrough", "Disable Passthrough",
2067 "Forces passing buffers through the postprocessor", FALSE,
2068 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY);
2069 g_object_class_install_property (klass, PROP_DISABLE_PASSTHROUGH,
2070 PROPERTIES (PROP_DISABLE_PASSTHROUGH));
2073 * GstVaPostProc:add-borders:
2075 * If set to %TRUE the filter will add black borders if necessary to
2076 * keep the display aspect ratio.
2080 PROPERTIES (PROP_ADD_BORDERS) = g_param_spec_boolean ("add-borders",
2082 "Add black borders if necessary to keep the display aspect ratio", FALSE,
2083 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING);
2084 g_object_class_install_property (klass, PROP_ADD_BORDERS,
2085 PROPERTIES (PROP_ADD_BORDERS));
2088 * GstVaPostProc:scale-method
2090 * Sets the scale method algorithm to use when resizing.
2094 PROPERTIES (PROP_SCALE_METHOD) = g_param_spec_enum ("scale-method",
2095 "Scale Method", "Scale method to use", GST_TYPE_VA_SCALE_METHOD,
2096 VA_FILTER_SCALING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
2097 | GST_PARAM_MUTABLE_PLAYING);
2098 g_object_class_install_property (klass, PROP_SCALE_METHOD,
2099 PROPERTIES (PROP_SCALE_METHOD));
2103 gst_va_vpp_class_init (gpointer g_class, gpointer class_data)
2105 GstCaps *doc_caps, *caps = NULL;
2106 GstPadTemplate *sink_pad_templ, *src_pad_templ;
2107 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
2108 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
2109 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2110 GstVaBaseTransformClass *btrans_class = GST_VA_BASE_TRANSFORM_CLASS (g_class);
2111 GstVaDisplay *display;
2112 GstVaFilter *filter;
2113 struct CData *cdata = class_data;
2117 parent_class = g_type_class_peek_parent (g_class);
2119 btrans_class->render_device_path = g_strdup (cdata->render_device_path);
2121 if (cdata->description) {
2122 long_name = g_strdup_printf ("VA-API Video Postprocessor in %s",
2123 cdata->description);
2125 long_name = g_strdup ("VA-API Video Postprocessor");
2128 klass = g_string_new ("Converter/Filter/Colorspace/Scaler/Video/Hardware");
2130 display = gst_va_display_drm_new_from_path (btrans_class->render_device_path);
2131 filter = gst_va_filter_new (display);
2133 if (gst_va_filter_open (filter)) {
2134 caps = gst_va_filter_get_caps (filter);
2136 /* adds any to enable passthrough */
2138 GstCaps *any_caps = gst_caps_new_empty_simple ("video/x-raw");
2139 gst_caps_set_features_simple (any_caps, gst_caps_features_new_any ());
2140 caps = gst_caps_merge (caps, any_caps);
2143 /* add converter klass */
2146 VAProcFilterType types[] = { VAProcFilterColorBalance,
2147 VAProcFilterSkinToneEnhancement, VAProcFilterSharpening,
2148 VAProcFilterNoiseReduction
2151 for (i = 0; i < G_N_ELEMENTS (types); i++) {
2152 if (gst_va_filter_has_filter (filter, types[i])) {
2153 g_string_prepend (klass, "Effect/");
2159 caps = gst_caps_from_string (caps_str);
2162 gst_element_class_set_metadata (element_class, long_name, klass->str,
2163 "VA-API based video postprocessor",
2164 "Víctor Jáquez <vjaquez@igalia.com>");
2166 g_string_free (klass, TRUE);
2168 doc_caps = gst_caps_from_string (caps_str);
2170 sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
2172 gst_element_class_add_pad_template (element_class, sink_pad_templ);
2173 gst_pad_template_set_documentation_caps (sink_pad_templ,
2174 gst_caps_ref (doc_caps));
2176 src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
2178 gst_element_class_add_pad_template (element_class, src_pad_templ);
2179 gst_pad_template_set_documentation_caps (src_pad_templ,
2180 gst_caps_ref (doc_caps));
2181 gst_caps_unref (doc_caps);
2183 gst_caps_unref (caps);
2185 object_class->dispose = gst_va_vpp_dispose;
2186 object_class->set_property = gst_va_vpp_set_property;
2187 object_class->get_property = gst_va_vpp_get_property;
2189 trans_class->propose_allocation =
2190 GST_DEBUG_FUNCPTR (gst_va_vpp_propose_allocation);
2191 trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_caps);
2192 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_fixate_caps);
2193 trans_class->before_transform =
2194 GST_DEBUG_FUNCPTR (gst_va_vpp_before_transform);
2195 trans_class->transform = GST_DEBUG_FUNCPTR (gst_va_vpp_transform);
2196 trans_class->transform_meta = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_meta);
2197 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_va_vpp_src_event);
2198 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_va_vpp_sink_event);
2200 trans_class->transform_ip_on_passthrough = FALSE;
2202 btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_va_vpp_set_info);
2203 btrans_class->update_properties =
2204 GST_DEBUG_FUNCPTR (gst_va_vpp_update_properties);
2206 gst_va_filter_install_properties (filter, object_class);
2208 _install_static_properties (object_class);
2211 g_free (cdata->description);
2212 g_free (cdata->render_device_path);
2214 gst_object_unref (filter);
2215 gst_object_unref (display);
2219 _create_colorbalance_channel (GstVaVpp * self, const gchar * label)
2221 GstColorBalanceChannel *channel;
2223 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2224 channel->label = g_strdup_printf ("VA-%s", label);
2225 channel->min_value = -1000;
2226 channel->max_value = 1000;
2228 self->channels = g_list_append (self->channels, channel);
2232 gst_va_vpp_init (GTypeInstance * instance, gpointer g_class)
2234 GstVaVpp *self = GST_VA_VPP (instance);
2237 self->direction = GST_VIDEO_ORIENTATION_IDENTITY;
2238 self->prev_direction = self->direction;
2239 self->tag_direction = GST_VIDEO_ORIENTATION_AUTO;
2241 pspec = g_object_class_find_property (g_class, "denoise");
2243 self->denoise = g_value_get_float (g_param_spec_get_default_value (pspec));
2245 pspec = g_object_class_find_property (g_class, "sharpen");
2247 self->sharpen = g_value_get_float (g_param_spec_get_default_value (pspec));
2249 pspec = g_object_class_find_property (g_class, "skin-tone");
2251 const GValue *value = g_param_spec_get_default_value (pspec);
2252 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
2253 self->skintone = g_value_get_boolean (value);
2255 self->skintone = g_value_get_float (value);
2259 pspec = g_object_class_find_property (g_class, "brightness");
2262 g_value_get_float (g_param_spec_get_default_value (pspec));
2263 _create_colorbalance_channel (self, "BRIGHTNESS");
2265 pspec = g_object_class_find_property (g_class, "contrast");
2267 self->contrast = g_value_get_float (g_param_spec_get_default_value (pspec));
2268 _create_colorbalance_channel (self, "CONTRAST");
2270 pspec = g_object_class_find_property (g_class, "hue");
2272 self->hue = g_value_get_float (g_param_spec_get_default_value (pspec));
2273 _create_colorbalance_channel (self, "HUE");
2275 pspec = g_object_class_find_property (g_class, "saturation");
2278 g_value_get_float (g_param_spec_get_default_value (pspec));
2279 _create_colorbalance_channel (self, "SATURATION");
2282 /* HDR tone mapping */
2283 pspec = g_object_class_find_property (g_class, "hdr-tone-mapping");
2286 g_value_get_boolean (g_param_spec_get_default_value (pspec));
2290 gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (instance), TRUE);
2294 _register_debug_category (gpointer data)
2296 GST_DEBUG_CATEGORY_INIT (gst_va_vpp_debug, "vapostproc", 0,
2297 "VA Video Postprocessor");
2300 G_PASTE (META_TAG_, type) = \
2301 g_quark_from_static_string (G_PASTE (G_PASTE (GST_META_TAG_VIDEO_, type), _STR))
2306 META_TAG_VIDEO = g_quark_from_static_string (GST_META_TAG_VIDEO_STR);
2312 gst_va_vpp_register (GstPlugin * plugin, GstVaDevice * device,
2313 gboolean has_colorbalance, guint rank)
2315 static GOnce debug_once = G_ONCE_INIT;
2317 GTypeInfo type_info = {
2318 .class_size = sizeof (GstVaVppClass),
2319 .class_init = gst_va_vpp_class_init,
2320 .instance_size = sizeof (GstVaVpp),
2321 .instance_init = gst_va_vpp_init,
2323 struct CData *cdata;
2325 gchar *type_name, *feature_name;
2327 g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
2328 g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
2330 cdata = g_new (struct CData, 1);
2331 cdata->description = NULL;
2332 cdata->render_device_path = g_strdup (device->render_device_path);
2334 type_info.class_data = cdata;
2336 type_name = g_strdup ("GstVaPostProc");
2337 feature_name = g_strdup ("vapostproc");
2339 /* The first postprocessor to be registered should use a constant
2340 * name, like vapostproc, for any additional postprocessors, we
2341 * create unique names, using inserting the render device name. */
2342 if (g_type_from_name (type_name)) {
2343 gchar *basename = g_path_get_basename (device->render_device_path);
2345 g_free (feature_name);
2346 type_name = g_strdup_printf ("GstVa%sPostProc", basename);
2347 feature_name = g_strdup_printf ("va%spostproc", basename);
2348 cdata->description = basename;
2350 /* lower rank for non-first device */
2355 g_once (&debug_once, _register_debug_category, NULL);
2357 type = g_type_register_static (GST_TYPE_VA_BASE_TRANSFORM, type_name,
2360 if (has_colorbalance) {
2361 const GInterfaceInfo info = { gst_va_vpp_colorbalance_init, NULL, NULL };
2362 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &info);
2365 ret = gst_element_register (plugin, feature_name, rank, type);
2368 g_free (feature_name);
2373 /* Color Balance interface */
2374 static const GList *
2375 gst_va_vpp_colorbalance_list_channels (GstColorBalance * balance)
2377 GstVaVpp *self = GST_VA_VPP (balance);
2379 return self->channels;
2382 /* This assumes --as happens with intel drivers-- that max values are
2383 * bigger than the simmetrical values of min values */
2385 make_max_simmetrical (GParamSpecFloat * fpspec)
2389 if (fpspec->default_value == 0)
2390 max = -fpspec->minimum;
2392 max = fpspec->default_value + ABS (fpspec->minimum - fpspec->default_value);
2394 return MIN (max, fpspec->maximum);
2398 _set_cb_val (GstVaVpp * self, const gchar * name,
2399 GstColorBalanceChannel * channel, gint value, gfloat * cb)
2401 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2403 GParamSpecFloat *fpspec;
2404 gfloat new_value, max;
2407 pspec = g_object_class_find_property (klass, name);
2411 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2412 max = make_max_simmetrical (fpspec);
2414 new_value = (value - channel->min_value) * (max - fpspec->minimum)
2415 / (channel->max_value - channel->min_value) + fpspec->minimum;
2417 GST_OBJECT_LOCK (self);
2418 changed = new_value != *cb;
2420 value = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2421 / (max - fpspec->minimum) + channel->min_value;
2422 GST_OBJECT_UNLOCK (self);
2425 GST_INFO_OBJECT (self, "%s: %d / %f", channel->label, value, new_value);
2426 gst_color_balance_value_changed (GST_COLOR_BALANCE (self), channel, value);
2427 g_atomic_int_set (&self->rebuild_filters, TRUE);
2434 gst_va_vpp_colorbalance_set_value (GstColorBalance * balance,
2435 GstColorBalanceChannel * channel, gint value)
2437 GstVaVpp *self = GST_VA_VPP (balance);
2439 if (g_str_has_suffix (channel->label, "HUE"))
2440 _set_cb_val (self, "hue", channel, value, &self->hue);
2441 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2442 _set_cb_val (self, "brightness", channel, value, &self->brightness);
2443 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2444 _set_cb_val (self, "contrast", channel, value, &self->contrast);
2445 else if (g_str_has_suffix (channel->label, "SATURATION"))
2446 _set_cb_val (self, "saturation", channel, value, &self->saturation);
2450 _get_cb_val (GstVaVpp * self, const gchar * name,
2451 GstColorBalanceChannel * channel, gfloat * cb, gint * val)
2453 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2455 GParamSpecFloat *fpspec;
2458 pspec = g_object_class_find_property (klass, name);
2462 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2463 max = make_max_simmetrical (fpspec);
2465 GST_OBJECT_LOCK (self);
2466 *val = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2467 / (max - fpspec->minimum) + channel->min_value;
2468 GST_OBJECT_UNLOCK (self);
2474 gst_va_vpp_colorbalance_get_value (GstColorBalance * balance,
2475 GstColorBalanceChannel * channel)
2477 GstVaVpp *self = GST_VA_VPP (balance);
2480 if (g_str_has_suffix (channel->label, "HUE"))
2481 _get_cb_val (self, "hue", channel, &self->hue, &value);
2482 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2483 _get_cb_val (self, "brightness", channel, &self->brightness, &value);
2484 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2485 _get_cb_val (self, "contrast", channel, &self->contrast, &value);
2486 else if (g_str_has_suffix (channel->label, "SATURATION"))
2487 _get_cb_val (self, "saturation", channel, &self->saturation, &value);
2492 static GstColorBalanceType
2493 gst_va_vpp_colorbalance_get_balance_type (GstColorBalance * balance)
2495 return GST_COLOR_BALANCE_HARDWARE;
2499 gst_va_vpp_colorbalance_init (gpointer iface, gpointer data)
2501 GstColorBalanceInterface *cbiface = iface;
2503 cbiface->list_channels = gst_va_vpp_colorbalance_list_channels;
2504 cbiface->set_value = gst_va_vpp_colorbalance_set_value;
2505 cbiface->get_value = gst_va_vpp_colorbalance_get_value;
2506 cbiface->get_balance_type = gst_va_vpp_colorbalance_get_balance_type;