va: compositor: Do not use allocator and pool from other display.
[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 /**
61  * GstVaCompositorPad:
62  *
63  * VA aggregator pad.
64  *
65  * Since: 1.22
66  */
67 struct _GstVaCompositorPad
68 {
69   GstVideoAggregatorPad parent;
70
71   /*< private> */
72   GstBufferPool *pool;
73
74   gint xpos;
75   gint ypos;
76   gint width;
77   gint height;
78   gdouble alpha;
79 };
80
81 enum
82 {
83   PROP_PAD_0,
84   PROP_PAD_XPOS,
85   PROP_PAD_YPOS,
86   PROP_PAD_WIDTH,
87   PROP_PAD_HEIGHT,
88   PROP_PAD_ALPHA,
89 };
90
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
96
97 G_DEFINE_TYPE (GstVaCompositorPad, gst_va_compositor_pad,
98     GST_TYPE_VIDEO_AGGREGATOR_PAD);
99
100 static void
101 gst_va_compositor_pad_get_property (GObject * object, guint prop_id,
102     GValue * value, GParamSpec * pspec)
103 {
104   GstVaCompositorPad *self = GST_VA_COMPOSITOR_PAD (object);
105
106   switch (prop_id) {
107     case PROP_PAD_XPOS:
108       g_value_set_int (value, self->xpos);
109       break;
110     case PROP_PAD_YPOS:
111       g_value_set_int (value, self->ypos);
112       break;
113     case PROP_PAD_WIDTH:
114       g_value_set_int (value, self->width);
115       break;
116     case PROP_PAD_HEIGHT:
117       g_value_set_int (value, self->height);
118       break;
119     case PROP_PAD_ALPHA:
120       g_value_set_double (value, self->alpha);
121       break;
122     default:
123       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
124       break;
125   }
126 }
127
128 static void
129 gst_va_compositor_pad_set_property (GObject * object, guint prop_id,
130     const GValue * value, GParamSpec * pspec)
131 {
132   GstVaCompositorPad *self = GST_VA_COMPOSITOR_PAD (object);
133
134   GST_OBJECT_LOCK (object);
135   switch (prop_id) {
136     case PROP_PAD_XPOS:
137       self->xpos = g_value_get_int (value);
138       break;
139     case PROP_PAD_YPOS:
140       self->ypos = g_value_get_int (value);
141       break;
142     case PROP_PAD_WIDTH:
143       self->width = g_value_get_int (value);
144       break;
145     case PROP_PAD_HEIGHT:
146       self->height = g_value_get_int (value);
147       break;
148     case PROP_PAD_ALPHA:
149       self->alpha = g_value_get_double (value);
150       break;
151     default:
152       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
153       break;
154   }
155   GST_OBJECT_UNLOCK (object);
156 }
157
158 static void
159 gst_va_compositor_pad_finalize (GObject * object)
160 {
161   GstVaCompositorPad *self = GST_VA_COMPOSITOR_PAD (object);
162
163   if (self->pool) {
164     gst_buffer_pool_set_active (self->pool, FALSE);
165     gst_clear_object (&self->pool);
166   }
167
168   G_OBJECT_CLASS (gst_va_compositor_pad_parent_class)->finalize (object);
169 }
170
171 static void
172 gst_va_compositor_pad_init (GstVaCompositorPad * self)
173 {
174   self->pool = NULL;
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;
180 }
181
182 static void
183 gst_va_compositor_pad_class_init (GstVaCompositorPadClass * klass)
184 {
185   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
186   GstVideoAggregatorPadClass *vaggpad_class =
187       GST_VIDEO_AGGREGATOR_PAD_CLASS (klass);
188
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;
192
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,
213           DEFAULT_PAD_ALPHA,
214           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
215
216   /* Don't use mapped video frames.  Handle video buffers directly */
217   vaggpad_class->prepare_frame = NULL;
218   vaggpad_class->clean_frame = NULL;
219 }
220
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))
225
226 typedef struct _GstVaCompositor GstVaCompositor;
227 typedef struct _GstVaCompositorClass GstVaCompositorClass;
228
229 struct _GstVaCompositorClass
230 {
231   GstVideoAggregatorClass parent_class;
232
233   /*< private > */
234   gchar *render_device_path;
235 };
236
237 struct _GstVaCompositor
238 {
239   GstVideoAggregator parent;
240
241   GstVaDisplay *display;
242   GstVaFilter *filter;
243
244   GstVideoInfo other_info;      /* downstream info */
245   GstBufferPool *other_pool;    /* downstream pool */
246
247   guint32 scale_method;
248 };
249
250 struct CData
251 {
252   gchar *render_device_path;
253   gchar *description;
254 };
255
256 enum
257 {
258   PROP_DEVICE_PATH = 1,
259   PROP_SCALE_METHOD,
260   N_PROPERTIES
261 };
262
263 static GParamSpec *properties[N_PROPERTIES];
264 static GstElementClass *parent_class = NULL;
265
266 static void
267 gst_va_compositor_set_property (GObject * object, guint prop_id,
268     const GValue * value, GParamSpec * pspec)
269 {
270   GstVaCompositor *self = GST_VA_COMPOSITOR (object);
271
272   switch (prop_id) {
273     case PROP_SCALE_METHOD:
274     {
275       GST_OBJECT_LOCK (object);
276       self->scale_method = g_value_get_enum (value);
277       GST_OBJECT_UNLOCK (object);
278       break;
279     }
280     default:
281       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
282   }
283 }
284
285 static void
286 gst_va_compositor_get_property (GObject * object, guint prop_id,
287     GValue * value, GParamSpec * pspec)
288 {
289   GstVaCompositor *self = GST_VA_COMPOSITOR (object);
290
291   switch (prop_id) {
292     case PROP_DEVICE_PATH:
293     {
294       if (!(self->display && GST_IS_VA_DISPLAY_DRM (self->display))) {
295         g_value_set_string (value, NULL);
296         return;
297       }
298       g_object_get_property (G_OBJECT (self->display), "path", value);
299       break;
300     }
301     case PROP_SCALE_METHOD:
302     {
303       GST_OBJECT_LOCK (object);
304       g_value_set_enum (value, self->scale_method);
305       GST_OBJECT_UNLOCK (object);
306       break;
307     }
308     default:
309       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
310   }
311 }
312
313 static gboolean
314 gst_va_compositor_start (GstAggregator * agg)
315 {
316   GstElement *element = GST_ELEMENT (agg);
317   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
318   GstVaCompositorClass *klass = GST_VA_COMPOSITOR_GET_CLASS (agg);
319
320   if (!gst_va_ensure_element_data (element, klass->render_device_path,
321           &self->display))
322     return FALSE;
323   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
324
325   self->filter = gst_va_filter_new (self->display);
326   if (!gst_va_filter_open (self->filter))
327     return FALSE;
328
329   return GST_AGGREGATOR_CLASS (parent_class)->start (agg);
330 }
331
332 static gboolean
333 gst_va_compositor_stop (GstAggregator * agg)
334 {
335   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
336
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]);
341
342   return GST_AGGREGATOR_CLASS (parent_class)->stop (agg);
343 }
344
345 static void
346 gst_va_compositor_dispose (GObject * object)
347 {
348   GstVaCompositor *self = GST_VA_COMPOSITOR (object);
349
350   if (self->other_pool) {
351     gst_buffer_pool_set_active (self->other_pool, FALSE);
352     gst_clear_object (&self->other_pool);
353   }
354
355   gst_clear_object (&self->display);
356
357   G_OBJECT_CLASS (parent_class)->dispose (object);
358 }
359
360 static GstPad *
361 gst_va_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ,
362     const gchar * req_name, const GstCaps * caps)
363 {
364   GstPad *newpad = GST_PAD (GST_ELEMENT_CLASS
365       (parent_class)->request_new_pad (element, templ, req_name, caps));
366
367   if (!newpad)
368     GST_DEBUG_OBJECT (element, "could not create/add pad");
369   else
370     gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
371         GST_OBJECT_NAME (newpad));
372
373   return newpad;
374 }
375
376 static void
377 gst_va_compositor_release_pad (GstElement * element, GstPad * pad)
378 {
379   GstVaCompositor *self = GST_VA_COMPOSITOR (element);
380
381   gst_child_proxy_child_removed (GST_CHILD_PROXY (self), G_OBJECT (pad),
382       GST_OBJECT_NAME (pad));
383
384   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
385 }
386
387 static void
388 gst_va_compositor_set_context (GstElement * element, GstContext * context)
389 {
390   GstVaDisplay *old_display, *new_display;
391   GstVaCompositor *self = GST_VA_COMPOSITOR (element);
392   GstVaCompositorClass *klass = GST_VA_COMPOSITOR_GET_CLASS (self);
393   gboolean ret;
394
395   old_display = self->display ? gst_object_ref (self->display) : NULL;
396   ret = gst_va_handle_set_context (element, context, klass->render_device_path,
397       &self->display);
398   new_display = self->display ? gst_object_ref (self->display) : NULL;
399
400   if (!ret
401       || (old_display && new_display && old_display != new_display
402           && self->filter)) {
403     GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
404         ("Can't replace VA display while operating"), (NULL));
405   }
406
407   gst_clear_object (&old_display);
408   gst_clear_object (&new_display);
409
410   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
411 }
412
413 static gboolean
414 _handle_context_query (GstVaCompositor * self, GstQuery * query)
415 {
416   GstVaDisplay *display = NULL;
417   gboolean ret = FALSE;
418
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);
422
423   return ret;
424 }
425
426 static GstCaps *
427 gst_va_compositor_sink_getcaps (GstPad * pad, GstCaps * filter)
428 {
429   GstCaps *sinkcaps;
430   GstCaps *template_caps;
431   GstCaps *filtered_caps;
432   GstCaps *returned_caps;
433
434   template_caps = gst_pad_get_pad_template_caps (pad);
435
436   sinkcaps = gst_pad_get_current_caps (pad);
437   if (!sinkcaps) {
438     sinkcaps = gst_caps_ref (template_caps);
439   } else {
440     sinkcaps = gst_caps_merge (sinkcaps, gst_caps_ref (template_caps));
441   }
442
443   if (filter) {
444     filtered_caps = gst_caps_intersect (sinkcaps, filter);
445     gst_caps_unref (sinkcaps);
446   } else {
447     filtered_caps = sinkcaps;
448   }
449
450   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
451
452   gst_caps_unref (template_caps);
453   gst_caps_unref (filtered_caps);
454
455   GST_DEBUG_OBJECT (pad, "returning %" GST_PTR_FORMAT, returned_caps);
456
457   return returned_caps;
458 }
459
460 static gboolean
461 gst_va_compositor_sink_acceptcaps (GstPad * pad, GstCaps * caps)
462 {
463   gboolean ret;
464   GstCaps *template_caps;
465
466   template_caps = gst_pad_get_pad_template_caps (pad);
467   template_caps = gst_caps_make_writable (template_caps);
468
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);
473
474   return ret;
475 }
476
477 static gboolean
478 gst_va_compositor_sink_query (GstAggregator * agg, GstAggregatorPad * pad,
479     GstQuery * query)
480 {
481   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
482
483   switch (GST_QUERY_TYPE (query)) {
484     case GST_QUERY_CONTEXT:
485     {
486       if (_handle_context_query (self, query))
487         return TRUE;
488       break;
489     }
490     case GST_QUERY_CAPS:
491     {
492       GstCaps *filter, *caps;
493
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);
498       return TRUE;
499     }
500     case GST_QUERY_ACCEPT_CAPS:
501     {
502       GstCaps *caps;
503       gboolean ret;
504
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);
508       return TRUE;
509     }
510     default:
511       break;
512   }
513
514   return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, pad, query);
515 }
516
517 static gboolean
518 gst_va_compositor_src_query (GstAggregator * agg, GstQuery * query)
519 {
520   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
521
522   switch (GST_QUERY_TYPE (query)) {
523     case GST_QUERY_CONTEXT:
524       if (_handle_context_query (self, query))
525         return TRUE;
526       break;
527     default:
528       break;
529   }
530
531   return GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
532 }
533
534 static GstAllocator *
535 gst_va_compositor_allocator_from_caps (GstVaCompositor * self, GstCaps * caps)
536 {
537   GstAllocator *allocator = NULL;
538
539   if (gst_caps_is_dmabuf (caps)) {
540     allocator = gst_va_dmabuf_allocator_new (self->display);
541   } else {
542     GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
543     allocator = gst_va_allocator_new (self->display, surface_formats);
544   }
545
546   return allocator;
547 }
548
549 /* Answer upstream allocation query. */
550 static gboolean
551 gst_va_compositor_propose_allocation (GstAggregator * agg,
552     GstAggregatorPad * aggpad, GstQuery * decide_query, GstQuery * query)
553 {
554   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
555   GstAllocator *allocator = NULL;
556   GstAllocationParams params = { 0, };
557   GstBufferPool *pool;
558   GstCaps *caps;
559   GstVideoInfo info;
560   gboolean update_allocator = FALSE;
561   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
562
563   gst_query_parse_allocation (query, &caps, NULL);
564
565   if (!caps)
566     return FALSE;
567
568   if (!gst_video_info_from_caps (&info, caps))
569     return FALSE;
570
571   if (gst_query_get_n_allocation_pools (query) > 0)
572     return TRUE;
573
574   size = GST_VIDEO_INFO_SIZE (&info);
575
576   if (gst_query_get_n_allocation_params (query) > 0) {
577     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
578     if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator)
579         && !GST_IS_VA_ALLOCATOR (allocator))
580       gst_clear_object (&allocator);
581     update_allocator = TRUE;
582   } else {
583     gst_allocation_params_init (&params);
584   }
585
586   if (!allocator) {
587     if (!(allocator = gst_va_compositor_allocator_from_caps (self, caps)))
588       return FALSE;
589   }
590
591   /* Now we have a VA-based allocator */
592
593   pool = gst_va_pool_new_with_config (caps, size, 1, 0, usage_hint,
594       GST_VA_FEATURE_AUTO, allocator, &params);
595   if (!pool) {
596     gst_object_unref (allocator);
597     goto config_failed;
598   }
599
600   if (update_allocator)
601     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
602   else
603     gst_query_add_allocation_param (query, allocator, &params);
604
605   gst_query_add_allocation_pool (query, pool, size, 1, 0);
606
607   GST_DEBUG_OBJECT (self,
608       "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
609       pool, allocator);
610
611   gst_object_unref (allocator);
612   gst_object_unref (pool);
613
614   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
615
616   return TRUE;
617
618 config_failed:
619   {
620     GST_ERROR_OBJECT (self, "failed to set config");
621     return FALSE;
622   }
623 }
624
625 static GstBufferPool *
626 _create_other_pool (GstAllocator * allocator, GstAllocationParams * params,
627     GstCaps * caps, guint size)
628 {
629   GstBufferPool *pool = NULL;
630   GstStructure *config;
631
632   pool = gst_video_buffer_pool_new ();
633   config = gst_buffer_pool_get_config (pool);
634
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);
639   }
640
641   return pool;
642 }
643
644 /* configure the allocation query that was answered downstream */
645 static gboolean
646 gst_va_compositor_decide_allocation (GstAggregator * agg, GstQuery * query)
647 {
648   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
649   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
650
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;
656   GstVideoInfo info;
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;
660
661   gst_query_parse_allocation (query, &caps, NULL);
662
663   gst_allocation_params_init (&other_params);
664   gst_allocation_params_init (&params);
665
666   if (!gst_video_info_from_caps (&info, caps)) {
667     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
668     return FALSE;
669   }
670
671   if (gst_query_get_n_allocation_params (query) > 0) {
672     GstVaDisplay *display;
673
674     gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
675     display = gst_va_allocator_peek_display (allocator);
676     if (!display) {
677       /* save the allocator for the other pool */
678       other_allocator = allocator;
679       allocator = NULL;
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;
684     }
685
686     update_allocator = TRUE;
687   } else {
688     update_allocator = FALSE;
689   }
690
691   if (gst_query_get_n_allocation_pools (query) > 0) {
692     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
693
694     if (pool) {
695       if (!GST_IS_VA_POOL (pool)) {
696         GST_DEBUG_OBJECT (self,
697             "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
698         other_pool = pool;
699         pool = NULL;
700       } else if (dont_use_other_pool) {
701         gst_clear_object (&pool);
702       }
703     }
704
705     update_pool = TRUE;
706   } else {
707     size = GST_VIDEO_INFO_SIZE (&info);
708     min = 1;
709     max = 0;
710     update_pool = FALSE;
711   }
712
713   if (!allocator) {
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)))
717       return FALSE;
718   }
719
720   if (!pool)
721     pool = gst_va_pool_new ();
722
723   config = gst_buffer_pool_get_config (pool);
724   gst_buffer_pool_config_set_allocator (config, allocator, &params);
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);
732     return FALSE;
733   }
734
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);
739   }
740
741   if (update_allocator)
742     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
743   else
744     gst_query_add_allocation_param (query, allocator, &params);
745
746   if (update_pool)
747     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
748   else
749     gst_query_add_allocation_pool (query, pool, size, min, max);
750
751   has_videometa = gst_query_find_allocation_meta (query,
752       GST_VIDEO_META_API_TYPE, NULL);
753
754   copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
755       && gst_caps_is_raw (caps));
756   if (copy_frames) {
757     if (other_pool) {
758       gst_object_replace ((GstObject **) & self->other_pool,
759           (GstObject *) other_pool);
760     } else {
761       self->other_pool =
762           _create_other_pool (other_allocator, &other_params, caps, size);
763     }
764     GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
765         self->other_pool);
766   } else {
767     gst_clear_object (&self->other_pool);
768   }
769
770   GST_DEBUG_OBJECT (self,
771       "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
772       pool, allocator);
773
774   gst_object_unref (allocator);
775   gst_object_unref (pool);
776   gst_clear_object (&other_allocator);
777   gst_clear_object (&other_pool);
778
779   return TRUE;
780 }
781
782 static GstBufferPool *
783 _get_sinkpad_pool (GstVaCompositor * self, GstVaCompositorPad * pad)
784 {
785   GstAllocator *allocator;
786   GstAllocationParams params = { 0, };
787   GstCaps *caps;
788   GstVideoInfo info;
789   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
790
791   if (pad->pool)
792     return pad->pool;
793
794   gst_allocation_params_init (&params);
795
796   caps = gst_pad_get_current_caps (GST_PAD (pad));
797   if (!caps)
798     return NULL;
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);
802     return NULL;
803   }
804
805   size = GST_VIDEO_INFO_SIZE (&info);
806
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, &params);
810   gst_caps_unref (caps);
811
812   if (!pad->pool) {
813     gst_object_unref (allocator);
814     return NULL;
815   }
816
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);
821   }
822
823   gst_object_unref (allocator);
824
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);
828     return NULL;
829   }
830
831   return pad->pool;
832 }
833
834 static inline gsize
835 _get_plane_data_size (GstVideoInfo * info, guint plane)
836 {
837   gint comp[GST_VIDEO_MAX_COMPONENTS];
838   gint height, padded_height;
839
840   gst_video_format_info_component (info->finfo, plane, comp);
841
842   height = GST_VIDEO_INFO_HEIGHT (info);
843   padded_height =
844       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], height);
845
846   return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
847 }
848
849 static gboolean
850 _try_import_dmabuf_unlocked (GstVaCompositor * self, GstVideoInfo * info,
851     GstBuffer * inbuf)
852 {
853   GstVideoMeta *meta;
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];
858
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);
862
863   /* This will eliminate most non-dmabuf out there */
864   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
865     return FALSE;
866
867   /* We cannot have multiple dmabuf per plane */
868   if (n_mem > n_planes)
869     return FALSE;
870
871   /* Update video info based on video meta */
872   if (meta) {
873     GST_VIDEO_INFO_WIDTH (info) = meta->width;
874     GST_VIDEO_INFO_HEIGHT (info) = meta->height;
875
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];
879     }
880   }
881
882   /* Find and validate all memories */
883   for (i = 0; i < n_planes; i++) {
884     guint plane_size;
885     guint length;
886     guint mem_idx;
887     gsize mem_skip;
888
889     plane_size = _get_plane_data_size (info, i);
890
891     if (!gst_buffer_find_memory (inbuf, info->offset[i], plane_size,
892             &mem_idx, &length, &mem_skip))
893       return FALSE;
894
895     /* We can't have more then one dmabuf per plane */
896     if (length != 1)
897       return FALSE;
898
899     mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
900
901     /* And all memory found must be dmabuf */
902     if (!gst_is_dmabuf_memory (mems[i]))
903       return FALSE;
904
905     offset[i] = mems[i]->offset + mem_skip;
906     fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
907   }
908
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);
912 }
913
914 extern GRecMutex GST_VA_SHARED_LOCK;
915
916 static gboolean
917 _try_import_buffer (GstVaCompositor * self, GstVideoInfo * info,
918     GstBuffer * inbuf)
919 {
920   VASurfaceID surface;
921   gboolean ret;
922
923   surface = gst_va_buffer_get_surface (inbuf);
924   if (surface != VA_INVALID_ID &&
925       (gst_va_buffer_peek_display (inbuf) == self->display))
926     return TRUE;
927
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);
931
932   return ret;
933 }
934
935 static GstFlowReturn
936 gst_va_compositor_import_buffer (GstVaCompositor * self,
937     GstVaCompositorPad * pad, GstBuffer * inbuf, GstBuffer ** buf)
938 {
939   GstBuffer *buffer = NULL;
940   GstBufferPool *pool;
941   GstFlowReturn ret;
942   GstCaps *caps;
943   GstVideoInfo info;
944   GstVideoFrame in_frame, out_frame;
945   gboolean imported, copied;
946
947   caps = gst_pad_get_current_caps (GST_PAD (pad));
948   if (!caps)
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;
954   }
955   gst_caps_unref (caps);
956
957   imported = _try_import_buffer (self, &info, inbuf);
958   if (imported) {
959     *buf = gst_buffer_ref (inbuf);
960     return GST_FLOW_OK;
961   }
962
963   GST_LOG_OBJECT (self, "copying input frame");
964
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;
970
971   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
972   if (ret != GST_FLOW_OK)
973     return ret;
974
975   if (!gst_video_frame_map (&in_frame, &info, inbuf, GST_MAP_READ))
976     goto invalid_buffer;
977
978   if (!gst_video_frame_map (&out_frame, &info, buffer, GST_MAP_WRITE)) {
979     gst_video_frame_unmap (&in_frame);
980     goto invalid_buffer;
981   }
982
983   copied = gst_video_frame_copy (&out_frame, &in_frame);
984
985   gst_video_frame_unmap (&out_frame);
986   gst_video_frame_unmap (&in_frame);
987
988   if (!copied)
989     goto invalid_buffer;
990
991   *buf = buffer;
992
993   return GST_FLOW_OK;
994
995 invalid_buffer:
996   {
997     GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL),
998         ("invalid video buffer received"));
999     if (buffer)
1000       gst_buffer_unref (buffer);
1001     return GST_FLOW_OK;
1002   }
1003 }
1004
1005 typedef struct _GstVaCompositorSampleGenerator GstVaCompositorSampleGenerator;
1006 struct _GstVaCompositorSampleGenerator
1007 {
1008   GstVaCompositor *comp;
1009   GList *current;
1010   GstVaComposeSample sample;
1011 };
1012
1013 static GstVaComposeSample *
1014 gst_va_compositor_sample_next (gpointer data)
1015 {
1016   GstVaCompositorSampleGenerator *generator;
1017   GstVideoAggregatorPad *vaggpad;
1018   GstVaCompositorPad *pad;
1019   GstBuffer *inbuf;
1020   GstBuffer *buf;
1021   GstFlowReturn res;
1022   GstVideoCropMeta *crop = NULL;
1023
1024   generator = (GstVaCompositorSampleGenerator *) data;
1025
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);
1030
1031     /* increment to next sinkpad */
1032     generator->current = generator->current->next;
1033
1034     /* reset sample */
1035     /* *INDENT-OFF* */
1036     generator->sample = (GstVaComposeSample) { 0, };
1037     /* *INDENT-ON* */
1038
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))
1042       continue;
1043
1044     inbuf = gst_video_aggregator_pad_get_current_buffer (vaggpad);
1045     pad = GST_VA_COMPOSITOR_PAD (vaggpad);
1046
1047     res = gst_va_compositor_import_buffer (generator->comp, pad, inbuf, &buf);
1048     if (res != GST_FLOW_OK)
1049       return &generator->sample;
1050
1051     crop = gst_buffer_get_video_crop_meta (buf);
1052
1053     GST_OBJECT_LOCK (vaggpad);
1054     /* *INDENT-OFF* */
1055     generator->sample = (GstVaComposeSample) {
1056       .buffer = buf,
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),
1062       },
1063       .output_region = (VARectangle) {
1064         .x = pad->xpos,
1065         .y = pad->ypos,
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,
1070       },
1071       .alpha = pad->alpha,
1072     };
1073     /* *INDENT-ON* */
1074     GST_OBJECT_UNLOCK (vaggpad);
1075
1076     return &generator->sample;
1077   }
1078
1079   return NULL;
1080 }
1081
1082 static gboolean
1083 gst_va_compositor_copy_output_buffer (GstVaCompositor * self,
1084     GstBuffer * src_buf, GstBuffer * dst_buf)
1085 {
1086   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (self);
1087   GstVideoFrame src_frame, dst_frame;
1088
1089   GST_LOG_OBJECT (self, "copying output buffer");
1090
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");
1093     return FALSE;
1094   }
1095
1096   if (!gst_video_frame_map (&dst_frame, &self->other_info, dst_buf,
1097           GST_MAP_WRITE)) {
1098     GST_ERROR_OBJECT (self, "couldn't map output buffer");
1099     gst_video_frame_unmap (&src_frame);
1100     return FALSE;
1101   }
1102
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);
1107     return FALSE;
1108   }
1109
1110   gst_video_frame_unmap (&src_frame);
1111   gst_video_frame_unmap (&dst_frame);
1112
1113   return TRUE;
1114 }
1115
1116 static GstFlowReturn
1117 gst_va_compositor_aggregate_frames (GstVideoAggregator * vagg,
1118     GstBuffer * outbuf)
1119 {
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;
1126
1127   if (self->other_pool) {
1128     /* create a va buffer for filter */
1129     ret = GST_VIDEO_AGGREGATOR_CLASS (parent_class)->create_output_buffer
1130         (vagg, &vabuffer);
1131     if (ret != GST_FLOW_OK)
1132       return ret;
1133
1134     need_copy = TRUE;
1135   } else {
1136     /* already a va buffer */
1137     vabuffer = gst_buffer_ref (outbuf);
1138   }
1139
1140   /* *INDENT-OFF* */
1141   generator = (GstVaCompositorSampleGenerator) {
1142     .comp = self,
1143     .current = GST_ELEMENT (self)->sinkpads,
1144   };
1145   tx = (GstVaComposeTransaction) {
1146     .next = gst_va_compositor_sample_next,
1147     .output = vabuffer,
1148     .user_data = (gpointer) &generator,
1149   };
1150   /* *INDENT-ON* */
1151
1152   GST_OBJECT_LOCK (self);
1153
1154   if (!gst_va_filter_set_scale_method (self->filter, self->scale_method))
1155     GST_WARNING_OBJECT (self, "couldn't set filter scale method");
1156
1157   if (!gst_va_filter_compose (self->filter, &tx)) {
1158     GST_ERROR_OBJECT (self, "couldn't apply filter");
1159     ret = GST_FLOW_ERROR;
1160   }
1161
1162   GST_OBJECT_UNLOCK (self);
1163
1164   if (ret != GST_FLOW_OK)
1165     goto done;
1166
1167   if (need_copy && !gst_va_compositor_copy_output_buffer (self, vabuffer,
1168           outbuf)) {
1169     GST_ERROR_OBJECT (self, "couldn't copy va buffer to output buffer");
1170     ret = GST_FLOW_ERROR;
1171   }
1172
1173 done:
1174   gst_buffer_unref (vabuffer);
1175   return ret;
1176 }
1177
1178 static GstFlowReturn
1179 gst_va_compositor_create_output_buffer (GstVideoAggregator * vagg,
1180     GstBuffer ** outbuf)
1181 {
1182   GstVaCompositor *self = GST_VA_COMPOSITOR (vagg);
1183   GstFlowReturn ret;
1184
1185   *outbuf = NULL;
1186
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
1190         (vagg, outbuf);
1191
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;
1198   }
1199
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;
1205   }
1206
1207   return GST_FLOW_OK;
1208 }
1209
1210 static gboolean
1211 gst_va_compositor_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
1212 {
1213   GstVaCompositor *self = GST_VA_COMPOSITOR (agg);
1214
1215   if (!gst_video_info_from_caps (&self->other_info, caps)) {
1216     GST_ERROR_OBJECT (self, "invalid caps");
1217     return FALSE;
1218   }
1219
1220   if (self->other_pool) {
1221     gst_buffer_pool_set_active (self->other_pool, FALSE);
1222     gst_clear_object (&self->other_pool);
1223   }
1224
1225   return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1226 }
1227
1228 static void
1229 gst_va_compositor_pad_get_output_size (GstVaCompositorPad * pad, gint * width,
1230     gint * height)
1231 {
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;
1237
1238   *width += MAX (pad->xpos, 0);
1239   *height += MAX (pad->ypos, 0);
1240 }
1241
1242 static GstCaps *
1243 gst_va_compositor_fixate_src_caps (GstAggregator * agg, GstCaps * caps)
1244 {
1245   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1246   GList *l;
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;
1251   GstStructure *s;
1252
1253   ret = gst_caps_make_writable (caps);
1254
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;
1260     gint fps_n, fps_d;
1261     gdouble cur_fps;
1262
1263     fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1264     fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1265
1266     gst_va_compositor_pad_get_output_size (pad, &this_width, &this_height);
1267
1268     if (best_width < this_width)
1269       best_width = this_width;
1270     if (best_height < this_height)
1271       best_height = this_height;
1272
1273     if (fps_d == 0)
1274       cur_fps = 0.0;
1275     else
1276       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1277
1278     if (best_fps < cur_fps) {
1279       best_fps = cur_fps;
1280       best_fps_n = fps_n;
1281       best_fps_d = fps_d;
1282     }
1283   }
1284   GST_OBJECT_UNLOCK (vagg);
1285
1286   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1287     best_fps_n = 25;
1288     best_fps_d = 1;
1289     best_fps = 25.0;
1290   }
1291
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,
1297         best_fps_d);
1298   } else {
1299     gst_structure_set (s, "framerate", GST_TYPE_FRACTION, best_fps_n,
1300         best_fps_d, NULL);
1301   }
1302
1303   return gst_caps_fixate (ret);
1304 }
1305
1306 /* *INDENT-OFF* */
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  }");
1312 /* *INDENT-ON* */
1313
1314 static void
1315 gst_va_compositor_class_init (gpointer g_class, gpointer class_data)
1316 {
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;
1327   gchar *long_name;
1328
1329   parent_class = g_type_class_peek_parent (g_class);
1330
1331   klass->render_device_path = g_strdup (cdata->render_device_path);
1332
1333   if (cdata->description) {
1334     long_name = g_strdup_printf ("VA-API Video Compositor in %s",
1335         cdata->description);
1336   } else {
1337     long_name = g_strdup ("VA-API Video Compositor");
1338   }
1339
1340   display = gst_va_display_drm_new_from_path (klass->render_device_path);
1341   filter = gst_va_filter_new (display);
1342
1343   if (gst_va_filter_open (filter)) {
1344     caps = gst_va_filter_get_caps (filter);
1345   } else {
1346     caps = gst_caps_from_string (caps_str);
1347   }
1348
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);
1354
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>");
1359
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);
1366
1367   doc_caps = gst_caps_from_string (caps_str);
1368
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);
1375
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));
1381
1382   gst_caps_unref (doc_caps);
1383   gst_caps_unref (caps);
1384
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);
1397
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);
1402
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);
1406
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);
1410
1411   gst_type_mark_as_plugin_api (GST_TYPE_VA_SCALE_METHOD, 0);
1412
1413   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
1414
1415   g_free (long_name);
1416   g_free (cdata->description);
1417   g_free (cdata->render_device_path);
1418   g_free (cdata);
1419   gst_object_unref (filter);
1420   gst_object_unref (display);
1421 }
1422
1423 static GObject *
1424 gst_va_compositor_child_proxy_get_child_by_index (GstChildProxy * proxy,
1425     guint index)
1426 {
1427   GstVaCompositor *self = GST_VA_COMPOSITOR (proxy);
1428   GObject *obj = NULL;
1429
1430   GST_OBJECT_LOCK (self);
1431   obj = g_list_nth_data (GST_ELEMENT_CAST (self)->sinkpads, index);
1432   if (obj)
1433     gst_object_ref (obj);
1434   GST_OBJECT_UNLOCK (self);
1435
1436   return obj;
1437 }
1438
1439 static guint
1440 gst_va_compositor_child_proxy_get_children_count (GstChildProxy * proxy)
1441 {
1442   GstVaCompositor *self = GST_VA_COMPOSITOR (proxy);
1443   guint count = 0;
1444
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);
1449
1450   return count;
1451 }
1452
1453 static void
1454 gst_va_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1455 {
1456   GstChildProxyInterface *iface = (GstChildProxyInterface *) g_iface;
1457
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;
1460 }
1461
1462 static void
1463 gst_va_compositor_init (GTypeInstance * instance, gpointer g_class)
1464 {
1465   GstVaCompositor *self = GST_VA_COMPOSITOR (instance);
1466
1467   self->other_pool = NULL;
1468 }
1469
1470 static gpointer
1471 _register_debug_category (gpointer data)
1472 {
1473   GST_DEBUG_CATEGORY_INIT (gst_va_compositor_debug, "vacompositor", 0,
1474       "VA Video Compositor");
1475
1476   return NULL;
1477 }
1478
1479 gboolean
1480 gst_va_compositor_register (GstPlugin * plugin, GstVaDevice * device,
1481     guint rank)
1482 {
1483   static GOnce debug_once = G_ONCE_INIT;
1484   GType type;
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,
1490   };
1491   GInterfaceInfo interface_info = {
1492     (GInterfaceInitFunc) gst_va_compositor_child_proxy_init,
1493   };
1494   struct CData *cdata;
1495   gboolean ret;
1496   gchar *type_name, *feature_name;
1497
1498   g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
1499   g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
1500
1501   cdata = g_new (struct CData, 1);
1502   cdata->description = NULL;
1503   cdata->render_device_path = g_strdup (device->render_device_path);
1504
1505   type_info.class_data = cdata;
1506
1507   type_name = g_strdup ("GstVaCompositor");
1508   feature_name = g_strdup ("vacompositor");
1509
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);
1515     g_free (type_name);
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;
1520
1521     /* lower rank for non-first device */
1522     if (rank > 0)
1523       rank--;
1524   }
1525
1526   g_once (&debug_once, _register_debug_category, NULL);
1527
1528   type = g_type_register_static (GST_TYPE_VIDEO_AGGREGATOR, type_name,
1529       &type_info, 0);
1530   g_type_add_interface_static (type, GST_TYPE_CHILD_PROXY, &interface_info);
1531
1532   ret = gst_element_register (plugin, feature_name, rank, type);
1533
1534   g_free (type_name);
1535   g_free (feature_name);
1536
1537   return ret;
1538 }