831 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			831 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GStreamer
 | |
|  * Copyright (C) 2020 Igalia, S.L.
 | |
|  *     Author: Víctor Jáquez <vjaquez@igalia.com>
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Library General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Library General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Library General Public
 | |
|  * License along with this library; if not, write to the
 | |
|  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | |
|  * Boston, MA 02110-1301, USA.
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_CONFIG_H
 | |
| #include "config.h"
 | |
| #endif
 | |
| 
 | |
| #include "gstvadecoder.h"
 | |
| 
 | |
| #include <gst/va/gstva.h>
 | |
| #include <gst/va/gstvavideoformat.h>
 | |
| 
 | |
| #include "gstvacaps.h"
 | |
| #include "gstvadisplay_priv.h"
 | |
| #include "gstvaprofile.h"
 | |
| 
 | |
| struct _GstVaDecoder
 | |
| {
 | |
|   GstObject parent;
 | |
| 
 | |
|   GArray *available_profiles;
 | |
|   GstCaps *srcpad_caps;
 | |
|   GstCaps *sinkpad_caps;
 | |
|   GstVaDisplay *display;
 | |
|   VAConfigID config;
 | |
|   VAContextID context;
 | |
|   VAProfile profile;
 | |
|   guint rt_format;
 | |
|   gint coded_width;
 | |
|   gint coded_height;
 | |
| };
 | |
| 
 | |
| GST_DEBUG_CATEGORY_STATIC (gst_va_decoder_debug);
 | |
| #define GST_CAT_DEFAULT gst_va_decoder_debug
 | |
| 
 | |
| #define gst_va_decoder_parent_class parent_class
 | |
| G_DEFINE_TYPE_WITH_CODE (GstVaDecoder, gst_va_decoder, GST_TYPE_OBJECT,
 | |
|     GST_DEBUG_CATEGORY_INIT (gst_va_decoder_debug, "vadecoder", 0,
 | |
|         "VA Decoder"));
 | |
| 
 | |
| enum
 | |
| {
 | |
|   PROP_DISPLAY = 1,
 | |
|   PROP_PROFILE,
 | |
|   PROP_WIDTH,
 | |
|   PROP_HEIGHT,
 | |
|   PROP_CHROMA,
 | |
|   N_PROPERTIES
 | |
| };
 | |
| 
 | |
| static GParamSpec *g_properties[N_PROPERTIES];
 | |
| 
 | |
| static gboolean _destroy_buffers (GstVaDecodePicture * pic);
 | |
| 
 | |
| static void
 | |
| gst_va_decoder_set_property (GObject * object, guint prop_id,
 | |
|     const GValue * value, GParamSpec * pspec)
 | |
| {
 | |
|   GstVaDecoder *self = GST_VA_DECODER (object);
 | |
| 
 | |
|   switch (prop_id) {
 | |
|     case PROP_DISPLAY:{
 | |
|       g_assert (!self->display);
 | |
|       self->display = g_value_dup_object (value);
 | |
|       break;
 | |
|     }
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_va_decoder_get_property (GObject * object, guint prop_id, GValue * value,
 | |
|     GParamSpec * pspec)
 | |
| {
 | |
|   GstVaDecoder *self = GST_VA_DECODER (object);
 | |
| 
 | |
|   switch (prop_id) {
 | |
|     case PROP_DISPLAY:
 | |
|       g_value_set_object (value, self->display);
 | |
|       break;
 | |
|     case PROP_PROFILE:
 | |
|       g_value_set_int (value, self->profile);
 | |
|       break;
 | |
|     case PROP_CHROMA:
 | |
|       g_value_set_uint (value, self->rt_format);
 | |
|       break;
 | |
|     case PROP_WIDTH:
 | |
|       g_value_set_int (value, self->coded_width);
 | |
|       break;
 | |
|     case PROP_HEIGHT:
 | |
|       g_value_set_int (value, self->coded_height);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_va_decoder_dispose (GObject * object)
 | |
| {
 | |
|   GstVaDecoder *self = GST_VA_DECODER (object);
 | |
| 
 | |
|   if (!gst_va_decoder_close (self))
 | |
|     GST_WARNING_OBJECT (self, "VaDecoder is not successfully closed");
 | |
| 
 | |
|   g_clear_pointer (&self->available_profiles, g_array_unref);
 | |
|   gst_clear_object (&self->display);
 | |
| 
 | |
|   G_OBJECT_CLASS (parent_class)->dispose (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_va_decoder_class_init (GstVaDecoderClass * klass)
 | |
| {
 | |
|   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | |
| 
 | |
|   gobject_class->set_property = gst_va_decoder_set_property;
 | |
|   gobject_class->get_property = gst_va_decoder_get_property;
 | |
|   gobject_class->dispose = gst_va_decoder_dispose;
 | |
| 
 | |
|   g_properties[PROP_DISPLAY] =
 | |
|       g_param_spec_object ("display", "GstVaDisplay", "GstVaDisplay object",
 | |
|       GST_TYPE_VA_DISPLAY,
 | |
|       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_properties[PROP_PROFILE] =
 | |
|       g_param_spec_int ("va-profile", "VAProfile", "VA Profile",
 | |
|       VAProfileNone, 50, VAProfileNone,
 | |
|       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_properties[PROP_CHROMA] =
 | |
|       g_param_spec_uint ("va-rt-format", "VARTFormat", "VA RT Format",
 | |
|       VA_RT_FORMAT_YUV420, VA_RT_FORMAT_PROTECTED, VA_RT_FORMAT_YUV420,
 | |
|       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_properties[PROP_WIDTH] =
 | |
|       g_param_spec_int ("coded-width", "coded-picture-width",
 | |
|       "coded picture width", 0, G_MAXINT, 0,
 | |
|       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_properties[PROP_HEIGHT] =
 | |
|       g_param_spec_int ("coded-height", "coded-picture-height",
 | |
|       "coded picture height", 0, G_MAXINT, 0,
 | |
|       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_va_decoder_init (GstVaDecoder * self)
 | |
| {
 | |
|   self->profile = VAProfileNone;
 | |
|   self->config = VA_INVALID_ID;
 | |
|   self->context = VA_INVALID_ID;
 | |
|   self->rt_format = 0;
 | |
|   self->coded_width = 0;
 | |
|   self->coded_height = 0;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| gst_va_decoder_initialize (GstVaDecoder * self, guint32 codec)
 | |
| {
 | |
|   if (self->available_profiles)
 | |
|     return FALSE;
 | |
| 
 | |
|   self->available_profiles = gst_va_display_get_profiles (self->display, codec,
 | |
|       VAEntrypointVLD);
 | |
| 
 | |
|   return (self->available_profiles != NULL);
 | |
| }
 | |
| 
 | |
| GstVaDecoder *
 | |
| gst_va_decoder_new (GstVaDisplay * display, guint32 codec)
 | |
| {
 | |
|   GstVaDecoder *self;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
 | |
| 
 | |
|   self = g_object_new (GST_TYPE_VA_DECODER, "display", display, NULL);
 | |
|   if (!gst_va_decoder_initialize (self, codec))
 | |
|     gst_clear_object (&self);
 | |
| 
 | |
|   return self;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_is_open (GstVaDecoder * self)
 | |
| {
 | |
|   gboolean ret;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   ret = (self->config != VA_INVALID_ID && self->profile != VAProfileNone);
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_open (GstVaDecoder * self, VAProfile profile, guint rt_format)
 | |
| {
 | |
|   VAConfigAttrib attrib = {
 | |
|     .type = VAConfigAttribRTFormat,
 | |
|     .value = rt_format,
 | |
|   };
 | |
|   VAConfigID config;
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (gst_va_decoder_is_open (self))
 | |
|     return TRUE;
 | |
| 
 | |
|   if (!gst_va_decoder_has_profile (self, profile)) {
 | |
|     GST_ERROR_OBJECT (self, "Unsupported profile: %s",
 | |
|         gst_va_profile_name (profile));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   dpy = gst_va_display_get_va_dpy (self->display);
 | |
|   status = vaCreateConfig (dpy, profile, VAEntrypointVLD, &attrib, 1, &config);
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   self->config = config;
 | |
|   self->profile = profile;
 | |
|   self->rt_format = rt_format;
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   /* now we should return now only this profile's caps */
 | |
|   gst_caps_replace (&self->srcpad_caps, NULL);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_close (GstVaDecoder * self)
 | |
| {
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self))
 | |
|     return TRUE;
 | |
| 
 | |
|   dpy = gst_va_display_get_va_dpy (self->display);
 | |
| 
 | |
|   if (self->context != VA_INVALID_ID) {
 | |
|     status = vaDestroyContext (dpy, self->context);
 | |
|     if (status != VA_STATUS_SUCCESS)
 | |
|       GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
 | |
|   }
 | |
| 
 | |
|   status = vaDestroyConfig (dpy, self->config);
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   gst_va_decoder_init (self);
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   gst_caps_replace (&self->srcpad_caps, NULL);
 | |
|   gst_caps_replace (&self->sinkpad_caps, NULL);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_set_frame_size_with_surfaces (GstVaDecoder * self,
 | |
|     gint coded_width, gint coded_height, GArray * surfaces)
 | |
| {
 | |
|   VAContextID context;
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
|   VASurfaceID *render_targets = NULL;
 | |
|   gint num_render_targets = 0;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   if (self->context != VA_INVALID_ID) {
 | |
|     GST_OBJECT_UNLOCK (self);
 | |
|     GST_INFO_OBJECT (self, "decoder already has a context");
 | |
|     return TRUE;
 | |
|   }
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self)) {
 | |
|     GST_ERROR_OBJECT (self, "decoder has not been opened yet");
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   if (surfaces) {
 | |
|     num_render_targets = surfaces->len;
 | |
|     render_targets = (VASurfaceID *) surfaces->data;
 | |
|   }
 | |
| 
 | |
|   dpy = gst_va_display_get_va_dpy (self->display);
 | |
| 
 | |
|   status = vaCreateContext (dpy, self->config, coded_width, coded_height,
 | |
|       VA_PROGRESSIVE, render_targets, num_render_targets, &context);
 | |
| 
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_ERROR_OBJECT (self, "vaCreateContext: %s", vaErrorStr (status));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   self->context = context;
 | |
|   self->coded_width = coded_width;
 | |
|   self->coded_height = coded_height;
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_set_frame_size (GstVaDecoder * self, gint coded_width,
 | |
|     gint coded_height)
 | |
| {
 | |
|   return gst_va_decoder_set_frame_size_with_surfaces (self, coded_width,
 | |
|       coded_height, NULL);
 | |
| }
 | |
| 
 | |
| /* This function is only used by codecs where frame size can change
 | |
|  * without a context reset, as for example VP9 */
 | |
| gboolean
 | |
| gst_va_decoder_update_frame_size (GstVaDecoder * self, gint coded_width,
 | |
|     gint coded_height)
 | |
| {
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self)) {
 | |
|     GST_ERROR_OBJECT (self, "decoder has not been opened yet");
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   if (self->context == VA_INVALID_ID) {
 | |
|     GST_OBJECT_UNLOCK (self);
 | |
|     GST_INFO_OBJECT (self, "decoder does not have a context");
 | |
|     return FALSE;
 | |
|   }
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   self->coded_width = coded_width;
 | |
|   self->coded_height = coded_height;
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| _get_codec_caps (GstVaDecoder * self)
 | |
| {
 | |
|   GstCaps *sinkpad_caps = NULL, *srcpad_caps = NULL;
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self)
 | |
|       && GST_IS_VA_DISPLAY_WRAPPED (self->display)) {
 | |
|     if (gst_va_caps_from_profiles (self->display, self->available_profiles,
 | |
|             VAEntrypointVLD, &sinkpad_caps, &srcpad_caps)) {
 | |
|       gst_caps_replace (&self->sinkpad_caps, sinkpad_caps);
 | |
|       gst_caps_replace (&self->srcpad_caps, srcpad_caps);
 | |
|       gst_caps_unref (srcpad_caps);
 | |
|       gst_caps_unref (sinkpad_caps);
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| GstCaps *
 | |
| gst_va_decoder_get_srcpad_caps (GstVaDecoder * self)
 | |
| {
 | |
|   GstCaps *srcpad_caps = NULL;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (g_atomic_pointer_get (&self->srcpad_caps))
 | |
|     return gst_caps_ref (self->srcpad_caps);
 | |
| 
 | |
|   if (_get_codec_caps (self))
 | |
|     return gst_caps_ref (self->srcpad_caps);
 | |
| 
 | |
|   if (gst_va_decoder_is_open (self)) {
 | |
|     srcpad_caps = gst_va_create_raw_caps_from_config (self->display,
 | |
|         self->config);
 | |
|     if (!srcpad_caps) {
 | |
|       GST_WARNING_OBJECT (self, "Invalid configuration caps");
 | |
|       return NULL;
 | |
|     }
 | |
|     gst_caps_replace (&self->srcpad_caps, srcpad_caps);
 | |
|     gst_caps_unref (srcpad_caps);
 | |
| 
 | |
|     return gst_caps_ref (self->srcpad_caps);
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| GstCaps *
 | |
| gst_va_decoder_get_sinkpad_caps (GstVaDecoder * self)
 | |
| {
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (g_atomic_pointer_get (&self->sinkpad_caps))
 | |
|     return gst_caps_ref (self->sinkpad_caps);
 | |
| 
 | |
|   if (_get_codec_caps (self))
 | |
|     return gst_caps_ref (self->sinkpad_caps);
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_has_profile (GstVaDecoder * self, VAProfile profile)
 | |
| {
 | |
|   gint i;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (profile == VAProfileNone)
 | |
|     return FALSE;
 | |
| 
 | |
|   for (i = 0; i < self->available_profiles->len; i++) {
 | |
|     if (g_array_index (self->available_profiles, VAProfile, i) == profile)
 | |
|       return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| gint
 | |
| gst_va_decoder_get_mem_types (GstVaDecoder * self)
 | |
| {
 | |
|   VASurfaceAttrib *attribs;
 | |
|   guint i, attrib_count;
 | |
|   gint ret = 0;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), 0);
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self))
 | |
|     return 0;
 | |
| 
 | |
|   attribs = gst_va_get_surface_attribs (self->display, self->config,
 | |
|       &attrib_count);
 | |
|   if (!attribs)
 | |
|     return 0;
 | |
| 
 | |
|   for (i = 0; i < attrib_count; i++) {
 | |
|     if (attribs[i].value.type != VAGenericValueTypeInteger)
 | |
|       continue;
 | |
|     switch (attribs[i].type) {
 | |
|       case VASurfaceAttribMemoryType:
 | |
|         ret = attribs[i].value.value.i;
 | |
|         break;
 | |
|       default:
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   g_free (attribs);
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| GArray *
 | |
| gst_va_decoder_get_surface_formats (GstVaDecoder * self)
 | |
| {
 | |
|   GArray *formats;
 | |
|   GstVideoFormat format;
 | |
|   VASurfaceAttrib *attribs;
 | |
|   guint i, attrib_count;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), NULL);
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self))
 | |
|     return NULL;
 | |
| 
 | |
|   attribs = gst_va_get_surface_attribs (self->display, self->config,
 | |
|       &attrib_count);
 | |
|   if (!attribs)
 | |
|     return NULL;
 | |
| 
 | |
|   formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
 | |
| 
 | |
|   for (i = 0; i < attrib_count; i++) {
 | |
|     if (attribs[i].value.type != VAGenericValueTypeInteger)
 | |
|       continue;
 | |
|     switch (attribs[i].type) {
 | |
|       case VASurfaceAttribPixelFormat:
 | |
|         format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
 | |
|         if (format != GST_VIDEO_FORMAT_UNKNOWN)
 | |
|           g_array_append_val (formats, format);
 | |
|         break;
 | |
|       default:
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   g_free (attribs);
 | |
| 
 | |
|   if (formats->len == 0) {
 | |
|     g_array_unref (formats);
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   return formats;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_add_param_buffer (GstVaDecoder * self, GstVaDecodePicture * pic,
 | |
|     gint type, gpointer data, gsize size)
 | |
| {
 | |
|   VABufferID buffer;
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
|   g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
 | |
|   g_return_val_if_fail (pic && data && size > 0, FALSE);
 | |
| 
 | |
|   dpy = gst_va_display_get_va_dpy (self->display);
 | |
|   status = vaCreateBuffer (dpy, self->context, type, size, 1, data, &buffer);
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   g_array_append_val (pic->buffers, buffer);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_add_slice_buffer_with_n_params (GstVaDecoder * self,
 | |
|     GstVaDecodePicture * pic, gpointer params_data, gsize params_size,
 | |
|     guint params_num, gpointer slice_data, gsize slice_size)
 | |
| {
 | |
|   VABufferID params_buffer, slice_buffer;
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
|   g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
 | |
|   g_return_val_if_fail (pic && slice_data && slice_size > 0
 | |
|       && params_data && params_size > 0, FALSE);
 | |
| 
 | |
|   dpy = gst_va_display_get_va_dpy (self->display);
 | |
|   status = vaCreateBuffer (dpy, self->context, VASliceParameterBufferType,
 | |
|       params_size, params_num, params_data, ¶ms_buffer);
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   status = vaCreateBuffer (dpy, self->context, VASliceDataBufferType,
 | |
|       slice_size, 1, slice_data, &slice_buffer);
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   g_array_append_val (pic->slices, params_buffer);
 | |
|   g_array_append_val (pic->slices, slice_buffer);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_add_slice_buffer (GstVaDecoder * self, GstVaDecodePicture * pic,
 | |
|     gpointer params_data, gsize params_size, gpointer slice_data,
 | |
|     gsize slice_size)
 | |
| {
 | |
|   return gst_va_decoder_add_slice_buffer_with_n_params (self, pic, params_data,
 | |
|       params_size, 1, slice_data, slice_size);
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_decode_with_aux_surface (GstVaDecoder * self,
 | |
|     GstVaDecodePicture * pic, gboolean use_aux)
 | |
| {
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
|   VASurfaceID surface = VA_INVALID_ID;
 | |
|   gboolean ret = FALSE;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
|   g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
 | |
|   g_return_val_if_fail (pic, FALSE);
 | |
| 
 | |
|   if (use_aux) {
 | |
|     surface = gst_va_decode_picture_get_aux_surface (pic);
 | |
|   } else {
 | |
|     surface = gst_va_decode_picture_get_surface (pic);
 | |
|   }
 | |
|   if (surface == VA_INVALID_ID) {
 | |
|     GST_ERROR_OBJECT (self, "Decode picture without VASurfaceID");
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   GST_TRACE_OBJECT (self, "Decode to surface %#x", surface);
 | |
| 
 | |
|   dpy = gst_va_display_get_va_dpy (self->display);
 | |
| 
 | |
|   status = vaBeginPicture (dpy, self->context, surface);
 | |
|   if (status != VA_STATUS_SUCCESS) {
 | |
|     GST_WARNING_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
 | |
|     goto fail_end_pic;
 | |
|   }
 | |
| 
 | |
|   if (pic->buffers->len > 0) {
 | |
|     status = vaRenderPicture (dpy, self->context,
 | |
|         (VABufferID *) pic->buffers->data, pic->buffers->len);
 | |
|     if (status != VA_STATUS_SUCCESS) {
 | |
|       GST_WARNING_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
 | |
|       goto fail_end_pic;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (pic->slices->len > 0) {
 | |
|     status = vaRenderPicture (dpy, self->context,
 | |
|         (VABufferID *) pic->slices->data, pic->slices->len);
 | |
|     if (status != VA_STATUS_SUCCESS) {
 | |
|       GST_WARNING_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
 | |
|       goto fail_end_pic;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   status = vaEndPicture (dpy, self->context);
 | |
|   if (status != VA_STATUS_SUCCESS)
 | |
|     GST_WARNING_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
 | |
|   else
 | |
|     ret = TRUE;
 | |
| 
 | |
| bail:
 | |
|   _destroy_buffers (pic);
 | |
| 
 | |
|   return ret;
 | |
| 
 | |
| fail_end_pic:
 | |
|   {
 | |
|     status = vaEndPicture (dpy, self->context);
 | |
|     goto bail;
 | |
|   }
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_decode (GstVaDecoder * self, GstVaDecodePicture * pic)
 | |
| {
 | |
|   return gst_va_decoder_decode_with_aux_surface (self, pic, FALSE);
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_config_is_equal (GstVaDecoder * self, VAProfile new_profile,
 | |
|     guint new_rtformat, gint new_width, gint new_height)
 | |
| {
 | |
|   gboolean ret;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   /* @TODO: Check if current buffers are large enough, and reuse
 | |
|    * them */
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   ret = (self->profile == new_profile && self->rt_format == new_rtformat
 | |
|       && self->coded_width == new_width && self->coded_height == new_height);
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| gst_va_decoder_get_config (GstVaDecoder * self, VAProfile * profile,
 | |
|     guint * rt_format, gint * width, gint * height)
 | |
| {
 | |
|   g_return_val_if_fail (GST_IS_VA_DECODER (self), FALSE);
 | |
| 
 | |
|   if (!gst_va_decoder_is_open (self))
 | |
|     return FALSE;
 | |
| 
 | |
|   GST_OBJECT_LOCK (self);
 | |
|   if (profile)
 | |
|     *profile = self->profile;
 | |
|   if (rt_format)
 | |
|     *rt_format = self->rt_format;
 | |
|   if (width)
 | |
|     *width = self->coded_width;
 | |
|   if (height)
 | |
|     *height = self->coded_height;
 | |
|   GST_OBJECT_UNLOCK (self);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| _destroy_buffers (GstVaDecodePicture * pic)
 | |
| {
 | |
|   GstVaDisplay *display;
 | |
|   VABufferID buffer;
 | |
|   VADisplay dpy;
 | |
|   VAStatus status;
 | |
|   guint i;
 | |
|   gboolean ret = TRUE;
 | |
| 
 | |
|   display = gst_va_buffer_peek_display (pic->gstbuffer);
 | |
|   if (!display)
 | |
|     return FALSE;
 | |
|   dpy = gst_va_display_get_va_dpy (display);
 | |
| 
 | |
|   if (pic->buffers) {
 | |
|     for (i = 0; i < pic->buffers->len; i++) {
 | |
|       buffer = g_array_index (pic->buffers, VABufferID, i);
 | |
|       status = vaDestroyBuffer (dpy, buffer);
 | |
|       if (status != VA_STATUS_SUCCESS) {
 | |
|         ret = FALSE;
 | |
|         GST_WARNING ("Failed to destroy parameter buffer: %s",
 | |
|             vaErrorStr (status));
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     pic->buffers = g_array_set_size (pic->buffers, 0);
 | |
|   }
 | |
| 
 | |
|   if (pic->slices) {
 | |
|     for (i = 0; i < pic->slices->len; i++) {
 | |
|       buffer = g_array_index (pic->slices, VABufferID, i);
 | |
|       status = vaDestroyBuffer (dpy, buffer);
 | |
|       if (status != VA_STATUS_SUCCESS) {
 | |
|         ret = FALSE;
 | |
|         GST_WARNING ("Failed to destroy slice buffer: %s", vaErrorStr (status));
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     pic->slices = g_array_set_size (pic->slices, 0);
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| GstVaDecodePicture *
 | |
| gst_va_decode_picture_new (GstVaDecoder * self, GstBuffer * buffer)
 | |
| {
 | |
|   GstVaDecodePicture *pic;
 | |
| 
 | |
|   g_return_val_if_fail (buffer && GST_IS_BUFFER (buffer), NULL);
 | |
|   g_return_val_if_fail (self && GST_IS_VA_DECODER (self), NULL);
 | |
| 
 | |
|   pic = g_new (GstVaDecodePicture, 1);
 | |
|   pic->gstbuffer = gst_buffer_ref (buffer);
 | |
|   pic->buffers = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 16);
 | |
|   pic->slices = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 64);
 | |
| 
 | |
|   return pic;
 | |
| }
 | |
| 
 | |
| VASurfaceID
 | |
| gst_va_decode_picture_get_surface (GstVaDecodePicture * pic)
 | |
| {
 | |
|   g_return_val_if_fail (pic, VA_INVALID_ID);
 | |
|   g_return_val_if_fail (pic->gstbuffer, VA_INVALID_ID);
 | |
| 
 | |
|   return gst_va_buffer_get_surface (pic->gstbuffer);
 | |
| }
 | |
| 
 | |
| VASurfaceID
 | |
| gst_va_decode_picture_get_aux_surface (GstVaDecodePicture * pic)
 | |
| {
 | |
|   g_return_val_if_fail (pic, VA_INVALID_ID);
 | |
|   g_return_val_if_fail (pic->gstbuffer, VA_INVALID_ID);
 | |
| 
 | |
|   return gst_va_buffer_get_aux_surface (pic->gstbuffer);
 | |
| }
 | |
| 
 | |
| void
 | |
| gst_va_decode_picture_free (GstVaDecodePicture * pic)
 | |
| {
 | |
|   g_return_if_fail (pic);
 | |
| 
 | |
|   _destroy_buffers (pic);
 | |
| 
 | |
|   gst_buffer_unref (pic->gstbuffer);
 | |
|   g_clear_pointer (&pic->buffers, g_array_unref);
 | |
|   g_clear_pointer (&pic->slices, g_array_unref);
 | |
| 
 | |
|   g_free (pic);
 | |
| }
 | |
| 
 | |
| GstVaDecodePicture *
 | |
| gst_va_decode_picture_dup (GstVaDecodePicture * pic)
 | |
| {
 | |
|   GstVaDecodePicture *dup;
 | |
| 
 | |
|   g_return_val_if_fail (pic, NULL);
 | |
| 
 | |
|   dup = g_new0 (GstVaDecodePicture, 1);
 | |
| 
 | |
|   /* dups only need gstbuffer */
 | |
|   dup->gstbuffer = gst_buffer_ref (pic->gstbuffer);
 | |
|   return dup;
 | |
| }
 |