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