1 /* GStreamer
2 * Copyright (C) 2020 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 the0
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "gstvabasedec.h"
22
23 #include "gstvaallocator.h"
24 #include "gstvacaps.h"
25 #include "gstvapool.h"
26 #include "gstvautils.h"
27 #include "gstvavideoformat.h"
28
29 #define GST_CAT_DEFAULT (base->debug_category)
30 #define GST_VA_BASE_DEC_GET_PARENT_CLASS(obj) (GST_VA_BASE_DEC_GET_CLASS(obj)->parent_decoder_class)
31
32 static gboolean
gst_va_base_dec_open(GstVideoDecoder * decoder)33 gst_va_base_dec_open (GstVideoDecoder * decoder)
34 {
35 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
36 GstVaBaseDecClass *klass = GST_VA_BASE_DEC_GET_CLASS (decoder);
37 gboolean ret = FALSE;
38
39 if (!gst_va_ensure_element_data (decoder, klass->render_device_path,
40 &base->display))
41 return FALSE;
42
43 if (!g_atomic_pointer_get (&base->decoder)) {
44 GstVaDecoder *va_decoder;
45
46 va_decoder = gst_va_decoder_new (base->display, klass->codec);
47 if (va_decoder)
48 ret = TRUE;
49
50 gst_object_replace ((GstObject **) (&base->decoder),
51 (GstObject *) va_decoder);
52 gst_clear_object (&va_decoder);
53 } else {
54 ret = TRUE;
55 }
56
57 base->apply_video_crop = FALSE;
58
59 return ret;
60 }
61
62 gboolean
gst_va_base_dec_close(GstVideoDecoder * decoder)63 gst_va_base_dec_close (GstVideoDecoder * decoder)
64 {
65 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
66
67 gst_clear_object (&base->decoder);
68 gst_clear_object (&base->display);
69
70 return TRUE;
71 }
72
73 static gboolean
gst_va_base_dec_stop(GstVideoDecoder * decoder)74 gst_va_base_dec_stop (GstVideoDecoder * decoder)
75 {
76 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
77
78 if (!gst_va_decoder_close (base->decoder))
79 return FALSE;
80
81 if (base->output_state)
82 gst_video_codec_state_unref (base->output_state);
83 base->output_state = NULL;
84
85 if (base->other_pool)
86 gst_buffer_pool_set_active (base->other_pool, FALSE);
87 gst_clear_object (&base->other_pool);
88
89 g_clear_pointer (&base->convert, gst_video_converter_free);
90
91 return GST_VIDEO_DECODER_CLASS (GST_VA_BASE_DEC_GET_PARENT_CLASS
92 (decoder))->stop (decoder);
93 }
94
95 static GstCaps *
gst_va_base_dec_getcaps(GstVideoDecoder * decoder,GstCaps * filter)96 gst_va_base_dec_getcaps (GstVideoDecoder * decoder, GstCaps * filter)
97 {
98 GstCaps *caps = NULL, *tmp;
99 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
100 GstVaDecoder *va_decoder = NULL;
101
102 gst_object_replace ((GstObject **) & va_decoder, (GstObject *) base->decoder);
103
104 if (va_decoder) {
105 caps = gst_va_decoder_get_sinkpad_caps (va_decoder);
106 gst_object_unref (va_decoder);
107 }
108
109 if (caps) {
110 if (filter) {
111 tmp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
112 gst_caps_unref (caps);
113 caps = tmp;
114 }
115 GST_LOG_OBJECT (base, "Returning caps %" GST_PTR_FORMAT, caps);
116 } else {
117 caps = gst_video_decoder_proxy_getcaps (decoder, NULL, filter);
118 }
119
120 return caps;
121 }
122
123 static gboolean
_query_context(GstVaBaseDec * self,GstQuery * query)124 _query_context (GstVaBaseDec * self, GstQuery * query)
125 {
126 GstVaDisplay *display = NULL;
127 gboolean ret;
128
129 gst_object_replace ((GstObject **) & display, (GstObject *) self->display);
130 ret = gst_va_handle_context_query (GST_ELEMENT_CAST (self), query, display);
131 gst_clear_object (&display);
132
133 return ret;
134 }
135
136 static gboolean
gst_va_base_dec_src_query(GstVideoDecoder * decoder,GstQuery * query)137 gst_va_base_dec_src_query (GstVideoDecoder * decoder, GstQuery * query)
138 {
139 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
140 gboolean ret = FALSE;
141
142 switch (GST_QUERY_TYPE (query)) {
143 case GST_QUERY_CONTEXT:{
144 ret = _query_context (base, query);
145 break;
146 }
147 case GST_QUERY_CAPS:{
148 GstCaps *caps = NULL, *tmp, *filter = NULL;
149 GstVaDecoder *va_decoder = NULL;
150 gboolean fixed_caps;
151
152 gst_object_replace ((GstObject **) & va_decoder,
153 (GstObject *) base->decoder);
154
155 gst_query_parse_caps (query, &filter);
156
157 fixed_caps = GST_PAD_IS_FIXED_CAPS (GST_VIDEO_DECODER_SRC_PAD (decoder));
158
159 if (!fixed_caps && va_decoder)
160 caps = gst_va_decoder_get_srcpad_caps (va_decoder);
161
162 gst_clear_object (&va_decoder);
163
164 if (caps) {
165 if (filter) {
166 tmp =
167 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
168 gst_caps_unref (caps);
169 caps = tmp;
170 }
171
172 GST_LOG_OBJECT (base, "Returning caps %" GST_PTR_FORMAT, caps);
173 gst_query_set_caps_result (query, caps);
174 gst_caps_unref (caps);
175 ret = TRUE;
176 break;
177 }
178 /* else jump to default */
179 }
180 default:
181 ret = GST_VIDEO_DECODER_CLASS (GST_VA_BASE_DEC_GET_PARENT_CLASS
182 (decoder))->src_query (decoder, query);
183 break;
184 }
185
186 return ret;
187 }
188
189 static gboolean
gst_va_base_dec_sink_query(GstVideoDecoder * decoder,GstQuery * query)190 gst_va_base_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query)
191 {
192 if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT)
193 return _query_context (GST_VA_BASE_DEC (decoder), query);
194 return GST_VIDEO_DECODER_CLASS (GST_VA_BASE_DEC_GET_PARENT_CLASS
195 (decoder))->sink_query (decoder, query);
196 }
197
198 static GstAllocator *
_create_allocator(GstVaBaseDec * base,GstCaps * caps)199 _create_allocator (GstVaBaseDec * base, GstCaps * caps)
200 {
201 GstAllocator *allocator = NULL;
202
203 if (gst_caps_is_dmabuf (caps))
204 allocator = gst_va_dmabuf_allocator_new (base->display);
205 else {
206 GArray *surface_formats =
207 gst_va_decoder_get_surface_formats (base->decoder);
208 allocator = gst_va_allocator_new (base->display, surface_formats);
209 }
210
211 return allocator;
212 }
213
214 static void
_create_other_pool(GstVaBaseDec * base,GstAllocator * allocator,GstAllocationParams * params,GstCaps * caps,guint size)215 _create_other_pool (GstVaBaseDec * base, GstAllocator * allocator,
216 GstAllocationParams * params, GstCaps * caps, guint size)
217 {
218 GstBufferPool *pool;
219 GstStructure *config;
220
221 gst_clear_object (&base->other_pool);
222
223 GST_DEBUG_OBJECT (base, "making new other pool for copy");
224
225 pool = gst_video_buffer_pool_new ();
226 config = gst_buffer_pool_get_config (pool);
227
228 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
229 gst_buffer_pool_config_set_allocator (config, allocator, params);
230 if (!gst_buffer_pool_set_config (pool, config)) {
231 GST_ERROR_OBJECT (base, "Couldn't configure other pool for copy.");
232 gst_clear_object (&pool);
233 }
234
235 base->other_pool = pool;
236 }
237
238 static gboolean
_need_video_crop(GstVaBaseDec * base)239 _need_video_crop (GstVaBaseDec * base)
240 {
241
242 if (base->need_valign &&
243 (base->valign.padding_left > 0 || base->valign.padding_top > 0))
244 return TRUE;
245
246 return FALSE;
247 }
248
249 /* This path for pool setting is a little complicated but not commonly
250 used. We deliberately separate it from the main path of pool setting. */
251 static gboolean
_decide_allocation_for_video_crop(GstVideoDecoder * decoder,GstQuery * query,GstCaps * caps,const GstVideoInfo * info)252 _decide_allocation_for_video_crop (GstVideoDecoder * decoder,
253 GstQuery * query, GstCaps * caps, const GstVideoInfo * info)
254 {
255 GstAllocator *allocator = NULL, *other_allocator = NULL;
256 GstAllocationParams other_params, params;
257 gboolean update_pool = FALSE, update_allocator = FALSE;
258 GstBufferPool *pool = NULL, *other_pool = NULL;
259 guint size = 0, min, max;
260 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
261 gboolean ret = TRUE;
262 GstCaps *va_caps = NULL;
263
264 /* If others provide a valid allocator, just use it. */
265 if (gst_query_get_n_allocation_params (query) > 0) {
266 gst_query_parse_nth_allocation_param (query, 0, &other_allocator,
267 &other_params);
268 update_allocator = TRUE;
269 } else {
270 gst_allocation_params_init (&other_params);
271 }
272
273 /* If others provide a valid pool, just use it. */
274 if (gst_query_get_n_allocation_pools (query) > 0) {
275 gst_query_parse_nth_allocation_pool (query, 0, &other_pool, &size, &min,
276 &max);
277
278 min += base->min_buffers;
279 size = MAX (size, GST_VIDEO_INFO_SIZE (info));
280 update_pool = TRUE;
281 } else {
282 size = GST_VIDEO_INFO_SIZE (info);
283 min = base->min_buffers;
284 max = 0;
285 }
286
287 /* Ensure that the other pool is ready */
288 if (gst_caps_is_raw (caps)) {
289 if (GST_IS_VA_POOL (other_pool))
290 gst_clear_object (&other_pool);
291
292 if (!other_pool) {
293 if (other_allocator && (GST_IS_VA_DMABUF_ALLOCATOR (other_allocator)
294 || GST_IS_VA_ALLOCATOR (other_allocator)))
295 gst_clear_object (&other_allocator);
296
297 _create_other_pool (base, other_allocator, &other_params, caps, size);
298 } else {
299 gst_object_replace ((GstObject **) & base->other_pool,
300 (GstObject *) other_pool);
301 }
302 } else {
303 GstStructure *other_config;
304
305 if (!GST_IS_VA_POOL (other_pool))
306 gst_clear_object (&other_pool);
307
308 if (!other_pool)
309 other_pool = gst_va_pool_new ();
310
311 if (other_allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (other_allocator)
312 || GST_IS_VA_ALLOCATOR (other_allocator)))
313 gst_clear_object (&other_allocator);
314
315 if (!other_allocator) {
316 other_allocator = _create_allocator (base, caps);
317 if (!other_allocator) {
318 ret = FALSE;
319 goto cleanup;
320 }
321 }
322
323 other_config = gst_buffer_pool_get_config (other_pool);
324
325 gst_buffer_pool_config_set_params (other_config, caps, size, min, max);
326 gst_buffer_pool_config_set_allocator (other_config, other_allocator,
327 &other_params);
328 /* Always support VideoMeta but no VideoCropMeta here. */
329 gst_buffer_pool_config_add_option (other_config,
330 GST_BUFFER_POOL_OPTION_VIDEO_META);
331
332 gst_buffer_pool_config_set_va_allocation_params (other_config, 0);
333
334 if (!gst_buffer_pool_set_config (other_pool, other_config)) {
335 ret = FALSE;
336 goto cleanup;
337 }
338
339 gst_object_replace ((GstObject **) & base->other_pool,
340 (GstObject *) other_pool);
341 }
342
343 /* Now setup the buffer pool for decoder */
344 pool = gst_va_pool_new ();
345
346 va_caps = gst_caps_copy (caps);
347 gst_caps_set_features_simple (va_caps,
348 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VA));
349
350 if (!(allocator = _create_allocator (base, va_caps))) {
351 ret = FALSE;
352 goto cleanup;
353 }
354
355 gst_allocation_params_init (¶ms);
356
357 {
358 GstStructure *config = gst_buffer_pool_get_config (pool);
359
360 gst_buffer_pool_config_set_params (config, caps, size, min, max);
361 gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
362 gst_buffer_pool_config_add_option (config,
363 GST_BUFFER_POOL_OPTION_VIDEO_META);
364
365 if (_need_video_crop (base))
366 gst_buffer_pool_config_set_va_alignment (config, &base->valign);
367
368 gst_buffer_pool_config_set_va_allocation_params (config,
369 VA_SURFACE_ATTRIB_USAGE_HINT_DECODER);
370
371 if (!gst_buffer_pool_set_config (pool, config)) {
372 ret = FALSE;
373 goto cleanup;
374 }
375 }
376
377 if (update_allocator)
378 gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
379 else
380 gst_query_add_allocation_param (query, allocator, ¶ms);
381
382 if (update_pool)
383 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
384 else
385 gst_query_add_allocation_pool (query, pool, size, min, max);
386
387 GST_WARNING_OBJECT (base, "We need to copy the output buffer manually "
388 "because of the top/left alignment, which may have low performance. "
389 "The element which supports VideoCropMeta such as 'vapostproc' can "
390 "avoid this.");
391 base->copy_frames = TRUE;
392 base->apply_video_crop = TRUE;
393
394 cleanup:
395 if (ret != TRUE)
396 gst_clear_object (&base->other_pool);
397 gst_clear_object (&allocator);
398 gst_clear_object (&other_allocator);
399 gst_clear_object (&pool);
400 gst_clear_object (&other_pool);
401 gst_clear_caps (&va_caps);
402
403 return ret;
404 }
405
406 /* We only support system pool and va pool. For va pool, its allocator
407 * should be va allocator or dma allocator.
408 * If output caps is memory:VAMemory, the pool should be a va pool
409 * with va allocator.
410 * If output caps is memory:DMABuf, the pool should be a va pool
411 * with dma allocator.
412 * We may need the other_pool to copy the decoder picture to the
413 * output buffer. We need to do this copy when:
414 * 1). The output caps is raw(system mem), but the downstream does
415 * not support VideoMeta and the strides and offsets of the va pool
416 * are different from the system memory pool, which means that the
417 * gst_video_frame_map() can not map the buffer correctly. Then we
418 * need a va pool with va allocator as an the internal pool and create
419 * a system pool as the other_pool to copy frames to system mem and
420 * output it.
421 * 2). The decoder has crop_top/left value > 0(e.g. the conformance
422 * window in the H265). Which means that the real output picture
423 * locates in the middle of the decoded buffer. If the downstream can
424 * support VideoCropMeta, a VideoCropMeta is added to notify the
425 * real picture's coordinate and size. But if not, we need to copy
426 * it manually and the other_pool is needed. We always assume that
427 * decoded picture starts from top-left corner, and so there is no
428 * need to do this if crop_bottom/right value > 0.
429 *
430 * 1. if crop_top/left value > 0 and the downstream does not support the
431 * VideoCropMeta, we always have the other_pool to do the copy(The pool
432 * may be provided by the downstream element, or created by ourself if
433 * no suitable one found).
434 * 2. get allocator in query
435 * 2.1 if allocator is not ours and caps is raw, keep it for other_pool.
436 * 3. get pool in query
437 * 3.1 if pool is not va, downstream doesn't support video meta and
438 * caps are raw, keep it as other_pool.
439 * 3.2 if there's no pool in query and and caps is raw, create other_pool
440 * as GstVideoPool with the non-va from query and query's params.
441 * 4. create our allocator and pool if they aren't in query
442 * 5. add or update pool and allocator in query
443 * 6. set our custom pool configuration
444 */
445 static gboolean
gst_va_base_dec_decide_allocation(GstVideoDecoder * decoder,GstQuery * query)446 gst_va_base_dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
447 {
448 GstAllocator *allocator = NULL, *other_allocator = NULL;
449 GstAllocationParams other_params, params;
450 GstBufferPool *pool = NULL, *other_pool = NULL;
451 GstCaps *caps = NULL;
452 GstVideoInfo info;
453 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
454 guint size = 0, min, max;
455 gboolean update_pool = FALSE, update_allocator = FALSE;
456 gboolean has_videometa, has_video_crop_meta;
457 gboolean ret = TRUE;
458
459 g_assert (base->min_buffers > 0);
460
461 gst_query_parse_allocation (query, &caps, NULL);
462
463 if (!(caps && gst_video_info_from_caps (&info, caps)))
464 goto wrong_caps;
465
466 has_videometa = gst_query_find_allocation_meta (query,
467 GST_VIDEO_META_API_TYPE, NULL);
468 has_video_crop_meta = has_videometa && gst_query_find_allocation_meta (query,
469 GST_VIDEO_CROP_META_API_TYPE, NULL);
470
471 /* 1. The output picture locates in the middle of the decoded buffer,
472 but the downstream element does not support VideoCropMeta, we
473 definitely need a copy.
474 2. Some codec such as H265, it does not clean the DPB when new SPS
475 comes. The new SPS may set the crop window to top-left corner and
476 so no video crop is needed here. But we may still have cached frames
477 in DPB which need a copy. */
478 if ((_need_video_crop (base) && !has_video_crop_meta) ||
479 base->apply_video_crop) {
480 return _decide_allocation_for_video_crop (decoder, query, caps, &info);
481 }
482
483 if (gst_query_get_n_allocation_params (query) > 0) {
484 gst_query_parse_nth_allocation_param (query, 0, &allocator, &other_params);
485 if (allocator && !(GST_IS_VA_DMABUF_ALLOCATOR (allocator)
486 || GST_IS_VA_ALLOCATOR (allocator))) {
487 /* save the allocator for the other pool */
488 other_allocator = allocator;
489 allocator = NULL;
490 }
491 update_allocator = TRUE;
492 } else {
493 gst_allocation_params_init (&other_params);
494 }
495
496 gst_allocation_params_init (¶ms);
497
498 if (gst_query_get_n_allocation_pools (query) > 0) {
499 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
500 if (pool) {
501 if (!GST_IS_VA_POOL (pool)) {
502 GST_DEBUG_OBJECT (base,
503 "may need other pool for copy frames %" GST_PTR_FORMAT, pool);
504 other_pool = pool;
505 pool = NULL;
506 }
507 }
508
509 min += base->min_buffers;
510 size = MAX (size, GST_VIDEO_INFO_SIZE (&info));
511
512 update_pool = TRUE;
513 } else {
514 size = GST_VIDEO_INFO_SIZE (&info);
515 min = base->min_buffers;
516 max = 0;
517 }
518
519 if (!allocator) {
520 if (!(allocator = _create_allocator (base, caps))) {
521 ret = FALSE;
522 goto cleanup;
523 }
524 }
525
526 if (!pool)
527 pool = gst_va_pool_new ();
528
529 {
530 GstStructure *config = gst_buffer_pool_get_config (pool);
531
532 gst_buffer_pool_config_set_params (config, caps, size, min, max);
533 gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
534 gst_buffer_pool_config_add_option (config,
535 GST_BUFFER_POOL_OPTION_VIDEO_META);
536
537 if (base->need_valign)
538 gst_buffer_pool_config_set_va_alignment (config, &base->valign);
539
540 gst_buffer_pool_config_set_va_allocation_params (config,
541 VA_SURFACE_ATTRIB_USAGE_HINT_DECODER);
542
543 if (!gst_buffer_pool_set_config (pool, config)) {
544 ret = FALSE;
545 goto cleanup;
546 }
547 }
548
549 if (update_allocator)
550 gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);
551 else
552 gst_query_add_allocation_param (query, allocator, ¶ms);
553
554 if (update_pool)
555 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
556 else
557 gst_query_add_allocation_pool (query, pool, size, min, max);
558
559 base->copy_frames = (!has_videometa && gst_va_pool_requires_video_meta (pool)
560 && gst_caps_is_raw (caps));
561 if (base->copy_frames) {
562 if (other_pool) {
563 gst_object_replace ((GstObject **) & base->other_pool,
564 (GstObject *) other_pool);
565 } else {
566 _create_other_pool (base, other_allocator, &other_params, caps, size);
567 }
568 GST_DEBUG_OBJECT (base, "Use the other pool for copy %" GST_PTR_FORMAT,
569 base->other_pool);
570 } else {
571 gst_clear_object (&base->other_pool);
572 }
573
574 cleanup:
575 gst_clear_object (&allocator);
576 gst_clear_object (&other_allocator);
577 gst_clear_object (&pool);
578 gst_clear_object (&other_pool);
579
580 /* There's no need to chain decoder's method since all what is
581 * needed is done. */
582 return ret;
583
584 wrong_caps:
585 {
586 GST_WARNING_OBJECT (base, "No valid caps");
587 return FALSE;
588 }
589 }
590
591 static void
gst_va_base_dec_set_context(GstElement * element,GstContext * context)592 gst_va_base_dec_set_context (GstElement * element, GstContext * context)
593 {
594 GstVaDisplay *old_display, *new_display;
595 GstVaBaseDec *base = GST_VA_BASE_DEC (element);
596 GstVaBaseDecClass *klass = GST_VA_BASE_DEC_GET_CLASS (base);
597 gboolean ret;
598
599 old_display = base->display ? gst_object_ref (base->display) : NULL;
600 ret = gst_va_handle_set_context (element, context, klass->render_device_path,
601 &base->display);
602 new_display = base->display ? gst_object_ref (base->display) : NULL;
603
604 if (!ret
605 || (old_display && new_display && old_display != new_display
606 && base->decoder)) {
607 GST_ELEMENT_WARNING (base, RESOURCE, BUSY,
608 ("Can't replace VA display while operating"), (NULL));
609 }
610
611 gst_clear_object (&old_display);
612 gst_clear_object (&new_display);
613
614 GST_ELEMENT_CLASS (GST_VA_BASE_DEC_GET_PARENT_CLASS
615 (element))->set_context (element, context);
616 }
617
618 void
gst_va_base_dec_init(GstVaBaseDec * base,GstDebugCategory * cat)619 gst_va_base_dec_init (GstVaBaseDec * base, GstDebugCategory * cat)
620 {
621 base->debug_category = cat;
622 }
623
624 void
gst_va_base_dec_class_init(GstVaBaseDecClass * klass,GstVaCodecs codec,const gchar * render_device_path,GstCaps * sink_caps,GstCaps * src_caps,GstCaps * doc_src_caps,GstCaps * doc_sink_caps)625 gst_va_base_dec_class_init (GstVaBaseDecClass * klass, GstVaCodecs codec,
626 const gchar * render_device_path, GstCaps * sink_caps, GstCaps * src_caps,
627 GstCaps * doc_src_caps, GstCaps * doc_sink_caps)
628 {
629 GstPadTemplate *sink_pad_templ, *src_pad_templ;
630 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
631 GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
632
633 klass->parent_decoder_class = g_type_class_peek_parent (klass);
634
635 klass->codec = codec;
636 klass->render_device_path = g_strdup (render_device_path);
637
638 sink_pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
639 sink_caps);
640 gst_element_class_add_pad_template (element_class, sink_pad_templ);
641
642 if (doc_sink_caps) {
643 gst_pad_template_set_documentation_caps (sink_pad_templ, doc_sink_caps);
644 gst_caps_unref (doc_sink_caps);
645 }
646
647 src_pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
648 src_caps);
649 gst_element_class_add_pad_template (element_class, src_pad_templ);
650
651 if (doc_src_caps) {
652 gst_pad_template_set_documentation_caps (src_pad_templ, doc_src_caps);
653 gst_caps_unref (doc_src_caps);
654 }
655
656 element_class->set_context = GST_DEBUG_FUNCPTR (gst_va_base_dec_set_context);
657
658 decoder_class->open = GST_DEBUG_FUNCPTR (gst_va_base_dec_open);
659 decoder_class->close = GST_DEBUG_FUNCPTR (gst_va_base_dec_close);
660 decoder_class->stop = GST_DEBUG_FUNCPTR (gst_va_base_dec_stop);
661 decoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_va_base_dec_getcaps);
662 decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_va_base_dec_src_query);
663 decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_va_base_dec_sink_query);
664 decoder_class->decide_allocation =
665 GST_DEBUG_FUNCPTR (gst_va_base_dec_decide_allocation);
666 }
667
668 static GstVideoFormat
_default_video_format_from_chroma(guint chroma_type)669 _default_video_format_from_chroma (guint chroma_type)
670 {
671 switch (chroma_type) {
672 /* 4:2:0 */
673 case VA_RT_FORMAT_YUV420:
674 return GST_VIDEO_FORMAT_NV12;
675 case VA_RT_FORMAT_YUV420_10:
676 return GST_VIDEO_FORMAT_P010_10LE;
677 case VA_RT_FORMAT_YUV420_12:
678 return GST_VIDEO_FORMAT_P012_LE;
679 /* 4:2:2 */
680 case VA_RT_FORMAT_YUV422:
681 return GST_VIDEO_FORMAT_UYVY;
682 case VA_RT_FORMAT_YUV422_10:
683 return GST_VIDEO_FORMAT_Y210;
684 case VA_RT_FORMAT_YUV422_12:
685 return GST_VIDEO_FORMAT_Y212_LE;
686 /* 4:4:4 */
687 case VA_RT_FORMAT_YUV444:
688 return GST_VIDEO_FORMAT_VUYA;
689 case VA_RT_FORMAT_YUV444_10:
690 return GST_VIDEO_FORMAT_Y410;
691 case VA_RT_FORMAT_YUV444_12:
692 return GST_VIDEO_FORMAT_Y412_LE;
693 default:
694 return GST_VIDEO_FORMAT_UNKNOWN;
695 }
696 }
697
698 /* Check whether the downstream supports VideoMeta; if not, we need to
699 * fallback to the system memory. */
700 static gboolean
_downstream_has_video_meta(GstVaBaseDec * base,GstCaps * caps)701 _downstream_has_video_meta (GstVaBaseDec * base, GstCaps * caps)
702 {
703 GstQuery *query;
704 gboolean ret = FALSE;
705
706 query = gst_query_new_allocation (caps, FALSE);
707 if (gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (base), query))
708 ret = gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
709 gst_query_unref (query);
710
711 return ret;
712 }
713
714 void
gst_va_base_dec_get_preferred_format_and_caps_features(GstVaBaseDec * base,GstVideoFormat * format,GstCapsFeatures ** capsfeatures)715 gst_va_base_dec_get_preferred_format_and_caps_features (GstVaBaseDec * base,
716 GstVideoFormat * format, GstCapsFeatures ** capsfeatures)
717 {
718 GstCaps *peer_caps, *preferred_caps = NULL;
719 GstCapsFeatures *features;
720 GstStructure *structure;
721 const GValue *v_format;
722 guint num_structures, i;
723 gboolean is_any;
724
725 g_return_if_fail (base);
726
727 /* verify if peer caps is any */
728 {
729 peer_caps =
730 gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (base), NULL);
731 is_any = gst_caps_is_any (peer_caps);
732 gst_clear_caps (&peer_caps);
733 }
734
735 peer_caps = gst_pad_get_allowed_caps (GST_VIDEO_DECODER_SRC_PAD (base));
736 GST_DEBUG_OBJECT (base, "Allowed caps %" GST_PTR_FORMAT, peer_caps);
737
738 /* prefer memory:VASurface over other caps features */
739 num_structures = gst_caps_get_size (peer_caps);
740 for (i = 0; i < num_structures; i++) {
741 features = gst_caps_get_features (peer_caps, i);
742 structure = gst_caps_get_structure (peer_caps, i);
743
744 if (gst_caps_features_is_any (features))
745 continue;
746
747 if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_VA)) {
748 preferred_caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
749 gst_caps_set_features_simple (preferred_caps,
750 gst_caps_features_copy (features));
751 break;
752 }
753 }
754
755 if (!preferred_caps)
756 preferred_caps = peer_caps;
757 else
758 gst_clear_caps (&peer_caps);
759
760 if (gst_caps_is_empty (preferred_caps)) {
761 if (capsfeatures)
762 *capsfeatures = NULL; /* system memory */
763 if (format)
764 *format = _default_video_format_from_chroma (base->rt_format);
765 goto bail;
766 }
767
768 if (capsfeatures) {
769 features = gst_caps_get_features (preferred_caps, 0);
770 if (features) {
771 *capsfeatures = gst_caps_features_copy (features);
772
773 if (is_any
774 && !gst_caps_features_is_equal (features,
775 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)
776 && !_downstream_has_video_meta (base, preferred_caps)) {
777 GST_INFO_OBJECT (base, "Downstream reports ANY caps but without"
778 " VideoMeta support; fallback to system memory.");
779 gst_caps_features_free (*capsfeatures);
780 *capsfeatures = NULL;
781 }
782 } else {
783 *capsfeatures = NULL;
784 }
785 }
786
787 if (!format)
788 goto bail;
789
790 structure = gst_caps_get_structure (preferred_caps, 0);
791 v_format = gst_structure_get_value (structure, "format");
792 if (!v_format)
793 *format = _default_video_format_from_chroma (base->rt_format);
794 else if (G_VALUE_HOLDS_STRING (v_format))
795 *format = gst_video_format_from_string (g_value_get_string (v_format));
796 else if (GST_VALUE_HOLDS_LIST (v_format)) {
797 guint num_values = gst_value_list_get_size (v_format);
798 for (i = 0; i < num_values; i++) {
799 GstVideoFormat fmt;
800 const GValue *v_fmt = gst_value_list_get_value (v_format, i);
801 if (!v_fmt)
802 continue;
803 fmt = gst_video_format_from_string (g_value_get_string (v_fmt));
804 if (gst_va_chroma_from_video_format (fmt) == base->rt_format) {
805 *format = fmt;
806 break;
807 }
808 }
809 if (i == num_values)
810 *format = _default_video_format_from_chroma (base->rt_format);
811 }
812
813 bail:
814 gst_clear_caps (&preferred_caps);
815 }
816
817 static gboolean
_copy_buffer_and_apply_video_crop(GstVaBaseDec * base,GstVideoFrame * src_frame,GstVideoFrame * dest_frame,GstVideoCropMeta * video_crop)818 _copy_buffer_and_apply_video_crop (GstVaBaseDec * base,
819 GstVideoFrame * src_frame, GstVideoFrame * dest_frame,
820 GstVideoCropMeta * video_crop)
821 {
822 GstVideoInfo dst_info = dest_frame->info;
823
824 dst_info.fps_n = src_frame->info.fps_n;
825 dst_info.fps_d = src_frame->info.fps_d;
826
827 if (base->convert) {
828 gboolean new_convert = FALSE;
829 gint x = 0, y = 0, width = 0, height = 0;
830 const GstStructure *config = gst_video_converter_get_config (base->convert);
831
832 if (!gst_structure_get_int (config, GST_VIDEO_CONVERTER_OPT_SRC_X, &x)
833 || !gst_structure_get_int (config, GST_VIDEO_CONVERTER_OPT_SRC_Y, &y)
834 || !gst_structure_get_int (config, GST_VIDEO_CONVERTER_OPT_SRC_WIDTH,
835 &width)
836 || !gst_structure_get_int (config, GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT,
837 &height))
838 new_convert = TRUE;
839
840 new_convert |= (video_crop->x != x);
841 new_convert |= (video_crop->y != y);
842 new_convert |= (video_crop->width != width);
843 new_convert |= (video_crop->height != height);
844
845 /* No need to check dest, it always has (0,0) -> (width, height) */
846
847 if (new_convert)
848 g_clear_pointer (&base->convert, gst_video_converter_free);
849 }
850
851 if (!base->convert) {
852 base->convert = gst_video_converter_new (&src_frame->info, &dst_info,
853 gst_structure_new ("options",
854 GST_VIDEO_CONVERTER_OPT_DITHER_METHOD,
855 GST_TYPE_VIDEO_DITHER_METHOD, GST_VIDEO_DITHER_NONE,
856 GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION,
857 G_TYPE_UINT, 0,
858 GST_VIDEO_CONVERTER_OPT_CHROMA_MODE,
859 GST_TYPE_VIDEO_CHROMA_MODE, GST_VIDEO_CHROMA_MODE_NONE,
860 GST_VIDEO_CONVERTER_OPT_MATRIX_MODE,
861 GST_TYPE_VIDEO_MATRIX_MODE, GST_VIDEO_MATRIX_MODE_NONE,
862 GST_VIDEO_CONVERTER_OPT_SRC_X, G_TYPE_INT, video_crop->x,
863 GST_VIDEO_CONVERTER_OPT_SRC_Y, G_TYPE_INT, video_crop->y,
864 GST_VIDEO_CONVERTER_OPT_SRC_WIDTH, G_TYPE_INT, video_crop->width,
865 GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT, G_TYPE_INT, video_crop->height,
866 GST_VIDEO_CONVERTER_OPT_DEST_X, G_TYPE_INT, 0,
867 GST_VIDEO_CONVERTER_OPT_DEST_Y, G_TYPE_INT, 0,
868 GST_VIDEO_CONVERTER_OPT_DEST_WIDTH, G_TYPE_INT, video_crop->width,
869 GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT, G_TYPE_INT, video_crop->height,
870 NULL));
871
872 if (!base->convert) {
873 GST_WARNING_OBJECT (base, "failed to create a video convert");
874 return FALSE;
875 }
876 }
877
878 gst_video_converter_frame (base->convert, src_frame, dest_frame);
879
880 return TRUE;
881 }
882
883 gboolean
gst_va_base_dec_copy_output_buffer(GstVaBaseDec * base,GstVideoCodecFrame * codec_frame)884 gst_va_base_dec_copy_output_buffer (GstVaBaseDec * base,
885 GstVideoCodecFrame * codec_frame)
886 {
887 GstVideoFrame src_frame;
888 GstVideoFrame dest_frame;
889 GstVideoInfo dest_vinfo;
890 GstVideoInfo *src_vinfo;
891 GstBuffer *buffer = NULL;
892 GstVideoCropMeta *video_crop;
893 GstFlowReturn ret;
894
895 g_return_val_if_fail (base && base->output_state, FALSE);
896
897 if (!base->other_pool)
898 return FALSE;
899
900 if (!gst_buffer_pool_set_active (base->other_pool, TRUE))
901 return FALSE;
902
903 src_vinfo = &base->output_state->info;
904 gst_video_info_set_format (&dest_vinfo, GST_VIDEO_INFO_FORMAT (src_vinfo),
905 base->width, base->height);
906
907 ret = gst_buffer_pool_acquire_buffer (base->other_pool, &buffer, NULL);
908 if (ret != GST_FLOW_OK)
909 goto fail;
910 if (!gst_video_frame_map (&src_frame, src_vinfo, codec_frame->output_buffer,
911 GST_MAP_READ))
912 goto fail;
913 if (!gst_video_frame_map (&dest_frame, &dest_vinfo, buffer, GST_MAP_WRITE)) {
914 gst_video_frame_unmap (&src_frame);
915 goto fail;
916 }
917
918 video_crop = gst_buffer_get_video_crop_meta (codec_frame->output_buffer);
919 if (video_crop) {
920 if (!_copy_buffer_and_apply_video_crop (base,
921 &src_frame, &dest_frame, video_crop)) {
922 gst_video_frame_unmap (&src_frame);
923 gst_video_frame_unmap (&dest_frame);
924 GST_ERROR_OBJECT (base, "fail to apply the video crop.");
925 goto fail;
926 }
927 } else {
928 /* gst_video_frame_copy can crop this, but does not know, so let
929 * make it think it's all right */
930 GST_VIDEO_INFO_WIDTH (&src_frame.info) = base->width;
931 GST_VIDEO_INFO_HEIGHT (&src_frame.info) = base->height;
932
933 if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
934 gst_video_frame_unmap (&src_frame);
935 gst_video_frame_unmap (&dest_frame);
936 goto fail;
937 }
938 }
939
940 gst_video_frame_unmap (&src_frame);
941 gst_video_frame_unmap (&dest_frame);
942 gst_buffer_replace (&codec_frame->output_buffer, buffer);
943 gst_buffer_unref (buffer);
944
945 return TRUE;
946
947 fail:
948 if (buffer)
949 gst_buffer_unref (buffer);
950
951 GST_ERROR_OBJECT (base, "Failed copy output buffer.");
952 return FALSE;
953 }
954