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, ¶ms);
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 (¶ms);
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, ¶ms);
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, ¶ms);
245 else
246 gst_query_add_allocation_param (query, allocator, ¶ms);
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 (¶ms);
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, ¶ms);
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, ¶ms);
384 else
385 gst_query_add_allocation_param (query, allocator, ¶ms);
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 (¶ms);
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, ¶ms);
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