facad18ce620902e75566a18907d575a929d1b81
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / va / gstvacompositor.c
1 /* GStreamer
2  * Copyright (C) 2022 Intel Corporation
3  *     Author: U. Artie Eoff <ullysses.a.eoff@intel.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:element-vacompositor
23  * @title: vacompositor
24  * @short_description: A VA-API based video compositing element
25  *
26  * A video compositing element that uses VA-API VPP to accelerate the compose,
27  * blending, and scaling of multiple inputs into one output.
28  *
29  * ## Example launch line
30  * ```
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.
37  * ```
38  *
39  * Since: 1.22
40  *
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include "gstvacompositor.h"
48
49 #include <gst/va/gstva.h>
50 #include <gst/video/video.h>
51 #include <va/va_drmcommon.h>
52
53 #include "gstvacaps.h"
54 #include "gstvadisplay_priv.h"
55 #include "gstvafilter.h"
56
57 GST_DEBUG_CATEGORY_STATIC (gst_va_compositor_debug);
58 #define GST_CAT_DEFAULT gst_va_compositor_debug
59
60 struct _GstVaCompositorPad
61 {
62   GstVideoAggregatorPad parent;
63
64   GstBufferPool *pool;
65
66   gint xpos;
67   gint ypos;
68   gint width;
69   gint height;
70   gdouble alpha;
71 };
72
73 enum
74 {
75   PROP_PAD_0,
76   PROP_PAD_XPOS,
77   PROP_PAD_YPOS,
78   PROP_PAD_WIDTH,
79   PROP_PAD_HEIGHT,
80   PROP_PAD_ALPHA,
81 };
82
83 #define DEFAULT_PAD_XPOS    0
84 #define DEFAULT_PAD_YPOS    0
85 #define DEFAULT_PAD_WIDTH   0
86 #define DEFAULT_PAD_HEIGHT  0
87 #define DEFAULT_PAD_ALPHA   1.0
88
89 G_DEFINE_TYPE (GstVaCompositorPad, gst_va_compositor_pad,
90     GST_TYPE_VIDEO_AGGREGATOR_PAD);
91
92 static void
93 gst_va_compositor_pad_get_property (GObject * object, guint prop_id,
94     GValue * value, GParamSpec * pspec)
95 {
96   GstVaCompositorPad *const self = GST_VA_COMPOSITOR_PAD (object);
97
98   switch (prop_id) {
99     case PROP_PAD_XPOS:
100       g_value_set_int (value, self->xpos);
101       break;
102     case PROP_PAD_YPOS:
103       g_value_set_int (value, self->ypos);
104       break;
105     case PROP_PAD_WIDTH:
106       g_value_set_int (value, self->width);
107       break;
108     case PROP_PAD_HEIGHT:
109       g_value_set_int (value, self->height);
110       break;
111     case PROP_PAD_ALPHA:
112       g_value_set_double (value, self->alpha);
113       break;
114     default:
115       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116       break;
117   }
118 }
119
120 static void
121 gst_va_compositor_pad_set_property (GObject * object, guint prop_id,
122     const GValue * value, GParamSpec * pspec)
123 {
124   GstVaCompositorPad *const self = GST_VA_COMPOSITOR_PAD (object);
125
126   GST_OBJECT_LOCK (object);
127   switch (prop_id) {
128     case PROP_PAD_XPOS:
129       self->xpos = g_value_get_int (value);
130       break;
131     case PROP_PAD_YPOS:
132       self->ypos = g_value_get_int (value);
133       break;
134     case PROP_PAD_WIDTH:
135       self->width = g_value_get_int (value);
136       break;
137     case PROP_PAD_HEIGHT:
138       self->height = g_value_get_int (value);
139       break;
140     case PROP_PAD_ALPHA:
141       self->alpha = g_value_get_double (value);
142       break;
143     default:
144       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145       break;
146   }
147   GST_OBJECT_UNLOCK (object);
148 }
149
150 static void
151 gst_va_compositor_pad_finalize (GObject * object)
152 {
153   GstVaCompositorPad *const self = GST_VA_COMPOSITOR_PAD (object);
154
155   if (self->pool) {
156     gst_buffer_pool_set_active (self->pool, FALSE);
157     gst_clear_object (&self->pool);
158   }
159
160   G_OBJECT_CLASS (gst_va_compositor_pad_parent_class)->finalize (object);
161 }
162
163 static void
164 gst_va_compositor_pad_init (GstVaCompositorPad * self)
165 {
166   self->pool = NULL;
167   self->xpos = DEFAULT_PAD_XPOS;
168   self->ypos = DEFAULT_PAD_YPOS;
169   self->width = DEFAULT_PAD_WIDTH;
170   self->height = DEFAULT_PAD_HEIGHT;
171   self->alpha = DEFAULT_PAD_ALPHA;
172 }
173
174 static void
175 gst_va_compositor_pad_class_init (GstVaCompositorPadClass * klass)
176 {
177   GObjectClass *const gobject_class = G_OBJECT_CLASS (klass);
178   GstVideoAggregatorPadClass *const vaggpad_class =
179       GST_VIDEO_AGGREGATOR_PAD_CLASS (klass);
180
181   gobject_class->finalize = gst_va_compositor_pad_finalize;
182   gobject_class->get_property = gst_va_compositor_pad_get_property;
183   gobject_class->set_property = gst_va_compositor_pad_set_property;
184
185   g_object_class_install_property (gobject_class, PROP_PAD_XPOS,
186       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
187           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS,
188           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
189   g_object_class_install_property (gobject_class, PROP_PAD_YPOS,
190       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
191           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS,
192           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
193   g_object_class_install_property (gobject_class, PROP_PAD_WIDTH,
194       g_param_spec_int ("width", "Width",
195           "Width of the picture (0, to use the width of the input frame)",
196           0, G_MAXINT, DEFAULT_PAD_WIDTH,
197           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
198   g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT,
199       g_param_spec_int ("height", "Height",
200           "Height of the picture (0, to use the height of the input frame)",
201           0, G_MAXINT, DEFAULT_PAD_HEIGHT,
202           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
203   g_object_class_install_property (gobject_class, PROP_PAD_ALPHA,
204       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
205           DEFAULT_PAD_ALPHA,
206           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
207
208   /* Don't use mapped video frames.  Handle video buffers directly */
209   vaggpad_class->prepare_frame = NULL;
210   vaggpad_class->clean_frame = NULL;
211 }
212
213 #define GST_VA_COMPOSITOR(obj) ((GstVaCompositor *) obj)
214 #define GST_VA_COMPOSITOR_CLASS(klass) ((GstVaCompositorClass *) klass)
215 #define GST_VA_COMPOSITOR_GET_CLASS(obj) \
216     (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaCompositorClass))
217
218 typedef struct _GstVaCompositor GstVaCompositor;
219 typedef struct _GstVaCompositorClass GstVaCompositorClass;
220
221 struct _GstVaCompositorClass
222 {
223   GstVideoAggregatorClass parent_class;
224
225   /*< private > */
226   gchar *render_device_path;
227 };
228
229 struct _GstVaCompositor
230 {
231   GstVideoAggregator parent;
232
233   GstVaDisplay *display;
234   GstVaFilter *filter;
235
236   GstVideoInfo other_info;      /* downstream info */
237   GstBufferPool *other_pool;    /* downstream pool */
238
239   guint32 scale_method;
240 };
241
242 struct CData
243 {
244   gchar *render_device_path;
245   gchar *description;
246 };
247
248 enum
249 {
250   PROP_DEVICE_PATH = 1,
251   PROP_SCALE_METHOD,
252   N_PROPERTIES
253 };
254
255 static GParamSpec *properties[N_PROPERTIES];
256 static GstElementClass *parent_class = NULL;
257
258 static void
259 gst_va_compositor_set_property (GObject * object, guint prop_id,
260     const GValue * value, GParamSpec * pspec)
261 {
262   GstVaCompositor *const self = GST_VA_COMPOSITOR (object);
263
264   switch (prop_id) {
265     case PROP_SCALE_METHOD:
266     {
267       GST_OBJECT_LOCK (object);
268       self->scale_method = g_value_get_enum (value);
269       GST_OBJECT_UNLOCK (object);
270       break;
271     }
272     default:
273       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
274   }
275 }
276
277 static void
278 gst_va_compositor_get_property (GObject * object, guint prop_id,
279     GValue * value, GParamSpec * pspec)
280 {
281   GstVaCompositor *const self = GST_VA_COMPOSITOR (object);
282
283   switch (prop_id) {
284     case PROP_DEVICE_PATH:
285     {
286       if (!(self->display && GST_IS_VA_DISPLAY_DRM (self->display))) {
287         g_value_set_string (value, NULL);
288         return;
289       }
290       g_object_get_property (G_OBJECT (self->display), "path", value);
291       break;
292     }
293     case PROP_SCALE_METHOD:
294     {
295       GST_OBJECT_LOCK (object);
296       g_value_set_enum (value, self->scale_method);
297       GST_OBJECT_UNLOCK (object);
298       break;
299     }
300     default:
301       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302   }
303 }
304
305 static gboolean
306 gst_va_compositor_start (GstAggregator * agg)
307 {
308   GstElement *const element = GST_ELEMENT (agg);
309   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
310   GstVaCompositorClass *const klass = GST_VA_COMPOSITOR_GET_CLASS (agg);
311
312   if (!gst_va_ensure_element_data (element, klass->render_device_path,
313           &self->display))
314     return FALSE;
315   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
316
317   self->filter = gst_va_filter_new (self->display);
318   if (!gst_va_filter_open (self->filter))
319     return FALSE;
320
321   return GST_AGGREGATOR_CLASS (parent_class)->start (agg);
322 }
323
324 static gboolean
325 gst_va_compositor_stop (GstAggregator * agg)
326 {
327   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
328
329   gst_va_filter_close (self->filter);
330   gst_clear_object (&self->filter);
331   gst_clear_object (&self->display);
332   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
333
334   return GST_AGGREGATOR_CLASS (parent_class)->stop (agg);
335 }
336
337 static void
338 gst_va_compositor_dispose (GObject * object)
339 {
340   GstVaCompositor *const self = GST_VA_COMPOSITOR (object);
341
342   if (self->other_pool) {
343     gst_buffer_pool_set_active (self->other_pool, FALSE);
344     gst_clear_object (&self->other_pool);
345   }
346
347   gst_clear_object (&self->display);
348
349   G_OBJECT_CLASS (parent_class)->dispose (object);
350 }
351
352 static GstPad *
353 gst_va_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
354     const gchar * req_name, const GstCaps * caps)
355 {
356   GstPad *newpad = GST_PAD (GST_ELEMENT_CLASS
357       (parent_class)->request_new_pad (element, templ, req_name, caps));
358
359   if (!newpad)
360     GST_DEBUG_OBJECT (element, "could not create/add pad");
361   else
362     gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
363         GST_OBJECT_NAME (newpad));
364
365   return newpad;
366 }
367
368 static void
369 gst_va_compositor_release_pad (GstElement * element, GstPad * pad)
370 {
371   GstVaCompositor *const self = GST_VA_COMPOSITOR (element);
372
373   gst_child_proxy_child_removed (GST_CHILD_PROXY (self), G_OBJECT (pad),
374       GST_OBJECT_NAME (pad));
375
376   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
377 }
378
379 static void
380 gst_va_compositor_set_context (GstElement * element, GstContext * context)
381 {
382   GstVaDisplay *old_display, *new_display;
383   GstVaCompositor *const self = GST_VA_COMPOSITOR (element);
384   GstVaCompositorClass *const klass = GST_VA_COMPOSITOR_GET_CLASS (self);
385   gboolean ret;
386
387   old_display = self->display ? gst_object_ref (self->display) : NULL;
388   ret = gst_va_handle_set_context (element, context, klass->render_device_path,
389       &self->display);
390   new_display = self->display ? gst_object_ref (self->display) : NULL;
391
392   if (!ret
393       || (old_display && new_display && old_display != new_display
394           && self->filter)) {
395     GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
396         ("Can't replace VA display while operating"), (NULL));
397   }
398
399   gst_clear_object (&old_display);
400   gst_clear_object (&new_display);
401
402   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
403 }
404
405 static gboolean
406 _handle_context_query (GstVaCompositor * const self, GstQuery * query)
407 {
408   GstVaDisplay *display = NULL;
409   gboolean ret = FALSE;
410
411   gst_object_replace ((GstObject **) & display, (GstObject *) self->display);
412   ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query, display);
413   gst_clear_object (&display);
414
415   return ret;
416 }
417
418 static GstCaps *
419 gst_va_compositor_sink_getcaps (GstPad * pad, GstCaps * filter)
420 {
421   GstCaps *sinkcaps;
422   GstCaps *template_caps;
423   GstCaps *filtered_caps;
424   GstCaps *returned_caps;
425
426   template_caps = gst_pad_get_pad_template_caps (pad);
427
428   sinkcaps = gst_pad_get_current_caps (pad);
429   if (!sinkcaps) {
430     sinkcaps = gst_caps_ref (template_caps);
431   } else {
432     sinkcaps = gst_caps_merge (sinkcaps, gst_caps_ref (template_caps));
433   }
434
435   if (filter) {
436     filtered_caps = gst_caps_intersect (sinkcaps, filter);
437     gst_caps_unref (sinkcaps);
438   } else {
439     filtered_caps = sinkcaps;
440   }
441
442   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
443
444   gst_caps_unref (template_caps);
445   gst_caps_unref (filtered_caps);
446
447   GST_DEBUG_OBJECT (pad, "returning %" GST_PTR_FORMAT, returned_caps);
448
449   return returned_caps;
450 }
451
452 static gboolean
453 gst_va_compositor_sink_acceptcaps (GstPad * pad, GstCaps * caps)
454 {
455   gboolean ret;
456   GstCaps *template_caps;
457
458   template_caps = gst_pad_get_pad_template_caps (pad);
459   template_caps = gst_caps_make_writable (template_caps);
460
461   ret = gst_caps_can_intersect (caps, template_caps);
462   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
463       (ret ? "" : "not "), caps);
464   gst_caps_unref (template_caps);
465
466   return ret;
467 }
468
469 static gboolean
470 gst_va_compositor_sink_query (GstAggregator * agg, GstAggregatorPad * pad,
471     GstQuery * query)
472 {
473   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
474
475   switch (GST_QUERY_TYPE (query)) {
476     case GST_QUERY_CONTEXT:
477     {
478       if (_handle_context_query (self, query))
479         return TRUE;
480       break;
481     }
482     case GST_QUERY_CAPS:
483     {
484       GstCaps *filter, *caps;
485
486       gst_query_parse_caps (query, &filter);
487       caps = gst_va_compositor_sink_getcaps (GST_PAD (pad), filter);
488       gst_query_set_caps_result (query, caps);
489       gst_caps_unref (caps);
490       return TRUE;
491     }
492     case GST_QUERY_ACCEPT_CAPS:
493     {
494       GstCaps *caps;
495       gboolean ret;
496
497       gst_query_parse_accept_caps (query, &caps);
498       ret = gst_va_compositor_sink_acceptcaps (GST_PAD (pad), caps);
499       gst_query_set_accept_caps_result (query, ret);
500       return TRUE;
501     }
502     default:
503       break;
504   }
505
506   return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, pad, query);
507 }
508
509 static gboolean
510 gst_va_compositor_src_query (GstAggregator * agg, GstQuery * query)
511 {
512   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
513
514   switch (GST_QUERY_TYPE (query)) {
515     case GST_QUERY_CONTEXT:
516       if (_handle_context_query (self, query))
517         return TRUE;
518       break;
519     default:
520       break;
521   }
522
523   return GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
524 }
525
526 static GstAllocator *
527 gst_va_compositor_allocator_from_caps (GstVaCompositor * const self,
528     GstCaps * caps)
529 {
530   GstAllocator *allocator = NULL;
531
532   if (gst_caps_is_dmabuf (caps)) {
533     allocator = gst_va_dmabuf_allocator_new (self->display);
534   } else {
535     GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
536     allocator = gst_va_allocator_new (self->display, surface_formats);
537   }
538
539   return allocator;
540 }
541
542 /* Answer upstream allocation query. */
543 static gboolean
544 gst_va_compositor_propose_allocation (GstAggregator * agg,
545     GstAggregatorPad * aggpad, GstQuery * decide_query, GstQuery * query)
546 {
547   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
548   GstAllocator *allocator = NULL;
549   GstAllocationParams params = { 0, };
550   GstBufferPool *pool;
551   GstCaps *caps;
552   GstVideoInfo info;
553   gboolean update_allocator = FALSE;
554   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
555
556   gst_query_parse_allocation (query, &caps, NULL);
557
558   if (!caps)
559     return FALSE;
560
561   if (!gst_video_info_from_caps (&info, caps))
562     return FALSE;
563
564   if (gst_query_get_n_allocation_pools (query) > 0)
565     return TRUE;
566
567   size = GST_VIDEO_INFO_SIZE (&info);
568
569   if (gst_query_get_n_allocation_params (query) > 0) {
570     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
571     if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator)
572         && !GST_IS_VA_ALLOCATOR (allocator))
573       gst_clear_object (&allocator);
574     update_allocator = TRUE;
575   } else {
576     gst_allocation_params_init (&params);
577   }
578
579   if (!allocator) {
580     if (!(allocator = gst_va_compositor_allocator_from_caps (self, caps)))
581       return FALSE;
582   }
583
584   /* Now we have a VA-based allocator */
585
586   pool = gst_va_pool_new_with_config (caps, size, 1, 0, usage_hint,
587       GST_VA_FEATURE_AUTO, allocator, &params);
588   if (!pool) {
589     gst_object_unref (allocator);
590     goto config_failed;
591   }
592
593   if (update_allocator)
594     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
595   else
596     gst_query_add_allocation_param (query, allocator, &params);
597
598   gst_query_add_allocation_pool (query, pool, size, 1, 0);
599
600   GST_DEBUG_OBJECT (self,
601       "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
602       pool, allocator);
603
604   gst_object_unref (allocator);
605   gst_object_unref (pool);
606
607   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
608
609   return TRUE;
610
611 config_failed:
612   {
613     GST_ERROR_OBJECT (self, "failed to set config");
614     return FALSE;
615   }
616 }
617
618 static GstBufferPool *
619 _create_other_pool (GstAllocator * allocator, GstAllocationParams * params,
620     GstCaps * caps, guint size)
621 {
622   GstBufferPool *pool = NULL;
623   GstStructure *config;
624
625   pool = gst_video_buffer_pool_new ();
626   config = gst_buffer_pool_get_config (pool);
627
628   gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
629   gst_buffer_pool_config_set_allocator (config, allocator, params);
630   if (!gst_buffer_pool_set_config (pool, config)) {
631     gst_clear_object (&pool);
632   }
633
634   return pool;
635 }
636
637 /* configure the allocation query that was answered downstream */
638 static gboolean
639 gst_va_compositor_decide_allocation (GstAggregator * agg, GstQuery * query)
640 {
641   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
642   GstVideoAggregator *const vagg = GST_VIDEO_AGGREGATOR (agg);
643
644   GstAllocator *allocator = NULL, *other_allocator = NULL;
645   GstAllocationParams params, other_params;
646   GstBufferPool *pool = NULL, *other_pool = NULL;
647   GstCaps *caps = NULL;
648   GstStructure *config;
649   GstVideoInfo info;
650   guint min, max, size = 0, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
651   gboolean update_pool, update_allocator, has_videometa, copy_frames;
652
653   gst_query_parse_allocation (query, &caps, NULL);
654
655   gst_allocation_params_init (&other_params);
656   gst_allocation_params_init (&params);
657
658   if (!gst_video_info_from_caps (&info, caps)) {
659     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
660     return FALSE;
661   }
662
663   if (gst_query_get_n_allocation_params (query) > 0) {
664     gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
665     if (allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (allocator)
666             || GST_IS_VA_ALLOCATOR (allocator))) {
667       /* save the allocator for the other pool */
668       other_allocator = allocator;
669       allocator = NULL;
670     }
671     update_allocator = TRUE;
672   } else {
673     update_allocator = FALSE;
674   }
675
676   if (gst_query_get_n_allocation_pools (query) > 0) {
677     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
678
679     if (pool) {
680       if (!GST_IS_VA_POOL (pool)) {
681         GST_DEBUG_OBJECT (self,
682             "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
683         other_pool = pool;
684         pool = NULL;
685       }
686     }
687
688     update_pool = TRUE;
689   } else {
690     size = GST_VIDEO_INFO_SIZE (&info);
691     min = 1;
692     max = 0;
693     update_pool = FALSE;
694   }
695
696   if (!allocator) {
697     if (gst_caps_is_dmabuf (caps) && GST_VIDEO_INFO_IS_RGB (&info))
698       usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
699     if (!(allocator = gst_va_compositor_allocator_from_caps (self, caps)))
700       return FALSE;
701   }
702
703   if (!pool)
704     pool = gst_va_pool_new ();
705
706   config = gst_buffer_pool_get_config (pool);
707   gst_buffer_pool_config_set_allocator (config, allocator, &params);
708   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
709   gst_buffer_pool_config_set_params (config, caps, size, min, max);
710   gst_buffer_pool_config_set_va_allocation_params (config, usage_hint,
711       GST_VA_FEATURE_AUTO);
712   if (!gst_buffer_pool_set_config (pool, config)) {
713     gst_object_unref (allocator);
714     gst_object_unref (pool);
715     return FALSE;
716   }
717
718   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
719     gst_va_dmabuf_allocator_get_format (allocator, &vagg->info, NULL);
720   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
721     gst_va_allocator_get_format (allocator, &vagg->info, NULL, NULL);
722   }
723
724   if (update_allocator)
725     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
726   else
727     gst_query_add_allocation_param (query, allocator, &params);
728
729   if (update_pool)
730     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
731   else
732     gst_query_add_allocation_pool (query, pool, size, min, max);
733
734   has_videometa = gst_query_find_allocation_meta (query,
735       GST_VIDEO_META_API_TYPE, NULL);
736
737   copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
738       && gst_caps_is_raw (caps));
739   if (copy_frames) {
740     if (other_pool) {
741       gst_object_replace ((GstObject **) & self->other_pool,
742           (GstObject *) other_pool);
743     } else {
744       self->other_pool =
745           _create_other_pool (other_allocator, &other_params, caps, size);
746     }
747     GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
748         self->other_pool);
749   } else {
750     gst_clear_object (&self->other_pool);
751   }
752
753   GST_DEBUG_OBJECT (self,
754       "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
755       pool, allocator);
756
757   gst_object_unref (allocator);
758   gst_object_unref (pool);
759   gst_clear_object (&other_allocator);
760   gst_clear_object (&other_pool);
761
762   return TRUE;
763 }
764
765 static GstBufferPool *
766 _get_sinkpad_pool (GstVaCompositor * const self, GstVaCompositorPad * const pad)
767 {
768   GstAllocator *allocator;
769   GstAllocationParams params = { 0, };
770   GstCaps *caps;
771   GstVideoInfo info;
772   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
773
774   if (pad->pool)
775     return pad->pool;
776
777   gst_allocation_params_init (&params);
778
779   caps = gst_pad_get_current_caps (GST_PAD (pad));
780   if (!caps)
781     return NULL;
782   if (!gst_video_info_from_caps (&info, caps)) {
783     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
784     gst_caps_unref (caps);
785     return NULL;
786   }
787
788   size = GST_VIDEO_INFO_SIZE (&info);
789
790   allocator = gst_va_compositor_allocator_from_caps (self, caps);
791   pad->pool = gst_va_pool_new_with_config (caps, size, 1, 0, usage_hint,
792       GST_VA_FEATURE_AUTO, allocator, &params);
793   gst_caps_unref (caps);
794
795   if (!pad->pool) {
796     gst_object_unref (allocator);
797     return NULL;
798   }
799
800   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
801     gst_va_dmabuf_allocator_get_format (allocator, &info, NULL);
802   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
803     gst_va_allocator_get_format (allocator, &info, NULL, NULL);
804   }
805
806   gst_object_unref (allocator);
807
808   if (!gst_buffer_pool_set_active (pad->pool, TRUE)) {
809     GST_WARNING_OBJECT (self, "failed to active the sinkpad pool %"
810         GST_PTR_FORMAT, pad->pool);
811     return NULL;
812   }
813
814   return pad->pool;
815 }
816
817 static inline gsize
818 _get_plane_data_size (GstVideoInfo * info, guint plane)
819 {
820   gint comp[GST_VIDEO_MAX_COMPONENTS];
821   gint height, padded_height;
822
823   gst_video_format_info_component (info->finfo, plane, comp);
824
825   height = GST_VIDEO_INFO_HEIGHT (info);
826   padded_height =
827       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], height);
828
829   return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
830 }
831
832 static gboolean
833 _try_import_dmabuf_unlocked (GstVaCompositor * const self, GstVideoInfo * info,
834     GstBuffer * inbuf)
835 {
836   GstVideoMeta *meta;
837   GstMemory *mems[GST_VIDEO_MAX_PLANES];
838   guint i, n_mem, n_planes;
839   gsize offset[GST_VIDEO_MAX_PLANES];
840   uintptr_t fd[GST_VIDEO_MAX_PLANES];
841
842   n_planes = GST_VIDEO_INFO_N_PLANES (info);
843   n_mem = gst_buffer_n_memory (inbuf);
844   meta = gst_buffer_get_video_meta (inbuf);
845
846   /* This will eliminate most non-dmabuf out there */
847   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
848     return FALSE;
849
850   /* We cannot have multiple dmabuf per plane */
851   if (n_mem > n_planes)
852     return FALSE;
853
854   /* Update video info based on video meta */
855   if (meta) {
856     GST_VIDEO_INFO_WIDTH (info) = meta->width;
857     GST_VIDEO_INFO_HEIGHT (info) = meta->height;
858
859     for (i = 0; i < meta->n_planes; i++) {
860       GST_VIDEO_INFO_PLANE_OFFSET (info, i) = meta->offset[i];
861       GST_VIDEO_INFO_PLANE_STRIDE (info, i) = meta->stride[i];
862     }
863   }
864
865   /* Find and validate all memories */
866   for (i = 0; i < n_planes; i++) {
867     guint plane_size;
868     guint length;
869     guint mem_idx;
870     gsize mem_skip;
871
872     plane_size = _get_plane_data_size (info, i);
873
874     if (!gst_buffer_find_memory (inbuf, info->offset[i], plane_size,
875             &mem_idx, &length, &mem_skip))
876       return FALSE;
877
878     /* We can't have more then one dmabuf per plane */
879     if (length != 1)
880       return FALSE;
881
882     mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
883
884     /* And all memory found must be dmabuf */
885     if (!gst_is_dmabuf_memory (mems[i]))
886       return FALSE;
887
888     offset[i] = mems[i]->offset + mem_skip;
889     fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
890   }
891
892   /* Now create a VASurfaceID for the buffer */
893   return gst_va_dmabuf_memories_setup (self->display, info, n_planes,
894       mems, fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ);
895 }
896
897 extern GRecMutex GST_VA_SHARED_LOCK;
898
899 static gboolean
900 _try_import_buffer (GstVaCompositor * const self,
901     GstVideoInfo info, GstBuffer * inbuf)
902 {
903   VASurfaceID surface;
904   gboolean ret;
905
906   surface = gst_va_buffer_get_surface (inbuf);
907   if (surface != VA_INVALID_ID)
908     return TRUE;
909
910   g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
911   ret = _try_import_dmabuf_unlocked (self, &info, inbuf);
912   g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
913
914   return ret;
915 }
916
917 static GstFlowReturn
918 gst_va_compositor_import_buffer (GstVaCompositor * const self,
919     GstVaCompositorPad * const pad, GstBuffer * inbuf, GstBuffer ** buf)
920 {
921   GstBuffer *buffer = NULL;
922   GstBufferPool *pool;
923   GstFlowReturn ret;
924   GstCaps *caps;
925   GstVideoInfo info;
926   GstVideoFrame in_frame, out_frame;
927   gboolean imported, copied;
928
929   caps = gst_pad_get_current_caps (GST_PAD (pad));
930   if (!caps)
931     return GST_FLOW_ERROR;
932   if (!gst_video_info_from_caps (&info, caps)) {
933     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
934     gst_caps_unref (caps);
935     return GST_FLOW_ERROR;
936   }
937   gst_caps_unref (caps);
938
939   imported = _try_import_buffer (self, info, inbuf);
940   if (imported) {
941     *buf = gst_buffer_ref (inbuf);
942     return GST_FLOW_OK;
943   }
944
945   GST_LOG_OBJECT (self, "copying input frame");
946
947   /* input buffer doesn't come from a vapool, thus it is required to
948    * have a pool, grab from it a new buffer and copy the input
949    * buffer to the new one */
950   if (!(pool = _get_sinkpad_pool (self, pad)))
951     return GST_FLOW_ERROR;
952
953   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
954   if (ret != GST_FLOW_OK)
955     return ret;
956
957   if (!gst_video_frame_map (&in_frame, &info, inbuf, GST_MAP_READ))
958     goto invalid_buffer;
959
960   if (!gst_video_frame_map (&out_frame, &info, buffer, GST_MAP_WRITE)) {
961     gst_video_frame_unmap (&in_frame);
962     goto invalid_buffer;
963   }
964
965   copied = gst_video_frame_copy (&out_frame, &in_frame);
966
967   gst_video_frame_unmap (&out_frame);
968   gst_video_frame_unmap (&in_frame);
969
970   if (!copied)
971     goto invalid_buffer;
972
973   *buf = buffer;
974
975   return GST_FLOW_OK;
976
977 invalid_buffer:
978   {
979     GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL),
980         ("invalid video buffer received"));
981     if (buffer)
982       gst_buffer_unref (buffer);
983     return GST_FLOW_OK;
984   }
985 }
986
987 typedef struct _GstVaCompositorSampleGenerator GstVaCompositorSampleGenerator;
988 struct _GstVaCompositorSampleGenerator
989 {
990   GstVaCompositor *comp;
991   GList *current;
992   GstVaComposeSample sample;
993 };
994
995 static GstVaComposeSample *
996 gst_va_compositor_sample_next (gpointer data)
997 {
998   GstVaCompositorSampleGenerator *generator;
999   GstVideoAggregatorPad *vaggpad;
1000   GstVaCompositorPad *pad;
1001   GstBuffer *inbuf;
1002   GstBuffer *buf;
1003   GstFlowReturn res;
1004   GstVideoCropMeta *crop = NULL;
1005
1006   generator = (GstVaCompositorSampleGenerator *) data;
1007
1008   /* at the end of the generator? */
1009   while (generator->current) {
1010     /* get the current sinkpad for processing */
1011     vaggpad = GST_VIDEO_AGGREGATOR_PAD (generator->current->data);
1012
1013     /* increment to next sinkpad */
1014     generator->current = generator->current->next;
1015
1016     /* reset sample */
1017     /* *INDENT-OFF* */
1018     generator->sample = (GstVaComposeSample) { 0, };
1019     /* *INDENT-ON* */
1020
1021     /* current sinkpad may not be queueing buffers yet (e.g. timestamp-offset)
1022      * or it may have reached EOS */
1023     if (!gst_video_aggregator_pad_has_current_buffer (vaggpad))
1024       continue;
1025
1026     inbuf = gst_video_aggregator_pad_get_current_buffer (vaggpad);
1027     pad = GST_VA_COMPOSITOR_PAD (vaggpad);
1028
1029     res = gst_va_compositor_import_buffer (generator->comp, pad, inbuf, &buf);
1030     if (res != GST_FLOW_OK)
1031       return &generator->sample;
1032
1033     crop = gst_buffer_get_video_crop_meta (buf);
1034
1035     GST_OBJECT_LOCK (vaggpad);
1036     /* *INDENT-OFF* */
1037     generator->sample = (GstVaComposeSample) {
1038       .buffer = buf,
1039       .input_region = (VARectangle) {
1040         .x = crop ? crop->x : 0,
1041         .y = crop ? crop->y : 0,
1042         .width = crop ? crop->width : GST_VIDEO_INFO_WIDTH (&vaggpad->info),
1043         .height = crop ? crop->height : GST_VIDEO_INFO_HEIGHT (&vaggpad->info),
1044       },
1045       .output_region = (VARectangle) {
1046         .x = pad->xpos,
1047         .y = pad->ypos,
1048         .width = (pad->width == DEFAULT_PAD_WIDTH)
1049             ? GST_VIDEO_INFO_WIDTH (&vaggpad->info) : pad->width,
1050         .height = (pad->height == DEFAULT_PAD_HEIGHT)
1051             ? GST_VIDEO_INFO_HEIGHT (&vaggpad->info) : pad->height,
1052       },
1053       .flags = generator->comp->scale_method,
1054       .alpha = pad->alpha,
1055     };
1056     /* *INDENT-ON* */
1057     GST_OBJECT_UNLOCK (vaggpad);
1058
1059     return &generator->sample;
1060   }
1061
1062   return NULL;
1063 }
1064
1065 static gboolean
1066 gst_va_compositor_copy_output_buffer (GstVaCompositor * const self,
1067     GstBuffer * src_buf, GstBuffer * dst_buf)
1068 {
1069   GstVideoAggregator *const vagg = GST_VIDEO_AGGREGATOR (self);
1070   GstVideoFrame src_frame, dst_frame;
1071
1072   GST_LOG_OBJECT (self, "copying output buffer");
1073
1074   if (!gst_video_frame_map (&src_frame, &vagg->info, src_buf, GST_MAP_READ)) {
1075     GST_ERROR_OBJECT (self, "couldn't map source buffer");
1076     return FALSE;
1077   }
1078
1079   if (!gst_video_frame_map (&dst_frame, &self->other_info, dst_buf,
1080           GST_MAP_WRITE)) {
1081     GST_ERROR_OBJECT (self, "couldn't map output buffer");
1082     gst_video_frame_unmap (&src_frame);
1083     return FALSE;
1084   }
1085
1086   if (!gst_video_frame_copy (&dst_frame, &src_frame)) {
1087     GST_ERROR_OBJECT (self, "couldn't copy output buffer");
1088     gst_video_frame_unmap (&src_frame);
1089     gst_video_frame_unmap (&dst_frame);
1090     return FALSE;
1091   }
1092
1093   gst_video_frame_unmap (&src_frame);
1094   gst_video_frame_unmap (&dst_frame);
1095
1096   return TRUE;
1097 }
1098
1099 static GstFlowReturn
1100 gst_va_compositor_aggregate_frames (GstVideoAggregator * vagg,
1101     GstBuffer * outbuf)
1102 {
1103   GstVaCompositor *const self = GST_VA_COMPOSITOR (vagg);
1104   GstVaCompositorSampleGenerator generator;
1105   GstVaComposeTransaction tx;
1106   GstBuffer *vabuffer;
1107   gboolean need_copy = FALSE;
1108   GstFlowReturn ret = GST_FLOW_OK;
1109
1110   if (self->other_pool) {
1111     /* create a va buffer for filter */
1112     ret = GST_VIDEO_AGGREGATOR_CLASS (parent_class)->create_output_buffer
1113         (vagg, &vabuffer);
1114     if (ret != GST_FLOW_OK)
1115       return ret;
1116
1117     need_copy = TRUE;
1118   } else {
1119     /* already a va buffer */
1120     vabuffer = gst_buffer_ref (outbuf);
1121   }
1122
1123   /* *INDENT-OFF* */
1124   generator = (GstVaCompositorSampleGenerator) {
1125     .comp = self,
1126     .current = GST_ELEMENT (self)->sinkpads,
1127   };
1128   tx = (GstVaComposeTransaction) {
1129     .next = gst_va_compositor_sample_next,
1130     .output = vabuffer,
1131     .user_data = (gpointer) &generator,
1132   };
1133   /* *INDENT-ON* */
1134
1135   GST_OBJECT_LOCK (self);
1136
1137   if (!gst_va_filter_compose (self->filter, &tx)) {
1138     GST_ERROR_OBJECT (self, "couldn't apply filter");
1139     ret = GST_FLOW_ERROR;
1140   }
1141
1142   GST_OBJECT_UNLOCK (self);
1143
1144   if (ret != GST_FLOW_OK)
1145     goto done;
1146
1147   if (need_copy && !gst_va_compositor_copy_output_buffer (self, vabuffer,
1148           outbuf)) {
1149     GST_ERROR_OBJECT (self, "couldn't copy va buffer to output buffer");
1150     ret = GST_FLOW_ERROR;
1151   }
1152
1153 done:
1154   gst_buffer_unref (vabuffer);
1155   return ret;
1156 }
1157
1158 static GstFlowReturn
1159 gst_va_compositor_create_output_buffer (GstVideoAggregator * vagg,
1160     GstBuffer ** outbuf)
1161 {
1162   GstVaCompositor *const self = GST_VA_COMPOSITOR (vagg);
1163   GstFlowReturn ret;
1164
1165   *outbuf = NULL;
1166
1167   if (!self->other_pool)
1168     /* no copy necessary, so use a va buffer directly */
1169     return GST_VIDEO_AGGREGATOR_CLASS (parent_class)->create_output_buffer
1170         (vagg, outbuf);
1171
1172   /* use output buffers from downstream pool for copy */
1173   if (!gst_buffer_pool_is_active (self->other_pool) &&
1174       !gst_buffer_pool_set_active (self->other_pool, TRUE)) {
1175     GST_ERROR_OBJECT (self, "failed to activate other pool %"
1176         GST_PTR_FORMAT, self->other_pool);
1177     return GST_FLOW_ERROR;
1178   }
1179
1180   /* acquire a buffer from downstream pool for copy */
1181   ret = gst_buffer_pool_acquire_buffer (self->other_pool, outbuf, NULL);
1182   if (ret != GST_FLOW_OK || !*outbuf) {
1183     GST_ERROR_OBJECT (self, "failed to acquire output buffer");
1184     return GST_FLOW_ERROR;
1185   }
1186
1187   return GST_FLOW_OK;
1188 }
1189
1190 static gboolean
1191 gst_va_compositor_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
1192 {
1193   GstVaCompositor *const self = GST_VA_COMPOSITOR (agg);
1194
1195   if (!gst_video_info_from_caps (&self->other_info, caps)) {
1196     GST_ERROR_OBJECT (self, "invalid caps");
1197     return FALSE;
1198   }
1199
1200   if (self->other_pool) {
1201     gst_buffer_pool_set_active (self->other_pool, FALSE);
1202     gst_clear_object (&self->other_pool);
1203   }
1204
1205   return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1206 }
1207
1208 static void
1209 gst_va_compositor_pad_get_output_size (GstVaCompositorPad * const pad,
1210     gint * width, gint * height)
1211 {
1212   GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
1213   *width = (pad->width == DEFAULT_PAD_WIDTH)
1214       ? GST_VIDEO_INFO_WIDTH (&vaggpad->info) : pad->width;
1215   *height = (pad->height == DEFAULT_PAD_HEIGHT)
1216       ? GST_VIDEO_INFO_HEIGHT (&vaggpad->info) : pad->height;
1217
1218   *width += MAX (pad->xpos, 0);
1219   *height += MAX (pad->ypos, 0);
1220 }
1221
1222 static GstCaps *
1223 gst_va_compositor_fixate_src_caps (GstAggregator * agg, GstCaps * caps)
1224 {
1225   GstVideoAggregator *const vagg = GST_VIDEO_AGGREGATOR (agg);
1226   GList *l;
1227   gint best_width = -1, best_height = -1;
1228   gint best_fps_n = -1, best_fps_d = -1;
1229   gdouble best_fps = 0.;
1230   GstCaps *ret = NULL;
1231   GstStructure *s;
1232
1233   ret = gst_caps_make_writable (caps);
1234
1235   GST_OBJECT_LOCK (vagg);
1236   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1237     GstVideoAggregatorPad *const vaggpad = l->data;
1238     GstVaCompositorPad *const pad = GST_VA_COMPOSITOR_PAD (vaggpad);
1239     gint this_width, this_height;
1240     gint fps_n, fps_d;
1241     gdouble cur_fps;
1242
1243     fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1244     fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1245
1246     gst_va_compositor_pad_get_output_size (pad, &this_width, &this_height);
1247
1248     if (best_width < this_width)
1249       best_width = this_width;
1250     if (best_height < this_height)
1251       best_height = this_height;
1252
1253     if (fps_d == 0)
1254       cur_fps = 0.0;
1255     else
1256       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1257
1258     if (best_fps < cur_fps) {
1259       best_fps = cur_fps;
1260       best_fps_n = fps_n;
1261       best_fps_d = fps_d;
1262     }
1263   }
1264   GST_OBJECT_UNLOCK (vagg);
1265
1266   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1267     best_fps_n = 25;
1268     best_fps_d = 1;
1269     best_fps = 25.0;
1270   }
1271
1272   s = gst_caps_get_structure (ret, 0);
1273   gst_structure_fixate_field_nearest_int (s, "width", best_width);
1274   gst_structure_fixate_field_nearest_int (s, "height", best_height);
1275   if (gst_structure_has_field (s, "framerate")) {
1276     gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1277         best_fps_d);
1278   } else {
1279     gst_structure_set (s, "framerate", GST_TYPE_FRACTION, best_fps_n,
1280         best_fps_d, NULL);
1281   }
1282
1283   return gst_caps_fixate (ret);
1284 }
1285
1286 /* *INDENT-OFF* */
1287 static const gchar *caps_str =
1288     GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
1289         "{ NV12, I420, YV12, YUY2, RGBA, BGRA, P010_10LE, ARGB, ABGR }") " ;"
1290     GST_VIDEO_CAPS_MAKE ("{ VUYA, GRAY8, NV12, NV21, YUY2, UYVY, YV12, "
1291         "I420, P010_10LE, RGBA, BGRA, ARGB, ABGR  }");
1292 /* *INDENT-ON* */
1293
1294 static void
1295 gst_va_compositor_class_init (gpointer g_class, gpointer class_data)
1296 {
1297   GstCaps *doc_caps, *caps = NULL;
1298   GstPadTemplate *sink_pad_templ, *src_pad_templ;
1299   GObjectClass *const object_class = G_OBJECT_CLASS (g_class);
1300   GstElementClass *const element_class = GST_ELEMENT_CLASS (g_class);
1301   GstAggregatorClass *const agg_class = GST_AGGREGATOR_CLASS (g_class);
1302   GstVideoAggregatorClass *const vagg_class =
1303       GST_VIDEO_AGGREGATOR_CLASS (g_class);
1304   GstVaCompositorClass *const klass = GST_VA_COMPOSITOR_CLASS (g_class);
1305   GstVaDisplay *display;
1306   GstVaFilter *filter;
1307   struct CData *cdata = class_data;
1308   gchar *long_name;
1309
1310   parent_class = g_type_class_peek_parent (g_class);
1311
1312   klass->render_device_path = g_strdup (cdata->render_device_path);
1313
1314   if (cdata->description) {
1315     long_name = g_strdup_printf ("VA-API Video Compositor in %s",
1316         cdata->description);
1317   } else {
1318     long_name = g_strdup ("VA-API Video Compositor");
1319   }
1320
1321   display = gst_va_display_drm_new_from_path (klass->render_device_path);
1322   filter = gst_va_filter_new (display);
1323
1324   if (gst_va_filter_open (filter)) {
1325     caps = gst_va_filter_get_caps (filter);
1326   } else {
1327     caps = gst_caps_from_string (caps_str);
1328   }
1329
1330   object_class->dispose = GST_DEBUG_FUNCPTR (gst_va_compositor_dispose);
1331   object_class->get_property =
1332       GST_DEBUG_FUNCPTR (gst_va_compositor_get_property);
1333   object_class->set_property =
1334       GST_DEBUG_FUNCPTR (gst_va_compositor_set_property);
1335
1336   gst_element_class_set_static_metadata (element_class, long_name,
1337       "Filter/Editor/Video/Compositor/Hardware",
1338       "VA-API based video compositor",
1339       "U. Artie Eoff <ullysses.a.eoff@intel.com>");
1340
1341   element_class->request_new_pad =
1342       GST_DEBUG_FUNCPTR (gst_va_compositor_request_new_pad);
1343   element_class->release_pad =
1344       GST_DEBUG_FUNCPTR (gst_va_compositor_release_pad);
1345   element_class->set_context =
1346       GST_DEBUG_FUNCPTR (gst_va_compositor_set_context);
1347
1348   doc_caps = gst_caps_from_string (caps_str);
1349
1350   sink_pad_templ = gst_pad_template_new_with_gtype ("sink_%u", GST_PAD_SINK,
1351       GST_PAD_REQUEST, caps, GST_TYPE_VA_COMPOSITOR_PAD);
1352   gst_element_class_add_pad_template (element_class, sink_pad_templ);
1353   gst_pad_template_set_documentation_caps (sink_pad_templ,
1354       gst_caps_ref (doc_caps));
1355
1356   src_pad_templ = gst_pad_template_new_with_gtype ("src", GST_PAD_SRC,
1357       GST_PAD_ALWAYS, caps, GST_TYPE_AGGREGATOR_PAD);
1358   gst_element_class_add_pad_template (element_class, src_pad_templ);
1359   gst_pad_template_set_documentation_caps (src_pad_templ,
1360       gst_caps_ref (doc_caps));
1361
1362   gst_caps_unref (doc_caps);
1363   gst_caps_unref (caps);
1364
1365   agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_va_compositor_sink_query);
1366   agg_class->src_query = GST_DEBUG_FUNCPTR (gst_va_compositor_src_query);
1367   agg_class->start = GST_DEBUG_FUNCPTR (gst_va_compositor_start);
1368   agg_class->stop = GST_DEBUG_FUNCPTR (gst_va_compositor_stop);
1369   agg_class->propose_allocation =
1370       GST_DEBUG_FUNCPTR (gst_va_compositor_propose_allocation);
1371   agg_class->fixate_src_caps =
1372       GST_DEBUG_FUNCPTR (gst_va_compositor_fixate_src_caps);
1373   agg_class->negotiated_src_caps =
1374       GST_DEBUG_FUNCPTR (gst_va_compositor_negotiated_src_caps);
1375   agg_class->decide_allocation =
1376       GST_DEBUG_FUNCPTR (gst_va_compositor_decide_allocation);
1377
1378   vagg_class->aggregate_frames =
1379       GST_DEBUG_FUNCPTR (gst_va_compositor_aggregate_frames);
1380   vagg_class->create_output_buffer =
1381       GST_DEBUG_FUNCPTR (gst_va_compositor_create_output_buffer);
1382
1383   properties[PROP_DEVICE_PATH] = g_param_spec_string ("device-path",
1384       "Device Path", "DRM device path", NULL,
1385       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1386
1387   properties[PROP_SCALE_METHOD] = g_param_spec_enum ("scale-method",
1388       "Scale Method", "Scale method to use", GST_TYPE_VA_SCALE_METHOD,
1389       VA_FILTER_SCALING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1390
1391   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
1392
1393   g_free (long_name);
1394   g_free (cdata->description);
1395   g_free (cdata->render_device_path);
1396   g_free (cdata);
1397   gst_object_unref (filter);
1398   gst_object_unref (display);
1399 }
1400
1401 static GObject *
1402 gst_va_compositor_child_proxy_get_child_by_index (GstChildProxy * proxy,
1403     guint index)
1404 {
1405   GstVaCompositor *self = GST_VA_COMPOSITOR (proxy);
1406   GObject *obj = NULL;
1407
1408   GST_OBJECT_LOCK (self);
1409   obj = g_list_nth_data (GST_ELEMENT_CAST (self)->sinkpads, index);
1410   if (obj)
1411     gst_object_ref (obj);
1412   GST_OBJECT_UNLOCK (self);
1413
1414   return obj;
1415 }
1416
1417 static guint
1418 gst_va_compositor_child_proxy_get_children_count (GstChildProxy * proxy)
1419 {
1420   GstVaCompositor *self = GST_VA_COMPOSITOR (proxy);
1421   guint count = 0;
1422
1423   GST_OBJECT_LOCK (self);
1424   count = GST_ELEMENT_CAST (self)->numsinkpads;
1425   GST_OBJECT_UNLOCK (self);
1426   GST_INFO_OBJECT (self, "Children Count: %d", count);
1427
1428   return count;
1429 }
1430
1431 static void
1432 gst_va_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1433 {
1434   GstChildProxyInterface *iface = (GstChildProxyInterface *) g_iface;
1435
1436   iface->get_child_by_index = gst_va_compositor_child_proxy_get_child_by_index;
1437   iface->get_children_count = gst_va_compositor_child_proxy_get_children_count;
1438 }
1439
1440 static void
1441 gst_va_compositor_init (GTypeInstance * instance, gpointer g_class)
1442 {
1443   GstVaCompositor *const self = GST_VA_COMPOSITOR (instance);
1444
1445   self->other_pool = NULL;
1446 }
1447
1448 static gpointer
1449 _register_debug_category (gpointer data)
1450 {
1451   GST_DEBUG_CATEGORY_INIT (gst_va_compositor_debug, "vacompositor", 0,
1452       "VA Video Compositor");
1453
1454   return NULL;
1455 }
1456
1457 gboolean
1458 gst_va_compositor_register (GstPlugin * plugin, GstVaDevice * device,
1459     guint rank)
1460 {
1461   static GOnce debug_once = G_ONCE_INIT;
1462   GType type;
1463   GTypeInfo type_info = {
1464     .class_size = sizeof (GstVaCompositorClass),
1465     .class_init = gst_va_compositor_class_init,
1466     .instance_size = sizeof (GstVaCompositor),
1467     .instance_init = gst_va_compositor_init,
1468   };
1469   GInterfaceInfo interface_info = {
1470     (GInterfaceInitFunc) gst_va_compositor_child_proxy_init,
1471   };
1472   struct CData *cdata;
1473   gboolean ret;
1474   gchar *type_name, *feature_name;
1475
1476   g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
1477   g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
1478
1479   cdata = g_new (struct CData, 1);
1480   cdata->description = NULL;
1481   cdata->render_device_path = g_strdup (device->render_device_path);
1482
1483   type_info.class_data = cdata;
1484
1485   type_name = g_strdup ("GstVaCompositor");
1486   feature_name = g_strdup ("vacompositor");
1487
1488   /* The first compositor to be registered should use a constant
1489    * name, like vacompositor, for any additional compositors, we
1490    * create unique names, using the render device name. */
1491   if (g_type_from_name (type_name)) {
1492     gchar *basename = g_path_get_basename (device->render_device_path);
1493     g_free (type_name);
1494     g_free (feature_name);
1495     type_name = g_strdup_printf ("GstVa%sCompositor", basename);
1496     feature_name = g_strdup_printf ("va%scompositor", basename);
1497     cdata->description = basename;
1498
1499     /* lower rank for non-first device */
1500     if (rank > 0)
1501       rank--;
1502   }
1503
1504   g_once (&debug_once, _register_debug_category, NULL);
1505
1506   type = g_type_register_static (GST_TYPE_VIDEO_AGGREGATOR, type_name,
1507       &type_info, 0);
1508   g_type_add_interface_static (type, GST_TYPE_CHILD_PROXY, &interface_info);
1509
1510   ret = gst_element_register (plugin, feature_name, rank, type);
1511
1512   g_free (type_name);
1513   g_free (feature_name);
1514
1515   return ret;
1516 }