Commit 2080066f authored by Jean-Paul Saman's avatar Jean-Paul Saman

VAAPI-XCB: Move VASubpicture code into a vasub.{c,h} file.

Split up vaapi_x11.c to easy adding a vaapi_glx.c. All common functionality wrt to
VASubpictures have been moved to a new file vasub.{c,h}.
parent 62ba8127
......@@ -20,6 +20,8 @@ libavcodec_plugin_la_SOURCES = \
if HAVE_XCB
libavcodec_plugin_la_SOURCES += \
vasub.h \
vasub.c \
vaapi_x11.c \
../../video_output/xcb/events.c \
../../video_output/xcb/xcb_events_vlc.h \
......
......@@ -24,8 +24,6 @@
#ifndef _VLC_VA_H
#define _VLC_VA_H 1
typedef struct vlc_va_conn_t vlc_va_conn_t;
typedef struct vlc_va_ops_t vlc_va_ops_t;
typedef struct vlc_va_t vlc_va_t;
struct vlc_va_t {
......
......@@ -25,6 +25,7 @@
* the created surfaces for use with decoding
* context.
*/
typedef struct vlc_va_conn_t vlc_va_conn_t;
struct vlc_va_conn_t
{
VADisplay p_display;
......
......@@ -3,7 +3,7 @@
* @brief VA API using X11 video output module for VLC media player
*/
/*****************************************************************************
* Copyright © 2011, M2X BV
* Copyright © 2011 - 2012, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
......@@ -57,6 +57,7 @@
#include <va/va_x11.h>
#include "va.h"
#include "vaapi.h"
#include "vasub.h"
#include <X11/Xutil.h>
......@@ -104,150 +105,7 @@ static void DisplayPicture(vout_display_t *, picture_t *, subpicture_t *);
static int Control(vout_display_t *, int, va_list);
static void Manage(vout_display_t *);
/* subpicture region cache management */
static unsigned int cache_counter = 0;
typedef struct
{
VASubpictureID i_id;
VAImage image;
struct {
int i_x, i_y;
int i_width, i_height;
} rect;
} vasubpicture_cache_t;
typedef struct
{
unsigned int i_id;
unsigned int i_refcount; /* refcount usage */
mtime_t i_start; /* start display date */
mtime_t i_stop; /* stop display date */
vlc_array_t subpictures; /* subpicture regions */
} subpicture_cache_t;
static vasubpicture_cache_t *cache_SubpictureCreate(vout_display_t *vd, const subpicture_t *subpicture,
const subpicture_region_t *region, const int flags);
static void cache_SubpictureDestroy(vout_display_t *vd, vlc_va_conn_t *conn, subpicture_cache_t *cache);
static subpicture_cache_t *cache_new(void)
{
subpicture_cache_t *cache = calloc(1, sizeof(subpicture_cache_t));
if (cache == NULL)
return NULL;
cache->i_id = ++cache_counter;
assert(cache_counter > 0);
vlc_array_init(&cache->subpictures);
return cache;
}
static void cache_destroy(subpicture_cache_t *cache)
{
assert(cache);
assert(cache->i_refcount == 0);
assert(vlc_array_count(&cache->subpictures) == 0);
vlc_array_clear(&cache->subpictures);
free(cache);
}
static subpicture_cache_t *cache_find(vlc_array_t *cache, const unsigned int id)
{
const int count = vlc_array_count(cache);
for (int i = 0; i < count; i++)
{
subpicture_cache_t *c = (subpicture_cache_t *) vlc_array_item_at_index(cache, i);
if (c)
{
if (c->i_id == id)
return c;
}
}
return NULL;
}
static inline void cache_ref(subpicture_cache_t *cache)
{
assert(cache);
cache->i_refcount++;
}
static inline void cache_unref(subpicture_cache_t *cache)
{
assert(cache);
assert(cache->i_refcount > 0);
if (cache->i_refcount > 0)
cache->i_refcount--;
}
static vasubpicture_cache_t *subpicture_cache_new( void )
{
vasubpicture_cache_t *cache = calloc(1, sizeof(vasubpicture_cache_t));
if (cache == NULL)
return NULL;
cache->i_id = VA_INVALID_ID;
cache->image.buf = VA_INVALID_ID;
cache->image.image_id = VA_INVALID_ID;
return cache;
}
static void subpicture_cache_destroy(vasubpicture_cache_t *cache)
{
assert(cache);
assert(cache->i_id == VA_INVALID_ID);
assert(cache->image.buf == VA_INVALID_ID);
assert(cache->image.image_id == VA_INVALID_ID);
free(cache);
}
/* VA API support functions */
static int VASubtitleFourCC(vout_display_t *vd)
{
vout_display_sys_t *sys = (vout_display_sys_t *) vd->sys;
sys->conn->lock();
int num = vaMaxNumSubpictureFormats(sys->conn->p_display);
if (num <= 0)
goto out_warning;
VAImageFormat *p_fmt = calloc(num, sizeof(*p_fmt));
unsigned int *flags = calloc(num, sizeof(*flags));
if (!p_fmt || !flags)
goto out;
unsigned int num_fmt = 0;
VAStatus status = vaQuerySubpictureFormats(sys->conn->p_display,
p_fmt, flags, &num_fmt);
if (status != VA_STATUS_SUCCESS)
goto out;
for (unsigned int i = 0; i < num_fmt; i++)
{
VAImageFormat format = p_fmt[i];
if (format.fourcc == VA_FOURCC_RGBA)
{
sys->sflags = flags[i];
memcpy((void*)&sys->sub_fmt, (void*)&format, sizeof(VAImageFormat));
free(p_fmt);
free(flags);
sys->conn->unlock();
return VLC_SUCCESS;
}
}
out:
free(p_fmt);
free(flags);
out_warning:
msg_Warn(vd, "VAAP-X11 does not support fourcc RGBA subtitles");
sys->conn->unlock();
return VLC_EGENERIC;
}
static int FindVAFourCC(vout_display_t *vd)
{
vout_display_sys_t *sys = (vout_display_sys_t *) vd->sys;
......@@ -466,13 +324,15 @@ int OpenVaapiX11(vlc_object_t *obj)
info.has_hide_mouse = false;
/* Setup vout_display_t once everything is fine */
if (VASubtitleFourCC(vd) == VLC_SUCCESS)
if (VASubtitleFourCC(sys->conn, VA_FOURCC_RGBA, &sys->sub_fmt, &sys->sflags) == VLC_SUCCESS)
{
info.subpicture_chromas = va_subpicture_chromas;
vd->prepare = Render;
}
else
{
msg_Warn(vd, "VAAP-XCB does not support fourcc RGBA subtitles");
vd->prepare = NULL;
info.subpicture_chromas = NULL;
}
......@@ -520,12 +380,8 @@ void CloseVaapiX11(vlc_object_t *obj)
vlc_array_remove(&sys->cache, i);
if (cache->i_refcount > 0)
cache_unref(cache);
msg_Info(vd, "cache id %d refcount %d", cache->i_id, cache->i_refcount);
cache_SubpictureDestroy(vd, sys->conn, cache);
cache_destroy(cache);
cache = NULL;
cache_SubpictureRelease(obj, sys->conn, &sys->cache, cache_GetCacheID(cache));
cache_SubpictureDestroy(obj, sys->conn, cache, true);
}
vlc_array_clear(&sys->cache);
......@@ -661,585 +517,6 @@ static int Control (vout_display_t *vd, int query, va_list ap)
}
}
static inline void vaapi_fixup_alpha(uint8_t *p_pixel, size_t i_size)
{
for (size_t p = 0; p < i_size; p += 4)
{
/* RGBA */
uint8_t *R = &p_pixel[p];
uint8_t *G = &p_pixel[p+1];
uint8_t *B = &p_pixel[p+2];
uint8_t *A = &p_pixel[p+3];
if (*A != 255) /* alpha transparancy */
{
*A = 0; /* 0 = transparent, 255 = opaque */
*R = 0;
*G = 0;
*B = 0;
}
}
}
static int CopyPictureToVAImage(vout_display_t *vd, picture_t *pic,
VAImage *image, VAImageFormat *fmt,
const bool fixup_alpha)
{
vout_display_sys_t *sys = vd->sys;
VAStatus status;
status = vaCreateImage(sys->conn->p_display, fmt,
pic->format.i_width, pic->format.i_height, image);
if (status != VA_STATUS_SUCCESS)
return VLC_EGENERIC;
/* Sanity checks */
if ((image->image_id == VA_INVALID_ID) ||
(image->format.fourcc != VA_FOURCC_RGBA) ||
(image->num_planes != (unsigned int) pic->i_planes) ||
(image->height != pic->format.i_height) ||
(image->width != pic->format.i_width) ||
(image->format.bits_per_pixel != pic->format.i_bits_per_pixel))
{
msg_Err(vd, "subpicture region: planes %d/%d, depth %d, width %d/%d, height %d/%d, bpp %d/%d",
image->num_planes, pic->i_planes,
image->format.depth,
image->width, pic->format.i_width,
image->height, pic->format.i_height,
image->format.bits_per_pixel, pic->format.i_bits_per_pixel);
goto out_va_free;
}
assert(image->image_id != VA_INVALID_ID);
assert(image->buf != VA_INVALID_ID);
void *p_base = NULL;
status = vaMapBuffer(sys->conn->p_display, image->buf, &p_base);
if (status != VA_STATUS_SUCCESS)
{
msg_Err(vd, "failed mapping image for copying (%d)", status);
msg_Err(vd, "subpicture region: planes %d/%d, depth %d, width %d/%d, height %d/%d, bpp %d/%d",
image->num_planes, pic->i_planes,
image->format.depth,
image->width, pic->format.i_width,
image->height, pic->format.i_height,
image->format.bits_per_pixel, pic->format.i_bits_per_pixel);
goto out_va_free;
}
assert(p_base); /* should not happen */
for (unsigned int i_plane = 0; i_plane < image->num_planes; i_plane++)
{
/* Does the pitch match? Then just copy it directly */
if (image->pitches[i_plane] == (unsigned int)pic->p[i_plane].i_pitch)
{
vlc_memcpy((uint8_t*)(((uint8_t*)p_base) + image->offsets[i_plane]),
pic->p[i_plane].p_pixels,
pic->p[i_plane].i_pitch * pic->p[i_plane].i_visible_lines);
}
else
{
/* proceed line by line */
uint8_t *p_in = pic->p[i_plane].p_pixels;
uint8_t *p_out = (uint8_t *)(((uint8_t*)p_base) + image->offsets[i_plane]);
int i_line = pic->format.i_y_offset * pic->p[i_plane].i_visible_pitch;
for (; i_line < pic->p[i_plane].i_visible_lines; i_line++)
{
/* NOTE: image->pitch[i_plane] includes image->offset[i_plane], thus
* visible_pitch = image->pitch[i_plane] - image->offset[i_plane] */
size_t i_size = __MIN(image->pitches[i_plane],
(unsigned int)pic->p[i_plane].i_pitch);
vlc_memcpy(&p_out[i_line * image->pitches[i_plane]], &p_in[i_line * pic->p[i_plane].i_pitch], i_size);
/* FIXME: Remove this code when VDPAU and XvBA backends for
* VAAPI properly support alpha or chroma keying in subpictures. */
if (fixup_alpha)
{
uint8_t *p_pixel = p_out + (i_line * image->pitches[i_plane]);
vaapi_fixup_alpha(p_pixel, i_size);
}
}
}
}
status = vaUnmapBuffer(sys->conn->p_display, image->buf);
if (status != VA_STATUS_SUCCESS)
{
msg_Err(vd, "failed unmapping image for copying (%d)", status);
goto out_va_free;
}
return VLC_SUCCESS;
out_va_free:
msg_Err(vd, "failed to copy region into vaImage id=%d", image->image_id);
vaDestroyImage(sys->conn->p_display, image->image_id);
image->image_id = VA_INVALID_ID;
image->buf = VA_INVALID_ID;
return VLC_EGENERIC;
}
/* Create subpicture cache for this subpicture and
* allocate needed VA API resources. Call this
* function when holding cache->lock */
static vasubpicture_cache_t *cache_SubpictureCreate(vout_display_t *vd, const subpicture_t *subpicture,
const subpicture_region_t *region, const int flags)
{
vout_display_sys_t *sys = (vout_display_sys_t *) vd->sys;
int canc = vlc_savecancel();
sys->conn->lock();
/* Copy pixels from region into VAImage */
vasubpicture_cache_t *vasub_cache = subpicture_cache_new();
if (!vasub_cache)
goto error;
VAImage *image = &vasub_cache->image;
image->image_id = VA_INVALID_ID;
image->buf = VA_INVALID_ID;
int err = CopyPictureToVAImage(vd, region->p_picture, image, &sys->sub_fmt,
(sys->sflags == 0x0) ? true : false);
if (err != VLC_SUCCESS)
{
subpicture_cache_destroy(vasub_cache);
goto error;
}
VAStatus status;
status = vaCreateSubpicture(sys->conn->p_display, image->image_id, &vasub_cache->i_id);
if (status != VA_STATUS_SUCCESS)
goto cleanup;
bool b_alpha = (flags & VA_SUBPICTURE_GLOBAL_ALPHA);
bool b_chroma = (flags & VA_SUBPICTURE_CHROMA_KEYING);
if (b_alpha)
{
assert(!b_chroma);
float global_alpha = (float)(subpicture->i_alpha * region->i_alpha / 255) / 255;
status = vaSetSubpictureGlobalAlpha(sys->conn->p_display, vasub_cache->i_id, global_alpha);
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed applying alpha value to subpicture");
}
if (b_chroma)
{
assert(!b_alpha);
/* chromakey values */
const unsigned int min = 0x0; /* ? */
const unsigned int max = 0x0000ff00; /* ? */
const unsigned int mask = (region->p_picture->format.i_rmask << 24) |
(region->p_picture->format.i_gmask << 16) |
(region->p_picture->format.i_bmask << 8) | 0;
status = vaSetSubpictureChromakey(sys->conn->p_display, vasub_cache->i_id,
min, max, mask);
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed applying chroma valuesto subpicture");
}
vasub_cache->rect.i_x = region->i_x;
vasub_cache->rect.i_y = region->i_y;
vasub_cache->rect.i_width = region->fmt.i_visible_width;
vasub_cache->rect.i_height = region->fmt.i_visible_height;
sys->conn->unlock();
vlc_restorecancel(canc);
return vasub_cache;
cleanup:
vaDestroyImage(sys->conn->p_display, image->image_id);
vasub_cache->i_id = VA_INVALID_ID;
image->image_id = VA_INVALID_ID;
image->buf = VA_INVALID_ID;
subpicture_cache_destroy(vasub_cache);
vasub_cache = NULL;
error:
sys->conn->unlock();
vlc_restorecancel(canc);
return NULL;
}
/* Destroy subpicture cache for this subpicture and
* release allocated VA API resources. Call this
* function when holding cache->lock */
static void cache_SubpictureDestroy(vout_display_t *vd, vlc_va_conn_t *conn, subpicture_cache_t *cache)
{
VLC_UNUSED(vd);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "cache_SubpictureDestroy cache %d", cache->i_id);
#endif
const int num = vlc_array_count(&cache->subpictures);
for (int i = num - 1; i >= 0; i--)
{
vasubpicture_cache_t *subpic;
subpic = (vasubpicture_cache_t *)vlc_array_item_at_index(&cache->subpictures, i);
if (!subpic)
continue;
vlc_array_remove(&cache->subpictures, i);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "- %d: sub_id %d, image_id %d",
i, subpic->i_id, subpic->image.image_id);
#endif
assert(subpic->i_id != VA_INVALID_ID);
assert(subpic->image.image_id != VA_INVALID_ID);
int canc = vlc_savecancel();
conn->lock();
vaDestroySubpicture(conn->p_display, subpic->i_id);
vaDestroyImage(conn->p_display, subpic->image.image_id);
subpic->i_id = VA_INVALID_ID;
subpic->image.buf = VA_INVALID_ID;
subpic->image.image_id = VA_INVALID_ID;
conn->unlock();
vlc_restorecancel(canc);
subpicture_cache_destroy(subpic);
subpic = NULL;
}
}
static int SubpictureRegionsLink(vout_display_t *vd, const picture_t *picture)
{
vout_display_sys_t *sys = vd->sys;
assert(picture->p_sys);
picture_sys_t *surface = picture->p_sys;
assert(surface->i_id != VA_INVALID_SURFACE);
vlc_mutex_lock(&sys->cache_lock);
subpicture_cache_t *cache = (subpicture_cache_t *) cache_find(&sys->cache, surface->i_cache);
if (cache == NULL)
goto error;
assert(cache->i_id > 0);
const int count = vlc_array_count(&cache->subpictures);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, " link surface %d cache %d refcount %d",
surface->i_id, cache->i_id, cache->i_refcount);
#endif
int i_sub = 0;
for (int i = 0; i < count; i++)
{
vasubpicture_cache_t *vasubpic;
vasubpic = (vasubpicture_cache_t *) vlc_array_item_at_index(&cache->subpictures, i);
if (!vasubpic)
goto cleanup;
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "+ %d: subid %d", i_sub, vasubpic->i_id);
#endif
/* NOTE: The video output core renders subpictures to picture size.
* in case of VAAPI SURFACES this is always the original picture size.
* Subpictures must be placed on the VAAPI SURFACE and vaPutSurface()
* does the final scaling.
*/
/* subpicture positioning */
unsigned int src_x = 0;
unsigned int src_w = src_x + vasubpic->rect.i_width;
unsigned int src_y = 0;
unsigned int src_h = src_y + vasubpic->rect.i_height;
sys->conn->lock();
/* Associate subpicture with surface */
VAStatus status;
status = vaAssociateSubpicture(sys->conn->p_display, vasubpic->i_id,
&surface->i_id, 1, /* array of surfaces */
/* src rectangle */
src_x, src_y, src_w, src_h,
/* dest rectangle */
vasubpic->rect.i_x, vasubpic->rect.i_y,
vasubpic->rect.i_width,
vasubpic->rect.i_height,
sys->sflags);
sys->conn->unlock();
if (status != VA_STATUS_SUCCESS)
goto cleanup;
vasubpic = NULL;
i_sub++;
}
vlc_mutex_unlock(&sys->cache_lock);
return VLC_SUCCESS;
cleanup:
/* Cleanup linked subpictures */
#ifdef VAAPI_DEBUG
msg_Err(vd, "unlink surface %d cache %d", surface->i_id, cache->i_id);
#endif
for (int i = 0; i < i_sub; i++)
{
vasubpicture_cache_t *sub = vlc_array_item_at_index(&cache->subpictures, i);
assert(sub);
if (sub->i_id != VA_INVALID_ID)
{
VAStatus status = VA_STATUS_ERROR_UNKNOWN;
sys->conn->lock();
status = vaDeassociateSubpicture(sys->conn->p_display,
sub->i_id, &surface->i_id, 1);
sys->conn->unlock();
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed unlinking subid: %d (error %d)",
sub->i_id, status);
}
}
error:
vlc_mutex_unlock(&sys->cache_lock);
return VLC_EGENERIC;
}
static void SubpictureRegionsUnlink(vout_display_t *vd, picture_t *picture)
{
vout_display_sys_t *sys = vd->sys;
picture_sys_t *surface = picture->p_sys;
assert(surface);
assert(surface->i_id != VA_INVALID_SURFACE);
vlc_mutex_lock(&sys->cache_lock);
assert(surface->i_cache > 0);
subpicture_cache_t *cache = cache_find(&sys->cache, surface->i_cache);
if (cache == NULL)
goto error;
assert(cache->i_id > 0);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "unlink surface %d cache %d size %d",
surface->i_id, surface->i_cache, count);
#endif
const int count = vlc_array_count(&cache->subpictures);
for (int i = 0; i < count; i++)
{
vasubpicture_cache_t *sub = vlc_array_item_at_index(&cache->subpictures, i);
assert(sub);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "- %u: subid %d", i, sub->i_id);
#endif
VAStatus status = VA_STATUS_ERROR_UNKNOWN;
if (sub->i_id != VA_INVALID_ID)
{
sys->conn->lock();
status = vaDeassociateSubpicture(sys->conn->p_display,
sub->i_id, &surface->i_id, 1);
sys->conn->unlock();
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed unlinking subid: %d (error %d)",
sub->i_id, status);
}
}
error:
vlc_mutex_unlock(&sys->cache_lock);
}
/* NOTE: call with sys->cache_lock held */
static void cache_SubpictureRelease(vout_display_t *vd, const int cache_id)
{
vout_display_sys_t *sys = vd->sys;
assert(cache_id > 0);
subpicture_cache_t *cache = cache_find(&sys->cache, cache_id);
if (cache)
{
#ifdef VAAPI_DEBUG
msg_Info(vd, "Release cache %d count %d refcount %d",
cache->i_id, vlc_array_count(&sys->cache), cache->i_refcount);
#endif
cache_unref(cache);
/* Do we still need need this subpicture cache ? */
if (cache->i_refcount == 0)
{
#ifdef VAAPI_DEBUG
msg_Info(vd, "Deleting subpicture cache %d refcount %d",
cache->i_id, cache->i_refcount);
#endif
int index = vlc_array_index_of_item(&sys->cache, (void*)cache);
vlc_array_remove(&sys->cache, index);
cache_SubpictureDestroy(vd, sys->conn, cache);
cache_destroy(cache);
cache = NULL;
}
}
}
/* NOTE: call with sys->cache_lock held */
static void cache_SubpictureHold(vout_display_t *vd, const int cache_id)
{
vout_display_sys_t *sys = vd->sys;
subpicture_cache_t *cache = cache_find(&sys->cache, cache_id);
if (cache)
{
#ifdef VAAPI_DEBUG
msg_Info(vd, "Subpicture cache %d refcount %d",
cache->i_id, cache->i_refcount);
#endif
cache_ref(cache);
}
}
static int RenderDirectSubpicture(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
vout_display_sys_t *sys = vd->sys;
picture_sys_t *surface = picture->p_sys;
subpicture_cache_t *cache = cache_new();
if (!cache)
return VLC_EGENERIC;
cache->i_start = subpicture->i_start;
cache->i_stop = subpicture->i_stop;
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "new subpicture %p prev %p cache %d surface %d",
subpicture, sys->render.prev_subpicture, cache->i_id, surface->i_id);
#endif
int i_sub = 0;
subpicture_region_t *region;
for (region = subpicture->p_region; region != NULL; region = region->p_next)
{
/* Skip non-RGBA regions for now */
if (region->fmt.i_chroma != VLC_CODEC_RGBA)
continue;
assert(region->p_picture->format.i_width > 0);
assert(region->p_picture->format.i_height > 0);
/* Create Subpicture */
vasubpicture_cache_t *vasub_cache;
vasub_cache = cache_SubpictureCreate(vd, subpicture, region, sys->sflags);
if (vasub_cache == NULL)
goto cleanup;
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "+ %d: subid %d", i_sub, vasub_cache->i_id);
#endif
vlc_array_append(&cache->subpictures, (void *)vasub_cache);
vasub_cache = NULL;
i_sub++;
}
/* remember what was created, so it can be released in DisplayPicture() */
vlc_mutex_lock(&sys->cache_lock);
vlc_array_append(&sys->cache, (void *)cache);
cache_ref(cache);
sys->render.i_cache = cache->i_id;
surface->i_cache = cache->i_id;
vlc_mutex_unlock(&sys->cache_lock);
return VLC_SUCCESS;
cleanup:
msg_Err(vd, "failed creating subpicture cache %d for surface %d",
cache->i_id, surface->i_id);
cache_SubpictureDestroy(vd, sys->conn, cache);
cache_destroy(cache);
cache = NULL;
return VLC_EGENERIC;
}
static int RenderCachedSubpictures(vout_display_t *vd, picture_t *picture,
subpicture_t *subpicture)
{
vout_display_sys_t *sys = vd->sys;
assert(picture);
assert(subpicture);
picture_sys_t *surface = picture->p_sys;
vlc_mutex_lock(&sys->cache_lock);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "reuse subpicture %p prev %p cache %d surface %d",
subpicture, sys->render.prev_subpicture,
sys->render.i_cache, surface->i_id);
#endif
subpicture_cache_t *cache = (subpicture_cache_t *) cache_find(&sys->cache, sys->render.i_cache);
if (cache == NULL)
goto cleanup;
assert(cache->i_id > 0);
#if 0
if ((cache->i_start != subpicture->i_start) &&
(cache->i_stop != subpicture->i_stop))
goto cleanup;
#endif
/* Check if all subpictures region pictures match */
const int count = vlc_array_count(&cache->subpictures);
int i_sub = 0;
subpicture_region_t *region;
for (region = subpicture->p_region; region != NULL; region = region->p_next)
{
/* Skip non-RGBA regions */
if (region->fmt.i_chroma != VLC_CODEC_RGBA)
continue;
/* Ooh: big booboo */
if (i_sub >= count)
abort();
vasubpicture_cache_t *vasubpic;
vasubpic = (vasubpicture_cache_t *) vlc_array_item_at_index(&cache->subpictures, i_sub);
if (!vasubpic)
goto cleanup;
if ((vasubpic->image.width != region->fmt.i_width) ||
(vasubpic->image.height != region->fmt.i_height))
goto cleanup;
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "+ %d: subid %d", i_sub, vasubpic->i_id);
#endif
vasubpic = NULL;
i_sub++;
}
/* FIXME: should not happen */
if (i_sub < count)
goto cleanup;
picture->p_sys->i_cache = cache->i_id;
cache_ref(cache);
vlc_mutex_unlock(&sys->cache_lock);
return VLC_SUCCESS;
cleanup:
/* Cannot reuse this cached subpicture */
msg_Warn(vd, "cannot reuse cached subpicture %d for surface %d -- resetting",
cache->i_id, surface->i_id);
vlc_mutex_unlock(&sys->cache_lock);
return VLC_EGENERIC;
}
static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
vout_display_sys_t *sys = vd->sys;
......@@ -1267,33 +544,51 @@ static void Render(vout_display_t *vd, picture_t *picture, subpicture_t *subpict
if (i_region == 0)
return;
/* */
vlc_mutex_lock(&sys->cache_lock);
/* same as previous subpicture? */
if ((sys->render.prev_subpicture == subpicture) &&
(sys->render.i_cache > 0))
{
if (RenderCachedSubpictures(vd, picture, subpicture) == VLC_SUCCESS)
return;
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "reuse subpicture %p prev %p cache %d surface %d",
subpicture, sys->render.prev_subpicture,
sys->render.i_cache, picture->p_sys->i_id);
#endif
int ret = RenderCachedSubpictures(VLC_OBJECT(vd), sys->conn, &sys->cache,
sys->render.i_cache, picture, subpicture);
if (ret == VLC_SUCCESS)
goto out;
}
/* Release exra reference */
if (sys->render.i_cache > 0)
{
vlc_mutex_lock(&sys->cache_lock);
cache_SubpictureRelease(vd, sys->render.i_cache);
vlc_mutex_unlock(&sys->cache_lock);
}
cache_SubpictureRelease(VLC_OBJECT(vd), sys->conn, &sys->cache, sys->render.i_cache);
/* render new subpicture */
if (RenderDirectSubpicture(vd, picture, subpicture) != VLC_SUCCESS)
return;
subpicture_cache_t *cache = RenderDirectSubpicture(VLC_OBJECT(vd), sys->conn, &sys->sub_fmt,
picture, subpicture, sys->sflags);
if (cache == NULL)
goto out;
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "new subpicture %p prev %p cache %d surface %d",
subpicture, sys->render.prev_subpicture, cache_GetCacheID(cache), surface->i_id);
#endif
/* remember what was created, so it can be released in DisplayPicture() */
vlc_array_append(&sys->cache, (void *)cache);
sys->render.i_cache = cache_GetCacheID(cache);
sys->render.prev_subpicture = subpicture;
sys->render.i_start = subpicture->i_start;
sys->render.i_stop = subpicture->i_stop;
/* Hold extra reference */
vlc_mutex_lock(&sys->cache_lock);
cache_SubpictureHold(vd, sys->render.i_cache);
cache_SubpictureHold(VLC_OBJECT(vd), &sys->cache, cache_GetCacheID(cache));
out:
vlc_mutex_unlock(&sys->cache_lock);
}
......@@ -1305,6 +600,8 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
sys->conn->lock();
int canc = vlc_savecancel();
vout_display_place_t place;
vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
......@@ -1317,11 +614,12 @@ static void DisplayVASurface(vout_display_t *vd, picture_t *picture)
/* NOTE: vaCreateContext() specifies VA_PROGRESSIVE */
picture->b_progressive ? VA_FRAME_PICTURE :
(picture->b_top_field_first ? VA_TOP_FIELD : VA_BOTTOM_FIELD));
if (status != VA_STATUS_SUCCESS)
msg_Err(vd, "failed displaying picture: %d (surface id=%d): %p",
status, surface->i_id, picture);
vlc_restorecancel(canc);
sys->conn->unlock();
}
......@@ -1344,19 +642,26 @@ static void DisplayPicture(vout_display_t *vd, picture_t *picture, subpicture_t
}
if (subpicture)
ret = SubpictureRegionsLink(vd, picture);
{
vlc_mutex_lock(&sys->cache_lock);
ret = SubpictureRegionsLink(VLC_OBJECT(vd), sys->conn, &sys->cache, picture, sys->sflags);
if (ret == VLC_EGENERIC)
msg_Err(vd, "Failed linking subpicture to surface");
vlc_mutex_unlock(&sys->cache_lock);
}
DisplayVASurface(vd, picture);
out:
if (subpicture)
{
if (ret == VLC_SUCCESS)
SubpictureRegionsUnlink(vd, picture);
vlc_mutex_lock(&sys->cache_lock);
if (ret == VLC_SUCCESS)
{
if (SubpictureRegionsUnlink(VLC_OBJECT(vd), sys->conn, &sys->cache, picture) == VLC_EGENERIC)
msg_Err(vd, "Could not find subpicture in cache");
}
if (picture->p_sys->i_cache > 0)
cache_SubpictureRelease(vd, picture->p_sys->i_cache);
cache_SubpictureRelease(VLC_OBJECT(vd), sys->conn, &sys->cache, picture->p_sys->i_cache);
picture->p_sys->i_cache = 0;
vlc_mutex_unlock(&sys->cache_lock);
......
/*****************************************************************************
* Copyright © 2012, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU 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 <stdlib.h>
#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_es.h>
#include <vlc_picture.h>
#include <vlc_subpicture.h>
#include <vlc_arrays.h>
#ifdef HAVE_AVCODEC_VAAPI
#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>
#include <va/va.h>
#include "vaapi.h"
#include "vasub.h"
/* subpicture region cache management */
static unsigned int cache_counter = 0;
struct vasubpicture_cache_t
{
VASubpictureID i_id;
VAImage image;
struct {
int i_x, i_y;
int i_width, i_height;
} rect;
};
struct subpicture_cache_t
{
unsigned int i_id;
unsigned int i_refcount; /* refcount usage */
mtime_t i_start; /* start display date */
mtime_t i_stop; /* stop display date */
vlc_array_t subpictures; /* subpicture regions */
};
/* */
static subpicture_cache_t *cache_new(void)
{
subpicture_cache_t *cache = calloc(1, sizeof(subpicture_cache_t));
if (cache == NULL)
return NULL;
cache->i_id = ++cache_counter;
assert(cache_counter > 0);
vlc_array_init(&cache->subpictures);
return cache;
}
static void cache_destroy(subpicture_cache_t *cache)
{
assert(cache);
assert(cache->i_refcount == 0);
assert(vlc_array_count(&cache->subpictures) == 0);
vlc_array_clear(&cache->subpictures);
free(cache);
}
static subpicture_cache_t *cache_find(vlc_array_t *cache, const unsigned int id)
{
const int count = vlc_array_count(cache);
for (int i = 0; i < count; i++)
{
subpicture_cache_t *c = (subpicture_cache_t *) vlc_array_item_at_index(cache, i);
if (c)
{
if (c->i_id == id)
return c;
}
}
return NULL;
}
static inline void cache_ref(subpicture_cache_t *cache)
{
assert(cache);
cache->i_refcount++;
}
static inline void cache_unref(subpicture_cache_t *cache)
{
assert(cache);
assert(cache->i_refcount > 0);
if (cache->i_refcount > 0)
cache->i_refcount--;
}
static vasubpicture_cache_t *subpicture_cache_new( void )
{
vasubpicture_cache_t *cache = calloc(1, sizeof(vasubpicture_cache_t));
if (cache == NULL)
return NULL;
cache->i_id = VA_INVALID_ID;
cache->image.buf = VA_INVALID_ID;
cache->image.image_id = VA_INVALID_ID;
return cache;
}
static void subpicture_cache_destroy(vasubpicture_cache_t *cache)
{
assert(cache);
assert(cache->i_id == VA_INVALID_ID);
assert(cache->image.buf == VA_INVALID_ID);
assert(cache->image.image_id == VA_INVALID_ID);
free(cache);
}
unsigned int cache_GetCacheID(const subpicture_cache_t *cache)
{
assert(cache);
return cache->i_id;
}
/* ------------------------ */
/* VA API support functions */
/* ------------------------ */
int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags)
{
vaconn->lock();
int num = vaMaxNumSubpictureFormats(vaconn->p_display);
if (num <= 0)
goto out_warning;
VAImageFormat *p_fmt = calloc(num, sizeof(*p_fmt));
unsigned int *vaflags = calloc(num, sizeof(*vaflags));
if (!p_fmt || !vaflags)
goto out;
unsigned int num_fmt = 0;
VAStatus status = vaQuerySubpictureFormats(vaconn->p_display,
p_fmt, vaflags, &num_fmt);
if (status != VA_STATUS_SUCCESS)
goto out;
for (unsigned int i = 0; i < num_fmt; i++)
{
VAImageFormat format = p_fmt[i];
if (format.fourcc == vafourcc)
{
*flags = vaflags[i];
memcpy((void*)sub_fmt, (void*)&format, sizeof(VAImageFormat));
free(p_fmt);
free(vaflags);
vaconn->unlock();
return VLC_SUCCESS;
}
}
out:
free(p_fmt);
free(vaflags);
out_warning:
vaconn->unlock();
return VLC_EGENERIC;
}
static inline void vaapi_fixup_alpha(uint8_t *p_pixel, size_t i_size)
{
for (size_t p = 0; p < i_size; p += 4)
{
/* RGBA */
uint8_t *R = &p_pixel[p];
uint8_t *G = &p_pixel[p+1];
uint8_t *B = &p_pixel[p+2];
uint8_t *A = &p_pixel[p+3];
if (*A != 255) /* alpha transparancy */
{
*A = 0; /* 0 = transparent, 255 = opaque */
*R = 0;
*G = 0;
*B = 0;
}
}
}
static int CopyPictureToVAImage(vlc_object_t *obj, vlc_va_conn_t *vaconn,
picture_t *pic, VAImage *image, VAImageFormat *fmt,
const bool fixup_alpha)
{
VAStatus status;
status = vaCreateImage(vaconn->p_display, fmt,
pic->format.i_width, pic->format.i_height, image);
if (status != VA_STATUS_SUCCESS)
return VLC_EGENERIC;
/* Sanity checks */
if ((image->image_id == VA_INVALID_ID) ||
(image->format.fourcc != VA_FOURCC_RGBA) ||
(image->num_planes != (unsigned int) pic->i_planes) ||
(image->height != pic->format.i_height) ||
(image->width != pic->format.i_width) ||
(image->format.bits_per_pixel != pic->format.i_bits_per_pixel))
{
msg_Err(obj, "subpicture region: planes %d/%d, depth %d, width %d/%d, height %d/%d, bpp %d/%d",
image->num_planes, pic->i_planes,
image->format.depth,
image->width, pic->format.i_width,
image->height, pic->format.i_height,
image->format.bits_per_pixel, pic->format.i_bits_per_pixel);
goto out_va_free;
}
assert(image->image_id != VA_INVALID_ID);
assert(image->buf != VA_INVALID_ID);
void *p_base = NULL;
status = vaMapBuffer(vaconn->p_display, image->buf, &p_base);
if (status != VA_STATUS_SUCCESS)
{
msg_Err(obj, "failed mapping image for copying (%d)", status);
msg_Err(obj, "subpicture region: planes %d/%d, depth %d, width %d/%d, height %d/%d, bpp %d/%d",
image->num_planes, pic->i_planes,
image->format.depth,
image->width, pic->format.i_width,
image->height, pic->format.i_height,
image->format.bits_per_pixel, pic->format.i_bits_per_pixel);
goto out_va_free;
}
assert(p_base); /* should not happen */
for (unsigned int i_plane = 0; i_plane < image->num_planes; i_plane++)
{
/* Does the pitch match? Then just copy it directly */
if (image->pitches[i_plane] == (unsigned int)pic->p[i_plane].i_pitch)
{
vlc_memcpy((uint8_t*)(((uint8_t*)p_base) + image->offsets[i_plane]),
pic->p[i_plane].p_pixels,
pic->p[i_plane].i_pitch * pic->p[i_plane].i_visible_lines);
}
else
{
/* proceed line by line */
uint8_t *p_in = pic->p[i_plane].p_pixels;
uint8_t *p_out = (uint8_t *)(((uint8_t*)p_base) + image->offsets[i_plane]);
int i_line = pic->format.i_y_offset * pic->p[i_plane].i_visible_pitch;
for (; i_line < pic->p[i_plane].i_visible_lines; i_line++)
{
/* NOTE: image->pitch[i_plane] includes image->offset[i_plane], thus
* visible_pitch = image->pitch[i_plane] - image->offset[i_plane] */
size_t i_size = __MIN(image->pitches[i_plane],
(unsigned int)pic->p[i_plane].i_pitch);
vlc_memcpy(&p_out[i_line * image->pitches[i_plane]], &p_in[i_line * pic->p[i_plane].i_pitch], i_size);
/* FIXME: Remove this code when VDPAU and XvBA backends for
* VAAPI properly support alpha or chroma keying in subpictures. */
if (fixup_alpha)
{
uint8_t *p_pixel = p_out + (i_line * image->pitches[i_plane]);
vaapi_fixup_alpha(p_pixel, i_size);
}
}
}
}
status = vaUnmapBuffer(vaconn->p_display, image->buf);
if (status != VA_STATUS_SUCCESS)
{
msg_Err(obj, "failed unmapping image for copying (%d)", status);
goto out_va_free;
}
return VLC_SUCCESS;
out_va_free:
msg_Err(obj, "failed to copy region into vaImage id=%d", image->image_id);
vaDestroyImage(vaconn->p_display, image->image_id);
image->image_id = VA_INVALID_ID;
image->buf = VA_INVALID_ID;
return VLC_EGENERIC;
}
/* Create subpicture cache for this subpicture and
* allocate needed VA API resources. Call this
* function when holding cache->lock */
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
const subpicture_t *subpicture, const subpicture_region_t *region, const int flags)
{
int canc = vlc_savecancel();
vaconn->lock();
/* Copy pixels from region into VAImage */
vasubpicture_cache_t *vasub_cache = subpicture_cache_new();
if (!vasub_cache)
goto error;
VAImage *image = &vasub_cache->image;
image->image_id = VA_INVALID_ID;
image->buf = VA_INVALID_ID;
int err = CopyPictureToVAImage(obj, vaconn, region->p_picture, image, fmt,
(flags == 0x0) ? true : false);
if (err != VLC_SUCCESS)
{
subpicture_cache_destroy(vasub_cache);
goto error;
}
VAStatus status;
status = vaCreateSubpicture(vaconn->p_display, image->image_id, &vasub_cache->i_id);
if (status != VA_STATUS_SUCCESS)
goto cleanup;
bool b_alpha = (flags & VA_SUBPICTURE_GLOBAL_ALPHA);
bool b_chroma = (flags & VA_SUBPICTURE_CHROMA_KEYING);
if (b_alpha)
{
assert(!b_chroma);
float global_alpha = (float)(subpicture->i_alpha * region->i_alpha / 255) / 255;
status = vaSetSubpictureGlobalAlpha(vaconn->p_display, vasub_cache->i_id, global_alpha);
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed applying alpha value to subpicture");
}
if (b_chroma)
{
assert(!b_alpha);
/* chromakey values */
const unsigned int min = 0x0; /* ? */
const unsigned int max = 0x0000ff00; /* ? */
const unsigned int mask = (region->p_picture->format.i_rmask << 24) |
(region->p_picture->format.i_gmask << 16) |
(region->p_picture->format.i_bmask << 8) | 0;
status = vaSetSubpictureChromakey(vaconn->p_display, vasub_cache->i_id,
min, max, mask);
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed applying chroma valuesto subpicture");
}
vasub_cache->rect.i_x = region->i_x;
vasub_cache->rect.i_y = region->i_y;
vasub_cache->rect.i_width = region->fmt.i_visible_width;
vasub_cache->rect.i_height = region->fmt.i_visible_height;
vaconn->unlock();
vlc_restorecancel(canc);
return vasub_cache;
cleanup:
vaDestroyImage(vaconn->p_display, image->image_id);
vasub_cache->i_id = VA_INVALID_ID;
image->image_id = VA_INVALID_ID;
image->buf = VA_INVALID_ID;
subpicture_cache_destroy(vasub_cache);
vasub_cache = NULL;
error:
vaconn->unlock();
vlc_restorecancel(canc);
return NULL;
}
/* Destroy subpicture cache for this subpicture and
* release allocated VA API resources. Call this
* function when holding cache->lock */
void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *vaconn, subpicture_cache_t *cache, const bool b_force)
{
VLC_UNUSED(obj);
#ifdef VAAPI_DEBUG
msg_Dbg(obj, "cache_SubpictureDestroy cache %d", cache->i_id);
#endif
int canc = vlc_savecancel();
vaconn->lock();
const int num = vlc_array_count(&cache->subpictures);
for (int i = num - 1; i >= 0; i--)
{
vasubpicture_cache_t *subpic;
subpic = (vasubpicture_cache_t *)vlc_array_item_at_index(&cache->subpictures, i);
if (!subpic)
continue;
vlc_array_remove(&cache->subpictures, i);
#ifdef VAAPI_DEBUG
msg_Dbg(obj, "- %d: sub_id %d, image_id %d",
i, subpic->i_id, subpic->image.image_id);
#endif
assert(subpic->i_id != VA_INVALID_ID);
assert(subpic->image.image_id != VA_INVALID_ID);
vaDestroySubpicture(vaconn->p_display, subpic->i_id);
vaDestroyImage(vaconn->p_display, subpic->image.image_id);
subpic->i_id = VA_INVALID_ID;
subpic->image.buf = VA_INVALID_ID;
subpic->image.image_id = VA_INVALID_ID;
subpicture_cache_destroy(subpic);
subpic = NULL;
}
if (b_force)
cache->i_refcount = 0;
cache_destroy(cache);
cache = NULL;
vaconn->unlock();
vlc_restorecancel(canc);
}
int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
const picture_t *picture, const int flags)
{
assert(cache_array);
assert(picture->p_sys);
picture_sys_t *surface = picture->p_sys;
assert(surface->i_id != VA_INVALID_SURFACE);
subpicture_cache_t *cache = (subpicture_cache_t *) cache_find(cache_array, surface->i_cache);
if (cache == NULL)
goto error;
assert(cache->i_id > 0);
const int count = vlc_array_count(&cache->subpictures);
#ifdef VAAPI_DEBUG
msg_Dbg(obj, " link surface %d cache %d refcount %d",
surface->i_id, cache->i_id, cache->i_refcount);
#endif
int i_sub = 0;
for (int i = 0; i < count; i++)
{
vasubpicture_cache_t *vasubpic;
vasubpic = (vasubpicture_cache_t *) vlc_array_item_at_index(&cache->subpictures, i);
if (!vasubpic)
goto cleanup;
#ifdef VAAPI_DEBUG
msg_Dbg(obj, "+ %d: subid %d", i_sub, vasubpic->i_id);
#endif
/* NOTE: The video output core renders subpictures to picture size.
* in case of VAAPI SURFACES this is always the original picture size.
* Subpictures must be placed on the VAAPI SURFACE and vaPutSurface()
* does the final scaling.
*/
/* subpicture positioning */
unsigned int src_x = 0;
unsigned int src_w = src_x + vasubpic->rect.i_width;
unsigned int src_y = 0;
unsigned int src_h = src_y + vasubpic->rect.i_height;
vaconn->lock();
/* Associate subpicture with surface */
VAStatus status;
status = vaAssociateSubpicture(vaconn->p_display, vasubpic->i_id,
&surface->i_id, 1, /* array of surfaces */
/* src rectangle */
src_x, src_y, src_w, src_h,
/* dest rectangle */
vasubpic->rect.i_x, vasubpic->rect.i_y,
vasubpic->rect.i_width,
vasubpic->rect.i_height,
flags);
vaconn->unlock();
if (status != VA_STATUS_SUCCESS)
goto cleanup;
vasubpic = NULL;
i_sub++;
}
return VLC_SUCCESS;
cleanup:
/* Cleanup linked subpictures */
#ifdef VAAPI_DEBUG
msg_Err(obj, "unlink surface %d cache %d", surface->i_id, cache->i_id);
#endif
for (int i = 0; i < i_sub; i++)
{
vasubpicture_cache_t *sub = vlc_array_item_at_index(&cache->subpictures, i);
assert(sub);
if (sub->i_id != VA_INVALID_ID)
{
VAStatus status = VA_STATUS_ERROR_UNKNOWN;
vaconn->lock();
status = vaDeassociateSubpicture(vaconn->p_display,
sub->i_id, &surface->i_id, 1);
vaconn->unlock();
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed unlinking subid: %d (error %d)",
sub->i_id, status);
}
}
error:
return VLC_EGENERIC;
}
int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn,
vlc_array_t *cache_array, const picture_t *picture)
{
assert(cache_array);
picture_sys_t *surface = picture->p_sys;
assert(surface);
assert(surface->i_id != VA_INVALID_SURFACE);
assert(surface->i_cache > 0);
subpicture_cache_t *cache = cache_find(cache_array, surface->i_cache);
if (cache == NULL)
return VLC_EGENERIC;
assert(cache->i_id > 0);
#ifdef VAAPI_DEBUG
msg_Dbg(obj, "unlink surface %d cache %d size %d",
surface->i_id, surface->i_cache, count);
#endif
const int count = vlc_array_count(&cache->subpictures);
for (int i = 0; i < count; i++)
{
vasubpicture_cache_t *sub = vlc_array_item_at_index(&cache->subpictures, i);
assert(sub);
#ifdef VAAPI_DEBUG
msg_Dbg(vd, "- %u: subid %d", i, sub->i_id);
#endif
VAStatus status = VA_STATUS_ERROR_UNKNOWN;
if (sub->i_id != VA_INVALID_ID)
{
vaconn->lock();
status = vaDeassociateSubpicture(vaconn->p_display,
sub->i_id, &surface->i_id, 1);
vaconn->unlock();
if (status != VA_STATUS_SUCCESS)
msg_Err(obj, "failed unlinking subid: %d (error %d)",
sub->i_id, status);
}
}
return VLC_SUCCESS;
}
/* NOTE: call with sys->cache_lock held */
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id)
{
assert(cache_array);
subpicture_cache_t *cache = cache_find(cache_array, cache_id);
if (cache)
{
#ifdef VAAPI_DEBUG
msg_Info(obj, "Release cache %d count %d refcount %d",
cache->i_id, vlc_array_count(cache_array), cache->i_refcount);
#endif
cache_unref(cache);
/* Do we still need need this subpicture cache ? */
if (cache->i_refcount == 0)
{
#ifdef VAAPI_DEBUG
msg_Info(obj, "Deleting subpicture cache %d refcount %d",
cache->i_id, cache->i_refcount);
#endif
int index = vlc_array_index_of_item(cache_array, (void*)cache);
vlc_array_remove(cache_array, index);
cache_SubpictureDestroy(obj, vaconn, cache, false);
}
#ifdef VAAPI_DEBUG
msg_Info(obj, "cache id %d refcount %d", cache_id, cache->i_refcount);
#endif
}
}
/* NOTE: call with sys->cache_lock held */
void cache_SubpictureHold(vlc_object_t *obj, vlc_array_t *cache_array, const int cache_id)
{
VLC_UNUSED(obj);
subpicture_cache_t *cache = cache_find(cache_array, cache_id);
if (cache)
{
#ifdef VAAPI_DEBUG
msg_Info(obj, "Subpicture cache %d refcount %d",
cache->i_id, cache->i_refcount);
#endif
cache_ref(cache);
}
}
subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
picture_t *picture, subpicture_t *subpicture, int flags)
{
picture_sys_t *surface = picture->p_sys;
subpicture_cache_t *cache = cache_new();
if (!cache)
return NULL;
cache->i_start = subpicture->i_start;
cache->i_stop = subpicture->i_stop;
int i_sub = 0;
subpicture_region_t *region;
for (region = subpicture->p_region; region != NULL; region = region->p_next)
{
/* Skip non-RGBA regions for now */
if (region->fmt.i_chroma != VLC_CODEC_RGBA)
continue;
assert(region->p_picture->format.i_width > 0);
assert(region->p_picture->format.i_height > 0);
/* Create Subpicture */
vasubpicture_cache_t *vasub_cache;
vasub_cache = cache_SubpictureCreate(obj, vaconn, fmt, subpicture, region, flags);
if (vasub_cache == NULL)
goto cleanup;
#ifdef VAAPI_DEBUG
msg_Dbg(obj, "+ %d: subid %d", i_sub, vasub_cache->i_id);
#endif
vlc_array_append(&cache->subpictures, (void *)vasub_cache);
vasub_cache = NULL;
i_sub++;
}
/* remember what was created, so it can be released in DisplayPicture() */
cache_ref(cache);
surface->i_cache = cache->i_id;
return cache;
cleanup:
msg_Err(obj, "failed creating subpicture cache %d for surface %d",
cache->i_id, surface->i_id);
cache_SubpictureDestroy(obj, vaconn, cache, false);
return NULL;
}
int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id,
picture_t *picture, subpicture_t *subpicture)
{
VLC_UNUSED(vaconn);
assert(picture);
assert(subpicture);
picture_sys_t *surface = picture->p_sys;
subpicture_cache_t *cache = (subpicture_cache_t *) cache_find(cache_array, cache_id);
if (cache == NULL)
goto cleanup;
assert(cache->i_id > 0);
#if 0
if ((cache->i_start != subpicture->i_start) &&
(cache->i_stop != subpicture->i_stop))
goto cleanup;
#endif
/* Check if all subpictures region pictures match */
const int count = vlc_array_count(&cache->subpictures);
int i_sub = 0;
subpicture_region_t *region;
for (region = subpicture->p_region; region != NULL; region = region->p_next)
{
/* Skip non-RGBA regions */
if (region->fmt.i_chroma != VLC_CODEC_RGBA)
continue;
/* Ooh: big booboo */
if (i_sub >= count)
abort();
vasubpicture_cache_t *vasubpic;
vasubpic = (vasubpicture_cache_t *) vlc_array_item_at_index(&cache->subpictures, i_sub);
if (!vasubpic)
goto cleanup;
if ((vasubpic->image.width != region->fmt.i_width) ||
(vasubpic->image.height != region->fmt.i_height))
goto cleanup;
#ifdef VAAPI_DEBUG
msg_Dbg(obj, "+ %d: subid %d", i_sub, vasubpic->i_id);
#endif
vasubpic = NULL;
i_sub++;
}
/* FIXME: should not happen */
if (i_sub < count)
goto cleanup;
picture->p_sys->i_cache = cache->i_id;
cache_ref(cache);
return VLC_SUCCESS;
cleanup:
/* Cannot reuse this cached subpicture */
msg_Warn(obj, "cannot reuse cached subpicture %d for surface %d -- resetting",
cache->i_id, surface->i_id);
return VLC_EGENERIC;
}
#endif
/*****************************************************************************
* Copyright © 2012, M2X BV
* Author: Jean-Paul Saman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU 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
****************************************************************************/
#ifndef VLC_VAAPI_VASUB_H
#define VLC_VAAPI_VASUB_H 1
/* Check if subtitle format is supported by VA API */
int VASubtitleFourCC(vlc_va_conn_t *vaconn, const unsigned int vafourcc, VAImageFormat *sub_fmt, int *flags);
/* */
typedef struct vasubpicture_cache_t vasubpicture_cache_t;
typedef struct subpicture_cache_t subpicture_cache_t;
/* Get Cache id */
unsigned int cache_GetCacheID(const subpicture_cache_t *cache);
/* Create new VASubpicture */
vasubpicture_cache_t *cache_SubpictureCreate(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *sub_fmt,
const subpicture_t *subpicture, const subpicture_region_t *region, const int flags);
/* Destory VASubpicture and cached subpicture */
void cache_SubpictureDestroy(vlc_object_t *obj, vlc_va_conn_t *conn, subpicture_cache_t *cache, const bool b_force);
/* Hold reference to subpicture cache object */
void cache_SubpictureHold(vlc_object_t *obj, vlc_array_t *cache_array, const int cache_id);
/* Release reference to subpicture cache object */
void cache_SubpictureRelease(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id);
/* Link VASubpicture to VASurface */
int SubpictureRegionsLink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
const picture_t *picture, const int flags);
/* Unlink VASubpicture from VASurface */
int SubpictureRegionsUnlink(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array,
const picture_t *picture);
/* Render VLC subpictures as VASubpictures and put them into a subpicture cache */
subpicture_cache_t *RenderDirectSubpicture(vlc_object_t *obj, vlc_va_conn_t *vaconn, VAImageFormat *fmt,
picture_t *picture, subpicture_t *subpicture, int flags);
/* Check if cached subpicture can be reused for the next Subpicture Render cycle */
int RenderCachedSubpictures(vlc_object_t *obj, vlc_va_conn_t *vaconn, vlc_array_t *cache_array, const int cache_id,
picture_t *picture, subpicture_t *subpicture);
#endif
......@@ -53,6 +53,7 @@
#include "avcodec.h"
#include "va.h"
typedef struct vlc_va_conn_t vlc_va_conn_t;
#if defined(HAVE_AVCODEC_VAAPI) || defined(HAVE_AVCODEC_DXVA2)
# define HAVE_AVCODEC_VA
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment