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/video/video.h>
67 #include <va/va_drmcommon.h>
69 #include "gstvaallocator.h"
70 #include "gstvabasetransform.h"
71 #include "gstvacaps.h"
72 #include "gstvadisplay_priv.h"
73 #include "gstvafilter.h"
74 #include "gstvapool.h"
76 GST_DEBUG_CATEGORY_STATIC (gst_va_vpp_debug);
77 #define GST_CAT_DEFAULT gst_va_vpp_debug
79 #define GST_VA_VPP(obj) ((GstVaVpp *) obj)
80 #define GST_VA_VPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaVppClass))
81 #define GST_VA_VPP_CLASS(klass) ((GstVaVppClass *) klass)
83 #define SWAP(a, b) do { const __typeof__ (a) t = a; a = b; b = t; } while (0)
85 typedef struct _GstVaVpp GstVaVpp;
86 typedef struct _GstVaVppClass GstVaVppClass;
90 /* GstVideoFilter overlaps functionality */
91 GstVaBaseTransformClass parent_class;
96 GstVaBaseTransform parent;
98 gboolean rebuild_filters;
109 gboolean auto_contrast;
110 gboolean auto_brightness;
111 gboolean auto_saturation;
112 GstVideoOrientationMethod direction;
113 GstVideoOrientationMethod prev_direction;
114 GstVideoOrientationMethod tag_direction;
115 gboolean add_borders;
119 gboolean hdr_mapping;
120 gboolean has_hdr_meta;
121 VAHdrMetaDataHDR10 hdr_meta;
126 static GstElementClass *parent_class = NULL;
130 gchar *render_device_path;
134 /* convertions that disable passthrough */
137 VPP_CONVERT_SIZE = 1 << 0,
138 VPP_CONVERT_FORMAT = 1 << 1,
139 VPP_CONVERT_FILTERS = 1 << 2,
140 VPP_CONVERT_DIRECTION = 1 << 3,
141 VPP_CONVERT_FEATURE = 1 << 4,
142 VPP_CONVERT_CROP = 1 << 5,
143 VPP_CONVERT_DUMMY = 1 << 6,
147 static const gchar *caps_str =
148 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
149 "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
150 GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
151 "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR }");
154 #define META_TAG_COLORSPACE meta_tag_colorspace_quark
155 static GQuark meta_tag_colorspace_quark;
156 #define META_TAG_SIZE meta_tag_size_quark
157 static GQuark meta_tag_size_quark;
158 #define META_TAG_ORIENTATION meta_tag_orientation_quark
159 static GQuark meta_tag_orientation_quark;
160 #define META_TAG_VIDEO meta_tag_video_quark
161 static GQuark meta_tag_video_quark;
163 static void gst_va_vpp_colorbalance_init (gpointer iface, gpointer data);
164 static void gst_va_vpp_rebuild_filters (GstVaVpp * self);
167 gst_va_vpp_dispose (GObject * object)
169 GstVaVpp *self = GST_VA_VPP (object);
172 g_list_free_full (g_steal_pointer (&self->channels), g_object_unref);
174 G_OBJECT_CLASS (parent_class)->dispose (object);
178 gst_va_vpp_update_passthrough (GstVaVpp * self, gboolean reconf)
180 GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
183 old = gst_base_transform_is_passthrough (trans);
185 GST_OBJECT_LOCK (self);
186 new = (self->op_flags == 0);
187 GST_OBJECT_UNLOCK (self);
190 GST_INFO_OBJECT (self, "%s passthrough", new ? "enabling" : "disabling");
192 gst_base_transform_reconfigure_src (trans);
193 gst_base_transform_set_passthrough (trans, new);
198 _update_properties_unlocked (GstVaVpp * self)
200 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
205 if ((self->direction != GST_VIDEO_ORIENTATION_AUTO
206 && self->direction != self->prev_direction)
207 || (self->direction == GST_VIDEO_ORIENTATION_AUTO
208 && self->tag_direction != self->prev_direction)) {
210 GstVideoOrientationMethod direction =
211 (self->direction == GST_VIDEO_ORIENTATION_AUTO) ?
212 self->tag_direction : self->direction;
214 if (!gst_va_filter_set_orientation (btrans->filter, direction)) {
215 if (self->direction == GST_VIDEO_ORIENTATION_AUTO)
216 self->tag_direction = self->prev_direction;
218 self->direction = self->prev_direction;
220 self->op_flags &= ~VPP_CONVERT_DIRECTION;
222 /* FIXME: unlocked bus warning message */
223 GST_WARNING_OBJECT (self,
224 "Driver cannot set resquested orientation. Setting it back.");
226 self->prev_direction = direction;
228 self->op_flags |= VPP_CONVERT_DIRECTION;
230 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (self));
233 self->op_flags &= ~VPP_CONVERT_DIRECTION;
238 gst_va_vpp_set_property (GObject * object, guint prop_id,
239 const GValue * value, GParamSpec * pspec)
241 GstVaVpp *self = GST_VA_VPP (object);
243 GST_OBJECT_LOCK (object);
245 case GST_VA_FILTER_PROP_DENOISE:
246 self->denoise = g_value_get_float (value);
247 g_atomic_int_set (&self->rebuild_filters, TRUE);
249 case GST_VA_FILTER_PROP_SHARPEN:
250 self->sharpen = g_value_get_float (value);
251 g_atomic_int_set (&self->rebuild_filters, TRUE);
253 case GST_VA_FILTER_PROP_SKINTONE:
254 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
255 self->skintone = (float) g_value_get_boolean (value);
257 self->skintone = g_value_get_float (value);
258 g_atomic_int_set (&self->rebuild_filters, TRUE);
260 case GST_VA_FILTER_PROP_VIDEO_DIR:{
261 GstVideoOrientationMethod direction = g_value_get_enum (value);
262 self->prev_direction = (direction == GST_VIDEO_ORIENTATION_AUTO) ?
263 self->tag_direction : self->direction;
264 self->direction = direction;
267 case GST_VA_FILTER_PROP_HUE:
268 self->hue = g_value_get_float (value);
269 g_atomic_int_set (&self->rebuild_filters, TRUE);
271 case GST_VA_FILTER_PROP_SATURATION:
272 self->saturation = g_value_get_float (value);
273 g_atomic_int_set (&self->rebuild_filters, TRUE);
275 case GST_VA_FILTER_PROP_BRIGHTNESS:
276 self->brightness = g_value_get_float (value);
277 g_atomic_int_set (&self->rebuild_filters, TRUE);
279 case GST_VA_FILTER_PROP_CONTRAST:
280 self->contrast = g_value_get_float (value);
281 g_atomic_int_set (&self->rebuild_filters, TRUE);
283 case GST_VA_FILTER_PROP_AUTO_SATURATION:
284 self->auto_saturation = g_value_get_boolean (value);
285 g_atomic_int_set (&self->rebuild_filters, TRUE);
287 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
288 self->auto_brightness = g_value_get_boolean (value);
289 g_atomic_int_set (&self->rebuild_filters, TRUE);
291 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
292 self->auto_contrast = g_value_get_boolean (value);
293 g_atomic_int_set (&self->rebuild_filters, TRUE);
295 case GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH:{
296 gboolean disable_passthrough = g_value_get_boolean (value);
297 if (disable_passthrough)
298 self->op_flags |= VPP_CONVERT_DUMMY;
300 self->op_flags &= ~VPP_CONVERT_DUMMY;
303 case GST_VA_FILTER_PROP_ADD_BORDERS:
304 self->add_borders = g_value_get_boolean (value);
306 case GST_VA_FILTER_PROP_HDR:
307 self->hdr_mapping = g_value_get_boolean (value);
308 g_atomic_int_set (&self->rebuild_filters, TRUE);
311 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
315 _update_properties_unlocked (self);
316 GST_OBJECT_UNLOCK (object);
318 gst_va_vpp_update_passthrough (self, FALSE);
322 gst_va_vpp_get_property (GObject * object, guint prop_id, GValue * value,
325 GstVaVpp *self = GST_VA_VPP (object);
327 GST_OBJECT_LOCK (object);
329 case GST_VA_FILTER_PROP_DENOISE:
330 g_value_set_float (value, self->denoise);
332 case GST_VA_FILTER_PROP_SHARPEN:
333 g_value_set_float (value, self->sharpen);
335 case GST_VA_FILTER_PROP_SKINTONE:
336 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
337 g_value_set_boolean (value, self->skintone > 0);
339 g_value_set_float (value, self->skintone);
341 case GST_VA_FILTER_PROP_VIDEO_DIR:
342 g_value_set_enum (value, self->direction);
344 case GST_VA_FILTER_PROP_HUE:
345 g_value_set_float (value, self->hue);
347 case GST_VA_FILTER_PROP_SATURATION:
348 g_value_set_float (value, self->saturation);
350 case GST_VA_FILTER_PROP_BRIGHTNESS:
351 g_value_set_float (value, self->brightness);
353 case GST_VA_FILTER_PROP_CONTRAST:
354 g_value_set_float (value, self->contrast);
356 case GST_VA_FILTER_PROP_AUTO_SATURATION:
357 g_value_set_boolean (value, self->auto_saturation);
359 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
360 g_value_set_boolean (value, self->auto_brightness);
362 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
363 g_value_set_boolean (value, self->auto_contrast);
365 case GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH:
366 g_value_set_boolean (value, (self->op_flags & VPP_CONVERT_DUMMY));
368 case GST_VA_FILTER_PROP_ADD_BORDERS:
369 g_value_set_boolean (value, self->add_borders);
371 case GST_VA_FILTER_PROP_HDR:
372 g_value_set_boolean (value, self->hdr_mapping);
375 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378 GST_OBJECT_UNLOCK (object);
382 gst_va_vpp_propose_allocation (GstBaseTransform * trans,
383 GstQuery * decide_query, GstQuery * query)
385 /* if we are not passthrough, we can handle crop meta */
387 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
389 return GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
390 decide_query, query);
394 gst_va_vpp_update_properties (GstVaBaseTransform * btrans)
396 GstVaVpp *self = GST_VA_VPP (btrans);
398 gst_va_vpp_rebuild_filters (self);
399 _update_properties_unlocked (self);
403 _set_hdr_metadata (GstVaVpp * self, GstCaps * caps)
405 GstVideoMasteringDisplayInfo mdinfo;
406 GstVideoContentLightLevel llevel;
408 self->has_hdr_meta = FALSE;
410 if (gst_video_mastering_display_info_from_caps (&mdinfo, caps)) {
411 self->hdr_meta.display_primaries_x[0] = mdinfo.display_primaries[1].x;
412 self->hdr_meta.display_primaries_x[1] = mdinfo.display_primaries[2].x;
413 self->hdr_meta.display_primaries_x[2] = mdinfo.display_primaries[0].x;
415 self->hdr_meta.display_primaries_y[0] = mdinfo.display_primaries[1].y;
416 self->hdr_meta.display_primaries_y[1] = mdinfo.display_primaries[2].y;
417 self->hdr_meta.display_primaries_y[2] = mdinfo.display_primaries[0].y;
419 self->hdr_meta.white_point_x = mdinfo.white_point.x;
420 self->hdr_meta.white_point_y = mdinfo.white_point.y;
422 self->hdr_meta.max_display_mastering_luminance =
423 mdinfo.max_display_mastering_luminance;
424 self->hdr_meta.min_display_mastering_luminance =
425 mdinfo.min_display_mastering_luminance;
427 self->has_hdr_meta = TRUE;
431 if (gst_video_content_light_level_from_caps (&llevel, caps)) {
432 self->hdr_meta.max_content_light_level = llevel.max_content_light_level;
433 self->hdr_meta.max_pic_average_light_level =
434 llevel.max_frame_average_light_level;
436 self->has_hdr_meta = TRUE;
439 /* rebuild filters only if hdr mapping is enabled */
440 g_atomic_int_set (&self->rebuild_filters, self->hdr_mapping);
444 gst_va_vpp_set_info (GstVaBaseTransform * btrans, GstCaps * incaps,
445 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
447 GstVaVpp *self = GST_VA_VPP (btrans);
448 GstCapsFeatures *infeat, *outfeat;
449 gint from_dar_n, from_dar_d, to_dar_n, to_dar_d;
451 if (GST_VIDEO_INFO_INTERLACE_MODE (in_info) !=
452 GST_VIDEO_INFO_INTERLACE_MODE (out_info)) {
453 GST_ERROR_OBJECT (self, "input and output formats do not match");
457 /* calculate possible borders if display-aspect-ratio change */
459 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (in_info),
460 GST_VIDEO_INFO_HEIGHT (in_info), GST_VIDEO_INFO_PAR_N (in_info),
461 GST_VIDEO_INFO_PAR_D (in_info), &from_dar_n, &from_dar_d)) {
462 from_dar_n = from_dar_d = -1;
465 if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (out_info),
466 GST_VIDEO_INFO_HEIGHT (out_info), GST_VIDEO_INFO_PAR_N (out_info),
467 GST_VIDEO_INFO_PAR_D (out_info), &to_dar_n, &to_dar_d)) {
468 to_dar_n = to_dar_d = -1;
471 /* if video-orientation changes consider it for borders */
472 switch (gst_va_filter_get_orientation (btrans->filter)) {
473 case GST_VIDEO_ORIENTATION_90R:
474 case GST_VIDEO_ORIENTATION_90L:
475 case GST_VIDEO_ORIENTATION_UL_LR:
476 case GST_VIDEO_ORIENTATION_UR_LL:
477 SWAP (from_dar_n, from_dar_d);
483 self->borders_h = self->borders_w = 0;
484 if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
485 if (self->add_borders) {
486 gint n, d, to_h, to_w;
488 if (from_dar_n != -1 && from_dar_d != -1
489 && gst_util_fraction_multiply (from_dar_n, from_dar_d,
490 out_info->par_d, out_info->par_n, &n, &d)) {
491 to_h = gst_util_uint64_scale_int (out_info->width, d, n);
492 if (to_h <= out_info->height) {
493 self->borders_h = out_info->height - to_h;
496 to_w = gst_util_uint64_scale_int (out_info->height, n, d);
497 g_assert (to_w <= out_info->width);
499 self->borders_w = out_info->width - to_w;
502 GST_WARNING_OBJECT (self, "Can't calculate borders");
505 GST_WARNING_OBJECT (self, "Can't keep DAR!");
510 if (!gst_video_info_is_equal (in_info, out_info)) {
511 if (GST_VIDEO_INFO_FORMAT (in_info) != GST_VIDEO_INFO_FORMAT (out_info))
512 self->op_flags |= VPP_CONVERT_FORMAT;
514 self->op_flags &= ~VPP_CONVERT_FORMAT;
516 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
517 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
518 || self->borders_h > 0 || self->borders_w > 0)
519 self->op_flags |= VPP_CONVERT_SIZE;
521 self->op_flags &= ~VPP_CONVERT_SIZE;
523 self->op_flags &= ~VPP_CONVERT_FORMAT & ~VPP_CONVERT_SIZE;
526 infeat = gst_caps_get_features (incaps, 0);
527 outfeat = gst_caps_get_features (outcaps, 0);
528 if (!gst_caps_features_is_equal (infeat, outfeat))
529 self->op_flags |= VPP_CONVERT_FEATURE;
531 self->op_flags &= ~VPP_CONVERT_FEATURE;
533 if (gst_va_filter_set_video_info (btrans->filter, in_info, out_info)) {
534 _set_hdr_metadata (self, incaps);
535 gst_va_vpp_update_passthrough (self, FALSE);
542 static inline gboolean
543 _get_filter_value (GstVaVpp * self, VAProcFilterType type, gfloat * value)
547 GST_OBJECT_LOCK (self);
549 case VAProcFilterNoiseReduction:
550 *value = self->denoise;
552 case VAProcFilterSharpening:
553 *value = self->sharpen;
555 case VAProcFilterSkinToneEnhancement:
556 *value = self->skintone;
562 GST_OBJECT_UNLOCK (self);
567 static inline gboolean
568 _add_filter_buffer (GstVaVpp * self, VAProcFilterType type,
569 const VAProcFilterCap * cap)
571 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
572 VAProcFilterParameterBuffer param;
575 if (!_get_filter_value (self, type, &value))
577 if (value == cap->range.default_value)
581 param = (VAProcFilterParameterBuffer) {
587 return gst_va_filter_add_filter_buffer (btrans->filter, ¶m,
591 static inline gboolean
592 _get_filter_cb_value (GstVaVpp * self, VAProcColorBalanceType type,
597 GST_OBJECT_LOCK (self);
599 case VAProcColorBalanceHue:
602 case VAProcColorBalanceSaturation:
603 *value = self->saturation;
605 case VAProcColorBalanceBrightness:
606 *value = self->brightness;
608 case VAProcColorBalanceContrast:
609 *value = self->contrast;
611 case VAProcColorBalanceAutoSaturation:
612 *value = self->auto_saturation;
614 case VAProcColorBalanceAutoBrightness:
615 *value = self->auto_brightness;
617 case VAProcColorBalanceAutoContrast:
618 *value = self->auto_contrast;
624 GST_OBJECT_UNLOCK (self);
629 static inline gboolean
630 _add_filter_cb_buffer (GstVaVpp * self,
631 const VAProcFilterCapColorBalance * caps, guint num_caps)
633 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
634 VAProcFilterParameterBufferColorBalance param[VAProcColorBalanceCount] =
640 for (i = 0; i < num_caps && i < VAProcColorBalanceCount; i++) {
641 if (!_get_filter_cb_value (self, caps[i].type, &value))
643 if (value == caps[i].range.default_value)
647 param[c++] = (VAProcFilterParameterBufferColorBalance) {
648 .type = VAProcFilterColorBalance,
649 .attrib = caps[i].type,
656 return gst_va_filter_add_filter_buffer (btrans->filter, param,
662 static inline gboolean
663 _add_filter_hdr_buffer (GstVaVpp * self,
664 const VAProcFilterCapHighDynamicRange * caps)
666 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
668 VAProcFilterParameterBufferHDRToneMapping params = {
669 .type = VAProcFilterHighDynamicRangeToneMapping,
671 .metadata_type = VAProcHighDynamicRangeMetadataHDR10,
672 .metadata = &self->hdr_meta,
673 .metadata_size = sizeof (self->hdr_meta),
678 /* if not has hdr meta, it may try later again */
679 if (!(self->has_hdr_meta && self->hdr_mapping))
682 if (!(caps && caps->metadata_type == VAProcHighDynamicRangeMetadataHDR10
683 && (caps->caps_flag & VA_TONE_MAPPING_HDR_TO_SDR)))
686 if (self->op_flags & VPP_CONVERT_FORMAT) {
687 GST_WARNING_OBJECT (self, "Cannot apply HDR with color conversion");
691 return gst_va_filter_add_filter_buffer (btrans->filter, ¶ms,
695 self->hdr_mapping = FALSE;
700 _build_filters (GstVaVpp * self)
702 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
703 static const VAProcFilterType filter_types[] = { VAProcFilterNoiseReduction,
704 VAProcFilterSharpening, VAProcFilterSkinToneEnhancement,
705 VAProcFilterColorBalance, VAProcFilterHighDynamicRangeToneMapping,
708 gboolean apply = FALSE;
710 for (i = 0; i < G_N_ELEMENTS (filter_types); i++) {
711 const gpointer caps = gst_va_filter_get_filter_caps (btrans->filter,
712 filter_types[i], &num_caps);
716 switch (filter_types[i]) {
717 case VAProcFilterNoiseReduction:
718 apply |= _add_filter_buffer (self, filter_types[i], caps);
720 case VAProcFilterSharpening:
721 apply |= _add_filter_buffer (self, filter_types[i], caps);
723 case VAProcFilterSkinToneEnhancement:
724 apply |= _add_filter_buffer (self, filter_types[i], caps);
726 case VAProcFilterColorBalance:
727 apply |= _add_filter_cb_buffer (self, caps, num_caps);
729 case VAProcFilterHighDynamicRangeToneMapping:
730 apply |= _add_filter_hdr_buffer (self, caps);
736 GST_OBJECT_LOCK (self);
738 self->op_flags |= VPP_CONVERT_FILTERS;
740 self->op_flags &= ~VPP_CONVERT_FILTERS;
741 GST_OBJECT_UNLOCK (self);
745 gst_va_vpp_rebuild_filters (GstVaVpp * self)
747 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
749 if (!g_atomic_int_get (&self->rebuild_filters))
752 gst_va_filter_drop_filter_buffers (btrans->filter);
753 _build_filters (self);
754 g_atomic_int_set (&self->rebuild_filters, FALSE);
758 gst_va_vpp_before_transform (GstBaseTransform * trans, GstBuffer * inbuf)
760 GstVaVpp *self = GST_VA_VPP (trans);
761 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
762 GstClockTime ts, stream_time;
763 gboolean is_passthrough;
765 ts = GST_BUFFER_TIMESTAMP (inbuf);
767 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, ts);
769 GST_TRACE_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
771 if (GST_CLOCK_TIME_IS_VALID (stream_time))
772 gst_object_sync_values (GST_OBJECT (self), stream_time);
774 gst_va_vpp_rebuild_filters (self);
775 gst_va_vpp_update_passthrough (self, TRUE);
777 /* cropping is only enabled if vapostproc is not in passthrough */
778 is_passthrough = gst_base_transform_is_passthrough (trans);
779 GST_OBJECT_LOCK (self);
780 if (!is_passthrough && gst_buffer_get_video_crop_meta (inbuf)) {
781 self->op_flags |= VPP_CONVERT_CROP;
783 self->op_flags &= ~VPP_CONVERT_CROP;
785 gst_va_filter_enable_cropping (btrans->filter,
786 (self->op_flags & VPP_CONVERT_CROP));
787 GST_OBJECT_UNLOCK (self);
791 gst_va_vpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
794 GstVaVpp *self = GST_VA_VPP (trans);
795 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
796 GstBuffer *buf = NULL;
797 GstFlowReturn res = GST_FLOW_OK;
798 GstVaSample src, dst;
800 if (G_UNLIKELY (!btrans->negotiated))
803 res = gst_va_base_transform_import_buffer (btrans, inbuf, &buf);
804 if (res != GST_FLOW_OK)
808 src = (GstVaSample) {
810 .flags = gst_va_buffer_get_surface_flags (buf, &btrans->in_info),
813 dst = (GstVaSample) {
815 .borders_h = self->borders_h,
816 .borders_w = self->borders_w,
817 .flags = gst_va_buffer_get_surface_flags (outbuf, &btrans->out_info),
821 if (!gst_va_filter_process (btrans->filter, &src, &dst)) {
822 gst_buffer_set_flags (outbuf, GST_BUFFER_FLAG_CORRUPTED);
825 gst_buffer_unref (buf);
832 GST_ELEMENT_ERROR (self, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
833 return GST_FLOW_NOT_NEGOTIATED;
838 gst_va_vpp_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
839 GstMeta * meta, GstBuffer * outbuf)
841 GstVaVpp *self = GST_VA_VPP (trans);
842 const GstMetaInfo *info = meta->info;
843 const gchar *const *tags;
845 tags = gst_meta_api_type_get_tags (info->api);
850 /* don't copy colorspace/size/orientation specific metadata */
851 if ((self->op_flags & VPP_CONVERT_FORMAT)
852 && gst_meta_api_type_has_tag (info->api, META_TAG_COLORSPACE))
854 else if ((self->op_flags & (VPP_CONVERT_SIZE | VPP_CONVERT_CROP))
855 && gst_meta_api_type_has_tag (info->api, META_TAG_SIZE))
857 else if ((self->op_flags & VPP_CONVERT_DIRECTION)
858 && gst_meta_api_type_has_tag (info->api, META_TAG_ORIENTATION))
860 else if (gst_meta_api_type_has_tag (info->api, META_TAG_VIDEO))
866 /* In structures with supported caps features it's:
867 * + Rangified resolution size.
868 * + Rangified "pixel-aspect-ratio" if present.
869 * + Removed "format", "colorimetry", "chroma-site"
871 * Structures with unsupported caps features are copied as-is.
874 gst_va_vpp_caps_remove_fields (GstCaps * caps)
877 GstStructure *structure;
878 GstCapsFeatures *features;
881 ret = gst_caps_new_empty ();
883 n = gst_caps_get_size (caps);
884 for (i = 0; i < n; i++) {
885 structure = gst_caps_get_structure (caps, i);
886 features = gst_caps_get_features (caps, i);
888 /* If this is already expressed by the existing caps
889 * skip this structure */
890 if (i > 0 && gst_caps_is_subset_structure_full (ret, structure, features))
893 structure = gst_structure_copy (structure);
895 m = gst_caps_features_get_size (features);
896 for (j = 0; j < m; j++) {
897 const gchar *feature = gst_caps_features_get_nth (features, j);
899 if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0
900 || g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_DMABUF) == 0
901 || g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_VA) == 0) {
903 /* rangify frame size */
904 gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
905 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
907 /* if pixel aspect ratio, make a range of it */
908 if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
909 gst_structure_set (structure, "pixel-aspect-ratio",
910 GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
913 /* remove format-related fields */
914 gst_structure_remove_fields (structure, "format", "colorimetry",
915 "chroma-site", NULL);
921 gst_caps_append_structure_full (ret, structure,
922 gst_caps_features_copy (features));
928 /* Returns all structures in @caps without @feature_name but now with
931 gst_va_vpp_complete_caps_features (const GstCaps * caps,
932 const gchar * feature_name)
937 tmp = gst_caps_new_empty ();
939 n = gst_caps_get_size (caps);
940 for (i = 0; i < n; i++) {
941 GstCapsFeatures *features, *orig_features;
942 GstStructure *s = gst_caps_get_structure (caps, i);
943 gboolean contained = FALSE;
945 orig_features = gst_caps_get_features (caps, i);
946 features = gst_caps_features_new (feature_name, NULL);
948 m = gst_caps_features_get_size (orig_features);
949 for (j = 0; j < m; j++) {
950 const gchar *feature = gst_caps_features_get_nth (orig_features, j);
952 /* if we already have the features */
953 if (gst_caps_features_contains (features, feature)) {
959 if (!contained && !gst_caps_is_subset_structure_full (tmp, s, features))
960 gst_caps_append_structure_full (tmp, gst_structure_copy (s), features);
962 gst_caps_features_free (features);
969 gst_va_vpp_transform_caps (GstBaseTransform * trans, GstPadDirection direction,
970 GstCaps * caps, GstCaps * filter)
972 GstVaVpp *self = GST_VA_VPP (trans);
973 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
974 GstCaps *ret, *tmp, *filter_caps;
976 GST_DEBUG_OBJECT (self,
977 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
978 (direction == GST_PAD_SINK) ? "sink" : "src");
980 filter_caps = gst_va_base_transform_get_filter_caps (btrans);
981 if (filter_caps && !gst_caps_can_intersect (caps, filter_caps)) {
982 ret = gst_caps_ref (caps);
986 ret = gst_va_vpp_caps_remove_fields (caps);
988 tmp = gst_va_vpp_complete_caps_features (ret, GST_CAPS_FEATURE_MEMORY_VA);
989 if (!gst_caps_is_subset (tmp, ret))
990 gst_caps_append (ret, tmp);
992 tmp = gst_va_vpp_complete_caps_features (ret, GST_CAPS_FEATURE_MEMORY_DMABUF);
993 if (!gst_caps_is_subset (tmp, ret))
994 gst_caps_append (ret, tmp);
996 tmp = gst_va_vpp_complete_caps_features (ret,
997 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
998 if (!gst_caps_is_subset (tmp, ret))
999 gst_caps_append (ret, tmp);
1003 GstCaps *intersection;
1006 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1007 gst_caps_unref (ret);
1011 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
1017 * This is an incomplete matrix of in formats and a score for the preferred output
1020 * out: RGB24 RGB16 ARGB AYUV YUV444 YUV422 YUV420 YUV411 YUV410 PAL GRAY
1022 * RGB24 0 2 1 2 2 3 4 5 6 7 8
1023 * RGB16 1 0 1 2 2 3 4 5 6 7 8
1024 * ARGB 2 3 0 1 4 5 6 7 8 9 10
1025 * AYUV 3 4 1 0 2 5 6 7 8 9 10
1026 * YUV444 2 4 3 1 0 5 6 7 8 9 10
1027 * YUV422 3 5 4 2 1 0 6 7 8 9 10
1028 * YUV420 4 6 5 3 2 1 0 7 8 9 10
1029 * YUV411 4 6 5 3 2 1 7 0 8 9 10
1030 * YUV410 6 8 7 5 4 3 2 1 0 9 10
1031 * PAL 1 3 2 6 4 6 7 8 9 0 10
1032 * GRAY 1 4 3 2 1 5 6 7 8 9 0
1034 * PAL or GRAY are never preferred, if we can we would convert to PAL instead
1036 * less subsampling is preferred and if any, preferably horizontal
1037 * We would like to keep the alpha, even if we would need to to colorspace conversion
1040 #define SCORE_FORMAT_CHANGE 1
1041 #define SCORE_DEPTH_CHANGE 1
1042 #define SCORE_ALPHA_CHANGE 1
1043 #define SCORE_CHROMA_W_CHANGE 1
1044 #define SCORE_CHROMA_H_CHANGE 1
1045 #define SCORE_PALETTE_CHANGE 1
1047 #define SCORE_COLORSPACE_LOSS 2 /* RGB <-> YUV */
1048 #define SCORE_DEPTH_LOSS 4 /* change bit depth */
1049 #define SCORE_ALPHA_LOSS 8 /* lose the alpha channel */
1050 #define SCORE_CHROMA_W_LOSS 16 /* vertical subsample */
1051 #define SCORE_CHROMA_H_LOSS 32 /* horizontal subsample */
1052 #define SCORE_PALETTE_LOSS 64 /* convert to palette format */
1053 #define SCORE_COLOR_LOSS 128 /* convert to GRAY */
1055 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
1056 GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
1057 #define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
1058 #define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
1060 /* calculate how much loss a conversion would be */
1062 score_value (GstVaVpp * self, const GstVideoFormatInfo * in_info,
1063 GstVideoFormat format, gint * min_loss,
1064 const GstVideoFormatInfo ** out_info)
1066 const GstVideoFormatInfo *t_info;
1067 GstVideoFormatFlags in_flags, t_flags;
1070 t_info = gst_video_format_get_info (format);
1071 if (!t_info || t_info->format == GST_VIDEO_FORMAT_UNKNOWN)
1074 /* accept input format immediately without loss */
1075 if (in_info == t_info) {
1081 loss = SCORE_FORMAT_CHANGE;
1083 in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
1084 in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1085 in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1086 in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1088 t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
1089 t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1090 t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1091 t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1093 if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
1094 loss += SCORE_PALETTE_CHANGE;
1095 if (t_flags & PALETTE_MASK)
1096 loss += SCORE_PALETTE_LOSS;
1099 if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
1100 loss += SCORE_COLORSPACE_LOSS;
1101 if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
1102 loss += SCORE_COLOR_LOSS;
1105 if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
1106 loss += SCORE_ALPHA_CHANGE;
1107 if (in_flags & ALPHA_MASK)
1108 loss += SCORE_ALPHA_LOSS;
1111 if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
1112 loss += SCORE_CHROMA_H_CHANGE;
1113 if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
1114 loss += SCORE_CHROMA_H_LOSS;
1116 if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
1117 loss += SCORE_CHROMA_W_CHANGE;
1118 if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
1119 loss += SCORE_CHROMA_W_LOSS;
1122 if ((in_info->bits) != (t_info->bits)) {
1123 loss += SCORE_DEPTH_CHANGE;
1124 if ((in_info->bits) > (t_info->bits))
1125 loss += SCORE_DEPTH_LOSS;
1128 GST_DEBUG_OBJECT (self, "score %s -> %s = %d",
1129 GST_VIDEO_FORMAT_INFO_NAME (in_info),
1130 GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
1132 if (loss < *min_loss) {
1133 GST_DEBUG_OBJECT (self, "found new best %d", loss);
1143 gst_va_vpp_fixate_format (GstVaVpp * self, GstCaps * caps, GstCaps * result)
1145 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1147 const gchar *in_format;
1148 const GstVideoFormatInfo *in_info, *out_info = NULL;
1149 GstCapsFeatures *features;
1151 gint min_loss = G_MAXINT;
1152 guint i, best_i, capslen;
1154 ins = gst_caps_get_structure (caps, 0);
1155 in_format = gst_structure_get_string (ins, "format");
1159 GST_DEBUG_OBJECT (self, "source format %s", in_format);
1162 gst_video_format_get_info (gst_video_format_from_string (in_format));
1167 capslen = gst_caps_get_size (result);
1168 GST_DEBUG_OBJECT (self, "iterate %d structures", capslen);
1169 for (i = 0; i < capslen; i++) {
1170 GstStructure *tests;
1171 const GValue *format;
1173 tests = gst_caps_get_structure (result, i);
1174 format = gst_structure_get_value (tests, "format");
1175 /* should not happen */
1179 features = gst_caps_get_features (result, i);
1181 if (GST_VALUE_HOLDS_LIST (format)) {
1184 len = gst_value_list_get_size (format);
1185 GST_DEBUG_OBJECT (self, "have %d formats", len);
1186 for (j = 0; j < len; j++) {
1189 val = gst_value_list_get_value (format, j);
1190 if (G_VALUE_HOLDS_STRING (val)) {
1191 fmt = gst_video_format_from_string (g_value_get_string (val));
1192 if (!gst_va_filter_has_video_format (btrans->filter, fmt, features))
1194 if (score_value (self, in_info, fmt, &min_loss, &out_info))
1200 } else if (G_VALUE_HOLDS_STRING (format)) {
1201 fmt = gst_video_format_from_string (g_value_get_string (format));
1202 if (!gst_va_filter_has_video_format (btrans->filter, fmt, features))
1204 if (score_value (self, in_info, fmt, &min_loss, &out_info))
1216 features = gst_caps_features_copy (gst_caps_get_features (result, best_i));
1217 out = gst_structure_copy (gst_caps_get_structure (result, best_i));
1218 gst_structure_set (out, "format", G_TYPE_STRING,
1219 GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
1220 ret = gst_caps_new_full (out, NULL);
1221 gst_caps_set_features_simple (ret, features);
1229 gst_va_vpp_fixate_size (GstVaVpp * self, GstPadDirection direction,
1230 GstCaps * caps, GstCaps * othercaps)
1232 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1233 GstStructure *ins, *outs;
1234 const GValue *from_par, *to_par;
1235 GValue fpar = { 0, };
1236 GValue tpar = { 0, };
1238 ins = gst_caps_get_structure (caps, 0);
1239 outs = gst_caps_get_structure (othercaps, 0);
1241 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1242 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1244 /* If we're fixating from the sinkpad we always set the PAR and
1245 * assume that missing PAR on the sinkpad means 1/1 and
1246 * missing PAR on the srcpad means undefined
1248 if (direction == GST_PAD_SINK) {
1250 g_value_init (&fpar, GST_TYPE_FRACTION);
1251 gst_value_set_fraction (&fpar, 1, 1);
1255 g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
1256 gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
1261 g_value_init (&tpar, GST_TYPE_FRACTION);
1262 gst_value_set_fraction (&tpar, 1, 1);
1265 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1269 g_value_init (&fpar, GST_TYPE_FRACTION);
1270 gst_value_set_fraction (&fpar, 1, 1);
1275 /* we have both PAR but they might not be fixated */
1277 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1279 gint from_dar_n, from_dar_d;
1282 /* from_par should be fixed */
1283 g_return_if_fail (gst_value_is_fixed (from_par));
1285 from_par_n = gst_value_get_fraction_numerator (from_par);
1286 from_par_d = gst_value_get_fraction_denominator (from_par);
1288 gst_structure_get_int (ins, "width", &from_w);
1289 gst_structure_get_int (ins, "height", &from_h);
1291 gst_structure_get_int (outs, "width", &w);
1292 gst_structure_get_int (outs, "height", &h);
1294 /* if video-orientation changes */
1295 switch (gst_va_filter_get_orientation (btrans->filter)) {
1296 case GST_VIDEO_ORIENTATION_90R:
1297 case GST_VIDEO_ORIENTATION_90L:
1298 case GST_VIDEO_ORIENTATION_UL_LR:
1299 case GST_VIDEO_ORIENTATION_UR_LL:
1300 SWAP (from_w, from_h);
1301 SWAP (from_par_n, from_par_d);
1307 /* if both width and height are already fixed, we can't do anything
1308 * about it anymore */
1312 GST_DEBUG_OBJECT (self, "dimensions already set to %dx%d, not fixating",
1314 if (!gst_value_is_fixed (to_par)) {
1315 if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
1316 from_par_n, from_par_d, w, h)) {
1317 GST_DEBUG_OBJECT (self, "fixating to_par to %dx%d", n, d);
1318 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1319 gst_structure_fixate_field_nearest_fraction (outs,
1320 "pixel-aspect-ratio", n, d);
1322 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1329 /* Calculate input DAR */
1330 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1331 &from_dar_n, &from_dar_d)) {
1332 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1333 ("Error calculating the output scaled size - integer overflow"));
1337 GST_DEBUG_OBJECT (self, "Input DAR is %d/%d", from_dar_n, from_dar_d);
1339 /* If either width or height are fixed there's not much we
1340 * can do either except choosing a height or width and PAR
1341 * that matches the DAR as good as possible
1345 gint set_w, set_par_n, set_par_d;
1347 GST_DEBUG_OBJECT (self, "height is fixed (%d)", h);
1349 /* If the PAR is fixed too, there's not much to do
1350 * except choosing the width that is nearest to the
1351 * width with the same DAR */
1352 if (gst_value_is_fixed (to_par)) {
1353 to_par_n = gst_value_get_fraction_numerator (to_par);
1354 to_par_d = gst_value_get_fraction_denominator (to_par);
1356 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1358 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1359 to_par_n, &num, &den)) {
1360 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1361 ("Error calculating the output scaled size - integer overflow"));
1365 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1366 gst_structure_fixate_field_nearest_int (outs, "width", w);
1371 /* The PAR is not fixed and it's quite likely that we can set
1372 * an arbitrary PAR. */
1374 /* Check if we can keep the input width */
1375 tmp = gst_structure_copy (outs);
1376 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1377 gst_structure_get_int (tmp, "width", &set_w);
1379 /* Might have failed but try to keep the DAR nonetheless by
1380 * adjusting the PAR */
1381 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
1382 &to_par_n, &to_par_d)) {
1383 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1384 ("Error calculating the output scaled size - integer overflow"));
1385 gst_structure_free (tmp);
1389 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1390 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1391 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1392 to_par_n, to_par_d);
1393 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1395 gst_structure_free (tmp);
1397 /* Check if the adjusted PAR is accepted */
1398 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1399 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1400 set_par_n != set_par_d)
1401 gst_structure_set (outs, "width", G_TYPE_INT, set_w,
1402 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1407 /* Otherwise scale the width to the new PAR and check if the
1408 * adjusted with is accepted. If all that fails we can't keep
1410 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1411 set_par_n, &num, &den)) {
1412 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1413 ("Error calculating the output scaled size - integer overflow"));
1417 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1418 gst_structure_fixate_field_nearest_int (outs, "width", w);
1419 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1420 set_par_n != set_par_d)
1421 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1422 set_par_n, set_par_d, NULL);
1427 gint set_h, set_par_n, set_par_d;
1429 GST_DEBUG_OBJECT (self, "width is fixed (%d)", w);
1431 /* If the PAR is fixed too, there's not much to do
1432 * except choosing the height that is nearest to the
1433 * height with the same DAR */
1434 if (gst_value_is_fixed (to_par)) {
1435 to_par_n = gst_value_get_fraction_numerator (to_par);
1436 to_par_d = gst_value_get_fraction_denominator (to_par);
1438 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1440 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1441 to_par_n, &num, &den)) {
1442 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1443 ("Error calculating the output scaled size - integer overflow"));
1447 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1448 gst_structure_fixate_field_nearest_int (outs, "height", h);
1453 /* The PAR is not fixed and it's quite likely that we can set
1454 * an arbitrary PAR. */
1456 /* Check if we can keep the input height */
1457 tmp = gst_structure_copy (outs);
1458 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1459 gst_structure_get_int (tmp, "height", &set_h);
1461 /* Might have failed but try to keep the DAR nonetheless by
1462 * adjusting the PAR */
1463 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
1464 &to_par_n, &to_par_d)) {
1465 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1466 ("Error calculating the output scaled size - integer overflow"));
1467 gst_structure_free (tmp);
1470 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1471 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1472 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1473 to_par_n, to_par_d);
1474 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1476 gst_structure_free (tmp);
1478 /* Check if the adjusted PAR is accepted */
1479 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1480 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1481 set_par_n != set_par_d)
1482 gst_structure_set (outs, "height", G_TYPE_INT, set_h,
1483 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1488 /* Otherwise scale the height to the new PAR and check if the
1489 * adjusted with is accepted. If all that fails we can't keep
1491 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1492 set_par_n, &num, &den)) {
1493 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1494 ("Error calculating the output scale sized - integer overflow"));
1498 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1499 gst_structure_fixate_field_nearest_int (outs, "height", h);
1500 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1501 set_par_n != set_par_d)
1502 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1503 set_par_n, set_par_d, NULL);
1506 } else if (gst_value_is_fixed (to_par)) {
1508 gint set_h, set_w, f_h, f_w;
1510 to_par_n = gst_value_get_fraction_numerator (to_par);
1511 to_par_d = gst_value_get_fraction_denominator (to_par);
1513 /* Calculate scale factor for the PAR change */
1514 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1515 to_par_d, &num, &den)) {
1516 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1517 ("Error calculating the output scaled size - integer overflow"));
1521 /* Try to keep the input height (because of interlacing) */
1522 tmp = gst_structure_copy (outs);
1523 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1524 gst_structure_get_int (tmp, "height", &set_h);
1526 /* This might have failed but try to scale the width
1527 * to keep the DAR nonetheless */
1528 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1529 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1530 gst_structure_get_int (tmp, "width", &set_w);
1531 gst_structure_free (tmp);
1533 /* We kept the DAR and the height is nearest to the original height */
1535 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1536 G_TYPE_INT, set_h, NULL);
1543 /* If the former failed, try to keep the input width at least */
1544 tmp = gst_structure_copy (outs);
1545 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1546 gst_structure_get_int (tmp, "width", &set_w);
1548 /* This might have failed but try to scale the width
1549 * to keep the DAR nonetheless */
1550 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1551 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1552 gst_structure_get_int (tmp, "height", &set_h);
1553 gst_structure_free (tmp);
1555 /* We kept the DAR and the width is nearest to the original width */
1557 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1558 G_TYPE_INT, set_h, NULL);
1562 /* If all this failed, keep the dimensions with the DAR that was closest
1563 * to the correct DAR. This changes the DAR but there's not much else to
1566 if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
1570 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1575 gint set_h, set_w, set_par_n, set_par_d, tmp2;
1577 /* width, height and PAR are not fixed but passthrough is not possible */
1579 /* First try to keep the height and width as good as possible
1581 tmp = gst_structure_copy (outs);
1582 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1583 gst_structure_get_int (tmp, "height", &set_h);
1584 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1585 gst_structure_get_int (tmp, "width", &set_w);
1587 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1588 &to_par_n, &to_par_d)) {
1589 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1590 ("Error calculating the output scaled size - integer overflow"));
1591 gst_structure_free (tmp);
1595 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1596 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1597 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1598 to_par_n, to_par_d);
1599 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1601 gst_structure_free (tmp);
1603 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1604 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1605 G_TYPE_INT, set_h, NULL);
1607 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1608 set_par_n != set_par_d)
1609 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1610 set_par_n, set_par_d, NULL);
1614 /* Otherwise try to scale width to keep the DAR with the set
1616 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1617 set_par_n, &num, &den)) {
1618 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1619 ("Error calculating the output scaled size - integer overflow"));
1623 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
1624 tmp = gst_structure_copy (outs);
1625 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1626 gst_structure_get_int (tmp, "width", &tmp2);
1627 gst_structure_free (tmp);
1630 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1631 G_TYPE_INT, set_h, NULL);
1632 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1633 set_par_n != set_par_d)
1634 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1635 set_par_n, set_par_d, NULL);
1639 /* ... or try the same with the height */
1640 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
1641 tmp = gst_structure_copy (outs);
1642 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1643 gst_structure_get_int (tmp, "height", &tmp2);
1644 gst_structure_free (tmp);
1647 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1648 G_TYPE_INT, tmp2, NULL);
1649 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1650 set_par_n != set_par_d)
1651 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1652 set_par_n, set_par_d, NULL);
1656 /* If all fails we can't keep the DAR and take the nearest values
1657 * for everything from the first try */
1658 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1659 G_TYPE_INT, set_h, NULL);
1660 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1661 set_par_n != set_par_d)
1662 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1663 set_par_n, set_par_d, NULL);
1668 if (from_par == &fpar)
1669 g_value_unset (&fpar);
1670 if (to_par == &tpar)
1671 g_value_unset (&tpar);
1675 subsampling_unchanged (GstVideoInfo * in_info, GstVideoInfo * out_info)
1678 const GstVideoFormatInfo *in_format, *out_format;
1680 if (GST_VIDEO_INFO_N_COMPONENTS (in_info) !=
1681 GST_VIDEO_INFO_N_COMPONENTS (out_info))
1684 in_format = in_info->finfo;
1685 out_format = out_info->finfo;
1687 for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (in_info); i++) {
1688 if (GST_VIDEO_FORMAT_INFO_W_SUB (in_format,
1689 i) != GST_VIDEO_FORMAT_INFO_W_SUB (out_format, i))
1691 if (GST_VIDEO_FORMAT_INFO_H_SUB (in_format,
1692 i) != GST_VIDEO_FORMAT_INFO_H_SUB (out_format, i))
1700 transfer_colorimetry_from_input (GstVaVpp * self, GstCaps * in_caps,
1703 GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
1704 GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
1705 gboolean have_colorimetry =
1706 gst_structure_has_field (out_caps_s, "colorimetry");
1707 gboolean have_chroma_site =
1708 gst_structure_has_field (out_caps_s, "chroma-site");
1710 /* If the output already has colorimetry and chroma-site, stop,
1711 * otherwise try and transfer what we can from the input caps */
1712 if (have_colorimetry && have_chroma_site)
1716 GstVideoInfo in_info, out_info;
1717 const GValue *in_colorimetry =
1718 gst_structure_get_value (in_caps_s, "colorimetry");
1720 if (!gst_video_info_from_caps (&in_info, in_caps)) {
1721 GST_WARNING_OBJECT (self,
1722 "Failed to convert sink pad caps to video info");
1725 if (!gst_video_info_from_caps (&out_info, out_caps)) {
1726 GST_WARNING_OBJECT (self, "Failed to convert src pad caps to video info");
1730 if (!have_colorimetry && in_colorimetry != NULL) {
1731 if ((GST_VIDEO_INFO_IS_YUV (&out_info)
1732 && GST_VIDEO_INFO_IS_YUV (&in_info))
1733 || (GST_VIDEO_INFO_IS_RGB (&out_info)
1734 && GST_VIDEO_INFO_IS_RGB (&in_info))
1735 || (GST_VIDEO_INFO_IS_GRAY (&out_info)
1736 && GST_VIDEO_INFO_IS_GRAY (&in_info))) {
1737 /* Can transfer the colorimetry intact from the input if it has it */
1738 gst_structure_set_value (out_caps_s, "colorimetry", in_colorimetry);
1740 gchar *colorimetry_str;
1742 /* Changing between YUV/RGB - forward primaries and transfer function, but use
1743 * default range and matrix.
1744 * the primaries is used for conversion between RGB and XYZ (CIE 1931 coordinate).
1745 * the transfer function could be another reference (e.g., HDR)
1747 out_info.colorimetry.primaries = in_info.colorimetry.primaries;
1748 out_info.colorimetry.transfer = in_info.colorimetry.transfer;
1751 gst_video_colorimetry_to_string (&out_info.colorimetry);
1752 gst_caps_set_simple (out_caps, "colorimetry", G_TYPE_STRING,
1753 colorimetry_str, NULL);
1754 g_free (colorimetry_str);
1758 /* Only YUV output needs chroma-site. If the input was also YUV and had the same chroma
1759 * subsampling, transfer the siting. If the sub-sampling is changing, then the planes get
1760 * scaled anyway so there's no real reason to prefer the input siting. */
1761 if (!have_chroma_site && GST_VIDEO_INFO_IS_YUV (&out_info)) {
1762 if (GST_VIDEO_INFO_IS_YUV (&in_info)) {
1763 const GValue *in_chroma_site =
1764 gst_structure_get_value (in_caps_s, "chroma-site");
1765 if (in_chroma_site != NULL
1766 && subsampling_unchanged (&in_info, &out_info))
1767 gst_structure_set_value (out_caps_s, "chroma-site", in_chroma_site);
1774 copy_misc_fields_from_input (GstCaps * in_caps, GstCaps * out_caps)
1776 const gchar *fields[] = { "interlace-mode", "field-order", "multiview-mode",
1777 "multiview-flags", "framerate"
1779 GstStructure *out_caps_s = gst_caps_get_structure (out_caps, 0);
1780 GstStructure *in_caps_s = gst_caps_get_structure (in_caps, 0);
1783 for (i = 0; i < G_N_ELEMENTS (fields); i++) {
1784 const GValue *in_field = gst_structure_get_value (in_caps_s, fields[i]);
1785 const GValue *out_field = gst_structure_get_value (out_caps_s, fields[i]);
1787 if (out_field && gst_value_is_fixed (out_field))
1791 gst_structure_set_value (out_caps_s, fields[i], in_field);
1796 remove_hdr_fields (GstCaps * caps)
1798 GstStructure *s = gst_caps_get_structure (caps, 0);
1800 gst_structure_remove_fields (s, "mastering-display-info",
1801 "content-light-level", "hdr-format", NULL);
1805 gst_va_vpp_fixate_caps (GstBaseTransform * trans, GstPadDirection direction,
1806 GstCaps * caps, GstCaps * othercaps)
1808 GstVaVpp *self = GST_VA_VPP (trans);
1811 GST_DEBUG_OBJECT (self,
1812 "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
1813 GST_PTR_FORMAT, othercaps, caps);
1815 /* will iterate in all structures to find one with "best color" */
1816 result = gst_va_vpp_fixate_format (self, caps, othercaps);
1820 gst_clear_caps (&othercaps);
1822 gst_va_vpp_fixate_size (self, direction, caps, result);
1824 /* some fields might be lost while feature caps conversion */
1825 copy_misc_fields_from_input (caps, result);
1827 /* fixate remaining fields */
1828 result = gst_caps_fixate (result);
1830 if (direction == GST_PAD_SINK) {
1831 if (gst_caps_is_subset (caps, result)) {
1832 gst_caps_replace (&result, caps);
1834 /* Try and preserve input colorimetry / chroma information */
1835 transfer_colorimetry_from_input (self, caps, result);
1836 if (self->hdr_mapping)
1837 remove_hdr_fields (result);
1841 GST_DEBUG_OBJECT (self, "fixated othercaps to %" GST_PTR_FORMAT, result);
1847 _get_scale_factor (GstVaVpp * self, gdouble * w_factor, gdouble * h_factor)
1849 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
1850 gdouble w = GST_VIDEO_INFO_WIDTH (&btrans->in_info);
1851 gdouble h = GST_VIDEO_INFO_HEIGHT (&btrans->in_info);
1853 switch (self->direction) {
1854 case GST_VIDEO_ORIENTATION_90R:
1855 case GST_VIDEO_ORIENTATION_90L:
1856 case GST_VIDEO_ORIENTATION_UR_LL:
1857 case GST_VIDEO_ORIENTATION_UL_LR:{
1867 *w_factor = GST_VIDEO_INFO_WIDTH (&btrans->out_info);
1870 *h_factor = GST_VIDEO_INFO_HEIGHT (&btrans->out_info);
1875 gst_va_vpp_src_event (GstBaseTransform * trans, GstEvent * event)
1877 GstVaVpp *self = GST_VA_VPP (trans);
1878 GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (trans);
1879 GstStructure *structure;
1880 const GstVideoInfo *in_info = &btrans->in_info, *out_info = &btrans->out_info;
1881 gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
1884 GST_TRACE_OBJECT (self, "handling %s event", GST_EVENT_TYPE_NAME (event));
1886 switch (GST_EVENT_TYPE (event)) {
1887 case GST_EVENT_NAVIGATION:
1888 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
1889 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
1890 || gst_va_filter_get_orientation (btrans->filter) !=
1891 GST_VIDEO_ORIENTATION_IDENTITY) {
1894 GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
1896 structure = (GstStructure *) gst_event_get_structure (event);
1897 if (!gst_structure_get_double (structure, "pointer_x", &x)
1898 || !gst_structure_get_double (structure, "pointer_y", &y))
1901 /* video-direction compensation */
1902 switch (self->direction) {
1903 case GST_VIDEO_ORIENTATION_90R:
1905 new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1907 case GST_VIDEO_ORIENTATION_90L:
1908 new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1911 case GST_VIDEO_ORIENTATION_UR_LL:
1912 new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1913 new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1915 case GST_VIDEO_ORIENTATION_UL_LR:
1919 case GST_VIDEO_ORIENTATION_180:
1920 /* FIXME: is this correct? */
1921 new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1922 new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1924 case GST_VIDEO_ORIENTATION_HORIZ:
1925 new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
1928 case GST_VIDEO_ORIENTATION_VERT:
1930 new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
1938 /* scale compensation */
1939 _get_scale_factor (self, &w_factor, &h_factor);
1943 /* crop compensation is done by videocrop */
1945 GST_TRACE_OBJECT (self, "from %fx%f to %fx%f", x, y, new_x, new_y);
1946 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
1947 "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
1954 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
1960 gst_va_vpp_sink_event (GstBaseTransform * trans, GstEvent * event)
1962 GstVaVpp *self = GST_VA_VPP (trans);
1963 GstTagList *taglist;
1966 switch (GST_EVENT_TYPE (event)) {
1968 gst_event_parse_tag (event, &taglist);
1970 if (!gst_tag_list_get_string (taglist, "image-orientation", &orientation))
1973 if (self->direction != GST_VIDEO_ORIENTATION_AUTO)
1976 GST_DEBUG_OBJECT (self, "tag orientation %s", orientation);
1978 GST_OBJECT_LOCK (self);
1979 if (!g_strcmp0 ("rotate-0", orientation))
1980 self->tag_direction = GST_VIDEO_ORIENTATION_IDENTITY;
1981 else if (!g_strcmp0 ("rotate-90", orientation))
1982 self->tag_direction = GST_VIDEO_ORIENTATION_90R;
1983 else if (!g_strcmp0 ("rotate-180", orientation))
1984 self->tag_direction = GST_VIDEO_ORIENTATION_180;
1985 else if (!g_strcmp0 ("rotate-270", orientation))
1986 self->tag_direction = GST_VIDEO_ORIENTATION_90L;
1987 else if (!g_strcmp0 ("flip-rotate-0", orientation))
1988 self->tag_direction = GST_VIDEO_ORIENTATION_HORIZ;
1989 else if (!g_strcmp0 ("flip-rotate-90", orientation))
1990 self->tag_direction = GST_VIDEO_ORIENTATION_UL_LR;
1991 else if (!g_strcmp0 ("flip-rotate-180", orientation))
1992 self->tag_direction = GST_VIDEO_ORIENTATION_VERT;
1993 else if (!g_strcmp0 ("flip-rotate-270", orientation))
1994 self->tag_direction = GST_VIDEO_ORIENTATION_UR_LL;
1996 _update_properties_unlocked (self);
1997 GST_OBJECT_UNLOCK (self);
1999 gst_va_vpp_update_passthrough (self, FALSE);
2006 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2010 gst_va_vpp_class_init (gpointer g_class, gpointer class_data)
2012 GstCaps *doc_caps, *caps = NULL;
2013 GstPadTemplate *sink_pad_templ, *src_pad_templ;
2014 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
2015 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
2016 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2017 GstVaBaseTransformClass *btrans_class = GST_VA_BASE_TRANSFORM_CLASS (g_class);
2018 GstVaDisplay *display;
2019 GstVaFilter *filter;
2020 struct CData *cdata = class_data;
2023 parent_class = g_type_class_peek_parent (g_class);
2025 btrans_class->render_device_path = g_strdup (cdata->render_device_path);
2027 if (cdata->description) {
2028 long_name = g_strdup_printf ("VA-API Video Postprocessor in %s",
2029 cdata->description);
2031 long_name = g_strdup ("VA-API Video Postprocessor");
2034 gst_element_class_set_metadata (element_class, long_name,
2035 "Filter/Converter/Video/Scaler/Hardware",
2036 "VA-API based video postprocessor",
2037 "Víctor Jáquez <vjaquez@igalia.com>");
2039 display = gst_va_display_drm_new_from_path (btrans_class->render_device_path);
2040 filter = gst_va_filter_new (display);
2042 if (gst_va_filter_open (filter)) {
2043 caps = gst_va_filter_get_caps (filter);
2045 /* adds any to enable passthrough */
2047 GstCaps *any_caps = gst_caps_new_empty_simple ("video/x-raw");
2048 gst_caps_set_features_simple (any_caps, gst_caps_features_new_any ());
2049 caps = gst_caps_merge (caps, any_caps);
2052 caps = gst_caps_from_string (caps_str);
2055 doc_caps = gst_caps_from_string (caps_str);
2057 sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
2059 gst_element_class_add_pad_template (element_class, sink_pad_templ);
2060 gst_pad_template_set_documentation_caps (sink_pad_templ,
2061 gst_caps_ref (doc_caps));
2063 src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
2065 gst_element_class_add_pad_template (element_class, src_pad_templ);
2066 gst_pad_template_set_documentation_caps (src_pad_templ,
2067 gst_caps_ref (doc_caps));
2068 gst_caps_unref (doc_caps);
2070 gst_caps_unref (caps);
2072 object_class->dispose = gst_va_vpp_dispose;
2073 object_class->set_property = gst_va_vpp_set_property;
2074 object_class->get_property = gst_va_vpp_get_property;
2076 trans_class->propose_allocation =
2077 GST_DEBUG_FUNCPTR (gst_va_vpp_propose_allocation);
2078 trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_caps);
2079 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_fixate_caps);
2080 trans_class->before_transform =
2081 GST_DEBUG_FUNCPTR (gst_va_vpp_before_transform);
2082 trans_class->transform = GST_DEBUG_FUNCPTR (gst_va_vpp_transform);
2083 trans_class->transform_meta = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_meta);
2084 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_va_vpp_src_event);
2085 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_va_vpp_sink_event);
2087 trans_class->transform_ip_on_passthrough = FALSE;
2089 btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_va_vpp_set_info);
2090 btrans_class->update_properties =
2091 GST_DEBUG_FUNCPTR (gst_va_vpp_update_properties);
2093 gst_va_filter_install_properties (filter, object_class);
2096 g_free (cdata->description);
2097 g_free (cdata->render_device_path);
2099 gst_object_unref (filter);
2100 gst_object_unref (display);
2104 _create_colorbalance_channel (GstVaVpp * self, const gchar * label)
2106 GstColorBalanceChannel *channel;
2108 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2109 channel->label = g_strdup_printf ("VA-%s", label);
2110 channel->min_value = -1000;
2111 channel->max_value = 1000;
2113 self->channels = g_list_append (self->channels, channel);
2117 gst_va_vpp_init (GTypeInstance * instance, gpointer g_class)
2119 GstVaVpp *self = GST_VA_VPP (instance);
2122 self->direction = GST_VIDEO_ORIENTATION_IDENTITY;
2123 self->prev_direction = self->direction;
2124 self->tag_direction = GST_VIDEO_ORIENTATION_AUTO;
2126 pspec = g_object_class_find_property (g_class, "denoise");
2128 self->denoise = g_value_get_float (g_param_spec_get_default_value (pspec));
2130 pspec = g_object_class_find_property (g_class, "sharpen");
2132 self->sharpen = g_value_get_float (g_param_spec_get_default_value (pspec));
2134 pspec = g_object_class_find_property (g_class, "skin-tone");
2136 const GValue *value = g_param_spec_get_default_value (pspec);
2137 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
2138 self->skintone = g_value_get_boolean (value);
2140 self->skintone = g_value_get_float (value);
2144 pspec = g_object_class_find_property (g_class, "brightness");
2147 g_value_get_float (g_param_spec_get_default_value (pspec));
2148 _create_colorbalance_channel (self, "BRIGHTNESS");
2150 pspec = g_object_class_find_property (g_class, "contrast");
2152 self->contrast = g_value_get_float (g_param_spec_get_default_value (pspec));
2153 _create_colorbalance_channel (self, "CONTRAST");
2155 pspec = g_object_class_find_property (g_class, "hue");
2157 self->hue = g_value_get_float (g_param_spec_get_default_value (pspec));
2158 _create_colorbalance_channel (self, "HUE");
2160 pspec = g_object_class_find_property (g_class, "saturation");
2163 g_value_get_float (g_param_spec_get_default_value (pspec));
2164 _create_colorbalance_channel (self, "SATURATION");
2167 /* HDR tone mapping */
2168 pspec = g_object_class_find_property (g_class, "hdr-tone-mapping");
2171 g_value_get_boolean (g_param_spec_get_default_value (pspec));
2175 gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (instance), TRUE);
2179 _register_debug_category (gpointer data)
2181 GST_DEBUG_CATEGORY_INIT (gst_va_vpp_debug, "vapostproc", 0,
2182 "VA Video Postprocessor");
2185 G_PASTE (META_TAG_, type) = \
2186 g_quark_from_static_string (G_PASTE (G_PASTE (GST_META_TAG_VIDEO_, type), _STR))
2191 META_TAG_VIDEO = g_quark_from_static_string (GST_META_TAG_VIDEO_STR);
2197 gst_va_vpp_register (GstPlugin * plugin, GstVaDevice * device,
2198 gboolean has_colorbalance, guint rank)
2200 static GOnce debug_once = G_ONCE_INIT;
2202 GTypeInfo type_info = {
2203 .class_size = sizeof (GstVaVppClass),
2204 .class_init = gst_va_vpp_class_init,
2205 .instance_size = sizeof (GstVaVpp),
2206 .instance_init = gst_va_vpp_init,
2208 struct CData *cdata;
2210 gchar *type_name, *feature_name;
2212 g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
2213 g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
2215 cdata = g_new (struct CData, 1);
2216 cdata->description = NULL;
2217 cdata->render_device_path = g_strdup (device->render_device_path);
2219 type_info.class_data = cdata;
2221 type_name = g_strdup ("GstVaPostProc");
2222 feature_name = g_strdup ("vapostproc");
2224 /* The first postprocessor to be registered should use a constant
2225 * name, like vapostproc, for any additional postprocessors, we
2226 * create unique names, using inserting the render device name. */
2227 if (g_type_from_name (type_name)) {
2228 gchar *basename = g_path_get_basename (device->render_device_path);
2230 g_free (feature_name);
2231 type_name = g_strdup_printf ("GstVa%sPostProc", basename);
2232 feature_name = g_strdup_printf ("va%spostproc", basename);
2233 cdata->description = basename;
2235 /* lower rank for non-first device */
2240 g_once (&debug_once, _register_debug_category, NULL);
2242 type = g_type_register_static (GST_TYPE_VA_BASE_TRANSFORM, type_name,
2245 if (has_colorbalance) {
2246 const GInterfaceInfo info = { gst_va_vpp_colorbalance_init, NULL, NULL };
2247 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &info);
2250 ret = gst_element_register (plugin, feature_name, rank, type);
2253 g_free (feature_name);
2258 /* Color Balance interface */
2259 static const GList *
2260 gst_va_vpp_colorbalance_list_channels (GstColorBalance * balance)
2262 GstVaVpp *self = GST_VA_VPP (balance);
2264 return self->channels;
2267 /* This assumes --as happens with intel drivers-- that max values are
2268 * bigger than the simmetrical values of min values */
2270 make_max_simmetrical (GParamSpecFloat * fpspec)
2274 if (fpspec->default_value == 0)
2275 max = -fpspec->minimum;
2277 max = fpspec->default_value + ABS (fpspec->minimum - fpspec->default_value);
2279 return MIN (max, fpspec->maximum);
2283 _set_cb_val (GstVaVpp * self, const gchar * name,
2284 GstColorBalanceChannel * channel, gint value, gfloat * cb)
2286 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2288 GParamSpecFloat *fpspec;
2289 gfloat new_value, max;
2292 pspec = g_object_class_find_property (klass, name);
2296 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2297 max = make_max_simmetrical (fpspec);
2299 new_value = (value - channel->min_value) * (max - fpspec->minimum)
2300 / (channel->max_value - channel->min_value) + fpspec->minimum;
2302 GST_OBJECT_LOCK (self);
2303 changed = new_value != *cb;
2305 value = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2306 / (max - fpspec->minimum) + channel->min_value;
2307 GST_OBJECT_UNLOCK (self);
2310 GST_INFO_OBJECT (self, "%s: %d / %f", channel->label, value, new_value);
2311 gst_color_balance_value_changed (GST_COLOR_BALANCE (self), channel, value);
2312 g_atomic_int_set (&self->rebuild_filters, TRUE);
2319 gst_va_vpp_colorbalance_set_value (GstColorBalance * balance,
2320 GstColorBalanceChannel * channel, gint value)
2322 GstVaVpp *self = GST_VA_VPP (balance);
2324 if (g_str_has_suffix (channel->label, "HUE"))
2325 _set_cb_val (self, "hue", channel, value, &self->hue);
2326 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2327 _set_cb_val (self, "brightness", channel, value, &self->brightness);
2328 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2329 _set_cb_val (self, "contrast", channel, value, &self->contrast);
2330 else if (g_str_has_suffix (channel->label, "SATURATION"))
2331 _set_cb_val (self, "saturation", channel, value, &self->saturation);
2335 _get_cb_val (GstVaVpp * self, const gchar * name,
2336 GstColorBalanceChannel * channel, gfloat * cb, gint * val)
2338 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2340 GParamSpecFloat *fpspec;
2343 pspec = g_object_class_find_property (klass, name);
2347 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2348 max = make_max_simmetrical (fpspec);
2350 GST_OBJECT_LOCK (self);
2351 *val = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2352 / (max - fpspec->minimum) + channel->min_value;
2353 GST_OBJECT_UNLOCK (self);
2359 gst_va_vpp_colorbalance_get_value (GstColorBalance * balance,
2360 GstColorBalanceChannel * channel)
2362 GstVaVpp *self = GST_VA_VPP (balance);
2365 if (g_str_has_suffix (channel->label, "HUE"))
2366 _get_cb_val (self, "hue", channel, &self->hue, &value);
2367 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2368 _get_cb_val (self, "brightness", channel, &self->brightness, &value);
2369 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2370 _get_cb_val (self, "contrast", channel, &self->contrast, &value);
2371 else if (g_str_has_suffix (channel->label, "SATURATION"))
2372 _get_cb_val (self, "saturation", channel, &self->saturation, &value);
2377 static GstColorBalanceType
2378 gst_va_vpp_colorbalance_get_balance_type (GstColorBalance * balance)
2380 return GST_COLOR_BALANCE_HARDWARE;
2384 gst_va_vpp_colorbalance_init (gpointer iface, gpointer data)
2386 GstColorBalanceInterface *cbiface = iface;
2388 cbiface->list_channels = gst_va_vpp_colorbalance_list_channels;
2389 cbiface->set_value = gst_va_vpp_colorbalance_set_value;
2390 cbiface->get_value = gst_va_vpp_colorbalance_get_value;
2391 cbiface->get_balance_type = gst_va_vpp_colorbalance_get_balance_type;