5ced2fa1048a284efb90a74657e9e50669af0840
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / va / gstvabasetransform.c
1 /* GStreamer
2  * Copyright (C) 2021 Igalia, S.L.
3  *     Author: Víctor Jáquez <vjaquez@igalia.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 the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstvabasetransform.h"
26
27 #include <gst/va/gstva.h>
28
29 #include "gstvacaps.h"
30
31 #define GST_CAT_DEFAULT gst_va_base_transform_debug
32 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
33
34 enum
35 {
36   PROP_DEVICE_PATH = 1,
37   N_PROPERTIES
38 };
39
40 static GParamSpec *properties[N_PROPERTIES];
41
42 struct _GstVaBaseTransformPrivate
43 {
44   GstVideoInfo srcpad_info;
45
46   GstBufferPool *other_pool;
47
48   GstCaps *sinkpad_caps;
49   GstVideoInfo sinkpad_info;
50   GstBufferPool *sinkpad_pool;
51
52   GstCaps *filter_caps;
53 };
54
55 /**
56  * GstVaBaseTransform:
57  *
58  * A base class implementation for VA-API filters.
59  *
60  * Since: 1.20
61  */
62 #define gst_va_base_transform_parent_class parent_class
63 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVaBaseTransform, gst_va_base_transform,
64     GST_TYPE_BASE_TRANSFORM, G_ADD_PRIVATE (GstVaBaseTransform)
65     GST_DEBUG_CATEGORY_INIT (gst_va_base_transform_debug,
66         "vabasetransform", 0, "vabasetransform element");
67     );
68
69 extern GRecMutex GST_VA_SHARED_LOCK;
70
71 static void
72 gst_va_base_transform_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec)
74 {
75   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (object);
76
77   switch (prop_id) {
78     case PROP_DEVICE_PATH:{
79       if (!(self->display && GST_IS_VA_DISPLAY_DRM (self->display))) {
80         g_value_set_string (value, NULL);
81         return;
82       }
83       g_object_get_property (G_OBJECT (self->display), "path", value);
84       break;
85     }
86     default:
87       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
88   }
89 }
90
91 static void
92 gst_va_base_transform_dispose (GObject * object)
93 {
94   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (object);
95
96   if (self->priv->other_pool) {
97     gst_buffer_pool_set_active (self->priv->other_pool, FALSE);
98     gst_clear_object (&self->priv->other_pool);
99   }
100
101   gst_clear_caps (&self->out_caps);
102   gst_clear_caps (&self->in_caps);
103
104   gst_clear_caps (&self->priv->filter_caps);
105
106   gst_clear_object (&self->filter);
107   gst_clear_object (&self->display);
108
109   if (self->priv->sinkpad_pool) {
110     gst_buffer_pool_set_active (self->priv->sinkpad_pool, FALSE);
111     gst_clear_object (&self->priv->sinkpad_pool);
112   }
113
114   gst_clear_caps (&self->priv->sinkpad_caps);
115
116   G_OBJECT_CLASS (parent_class)->dispose (object);
117 }
118
119 static void
120 gst_va_base_transform_init (GstVaBaseTransform * self)
121 {
122   gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (self), TRUE);
123
124   self->priv = gst_va_base_transform_get_instance_private (self);
125 }
126
127 static gboolean
128 gst_va_base_transform_query (GstBaseTransform * trans,
129     GstPadDirection direction, GstQuery * query)
130 {
131   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans);
132   gboolean ret = FALSE;
133
134   switch (GST_QUERY_TYPE (query)) {
135     case GST_QUERY_CONTEXT:
136     {
137       GstVaDisplay *display = NULL;
138
139       gst_object_replace ((GstObject **) & display,
140           (GstObject *) self->display);
141       ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query,
142           display);
143       gst_clear_object (&display);
144       break;
145     }
146     default:
147       ret = GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
148           query);
149       break;
150   }
151
152   return ret;
153 }
154
155 static gboolean
156 gst_va_base_transform_set_caps (GstBaseTransform * trans, GstCaps * incaps,
157     GstCaps * outcaps)
158 {
159   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans);
160   GstVaBaseTransformClass *fclass;
161   GstVideoInfo in_info, out_info;
162   gboolean res;
163
164   /* input caps */
165   if (!gst_video_info_from_caps (&in_info, incaps))
166     goto invalid_caps;
167
168   /* output caps */
169   if (!gst_video_info_from_caps (&out_info, outcaps))
170     goto invalid_caps;
171
172   fclass = GST_VA_BASE_TRANSFORM_GET_CLASS (self);
173   if (fclass->set_info)
174     res = fclass->set_info (self, incaps, &in_info, outcaps, &out_info);
175   else
176     res = TRUE;
177
178   self->negotiated = res;
179
180   if (res) {
181     gst_caps_replace (&self->in_caps, incaps);
182     gst_caps_replace (&self->out_caps, outcaps);
183
184     self->in_info = in_info;
185     self->out_info = out_info;
186   }
187
188   if (self->priv->sinkpad_pool) {
189     gst_buffer_pool_set_active (self->priv->sinkpad_pool, FALSE);
190     gst_clear_object (&self->priv->sinkpad_pool);
191   }
192
193   if (self->priv->other_pool) {
194     gst_buffer_pool_set_active (self->priv->other_pool, FALSE);
195     gst_clear_object (&self->priv->other_pool);
196   }
197
198   return res;
199
200   /* ERRORS */
201 invalid_caps:
202   {
203     GST_ERROR_OBJECT (self, "invalid caps");
204     self->negotiated = FALSE;
205     return FALSE;
206   }
207 }
208
209 /* Answer upstream allocation query. */
210 static gboolean
211 gst_va_base_transform_propose_allocation (GstBaseTransform * trans,
212     GstQuery * decide_query, GstQuery * query)
213 {
214   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans);
215   GstAllocator *allocator = NULL;
216   GstAllocationParams params = { 0, };
217   GstBufferPool *pool;
218   GstCaps *caps;
219   GstVideoInfo info;
220   gboolean update_allocator = FALSE;
221   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;        /* it migth be
222                                                                          * used by a va
223                                                                          * decoder */
224
225   gst_clear_caps (&self->priv->sinkpad_caps);
226
227   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
228           decide_query, query))
229     return FALSE;
230
231   /* passthrough, we're done */
232   if (!decide_query)
233     return TRUE;
234
235   if (gst_query_get_n_allocation_pools (query) > 0)
236     return TRUE;
237
238   gst_query_parse_allocation (query, &caps, NULL);
239   if (!caps)
240     return FALSE;
241   if (!gst_video_info_from_caps (&info, caps)) {
242     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, caps);
243     return FALSE;
244   }
245
246   size = GST_VIDEO_INFO_SIZE (&info);
247
248   if (gst_query_get_n_allocation_params (query) > 0) {
249     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
250     if (!GST_IS_VA_DMABUF_ALLOCATOR (allocator)
251         && !GST_IS_VA_ALLOCATOR (allocator))
252       gst_clear_object (&allocator);
253     update_allocator = TRUE;
254   } else {
255     gst_allocation_params_init (&params);
256   }
257
258   if (!allocator) {
259     if (!(allocator = gst_va_base_transform_allocator_from_caps (self, caps)))
260       return FALSE;
261   }
262
263   pool = gst_va_pool_new_with_config (caps, size, 1 + self->extra_min_buffers,
264       0, usage_hint, GST_VA_FEATURE_AUTO, allocator, &params);
265   if (!pool) {
266     gst_object_unref (allocator);
267     goto config_failed;
268   }
269
270   if (update_allocator)
271     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
272   else
273     gst_query_add_allocation_param (query, allocator, &params);
274
275   gst_query_add_allocation_pool (query, pool, size, 1 + self->extra_min_buffers,
276       0);
277
278   GST_DEBUG_OBJECT (self,
279       "proposing %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
280       pool, allocator);
281
282   gst_object_unref (allocator);
283   gst_object_unref (pool);
284
285   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
286
287   self->priv->sinkpad_caps = gst_caps_ref (caps);
288
289   return TRUE;
290
291   /* ERRORS */
292 config_failed:
293   {
294     GST_ERROR_OBJECT (self, "failed to set config");
295     return FALSE;
296   }
297 }
298
299 static GstBufferPool *
300 _create_other_pool (GstAllocator * allocator,
301     GstAllocationParams * params, GstCaps * caps, guint size)
302 {
303   GstBufferPool *pool = NULL;
304   GstStructure *config;
305
306   pool = gst_video_buffer_pool_new ();
307   config = gst_buffer_pool_get_config (pool);
308
309   gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
310   gst_buffer_pool_config_set_allocator (config, allocator, params);
311   if (!gst_buffer_pool_set_config (pool, config)) {
312     gst_clear_object (&pool);
313   }
314
315   return pool;
316 }
317
318 /* configure the allocation query that was answered downstream, we can
319  * configure some properties on it. Only it's called when not in
320  * passthrough mode. */
321 static gboolean
322 gst_va_base_transform_decide_allocation (GstBaseTransform * trans,
323     GstQuery * query)
324 {
325   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans);
326   GstAllocator *allocator = NULL, *other_allocator = NULL;
327   GstAllocationParams params, other_params;
328   GstBufferPool *pool = NULL, *other_pool = NULL;
329   GstCaps *outcaps = NULL;
330   GstStructure *config;
331   GstVideoInfo vinfo;
332   guint min, max, size = 0, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
333   gboolean update_pool, update_allocator, has_videometa, copy_frames;
334   gboolean dont_use_other_pool = FALSE;
335
336   gst_query_parse_allocation (query, &outcaps, NULL);
337
338   gst_allocation_params_init (&other_params);
339   gst_allocation_params_init (&params);
340
341   if (!gst_video_info_from_caps (&vinfo, outcaps)) {
342     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, outcaps);
343     return FALSE;
344   }
345
346   if (gst_query_get_n_allocation_params (query) > 0) {
347     GstVaDisplay *display;
348
349     gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
350     display = gst_va_allocator_peek_display (allocator);
351     if (!display) {
352       /* save the allocator for the other pool */
353       other_allocator = allocator;
354       allocator = NULL;
355     } else if (display != self->display) {
356       /* The allocator and pool belong to other display, we should not use. */
357       gst_clear_object (&allocator);
358       dont_use_other_pool = TRUE;
359     }
360
361     update_allocator = TRUE;
362   } else {
363     update_allocator = FALSE;
364   }
365
366   if (gst_query_get_n_allocation_pools (query) > 0) {
367     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
368
369     if (pool) {
370       if (!GST_IS_VA_POOL (pool)) {
371         GST_DEBUG_OBJECT (self,
372             "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
373         other_pool = pool;
374         pool = NULL;
375       } else if (dont_use_other_pool) {
376         gst_clear_object (&pool);
377       }
378     }
379
380     update_pool = TRUE;
381   } else {
382     size = GST_VIDEO_INFO_SIZE (&vinfo);
383     min = 1;
384     max = 0;
385     update_pool = FALSE;
386   }
387
388   if (!allocator) {
389     /* XXX(victor): USAGE_HINT_VPP_WRITE creates tiled dmabuf frames
390      * in iHD */
391     if (gst_caps_is_dmabuf (outcaps) && GST_VIDEO_INFO_IS_RGB (&vinfo))
392       usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
393     if (!(allocator =
394             gst_va_base_transform_allocator_from_caps (self, outcaps)))
395       return FALSE;
396   }
397
398   if (!pool)
399     pool = gst_va_pool_new ();
400
401   config = gst_buffer_pool_get_config (pool);
402   gst_buffer_pool_config_set_allocator (config, allocator, &params);
403   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
404   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
405   gst_buffer_pool_config_set_va_allocation_params (config, usage_hint,
406       GST_VA_FEATURE_AUTO);
407   if (!gst_buffer_pool_set_config (pool, config)) {
408     gst_object_unref (allocator);
409     gst_object_unref (pool);
410     return FALSE;
411   }
412
413   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
414     gst_va_dmabuf_allocator_get_format (allocator, &self->priv->srcpad_info,
415         NULL);
416   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
417     gst_va_allocator_get_format (allocator, &self->priv->srcpad_info, NULL,
418         NULL);
419   }
420
421   if (update_allocator)
422     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
423   else
424     gst_query_add_allocation_param (query, allocator, &params);
425
426   if (update_pool)
427     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
428   else
429     gst_query_add_allocation_pool (query, pool, size, min, max);
430
431   has_videometa = gst_query_find_allocation_meta (query,
432       GST_VIDEO_META_API_TYPE, NULL);
433
434   copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
435       && gst_caps_is_raw (outcaps));
436   if (copy_frames) {
437     if (other_pool) {
438       gst_object_replace ((GstObject **) & self->priv->other_pool,
439           (GstObject *) other_pool);
440     } else {
441       self->priv->other_pool =
442           _create_other_pool (other_allocator, &other_params, outcaps, size);
443     }
444     GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
445         self->priv->other_pool);
446   } else {
447     gst_clear_object (&self->priv->other_pool);
448   }
449
450   GST_DEBUG_OBJECT (self,
451       "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
452       pool, allocator);
453
454   gst_object_unref (allocator);
455   gst_object_unref (pool);
456   gst_clear_object (&other_allocator);
457   gst_clear_object (&other_pool);
458
459   /* removes allocation metas */
460   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
461       query);
462
463 }
464
465 /* output buffers must be from our VA-based pool, they cannot be
466  * system-allocated */
467 static gboolean
468 gst_va_base_transform_transform_size (GstBaseTransform * trans,
469     GstPadDirection direction, GstCaps * caps, gsize size,
470     GstCaps * othercaps, gsize * othersize)
471 {
472   return FALSE;
473 }
474
475 static GstFlowReturn
476 gst_va_base_transform_generate_output (GstBaseTransform * trans,
477     GstBuffer ** outbuf)
478 {
479   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans);
480   GstVideoFrame src_frame;
481   GstVideoFrame dest_frame;
482   GstBuffer *buffer = NULL;
483   GstFlowReturn ret;
484
485   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
486       outbuf);
487
488   if (ret != GST_FLOW_OK || *outbuf == NULL)
489     return ret;
490
491   if (!self->priv->other_pool)
492     return GST_FLOW_OK;
493
494   /* Now need to copy the output buffer */
495   ret = GST_FLOW_ERROR;
496
497   if (!gst_buffer_pool_set_active (self->priv->other_pool, TRUE)) {
498     GST_WARNING_OBJECT (self, "failed to active the other pool %"
499         GST_PTR_FORMAT, self->priv->other_pool);
500     goto out;
501   }
502
503   ret = gst_buffer_pool_acquire_buffer (self->priv->other_pool, &buffer, NULL);
504   if (ret != GST_FLOW_OK)
505     goto out;
506
507   if (!gst_video_frame_map (&src_frame, &self->priv->srcpad_info, *outbuf,
508           GST_MAP_READ))
509     goto out;
510
511   if (!gst_video_frame_map (&dest_frame, &self->out_info, buffer,
512           GST_MAP_WRITE)) {
513     gst_video_frame_unmap (&src_frame);
514     goto out;
515   }
516
517   if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
518     gst_video_frame_unmap (&src_frame);
519     gst_video_frame_unmap (&dest_frame);
520     goto out;
521   }
522
523   gst_video_frame_unmap (&src_frame);
524   gst_video_frame_unmap (&dest_frame);
525
526   gst_buffer_replace (outbuf, buffer);
527   ret = GST_FLOW_OK;
528
529 out:
530   gst_clear_buffer (&buffer);
531   return ret;
532 }
533
534 static GstStateChangeReturn
535 gst_va_base_transform_change_state (GstElement * element,
536     GstStateChange transition)
537 {
538   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (element);
539   GstVaBaseTransformClass *klass = GST_VA_BASE_TRANSFORM_GET_CLASS (element);
540   GstStateChangeReturn ret;
541
542   switch (transition) {
543     case GST_STATE_CHANGE_NULL_TO_READY:
544       if (!gst_va_ensure_element_data (element, klass->render_device_path,
545               &self->display))
546         goto open_failed;
547       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
548       gst_clear_caps (&self->priv->filter_caps);
549       gst_clear_object (&self->filter);
550       self->filter = gst_va_filter_new (self->display);
551       if (!gst_va_filter_open (self->filter))
552         goto open_failed;
553       if (klass->update_properties)
554         klass->update_properties (self);
555       break;
556     default:
557       break;
558   }
559
560   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
561
562   switch (transition) {
563     case GST_STATE_CHANGE_PAUSED_TO_READY:
564       gst_va_filter_close (self->filter);
565       break;
566     case GST_STATE_CHANGE_READY_TO_NULL:
567       gst_clear_caps (&self->priv->filter_caps);
568       gst_clear_object (&self->filter);
569       gst_clear_object (&self->display);
570       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
571       break;
572     default:
573       break;
574   }
575
576   return ret;
577
578   /* Errors */
579 open_failed:
580   {
581     GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to open VPP"));
582     return GST_STATE_CHANGE_FAILURE;
583   }
584 }
585
586 static void
587 gst_va_base_transform_set_context (GstElement * element, GstContext * context)
588 {
589   GstVaDisplay *old_display, *new_display;
590   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (element);
591   GstVaBaseTransformClass *klass = GST_VA_BASE_TRANSFORM_GET_CLASS (self);
592   gboolean ret;
593
594   old_display = self->display ? gst_object_ref (self->display) : NULL;
595   ret = gst_va_handle_set_context (element, context, klass->render_device_path,
596       &self->display);
597   new_display = self->display ? gst_object_ref (self->display) : NULL;
598
599   if (!ret
600       || (old_display && new_display && old_display != new_display
601           && self->filter)) {
602     GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
603         ("Can't replace VA display while operating"), (NULL));
604   }
605
606   gst_clear_object (&old_display);
607   gst_clear_object (&new_display);
608
609   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
610 }
611
612 static void
613 gst_va_base_transform_class_init (GstVaBaseTransformClass * klass)
614 {
615   GObjectClass *gobject_class;
616   GstElementClass *element_class;
617   GstBaseTransformClass *trans_class;
618
619   gobject_class = G_OBJECT_CLASS (klass);
620   element_class = GST_ELEMENT_CLASS (klass);
621   trans_class = GST_BASE_TRANSFORM_CLASS (klass);
622
623   gobject_class->dispose = gst_va_base_transform_dispose;
624   gobject_class->get_property = gst_va_base_transform_get_property;
625
626   trans_class->query = GST_DEBUG_FUNCPTR (gst_va_base_transform_query);
627   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_va_base_transform_set_caps);
628   trans_class->propose_allocation =
629       GST_DEBUG_FUNCPTR (gst_va_base_transform_propose_allocation);
630   trans_class->decide_allocation =
631       GST_DEBUG_FUNCPTR (gst_va_base_transform_decide_allocation);
632   trans_class->transform_size =
633       GST_DEBUG_FUNCPTR (gst_va_base_transform_transform_size);
634   trans_class->generate_output =
635       GST_DEBUG_FUNCPTR (gst_va_base_transform_generate_output);
636
637   element_class->set_context =
638       GST_DEBUG_FUNCPTR (gst_va_base_transform_set_context);
639   element_class->change_state =
640       GST_DEBUG_FUNCPTR (gst_va_base_transform_change_state);
641
642   /**
643    * GstVaBaseTransform:device-path:
644    *
645    * It shows the DRM device path used for the VA operation, if any.
646    *
647    * Since: 1.22
648    */
649   properties[PROP_DEVICE_PATH] = g_param_spec_string ("device-path",
650       "Device Path", "DRM device path", NULL,
651       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
652
653   g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
654
655   gst_type_mark_as_plugin_api (GST_TYPE_VA_BASE_TRANSFORM, 0);
656 }
657
658 GstAllocator *
659 gst_va_base_transform_allocator_from_caps (GstVaBaseTransform * self,
660     GstCaps * caps)
661 {
662   GstAllocator *allocator = NULL;
663
664   if (gst_caps_is_dmabuf (caps)) {
665     allocator = gst_va_dmabuf_allocator_new (self->display);
666   } else {
667     GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
668     allocator = gst_va_allocator_new (self->display, surface_formats);
669   }
670
671   return allocator;
672 }
673
674 static inline gsize
675 _get_plane_data_size (GstVideoInfo * info, guint plane)
676 {
677   gint comp[GST_VIDEO_MAX_COMPONENTS];
678   gint height, padded_height;
679
680   gst_video_format_info_component (info->finfo, plane, comp);
681
682   height = GST_VIDEO_INFO_HEIGHT (info);
683   padded_height =
684       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], height);
685
686   return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
687 }
688
689 static gboolean
690 _try_import_dmabuf_unlocked (GstVaBaseTransform * self, GstBuffer * inbuf)
691 {
692   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
693   GstVideoMeta *meta;
694   GstVideoInfo in_info = btrans->in_info;
695   GstMemory *mems[GST_VIDEO_MAX_PLANES];
696   guint i, n_mem, n_planes;
697   gsize offset[GST_VIDEO_MAX_PLANES];
698   uintptr_t fd[GST_VIDEO_MAX_PLANES];
699
700   n_planes = GST_VIDEO_INFO_N_PLANES (&in_info);
701   n_mem = gst_buffer_n_memory (inbuf);
702   meta = gst_buffer_get_video_meta (inbuf);
703
704   /* This will eliminate most non-dmabuf out there */
705   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
706     return FALSE;
707
708   /* We cannot have multiple dmabuf per plane */
709   if (n_mem > n_planes)
710     return FALSE;
711
712   /* Update video info based on video meta */
713   if (meta) {
714     GST_VIDEO_INFO_WIDTH (&in_info) = meta->width;
715     GST_VIDEO_INFO_HEIGHT (&in_info) = meta->height;
716
717     for (i = 0; i < meta->n_planes; i++) {
718       GST_VIDEO_INFO_PLANE_OFFSET (&in_info, i) = meta->offset[i];
719       GST_VIDEO_INFO_PLANE_STRIDE (&in_info, i) = meta->stride[i];
720     }
721   }
722
723   /* Find and validate all memories */
724   for (i = 0; i < n_planes; i++) {
725     guint plane_size;
726     guint length;
727     guint mem_idx;
728     gsize mem_skip;
729
730     plane_size = _get_plane_data_size (&in_info, i);
731
732     if (!gst_buffer_find_memory (inbuf, in_info.offset[i], plane_size,
733             &mem_idx, &length, &mem_skip))
734       return FALSE;
735
736     /* We can't have more then one dmabuf per plane */
737     if (length != 1)
738       return FALSE;
739
740     mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
741
742     /* And all memory found must be dmabuf */
743     if (!gst_is_dmabuf_memory (mems[i]))
744       return FALSE;
745
746     offset[i] = mems[i]->offset + mem_skip;
747     fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
748   }
749
750   /* Now create a VASurfaceID for the buffer */
751   return gst_va_dmabuf_memories_setup (btrans->display, &in_info, n_planes,
752       mems, fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ);
753 }
754
755 static GstBufferPool *
756 _get_sinkpad_pool (GstVaBaseTransform * self)
757 {
758   GstAllocator *allocator;
759   GstAllocationParams params = { 0, };
760   GstCaps *caps;
761   GstVideoInfo in_info;
762   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
763
764   if (self->priv->sinkpad_pool)
765     return self->priv->sinkpad_pool;
766
767   gst_allocation_params_init (&params);
768
769   if (self->priv->sinkpad_caps) {
770     caps = self->priv->sinkpad_caps;
771     gst_video_info_from_caps (&in_info, caps);
772   } else {
773     caps = self->in_caps;
774     in_info = self->in_info;
775   }
776
777   size = GST_VIDEO_INFO_SIZE (&in_info);
778
779   allocator = gst_va_base_transform_allocator_from_caps (self, caps);
780   self->priv->sinkpad_pool = gst_va_pool_new_with_config (caps, size, 1, 0,
781       usage_hint, GST_VA_FEATURE_AUTO, allocator, &params);
782   if (!self->priv->sinkpad_pool) {
783     gst_object_unref (allocator);
784     return NULL;
785   }
786
787   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
788     gst_va_dmabuf_allocator_get_format (allocator, &self->priv->sinkpad_info,
789         NULL);
790   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
791     gst_va_allocator_get_format (allocator, &self->priv->sinkpad_info, NULL,
792         NULL);
793   }
794
795   gst_object_unref (allocator);
796
797   if (!gst_buffer_pool_set_active (self->priv->sinkpad_pool, TRUE)) {
798     GST_WARNING_OBJECT (self, "failed to active the sinkpad pool %"
799         GST_PTR_FORMAT, self->priv->sinkpad_pool);
800     return NULL;
801   }
802
803   return self->priv->sinkpad_pool;
804 }
805
806 static gboolean
807 _try_import_buffer (GstVaBaseTransform * self, GstBuffer * inbuf)
808 {
809   VASurfaceID surface;
810   gboolean ret;
811
812   surface = gst_va_buffer_get_surface (inbuf);
813   if (surface != VA_INVALID_ID &&
814       (gst_va_buffer_peek_display (inbuf) == self->display))
815     return TRUE;
816
817   g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
818   ret = _try_import_dmabuf_unlocked (self, inbuf);
819   g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
820
821   return ret;
822 }
823
824 GstFlowReturn
825 gst_va_base_transform_import_buffer (GstVaBaseTransform * self,
826     GstBuffer * inbuf, GstBuffer ** buf)
827 {
828   GstBuffer *buffer = NULL;
829   GstBufferPool *pool;
830   GstFlowReturn ret;
831   GstVideoFrame in_frame, out_frame;
832   gboolean imported, copied;
833
834   g_return_val_if_fail (GST_IS_VA_BASE_TRANSFORM (self), GST_FLOW_ERROR);
835
836   imported = _try_import_buffer (self, inbuf);
837   if (imported) {
838     *buf = gst_buffer_ref (inbuf);
839     return GST_FLOW_OK;
840   }
841
842   /* input buffer doesn't come from a vapool, thus it is required to
843    * have a pool, grab from it a new buffer and copy the input
844    * buffer to the new one */
845   if (!(pool = _get_sinkpad_pool (self)))
846     return GST_FLOW_ERROR;
847
848   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
849   if (ret != GST_FLOW_OK)
850     return ret;
851
852   GST_LOG_OBJECT (self, "copying input frame");
853
854   if (!gst_video_frame_map (&in_frame, &self->in_info, inbuf, GST_MAP_READ))
855     goto invalid_buffer;
856
857   if (!gst_video_frame_map (&out_frame, &self->priv->sinkpad_info, buffer,
858           GST_MAP_WRITE)) {
859     gst_video_frame_unmap (&in_frame);
860     goto invalid_buffer;
861   }
862
863   copied = gst_video_frame_copy (&out_frame, &in_frame);
864
865   gst_video_frame_unmap (&out_frame);
866   gst_video_frame_unmap (&in_frame);
867
868   if (!copied)
869     goto invalid_buffer;
870
871   /* copy metadata, default implemenation of baseclass will copy everything
872    * what we need */
873   GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
874       (GST_BASE_TRANSFORM_CAST (self), inbuf, buffer);
875
876   *buf = buffer;
877
878   return GST_FLOW_OK;
879
880 invalid_buffer:
881   {
882     GST_ELEMENT_WARNING (self, STREAM, FORMAT, (NULL),
883         ("invalid video buffer received"));
884     if (buffer)
885       gst_buffer_unref (buffer);
886     return GST_FLOW_ERROR;
887   }
888 }
889
890 GstCaps *
891 gst_va_base_transform_get_filter_caps (GstVaBaseTransform * self)
892 {
893   g_return_val_if_fail (GST_IS_VA_BASE_TRANSFORM (self), NULL);
894
895   GST_OBJECT_LOCK (self);
896   if (self->priv->filter_caps) {
897     GST_OBJECT_UNLOCK (self);
898     return self->priv->filter_caps;
899   }
900   GST_OBJECT_UNLOCK (self);
901
902   if (!self->filter)
903     return NULL;
904
905   GST_OBJECT_LOCK (self);
906   self->priv->filter_caps = gst_va_filter_get_caps (self->filter);
907   GST_OBJECT_UNLOCK (self);
908   return self->priv->filter_caps;
909 }