2 * Copyright (C) 2022 Intel Corporation
3 * Author: U. Artie Eoff <ullysses.a.eoff@intel.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-vacompositor
23 * @title: vacompositor
24 * @short_description: A VA-API based video compositing element
26 * A video compositing element that uses VA-API VPP to accelerate the compose,
27 * blending, and scaling of multiple inputs into one output.
29 * ## Example launch line
31 * gst-launch-1.0 videotestsrc \
32 * ! "video/x-raw,format=(string)NV12,width=640,height=480" \
33 * ! tee name=testsrc ! queue ! vacompositor name=comp \
34 * sink_1::width=160 sink_1::height=120 sink_1::xpos=480 \
35 * sink_1::ypos=360 sink_1::alpha=0.75 \
36 * ! autovideosink testsrc. ! queue ! comp.
47 #include "gstvacompositor.h"
49 #include <gst/va/gstva.h>
50 #include <gst/video/video.h>
51 #include <va/va_drmcommon.h>
53 #include "gstvacaps.h"
54 #include "gstvadisplay_priv.h"
55 #include "gstvafilter.h"
57 GST_DEBUG_CATEGORY_STATIC (gst_va_compositor_debug);
58 #define GST_CAT_DEFAULT gst_va_compositor_debug
67 struct _GstVaCompositorPad
69 GstVideoAggregatorPad parent;
91 #define DEFAULT_PAD_XPOS 0
92 #define DEFAULT_PAD_YPOS 0
93 #define DEFAULT_PAD_WIDTH 0
94 #define DEFAULT_PAD_HEIGHT 0
95 #define DEFAULT_PAD_ALPHA 1.0
97 G_DEFINE_TYPE (GstVaCompositorPad, gst_va_compositor_pad,
98 GST_TYPE_VIDEO_AGGREGATOR_PAD);
101 gst_va_compositor_pad_get_property (GObject * object, guint prop_id,
102 GValue * value, GParamSpec * pspec)
104 GstVaCompositorPad *self = GST_VA_COMPOSITOR_PAD (object);
108 g_value_set_int (value, self->xpos);
111 g_value_set_int (value, self->ypos);
114 g_value_set_int (value, self->width);
116 case PROP_PAD_HEIGHT:
117 g_value_set_int (value, self->height);
120 g_value_set_double (value, self->alpha);
123 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129 gst_va_compositor_pad_set_property (GObject * object, guint prop_id,
130 const GValue * value, GParamSpec * pspec)
132 GstVaCompositorPad *self = GST_VA_COMPOSITOR_PAD (object);
134 GST_OBJECT_LOCK (object);
137 self->xpos = g_value_get_int (value);
140 self->ypos = g_value_get_int (value);
143 self->width = g_value_get_int (value);
145 case PROP_PAD_HEIGHT:
146 self->height = g_value_get_int (value);
149 self->alpha = g_value_get_double (value);
152 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
155 GST_OBJECT_UNLOCK (object);
159 gst_va_compositor_pad_finalize (GObject * object)
161 GstVaCompositorPad *self = GST_VA_COMPOSITOR_PAD (object);
164 gst_buffer_pool_set_active (self->pool, FALSE);
165 gst_clear_object (&self->pool);
168 G_OBJECT_CLASS (gst_va_compositor_pad_parent_class)->finalize (object);
172 gst_va_compositor_pad_init (GstVaCompositorPad * self)
175 self->xpos = DEFAULT_PAD_XPOS;
176 self->ypos = DEFAULT_PAD_YPOS;
177 self->width = DEFAULT_PAD_WIDTH;
178 self->height = DEFAULT_PAD_HEIGHT;
179 self->alpha = DEFAULT_PAD_ALPHA;
183 gst_va_compositor_pad_class_init (GstVaCompositorPadClass * klass)
185 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
186 GstVideoAggregatorPadClass *vaggpad_class =
187 GST_VIDEO_AGGREGATOR_PAD_CLASS (klass);
189 gobject_class->finalize = gst_va_compositor_pad_finalize;
190 gobject_class->get_property = gst_va_compositor_pad_get_property;
191 gobject_class->set_property = gst_va_compositor_pad_set_property;
193 g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
194 g_param_spec_int ("xpos", "X Position", "X Position of the picture",
195 G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
196 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
197 g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
198 g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
199 G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
200 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
201 g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
202 g_param_spec_int ("width", "Width",
203 "Width of the picture (0, to use the width of the input frame)",
204 0, G_MAXINT, DEFAULT_PAD_WIDTH,
205 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
206 g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
207 g_param_spec_int ("height", "Height",
208 "Height of the picture (0, to use the height of the input frame)",
209 0, G_MAXINT, DEFAULT_PAD_HEIGHT,
210 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
211 g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
212 g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
214 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
216 /* Don't use mapped video frames. Handle video buffers directly */
217 vaggpad_class->prepare_frame = NULL;
218 vaggpad_class->clean_frame = NULL;
221 #define GST_VA_COMPOSITOR(obj) ((GstVaCompositor *) obj)
222 #define GST_VA_COMPOSITOR_CLASS(klass) ((GstVaCompositorClass *) klass)
223 #define GST_VA_COMPOSITOR_GET_CLASS(obj) \
224 (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaCompositorClass))
226 typedef struct _GstVaCompositor GstVaCompositor;
227 typedef struct _GstVaCompositorClass GstVaCompositorClass;
229 struct _GstVaCompositorClass
231 GstVideoAggregatorClass parent_class;
234 gchar *render_device_path;
237 struct _GstVaCompositor
239 GstVideoAggregator parent;
241 GstVaDisplay *display;
244 GstVideoInfo other_info; /* downstream info */
245 GstBufferPool *other_pool; /* downstream pool */
247 guint32 scale_method;
252 gchar *render_device_path;
258 PROP_DEVICE_PATH = 1,
263 static GParamSpec *properties[N_PROPERTIES];
264 static GstElementClass *parent_class = NULL;
267 gst_va_compositor_set_property (GObject * object, guint prop_id,
268 const GValue * value, GParamSpec * pspec)
270 GstVaCompositor *self = GST_VA_COMPOSITOR (object);
273 case PROP_SCALE_METHOD:
275 GST_OBJECT_LOCK (object);
276 self->scale_method = g_value_get_enum (value);
277 GST_OBJECT_UNLOCK (object);
281 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286 gst_va_compositor_get_property (GObject * object, guint prop_id,
287 GValue * value, GParamSpec * pspec)
289 GstVaCompositor *self = GST_VA_COMPOSITOR (object);
292 case PROP_DEVICE_PATH:
294 if (!(self->display && GST_IS_VA_DISPLAY_DRM (self->display))) {
295 g_value_set_string (value, NULL);
298 g_object_get_property (G_OBJECT (self->display), "path", value);
301 case PROP_SCALE_METHOD:
303 GST_OBJECT_LOCK (object);
304 g_value_set_enum (value, self->scale_method);
305 GST_OBJECT_UNLOCK (object);
309 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
314 gst_va_compositor_start (GstAggregator * agg)
316 GstElement *element = GST_ELEMENT (agg);
317 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
318 GstVaCompositorClass *klass = GST_VA_COMPOSITOR_GET_CLASS (agg);
320 if (!gst_va_ensure_element_data (element, klass->render_device_path,
323 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
325 self->filter = gst_va_filter_new (self->display);
326 if (!gst_va_filter_open (self->filter))
329 return GST_AGGREGATOR_CLASS (parent_class)->start (agg);
333 gst_va_compositor_stop (GstAggregator * agg)
335 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
337 gst_va_filter_close (self->filter);
338 gst_clear_object (&self->filter);
339 gst_clear_object (&self->display);
340 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
342 return GST_AGGREGATOR_CLASS (parent_class)->stop (agg);
346 gst_va_compositor_dispose (GObject * object)
348 GstVaCompositor *self = GST_VA_COMPOSITOR (object);
350 if (self->other_pool) {
351 gst_buffer_pool_set_active (self->other_pool, FALSE);
352 gst_clear_object (&self->other_pool);
355 gst_clear_object (&self->display);
357 G_OBJECT_CLASS (parent_class)->dispose (object);
361 gst_va_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
362 const gchar * req_name, const GstCaps * caps)
364 GstPad *newpad = GST_PAD (GST_ELEMENT_CLASS
365 (parent_class)->request_new_pad (element, templ, req_name, caps));
368 GST_DEBUG_OBJECT (element, "could not create/add pad");
370 gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
371 GST_OBJECT_NAME (newpad));
377 gst_va_compositor_release_pad (GstElement * element, GstPad * pad)
379 GstVaCompositor *self = GST_VA_COMPOSITOR (element);
381 gst_child_proxy_child_removed (GST_CHILD_PROXY (self), G_OBJECT (pad),
382 GST_OBJECT_NAME (pad));
384 GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
388 gst_va_compositor_set_context (GstElement * element, GstContext * context)
390 GstVaDisplay *old_display, *new_display;
391 GstVaCompositor *self = GST_VA_COMPOSITOR (element);
392 GstVaCompositorClass *klass = GST_VA_COMPOSITOR_GET_CLASS (self);
395 old_display = self->display ? gst_object_ref (self->display) : NULL;
396 ret = gst_va_handle_set_context (element, context, klass->render_device_path,
398 new_display = self->display ? gst_object_ref (self->display) : NULL;
401 || (old_display && new_display && old_display != new_display
403 GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
404 ("Can't replace VA display while operating"), (NULL));
407 gst_clear_object (&old_display);
408 gst_clear_object (&new_display);
410 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
414 _handle_context_query (GstVaCompositor * self, GstQuery * query)
416 GstVaDisplay *display = NULL;
417 gboolean ret = FALSE;
419 gst_object_replace ((GstObject **) & display, (GstObject *) self->display);
420 ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query, display);
421 gst_clear_object (&display);
427 gst_va_compositor_sink_getcaps (GstPad * pad, GstCaps * filter)
430 GstCaps *template_caps;
431 GstCaps *filtered_caps;
432 GstCaps *returned_caps;
434 template_caps = gst_pad_get_pad_template_caps (pad);
436 sinkcaps = gst_pad_get_current_caps (pad);
438 sinkcaps = gst_caps_ref (template_caps);
440 sinkcaps = gst_caps_merge (sinkcaps, gst_caps_ref (template_caps));
444 filtered_caps = gst_caps_intersect (sinkcaps, filter);
445 gst_caps_unref (sinkcaps);
447 filtered_caps = sinkcaps;
450 returned_caps = gst_caps_intersect (filtered_caps, template_caps);
452 gst_caps_unref (template_caps);
453 gst_caps_unref (filtered_caps);
455 GST_DEBUG_OBJECT (pad, "returning %" GST_PTR_FORMAT, returned_caps);
457 return returned_caps;
461 gst_va_compositor_sink_acceptcaps (GstPad * pad, GstCaps * caps)
464 GstCaps *template_caps;
466 template_caps = gst_pad_get_pad_template_caps (pad);
467 template_caps = gst_caps_make_writable (template_caps);
469 ret = gst_caps_can_intersect (caps, template_caps);
470 GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
471 (ret ? "" : "not "), caps);
472 gst_caps_unref (template_caps);
478 gst_va_compositor_sink_query (GstAggregator * agg, GstAggregatorPad * pad,
481 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
483 switch (GST_QUERY_TYPE (query)) {
484 case GST_QUERY_CONTEXT:
486 if (_handle_context_query (self, query))
492 GstCaps *filter, *caps;
494 gst_query_parse_caps (query, &filter);
495 caps = gst_va_compositor_sink_getcaps (GST_PAD (pad), filter);
496 gst_query_set_caps_result (query, caps);
497 gst_caps_unref (caps);
500 case GST_QUERY_ACCEPT_CAPS:
505 gst_query_parse_accept_caps (query, &caps);
506 ret = gst_va_compositor_sink_acceptcaps (GST_PAD (pad), caps);
507 gst_query_set_accept_caps_result (query, ret);
514 return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, pad, query);
518 gst_va_compositor_src_query (GstAggregator * agg, GstQuery * query)
520 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
522 switch (GST_QUERY_TYPE (query)) {
523 case GST_QUERY_CONTEXT:
524 if (_handle_context_query (self, query))
531 return GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
534 static GstAllocator *
535 gst_va_compositor_allocator_from_caps (GstVaCompositor * self, GstCaps * caps)
537 GstAllocator *allocator = NULL;
539 if (gst_caps_is_dmabuf (caps)) {
540 allocator = gst_va_dmabuf_allocator_new (self->display);
542 GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
543 allocator = gst_va_allocator_new (self->display, surface_formats);
549 /* Answer upstream allocation query. */
551 gst_va_compositor_propose_allocation (GstAggregator * agg,
552 GstAggregatorPad * aggpad, GstQuery * decide_query, GstQuery * query)
554 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
555 GstAllocator *allocator = NULL;
556 GstAllocationParams params = { 0, };
560 gboolean update_allocator = FALSE;
561 guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
563 gst_query_parse_allocation (query, &caps, NULL);
568 if (!gst_video_info_from_caps (&info, caps))
571 if (gst_query_get_n_allocation_pools (query) > 0)
574 size = GST_VIDEO_INFO_SIZE (&info);
576 if (gst_query_get_n_allocation_params (query) > 0) {
577 gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
578 if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator)
579 && !GST_IS_VA_ALLOCATOR (allocator))
580 gst_clear_object (&allocator);
581 update_allocator = TRUE;
583 gst_allocation_params_init (¶ms);
587 if (!(allocator = gst_va_compositor_allocator_from_caps (self, caps)))
591 /* Now we have a VA-based allocator */
593 pool = gst_va_pool_new_with_config (caps, size, 1, 0, usage_hint,
594 GST_VA_FEATURE_AUTO, allocator, ¶ms);
596 gst_object_unref (allocator);
600 if (update_allocator)
601 gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
603 gst_query_add_allocation_param (query, allocator, ¶ms);
605 gst_query_add_allocation_pool (query, pool, size, 1, 0);
607 GST_DEBUG_OBJECT (self,
608 "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
611 gst_object_unref (allocator);
612 gst_object_unref (pool);
614 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
620 GST_ERROR_OBJECT (self, "failed to set config");
625 static GstBufferPool *
626 _create_other_pool (GstAllocator * allocator, GstAllocationParams * params,
627 GstCaps * caps, guint size)
629 GstBufferPool *pool = NULL;
630 GstStructure *config;
632 pool = gst_video_buffer_pool_new ();
633 config = gst_buffer_pool_get_config (pool);
635 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
636 gst_buffer_pool_config_set_allocator (config, allocator, params);
637 if (!gst_buffer_pool_set_config (pool, config)) {
638 gst_clear_object (&pool);
644 /* configure the allocation query that was answered downstream */
646 gst_va_compositor_decide_allocation (GstAggregator * agg, GstQuery * query)
648 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
649 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
651 GstAllocator *allocator = NULL, *other_allocator = NULL;
652 GstAllocationParams params, other_params;
653 GstBufferPool *pool = NULL, *other_pool = NULL;
654 GstCaps *caps = NULL;
655 GstStructure *config;
657 guint min, max, size = 0, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
658 gboolean update_pool, update_allocator, has_videometa, copy_frames;
659 gboolean dont_use_other_pool = FALSE;
661 gst_query_parse_allocation (query, &caps, NULL);
663 gst_allocation_params_init (&other_params);
664 gst_allocation_params_init (¶ms);
666 if (!gst_video_info_from_caps (&info, caps)) {
667 GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
671 if (gst_query_get_n_allocation_params (query) > 0) {
672 GstVaDisplay *display;
674 gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
675 display = gst_va_allocator_peek_display (allocator);
677 /* save the allocator for the other pool */
678 other_allocator = allocator;
680 } else if (display != self->display) {
681 /* The allocator and pool belong to other display, we should not use. */
682 gst_clear_object (&allocator);
683 dont_use_other_pool = TRUE;
686 update_allocator = TRUE;
688 update_allocator = FALSE;
691 if (gst_query_get_n_allocation_pools (query) > 0) {
692 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
695 if (!GST_IS_VA_POOL (pool)) {
696 GST_DEBUG_OBJECT (self,
697 "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
700 } else if (dont_use_other_pool) {
701 gst_clear_object (&pool);
707 size = GST_VIDEO_INFO_SIZE (&info);
714 if (gst_caps_is_dmabuf (caps) && GST_VIDEO_INFO_IS_RGB (&info))
715 usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
716 if (!(allocator = gst_va_compositor_allocator_from_caps (self, caps)))
721 pool = gst_va_pool_new ();
723 config = gst_buffer_pool_get_config (pool);
724 gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
725 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
726 gst_buffer_pool_config_set_params (config, caps, size, min, max);
727 gst_buffer_pool_config_set_va_allocation_params (config, usage_hint,
728 GST_VA_FEATURE_AUTO);
729 if (!gst_buffer_pool_set_config (pool, config)) {
730 gst_object_unref (allocator);
731 gst_object_unref (pool);
735 if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
736 gst_va_dmabuf_allocator_get_format (allocator, &vagg->info, NULL);
737 } else if (GST_IS_VA_ALLOCATOR (allocator)) {
738 gst_va_allocator_get_format (allocator, &vagg->info, NULL, NULL);
741 if (update_allocator)
742 gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
744 gst_query_add_allocation_param (query, allocator, ¶ms);
747 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
749 gst_query_add_allocation_pool (query, pool, size, min, max);
751 has_videometa = gst_query_find_allocation_meta (query,
752 GST_VIDEO_META_API_TYPE, NULL);
754 copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
755 && gst_caps_is_raw (caps));
758 gst_object_replace ((GstObject **) & self->other_pool,
759 (GstObject *) other_pool);
762 _create_other_pool (other_allocator, &other_params, caps, size);
764 GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
767 gst_clear_object (&self->other_pool);
770 GST_DEBUG_OBJECT (self,
771 "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
774 gst_object_unref (allocator);
775 gst_object_unref (pool);
776 gst_clear_object (&other_allocator);
777 gst_clear_object (&other_pool);
782 static GstBufferPool *
783 _get_sinkpad_pool (GstVaCompositor * self, GstVaCompositorPad * pad)
785 GstAllocator *allocator;
786 GstAllocationParams params = { 0, };
789 guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
794 gst_allocation_params_init (¶ms);
796 caps = gst_pad_get_current_caps (GST_PAD (pad));
799 if (!gst_video_info_from_caps (&info, caps)) {
800 GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
801 gst_caps_unref (caps);
805 size = GST_VIDEO_INFO_SIZE (&info);
807 allocator = gst_va_compositor_allocator_from_caps (self, caps);
808 pad->pool = gst_va_pool_new_with_config (caps, size, 1, 0, usage_hint,
809 GST_VA_FEATURE_AUTO, allocator, ¶ms);
810 gst_caps_unref (caps);
813 gst_object_unref (allocator);
817 if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
818 gst_va_dmabuf_allocator_get_format (allocator, &info, NULL);
819 } else if (GST_IS_VA_ALLOCATOR (allocator)) {
820 gst_va_allocator_get_format (allocator, &info, NULL, NULL);
823 gst_object_unref (allocator);
825 if (!gst_buffer_pool_set_active (pad->pool, TRUE)) {
826 GST_WARNING_OBJECT (self, "failed to active the sinkpad pool %"
827 GST_PTR_FORMAT, pad->pool);
835 _get_plane_data_size (GstVideoInfo * info, guint plane)
837 gint comp[GST_VIDEO_MAX_COMPONENTS];
838 gint height, padded_height;
840 gst_video_format_info_component (info->finfo, plane, comp);
842 height = GST_VIDEO_INFO_HEIGHT (info);
844 GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], height);
846 return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
850 _try_import_dmabuf_unlocked (GstVaCompositor * self, GstVideoInfo * info,
854 GstMemory *mems[GST_VIDEO_MAX_PLANES];
855 guint i, n_mem, n_planes;
856 gsize offset[GST_VIDEO_MAX_PLANES];
857 uintptr_t fd[GST_VIDEO_MAX_PLANES];
859 n_planes = GST_VIDEO_INFO_N_PLANES (info);
860 n_mem = gst_buffer_n_memory (inbuf);
861 meta = gst_buffer_get_video_meta (inbuf);
863 /* This will eliminate most non-dmabuf out there */
864 if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
867 /* We cannot have multiple dmabuf per plane */
868 if (n_mem > n_planes)
871 /* Update video info based on video meta */
873 GST_VIDEO_INFO_WIDTH (info) = meta->width;
874 GST_VIDEO_INFO_HEIGHT (info) = meta->height;
876 for (i = 0; i < meta->n_planes; i++) {
877 GST_VIDEO_INFO_PLANE_OFFSET (info, i) = meta->offset[i];
878 GST_VIDEO_INFO_PLANE_STRIDE (info, i) = meta->stride[i];
882 /* Find and validate all memories */
883 for (i = 0; i < n_planes; i++) {
889 plane_size = _get_plane_data_size (info, i);
891 if (!gst_buffer_find_memory (inbuf, info->offset[i], plane_size,
892 &mem_idx, &length, &mem_skip))
895 /* We can't have more then one dmabuf per plane */
899 mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
901 /* And all memory found must be dmabuf */
902 if (!gst_is_dmabuf_memory (mems[i]))
905 offset[i] = mems[i]->offset + mem_skip;
906 fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
909 /* Now create a VASurfaceID for the buffer */
910 return gst_va_dmabuf_memories_setup (self->display, info, n_planes,
911 mems, fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ);
914 extern GRecMutex GST_VA_SHARED_LOCK;
917 _try_import_buffer (GstVaCompositor * self, GstVideoInfo * info,
923 surface = gst_va_buffer_get_surface (inbuf);
924 if (surface != VA_INVALID_ID &&
925 (gst_va_buffer_peek_display (inbuf) == self->display))
928 g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
929 ret = _try_import_dmabuf_unlocked (self, info, inbuf);
930 g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
936 gst_va_compositor_import_buffer (GstVaCompositor * self,
937 GstVaCompositorPad * pad, GstBuffer * inbuf, GstBuffer ** buf)
939 GstBuffer *buffer = NULL;
944 GstVideoFrame in_frame, out_frame;
945 gboolean imported, copied;
947 caps = gst_pad_get_current_caps (GST_PAD (pad));
949 return GST_FLOW_ERROR;
950 if (!gst_video_info_from_caps (&info, caps)) {
951 GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
952 gst_caps_unref (caps);
953 return GST_FLOW_ERROR;
955 gst_caps_unref (caps);
957 imported = _try_import_buffer (self, &info, inbuf);
959 *buf = gst_buffer_ref (inbuf);
963 GST_LOG_OBJECT (self, "copying input frame");
965 /* input buffer doesn't come from a vapool, thus it is required to
966 * have a pool, grab from it a new buffer and copy the input
967 * buffer to the new one */
968 if (!(pool = _get_sinkpad_pool (self, pad)))
969 return GST_FLOW_ERROR;
971 ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
972 if (ret != GST_FLOW_OK)
975 if (!gst_video_frame_map (&in_frame, &info, inbuf, GST_MAP_READ))
978 if (!gst_video_frame_map (&out_frame, &info, buffer, GST_MAP_WRITE)) {
979 gst_video_frame_unmap (&in_frame);
983 copied = gst_video_frame_copy (&out_frame, &in_frame);
985 gst_video_frame_unmap (&out_frame);
986 gst_video_frame_unmap (&in_frame);
997 GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL),
998 ("invalid video buffer received"));
1000 gst_buffer_unref (buffer);
1005 typedef struct _GstVaCompositorSampleGenerator GstVaCompositorSampleGenerator;
1006 struct _GstVaCompositorSampleGenerator
1008 GstVaCompositor *comp;
1010 GstVaComposeSample sample;
1013 static GstVaComposeSample *
1014 gst_va_compositor_sample_next (gpointer data)
1016 GstVaCompositorSampleGenerator *generator;
1017 GstVideoAggregatorPad *vaggpad;
1018 GstVaCompositorPad *pad;
1022 GstVideoCropMeta *crop = NULL;
1024 generator = (GstVaCompositorSampleGenerator *) data;
1026 /* at the end of the generator? */
1027 while (generator->current) {
1028 /* get the current sinkpad for processing */
1029 vaggpad = GST_VIDEO_AGGREGATOR_PAD (generator->current->data);
1031 /* increment to next sinkpad */
1032 generator->current = generator->current->next;
1036 generator->sample = (GstVaComposeSample) { 0, };
1039 /* current sinkpad may not be queueing buffers yet (e.g. timestamp-offset)
1040 * or it may have reached EOS */
1041 if (!gst_video_aggregator_pad_has_current_buffer (vaggpad))
1044 inbuf = gst_video_aggregator_pad_get_current_buffer (vaggpad);
1045 pad = GST_VA_COMPOSITOR_PAD (vaggpad);
1047 res = gst_va_compositor_import_buffer (generator->comp, pad, inbuf, &buf);
1048 if (res != GST_FLOW_OK)
1049 return &generator->sample;
1051 crop = gst_buffer_get_video_crop_meta (buf);
1053 GST_OBJECT_LOCK (vaggpad);
1055 generator->sample = (GstVaComposeSample) {
1057 .input_region = (VARectangle) {
1058 .x = crop ? crop->x : 0,
1059 .y = crop ? crop->y : 0,
1060 .width = crop ? crop->width : GST_VIDEO_INFO_WIDTH (&vaggpad->info),
1061 .height = crop ? crop->height : GST_VIDEO_INFO_HEIGHT (&vaggpad->info),
1063 .output_region = (VARectangle) {
1066 .width = (pad->width == DEFAULT_PAD_WIDTH)
1067 ? GST_VIDEO_INFO_WIDTH (&vaggpad->info) : pad->width,
1068 .height = (pad->height == DEFAULT_PAD_HEIGHT)
1069 ? GST_VIDEO_INFO_HEIGHT (&vaggpad->info) : pad->height,
1071 .alpha = pad->alpha,
1074 GST_OBJECT_UNLOCK (vaggpad);
1076 return &generator->sample;
1083 gst_va_compositor_copy_output_buffer (GstVaCompositor * self,
1084 GstBuffer * src_buf, GstBuffer * dst_buf)
1086 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (self);
1087 GstVideoFrame src_frame, dst_frame;
1089 GST_LOG_OBJECT (self, "copying output buffer");
1091 if (!gst_video_frame_map (&src_frame, &vagg->info, src_buf, GST_MAP_READ)) {
1092 GST_ERROR_OBJECT (self, "couldn't map source buffer");
1096 if (!gst_video_frame_map (&dst_frame, &self->other_info, dst_buf,
1098 GST_ERROR_OBJECT (self, "couldn't map output buffer");
1099 gst_video_frame_unmap (&src_frame);
1103 if (!gst_video_frame_copy (&dst_frame, &src_frame)) {
1104 GST_ERROR_OBJECT (self, "couldn't copy output buffer");
1105 gst_video_frame_unmap (&src_frame);
1106 gst_video_frame_unmap (&dst_frame);
1110 gst_video_frame_unmap (&src_frame);
1111 gst_video_frame_unmap (&dst_frame);
1116 static GstFlowReturn
1117 gst_va_compositor_aggregate_frames (GstVideoAggregator * vagg,
1120 GstVaCompositor *self = GST_VA_COMPOSITOR (vagg);
1121 GstVaCompositorSampleGenerator generator;
1122 GstVaComposeTransaction tx;
1123 GstBuffer *vabuffer;
1124 gboolean need_copy = FALSE;
1125 GstFlowReturn ret = GST_FLOW_OK;
1127 if (self->other_pool) {
1128 /* create a va buffer for filter */
1129 ret = GST_VIDEO_AGGREGATOR_CLASS (parent_class)->create_output_buffer
1131 if (ret != GST_FLOW_OK)
1136 /* already a va buffer */
1137 vabuffer = gst_buffer_ref (outbuf);
1141 generator = (GstVaCompositorSampleGenerator) {
1143 .current = GST_ELEMENT (self)->sinkpads,
1145 tx = (GstVaComposeTransaction) {
1146 .next = gst_va_compositor_sample_next,
1148 .user_data = (gpointer) &generator,
1152 GST_OBJECT_LOCK (self);
1154 if (!gst_va_filter_set_scale_method (self->filter, self->scale_method))
1155 GST_WARNING_OBJECT (self, "couldn't set filter scale method");
1157 if (!gst_va_filter_compose (self->filter, &tx)) {
1158 GST_ERROR_OBJECT (self, "couldn't apply filter");
1159 ret = GST_FLOW_ERROR;
1162 GST_OBJECT_UNLOCK (self);
1164 if (ret != GST_FLOW_OK)
1167 if (need_copy && !gst_va_compositor_copy_output_buffer (self, vabuffer,
1169 GST_ERROR_OBJECT (self, "couldn't copy va buffer to output buffer");
1170 ret = GST_FLOW_ERROR;
1174 gst_buffer_unref (vabuffer);
1178 static GstFlowReturn
1179 gst_va_compositor_create_output_buffer (GstVideoAggregator * vagg,
1180 GstBuffer ** outbuf)
1182 GstVaCompositor *self = GST_VA_COMPOSITOR (vagg);
1187 if (!self->other_pool)
1188 /* no copy necessary, so use a va buffer directly */
1189 return GST_VIDEO_AGGREGATOR_CLASS (parent_class)->create_output_buffer
1192 /* use output buffers from downstream pool for copy */
1193 if (!gst_buffer_pool_is_active (self->other_pool) &&
1194 !gst_buffer_pool_set_active (self->other_pool, TRUE)) {
1195 GST_ERROR_OBJECT (self, "failed to activate other pool %"
1196 GST_PTR_FORMAT, self->other_pool);
1197 return GST_FLOW_ERROR;
1200 /* acquire a buffer from downstream pool for copy */
1201 ret = gst_buffer_pool_acquire_buffer (self->other_pool, outbuf, NULL);
1202 if (ret != GST_FLOW_OK || !*outbuf) {
1203 GST_ERROR_OBJECT (self, "failed to acquire output buffer");
1204 return GST_FLOW_ERROR;
1211 gst_va_compositor_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
1213 GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
1215 if (!gst_video_info_from_caps (&self->other_info, caps)) {
1216 GST_ERROR_OBJECT (self, "invalid caps");
1220 if (self->other_pool) {
1221 gst_buffer_pool_set_active (self->other_pool, FALSE);
1222 gst_clear_object (&self->other_pool);
1225 return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1229 gst_va_compositor_pad_get_output_size (GstVaCompositorPad * pad, gint * width,
1232 GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
1233 *width = (pad->width == DEFAULT_PAD_WIDTH)
1234 ? GST_VIDEO_INFO_WIDTH (&vaggpad->info) : pad->width;
1235 *height = (pad->height == DEFAULT_PAD_HEIGHT)
1236 ? GST_VIDEO_INFO_HEIGHT (&vaggpad->info) : pad->height;
1238 *width += MAX (pad->xpos, 0);
1239 *height += MAX (pad->ypos, 0);
1243 gst_va_compositor_fixate_src_caps (GstAggregator * agg, GstCaps * caps)
1245 GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1247 gint best_width = -1, best_height = -1;
1248 gint best_fps_n = -1, best_fps_d = -1;
1249 gdouble best_fps = 0.;
1250 GstCaps *ret = NULL;
1253 ret = gst_caps_make_writable (caps);
1255 GST_OBJECT_LOCK (vagg);
1256 for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1257 GstVideoAggregatorPad *vaggpad = l->data;
1258 GstVaCompositorPad *pad = GST_VA_COMPOSITOR_PAD (vaggpad);
1259 gint this_width, this_height;
1263 fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1264 fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1266 gst_va_compositor_pad_get_output_size (pad, &this_width, &this_height);
1268 if (best_width < this_width)
1269 best_width = this_width;
1270 if (best_height < this_height)
1271 best_height = this_height;
1276 gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1278 if (best_fps < cur_fps) {
1284 GST_OBJECT_UNLOCK (vagg);
1286 if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1292 s = gst_caps_get_structure (ret, 0);
1293 gst_structure_fixate_field_nearest_int (s, "width", best_width);
1294 gst_structure_fixate_field_nearest_int (s, "height", best_height);
1295 if (gst_structure_has_field (s, "framerate")) {
1296 gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1299 gst_structure_set (s, "framerate", GST_TYPE_FRACTION, best_fps_n,
1303 return gst_caps_fixate (ret);
1307 static const gchar *caps_str =
1308 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
1309 "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
1310 GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
1311 "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR }");
1315 gst_va_compositor_class_init (gpointer g_class, gpointer class_data)
1317 GstCaps *doc_caps, *caps = NULL;
1318 GstPadTemplate *sink_pad_templ, *src_pad_templ;
1319 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
1320 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1321 GstAggregatorClass *agg_class = GST_AGGREGATOR_CLASS (g_class);
1322 GstVideoAggregatorClass *vagg_class = GST_VIDEO_AGGREGATOR_CLASS (g_class);
1323 GstVaCompositorClass *klass = GST_VA_COMPOSITOR_CLASS (g_class);
1324 GstVaDisplay *display;
1325 GstVaFilter *filter;
1326 struct CData *cdata = class_data;
1329 parent_class = g_type_class_peek_parent (g_class);
1331 klass->render_device_path = g_strdup (cdata->render_device_path);
1333 if (cdata->description) {
1334 long_name = g_strdup_printf ("VA-API Video Compositor in %s",
1335 cdata->description);
1337 long_name = g_strdup ("VA-API Video Compositor");
1340 display = gst_va_display_drm_new_from_path (klass->render_device_path);
1341 filter = gst_va_filter_new (display);
1343 if (gst_va_filter_open (filter)) {
1344 caps = gst_va_filter_get_caps (filter);
1346 caps = gst_caps_from_string (caps_str);
1349 object_class->dispose = GST_DEBUG_FUNCPTR (gst_va_compositor_dispose);
1350 object_class->get_property =
1351 GST_DEBUG_FUNCPTR (gst_va_compositor_get_property);
1352 object_class->set_property =
1353 GST_DEBUG_FUNCPTR (gst_va_compositor_set_property);
1355 gst_element_class_set_static_metadata (element_class, long_name,
1356 "Filter/Editor/Video/Compositor/Hardware",
1357 "VA-API based video compositor",
1358 "U. Artie Eoff <ullysses.a.eoff@intel.com>");
1360 element_class->request_new_pad =
1361 GST_DEBUG_FUNCPTR (gst_va_compositor_request_new_pad);
1362 element_class->release_pad =
1363 GST_DEBUG_FUNCPTR (gst_va_compositor_release_pad);
1364 element_class->set_context =
1365 GST_DEBUG_FUNCPTR (gst_va_compositor_set_context);
1367 doc_caps = gst_caps_from_string (caps_str);
1369 sink_pad_templ = gst_pad_template_new_with_gtype ("sink_%u", GST_PAD_SINK,
1370 GST_PAD_REQUEST, caps, GST_TYPE_VA_COMPOSITOR_PAD);
1371 gst_element_class_add_pad_template (element_class, sink_pad_templ);
1372 gst_pad_template_set_documentation_caps (sink_pad_templ,
1373 gst_caps_ref (doc_caps));
1374 gst_type_mark_as_plugin_api (GST_TYPE_VA_COMPOSITOR_PAD, 0);
1376 src_pad_templ = gst_pad_template_new_with_gtype ("src", GST_PAD_SRC,
1377 GST_PAD_ALWAYS, caps, GST_TYPE_AGGREGATOR_PAD);
1378 gst_element_class_add_pad_template (element_class, src_pad_templ);
1379 gst_pad_template_set_documentation_caps (src_pad_templ,
1380 gst_caps_ref (doc_caps));
1382 gst_caps_unref (doc_caps);
1383 gst_caps_unref (caps);
1385 agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_va_compositor_sink_query);
1386 agg_class->src_query = GST_DEBUG_FUNCPTR (gst_va_compositor_src_query);
1387 agg_class->start = GST_DEBUG_FUNCPTR (gst_va_compositor_start);
1388 agg_class->stop = GST_DEBUG_FUNCPTR (gst_va_compositor_stop);
1389 agg_class->propose_allocation =
1390 GST_DEBUG_FUNCPTR (gst_va_compositor_propose_allocation);
1391 agg_class->fixate_src_caps =
1392 GST_DEBUG_FUNCPTR (gst_va_compositor_fixate_src_caps);
1393 agg_class->negotiated_src_caps =
1394 GST_DEBUG_FUNCPTR (gst_va_compositor_negotiated_src_caps);
1395 agg_class->decide_allocation =
1396 GST_DEBUG_FUNCPTR (gst_va_compositor_decide_allocation);
1398 vagg_class->aggregate_frames =
1399 GST_DEBUG_FUNCPTR (gst_va_compositor_aggregate_frames);
1400 vagg_class->create_output_buffer =
1401 GST_DEBUG_FUNCPTR (gst_va_compositor_create_output_buffer);
1403 properties[PROP_DEVICE_PATH] = g_param_spec_string ("device-path",
1404 "Device Path", "DRM device path", NULL,
1405 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1407 properties[PROP_SCALE_METHOD] = g_param_spec_enum ("scale-method",
1408 "Scale Method", "Scale method to use", GST_TYPE_VA_SCALE_METHOD,
1409 VA_FILTER_SCALING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1411 gst_type_mark_as_plugin_api (GST_TYPE_VA_SCALE_METHOD, 0);
1413 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
1416 g_free (cdata->description);
1417 g_free (cdata->render_device_path);
1419 gst_object_unref (filter);
1420 gst_object_unref (display);
1424 gst_va_compositor_child_proxy_get_child_by_index (GstChildProxy * proxy,
1427 GstVaCompositor *self = GST_VA_COMPOSITOR (proxy);
1428 GObject *obj = NULL;
1430 GST_OBJECT_LOCK (self);
1431 obj = g_list_nth_data (GST_ELEMENT_CAST (self)->sinkpads, index);
1433 gst_object_ref (obj);
1434 GST_OBJECT_UNLOCK (self);
1440 gst_va_compositor_child_proxy_get_children_count (GstChildProxy * proxy)
1442 GstVaCompositor *self = GST_VA_COMPOSITOR (proxy);
1445 GST_OBJECT_LOCK (self);
1446 count = GST_ELEMENT_CAST (self)->numsinkpads;
1447 GST_OBJECT_UNLOCK (self);
1448 GST_INFO_OBJECT (self, "Children Count: %d", count);
1454 gst_va_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1456 GstChildProxyInterface *iface = (GstChildProxyInterface *) g_iface;
1458 iface->get_child_by_index = gst_va_compositor_child_proxy_get_child_by_index;
1459 iface->get_children_count = gst_va_compositor_child_proxy_get_children_count;
1463 gst_va_compositor_init (GTypeInstance * instance, gpointer g_class)
1465 GstVaCompositor *self = GST_VA_COMPOSITOR (instance);
1467 self->other_pool = NULL;
1471 _register_debug_category (gpointer data)
1473 GST_DEBUG_CATEGORY_INIT (gst_va_compositor_debug, "vacompositor", 0,
1474 "VA Video Compositor");
1480 gst_va_compositor_register (GstPlugin * plugin, GstVaDevice * device,
1483 static GOnce debug_once = G_ONCE_INIT;
1485 GTypeInfo type_info = {
1486 .class_size = sizeof (GstVaCompositorClass),
1487 .class_init = gst_va_compositor_class_init,
1488 .instance_size = sizeof (GstVaCompositor),
1489 .instance_init = gst_va_compositor_init,
1491 GInterfaceInfo interface_info = {
1492 (GInterfaceInitFunc) gst_va_compositor_child_proxy_init,
1494 struct CData *cdata;
1496 gchar *type_name, *feature_name;
1498 g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
1499 g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
1501 cdata = g_new (struct CData, 1);
1502 cdata->description = NULL;
1503 cdata->render_device_path = g_strdup (device->render_device_path);
1505 type_info.class_data = cdata;
1507 type_name = g_strdup ("GstVaCompositor");
1508 feature_name = g_strdup ("vacompositor");
1510 /* The first compositor to be registered should use a constant
1511 * name, like vacompositor, for any additional compositors, we
1512 * create unique names, using the render device name. */
1513 if (g_type_from_name (type_name)) {
1514 gchar *basename = g_path_get_basename (device->render_device_path);
1516 g_free (feature_name);
1517 type_name = g_strdup_printf ("GstVa%sCompositor", basename);
1518 feature_name = g_strdup_printf ("va%scompositor", basename);
1519 cdata->description = basename;
1521 /* lower rank for non-first device */
1526 g_once (&debug_once, _register_debug_category, NULL);
1528 type = g_type_register_static (GST_TYPE_VIDEO_AGGREGATOR, type_name,
1530 g_type_add_interface_static (type, GST_TYPE_CHILD_PROXY, &interface_info);
1532 ret = gst_element_register (plugin, feature_name, rank, type);
1535 g_free (feature_name);