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.
73 #include <gst/base/gstbasetransform.h>
74 #include <gst/video/video.h>
76 #include <va/va_drmcommon.h>
78 #include "gstvaallocator.h"
79 #include "gstvacaps.h"
80 #include "gstvadisplay_priv.h"
81 #include "gstvafilter.h"
82 #include "gstvapool.h"
83 #include "gstvautils.h"
85 GST_DEBUG_CATEGORY_STATIC (gst_va_vpp_debug);
86 #define GST_CAT_DEFAULT gst_va_vpp_debug
88 #define GST_VA_VPP(obj) ((GstVaVpp *) obj)
89 #define GST_VA_VPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaVppClass))
90 #define GST_VA_VPP_CLASS(klass) ((GstVaVppClass *) klass)
92 #define SWAP(a, b) do { const __typeof__ (a) t = a; a = b; b = t; } while (0)
94 typedef struct _GstVaVpp GstVaVpp;
95 typedef struct _GstVaVppClass GstVaVppClass;
99 /* GstVideoFilter overlaps functionality */
100 GstBaseTransformClass parent_class;
102 gchar *render_device_path;
107 GstBaseTransform parent;
109 GstVaDisplay *display;
115 GstVideoInfo in_info;
116 GstVideoInfo out_info;
119 GstBufferPool *sinkpad_pool;
120 GstVideoInfo sinkpad_info;
122 GstBufferPool *other_pool;
123 GstVideoInfo srcpad_info;
125 gboolean rebuild_filters;
126 gboolean forward_crop;
137 gboolean auto_contrast;
138 gboolean auto_brightness;
139 gboolean auto_saturation;
140 GstVideoOrientationMethod direction;
141 GstVideoOrientationMethod prev_direction;
142 GstVideoOrientationMethod tag_direction;
147 static GstElementClass *parent_class = NULL;
151 gchar *render_device_path;
155 /* convertions that disable passthrough */
158 VPP_CONVERT_SIZE = 1 << 0,
159 VPP_CONVERT_FORMAT = 1 << 1,
160 VPP_CONVERT_FILTERS = 1 << 2,
161 VPP_CONVERT_DIRECTION = 1 << 3,
162 VPP_CONVERT_FEATURE = 1 << 4,
163 VPP_CONVERT_CROP = 1 << 5,
164 VPP_CONVERT_DUMMY = 1 << 6,
167 extern GRecMutex GST_VA_SHARED_LOCK;
170 static const gchar *caps_str = GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("memory:VAMemory",
171 "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
172 GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
173 "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR }");
176 #define META_TAG_COLORSPACE meta_tag_colorspace_quark
177 static GQuark meta_tag_colorspace_quark;
178 #define META_TAG_SIZE meta_tag_size_quark
179 static GQuark meta_tag_size_quark;
180 #define META_TAG_ORIENTATION meta_tag_orientation_quark
181 static GQuark meta_tag_orientation_quark;
182 #define META_TAG_VIDEO meta_tag_video_quark
183 static GQuark meta_tag_video_quark;
185 static void gst_va_vpp_colorbalance_init (gpointer iface, gpointer data);
186 static void gst_va_vpp_rebuild_filters (GstVaVpp * self);
189 gst_va_vpp_dispose (GObject * object)
191 GstVaVpp *self = GST_VA_VPP (object);
194 g_list_free_full (g_steal_pointer (&self->channels), g_object_unref);
196 if (self->sinkpad_pool) {
197 gst_buffer_pool_set_active (self->sinkpad_pool, FALSE);
198 gst_clear_object (&self->sinkpad_pool);
201 if (self->other_pool) {
202 gst_buffer_pool_set_active (self->other_pool, FALSE);
203 gst_clear_object (&self->other_pool);
206 gst_clear_caps (&self->incaps);
207 gst_clear_caps (&self->outcaps);
208 gst_clear_caps (&self->alloccaps);
210 gst_clear_object (&self->filter);
211 gst_clear_object (&self->display);
213 G_OBJECT_CLASS (parent_class)->dispose (object);
217 gst_va_vpp_update_passthrough (GstVaVpp * self, gboolean reconf)
219 GstBaseTransform *trans = GST_BASE_TRANSFORM (self);
222 old = gst_base_transform_is_passthrough (trans);
224 GST_OBJECT_LOCK (self);
225 new = (self->op_flags == 0);
226 GST_OBJECT_UNLOCK (self);
229 GST_INFO_OBJECT (self, "%s passthrough", new ? "enabling" : "disabling");
231 gst_base_transform_reconfigure_src (trans);
232 gst_base_transform_set_passthrough (trans, new);
237 _update_properties_unlocked (GstVaVpp * self)
242 if ((self->direction != GST_VIDEO_ORIENTATION_AUTO
243 && self->direction != self->prev_direction)
244 || (self->direction == GST_VIDEO_ORIENTATION_AUTO
245 && self->tag_direction != self->prev_direction)) {
247 GstVideoOrientationMethod direction =
248 (self->direction == GST_VIDEO_ORIENTATION_AUTO) ?
249 self->tag_direction : self->direction;
251 if (!gst_va_filter_set_orientation (self->filter, direction)) {
252 if (self->direction == GST_VIDEO_ORIENTATION_AUTO)
253 self->tag_direction = self->prev_direction;
255 self->direction = self->prev_direction;
257 self->op_flags &= ~VPP_CONVERT_DIRECTION;
259 /* FIXME: unlocked bus warning message */
260 GST_WARNING_OBJECT (self,
261 "Driver cannot set resquested orientation. Setting it back.");
263 self->prev_direction = direction;
265 self->op_flags |= VPP_CONVERT_DIRECTION;
267 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (self));
270 self->op_flags &= ~VPP_CONVERT_DIRECTION;
275 gst_va_vpp_set_property (GObject * object, guint prop_id,
276 const GValue * value, GParamSpec * pspec)
278 GstVaVpp *self = GST_VA_VPP (object);
280 GST_OBJECT_LOCK (object);
282 case GST_VA_FILTER_PROP_DENOISE:
283 self->denoise = g_value_get_float (value);
284 g_atomic_int_set (&self->rebuild_filters, TRUE);
286 case GST_VA_FILTER_PROP_SHARPEN:
287 self->sharpen = g_value_get_float (value);
288 g_atomic_int_set (&self->rebuild_filters, TRUE);
290 case GST_VA_FILTER_PROP_SKINTONE:
291 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
292 self->skintone = (float) g_value_get_boolean (value);
294 self->skintone = g_value_get_float (value);
295 g_atomic_int_set (&self->rebuild_filters, TRUE);
297 case GST_VA_FILTER_PROP_VIDEO_DIR:{
298 GstVideoOrientationMethod direction = g_value_get_enum (value);
299 self->prev_direction = (direction == GST_VIDEO_ORIENTATION_AUTO) ?
300 self->tag_direction : self->direction;
301 self->direction = direction;
304 case GST_VA_FILTER_PROP_HUE:
305 self->hue = g_value_get_float (value);
306 g_atomic_int_set (&self->rebuild_filters, TRUE);
308 case GST_VA_FILTER_PROP_SATURATION:
309 self->saturation = g_value_get_float (value);
310 g_atomic_int_set (&self->rebuild_filters, TRUE);
312 case GST_VA_FILTER_PROP_BRIGHTNESS:
313 self->brightness = g_value_get_float (value);
314 g_atomic_int_set (&self->rebuild_filters, TRUE);
316 case GST_VA_FILTER_PROP_CONTRAST:
317 self->contrast = g_value_get_float (value);
318 g_atomic_int_set (&self->rebuild_filters, TRUE);
320 case GST_VA_FILTER_PROP_AUTO_SATURATION:
321 self->auto_saturation = g_value_get_boolean (value);
322 g_atomic_int_set (&self->rebuild_filters, TRUE);
324 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
325 self->auto_brightness = g_value_get_boolean (value);
326 g_atomic_int_set (&self->rebuild_filters, TRUE);
328 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
329 self->auto_contrast = g_value_get_boolean (value);
330 g_atomic_int_set (&self->rebuild_filters, TRUE);
332 case GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH:{
333 gboolean disable_passthrough = g_value_get_boolean (value);
334 if (disable_passthrough)
335 self->op_flags |= VPP_CONVERT_DUMMY;
337 self->op_flags &= ~VPP_CONVERT_DUMMY;
341 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
345 _update_properties_unlocked (self);
346 GST_OBJECT_UNLOCK (object);
348 /* no reconfig here because it's done in
349 * _update_properties_unlocked() */
350 gst_va_vpp_update_passthrough (self, FALSE);
354 gst_va_vpp_get_property (GObject * object, guint prop_id, GValue * value,
357 GstVaVpp *self = GST_VA_VPP (object);
359 GST_OBJECT_LOCK (object);
361 case GST_VA_FILTER_PROP_DENOISE:
362 g_value_set_float (value, self->denoise);
364 case GST_VA_FILTER_PROP_SHARPEN:
365 g_value_set_float (value, self->sharpen);
367 case GST_VA_FILTER_PROP_SKINTONE:
368 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
369 g_value_set_boolean (value, self->skintone > 0);
371 g_value_set_float (value, self->skintone);
373 case GST_VA_FILTER_PROP_VIDEO_DIR:
374 g_value_set_enum (value, self->direction);
376 case GST_VA_FILTER_PROP_HUE:
377 g_value_set_float (value, self->hue);
379 case GST_VA_FILTER_PROP_SATURATION:
380 g_value_set_float (value, self->saturation);
382 case GST_VA_FILTER_PROP_BRIGHTNESS:
383 g_value_set_float (value, self->brightness);
385 case GST_VA_FILTER_PROP_CONTRAST:
386 g_value_set_float (value, self->contrast);
388 case GST_VA_FILTER_PROP_AUTO_SATURATION:
389 g_value_set_boolean (value, self->auto_saturation);
391 case GST_VA_FILTER_PROP_AUTO_BRIGHTNESS:
392 g_value_set_boolean (value, self->auto_brightness);
394 case GST_VA_FILTER_PROP_AUTO_CONTRAST:
395 g_value_set_boolean (value, self->auto_contrast);
397 case GST_VA_FILTER_PROP_DISABLE_PASSTHROUGH:
398 g_value_set_boolean (value, (self->op_flags & VPP_CONVERT_DUMMY));
401 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404 GST_OBJECT_UNLOCK (object);
407 static GstStateChangeReturn
408 gst_va_vpp_change_state (GstElement * element, GstStateChange transition)
410 GstVaVpp *self = GST_VA_VPP (element);
411 GstVaVppClass *klass = GST_VA_VPP_GET_CLASS (element);
412 GstStateChangeReturn ret;
414 switch (transition) {
415 case GST_STATE_CHANGE_NULL_TO_READY:
416 if (!gst_va_ensure_element_data (element, klass->render_device_path,
420 self->filter = gst_va_filter_new (self->display);
421 if (!gst_va_filter_open (self->filter))
423 _update_properties_unlocked (self);
424 gst_va_vpp_rebuild_filters (self);
425 gst_va_vpp_update_passthrough (self, FALSE);
431 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
433 switch (transition) {
434 case GST_STATE_CHANGE_PAUSED_TO_READY:
435 gst_va_filter_close (self->filter);
437 case GST_STATE_CHANGE_READY_TO_NULL:
438 gst_clear_object (&self->filter);
439 gst_clear_object (&self->display);
450 GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to open VPP"));
451 return GST_STATE_CHANGE_FAILURE;
456 gst_va_vpp_set_context (GstElement * element, GstContext * context)
458 GstVaDisplay *old_display, *new_display;
459 GstVaVpp *self = GST_VA_VPP (element);
460 GstVaVppClass *klass = GST_VA_VPP_GET_CLASS (self);
463 old_display = self->display ? gst_object_ref (self->display) : NULL;
464 ret = gst_va_handle_set_context (element, context, klass->render_device_path,
466 new_display = self->display ? gst_object_ref (self->display) : NULL;
469 || (old_display && new_display && old_display != new_display
471 GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
472 ("Can't replace VA display while operating"), (NULL));
475 gst_clear_object (&old_display);
476 gst_clear_object (&new_display);
478 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
481 static GstAllocator *
482 _create_allocator (GstVaVpp * self, GstCaps * caps)
484 GstAllocator *allocator = NULL;
486 if (gst_caps_is_dmabuf (caps)) {
487 allocator = gst_va_dmabuf_allocator_new (self->display);
489 GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
490 allocator = gst_va_allocator_new (self->display, surface_formats);
496 static GstBufferPool *
497 _create_sinkpad_bufferpool (GstCaps * caps, guint size, guint min_buffers,
498 guint max_buffers, guint usage_hint, GstAllocator * allocator,
499 GstAllocationParams * alloc_params)
502 GstStructure *config;
504 pool = gst_va_pool_new ();
506 config = gst_buffer_pool_get_config (pool);
507 gst_buffer_pool_config_set_params (config, caps, size, min_buffers,
509 gst_buffer_pool_config_set_va_allocation_params (config, usage_hint);
510 gst_buffer_pool_config_set_allocator (config, allocator, alloc_params);
511 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
513 if (!gst_buffer_pool_set_config (pool, config))
514 gst_clear_object (&pool);
519 /* Answer the allocation query downstream. */
521 gst_va_vpp_propose_allocation (GstBaseTransform * trans,
522 GstQuery * decide_query, GstQuery * query)
524 GstVaVpp *self = GST_VA_VPP (trans);
530 gst_clear_caps (&self->alloccaps);
532 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
533 decide_query, query)) {
534 self->forward_crop = FALSE;
539 (gst_query_find_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE,
541 && gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL));
543 gst_query_parse_allocation (query, &caps, NULL);
546 if (!gst_video_info_from_caps (&info, caps))
549 self->alloccaps = gst_caps_ref (caps);
551 /* passthrough, we're done */
552 if (decide_query == NULL)
555 size = GST_VIDEO_INFO_SIZE (&info);
557 if (gst_query_get_n_allocation_pools (query) == 0) {
558 GstAllocator *allocator = NULL;
559 GstAllocationParams params = { 0, };
560 gboolean update_allocator = FALSE;
561 guint usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC; /* it migth be used by a va decoder */
563 if (gst_query_get_n_allocation_params (query) > 0) {
564 gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
565 if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator)
566 && !GST_IS_VA_ALLOCATOR (allocator))
567 gst_clear_object (&allocator);
568 update_allocator = TRUE;
570 gst_allocation_params_init (¶ms);
574 if (!(allocator = _create_allocator (self, caps)))
578 pool = _create_sinkpad_bufferpool (caps, size, 1, 0, usage_hint, allocator,
581 gst_object_unref (allocator);
585 if (update_allocator)
586 gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
588 gst_query_add_allocation_param (query, allocator, ¶ms);
590 gst_query_add_allocation_pool (query, pool, size, 1, 0);
592 GST_DEBUG_OBJECT (self,
593 "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
596 gst_object_unref (allocator);
597 gst_object_unref (pool);
599 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
600 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
608 GST_ERROR_OBJECT (self, "failed to set config");
613 static GstBufferPool *
614 _create_other_pool (GstAllocator * allocator,
615 GstAllocationParams * params, GstCaps * caps, guint size)
617 GstBufferPool *pool = NULL;
618 GstStructure *config;
620 pool = gst_video_buffer_pool_new ();
621 config = gst_buffer_pool_get_config (pool);
623 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
624 gst_buffer_pool_config_set_allocator (config, allocator, params);
625 if (!gst_buffer_pool_set_config (pool, config)) {
626 gst_clear_object (&pool);
632 /* configure the allocation query that was answered downstream, we can
633 * configure some properties on it. Only called when not in
634 * passthrough mode. */
636 gst_va_vpp_decide_allocation (GstBaseTransform * trans, GstQuery * query)
638 GstVaVpp *self = GST_VA_VPP (trans);
639 GstAllocator *allocator = NULL, *other_allocator = NULL;
640 GstAllocationParams params, other_params;
641 GstBufferPool *pool = NULL, *other_pool = NULL;
642 GstCaps *outcaps = NULL;
643 GstStructure *config;
645 guint min, max, size = 0, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
646 gboolean update_pool, update_allocator, has_videometa, copy_frames;
648 gst_query_parse_allocation (query, &outcaps, NULL);
650 gst_allocation_params_init (&other_params);
651 gst_allocation_params_init (¶ms);
653 if (!gst_video_info_from_caps (&vinfo, outcaps)) {
654 GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, outcaps);
658 if (gst_query_get_n_allocation_params (query) > 0) {
659 gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
660 if (allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (allocator)
661 || GST_IS_VA_ALLOCATOR (allocator))) {
662 /* save the allocator for the other pool */
663 other_allocator = allocator;
666 update_allocator = TRUE;
668 update_allocator = FALSE;
671 if (gst_query_get_n_allocation_pools (query) > 0) {
672 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
675 if (!GST_IS_VA_POOL (pool)) {
676 GST_DEBUG_OBJECT (self,
677 "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
685 size = GST_VIDEO_INFO_SIZE (&vinfo);
692 /* XXX(victor): VPP_WRITE uses a tiled drm modifier by iHD */
693 if (gst_caps_is_dmabuf (outcaps) && GST_VIDEO_INFO_IS_RGB (&vinfo))
694 usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
695 if (!(allocator = _create_allocator (self, outcaps)))
700 pool = gst_va_pool_new ();
702 config = gst_buffer_pool_get_config (pool);
703 gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
704 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
705 gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
706 gst_buffer_pool_config_set_va_allocation_params (config, usage_hint);
707 gst_buffer_pool_set_config (pool, config);
709 if (update_allocator)
710 gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
712 gst_query_add_allocation_param (query, allocator, ¶ms);
714 if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
715 gst_va_dmabuf_allocator_get_format (allocator, &vinfo, NULL);
716 } else if (GST_IS_VA_ALLOCATOR (allocator)) {
717 gst_va_allocator_get_format (allocator, &vinfo, NULL);
719 self->srcpad_info = vinfo;
722 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
724 gst_query_add_allocation_pool (query, pool, size, min, max);
726 has_videometa = gst_query_find_allocation_meta (query,
727 GST_VIDEO_META_API_TYPE, NULL);
729 copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
730 && gst_caps_is_raw (outcaps));
733 gst_object_replace ((GstObject **) & self->other_pool,
734 (GstObject *) other_pool);
737 _create_other_pool (other_allocator, &other_params, outcaps, size);
739 GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
742 gst_clear_object (&self->other_pool);
745 GST_DEBUG_OBJECT (self,
746 "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
749 gst_object_unref (allocator);
750 gst_object_unref (pool);
751 gst_clear_object (&other_allocator);
752 gst_clear_object (&other_pool);
754 /* removes allocation metas */
755 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
760 gst_va_vpp_query (GstBaseTransform * trans, GstPadDirection direction,
763 GstVaVpp *self = GST_VA_VPP (trans);
764 gboolean ret = FALSE;
766 switch (GST_QUERY_TYPE (query)) {
767 case GST_QUERY_CONTEXT:
769 GstVaDisplay *display = NULL;
771 gst_object_replace ((GstObject **) & display,
772 (GstObject *) self->display);
773 ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query,
775 gst_clear_object (&display);
779 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
787 /* output buffers must be from our VA-based pool, they cannot be
788 * system-allocated */
790 gst_va_vpp_transform_size (GstBaseTransform * trans,
791 GstPadDirection direction, GstCaps * caps, gsize size,
792 GstCaps * othercaps, gsize * othersize)
798 gst_va_vpp_set_caps (GstBaseTransform * trans, GstCaps * incaps,
801 GstVaVpp *self = GST_VA_VPP (trans);
802 GstVideoInfo in_info, out_info;
803 GstCapsFeatures *infeat, *outfeat;
806 if (!gst_video_info_from_caps (&in_info, incaps))
810 if (!gst_video_info_from_caps (&out_info, outcaps))
813 if (!gst_video_info_is_equal (&in_info, &out_info)) {
814 if (GST_VIDEO_INFO_FORMAT (&in_info) != GST_VIDEO_INFO_FORMAT (&out_info))
815 self->op_flags |= VPP_CONVERT_FORMAT;
817 self->op_flags &= ~VPP_CONVERT_FORMAT;
819 if (GST_VIDEO_INFO_WIDTH (&in_info) != GST_VIDEO_INFO_WIDTH (&out_info)
820 || GST_VIDEO_INFO_HEIGHT (&in_info) !=
821 GST_VIDEO_INFO_HEIGHT (&out_info))
822 self->op_flags |= VPP_CONVERT_SIZE;
824 self->op_flags &= ~VPP_CONVERT_SIZE;
826 self->op_flags &= ~VPP_CONVERT_FORMAT & ~VPP_CONVERT_SIZE;
829 infeat = gst_caps_get_features (incaps, 0);
830 outfeat = gst_caps_get_features (outcaps, 0);
831 if (!gst_caps_features_is_equal (infeat, outfeat))
832 self->op_flags |= VPP_CONVERT_FEATURE;
834 self->op_flags &= ~VPP_CONVERT_FEATURE;
836 if (self->sinkpad_pool) {
837 gst_buffer_pool_set_active (self->sinkpad_pool, FALSE);
838 gst_clear_object (&self->sinkpad_pool);
841 if (self->other_pool) {
842 gst_buffer_pool_set_active (self->other_pool, FALSE);
843 gst_clear_object (&self->other_pool);
846 gst_caps_replace (&self->incaps, incaps);
847 gst_caps_replace (&self->outcaps, outcaps);
849 self->in_info = in_info;
850 self->out_info = out_info;
853 gst_va_filter_set_formats (self->filter, &self->in_info, &self->out_info);
855 if (self->negotiated)
856 gst_va_vpp_update_passthrough (self, FALSE);
858 return self->negotiated;
863 GST_ERROR_OBJECT (self, "invalid caps");
864 self->negotiated = FALSE;
869 static inline gboolean
870 _get_filter_value (GstVaVpp * self, VAProcFilterType type, gfloat * value)
874 GST_OBJECT_LOCK (self);
876 case VAProcFilterNoiseReduction:
877 *value = self->denoise;
879 case VAProcFilterSharpening:
880 *value = self->sharpen;
882 case VAProcFilterSkinToneEnhancement:
883 *value = self->skintone;
889 GST_OBJECT_UNLOCK (self);
894 static inline gboolean
895 _add_filter_buffer (GstVaVpp * self, VAProcFilterType type,
896 const VAProcFilterCap * cap)
898 VAProcFilterParameterBuffer param;
901 if (!_get_filter_value (self, type, &value))
903 if (value == cap->range.default_value)
907 param = (VAProcFilterParameterBuffer) {
914 gst_va_filter_add_filter_buffer (self->filter, ¶m, sizeof (param), 1);
917 static inline gboolean
918 _get_filter_cb_value (GstVaVpp * self, VAProcColorBalanceType type,
923 GST_OBJECT_LOCK (self);
925 case VAProcColorBalanceHue:
928 case VAProcColorBalanceSaturation:
929 *value = self->saturation;
931 case VAProcColorBalanceBrightness:
932 *value = self->brightness;
934 case VAProcColorBalanceContrast:
935 *value = self->contrast;
937 case VAProcColorBalanceAutoSaturation:
938 *value = self->auto_saturation;
940 case VAProcColorBalanceAutoBrightness:
941 *value = self->auto_brightness;
943 case VAProcColorBalanceAutoContrast:
944 *value = self->auto_contrast;
950 GST_OBJECT_UNLOCK (self);
955 static inline gboolean
956 _add_filter_cb_buffer (GstVaVpp * self,
957 const VAProcFilterCapColorBalance * caps, guint num_caps)
959 VAProcFilterParameterBufferColorBalance param[VAProcColorBalanceCount] =
965 for (i = 0; i < num_caps && i < VAProcColorBalanceCount; i++) {
966 if (!_get_filter_cb_value (self, caps[i].type, &value))
968 if (value == caps[i].range.default_value)
972 param[c++] = (VAProcFilterParameterBufferColorBalance) {
973 .type = VAProcFilterColorBalance,
974 .attrib = caps[i].type,
981 return gst_va_filter_add_filter_buffer (self->filter, param,
988 _build_filters (GstVaVpp * self)
990 static const VAProcFilterType filter_types[] = { VAProcFilterNoiseReduction,
991 VAProcFilterSharpening, VAProcFilterSkinToneEnhancement,
992 VAProcFilterColorBalance,
995 gboolean apply = FALSE;
997 for (i = 0; i < G_N_ELEMENTS (filter_types); i++) {
998 const gpointer caps = gst_va_filter_get_filter_caps (self->filter,
999 filter_types[i], &num_caps);
1003 switch (filter_types[i]) {
1004 case VAProcFilterNoiseReduction:
1005 apply |= _add_filter_buffer (self, filter_types[i], caps);
1007 case VAProcFilterSharpening:
1008 apply |= _add_filter_buffer (self, filter_types[i], caps);
1010 case VAProcFilterSkinToneEnhancement:
1011 apply |= _add_filter_buffer (self, filter_types[i], caps);
1013 case VAProcFilterColorBalance:
1014 apply |= _add_filter_cb_buffer (self, caps, num_caps);
1021 GST_OBJECT_LOCK (self);
1023 self->op_flags |= VPP_CONVERT_FILTERS;
1025 self->op_flags &= ~VPP_CONVERT_FILTERS;
1026 GST_OBJECT_UNLOCK (self);
1030 gst_va_vpp_rebuild_filters (GstVaVpp * self)
1032 if (!g_atomic_int_get (&self->rebuild_filters))
1035 gst_va_filter_drop_filter_buffers (self->filter);
1036 _build_filters (self);
1037 g_atomic_int_set (&self->rebuild_filters, FALSE);
1041 gst_va_vpp_before_transform (GstBaseTransform * trans, GstBuffer * inbuf)
1043 GstVaVpp *self = GST_VA_VPP (trans);
1044 GstClockTime ts, stream_time;
1046 ts = GST_BUFFER_TIMESTAMP (inbuf);
1048 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, ts);
1050 GST_TRACE_OBJECT (self, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
1052 if (GST_CLOCK_TIME_IS_VALID (stream_time))
1053 gst_object_sync_values (GST_OBJECT (self), stream_time);
1055 GST_OBJECT_LOCK (self);
1056 if (gst_buffer_get_video_crop_meta (inbuf)) {
1057 /* enable cropping if either already do operations on frame or
1058 * downstream doesn't support cropping */
1059 if (self->op_flags == 0 && self->forward_crop) {
1060 self->op_flags &= ~VPP_CONVERT_CROP;
1062 self->op_flags |= VPP_CONVERT_CROP;
1065 self->op_flags &= ~VPP_CONVERT_CROP;
1067 gst_va_filter_enable_cropping (self->filter,
1068 (self->op_flags & VPP_CONVERT_CROP));
1069 GST_OBJECT_UNLOCK (self);
1071 gst_va_vpp_rebuild_filters (self);
1072 gst_va_vpp_update_passthrough (self, TRUE);
1076 _get_plane_data_size (GstVideoInfo * info, guint plane)
1078 gint height, padded_height;
1080 height = GST_VIDEO_INFO_HEIGHT (info);
1082 GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, height);
1084 return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
1088 _try_import_dmabuf_unlocked (GstVaVpp * self, GstBuffer * inbuf)
1091 GstVideoInfo in_info = self->in_info;
1092 GstMemory *mems[GST_VIDEO_MAX_PLANES];
1093 guint i, n_mem, n_planes;
1094 gsize offset[GST_VIDEO_MAX_PLANES];
1095 uintptr_t fd[GST_VIDEO_MAX_PLANES];
1097 n_planes = GST_VIDEO_INFO_N_PLANES (&in_info);
1098 n_mem = gst_buffer_n_memory (inbuf);
1099 meta = gst_buffer_get_video_meta (inbuf);
1101 /* This will eliminate most non-dmabuf out there */
1102 if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
1105 /* We cannot have multiple dmabuf per plane */
1106 if (n_mem > n_planes)
1109 /* Update video info based on video meta */
1111 GST_VIDEO_INFO_WIDTH (&in_info) = meta->width;
1112 GST_VIDEO_INFO_HEIGHT (&in_info) = meta->height;
1114 for (i = 0; i < meta->n_planes; i++) {
1115 GST_VIDEO_INFO_PLANE_OFFSET (&in_info, i) = meta->offset[i];
1116 GST_VIDEO_INFO_PLANE_STRIDE (&in_info, i) = meta->stride[i];
1120 /* Find and validate all memories */
1121 for (i = 0; i < n_planes; i++) {
1127 plane_size = _get_plane_data_size (&in_info, i);
1129 if (!gst_buffer_find_memory (inbuf, in_info.offset[i], plane_size,
1130 &mem_idx, &length, &mem_skip))
1133 /* We can't have more then one dmabuf per plane */
1137 mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
1139 /* And all memory found must be dmabuf */
1140 if (!gst_is_dmabuf_memory (mems[i]))
1143 offset[i] = mems[i]->offset + mem_skip;
1144 fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
1147 /* Now create a VASurfaceID for the buffer */
1148 return gst_va_dmabuf_memories_setup (self->display, &in_info, n_planes, mems,
1149 fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ);
1152 static GstBufferPool *
1153 _get_sinkpad_pool (GstVaVpp * self)
1155 GstAllocator *allocator;
1156 GstAllocationParams params;
1158 GstVideoInfo alloc_info, in_info;
1159 guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
1161 if (self->sinkpad_pool)
1162 return self->sinkpad_pool;
1164 gst_allocation_params_init (¶ms);
1166 if (self->alloccaps) {
1167 caps = self->alloccaps;
1168 gst_video_info_from_caps (&in_info, caps);
1170 caps = self->incaps;
1171 in_info = self->in_info;
1174 size = GST_VIDEO_INFO_SIZE (&in_info);
1176 allocator = _create_allocator (self, caps);
1178 self->sinkpad_pool = _create_sinkpad_bufferpool (caps, size, 1, 0, usage_hint,
1179 allocator, ¶ms);
1181 if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
1182 if (!gst_va_dmabuf_allocator_get_format (allocator, &alloc_info, NULL))
1183 alloc_info = in_info;
1184 } else if (GST_IS_VA_ALLOCATOR (allocator)) {
1185 if (!gst_va_allocator_get_format (allocator, &alloc_info, NULL))
1186 alloc_info = in_info;
1189 gst_object_unref (allocator);
1191 if (self->sinkpad_pool) {
1192 self->sinkpad_info = alloc_info;
1193 gst_buffer_pool_set_active (self->sinkpad_pool, TRUE);
1196 return self->sinkpad_pool;
1200 _try_import_buffer (GstVaVpp * self, GstBuffer * inbuf)
1202 VASurfaceID surface;
1205 surface = gst_va_buffer_get_surface (inbuf);
1206 if (surface != VA_INVALID_ID)
1209 g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
1210 ret = _try_import_dmabuf_unlocked (self, inbuf);
1211 g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
1216 static GstFlowReturn
1217 gst_va_vpp_import_input_buffer (GstVaVpp * self, GstBuffer * inbuf,
1220 GstBuffer *buffer = NULL;
1221 GstBufferPool *pool;
1223 GstVideoFrame in_frame, out_frame;
1224 gboolean imported, copied;
1226 imported = _try_import_buffer (self, inbuf);
1228 *buf = gst_buffer_ref (inbuf);
1232 /* input buffer doesn't come from a vapool, thus it is required to
1233 * have a pool, grab from it a new buffer and copy the input
1234 * buffer to the new one */
1235 if (!(pool = _get_sinkpad_pool (self)))
1236 return GST_FLOW_ERROR;
1238 ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
1239 if (ret != GST_FLOW_OK)
1242 GST_LOG_OBJECT (self, "copying input frame");
1244 if (!gst_video_frame_map (&in_frame, &self->in_info, inbuf, GST_MAP_READ))
1245 goto invalid_buffer;
1247 if (!gst_video_frame_map (&out_frame, &self->sinkpad_info, buffer,
1249 gst_video_frame_unmap (&in_frame);
1250 goto invalid_buffer;
1253 copied = gst_video_frame_copy (&out_frame, &in_frame);
1255 gst_video_frame_unmap (&out_frame);
1256 gst_video_frame_unmap (&in_frame);
1259 goto invalid_buffer;
1261 /* strictly speaking this is not needed but let's play safe */
1262 if (!gst_buffer_copy_into (buffer, inbuf, GST_BUFFER_COPY_FLAGS |
1263 GST_BUFFER_COPY_TIMESTAMPS, 0, -1))
1264 return GST_FLOW_ERROR;
1272 GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL),
1273 ("invalid video buffer received"));
1275 gst_buffer_unref (buffer);
1280 static GstFlowReturn
1281 gst_va_vpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1284 GstVaVpp *self = GST_VA_VPP (trans);
1285 GstBuffer *buf = NULL;
1286 GstFlowReturn res = GST_FLOW_OK;
1287 GstVaSample src, dst;
1289 if (G_UNLIKELY (!self->negotiated))
1290 goto unknown_format;
1292 res = gst_va_vpp_import_input_buffer (self, inbuf, &buf);
1293 if (res != GST_FLOW_OK)
1297 src = (GstVaSample) {
1301 dst = (GstVaSample) {
1306 if (!gst_va_filter_convert_surface (self->filter, &src, &dst)) {
1307 gst_buffer_set_flags (outbuf, GST_BUFFER_FLAG_CORRUPTED);
1310 gst_buffer_unref (buf);
1317 GST_ELEMENT_ERROR (self, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
1318 return GST_FLOW_NOT_NEGOTIATED;
1323 gst_va_vpp_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
1324 GstMeta * meta, GstBuffer * outbuf)
1326 GstVaVpp *self = GST_VA_VPP (trans);
1327 const GstMetaInfo *info = meta->info;
1328 const gchar *const *tags;
1330 tags = gst_meta_api_type_get_tags (info->api);
1335 /* don't copy colorspace/size/orientation specific metadata */
1336 if ((self->op_flags & VPP_CONVERT_FORMAT)
1337 && gst_meta_api_type_has_tag (info->api, META_TAG_COLORSPACE))
1339 else if ((self->op_flags & (VPP_CONVERT_SIZE | VPP_CONVERT_CROP))
1340 && gst_meta_api_type_has_tag (info->api, META_TAG_SIZE))
1342 else if ((self->op_flags & VPP_CONVERT_DIRECTION)
1343 && gst_meta_api_type_has_tag (info->api, META_TAG_ORIENTATION))
1345 else if (gst_meta_api_type_has_tag (info->api, META_TAG_VIDEO))
1348 return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (trans, outbuf,
1352 /* Remove all the info for the cases when we can actually convert:
1353 * Delete all the video "format", rangify the resolution size, also
1354 * remove "colorimetry", "chroma-site" and "pixel-aspect-ratio". All
1355 * the missing caps features should be added based on the template,
1356 * and the caps features' order in @caps is kept */
1358 gst_va_vpp_complete_caps_features (GstCaps * caps, GstCaps * tmpl_caps)
1360 GstCaps *ret, *full_caps;
1361 GstStructure *structure;
1362 GstCapsFeatures *features;
1363 gboolean has_sys_mem = FALSE, has_dma = FALSE, has_va = FALSE;
1366 full_caps = gst_caps_new_empty ();
1368 n = gst_caps_get_size (caps);
1369 for (i = 0; i < n; i++) {
1370 structure = gst_caps_get_structure (caps, i);
1371 features = gst_caps_get_features (caps, i);
1373 /* If this is already expressed by the existing caps
1374 * skip this structure */
1376 && gst_caps_is_subset_structure_full (full_caps, structure, features))
1379 if (gst_caps_features_is_any (features))
1382 if (gst_caps_features_is_equal (features,
1383 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
1386 gboolean valid = FALSE;
1388 if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
1392 if (gst_caps_features_contains (features, "memory:VAMemory")) {
1396 /* Not contain our supported feature */
1401 structure = gst_structure_copy (structure);
1403 gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1404 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
1405 /* if pixel aspect ratio, make a range of it */
1406 if (gst_structure_has_field (structure, "pixel-aspect-ratio")) {
1407 gst_structure_set (structure, "pixel-aspect-ratio",
1408 GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1, NULL);
1410 gst_structure_remove_fields (structure, "format", "colorimetry",
1411 "chroma-site", NULL);
1413 gst_caps_append_structure_full (full_caps, structure,
1414 gst_caps_features_copy (features));
1417 /* Adding the missing features. */
1418 n = gst_caps_get_size (tmpl_caps);
1419 for (i = 0; i < n; i++) {
1420 structure = gst_caps_get_structure (tmpl_caps, i);
1421 features = gst_caps_get_features (tmpl_caps, i);
1423 if (gst_caps_features_contains (features, "memory:VAMemory") && !has_va)
1424 gst_caps_append_structure_full (full_caps, gst_structure_copy (structure),
1425 gst_caps_features_copy (features));
1427 if (gst_caps_features_contains (features,
1428 GST_CAPS_FEATURE_MEMORY_DMABUF) && !has_dma)
1429 gst_caps_append_structure_full (full_caps, gst_structure_copy (structure),
1430 gst_caps_features_copy (features));
1432 if (gst_caps_features_is_equal (features,
1433 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY) && !has_sys_mem)
1434 gst_caps_append_structure_full (full_caps, gst_structure_copy (structure),
1435 gst_caps_features_copy (features));
1438 ret = gst_caps_intersect_full (full_caps, tmpl_caps,
1439 GST_CAPS_INTERSECT_FIRST);
1440 gst_caps_unref (full_caps);
1446 gst_va_vpp_transform_caps (GstBaseTransform * trans, GstPadDirection direction,
1447 GstCaps * caps, GstCaps * filter)
1449 GstVaVpp *self = GST_VA_VPP (trans);
1450 GstCaps *ret, *tmpl_caps;
1452 GST_DEBUG_OBJECT (self,
1453 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
1454 (direction == GST_PAD_SINK) ? "sink" : "src");
1456 if (direction == GST_PAD_SINK) {
1458 gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SRC_PAD (trans));
1461 gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SINK_PAD (trans));
1464 ret = gst_va_vpp_complete_caps_features (caps, tmpl_caps);
1466 gst_caps_unref (tmpl_caps);
1469 GstCaps *intersection;
1472 gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
1473 gst_caps_unref (ret);
1477 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
1483 * This is an incomplete matrix of in formats and a score for the preferred output
1486 * out: RGB24 RGB16 ARGB AYUV YUV444 YUV422 YUV420 YUV411 YUV410 PAL GRAY
1488 * RGB24 0 2 1 2 2 3 4 5 6 7 8
1489 * RGB16 1 0 1 2 2 3 4 5 6 7 8
1490 * ARGB 2 3 0 1 4 5 6 7 8 9 10
1491 * AYUV 3 4 1 0 2 5 6 7 8 9 10
1492 * YUV444 2 4 3 1 0 5 6 7 8 9 10
1493 * YUV422 3 5 4 2 1 0 6 7 8 9 10
1494 * YUV420 4 6 5 3 2 1 0 7 8 9 10
1495 * YUV411 4 6 5 3 2 1 7 0 8 9 10
1496 * YUV410 6 8 7 5 4 3 2 1 0 9 10
1497 * PAL 1 3 2 6 4 6 7 8 9 0 10
1498 * GRAY 1 4 3 2 1 5 6 7 8 9 0
1500 * PAL or GRAY are never preferred, if we can we would convert to PAL instead
1502 * less subsampling is preferred and if any, preferably horizontal
1503 * We would like to keep the alpha, even if we would need to to colorspace conversion
1506 #define SCORE_FORMAT_CHANGE 1
1507 #define SCORE_DEPTH_CHANGE 1
1508 #define SCORE_ALPHA_CHANGE 1
1509 #define SCORE_CHROMA_W_CHANGE 1
1510 #define SCORE_CHROMA_H_CHANGE 1
1511 #define SCORE_PALETTE_CHANGE 1
1513 #define SCORE_COLORSPACE_LOSS 2 /* RGB <-> YUV */
1514 #define SCORE_DEPTH_LOSS 4 /* change bit depth */
1515 #define SCORE_ALPHA_LOSS 8 /* lose the alpha channel */
1516 #define SCORE_CHROMA_W_LOSS 16 /* vertical subsample */
1517 #define SCORE_CHROMA_H_LOSS 32 /* horizontal subsample */
1518 #define SCORE_PALETTE_LOSS 64 /* convert to palette format */
1519 #define SCORE_COLOR_LOSS 128 /* convert to GRAY */
1521 #define COLORSPACE_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
1522 GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
1523 #define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
1524 #define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
1526 /* calculate how much loss a conversion would be */
1528 score_value (GstVaVpp * self, const GstVideoFormatInfo * in_info,
1529 const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
1532 const GstVideoFormatInfo *t_info;
1533 GstVideoFormatFlags in_flags, t_flags;
1536 fname = g_value_get_string (val);
1537 t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
1538 if (!t_info || t_info->format == GST_VIDEO_FORMAT_UNKNOWN)
1541 /* accept input format immediately without loss */
1542 if (in_info == t_info) {
1548 loss = SCORE_FORMAT_CHANGE;
1550 in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
1551 in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1552 in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1553 in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1555 t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
1556 t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
1557 t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
1558 t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
1560 if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK)) {
1561 loss += SCORE_PALETTE_CHANGE;
1562 if (t_flags & PALETTE_MASK)
1563 loss += SCORE_PALETTE_LOSS;
1566 if ((t_flags & COLORSPACE_MASK) != (in_flags & COLORSPACE_MASK)) {
1567 loss += SCORE_COLORSPACE_LOSS;
1568 if (t_flags & GST_VIDEO_FORMAT_FLAG_GRAY)
1569 loss += SCORE_COLOR_LOSS;
1572 if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK)) {
1573 loss += SCORE_ALPHA_CHANGE;
1574 if (in_flags & ALPHA_MASK)
1575 loss += SCORE_ALPHA_LOSS;
1578 if ((in_info->h_sub[1]) != (t_info->h_sub[1])) {
1579 loss += SCORE_CHROMA_H_CHANGE;
1580 if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
1581 loss += SCORE_CHROMA_H_LOSS;
1583 if ((in_info->w_sub[1]) != (t_info->w_sub[1])) {
1584 loss += SCORE_CHROMA_W_CHANGE;
1585 if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
1586 loss += SCORE_CHROMA_W_LOSS;
1589 if ((in_info->bits) != (t_info->bits)) {
1590 loss += SCORE_DEPTH_CHANGE;
1591 if ((in_info->bits) > (t_info->bits))
1592 loss += SCORE_DEPTH_LOSS;
1595 GST_DEBUG_OBJECT (self, "score %s -> %s = %d",
1596 GST_VIDEO_FORMAT_INFO_NAME (in_info),
1597 GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
1599 if (loss < *min_loss) {
1600 GST_DEBUG_OBJECT (self, "found new best %d", loss);
1607 gst_va_vpp_fixate_format (GstVaVpp * self, GstCaps * caps, GstCaps * result)
1609 GstStructure *ins, *outs;
1610 const gchar *in_format;
1611 const GstVideoFormatInfo *in_info, *out_info = NULL;
1612 gint min_loss = G_MAXINT;
1615 ins = gst_caps_get_structure (caps, 0);
1616 in_format = gst_structure_get_string (ins, "format");
1620 GST_DEBUG_OBJECT (self, "source format %s", in_format);
1623 gst_video_format_get_info (gst_video_format_from_string (in_format));
1627 outs = gst_caps_get_structure (result, 0);
1629 capslen = gst_caps_get_size (result);
1630 GST_DEBUG_OBJECT (self, "iterate %d structures", capslen);
1631 for (i = 0; i < capslen; i++) {
1632 GstStructure *tests;
1633 const GValue *format;
1635 tests = gst_caps_get_structure (result, i);
1636 format = gst_structure_get_value (tests, "format");
1637 gst_structure_remove_fields (tests, "height", "width", "pixel-aspect-ratio",
1638 "display-aspect-ratio", NULL);
1639 /* should not happen */
1643 if (GST_VALUE_HOLDS_LIST (format)) {
1646 len = gst_value_list_get_size (format);
1647 GST_DEBUG_OBJECT (self, "have %d formats", len);
1648 for (j = 0; j < len; j++) {
1651 val = gst_value_list_get_value (format, j);
1652 if (G_VALUE_HOLDS_STRING (val)) {
1653 score_value (self, in_info, val, &min_loss, &out_info);
1658 } else if (G_VALUE_HOLDS_STRING (format)) {
1659 score_value (self, in_info, format, &min_loss, &out_info);
1663 gst_structure_set (outs, "format", G_TYPE_STRING,
1664 GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
1668 gst_va_vpp_get_fixed_format (GstVaVpp * self, GstPadDirection direction,
1669 GstCaps * caps, GstCaps * othercaps)
1673 result = gst_caps_intersect (othercaps, caps);
1674 if (gst_caps_is_empty (result)) {
1675 gst_caps_unref (result);
1676 result = gst_caps_copy (othercaps);
1679 gst_va_vpp_fixate_format (self, caps, result);
1681 /* fixate remaining fields */
1682 result = gst_caps_fixate (result);
1684 if (direction == GST_PAD_SINK) {
1685 if (gst_caps_is_subset (caps, result)) {
1686 gst_caps_replace (&result, caps);
1694 gst_va_vpp_fixate_size (GstVaVpp * self, GstPadDirection direction,
1695 GstCaps * caps, GstCaps * othercaps)
1697 GstStructure *ins, *outs;
1698 const GValue *from_par, *to_par;
1699 GValue fpar = { 0, };
1700 GValue tpar = { 0, };
1702 othercaps = gst_caps_truncate (othercaps);
1703 othercaps = gst_caps_make_writable (othercaps);
1704 ins = gst_caps_get_structure (caps, 0);
1705 outs = gst_caps_get_structure (othercaps, 0);
1707 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1708 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1710 /* If we're fixating from the sinkpad we always set the PAR and
1711 * assume that missing PAR on the sinkpad means 1/1 and
1712 * missing PAR on the srcpad means undefined
1714 if (direction == GST_PAD_SINK) {
1716 g_value_init (&fpar, GST_TYPE_FRACTION);
1717 gst_value_set_fraction (&fpar, 1, 1);
1721 g_value_init (&tpar, GST_TYPE_FRACTION_RANGE);
1722 gst_value_set_fraction_range_full (&tpar, 1, G_MAXINT, G_MAXINT, 1);
1727 g_value_init (&tpar, GST_TYPE_FRACTION);
1728 gst_value_set_fraction (&tpar, 1, 1);
1731 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1735 g_value_init (&fpar, GST_TYPE_FRACTION);
1736 gst_value_set_fraction (&fpar, 1, 1);
1741 /* we have both PAR but they might not be fixated */
1743 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1745 gint from_dar_n, from_dar_d;
1748 /* from_par should be fixed */
1749 g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
1751 from_par_n = gst_value_get_fraction_numerator (from_par);
1752 from_par_d = gst_value_get_fraction_denominator (from_par);
1754 gst_structure_get_int (ins, "width", &from_w);
1755 gst_structure_get_int (ins, "height", &from_h);
1757 gst_structure_get_int (outs, "width", &w);
1758 gst_structure_get_int (outs, "height", &h);
1760 /* if video-orientation changes */
1761 switch (gst_va_filter_get_orientation (self->filter)) {
1762 case GST_VIDEO_ORIENTATION_90R:
1763 case GST_VIDEO_ORIENTATION_90L:
1764 case GST_VIDEO_ORIENTATION_UL_LR:
1765 case GST_VIDEO_ORIENTATION_UR_LL:
1766 if (direction == GST_PAD_SINK) {
1767 SWAP (from_w, from_h);
1768 SWAP (from_par_n, from_par_d);
1769 } else if (direction == GST_PAD_SRC) {
1771 /* there's no need to swap 1/1 par */
1778 /* if both width and height are already fixed, we can't do anything
1779 * about it anymore */
1783 GST_DEBUG_OBJECT (self, "dimensions already set to %dx%d, not fixating",
1785 if (!gst_value_is_fixed (to_par)) {
1786 if (gst_video_calculate_display_ratio (&n, &d, from_w, from_h,
1787 from_par_n, from_par_d, w, h)) {
1788 GST_DEBUG_OBJECT (self, "fixating to_par to %dx%d", n, d);
1789 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1790 gst_structure_fixate_field_nearest_fraction (outs,
1791 "pixel-aspect-ratio", n, d);
1793 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1800 /* Calculate input DAR */
1801 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1802 &from_dar_n, &from_dar_d)) {
1803 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1804 ("Error calculating the output scaled size - integer overflow"));
1808 GST_DEBUG_OBJECT (self, "Input DAR is %d/%d", from_dar_n, from_dar_d);
1810 /* If either width or height are fixed there's not much we
1811 * can do either except choosing a height or width and PAR
1812 * that matches the DAR as good as possible
1816 gint set_w, set_par_n, set_par_d;
1818 GST_DEBUG_OBJECT (self, "height is fixed (%d)", h);
1820 /* If the PAR is fixed too, there's not much to do
1821 * except choosing the width that is nearest to the
1822 * width with the same DAR */
1823 if (gst_value_is_fixed (to_par)) {
1824 to_par_n = gst_value_get_fraction_numerator (to_par);
1825 to_par_d = gst_value_get_fraction_denominator (to_par);
1827 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1829 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1830 to_par_n, &num, &den)) {
1831 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1832 ("Error calculating the output scaled size - integer overflow"));
1836 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1837 gst_structure_fixate_field_nearest_int (outs, "width", w);
1842 /* The PAR is not fixed and it's quite likely that we can set
1843 * an arbitrary PAR. */
1845 /* Check if we can keep the input width */
1846 tmp = gst_structure_copy (outs);
1847 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1848 gst_structure_get_int (tmp, "width", &set_w);
1850 /* Might have failed but try to keep the DAR nonetheless by
1851 * adjusting the PAR */
1852 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, h, set_w,
1853 &to_par_n, &to_par_d)) {
1854 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1855 ("Error calculating the output scaled size - integer overflow"));
1856 gst_structure_free (tmp);
1860 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1861 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1862 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1863 to_par_n, to_par_d);
1864 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1866 gst_structure_free (tmp);
1868 /* Check if the adjusted PAR is accepted */
1869 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1870 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1871 set_par_n != set_par_d)
1872 gst_structure_set (outs, "width", G_TYPE_INT, set_w,
1873 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1878 /* Otherwise scale the width to the new PAR and check if the
1879 * adjusted with is accepted. If all that fails we can't keep
1881 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1882 set_par_n, &num, &den)) {
1883 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1884 ("Error calculating the output scaled size - integer overflow"));
1888 w = (guint) gst_util_uint64_scale_int_round (h, num, den);
1889 gst_structure_fixate_field_nearest_int (outs, "width", w);
1890 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1891 set_par_n != set_par_d)
1892 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1893 set_par_n, set_par_d, NULL);
1898 gint set_h, set_par_n, set_par_d;
1900 GST_DEBUG_OBJECT (self, "width is fixed (%d)", w);
1902 /* If the PAR is fixed too, there's not much to do
1903 * except choosing the height that is nearest to the
1904 * height with the same DAR */
1905 if (gst_value_is_fixed (to_par)) {
1906 to_par_n = gst_value_get_fraction_numerator (to_par);
1907 to_par_d = gst_value_get_fraction_denominator (to_par);
1909 GST_DEBUG_OBJECT (self, "PAR is fixed %d/%d", to_par_n, to_par_d);
1911 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1912 to_par_n, &num, &den)) {
1913 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1914 ("Error calculating the output scaled size - integer overflow"));
1918 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1919 gst_structure_fixate_field_nearest_int (outs, "height", h);
1924 /* The PAR is not fixed and it's quite likely that we can set
1925 * an arbitrary PAR. */
1927 /* Check if we can keep the input height */
1928 tmp = gst_structure_copy (outs);
1929 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1930 gst_structure_get_int (tmp, "height", &set_h);
1932 /* Might have failed but try to keep the DAR nonetheless by
1933 * adjusting the PAR */
1934 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, w,
1935 &to_par_n, &to_par_d)) {
1936 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1937 ("Error calculating the output scaled size - integer overflow"));
1938 gst_structure_free (tmp);
1941 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1942 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1943 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1944 to_par_n, to_par_d);
1945 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1947 gst_structure_free (tmp);
1949 /* Check if the adjusted PAR is accepted */
1950 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1951 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1952 set_par_n != set_par_d)
1953 gst_structure_set (outs, "height", G_TYPE_INT, set_h,
1954 "pixel-aspect-ratio", GST_TYPE_FRACTION, set_par_n, set_par_d,
1959 /* Otherwise scale the height to the new PAR and check if the
1960 * adjusted with is accepted. If all that fails we can't keep
1962 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1963 set_par_n, &num, &den)) {
1964 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1965 ("Error calculating the output scale sized - integer overflow"));
1969 h = (guint) gst_util_uint64_scale_int_round (w, den, num);
1970 gst_structure_fixate_field_nearest_int (outs, "height", h);
1971 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1972 set_par_n != set_par_d)
1973 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1974 set_par_n, set_par_d, NULL);
1977 } else if (gst_value_is_fixed (to_par)) {
1979 gint set_h, set_w, f_h, f_w;
1981 to_par_n = gst_value_get_fraction_numerator (to_par);
1982 to_par_d = gst_value_get_fraction_denominator (to_par);
1984 /* Calculate scale factor for the PAR change */
1985 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1986 to_par_d, &num, &den)) {
1987 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1988 ("Error calculating the output scaled size - integer overflow"));
1992 /* Try to keep the input height (because of interlacing) */
1993 tmp = gst_structure_copy (outs);
1994 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1995 gst_structure_get_int (tmp, "height", &set_h);
1997 /* This might have failed but try to scale the width
1998 * to keep the DAR nonetheless */
1999 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
2000 gst_structure_fixate_field_nearest_int (tmp, "width", w);
2001 gst_structure_get_int (tmp, "width", &set_w);
2002 gst_structure_free (tmp);
2004 /* We kept the DAR and the height is nearest to the original height */
2006 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2007 G_TYPE_INT, set_h, NULL);
2014 /* If the former failed, try to keep the input width at least */
2015 tmp = gst_structure_copy (outs);
2016 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
2017 gst_structure_get_int (tmp, "width", &set_w);
2019 /* This might have failed but try to scale the width
2020 * to keep the DAR nonetheless */
2021 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
2022 gst_structure_fixate_field_nearest_int (tmp, "height", h);
2023 gst_structure_get_int (tmp, "height", &set_h);
2024 gst_structure_free (tmp);
2026 /* We kept the DAR and the width is nearest to the original width */
2028 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2029 G_TYPE_INT, set_h, NULL);
2033 /* If all this failed, keep the dimensions with the DAR that was closest
2034 * to the correct DAR. This changes the DAR but there's not much else to
2037 if (set_w * ABS (set_h - h) < ABS (f_w - w) * f_h) {
2041 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
2046 gint set_h, set_w, set_par_n, set_par_d, tmp2;
2048 /* width, height and PAR are not fixed but passthrough is not possible */
2050 /* First try to keep the height and width as good as possible
2052 tmp = gst_structure_copy (outs);
2053 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
2054 gst_structure_get_int (tmp, "height", &set_h);
2055 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
2056 gst_structure_get_int (tmp, "width", &set_w);
2058 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
2059 &to_par_n, &to_par_d)) {
2060 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
2061 ("Error calculating the output scaled size - integer overflow"));
2062 gst_structure_free (tmp);
2066 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
2067 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
2068 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
2069 to_par_n, to_par_d);
2070 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
2072 gst_structure_free (tmp);
2074 if (set_par_n == to_par_n && set_par_d == to_par_d) {
2075 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2076 G_TYPE_INT, set_h, NULL);
2078 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2079 set_par_n != set_par_d)
2080 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2081 set_par_n, set_par_d, NULL);
2085 /* Otherwise try to scale width to keep the DAR with the set
2087 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
2088 set_par_n, &num, &den)) {
2089 GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
2090 ("Error calculating the output scaled size - integer overflow"));
2094 w = (guint) gst_util_uint64_scale_int_round (set_h, num, den);
2095 tmp = gst_structure_copy (outs);
2096 gst_structure_fixate_field_nearest_int (tmp, "width", w);
2097 gst_structure_get_int (tmp, "width", &tmp2);
2098 gst_structure_free (tmp);
2101 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
2102 G_TYPE_INT, set_h, NULL);
2103 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2104 set_par_n != set_par_d)
2105 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2106 set_par_n, set_par_d, NULL);
2110 /* ... or try the same with the height */
2111 h = (guint) gst_util_uint64_scale_int_round (set_w, den, num);
2112 tmp = gst_structure_copy (outs);
2113 gst_structure_fixate_field_nearest_int (tmp, "height", h);
2114 gst_structure_get_int (tmp, "height", &tmp2);
2115 gst_structure_free (tmp);
2118 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2119 G_TYPE_INT, tmp2, NULL);
2120 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2121 set_par_n != set_par_d)
2122 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2123 set_par_n, set_par_d, NULL);
2127 /* If all fails we can't keep the DAR and take the nearest values
2128 * for everything from the first try */
2129 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
2130 G_TYPE_INT, set_h, NULL);
2131 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
2132 set_par_n != set_par_d)
2133 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2134 set_par_n, set_par_d, NULL);
2139 if (from_par == &fpar)
2140 g_value_unset (&fpar);
2141 if (to_par == &tpar)
2142 g_value_unset (&tpar);
2148 gst_va_vpp_fixate_caps (GstBaseTransform * trans, GstPadDirection direction,
2149 GstCaps * caps, GstCaps * othercaps)
2151 GstVaVpp *self = GST_VA_VPP (trans);
2154 GST_DEBUG_OBJECT (self,
2155 "trying to fixate othercaps %" GST_PTR_FORMAT " based on caps %"
2156 GST_PTR_FORMAT, othercaps, caps);
2158 format = gst_va_vpp_get_fixed_format (self, direction, caps, othercaps);
2160 if (gst_caps_is_empty (format)) {
2161 GST_ERROR_OBJECT (self, "Could not convert formats");
2165 othercaps = gst_va_vpp_fixate_size (self, direction, caps, othercaps);
2166 if (gst_caps_get_size (othercaps) == 1) {
2168 const gchar *format_fields[] = { "format", "colorimetry", "chroma-site" };
2169 GstStructure *format_struct = gst_caps_get_structure (format, 0);
2170 GstStructure *fixated_struct;
2172 othercaps = gst_caps_make_writable (othercaps);
2173 fixated_struct = gst_caps_get_structure (othercaps, 0);
2175 for (i = 0; i < G_N_ELEMENTS (format_fields); i++) {
2176 if (gst_structure_has_field (format_struct, format_fields[i])) {
2177 gst_structure_set (fixated_struct, format_fields[i], G_TYPE_STRING,
2178 gst_structure_get_string (format_struct, format_fields[i]), NULL);
2180 gst_structure_remove_field (fixated_struct, format_fields[i]);
2184 /* copy the framerate */
2186 const GValue *framerate =
2187 gst_structure_get_value (fixated_struct, "framerate");
2188 if (framerate && !gst_value_is_fixed (framerate)) {
2189 GstStructure *st = gst_caps_get_structure (caps, 0);
2190 const GValue *fixated_framerate =
2191 gst_structure_get_value (st, "framerate");
2192 gst_structure_set_value (fixated_struct, "framerate",
2197 gst_caps_unref (format);
2199 GST_DEBUG_OBJECT (self, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
2205 _get_scale_factor (GstVaVpp * self, gdouble * w_factor, gdouble * h_factor)
2207 gdouble w = GST_VIDEO_INFO_WIDTH (&self->in_info);
2208 gdouble h = GST_VIDEO_INFO_HEIGHT (&self->in_info);
2210 switch (self->direction) {
2211 case GST_VIDEO_ORIENTATION_90R:
2212 case GST_VIDEO_ORIENTATION_90L:
2213 case GST_VIDEO_ORIENTATION_UR_LL:
2214 case GST_VIDEO_ORIENTATION_UL_LR:{
2224 /* TODO: add cropping factor */
2225 *w_factor = GST_VIDEO_INFO_WIDTH (&self->out_info);
2228 *h_factor = GST_VIDEO_INFO_HEIGHT (&self->out_info);
2233 gst_va_vpp_src_event (GstBaseTransform * trans, GstEvent * event)
2235 GstVaVpp *self = GST_VA_VPP (trans);
2236 GstStructure *structure;
2237 const GstVideoInfo *in_info = &self->in_info, *out_info = &self->out_info;
2238 gdouble new_x = 0, new_y = 0, x = 0, y = 0, w_factor = 1, h_factor = 1;
2241 GST_TRACE_OBJECT (self, "handling %s event", GST_EVENT_TYPE_NAME (event));
2243 switch (GST_EVENT_TYPE (event)) {
2244 case GST_EVENT_NAVIGATION:
2245 if (GST_VIDEO_INFO_WIDTH (in_info) != GST_VIDEO_INFO_WIDTH (out_info)
2246 || GST_VIDEO_INFO_HEIGHT (in_info) != GST_VIDEO_INFO_HEIGHT (out_info)
2247 || gst_va_filter_get_orientation (self->filter) !=
2248 GST_VIDEO_ORIENTATION_IDENTITY) {
2251 GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
2253 structure = (GstStructure *) gst_event_get_structure (event);
2254 if (!gst_structure_get_double (structure, "pointer_x", &x)
2255 || !gst_structure_get_double (structure, "pointer_y", &y))
2258 /* video-direction compensation */
2259 switch (self->direction) {
2260 case GST_VIDEO_ORIENTATION_90R:
2262 new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2264 case GST_VIDEO_ORIENTATION_90L:
2265 new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2268 case GST_VIDEO_ORIENTATION_UR_LL:
2269 new_x = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2270 new_y = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2272 case GST_VIDEO_ORIENTATION_UL_LR:
2276 case GST_VIDEO_ORIENTATION_180:
2277 /* FIXME: is this correct? */
2278 new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2279 new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2281 case GST_VIDEO_ORIENTATION_HORIZ:
2282 new_x = GST_VIDEO_INFO_WIDTH (in_info) - 1 - x;
2285 case GST_VIDEO_ORIENTATION_VERT:
2287 new_y = GST_VIDEO_INFO_HEIGHT (in_info) - 1 - y;
2295 /* scale compensation */
2296 _get_scale_factor (self, &w_factor, &h_factor);
2300 /* TODO: crop compensation */
2302 GST_TRACE_OBJECT (self, "from %fx%f to %fx%f", x, y, new_x, new_y);
2303 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
2304 "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
2311 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
2317 gst_va_vpp_sink_event (GstBaseTransform * trans, GstEvent * event)
2319 GstVaVpp *self = GST_VA_VPP (trans);
2320 GstTagList *taglist;
2323 switch (GST_EVENT_TYPE (event)) {
2325 gst_event_parse_tag (event, &taglist);
2327 if (!gst_tag_list_get_string (taglist, "image-orientation", &orientation))
2330 if (self->direction != GST_VIDEO_ORIENTATION_AUTO)
2333 GST_DEBUG_OBJECT (self, "tag orientation %s", orientation);
2335 GST_OBJECT_LOCK (self);
2336 if (!g_strcmp0 ("rotate-0", orientation))
2337 self->tag_direction = GST_VIDEO_ORIENTATION_IDENTITY;
2338 else if (!g_strcmp0 ("rotate-90", orientation))
2339 self->tag_direction = GST_VIDEO_ORIENTATION_90R;
2340 else if (!g_strcmp0 ("rotate-180", orientation))
2341 self->tag_direction = GST_VIDEO_ORIENTATION_180;
2342 else if (!g_strcmp0 ("rotate-270", orientation))
2343 self->tag_direction = GST_VIDEO_ORIENTATION_90L;
2344 else if (!g_strcmp0 ("flip-rotate-0", orientation))
2345 self->tag_direction = GST_VIDEO_ORIENTATION_HORIZ;
2346 else if (!g_strcmp0 ("flip-rotate-90", orientation))
2347 self->tag_direction = GST_VIDEO_ORIENTATION_UL_LR;
2348 else if (!g_strcmp0 ("flip-rotate-180", orientation))
2349 self->tag_direction = GST_VIDEO_ORIENTATION_VERT;
2350 else if (!g_strcmp0 ("flip-rotate-270", orientation))
2351 self->tag_direction = GST_VIDEO_ORIENTATION_UR_LL;
2353 _update_properties_unlocked (self);
2354 GST_OBJECT_UNLOCK (self);
2356 /* no reconfig here because it's done in
2357 * _update_properties_unlocked */
2358 gst_va_vpp_update_passthrough (self, FALSE);
2365 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
2368 static GstFlowReturn
2369 gst_va_vpp_generate_output (GstBaseTransform * trans, GstBuffer ** outbuf)
2371 GstVaVpp *self = GST_VA_VPP (trans);
2372 GstVideoFrame src_frame;
2373 GstVideoFrame dest_frame;
2374 GstBuffer *buffer = NULL;
2377 ret = GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
2380 if (ret != GST_FLOW_OK || *outbuf == NULL)
2383 if (!self->other_pool)
2386 /* Now need to copy the output buffer */
2387 ret = GST_FLOW_ERROR;
2389 if (!gst_buffer_pool_set_active (self->other_pool, TRUE)) {
2390 GST_WARNING_OBJECT (self, "failed to active the other pool %"
2391 GST_PTR_FORMAT, self->other_pool);
2395 ret = gst_buffer_pool_acquire_buffer (self->other_pool, &buffer, NULL);
2396 if (ret != GST_FLOW_OK)
2399 if (!gst_video_frame_map (&src_frame, &self->srcpad_info, *outbuf,
2403 if (!gst_video_frame_map (&dest_frame, &self->out_info, buffer,
2405 gst_video_frame_unmap (&src_frame);
2409 if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
2410 gst_video_frame_unmap (&src_frame);
2411 gst_video_frame_unmap (&dest_frame);
2415 gst_video_frame_unmap (&src_frame);
2416 gst_video_frame_unmap (&dest_frame);
2418 gst_buffer_replace (outbuf, buffer);
2422 gst_clear_buffer (&buffer);
2427 gst_va_vpp_class_init (gpointer g_class, gpointer class_data)
2429 GstCaps *doc_caps, *caps = NULL;
2430 GstPadTemplate *sink_pad_templ, *src_pad_templ;
2431 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
2432 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
2433 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2434 GstVaDisplay *display;
2435 GstVaFilter *filter;
2436 GstVaVppClass *klass = GST_VA_VPP_CLASS (g_class);
2437 struct CData *cdata = class_data;
2440 parent_class = g_type_class_peek_parent (g_class);
2442 klass->render_device_path = g_strdup (cdata->render_device_path);
2444 if (cdata->description) {
2445 long_name = g_strdup_printf ("VA-API Video Postprocessor in %s",
2446 cdata->description);
2448 long_name = g_strdup ("VA-API Video Postprocessor");
2451 gst_element_class_set_metadata (element_class, long_name,
2452 "Filter/Converter/Video/Scaler/Hardware",
2453 "VA-API based video postprocessor",
2454 "Víctor Jáquez <vjaquez@igalia.com>");
2456 display = gst_va_display_drm_new_from_path (klass->render_device_path);
2457 filter = gst_va_filter_new (display);
2459 if (gst_va_filter_open (filter))
2460 caps = gst_va_filter_get_caps (filter);
2462 caps = gst_caps_from_string (caps_str);
2464 doc_caps = gst_caps_from_string (caps_str);
2466 sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
2468 gst_element_class_add_pad_template (element_class, sink_pad_templ);
2469 gst_pad_template_set_documentation_caps (sink_pad_templ,
2470 gst_caps_ref (doc_caps));
2472 src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
2474 gst_element_class_add_pad_template (element_class, src_pad_templ);
2475 gst_pad_template_set_documentation_caps (src_pad_templ,
2476 gst_caps_ref (doc_caps));
2477 gst_caps_unref (doc_caps);
2479 gst_caps_unref (caps);
2481 object_class->dispose = gst_va_vpp_dispose;
2482 object_class->set_property = gst_va_vpp_set_property;
2483 object_class->get_property = gst_va_vpp_get_property;
2485 element_class->change_state = GST_DEBUG_FUNCPTR (gst_va_vpp_change_state);
2486 element_class->set_context = GST_DEBUG_FUNCPTR (gst_va_vpp_set_context);
2488 trans_class->propose_allocation =
2489 GST_DEBUG_FUNCPTR (gst_va_vpp_propose_allocation);
2490 trans_class->decide_allocation =
2491 GST_DEBUG_FUNCPTR (gst_va_vpp_decide_allocation);
2492 trans_class->query = GST_DEBUG_FUNCPTR (gst_va_vpp_query);
2493 trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_caps);
2494 trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_fixate_caps);
2495 trans_class->transform_size = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_size);
2496 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_va_vpp_set_caps);
2497 trans_class->before_transform =
2498 GST_DEBUG_FUNCPTR (gst_va_vpp_before_transform);
2499 trans_class->transform = GST_DEBUG_FUNCPTR (gst_va_vpp_transform);
2500 trans_class->transform_meta = GST_DEBUG_FUNCPTR (gst_va_vpp_transform_meta);
2501 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_va_vpp_src_event);
2502 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_va_vpp_sink_event);
2503 trans_class->generate_output = GST_DEBUG_FUNCPTR (gst_va_vpp_generate_output);
2505 trans_class->transform_ip_on_passthrough = FALSE;
2507 gst_va_filter_install_properties (filter, object_class);
2510 g_free (cdata->description);
2511 g_free (cdata->render_device_path);
2513 gst_object_unref (filter);
2514 gst_object_unref (display);
2518 _create_colorbalance_channel (GstVaVpp * self, const gchar * label)
2520 GstColorBalanceChannel *channel;
2522 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2523 channel->label = g_strdup_printf ("VA-%s", label);
2524 channel->min_value = -1000;
2525 channel->max_value = 1000;
2527 self->channels = g_list_append (self->channels, channel);
2531 gst_va_vpp_init (GTypeInstance * instance, gpointer g_class)
2533 GstVaVpp *self = GST_VA_VPP (instance);
2536 self->direction = GST_VIDEO_ORIENTATION_IDENTITY;
2537 self->prev_direction = self->direction;
2538 self->tag_direction = GST_VIDEO_ORIENTATION_AUTO;
2540 pspec = g_object_class_find_property (g_class, "denoise");
2542 self->denoise = g_value_get_float (g_param_spec_get_default_value (pspec));
2544 pspec = g_object_class_find_property (g_class, "sharpen");
2546 self->sharpen = g_value_get_float (g_param_spec_get_default_value (pspec));
2548 pspec = g_object_class_find_property (g_class, "skin-tone");
2550 const GValue *value = g_param_spec_get_default_value (pspec);
2551 if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
2552 self->skintone = g_value_get_boolean (value);
2554 self->skintone = g_value_get_float (value);
2558 pspec = g_object_class_find_property (g_class, "brightness");
2561 g_value_get_float (g_param_spec_get_default_value (pspec));
2562 _create_colorbalance_channel (self, "BRIGHTNESS");
2564 pspec = g_object_class_find_property (g_class, "contrast");
2566 self->contrast = g_value_get_float (g_param_spec_get_default_value (pspec));
2567 _create_colorbalance_channel (self, "CONTRAST");
2569 pspec = g_object_class_find_property (g_class, "hue");
2571 self->hue = g_value_get_float (g_param_spec_get_default_value (pspec));
2572 _create_colorbalance_channel (self, "HUE");
2574 pspec = g_object_class_find_property (g_class, "saturation");
2577 g_value_get_float (g_param_spec_get_default_value (pspec));
2578 _create_colorbalance_channel (self, "SATURATION");
2582 gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (instance), TRUE);
2586 _register_debug_category (gpointer data)
2588 GST_DEBUG_CATEGORY_INIT (gst_va_vpp_debug, "vavpp", 0,
2589 "VA Video Postprocessor");
2592 G_PASTE (META_TAG_, type) = \
2593 g_quark_from_static_string (G_PASTE (G_PASTE (GST_META_TAG_VIDEO_, type), _STR))
2598 META_TAG_VIDEO = g_quark_from_static_string (GST_META_TAG_VIDEO_STR);
2604 gst_va_vpp_register (GstPlugin * plugin, GstVaDevice * device, guint rank)
2606 static GOnce debug_once = G_ONCE_INIT;
2608 GTypeInfo type_info = {
2609 .class_size = sizeof (GstVaVppClass),
2610 .class_init = gst_va_vpp_class_init,
2611 .instance_size = sizeof (GstVaVpp),
2612 .instance_init = gst_va_vpp_init,
2614 struct CData *cdata;
2616 gchar *type_name, *feature_name;
2618 g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
2619 g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
2621 cdata = g_new (struct CData, 1);
2622 cdata->description = NULL;
2623 cdata->render_device_path = g_strdup (device->render_device_path);
2625 type_info.class_data = cdata;
2627 type_name = g_strdup ("GstVaPostProc");
2628 feature_name = g_strdup ("vapostproc");
2630 /* The first postprocessor to be registered should use a constant
2631 * name, like vapostproc, for any additional postprocessors, we
2632 * create unique names, using inserting the render device name. */
2633 if (g_type_from_name (type_name)) {
2634 gchar *basename = g_path_get_basename (device->render_device_path);
2636 g_free (feature_name);
2637 type_name = g_strdup_printf ("GstVa%sPostProc", basename);
2638 feature_name = g_strdup_printf ("va%spostproc", basename);
2639 cdata->description = basename;
2641 /* lower rank for non-first device */
2646 g_once (&debug_once, _register_debug_category, NULL);
2648 type = g_type_register_static (GST_TYPE_BASE_TRANSFORM, type_name, &type_info,
2652 GstVaFilter *filter = gst_va_filter_new (device->display);
2653 if (gst_va_filter_open (filter)
2654 && gst_va_filter_has_filter (filter, VAProcFilterColorBalance)) {
2655 const GInterfaceInfo info = { gst_va_vpp_colorbalance_init, NULL, NULL };
2656 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &info);
2658 gst_object_unref (filter);
2661 ret = gst_element_register (plugin, feature_name, rank, type);
2664 g_free (feature_name);
2669 /* Color Balance interface */
2670 static const GList *
2671 gst_va_vpp_colorbalance_list_channels (GstColorBalance * balance)
2673 GstVaVpp *self = GST_VA_VPP (balance);
2675 return self->channels;
2679 _set_cb_val (GstVaVpp * self, const gchar * name,
2680 GstColorBalanceChannel * channel, gint value, gfloat * cb)
2682 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2684 GParamSpecFloat *fpspec;
2688 pspec = g_object_class_find_property (klass, name);
2692 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2693 new_value = (value - channel->min_value) * (fpspec->maximum - fpspec->minimum)
2694 / (channel->max_value - channel->min_value) + fpspec->minimum;
2696 GST_OBJECT_LOCK (self);
2697 changed = new_value != *cb;
2699 value = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2700 / (fpspec->maximum - fpspec->minimum) + channel->min_value;
2701 GST_OBJECT_UNLOCK (self);
2704 GST_INFO_OBJECT (self, "%s: %d / %f", channel->label, value, new_value);
2705 gst_color_balance_value_changed (GST_COLOR_BALANCE (self), channel, value);
2706 g_atomic_int_set (&self->rebuild_filters, TRUE);
2713 gst_va_vpp_colorbalance_set_value (GstColorBalance * balance,
2714 GstColorBalanceChannel * channel, gint value)
2716 GstVaVpp *self = GST_VA_VPP (balance);
2718 if (g_str_has_suffix (channel->label, "HUE"))
2719 _set_cb_val (self, "hue", channel, value, &self->hue);
2720 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2721 _set_cb_val (self, "brightness", channel, value, &self->brightness);
2722 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2723 _set_cb_val (self, "contrast", channel, value, &self->contrast);
2724 else if (g_str_has_suffix (channel->label, "SATURATION"))
2725 _set_cb_val (self, "saturation", channel, value, &self->saturation);
2729 _get_cb_val (GstVaVpp * self, const gchar * name,
2730 GstColorBalanceChannel * channel, gfloat * cb, gint * val)
2732 GObjectClass *klass = G_OBJECT_CLASS (GST_VA_VPP_GET_CLASS (self));
2734 GParamSpecFloat *fpspec;
2736 pspec = g_object_class_find_property (klass, name);
2740 fpspec = G_PARAM_SPEC_FLOAT (pspec);
2742 GST_OBJECT_LOCK (self);
2743 *val = (*cb + fpspec->minimum) * (channel->max_value - channel->min_value)
2744 / (fpspec->maximum - fpspec->minimum) + channel->min_value;
2745 GST_OBJECT_UNLOCK (self);
2751 gst_va_vpp_colorbalance_get_value (GstColorBalance * balance,
2752 GstColorBalanceChannel * channel)
2754 GstVaVpp *self = GST_VA_VPP (balance);
2757 if (g_str_has_suffix (channel->label, "HUE"))
2758 _get_cb_val (self, "hue", channel, &self->hue, &value);
2759 else if (g_str_has_suffix (channel->label, "BRIGHTNESS"))
2760 _get_cb_val (self, "brightness", channel, &self->brightness, &value);
2761 else if (g_str_has_suffix (channel->label, "CONTRAST"))
2762 _get_cb_val (self, "contrast", channel, &self->contrast, &value);
2763 else if (g_str_has_suffix (channel->label, "SATURATION"))
2764 _get_cb_val (self, "saturation", channel, &self->saturation, &value);
2769 static GstColorBalanceType
2770 gst_va_vpp_colorbalance_get_balance_type (GstColorBalance * balance)
2772 return GST_COLOR_BALANCE_HARDWARE;
2776 gst_va_vpp_colorbalance_init (gpointer iface, gpointer data)
2778 GstColorBalanceInterface *cbiface = iface;
2780 cbiface->list_channels = gst_va_vpp_colorbalance_list_channels;
2781 cbiface->set_value = gst_va_vpp_colorbalance_set_value;
2782 cbiface->get_value = gst_va_vpp_colorbalance_get_value;
2783 cbiface->get_balance_type = gst_va_vpp_colorbalance_get_balance_type;