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