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