|
|
|
@ -35,6 +35,11 @@ |
|
|
|
|
|
|
|
#include <va/va.h> |
|
|
|
|
|
|
|
#include <vlc_common.h> |
|
|
|
#include <vlc_atomic.h> |
|
|
|
#include <vlc_fourcc.h> |
|
|
|
#include <vlc_picture_pool.h> |
|
|
|
|
|
|
|
/* This macro is designed to wrap any VA call, and in case of failure,
|
|
|
|
display the VA error string then goto the 'error' label (which you must |
|
|
|
define). */ |
|
|
|
@ -251,3 +256,328 @@ vlc_vaapi_EndPicture(vlc_object_t *o, VADisplay dpy, VAContextID ctx) |
|
|
|
return VLC_SUCCESS; |
|
|
|
error: return VLC_EGENERIC; |
|
|
|
} |
|
|
|
|
|
|
|
/*****************
|
|
|
|
* VAAPI helpers * |
|
|
|
*****************/ |
|
|
|
|
|
|
|
static bool |
|
|
|
IsVaProfileSupported(VADisplay dpy, VAProfile i_profile) |
|
|
|
{ |
|
|
|
/* Check if the selected profile is supported */ |
|
|
|
if (i_profile == VAProfileNone) |
|
|
|
return true; |
|
|
|
int i_profiles_nb = vaMaxNumProfiles(dpy); |
|
|
|
if (i_profiles_nb < 0) |
|
|
|
return false; |
|
|
|
VAProfile *p_profiles_list = calloc(i_profiles_nb, sizeof(VAProfile)); |
|
|
|
if (!p_profiles_list) |
|
|
|
return false; |
|
|
|
|
|
|
|
bool b_supported_profile = false; |
|
|
|
VAStatus status = |
|
|
|
vaQueryConfigProfiles(dpy, p_profiles_list, &i_profiles_nb); |
|
|
|
if (status != VA_STATUS_SUCCESS) |
|
|
|
goto error; |
|
|
|
|
|
|
|
for (int i = 0; i < i_profiles_nb; i++) |
|
|
|
{ |
|
|
|
if (p_profiles_list[i] == i_profile) |
|
|
|
{ |
|
|
|
b_supported_profile = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
error: |
|
|
|
free(p_profiles_list); |
|
|
|
return b_supported_profile; |
|
|
|
} |
|
|
|
|
|
|
|
static bool |
|
|
|
IsEntrypointAvailable(VADisplay dpy, VAProfile i_profile, |
|
|
|
VAEntrypoint entrypoint) |
|
|
|
{ |
|
|
|
VAEntrypoint * entrypoints; |
|
|
|
int num_entrypoints = vaMaxNumEntrypoints(dpy); |
|
|
|
bool ret = false; |
|
|
|
|
|
|
|
if (num_entrypoints <= 0) |
|
|
|
return false; |
|
|
|
entrypoints = malloc(num_entrypoints * sizeof(VAEntrypoint)); |
|
|
|
|
|
|
|
if (!entrypoints) |
|
|
|
return false; |
|
|
|
|
|
|
|
VAStatus status = |
|
|
|
vaQueryConfigEntrypoints(dpy, i_profile, entrypoints, &num_entrypoints); |
|
|
|
if (status != VA_STATUS_SUCCESS) |
|
|
|
goto error; |
|
|
|
|
|
|
|
for (int i = 0; i < num_entrypoints; ++i) |
|
|
|
if (entrypoint == entrypoints[i]) |
|
|
|
{ |
|
|
|
ret = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
error: |
|
|
|
free(entrypoints); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
VAConfigID |
|
|
|
vlc_vaapi_CreateConfigChecked(vlc_object_t *o, VADisplay dpy, |
|
|
|
VAProfile i_profile, VAEntrypoint entrypoint, |
|
|
|
int va_force_fourcc) |
|
|
|
{ |
|
|
|
if (!IsVaProfileSupported(dpy, i_profile)) |
|
|
|
{ |
|
|
|
msg_Err(o, "profile(%d) is not supported", i_profile); |
|
|
|
return VA_INVALID_ID; |
|
|
|
} |
|
|
|
if (!IsEntrypointAvailable(dpy, i_profile, entrypoint)) |
|
|
|
{ |
|
|
|
msg_Err(o, "entrypoint(%d) is not available", entrypoint); |
|
|
|
return VA_INVALID_ID; |
|
|
|
} |
|
|
|
|
|
|
|
/* Create a VA configuration */ |
|
|
|
VAConfigAttrib attrib = { |
|
|
|
.type = VAConfigAttribRTFormat, |
|
|
|
}; |
|
|
|
if (vaGetConfigAttributes(dpy, i_profile, entrypoint, &attrib, 1)) |
|
|
|
{ |
|
|
|
msg_Err(o, "vaGetConfigAttributes failed"); |
|
|
|
return VA_INVALID_ID; |
|
|
|
} |
|
|
|
|
|
|
|
/* Not sure what to do if not, I don't have a way to test */ |
|
|
|
if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) |
|
|
|
{ |
|
|
|
msg_Err(o, "config doesn't support VA_RT_FORMAT_YUV420"); |
|
|
|
return VA_INVALID_ID; |
|
|
|
} |
|
|
|
|
|
|
|
unsigned int num_sattribs; |
|
|
|
VASurfaceAttrib *sattribs = NULL; |
|
|
|
VAConfigID va_config_id = VA_INVALID_ID; |
|
|
|
VA_CALL(o, vaCreateConfig, dpy, i_profile, entrypoint, &attrib, 1, |
|
|
|
&va_config_id); |
|
|
|
|
|
|
|
if (va_force_fourcc == 0) |
|
|
|
return va_config_id; |
|
|
|
|
|
|
|
/* Fetch VASurfaceAttrib list to make sure the decoder can output NV12 */ |
|
|
|
if (vaQuerySurfaceAttributes(dpy, va_config_id, NULL, &num_sattribs) |
|
|
|
!= VA_STATUS_SUCCESS) |
|
|
|
goto error; |
|
|
|
|
|
|
|
sattribs = malloc(num_sattribs * sizeof(*sattribs)); |
|
|
|
if (sattribs == NULL) |
|
|
|
goto error; |
|
|
|
if (vaQuerySurfaceAttributes(dpy, va_config_id, sattribs, &num_sattribs) |
|
|
|
!= VA_STATUS_SUCCESS) |
|
|
|
goto error; |
|
|
|
|
|
|
|
for (unsigned i = 0; i < num_sattribs; ++i) |
|
|
|
{ |
|
|
|
VASurfaceAttrib *sattrib = &sattribs[i]; |
|
|
|
if (sattrib->type == VASurfaceAttribPixelFormat |
|
|
|
&& sattrib->flags & VA_SURFACE_ATTRIB_SETTABLE |
|
|
|
&& sattrib->value.value.i == va_force_fourcc) |
|
|
|
{ |
|
|
|
free(sattribs); |
|
|
|
return va_config_id; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
error: |
|
|
|
free(sattribs); |
|
|
|
if (va_config_id != VA_INVALID_ID) |
|
|
|
{ |
|
|
|
msg_Err(o, "config doesn't support forced fourcc"); |
|
|
|
vlc_vaapi_DestroyConfig(o, dpy, va_config_id); |
|
|
|
} |
|
|
|
return VA_INVALID_ID; |
|
|
|
} |
|
|
|
|
|
|
|
struct vaapi_pic_ctx |
|
|
|
{ |
|
|
|
picture_context_t s; |
|
|
|
VASurfaceID surface; |
|
|
|
picture_t *picref; |
|
|
|
}; |
|
|
|
|
|
|
|
struct pic_sys_vaapi_instance |
|
|
|
{ |
|
|
|
atomic_int refcount; |
|
|
|
VADisplay dpy; |
|
|
|
unsigned num_render_targets; |
|
|
|
VASurfaceID render_targets[]; |
|
|
|
}; |
|
|
|
|
|
|
|
struct picture_sys_t |
|
|
|
{ |
|
|
|
struct pic_sys_vaapi_instance *instance; |
|
|
|
struct vaapi_pic_ctx ctx; |
|
|
|
}; |
|
|
|
|
|
|
|
static void |
|
|
|
pool_pic_destroy_cb(picture_t *pic) |
|
|
|
{ |
|
|
|
picture_sys_t *p_sys = pic->p_sys; |
|
|
|
struct pic_sys_vaapi_instance *instance = p_sys->instance; |
|
|
|
|
|
|
|
if (atomic_fetch_sub(&instance->refcount, 1) == 1) |
|
|
|
{ |
|
|
|
vaDestroySurfaces(instance->dpy, instance->render_targets, |
|
|
|
instance->num_render_targets); |
|
|
|
vlc_vaapi_ReleaseInstance(instance->dpy); |
|
|
|
free(instance); |
|
|
|
} |
|
|
|
free(pic->p_sys); |
|
|
|
free(pic); |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
pic_ctx_destroy_cb(struct picture_context_t *opaque) |
|
|
|
{ |
|
|
|
struct vaapi_pic_ctx *ctx = (struct vaapi_pic_ctx *) opaque; |
|
|
|
picture_Release(ctx->picref); |
|
|
|
free(opaque); |
|
|
|
} |
|
|
|
|
|
|
|
static struct picture_context_t * |
|
|
|
pic_ctx_copy_cb(struct picture_context_t *opaque) |
|
|
|
{ |
|
|
|
struct vaapi_pic_ctx *src_ctx = (struct vaapi_pic_ctx *) opaque; |
|
|
|
struct vaapi_pic_ctx *dst_ctx = malloc(sizeof *dst_ctx); |
|
|
|
if (dst_ctx == NULL) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
dst_ctx->s.destroy = pic_ctx_destroy_cb; |
|
|
|
dst_ctx->s.copy = pic_ctx_copy_cb; |
|
|
|
dst_ctx->surface = src_ctx->surface; |
|
|
|
dst_ctx->picref = picture_Hold(src_ctx->picref); |
|
|
|
return &dst_ctx->s; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
pic_sys_ctx_destroy_cb(struct picture_context_t *opaque) |
|
|
|
{ |
|
|
|
(void) opaque; |
|
|
|
} |
|
|
|
|
|
|
|
picture_pool_t * |
|
|
|
vlc_vaapi_PoolNew(vlc_object_t *o, VADisplay dpy, unsigned count, |
|
|
|
VASurfaceID **render_targets, |
|
|
|
const video_format_t *restrict fmt, |
|
|
|
unsigned va_rt_format, int va_force_fourcc) |
|
|
|
{ |
|
|
|
struct pic_sys_vaapi_instance *instance = |
|
|
|
malloc(sizeof(*instance) + count * sizeof(VASurfaceID)); |
|
|
|
if (!instance) |
|
|
|
return NULL; |
|
|
|
instance->num_render_targets = count; |
|
|
|
atomic_init(&instance->refcount, 0); |
|
|
|
|
|
|
|
VASurfaceAttrib *attribs = NULL; |
|
|
|
unsigned num_attribs = 0; |
|
|
|
VASurfaceAttrib fourcc_attribs[1] = { |
|
|
|
{ |
|
|
|
.type = VASurfaceAttribPixelFormat, |
|
|
|
.flags = VA_SURFACE_ATTRIB_SETTABLE, |
|
|
|
.value.type = VAGenericValueTypeInteger, |
|
|
|
.value.value.i = va_force_fourcc, |
|
|
|
} |
|
|
|
}; |
|
|
|
if (va_force_fourcc != 0) |
|
|
|
{ |
|
|
|
attribs = fourcc_attribs; |
|
|
|
num_attribs = 1; |
|
|
|
} |
|
|
|
|
|
|
|
picture_t *pics[count]; |
|
|
|
|
|
|
|
VA_CALL(o, vaCreateSurfaces, dpy, va_rt_format, |
|
|
|
fmt->i_visible_width, fmt->i_visible_height, |
|
|
|
instance->render_targets, instance->num_render_targets, |
|
|
|
attribs, num_attribs); |
|
|
|
|
|
|
|
for (unsigned i = 0; i < count; i++) |
|
|
|
{ |
|
|
|
picture_sys_t *p_sys = malloc(sizeof *p_sys); |
|
|
|
if (p_sys == NULL) |
|
|
|
{ |
|
|
|
count = i; |
|
|
|
goto error_pic; |
|
|
|
} |
|
|
|
p_sys->instance = instance; |
|
|
|
p_sys->ctx.s.destroy = pic_sys_ctx_destroy_cb; |
|
|
|
p_sys->ctx.s.copy = pic_ctx_copy_cb; |
|
|
|
p_sys->ctx.surface = instance->render_targets[i]; |
|
|
|
p_sys->ctx.picref = NULL; |
|
|
|
picture_resource_t rsc = { |
|
|
|
.p_sys = p_sys, |
|
|
|
.pf_destroy = pool_pic_destroy_cb, |
|
|
|
}; |
|
|
|
pics[i] = picture_NewFromResource(fmt, &rsc); |
|
|
|
if (pics[i] == NULL) |
|
|
|
{ |
|
|
|
free(p_sys); |
|
|
|
count = i; |
|
|
|
goto error_pic; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
picture_pool_t *pool = picture_pool_New(count, pics); |
|
|
|
if (!pool) |
|
|
|
goto error_pic; |
|
|
|
|
|
|
|
atomic_store(&instance->refcount, count); |
|
|
|
instance->dpy = vlc_vaapi_GetInstance(); /* Hold a reference on VADisplay */ |
|
|
|
assert(instance->dpy == dpy); |
|
|
|
|
|
|
|
*render_targets = instance->render_targets; |
|
|
|
return pool; |
|
|
|
|
|
|
|
error_pic: |
|
|
|
while (count > 0) |
|
|
|
picture_Release(pics[--count]); |
|
|
|
|
|
|
|
VA_CALL(o, vaDestroySurfaces, instance->dpy, instance->render_targets, |
|
|
|
instance->num_render_targets); |
|
|
|
|
|
|
|
error: |
|
|
|
free(instance); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
unsigned |
|
|
|
vlc_vaapi_PicSysGetRenderTargets(picture_sys_t *sys, |
|
|
|
VASurfaceID **render_targets) |
|
|
|
{ |
|
|
|
assert(sys && sys->instance); |
|
|
|
*render_targets = sys->instance->render_targets; |
|
|
|
return sys->instance->num_render_targets; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
vlc_vaapi_PicAttachContext(picture_t *pic) |
|
|
|
{ |
|
|
|
assert(pic->p_sys != NULL); |
|
|
|
assert(pic->context == NULL); |
|
|
|
|
|
|
|
pic->p_sys->ctx.picref = pic; |
|
|
|
pic->context = &pic->p_sys->ctx.s; |
|
|
|
} |
|
|
|
|
|
|
|
VASurfaceID |
|
|
|
vlc_vaapi_PicGetSurface(picture_t *pic) |
|
|
|
{ |
|
|
|
assert(pic->context); |
|
|
|
|
|
|
|
return ((struct vaapi_pic_ctx *)pic->context)->surface; |
|
|
|
} |
|
|
|
|