va: basetransform: Add device-path read-only property.
[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 "gstvaallocator.h"
28 #include "gstvacaps.h"
29 #include "gstvapool.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_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, 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
335   gst_query_parse_allocation (query, &outcaps, NULL);
336
337   gst_allocation_params_init (&other_params);
338   gst_allocation_params_init (&params);
339
340   if (!gst_video_info_from_caps (&vinfo, outcaps)) {
341     GST_ERROR_OBJECT (self, "Cannot parse caps %" GST_PTR_FORMAT, outcaps);
342     return FALSE;
343   }
344
345   if (gst_query_get_n_allocation_params (query) > 0) {
346     gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
347     if (allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (allocator)
348             || GST_IS_VA_ALLOCATOR (allocator))) {
349       /* save the allocator for the other pool */
350       other_allocator = allocator;
351       allocator = NULL;
352     }
353     update_allocator = TRUE;
354   } else {
355     update_allocator = FALSE;
356   }
357
358   if (gst_query_get_n_allocation_pools (query) > 0) {
359     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
360
361     if (pool) {
362       if (!GST_IS_VA_POOL (pool)) {
363         GST_DEBUG_OBJECT (self,
364             "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
365         other_pool = pool;
366         pool = NULL;
367       }
368     }
369
370     update_pool = TRUE;
371   } else {
372     size = GST_VIDEO_INFO_SIZE (&vinfo);
373     min = 1;
374     max = 0;
375     update_pool = FALSE;
376   }
377
378   if (!allocator) {
379     /* XXX(victor): USAGE_HINT_VPP_WRITE creates tiled dmabuf frames
380      * in iHD */
381     if (gst_caps_is_dmabuf (outcaps) && GST_VIDEO_INFO_IS_RGB (&vinfo))
382       usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
383     if (!(allocator =
384             gst_va_base_transform_allocator_from_caps (self, outcaps)))
385       return FALSE;
386   }
387
388   if (!pool)
389     pool = gst_va_pool_new ();
390
391   config = gst_buffer_pool_get_config (pool);
392   gst_buffer_pool_config_set_allocator (config, allocator, &params);
393   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
394   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
395   gst_buffer_pool_config_set_va_allocation_params (config, usage_hint);
396   if (!gst_buffer_pool_set_config (pool, config)) {
397     gst_object_unref (allocator);
398     gst_object_unref (pool);
399     return FALSE;
400   }
401
402   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
403     gst_va_dmabuf_allocator_get_format (allocator, &self->priv->srcpad_info,
404         NULL);
405   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
406     gst_va_allocator_get_format (allocator, &self->priv->srcpad_info, NULL);
407   }
408
409   if (update_allocator)
410     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
411   else
412     gst_query_add_allocation_param (query, allocator, &params);
413
414   if (update_pool)
415     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
416   else
417     gst_query_add_allocation_pool (query, pool, size, min, max);
418
419   has_videometa = gst_query_find_allocation_meta (query,
420       GST_VIDEO_META_API_TYPE, NULL);
421
422   copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
423       && gst_caps_is_raw (outcaps));
424   if (copy_frames) {
425     if (other_pool) {
426       gst_object_replace ((GstObject **) & self->priv->other_pool,
427           (GstObject *) other_pool);
428     } else {
429       self->priv->other_pool =
430           _create_other_pool (other_allocator, &other_params, outcaps, size);
431     }
432     GST_DEBUG_OBJECT (self, "Use the other pool for copy %" GST_PTR_FORMAT,
433         self->priv->other_pool);
434   } else {
435     gst_clear_object (&self->priv->other_pool);
436   }
437
438   GST_DEBUG_OBJECT (self,
439       "decided pool %" GST_PTR_FORMAT " with allocator %" GST_PTR_FORMAT,
440       pool, allocator);
441
442   gst_object_unref (allocator);
443   gst_object_unref (pool);
444   gst_clear_object (&other_allocator);
445   gst_clear_object (&other_pool);
446
447   /* removes allocation metas */
448   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
449       query);
450
451 }
452
453 /* output buffers must be from our VA-based pool, they cannot be
454  * system-allocated */
455 static gboolean
456 gst_va_base_transform_transform_size (GstBaseTransform * trans,
457     GstPadDirection direction, GstCaps * caps, gsize size,
458     GstCaps * othercaps, gsize * othersize)
459 {
460   return FALSE;
461 }
462
463 static GstFlowReturn
464 gst_va_base_transform_generate_output (GstBaseTransform * trans,
465     GstBuffer ** outbuf)
466 {
467   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (trans);
468   GstVideoFrame src_frame;
469   GstVideoFrame dest_frame;
470   GstBuffer *buffer = NULL;
471   GstFlowReturn ret;
472
473   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
474       outbuf);
475
476   if (ret != GST_FLOW_OK || *outbuf == NULL)
477     return ret;
478
479   if (!self->priv->other_pool)
480     return GST_FLOW_OK;
481
482   /* Now need to copy the output buffer */
483   ret = GST_FLOW_ERROR;
484
485   if (!gst_buffer_pool_set_active (self->priv->other_pool, TRUE)) {
486     GST_WARNING_OBJECT (self, "failed to active the other pool %"
487         GST_PTR_FORMAT, self->priv->other_pool);
488     goto out;
489   }
490
491   ret = gst_buffer_pool_acquire_buffer (self->priv->other_pool, &buffer, NULL);
492   if (ret != GST_FLOW_OK)
493     goto out;
494
495   if (!gst_video_frame_map (&src_frame, &self->priv->srcpad_info, *outbuf,
496           GST_MAP_READ))
497     goto out;
498
499   if (!gst_video_frame_map (&dest_frame, &self->out_info, buffer,
500           GST_MAP_WRITE)) {
501     gst_video_frame_unmap (&src_frame);
502     goto out;
503   }
504
505   if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
506     gst_video_frame_unmap (&src_frame);
507     gst_video_frame_unmap (&dest_frame);
508     goto out;
509   }
510
511   gst_video_frame_unmap (&src_frame);
512   gst_video_frame_unmap (&dest_frame);
513
514   gst_buffer_replace (outbuf, buffer);
515   ret = GST_FLOW_OK;
516
517 out:
518   gst_clear_buffer (&buffer);
519   return ret;
520 }
521
522 static GstStateChangeReturn
523 gst_va_base_transform_change_state (GstElement * element,
524     GstStateChange transition)
525 {
526   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (element);
527   GstVaBaseTransformClass *klass = GST_VA_BASE_TRANSFORM_GET_CLASS (element);
528   GstStateChangeReturn ret;
529
530   switch (transition) {
531     case GST_STATE_CHANGE_NULL_TO_READY:
532       if (!gst_va_ensure_element_data (element, klass->render_device_path,
533               &self->display))
534         goto open_failed;
535       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
536       gst_clear_caps (&self->priv->filter_caps);
537       gst_clear_object (&self->filter);
538       self->filter = gst_va_filter_new (self->display);
539       if (!gst_va_filter_open (self->filter))
540         goto open_failed;
541       if (klass->update_properties)
542         klass->update_properties (self);
543       break;
544     default:
545       break;
546   }
547
548   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
549
550   switch (transition) {
551     case GST_STATE_CHANGE_PAUSED_TO_READY:
552       gst_va_filter_close (self->filter);
553       break;
554     case GST_STATE_CHANGE_READY_TO_NULL:
555       gst_clear_caps (&self->priv->filter_caps);
556       gst_clear_object (&self->filter);
557       gst_clear_object (&self->display);
558       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEVICE_PATH]);
559       break;
560     default:
561       break;
562   }
563
564   return ret;
565
566   /* Errors */
567 open_failed:
568   {
569     GST_ELEMENT_ERROR (self, LIBRARY, INIT, (NULL), ("Failed to open VPP"));
570     return GST_STATE_CHANGE_FAILURE;
571   }
572 }
573
574 static void
575 gst_va_base_transform_set_context (GstElement * element, GstContext * context)
576 {
577   GstVaDisplay *old_display, *new_display;
578   GstVaBaseTransform *self = GST_VA_BASE_TRANSFORM (element);
579   GstVaBaseTransformClass *klass = GST_VA_BASE_TRANSFORM_GET_CLASS (self);
580   gboolean ret;
581
582   old_display = self->display ? gst_object_ref (self->display) : NULL;
583   ret = gst_va_handle_set_context (element, context, klass->render_device_path,
584       &self->display);
585   new_display = self->display ? gst_object_ref (self->display) : NULL;
586
587   if (!ret
588       || (old_display && new_display && old_display != new_display
589           && self->filter)) {
590     GST_ELEMENT_WARNING (element, RESOURCE, BUSY,
591         ("Can't replace VA display while operating"), (NULL));
592   }
593
594   gst_clear_object (&old_display);
595   gst_clear_object (&new_display);
596
597   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
598 }
599
600 static void
601 gst_va_base_transform_class_init (GstVaBaseTransformClass * klass)
602 {
603   GObjectClass *gobject_class;
604   GstElementClass *element_class;
605   GstBaseTransformClass *trans_class;
606
607   gobject_class = G_OBJECT_CLASS (klass);
608   element_class = GST_ELEMENT_CLASS (klass);
609   trans_class = GST_BASE_TRANSFORM_CLASS (klass);
610
611   gobject_class->dispose = gst_va_base_transform_dispose;
612   gobject_class->get_property = gst_va_base_transform_get_property;
613
614   trans_class->query = GST_DEBUG_FUNCPTR (gst_va_base_transform_query);
615   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_va_base_transform_set_caps);
616   trans_class->propose_allocation =
617       GST_DEBUG_FUNCPTR (gst_va_base_transform_propose_allocation);
618   trans_class->decide_allocation =
619       GST_DEBUG_FUNCPTR (gst_va_base_transform_decide_allocation);
620   trans_class->transform_size =
621       GST_DEBUG_FUNCPTR (gst_va_base_transform_transform_size);
622   trans_class->generate_output =
623       GST_DEBUG_FUNCPTR (gst_va_base_transform_generate_output);
624
625   element_class->set_context =
626       GST_DEBUG_FUNCPTR (gst_va_base_transform_set_context);
627   element_class->change_state =
628       GST_DEBUG_FUNCPTR (gst_va_base_transform_change_state);
629
630   properties[PROP_DEVICE_PATH] = g_param_spec_string ("device-path",
631       "Device Path", "DRM device path", NULL,
632       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
633
634   g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
635
636   gst_type_mark_as_plugin_api (GST_TYPE_VA_BASE_TRANSFORM, 0);
637 }
638
639 GstAllocator *
640 gst_va_base_transform_allocator_from_caps (GstVaBaseTransform * self,
641     GstCaps * caps)
642 {
643   GstAllocator *allocator = NULL;
644
645   if (gst_caps_is_dmabuf (caps)) {
646     allocator = gst_va_dmabuf_allocator_new (self->display);
647   } else {
648     GArray *surface_formats = gst_va_filter_get_surface_formats (self->filter);
649     allocator = gst_va_allocator_new (self->display, surface_formats);
650   }
651
652   return allocator;
653 }
654
655 static inline gsize
656 _get_plane_data_size (GstVideoInfo * info, guint plane)
657 {
658   gint comp[GST_VIDEO_MAX_COMPONENTS];
659   gint height, padded_height;
660
661   gst_video_format_info_component (info->finfo, plane, comp);
662
663   height = GST_VIDEO_INFO_HEIGHT (info);
664   padded_height =
665       GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, comp[0], height);
666
667   return GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
668 }
669
670 static gboolean
671 _try_import_dmabuf_unlocked (GstVaBaseTransform * self, GstBuffer * inbuf)
672 {
673   GstVaBaseTransform *btrans = GST_VA_BASE_TRANSFORM (self);
674   GstVideoMeta *meta;
675   GstVideoInfo in_info = btrans->in_info;
676   GstMemory *mems[GST_VIDEO_MAX_PLANES];
677   guint i, n_mem, n_planes;
678   gsize offset[GST_VIDEO_MAX_PLANES];
679   uintptr_t fd[GST_VIDEO_MAX_PLANES];
680
681   n_planes = GST_VIDEO_INFO_N_PLANES (&in_info);
682   n_mem = gst_buffer_n_memory (inbuf);
683   meta = gst_buffer_get_video_meta (inbuf);
684
685   /* This will eliminate most non-dmabuf out there */
686   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
687     return FALSE;
688
689   /* We cannot have multiple dmabuf per plane */
690   if (n_mem > n_planes)
691     return FALSE;
692
693   /* Update video info based on video meta */
694   if (meta) {
695     GST_VIDEO_INFO_WIDTH (&in_info) = meta->width;
696     GST_VIDEO_INFO_HEIGHT (&in_info) = meta->height;
697
698     for (i = 0; i < meta->n_planes; i++) {
699       GST_VIDEO_INFO_PLANE_OFFSET (&in_info, i) = meta->offset[i];
700       GST_VIDEO_INFO_PLANE_STRIDE (&in_info, i) = meta->stride[i];
701     }
702   }
703
704   /* Find and validate all memories */
705   for (i = 0; i < n_planes; i++) {
706     guint plane_size;
707     guint length;
708     guint mem_idx;
709     gsize mem_skip;
710
711     plane_size = _get_plane_data_size (&in_info, i);
712
713     if (!gst_buffer_find_memory (inbuf, in_info.offset[i], plane_size,
714             &mem_idx, &length, &mem_skip))
715       return FALSE;
716
717     /* We can't have more then one dmabuf per plane */
718     if (length != 1)
719       return FALSE;
720
721     mems[i] = gst_buffer_peek_memory (inbuf, mem_idx);
722
723     /* And all memory found must be dmabuf */
724     if (!gst_is_dmabuf_memory (mems[i]))
725       return FALSE;
726
727     offset[i] = mems[i]->offset + mem_skip;
728     fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
729   }
730
731   /* Now create a VASurfaceID for the buffer */
732   return gst_va_dmabuf_memories_setup (btrans->display, &in_info, n_planes,
733       mems, fd, offset, VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ);
734 }
735
736 static GstBufferPool *
737 _get_sinkpad_pool (GstVaBaseTransform * self)
738 {
739   GstAllocator *allocator;
740   GstAllocationParams params = { 0, };
741   GstCaps *caps;
742   GstVideoInfo in_info;
743   guint size, usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
744
745   if (self->priv->sinkpad_pool)
746     return self->priv->sinkpad_pool;
747
748   gst_allocation_params_init (&params);
749
750   if (self->priv->sinkpad_caps) {
751     caps = self->priv->sinkpad_caps;
752     gst_video_info_from_caps (&in_info, caps);
753   } else {
754     caps = self->in_caps;
755     in_info = self->in_info;
756   }
757
758   size = GST_VIDEO_INFO_SIZE (&in_info);
759
760   allocator = gst_va_base_transform_allocator_from_caps (self, caps);
761   self->priv->sinkpad_pool = gst_va_pool_new_with_config (caps, size, 1, 0,
762       usage_hint, allocator, &params);
763   if (!self->priv->sinkpad_pool) {
764     gst_object_unref (allocator);
765     return NULL;
766   }
767
768   if (GST_IS_VA_DMABUF_ALLOCATOR (allocator)) {
769     gst_va_dmabuf_allocator_get_format (allocator, &self->priv->sinkpad_info,
770         NULL);
771   } else if (GST_IS_VA_ALLOCATOR (allocator)) {
772     gst_va_allocator_get_format (allocator, &self->priv->sinkpad_info, NULL);
773   }
774
775   gst_object_unref (allocator);
776
777   if (!gst_buffer_pool_set_active (self->priv->sinkpad_pool, TRUE)) {
778     GST_WARNING_OBJECT (self, "failed to active the sinkpad pool %"
779         GST_PTR_FORMAT, self->priv->sinkpad_pool);
780     return NULL;
781   }
782
783   return self->priv->sinkpad_pool;
784 }
785
786 static gboolean
787 _try_import_buffer (GstVaBaseTransform * self, GstBuffer * inbuf)
788 {
789   VASurfaceID surface;
790   gboolean ret;
791
792   surface = gst_va_buffer_get_surface (inbuf);
793   if (surface != VA_INVALID_ID)
794     return TRUE;
795
796   g_rec_mutex_lock (&GST_VA_SHARED_LOCK);
797   ret = _try_import_dmabuf_unlocked (self, inbuf);
798   g_rec_mutex_unlock (&GST_VA_SHARED_LOCK);
799
800   return ret;
801 }
802
803 GstFlowReturn
804 gst_va_base_transform_import_buffer (GstVaBaseTransform * self,
805     GstBuffer * inbuf, GstBuffer ** buf)
806 {
807   GstBuffer *buffer = NULL;
808   GstBufferPool *pool;
809   GstFlowReturn ret;
810   GstVideoFrame in_frame, out_frame;
811   gboolean imported, copied;
812
813   g_return_val_if_fail (GST_IS_VA_BASE_TRANSFORM (self), GST_FLOW_ERROR);
814
815   imported = _try_import_buffer (self, inbuf);
816   if (imported) {
817     *buf = gst_buffer_ref (inbuf);
818     return GST_FLOW_OK;
819   }
820
821   /* input buffer doesn't come from a vapool, thus it is required to
822    * have a pool, grab from it a new buffer and copy the input
823    * buffer to the new one */
824   if (!(pool = _get_sinkpad_pool (self)))
825     return GST_FLOW_ERROR;
826
827   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, NULL);
828   if (ret != GST_FLOW_OK)
829     return ret;
830
831   GST_LOG_OBJECT (self, "copying input frame");
832
833   if (!gst_video_frame_map (&in_frame, &self->in_info, inbuf, GST_MAP_READ))
834     goto invalid_buffer;
835
836   if (!gst_video_frame_map (&out_frame, &self->priv->sinkpad_info, buffer,
837           GST_MAP_WRITE)) {
838     gst_video_frame_unmap (&in_frame);
839     goto invalid_buffer;
840   }
841
842   copied = gst_video_frame_copy (&out_frame, &in_frame);
843
844   gst_video_frame_unmap (&out_frame);
845   gst_video_frame_unmap (&in_frame);
846
847   if (!copied)
848     goto invalid_buffer;
849
850   /* copy metadata, default implemenation of baseclass will copy everything
851    * what we need */
852   GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
853       (GST_BASE_TRANSFORM_CAST (self), inbuf, buffer);
854
855   *buf = buffer;
856
857   return GST_FLOW_OK;
858
859 invalid_buffer:
860   {
861     GST_ELEMENT_WARNING (self, CORE, NOT_IMPLEMENTED, (NULL),
862         ("invalid video buffer received"));
863     if (buffer)
864       gst_buffer_unref (buffer);
865     return GST_FLOW_OK;
866   }
867 }
868
869 GstCaps *
870 gst_va_base_transform_get_filter_caps (GstVaBaseTransform * self)
871 {
872   g_return_val_if_fail (GST_IS_VA_BASE_TRANSFORM (self), NULL);
873
874   GST_OBJECT_LOCK (self);
875   if (self->priv->filter_caps) {
876     GST_OBJECT_UNLOCK (self);
877     return self->priv->filter_caps;
878   }
879   GST_OBJECT_UNLOCK (self);
880
881   if (!self->filter)
882     return NULL;
883
884   GST_OBJECT_LOCK (self);
885   self->priv->filter_caps = gst_va_filter_get_caps (self->filter);
886   GST_OBJECT_UNLOCK (self);
887   return self->priv->filter_caps;
888 }