mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-26 18:20:44 +00:00
67eea92044
Drop obsolete GST_VAAPI_IS_xxx() helper macros since we are no longer deriving from GObject and so those were only checking for whether the argument was NULL or not. This is now irrelevant, and even confusing to some extent, because we no longer have type checking. Note: this incurs more type checking (review) but the libgstvaapi is rather small, so this is manageable.
987 lines
27 KiB
C
987 lines
27 KiB
C
/*
|
|
* gstvaapicontext.c - VA context abstraction
|
|
*
|
|
* Copyright (C) 2010-2011 Splitted-Desktop Systems
|
|
* Copyright (C) 2011-2013 Intel Corporation
|
|
*
|
|
* 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 <assert.h>
|
|
#include "gstvaapicompat.h"
|
|
#include "gstvaapicontext.h"
|
|
#include "gstvaapiobject_priv.h"
|
|
#include "gstvaapisurface.h"
|
|
#include "gstvaapisurface_priv.h"
|
|
#include "gstvaapisurfacepool.h"
|
|
#include "gstvaapisurfaceproxy.h"
|
|
#include "gstvaapivideopool_priv.h"
|
|
#include "gstvaapiimage.h"
|
|
#include "gstvaapisubpicture.h"
|
|
#include "gstvaapiutils.h"
|
|
|
|
#define DEBUG 1
|
|
#include "gstvaapidebug.h"
|
|
|
|
typedef struct _GstVaapiContextClass GstVaapiContextClass;
|
|
|
|
/**
|
|
* GstVaapiContext:
|
|
*
|
|
* A VA context wrapper.
|
|
*/
|
|
struct _GstVaapiContext {
|
|
/*< private >*/
|
|
GstVaapiObject parent_instance;
|
|
|
|
GstVaapiContextInfo info;
|
|
VAConfigID config_id;
|
|
GPtrArray *surfaces;
|
|
GstVaapiVideoPool *surfaces_pool;
|
|
GPtrArray *overlays[2];
|
|
guint overlay_id;
|
|
};
|
|
|
|
/**
|
|
* GstVaapiContextClass:
|
|
*
|
|
* A VA context wrapper class.
|
|
*/
|
|
struct _GstVaapiContextClass {
|
|
/*< private >*/
|
|
GstVaapiObjectClass parent_class;
|
|
};
|
|
|
|
typedef struct _GstVaapiOverlayRectangle GstVaapiOverlayRectangle;
|
|
struct _GstVaapiOverlayRectangle {
|
|
GstVaapiContext *context;
|
|
GstVaapiSubpicture *subpicture;
|
|
GstVaapiRectangle render_rect;
|
|
guint seq_num;
|
|
guint layer_id;
|
|
GstBuffer *rect_buffer;
|
|
GstVideoOverlayRectangle *rect;
|
|
guint is_associated : 1;
|
|
};
|
|
|
|
static guint
|
|
get_max_ref_frames(GstVaapiProfile profile)
|
|
{
|
|
guint ref_frames;
|
|
|
|
switch (gst_vaapi_profile_get_codec(profile)) {
|
|
case GST_VAAPI_CODEC_H264: ref_frames = 16; break;
|
|
case GST_VAAPI_CODEC_JPEG: ref_frames = 0; break;
|
|
default: ref_frames = 2; break;
|
|
}
|
|
return ref_frames;
|
|
}
|
|
|
|
static inline void
|
|
gst_video_overlay_rectangle_replace(GstVideoOverlayRectangle **old_rect_ptr,
|
|
GstVideoOverlayRectangle *new_rect)
|
|
{
|
|
gst_mini_object_replace((GstMiniObject **)old_rect_ptr,
|
|
GST_MINI_OBJECT_CAST(new_rect));
|
|
}
|
|
|
|
#define overlay_rectangle_ref(overlay) \
|
|
gst_vaapi_mini_object_ref(GST_VAAPI_MINI_OBJECT(overlay))
|
|
|
|
#define overlay_rectangle_unref(overlay) \
|
|
gst_vaapi_mini_object_unref(GST_VAAPI_MINI_OBJECT(overlay))
|
|
|
|
#define overlay_rectangle_replace(old_overlay_ptr, new_overlay) \
|
|
gst_vaapi_mini_object_replace((GstVaapiMiniObject **)(old_overlay_ptr), \
|
|
(GstVaapiMiniObject *)(new_overlay))
|
|
|
|
static void
|
|
overlay_rectangle_finalize(GstVaapiOverlayRectangle *overlay);
|
|
|
|
static gboolean
|
|
overlay_rectangle_associate(GstVaapiOverlayRectangle *overlay);
|
|
|
|
static gboolean
|
|
overlay_rectangle_deassociate(GstVaapiOverlayRectangle *overlay);
|
|
|
|
static inline const GstVaapiMiniObjectClass *
|
|
overlay_rectangle_class(void)
|
|
{
|
|
static const GstVaapiMiniObjectClass GstVaapiOverlayRectangleClass = {
|
|
sizeof(GstVaapiOverlayRectangle),
|
|
(GDestroyNotify)overlay_rectangle_finalize
|
|
};
|
|
return &GstVaapiOverlayRectangleClass;
|
|
}
|
|
|
|
static GstVaapiOverlayRectangle *
|
|
overlay_rectangle_new(GstVideoOverlayRectangle *rect, GstVaapiContext *context,
|
|
guint layer_id)
|
|
{
|
|
GstVaapiOverlayRectangle *overlay;
|
|
GstVaapiRectangle *render_rect;
|
|
guint width, height, flags;
|
|
gint x, y;
|
|
|
|
overlay = (GstVaapiOverlayRectangle *)
|
|
gst_vaapi_mini_object_new0(overlay_rectangle_class());
|
|
if (!overlay)
|
|
return NULL;
|
|
|
|
overlay->context = context;
|
|
overlay->seq_num = gst_video_overlay_rectangle_get_seqnum(rect);
|
|
overlay->layer_id = layer_id;
|
|
overlay->rect = gst_video_overlay_rectangle_ref(rect);
|
|
|
|
flags = gst_video_overlay_rectangle_get_flags(rect);
|
|
gst_buffer_replace(&overlay->rect_buffer,
|
|
gst_video_overlay_rectangle_get_pixels_unscaled_raw(rect, flags));
|
|
if (!overlay->rect_buffer)
|
|
goto error;
|
|
|
|
overlay->subpicture = gst_vaapi_subpicture_new_from_overlay_rectangle(
|
|
GST_VAAPI_OBJECT_DISPLAY(context), rect);
|
|
if (!overlay->subpicture)
|
|
goto error;
|
|
|
|
gst_video_overlay_rectangle_get_render_rectangle(rect,
|
|
&x, &y, &width, &height);
|
|
render_rect = &overlay->render_rect;
|
|
render_rect->x = x;
|
|
render_rect->y = y;
|
|
render_rect->width = width;
|
|
render_rect->height = height;
|
|
return overlay;
|
|
|
|
error:
|
|
overlay_rectangle_unref(overlay);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
overlay_rectangle_finalize(GstVaapiOverlayRectangle *overlay)
|
|
{
|
|
gst_buffer_replace(&overlay->rect_buffer, NULL);
|
|
gst_video_overlay_rectangle_unref(overlay->rect);
|
|
|
|
if (overlay->subpicture) {
|
|
overlay_rectangle_deassociate(overlay);
|
|
gst_vaapi_object_unref(overlay->subpicture);
|
|
overlay->subpicture = NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
overlay_rectangle_associate(GstVaapiOverlayRectangle *overlay)
|
|
{
|
|
GstVaapiSubpicture * const subpicture = overlay->subpicture;
|
|
GPtrArray * const surfaces = overlay->context->surfaces;
|
|
guint i, n_associated;
|
|
|
|
if (overlay->is_associated)
|
|
return TRUE;
|
|
|
|
n_associated = 0;
|
|
for (i = 0; i < surfaces->len; i++) {
|
|
GstVaapiSurface * const surface = g_ptr_array_index(surfaces, i);
|
|
if (gst_vaapi_surface_associate_subpicture(surface, subpicture,
|
|
NULL, &overlay->render_rect))
|
|
n_associated++;
|
|
}
|
|
|
|
overlay->is_associated = TRUE;
|
|
return n_associated == surfaces->len;
|
|
}
|
|
|
|
static gboolean
|
|
overlay_rectangle_deassociate(GstVaapiOverlayRectangle *overlay)
|
|
{
|
|
GstVaapiSubpicture * const subpicture = overlay->subpicture;
|
|
GPtrArray * const surfaces = overlay->context->surfaces;
|
|
guint i, n_associated;
|
|
|
|
if (!overlay->is_associated)
|
|
return TRUE;
|
|
|
|
n_associated = surfaces->len;
|
|
for (i = 0; i < surfaces->len; i++) {
|
|
GstVaapiSurface * const surface = g_ptr_array_index(surfaces, i);
|
|
if (gst_vaapi_surface_deassociate_subpicture(surface, subpicture))
|
|
n_associated--;
|
|
}
|
|
|
|
overlay->is_associated = FALSE;
|
|
return n_associated == 0;
|
|
}
|
|
|
|
static gboolean
|
|
overlay_rectangle_changed_pixels(GstVaapiOverlayRectangle *overlay,
|
|
GstVideoOverlayRectangle *rect)
|
|
{
|
|
guint flags;
|
|
GstBuffer *buffer;
|
|
|
|
if (overlay->seq_num == gst_video_overlay_rectangle_get_seqnum(rect))
|
|
return FALSE;
|
|
|
|
flags = to_GstVideoOverlayFormatFlags(
|
|
gst_vaapi_subpicture_get_flags(overlay->subpicture));
|
|
|
|
buffer = gst_video_overlay_rectangle_get_pixels_unscaled_raw(rect, flags);
|
|
if (!buffer)
|
|
return FALSE;
|
|
#if GST_CHECK_VERSION(1,0,0)
|
|
{
|
|
const guint n_blocks = gst_buffer_n_memory(buffer);
|
|
gsize ofs;
|
|
guint i;
|
|
|
|
if (buffer == overlay->rect_buffer)
|
|
return TRUE;
|
|
|
|
if (n_blocks != gst_buffer_n_memory(overlay->rect_buffer))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < n_blocks; i++) {
|
|
GstMemory * const mem1 = gst_buffer_peek_memory(buffer, i);
|
|
GstMemory * const mem2 =
|
|
gst_buffer_peek_memory(overlay->rect_buffer, i);
|
|
if (!gst_memory_is_span(mem1, mem2, &ofs))
|
|
return FALSE;
|
|
}
|
|
}
|
|
#else
|
|
if (GST_BUFFER_DATA(overlay->rect_buffer) != GST_BUFFER_DATA(buffer))
|
|
return FALSE;
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
overlay_rectangle_changed_render_rect(GstVaapiOverlayRectangle *overlay,
|
|
GstVideoOverlayRectangle *rect)
|
|
{
|
|
GstVaapiRectangle * const render_rect = &overlay->render_rect;
|
|
guint width, height;
|
|
gint x, y;
|
|
|
|
gst_video_overlay_rectangle_get_render_rectangle(rect,
|
|
&x, &y, &width, &height);
|
|
|
|
if (x == render_rect->x &&
|
|
y == render_rect->y &&
|
|
width == render_rect->width &&
|
|
height == render_rect->height)
|
|
return FALSE;
|
|
|
|
render_rect->x = x;
|
|
render_rect->y = y;
|
|
render_rect->width = width;
|
|
render_rect->height = height;
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
overlay_rectangle_update_global_alpha(GstVaapiOverlayRectangle *overlay,
|
|
GstVideoOverlayRectangle *rect)
|
|
{
|
|
#ifdef HAVE_GST_VIDEO_OVERLAY_HWCAPS
|
|
const guint flags = gst_video_overlay_rectangle_get_flags(rect);
|
|
if (!(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA))
|
|
return TRUE;
|
|
#endif
|
|
return gst_vaapi_subpicture_set_global_alpha(overlay->subpicture,
|
|
gst_video_overlay_rectangle_get_global_alpha(rect));
|
|
}
|
|
|
|
static gboolean
|
|
overlay_rectangle_update(GstVaapiOverlayRectangle *overlay,
|
|
GstVideoOverlayRectangle *rect, gboolean *reassociate_ptr)
|
|
{
|
|
if (overlay_rectangle_changed_pixels(overlay, rect))
|
|
return FALSE;
|
|
if (overlay_rectangle_changed_render_rect(overlay, rect))
|
|
*reassociate_ptr = TRUE;
|
|
if (!overlay_rectangle_update_global_alpha(overlay, rect))
|
|
return FALSE;
|
|
gst_video_overlay_rectangle_replace(&overlay->rect, rect);
|
|
return TRUE;
|
|
}
|
|
|
|
static inline GPtrArray *
|
|
overlay_new(void)
|
|
{
|
|
return g_ptr_array_new_with_free_func(
|
|
(GDestroyNotify)gst_vaapi_mini_object_unref);
|
|
}
|
|
|
|
static void
|
|
overlay_destroy(GPtrArray **overlay_ptr)
|
|
{
|
|
GPtrArray * const overlay = *overlay_ptr;
|
|
|
|
if (!overlay)
|
|
return;
|
|
g_ptr_array_unref(overlay);
|
|
*overlay_ptr = NULL;
|
|
}
|
|
|
|
static void
|
|
overlay_clear(GPtrArray *overlay)
|
|
{
|
|
if (overlay && overlay->len > 0)
|
|
g_ptr_array_remove_range(overlay, 0, overlay->len);
|
|
}
|
|
|
|
static GstVaapiOverlayRectangle *
|
|
overlay_lookup(GPtrArray *overlays, GstVideoOverlayRectangle *rect)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < overlays->len; i++) {
|
|
GstVaapiOverlayRectangle * const overlay =
|
|
g_ptr_array_index(overlays, i);
|
|
|
|
if (overlay->rect == rect)
|
|
return overlay;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
overlay_reassociate(GPtrArray *overlays)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < overlays->len; i++)
|
|
overlay_rectangle_deassociate(g_ptr_array_index(overlays, i));
|
|
|
|
for (i = 0; i < overlays->len; i++) {
|
|
if (!overlay_rectangle_associate(g_ptr_array_index(overlays, i)))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_context_clear_overlay(GstVaapiContext *context)
|
|
{
|
|
overlay_clear(context->overlays[0]);
|
|
overlay_clear(context->overlays[1]);
|
|
context->overlay_id = 0;
|
|
}
|
|
|
|
static inline void
|
|
gst_vaapi_context_destroy_overlay(GstVaapiContext *context)
|
|
{
|
|
gst_vaapi_context_clear_overlay(context);
|
|
}
|
|
|
|
static void
|
|
unref_surface_cb(gpointer data, gpointer user_data)
|
|
{
|
|
GstVaapiSurface * const surface = GST_VAAPI_SURFACE(data);
|
|
|
|
gst_vaapi_surface_set_parent_context(surface, NULL);
|
|
gst_vaapi_object_unref(surface);
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_context_destroy_surfaces(GstVaapiContext *context)
|
|
{
|
|
gst_vaapi_context_destroy_overlay(context);
|
|
|
|
if (context->surfaces) {
|
|
g_ptr_array_foreach(context->surfaces, unref_surface_cb, NULL);
|
|
g_ptr_array_free(context->surfaces, TRUE);
|
|
context->surfaces = NULL;
|
|
}
|
|
gst_vaapi_video_pool_replace(&context->surfaces_pool, NULL);
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_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 %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(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()"))
|
|
g_warning("failed to destroy context %" GST_VAAPI_ID_FORMAT,
|
|
GST_VAAPI_ID_ARGS(context_id));
|
|
GST_VAAPI_OBJECT_ID(context) = VA_INVALID_ID;
|
|
}
|
|
|
|
if (context->config_id != VA_INVALID_ID) {
|
|
GST_VAAPI_DISPLAY_LOCK(display);
|
|
status = vaDestroyConfig(
|
|
GST_VAAPI_DISPLAY_VADISPLAY(display),
|
|
context->config_id
|
|
);
|
|
GST_VAAPI_DISPLAY_UNLOCK(display);
|
|
if (!vaapi_check_status(status, "vaDestroyConfig()"))
|
|
g_warning("failed to destroy config %" GST_VAAPI_ID_FORMAT,
|
|
GST_VAAPI_ID_ARGS(context->config_id));
|
|
context->config_id = VA_INVALID_ID;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_context_create_overlay(GstVaapiContext *context)
|
|
{
|
|
if (!context->overlays[0] || !context->overlays[1])
|
|
return FALSE;
|
|
|
|
gst_vaapi_context_clear_overlay(context);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_context_create_surfaces(GstVaapiContext *context)
|
|
{
|
|
const GstVaapiContextInfo * const cip = &context->info;
|
|
GstCaps *caps;
|
|
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_create_overlay(context))
|
|
return FALSE;
|
|
|
|
if (!context->surfaces) {
|
|
context->surfaces = g_ptr_array_new();
|
|
if (!context->surfaces)
|
|
return FALSE;
|
|
}
|
|
|
|
if (!context->surfaces_pool) {
|
|
caps = gst_caps_new_simple(
|
|
GST_VAAPI_SURFACE_CAPS_NAME,
|
|
"type", G_TYPE_STRING, "vaapi",
|
|
"width", G_TYPE_INT, cip->width,
|
|
"height", G_TYPE_INT, cip->height,
|
|
NULL
|
|
);
|
|
if (!caps)
|
|
return FALSE;
|
|
context->surfaces_pool = gst_vaapi_surface_pool_new(
|
|
GST_VAAPI_OBJECT_DISPLAY(context),
|
|
caps
|
|
);
|
|
gst_caps_unref(caps);
|
|
if (!context->surfaces_pool)
|
|
return FALSE;
|
|
}
|
|
|
|
num_surfaces = cip->ref_frames + SCRATCH_SURFACES_COUNT;
|
|
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
|
|
gst_vaapi_context_create(GstVaapiContext *context)
|
|
{
|
|
const GstVaapiContextInfo * const cip = &context->info;
|
|
GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(context);
|
|
VAProfile va_profile;
|
|
VAEntrypoint va_entrypoint;
|
|
VAConfigAttrib attrib;
|
|
VAContextID context_id;
|
|
VASurfaceID surface_id;
|
|
VAStatus status;
|
|
GArray *surfaces = NULL;
|
|
gboolean success = FALSE;
|
|
guint i;
|
|
|
|
if (!context->surfaces && !gst_vaapi_context_create_surfaces(context))
|
|
goto end;
|
|
|
|
surfaces = g_array_sized_new(
|
|
FALSE,
|
|
FALSE,
|
|
sizeof(VASurfaceID),
|
|
context->surfaces->len
|
|
);
|
|
if (!surfaces)
|
|
goto end;
|
|
|
|
for (i = 0; i < context->surfaces->len; i++) {
|
|
GstVaapiSurface * const surface =
|
|
g_ptr_array_index(context->surfaces, i);
|
|
if (!surface)
|
|
goto end;
|
|
surface_id = GST_VAAPI_OBJECT_ID(surface);
|
|
g_array_append_val(surfaces, surface_id);
|
|
}
|
|
assert(surfaces->len == context->surfaces->len);
|
|
|
|
if (!cip->profile || !cip->entrypoint)
|
|
goto end;
|
|
va_profile = gst_vaapi_profile_get_va_profile(cip->profile);
|
|
va_entrypoint = gst_vaapi_entrypoint_get_va_entrypoint(cip->entrypoint);
|
|
|
|
GST_VAAPI_DISPLAY_LOCK(display);
|
|
attrib.type = VAConfigAttribRTFormat;
|
|
status = vaGetConfigAttributes(
|
|
GST_VAAPI_DISPLAY_VADISPLAY(display),
|
|
va_profile,
|
|
va_entrypoint,
|
|
&attrib, 1
|
|
);
|
|
GST_VAAPI_DISPLAY_UNLOCK(display);
|
|
if (!vaapi_check_status(status, "vaGetConfigAttributes()"))
|
|
goto end;
|
|
if (!(attrib.value & VA_RT_FORMAT_YUV420))
|
|
goto end;
|
|
|
|
GST_VAAPI_DISPLAY_LOCK(display);
|
|
status = vaCreateConfig(
|
|
GST_VAAPI_DISPLAY_VADISPLAY(display),
|
|
va_profile,
|
|
va_entrypoint,
|
|
&attrib, 1,
|
|
&context->config_id
|
|
);
|
|
GST_VAAPI_DISPLAY_UNLOCK(display);
|
|
if (!vaapi_check_status(status, "vaCreateConfig()"))
|
|
goto end;
|
|
|
|
GST_VAAPI_DISPLAY_LOCK(display);
|
|
status = vaCreateContext(
|
|
GST_VAAPI_DISPLAY_VADISPLAY(display),
|
|
context->config_id,
|
|
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 end;
|
|
|
|
GST_DEBUG("context %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(context_id));
|
|
GST_VAAPI_OBJECT_ID(context) = context_id;
|
|
success = TRUE;
|
|
end:
|
|
if (surfaces)
|
|
g_array_free(surfaces, TRUE);
|
|
return success;
|
|
}
|
|
|
|
static inline void
|
|
gst_vaapi_context_init(GstVaapiContext *context, const GstVaapiContextInfo *cip)
|
|
{
|
|
context->info = *cip;
|
|
context->config_id = VA_INVALID_ID;
|
|
context->overlays[0] = overlay_new();
|
|
context->overlays[1] = overlay_new();
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_context_finalize(GstVaapiContext *context)
|
|
{
|
|
overlay_destroy(&context->overlays[0]);
|
|
overlay_destroy(&context->overlays[1]);
|
|
gst_vaapi_context_destroy(context);
|
|
gst_vaapi_context_destroy_surfaces(context);
|
|
}
|
|
|
|
GST_VAAPI_OBJECT_DEFINE_CLASS(GstVaapiContext, gst_vaapi_context)
|
|
|
|
/**
|
|
* gst_vaapi_context_new:
|
|
* @display: a #GstVaapiDisplay
|
|
* @profile: a #GstVaapiProfile
|
|
* @entrypoint: a #GstVaapiEntrypoint
|
|
* @width: coded width from the bitstream
|
|
* @height: coded height from the bitstream
|
|
*
|
|
* Creates a new #GstVaapiContext with the specified codec @profile
|
|
* and @entrypoint.
|
|
*
|
|
* Return value: the newly allocated #GstVaapiContext object
|
|
*/
|
|
GstVaapiContext *
|
|
gst_vaapi_context_new(
|
|
GstVaapiDisplay *display,
|
|
GstVaapiProfile profile,
|
|
GstVaapiEntrypoint entrypoint,
|
|
guint width,
|
|
guint height
|
|
)
|
|
{
|
|
GstVaapiContextInfo info;
|
|
|
|
info.profile = profile;
|
|
info.entrypoint = entrypoint;
|
|
info.width = width;
|
|
info.height = height;
|
|
info.ref_frames = get_max_ref_frames(profile);
|
|
return gst_vaapi_context_new_full(display, &info);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_new_full:
|
|
* @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_full(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 (!gst_vaapi_context_create(context))
|
|
goto error;
|
|
return context;
|
|
|
|
error:
|
|
gst_vaapi_object_unref(context);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_reset:
|
|
* @context: a #GstVaapiContext
|
|
* @profile: a #GstVaapiProfile
|
|
* @entrypoint: a #GstVaapiEntrypoint
|
|
* @width: coded width from the bitstream
|
|
* @height: coded height from the bitstream
|
|
*
|
|
* Resets @context to the specified codec @profile and @entrypoint.
|
|
* The surfaces will be reallocated if the coded size changed.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
gboolean
|
|
gst_vaapi_context_reset(
|
|
GstVaapiContext *context,
|
|
GstVaapiProfile profile,
|
|
GstVaapiEntrypoint entrypoint,
|
|
unsigned int width,
|
|
unsigned int height
|
|
)
|
|
{
|
|
GstVaapiContextInfo info;
|
|
|
|
info.profile = profile;
|
|
info.entrypoint = entrypoint;
|
|
info.width = width;
|
|
info.height = height;
|
|
info.ref_frames = context->info.ref_frames;
|
|
|
|
return gst_vaapi_context_reset_full(context, &info);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_reset_full:
|
|
* @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_full(GstVaapiContext *context,
|
|
const GstVaapiContextInfo *new_cip)
|
|
{
|
|
GstVaapiContextInfo * const cip = &context->info;
|
|
gboolean size_changed, codec_changed;
|
|
|
|
size_changed = cip->width != new_cip->width ||
|
|
cip->height != new_cip->height;
|
|
if (size_changed) {
|
|
gst_vaapi_context_destroy_surfaces(context);
|
|
cip->width = new_cip->width;
|
|
cip->height = new_cip->height;
|
|
}
|
|
|
|
codec_changed = cip->profile != new_cip->profile ||
|
|
cip->entrypoint != new_cip->entrypoint;
|
|
if (codec_changed) {
|
|
gst_vaapi_context_destroy(context);
|
|
cip->profile = new_cip->profile;
|
|
cip->entrypoint = new_cip->entrypoint;
|
|
}
|
|
|
|
if (size_changed && !gst_vaapi_context_create_surfaces(context))
|
|
return FALSE;
|
|
|
|
if (codec_changed && !gst_vaapi_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_profile:
|
|
* @context: a #GstVaapiContext
|
|
*
|
|
* Returns the VA profile used by the @context.
|
|
*
|
|
* Return value: the VA profile used by the @context
|
|
*/
|
|
GstVaapiProfile
|
|
gst_vaapi_context_get_profile(GstVaapiContext *context)
|
|
{
|
|
g_return_val_if_fail(context != NULL, 0);
|
|
|
|
return context->info.profile;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_set_profile:
|
|
* @context: a #GstVaapiContext
|
|
* @profile: the new #GstVaapiProfile to use
|
|
*
|
|
* Sets the new @profile to use with the @context. If @profile matches
|
|
* the previous profile, this call has no effect. Otherwise, the
|
|
* underlying VA context is recreated, while keeping the previously
|
|
* allocated surfaces.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
gboolean
|
|
gst_vaapi_context_set_profile(GstVaapiContext *context, GstVaapiProfile profile)
|
|
{
|
|
g_return_val_if_fail(context != NULL, FALSE);
|
|
g_return_val_if_fail(profile, FALSE);
|
|
|
|
return gst_vaapi_context_reset(context,
|
|
profile,
|
|
context->info.entrypoint,
|
|
context->info.width,
|
|
context->info.height);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_get_entrypoint:
|
|
* @context: a #GstVaapiContext
|
|
*
|
|
* Returns the VA entrypoint used by the @context
|
|
*
|
|
* Return value: the VA entrypoint used by the @context
|
|
*/
|
|
GstVaapiEntrypoint
|
|
gst_vaapi_context_get_entrypoint(GstVaapiContext *context)
|
|
{
|
|
g_return_val_if_fail(context != NULL, 0);
|
|
|
|
return context->info.entrypoint;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_context_get_size:
|
|
* @context: a #GstVaapiContext
|
|
* @pwidth: return location for the width, or %NULL
|
|
* @pheight: return location for the height, or %NULL
|
|
*
|
|
* Retrieves the size of the surfaces attached to @context.
|
|
*/
|
|
void
|
|
gst_vaapi_context_get_size(
|
|
GstVaapiContext *context,
|
|
guint *pwidth,
|
|
guint *pheight
|
|
)
|
|
{
|
|
g_return_if_fail(context != NULL);
|
|
|
|
if (pwidth)
|
|
*pwidth = context->info.width;
|
|
|
|
if (pheight)
|
|
*pheight = context->info.height;
|
|
}
|
|
|
|
/**
|
|
* 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_apply_composition:
|
|
* @context: a #GstVaapiContext
|
|
* @composition: a #GstVideoOverlayComposition
|
|
*
|
|
* Applies video composition planes to all surfaces bound to @context.
|
|
* This helper function resets any additional subpictures the user may
|
|
* have associated himself. A %NULL @composition will also clear all
|
|
* the existing subpictures.
|
|
*
|
|
* Return value: %TRUE if all composition planes could be applied,
|
|
* %FALSE otherwise
|
|
*/
|
|
gboolean
|
|
gst_vaapi_context_apply_composition(
|
|
GstVaapiContext *context,
|
|
GstVideoOverlayComposition *composition
|
|
)
|
|
{
|
|
GPtrArray *curr_overlay, *next_overlay;
|
|
guint i, n_rectangles;
|
|
gboolean reassociate = FALSE;
|
|
|
|
g_return_val_if_fail(context != NULL, FALSE);
|
|
|
|
if (!context->surfaces)
|
|
return FALSE;
|
|
|
|
if (!composition) {
|
|
gst_vaapi_context_clear_overlay(context);
|
|
return TRUE;
|
|
}
|
|
|
|
curr_overlay = context->overlays[context->overlay_id];
|
|
next_overlay = context->overlays[context->overlay_id ^ 1];
|
|
overlay_clear(next_overlay);
|
|
|
|
n_rectangles = gst_video_overlay_composition_n_rectangles(composition);
|
|
for (i = 0; i < n_rectangles; i++) {
|
|
GstVideoOverlayRectangle * const rect =
|
|
gst_video_overlay_composition_get_rectangle(composition, i);
|
|
GstVaapiOverlayRectangle *overlay;
|
|
|
|
overlay = overlay_lookup(curr_overlay, rect);
|
|
if (overlay && overlay_rectangle_update(overlay, rect, &reassociate)) {
|
|
overlay_rectangle_ref(overlay);
|
|
if (overlay->layer_id != i)
|
|
reassociate = TRUE;
|
|
}
|
|
else {
|
|
overlay = overlay_rectangle_new(rect, context, i);
|
|
if (!overlay) {
|
|
GST_WARNING("could not create VA overlay rectangle");
|
|
goto error;
|
|
}
|
|
reassociate = TRUE;
|
|
}
|
|
g_ptr_array_add(next_overlay, overlay);
|
|
}
|
|
|
|
overlay_clear(curr_overlay);
|
|
context->overlay_id ^= 1;
|
|
|
|
if (reassociate && !overlay_reassociate(next_overlay))
|
|
return FALSE;
|
|
return TRUE;
|
|
|
|
error:
|
|
gst_vaapi_context_clear_overlay(context);
|
|
return FALSE;
|
|
}
|