mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 03:19:40 +00:00
4481c155b8
Make sure to configure the encoder with the set of packed headers we intend to generate and submit. i.e. make selection of packed headers to submit more robust.
477 lines
14 KiB
C
477 lines
14 KiB
C
/*
|
|
* gstvaapicontext.c - VA context abstraction
|
|
*
|
|
* Copyright (C) 2010-2011 Splitted-Desktop Systems
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
|
|
* Copyright (C) 2011-2014 Intel Corporation
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstvaapicontext
|
|
* @short_description: VA context abstraction
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include "gstvaapicompat.h"
|
|
#include "gstvaapicontext.h"
|
|
#include "gstvaapicontext_overlay.h"
|
|
#include "gstvaapidisplay_priv.h"
|
|
#include "gstvaapiobject_priv.h"
|
|
#include "gstvaapisurface.h"
|
|
#include "gstvaapisurface_priv.h"
|
|
#include "gstvaapisurfacepool.h"
|
|
#include "gstvaapisurfaceproxy.h"
|
|
#include "gstvaapivideopool_priv.h"
|
|
#include "gstvaapiutils.h"
|
|
|
|
#define DEBUG 1
|
|
#include "gstvaapidebug.h"
|
|
|
|
static void
|
|
unref_surface_cb (GstVaapiSurface * surface)
|
|
{
|
|
gst_vaapi_surface_set_parent_context (surface, NULL);
|
|
gst_vaapi_object_unref (surface);
|
|
}
|
|
|
|
static void
|
|
context_destroy_surfaces (GstVaapiContext * context)
|
|
{
|
|
gst_vaapi_context_overlay_reset (context);
|
|
|
|
if (context->surfaces) {
|
|
g_ptr_array_unref (context->surfaces);
|
|
context->surfaces = NULL;
|
|
}
|
|
gst_vaapi_video_pool_replace (&context->surfaces_pool, NULL);
|
|
}
|
|
|
|
static void
|
|
context_destroy (GstVaapiContext * context)
|
|
{
|
|
GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (context);
|
|
VAContextID context_id;
|
|
VAStatus status;
|
|
|
|
context_id = GST_VAAPI_OBJECT_ID (context);
|
|
GST_DEBUG ("context 0x%08x", context_id);
|
|
|
|
if (context_id != VA_INVALID_ID) {
|
|
GST_VAAPI_DISPLAY_LOCK (display);
|
|
status = vaDestroyContext (GST_VAAPI_DISPLAY_VADISPLAY (display),
|
|
context_id);
|
|
GST_VAAPI_DISPLAY_UNLOCK (display);
|
|
if (!vaapi_check_status (status, "vaDestroyContext()"))
|
|
GST_WARNING ("failed to destroy context 0x%08x", context_id);
|
|
GST_VAAPI_OBJECT_ID (context) = VA_INVALID_ID;
|
|
}
|
|
|
|
if (context->va_config != VA_INVALID_ID) {
|
|
GST_VAAPI_DISPLAY_LOCK (display);
|
|
status = vaDestroyConfig (GST_VAAPI_DISPLAY_VADISPLAY (display),
|
|
context->va_config);
|
|
GST_VAAPI_DISPLAY_UNLOCK (display);
|
|
if (!vaapi_check_status (status, "vaDestroyConfig()"))
|
|
GST_WARNING ("failed to destroy config 0x%08x", context->va_config);
|
|
context->va_config = VA_INVALID_ID;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
context_create_surfaces (GstVaapiContext * context)
|
|
{
|
|
const GstVaapiContextInfo *const cip = &context->info;
|
|
GstVideoInfo vi;
|
|
GstVaapiSurface *surface;
|
|
guint i, num_surfaces;
|
|
|
|
/* Number of scratch surfaces beyond those used as reference */
|
|
const guint SCRATCH_SURFACES_COUNT = 4;
|
|
|
|
if (!gst_vaapi_context_overlay_reset (context))
|
|
return FALSE;
|
|
|
|
num_surfaces = cip->ref_frames + SCRATCH_SURFACES_COUNT;
|
|
if (!context->surfaces) {
|
|
context->surfaces = g_ptr_array_new_full (num_surfaces,
|
|
(GDestroyNotify) unref_surface_cb);
|
|
if (!context->surfaces)
|
|
return FALSE;
|
|
}
|
|
|
|
if (!context->surfaces_pool) {
|
|
gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
|
|
cip->width, cip->height);
|
|
context->surfaces_pool =
|
|
gst_vaapi_surface_pool_new (GST_VAAPI_OBJECT_DISPLAY (context), &vi);
|
|
if (!context->surfaces_pool)
|
|
return FALSE;
|
|
}
|
|
gst_vaapi_video_pool_set_capacity (context->surfaces_pool, num_surfaces);
|
|
|
|
for (i = context->surfaces->len; i < num_surfaces; i++) {
|
|
surface = gst_vaapi_surface_new (GST_VAAPI_OBJECT_DISPLAY (context),
|
|
GST_VAAPI_CHROMA_TYPE_YUV420, cip->width, cip->height);
|
|
if (!surface)
|
|
return FALSE;
|
|
gst_vaapi_surface_set_parent_context (surface, context);
|
|
g_ptr_array_add (context->surfaces, surface);
|
|
if (!gst_vaapi_video_pool_add_object (context->surfaces_pool, surface))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
context_create (GstVaapiContext * context)
|
|
{
|
|
const GstVaapiContextInfo *const cip = &context->info;
|
|
GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (context);
|
|
guint va_rate_control;
|
|
VAConfigAttrib attribs[3], *attrib = attribs;
|
|
VAContextID context_id;
|
|
VASurfaceID surface_id;
|
|
VAStatus status;
|
|
GArray *surfaces = NULL;
|
|
gboolean success = FALSE;
|
|
guint i, value;
|
|
|
|
if (!context->surfaces && !context_create_surfaces (context))
|
|
goto cleanup;
|
|
|
|
/* Create VA surfaces list for vaCreateContext() */
|
|
surfaces = g_array_sized_new (FALSE,
|
|
FALSE, sizeof (VASurfaceID), context->surfaces->len);
|
|
if (!surfaces)
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < context->surfaces->len; i++) {
|
|
GstVaapiSurface *const surface = g_ptr_array_index (context->surfaces, i);
|
|
if (!surface)
|
|
goto cleanup;
|
|
surface_id = GST_VAAPI_OBJECT_ID (surface);
|
|
g_array_append_val (surfaces, surface_id);
|
|
}
|
|
g_assert (surfaces->len == context->surfaces->len);
|
|
|
|
/* Reset profile and entrypoint */
|
|
if (!cip->profile || !cip->entrypoint)
|
|
goto cleanup;
|
|
context->va_profile = gst_vaapi_profile_get_va_profile (cip->profile);
|
|
context->va_entrypoint =
|
|
gst_vaapi_entrypoint_get_va_entrypoint (cip->entrypoint);
|
|
|
|
/* Validate VA surface format */
|
|
attrib->type = VAConfigAttribRTFormat;
|
|
if (!gst_vaapi_context_get_attribute (context, attrib->type, &value))
|
|
goto cleanup;
|
|
if (!(value & VA_RT_FORMAT_YUV420))
|
|
goto cleanup;
|
|
attrib->value = VA_RT_FORMAT_YUV420;
|
|
attrib++;
|
|
|
|
switch (cip->usage) {
|
|
case GST_VAAPI_CONTEXT_USAGE_ENCODE:
|
|
{
|
|
const GstVaapiConfigInfoEncoder *const config = &cip->config.encoder;
|
|
|
|
/* Rate control */
|
|
attrib->type = VAConfigAttribRateControl;
|
|
if (!gst_vaapi_context_get_attribute (context, attrib->type, &value))
|
|
goto cleanup;
|
|
|
|
va_rate_control = from_GstVaapiRateControl (config->rc_mode);
|
|
if ((value & va_rate_control) != va_rate_control) {
|
|
GST_ERROR ("unsupported %s rate control",
|
|
string_of_VARateControl (va_rate_control));
|
|
goto cleanup;
|
|
}
|
|
attrib->value = va_rate_control;
|
|
attrib++;
|
|
|
|
/* Packed headers */
|
|
if (config->packed_headers) {
|
|
attrib->type = VAConfigAttribEncPackedHeaders;
|
|
if (!gst_vaapi_context_get_attribute (context, attrib->type, &value))
|
|
goto cleanup;
|
|
|
|
if ((value & config->packed_headers) != config->packed_headers) {
|
|
GST_ERROR ("unsupported packed headers 0x%08x",
|
|
config->packed_headers & ~(value & config->packed_headers));
|
|
goto cleanup;
|
|
}
|
|
attrib->value = config->packed_headers;
|
|
attrib++;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
GST_VAAPI_DISPLAY_LOCK (display);
|
|
status = vaCreateConfig (GST_VAAPI_DISPLAY_VADISPLAY (display),
|
|
context->va_profile, context->va_entrypoint, attribs, attrib - attribs,
|
|
&context->va_config);
|
|
GST_VAAPI_DISPLAY_UNLOCK (display);
|
|
if (!vaapi_check_status (status, "vaCreateConfig()"))
|
|
goto cleanup;
|
|
|
|
GST_VAAPI_DISPLAY_LOCK (display);
|
|
status = vaCreateContext (GST_VAAPI_DISPLAY_VADISPLAY (display),
|
|
context->va_config, cip->width, cip->height, VA_PROGRESSIVE,
|
|
(VASurfaceID *) surfaces->data, surfaces->len, &context_id);
|
|
GST_VAAPI_DISPLAY_UNLOCK (display);
|
|
if (!vaapi_check_status (status, "vaCreateContext()"))
|
|
goto cleanup;
|
|
|
|
GST_DEBUG ("context 0x%08x", context_id);
|
|
GST_VAAPI_OBJECT_ID (context) = context_id;
|
|
success = TRUE;
|
|
|
|
cleanup:
|
|
if (surfaces)
|
|
g_array_free (surfaces, TRUE);
|
|
return success;
|
|
}
|
|
|
|
/** Updates config for encoding. Returns %TRUE if config changed */
|
|
static gboolean
|
|
context_update_config_encoder (GstVaapiContext * context,
|
|
const GstVaapiConfigInfoEncoder * new_config)
|
|
{
|
|
GstVaapiConfigInfoEncoder *const config = &context->info.config.encoder;
|
|
gboolean config_changed = FALSE;
|
|
|
|
g_assert (context->info.usage == GST_VAAPI_CONTEXT_USAGE_ENCODE);
|
|
|
|
if (config->rc_mode != new_config->rc_mode) {
|
|
config->rc_mode = new_config->rc_mode;
|
|
config_changed = TRUE;
|
|
}
|
|
|
|
if (config->packed_headers != new_config->packed_headers) {
|
|
config->packed_headers = new_config->packed_headers;
|
|
config_changed = TRUE;
|
|
}
|
|
return config_changed;
|
|
}
|
|
|
|
static inline void
|
|
gst_vaapi_context_init (GstVaapiContext * context,
|
|
const GstVaapiContextInfo * cip)
|
|
{
|
|
context->info = *cip;
|
|
context->va_config = VA_INVALID_ID;
|
|
gst_vaapi_context_overlay_init (context);
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_context_finalize (GstVaapiContext * context)
|
|
{
|
|
context_destroy (context);
|
|
context_destroy_surfaces (context);
|
|
gst_vaapi_context_overlay_finalize (context);
|
|
}
|
|
|
|
GST_VAAPI_OBJECT_DEFINE_CLASS (GstVaapiContext, gst_vaapi_context);
|
|
|
|
/**
|
|
* gst_vaapi_context_new:
|
|
* @display: a #GstVaapiDisplay
|
|
* @cip: a pointer to the #GstVaapiContextInfo
|
|
*
|
|
* Creates a new #GstVaapiContext with the configuration specified by
|
|
* @cip, thus including profile, entry-point, encoded size and maximum
|
|
* number of reference frames reported by the bitstream.
|
|
*
|
|
* Return value: the newly allocated #GstVaapiContext object
|
|
*/
|
|
GstVaapiContext *
|
|
gst_vaapi_context_new (GstVaapiDisplay * display,
|
|
const GstVaapiContextInfo * cip)
|
|
{
|
|
GstVaapiContext *context;
|
|
|
|
g_return_val_if_fail (cip->profile, NULL);
|
|
g_return_val_if_fail (cip->entrypoint, NULL);
|
|
g_return_val_if_fail (cip->width > 0, NULL);
|
|
g_return_val_if_fail (cip->height > 0, NULL);
|
|
|
|
context = gst_vaapi_object_new (gst_vaapi_context_class (), display);
|
|
if (!context)
|
|
return NULL;
|
|
|
|
gst_vaapi_context_init (context, cip);
|
|
if (!context_create (context))
|
|
goto error;
|
|
return context;
|
|
|
|
error:
|
|
gst_vaapi_object_unref (context);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_reset:
|
|
* @context: a #GstVaapiContext
|
|
* @new_cip: a pointer to the new #GstVaapiContextInfo details
|
|
*
|
|
* Resets @context to the configuration specified by @new_cip, thus
|
|
* including profile, entry-point, encoded size and maximum number of
|
|
* reference frames reported by the bitstream.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
gboolean
|
|
gst_vaapi_context_reset (GstVaapiContext * context,
|
|
const GstVaapiContextInfo * new_cip)
|
|
{
|
|
GstVaapiContextInfo *const cip = &context->info;
|
|
gboolean size_changed, config_changed;
|
|
|
|
size_changed = cip->width != new_cip->width || cip->height != new_cip->height;
|
|
if (size_changed) {
|
|
cip->width = new_cip->width;
|
|
cip->height = new_cip->height;
|
|
}
|
|
|
|
config_changed = cip->profile != new_cip->profile ||
|
|
cip->entrypoint != new_cip->entrypoint;
|
|
if (config_changed) {
|
|
cip->profile = new_cip->profile;
|
|
cip->entrypoint = new_cip->entrypoint;
|
|
}
|
|
|
|
if (cip->usage != new_cip->usage) {
|
|
cip->usage = new_cip->usage;
|
|
config_changed = TRUE;
|
|
memcpy (&cip->config, &new_cip->config, sizeof (cip->config));
|
|
} else if (new_cip->usage == GST_VAAPI_CONTEXT_USAGE_ENCODE) {
|
|
if (context_update_config_encoder (context, &new_cip->config.encoder))
|
|
config_changed = TRUE;
|
|
}
|
|
|
|
if (size_changed)
|
|
context_destroy_surfaces (context);
|
|
if (config_changed)
|
|
context_destroy (context);
|
|
|
|
if (size_changed && !context_create_surfaces (context))
|
|
return FALSE;
|
|
if (config_changed && !context_create (context))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_get_id:
|
|
* @context: a #GstVaapiContext
|
|
*
|
|
* Returns the underlying VAContextID of the @context.
|
|
*
|
|
* Return value: the underlying VA context id
|
|
*/
|
|
GstVaapiID
|
|
gst_vaapi_context_get_id (GstVaapiContext * context)
|
|
{
|
|
g_return_val_if_fail (context != NULL, VA_INVALID_ID);
|
|
|
|
return GST_VAAPI_OBJECT_ID (context);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_get_surface_proxy:
|
|
* @context: a #GstVaapiContext
|
|
*
|
|
* Acquires a free surface, wrapped into a #GstVaapiSurfaceProxy. The
|
|
* returned surface will be automatically released when the proxy is
|
|
* destroyed. So, it is enough to call gst_vaapi_surface_proxy_unref()
|
|
* after usage.
|
|
*
|
|
* This function returns %NULL if there is no free surface available
|
|
* in the pool. The surfaces are pre-allocated during context creation
|
|
* though.
|
|
*
|
|
* Return value: a free surface, or %NULL if none is available
|
|
*/
|
|
GstVaapiSurfaceProxy *
|
|
gst_vaapi_context_get_surface_proxy (GstVaapiContext * context)
|
|
{
|
|
g_return_val_if_fail (context != NULL, NULL);
|
|
|
|
return
|
|
gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
|
|
(context->surfaces_pool));
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_get_surface_count:
|
|
* @context: a #GstVaapiContext
|
|
*
|
|
* Retrieves the number of free surfaces left in the pool.
|
|
*
|
|
* Return value: the number of free surfaces available in the pool
|
|
*/
|
|
guint
|
|
gst_vaapi_context_get_surface_count (GstVaapiContext * context)
|
|
{
|
|
g_return_val_if_fail (context != NULL, 0);
|
|
|
|
return gst_vaapi_video_pool_get_size (context->surfaces_pool);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_get_attribute:
|
|
* @context: a #GstVaapiContext
|
|
* @type: a VA config attribute type
|
|
* @out_value_ptr: return location for the config attribute value
|
|
*
|
|
* Determines the value for the VA config attribute @type.
|
|
*
|
|
* Note: this function only returns success if the VA driver does
|
|
* actually know about this config attribute type and that it returned
|
|
* a valid value for it.
|
|
*
|
|
* Return value: %TRUE if the VA driver knows about the requested
|
|
* config attribute and returned a valid value, %FALSE otherwise
|
|
*/
|
|
gboolean
|
|
gst_vaapi_context_get_attribute (GstVaapiContext * context,
|
|
VAConfigAttribType type, guint * out_value_ptr)
|
|
{
|
|
VAConfigAttrib attrib;
|
|
VAStatus status;
|
|
|
|
g_return_val_if_fail (context != NULL, FALSE);
|
|
|
|
GST_VAAPI_OBJECT_LOCK_DISPLAY (context);
|
|
attrib.type = type;
|
|
status = vaGetConfigAttributes (GST_VAAPI_OBJECT_VADISPLAY (context),
|
|
context->va_profile, context->va_entrypoint, &attrib, 1);
|
|
GST_VAAPI_OBJECT_UNLOCK_DISPLAY (context);
|
|
if (!vaapi_check_status (status, "vaGetConfigAttributes()"))
|
|
return FALSE;
|
|
if (attrib.value == VA_ATTRIB_NOT_SUPPORTED)
|
|
return FALSE;
|
|
|
|
if (out_value_ptr)
|
|
*out_value_ptr = attrib.value;
|
|
return TRUE;
|
|
}
|