2 * Copyright (C) 2020 Igalia, S.L.
3 * Author: Víctor Jáquez <vjaquez@igalia.com>
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.
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.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstvadecoder.h"
27 #include <gst/va/gstva.h>
28 #include <gst/va/gstvavideoformat.h>
30 #include "gstvacaps.h"
31 #include "gstvadisplay_priv.h"
32 #include "gstvaprofile.h"
38 GArray *available_profiles;
40 GstCaps *sinkpad_caps;
41 GstVaDisplay *display;
50 GST_DEBUG_CATEGORY_STATIC (gst_va_decoder_debug);
51 #define GST_CAT_DEFAULT gst_va_decoder_debug
53 #define gst_va_decoder_parent_class parent_class
54 G_DEFINE_TYPE_WITH_CODE (GstVaDecoder, gst_va_decoder, GST_TYPE_OBJECT,
55 GST_DEBUG_CATEGORY_INIT (gst_va_decoder_debug, "vadecoder", 0,
68 static GParamSpec *g_properties[N_PROPERTIES];
70 static gboolean _destroy_buffers (GstVaDecodePicture * pic);
73 gst_va_decoder_set_property (GObject * object, guint prop_id,
74 const GValue * value, GParamSpec * pspec)
76 GstVaDecoder *self = GST_VA_DECODER (object);
80 g_assert (!self->display);
81 self->display = g_value_dup_object (value);
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
91 gst_va_decoder_get_property (GObject * object, guint prop_id, GValue * value,
94 GstVaDecoder *self = GST_VA_DECODER (object);
98 g_value_set_object (value, self->display);
101 g_value_set_int (value, self->profile);
104 g_value_set_uint (value, self->rt_format);
107 g_value_set_int (value, self->coded_width);
110 g_value_set_int (value, self->coded_height);
113 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
119 gst_va_decoder_dispose (GObject * object)
121 GstVaDecoder *self = GST_VA_DECODER (object);
123 if (!gst_va_decoder_close (self))
124 GST_WARNING_OBJECT (self, "VaDecoder is not successfully closed");
126 g_clear_pointer (&self->available_profiles, g_array_unref);
127 gst_clear_object (&self->display);
129 G_OBJECT_CLASS (parent_class)->dispose (object);
133 gst_va_decoder_class_init (GstVaDecoderClass * klass)
135 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
137 gobject_class->set_property = gst_va_decoder_set_property;
138 gobject_class->get_property = gst_va_decoder_get_property;
139 gobject_class->dispose = gst_va_decoder_dispose;
141 g_properties[PROP_DISPLAY] =
142 g_param_spec_object ("display", "GstVaDisplay", "GstVaDisplay object",
144 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
146 g_properties[PROP_PROFILE] =
147 g_param_spec_int ("va-profile", "VAProfile", "VA Profile",
148 VAProfileNone, 50, VAProfileNone,
149 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
151 g_properties[PROP_CHROMA] =
152 g_param_spec_uint ("va-rt-format", "VARTFormat", "VA RT Format",
153 VA_RT_FORMAT_YUV420, VA_RT_FORMAT_PROTECTED, VA_RT_FORMAT_YUV420,
154 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
156 g_properties[PROP_WIDTH] =
157 g_param_spec_int ("coded-width", "coded-picture-width",
158 "coded picture width", 0, G_MAXINT, 0,
159 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
161 g_properties[PROP_HEIGHT] =
162 g_param_spec_int ("coded-height", "coded-picture-height",
163 "coded picture height", 0, G_MAXINT, 0,
164 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
166 g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
170 gst_va_decoder_init (GstVaDecoder * self)
172 self->profile = VAProfileNone;
173 self->config = VA_INVALID_ID;
174 self->context = VA_INVALID_ID;
176 self->coded_width = 0;
177 self->coded_height = 0;
181 gst_va_decoder_initialize (GstVaDecoder * self, guint32 codec)
183 if (self->available_profiles)
186 self->available_profiles = gst_va_display_get_profiles (self->display, codec,
189 return (self->available_profiles != NULL);
193 gst_va_decoder_new (GstVaDisplay * display, guint32 codec)
197 g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
199 self = g_object_new (GST_TYPE_VA_DECODER, "display", display, NULL);
200 if (!gst_va_decoder_initialize (self, codec))
201 gst_clear_object (&self);
207 gst_va_decoder_is_open (GstVaDecoder * self)
211 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
213 GST_OBJECT_LOCK (self);
214 ret = (self->config != VA_INVALID_ID && self->profile != VAProfileNone);
215 GST_OBJECT_UNLOCK (self);
220 gst_va_decoder_open (GstVaDecoder * self, VAProfile profile, guint rt_format)
222 VAConfigAttrib attrib = {
223 .type = VAConfigAttribRTFormat,
230 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
232 if (gst_va_decoder_is_open (self))
235 if (!gst_va_decoder_has_profile (self, profile)) {
236 GST_ERROR_OBJECT (self, "Unsupported profile: %s",
237 gst_va_profile_name (profile));
241 dpy = gst_va_display_get_va_dpy (self->display);
242 status = vaCreateConfig (dpy, profile, VAEntrypointVLD, &attrib, 1, &config);
243 if (status != VA_STATUS_SUCCESS) {
244 GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
248 GST_OBJECT_LOCK (self);
249 self->config = config;
250 self->profile = profile;
251 self->rt_format = rt_format;
252 GST_OBJECT_UNLOCK (self);
254 /* now we should return now only this profile's caps */
255 gst_caps_replace (&self->srcpad_caps, NULL);
261 gst_va_decoder_close (GstVaDecoder * self)
266 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
268 if (!gst_va_decoder_is_open (self))
271 dpy = gst_va_display_get_va_dpy (self->display);
273 if (self->context != VA_INVALID_ID) {
274 status = vaDestroyContext (dpy, self->context);
275 if (status != VA_STATUS_SUCCESS)
276 GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
279 status = vaDestroyConfig (dpy, self->config);
280 if (status != VA_STATUS_SUCCESS) {
281 GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
285 GST_OBJECT_LOCK (self);
286 gst_va_decoder_init (self);
287 GST_OBJECT_UNLOCK (self);
289 gst_caps_replace (&self->srcpad_caps, NULL);
290 gst_caps_replace (&self->sinkpad_caps, NULL);
296 gst_va_decoder_set_frame_size_with_surfaces (GstVaDecoder * self,
297 gint coded_width, gint coded_height, GArray * surfaces)
302 VASurfaceID *render_targets = NULL;
303 gint num_render_targets = 0;
305 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
307 GST_OBJECT_LOCK (self);
308 if (self->context != VA_INVALID_ID) {
309 GST_OBJECT_UNLOCK (self);
310 GST_INFO_OBJECT (self, "decoder already has a context");
313 GST_OBJECT_UNLOCK (self);
315 if (!gst_va_decoder_is_open (self)) {
316 GST_ERROR_OBJECT (self, "decoder has not been opened yet");
321 num_render_targets = surfaces->len;
322 render_targets = (VASurfaceID *) surfaces->data;
325 dpy = gst_va_display_get_va_dpy (self->display);
327 status = vaCreateContext (dpy, self->config, coded_width, coded_height,
328 VA_PROGRESSIVE, render_targets, num_render_targets, &context);
330 if (status != VA_STATUS_SUCCESS) {
331 GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
335 GST_OBJECT_LOCK (self);
336 self->context = context;
337 self->coded_width = coded_width;
338 self->coded_height = coded_height;
339 GST_OBJECT_UNLOCK (self);
345 gst_va_decoder_set_frame_size (GstVaDecoder * self, gint coded_width,
348 return gst_va_decoder_set_frame_size_with_surfaces (self, coded_width,
352 /* This function is only used by codecs where frame size can change
353 * without a context reset, as for example VP9 */
355 gst_va_decoder_update_frame_size (GstVaDecoder * self, gint coded_width,
358 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
360 if (!gst_va_decoder_is_open (self)) {
361 GST_ERROR_OBJECT (self, "decoder has not been opened yet");
365 GST_OBJECT_LOCK (self);
366 if (self->context == VA_INVALID_ID) {
367 GST_OBJECT_UNLOCK (self);
368 GST_INFO_OBJECT (self, "decoder does not have a context");
371 GST_OBJECT_UNLOCK (self);
374 GST_OBJECT_LOCK (self);
375 self->coded_width = coded_width;
376 self->coded_height = coded_height;
377 GST_OBJECT_UNLOCK (self);
383 _get_codec_caps (GstVaDecoder * self)
385 GstCaps *sinkpad_caps = NULL, *srcpad_caps = NULL;
387 if (!gst_va_decoder_is_open (self)
388 && GST_IS_VA_DISPLAY_WRAPPED (self->display)) {
389 if (gst_va_caps_from_profiles (self->display, self->available_profiles,
390 VAEntrypointVLD, &sinkpad_caps, &srcpad_caps)) {
391 gst_caps_replace (&self->sinkpad_caps, sinkpad_caps);
392 gst_caps_replace (&self->srcpad_caps, srcpad_caps);
393 gst_caps_unref (srcpad_caps);
394 gst_caps_unref (sinkpad_caps);
404 gst_va_decoder_get_srcpad_caps (GstVaDecoder * self)
406 GstCaps *srcpad_caps = NULL;
408 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
410 if (g_atomic_pointer_get (&self->srcpad_caps))
411 return gst_caps_ref (self->srcpad_caps);
413 if (_get_codec_caps (self))
414 return gst_caps_ref (self->srcpad_caps);
416 if (gst_va_decoder_is_open (self)) {
417 srcpad_caps = gst_va_create_raw_caps_from_config (self->display,
420 GST_WARNING_OBJECT (self, "Invalid configuration caps");
423 gst_caps_replace (&self->srcpad_caps, srcpad_caps);
424 gst_caps_unref (srcpad_caps);
426 return gst_caps_ref (self->srcpad_caps);
433 gst_va_decoder_get_sinkpad_caps (GstVaDecoder * self)
435 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
437 if (g_atomic_pointer_get (&self->sinkpad_caps))
438 return gst_caps_ref (self->sinkpad_caps);
440 if (_get_codec_caps (self))
441 return gst_caps_ref (self->sinkpad_caps);
447 gst_va_decoder_has_profile (GstVaDecoder * self, VAProfile profile)
451 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
453 if (profile == VAProfileNone)
456 for (i = 0; i < self->available_profiles->len; i++) {
457 if (g_array_index (self->available_profiles, VAProfile, i) == profile)
465 gst_va_decoder_get_mem_types (GstVaDecoder * self)
467 VASurfaceAttrib *attribs;
468 guint i, attrib_count;
471 g_return_val_if_fail (GST_IS_VA_DECODER (self), 0);
473 if (!gst_va_decoder_is_open (self))
476 attribs = gst_va_get_surface_attribs (self->display, self->config,
481 for (i = 0; i < attrib_count; i++) {
482 if (attribs[i].value.type != VAGenericValueTypeInteger)
484 switch (attribs[i].type) {
485 case VASurfaceAttribMemoryType:
486 ret = attribs[i].value.value.i;
499 gst_va_decoder_get_surface_formats (GstVaDecoder * self)
502 GstVideoFormat format;
503 VASurfaceAttrib *attribs;
504 guint i, attrib_count;
506 g_return_val_if_fail (GST_IS_VA_DECODER (self), NULL);
508 if (!gst_va_decoder_is_open (self))
511 attribs = gst_va_get_surface_attribs (self->display, self->config,
516 formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
518 for (i = 0; i < attrib_count; i++) {
519 if (attribs[i].value.type != VAGenericValueTypeInteger)
521 switch (attribs[i].type) {
522 case VASurfaceAttribPixelFormat:
523 format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
524 if (format != GST_VIDEO_FORMAT_UNKNOWN)
525 g_array_append_val (formats, format);
534 if (formats->len == 0) {
535 g_array_unref (formats);
543 gst_va_decoder_add_param_buffer (GstVaDecoder * self, GstVaDecodePicture * pic,
544 gint type, gpointer data, gsize size)
550 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
551 g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
552 g_return_val_if_fail (pic && data && size > 0, FALSE);
554 dpy = gst_va_display_get_va_dpy (self->display);
555 status = vaCreateBuffer (dpy, self->context, type, size, 1, data, &buffer);
556 if (status != VA_STATUS_SUCCESS) {
557 GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
561 g_array_append_val (pic->buffers, buffer);
566 gst_va_decoder_add_slice_buffer_with_n_params (GstVaDecoder * self,
567 GstVaDecodePicture * pic, gpointer params_data, gsize params_size,
568 guint params_num, gpointer slice_data, gsize slice_size)
570 VABufferID params_buffer, slice_buffer;
574 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
575 g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
576 g_return_val_if_fail (pic && slice_data && slice_size > 0
577 && params_data && params_size > 0, FALSE);
579 dpy = gst_va_display_get_va_dpy (self->display);
580 status = vaCreateBuffer (dpy, self->context, VASliceParameterBufferType,
581 params_size, params_num, params_data, ¶ms_buffer);
582 if (status != VA_STATUS_SUCCESS) {
583 GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
587 status = vaCreateBuffer (dpy, self->context, VASliceDataBufferType,
588 slice_size, 1, slice_data, &slice_buffer);
589 if (status != VA_STATUS_SUCCESS) {
590 GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
594 g_array_append_val (pic->slices, params_buffer);
595 g_array_append_val (pic->slices, slice_buffer);
601 gst_va_decoder_add_slice_buffer (GstVaDecoder * self, GstVaDecodePicture * pic,
602 gpointer params_data, gsize params_size, gpointer slice_data,
605 return gst_va_decoder_add_slice_buffer_with_n_params (self, pic, params_data,
606 params_size, 1, slice_data, slice_size);
610 gst_va_decoder_decode_with_aux_surface (GstVaDecoder * self,
611 GstVaDecodePicture * pic, gboolean use_aux)
615 VASurfaceID surface = VA_INVALID_ID;
616 gboolean ret = FALSE;
618 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
619 g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
620 g_return_val_if_fail (pic, FALSE);
623 surface = gst_va_decode_picture_get_aux_surface (pic);
625 surface = gst_va_decode_picture_get_surface (pic);
627 if (surface == VA_INVALID_ID) {
628 GST_ERROR_OBJECT (self, "Decode picture without VASurfaceID");
632 GST_TRACE_OBJECT (self, "Decode to surface %#x", surface);
634 dpy = gst_va_display_get_va_dpy (self->display);
636 status = vaBeginPicture (dpy, self->context, surface);
637 if (status != VA_STATUS_SUCCESS) {
638 GST_WARNING_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
642 if (pic->buffers->len > 0) {
643 status = vaRenderPicture (dpy, self->context,
644 (VABufferID *) pic->buffers->data, pic->buffers->len);
645 if (status != VA_STATUS_SUCCESS) {
646 GST_WARNING_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
651 if (pic->slices->len > 0) {
652 status = vaRenderPicture (dpy, self->context,
653 (VABufferID *) pic->slices->data, pic->slices->len);
654 if (status != VA_STATUS_SUCCESS) {
655 GST_WARNING_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
660 status = vaEndPicture (dpy, self->context);
661 if (status != VA_STATUS_SUCCESS)
662 GST_WARNING_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
667 _destroy_buffers (pic);
673 status = vaEndPicture (dpy, self->context);
679 gst_va_decoder_decode (GstVaDecoder * self, GstVaDecodePicture * pic)
681 return gst_va_decoder_decode_with_aux_surface (self, pic, FALSE);
685 gst_va_decoder_config_is_equal (GstVaDecoder * self, VAProfile new_profile,
686 guint new_rtformat, gint new_width, gint new_height)
690 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
692 /* @TODO: Check if current buffers are large enough, and reuse
694 GST_OBJECT_LOCK (self);
695 ret = (self->profile == new_profile && self->rt_format == new_rtformat
696 && self->coded_width == new_width && self->coded_height == new_height);
697 GST_OBJECT_UNLOCK (self);
703 gst_va_decoder_get_config (GstVaDecoder * self, VAProfile * profile,
704 guint * rt_format, gint * width, gint * height)
706 g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
708 if (!gst_va_decoder_is_open (self))
711 GST_OBJECT_LOCK (self);
713 *profile = self->profile;
715 *rt_format = self->rt_format;
717 *width = self->coded_width;
719 *height = self->coded_height;
720 GST_OBJECT_UNLOCK (self);
726 _destroy_buffers (GstVaDecodePicture * pic)
734 g_return_val_if_fail (GST_IS_VA_DISPLAY (pic->display), FALSE);
736 dpy = gst_va_display_get_va_dpy (pic->display);
739 for (i = 0; i < pic->buffers->len; i++) {
740 buffer = g_array_index (pic->buffers, VABufferID, i);
741 status = vaDestroyBuffer (dpy, buffer);
742 if (status != VA_STATUS_SUCCESS) {
744 GST_WARNING ("Failed to destroy parameter buffer: %s",
745 vaErrorStr (status));
749 pic->buffers = g_array_set_size (pic->buffers, 0);
753 for (i = 0; i < pic->slices->len; i++) {
754 buffer = g_array_index (pic->slices, VABufferID, i);
755 status = vaDestroyBuffer (dpy, buffer);
756 if (status != VA_STATUS_SUCCESS) {
758 GST_WARNING ("Failed to destroy slice buffer: %s", vaErrorStr (status));
762 pic->slices = g_array_set_size (pic->slices, 0);
769 gst_va_decode_picture_new (GstVaDecoder * self, GstBuffer * buffer)
771 GstVaDecodePicture *pic;
773 g_return_val_if_fail (buffer && GST_IS_BUFFER (buffer), NULL);
774 g_return_val_if_fail (self && GST_IS_VA_DECODER (self), NULL);
776 pic = g_slice_new (GstVaDecodePicture);
777 pic->gstbuffer = gst_buffer_ref (buffer);
778 pic->buffers = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 16);
779 pic->slices = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 64);
780 pic->display = gst_object_ref (self->display);
786 gst_va_decode_picture_get_surface (GstVaDecodePicture * pic)
788 g_return_val_if_fail (pic, VA_INVALID_ID);
789 g_return_val_if_fail (pic->gstbuffer, VA_INVALID_ID);
791 return gst_va_buffer_get_surface (pic->gstbuffer);
795 gst_va_decode_picture_get_aux_surface (GstVaDecodePicture * pic)
797 g_return_val_if_fail (pic, VA_INVALID_ID);
798 g_return_val_if_fail (pic->gstbuffer, VA_INVALID_ID);
800 return gst_va_buffer_get_aux_surface (pic->gstbuffer);
804 gst_va_decode_picture_free (GstVaDecodePicture * pic)
806 g_return_if_fail (pic);
808 _destroy_buffers (pic);
810 gst_buffer_unref (pic->gstbuffer);
811 g_clear_pointer (&pic->buffers, g_array_unref);
812 g_clear_pointer (&pic->slices, g_array_unref);
813 gst_clear_object (&pic->display);
815 g_slice_free (GstVaDecodePicture, pic);
819 gst_va_decode_picture_dup (GstVaDecodePicture * pic)
821 GstVaDecodePicture *dup;
823 g_return_val_if_fail (pic, NULL);
825 dup = g_slice_new0 (GstVaDecodePicture);
827 dup->display = gst_object_ref (pic->display);
828 /* dups only need gstbuffer */
829 dup->gstbuffer = gst_buffer_ref (pic->gstbuffer);