libvo, vo_vdpau: make the EOSD packer code from vo_vdpau generic

The code in eosd_packer.c/.h is taken from vo_vdpau.c and has been made
independent from vdpau API specifics. This allows other VOs, which need
to pack the small EOSD images into a large surface for efficiency, to use
this code.
This commit is contained in:
wm4 2011-10-27 12:07:10 +02:00
parent 827faa3843
commit 4010dd0b1a
4 changed files with 377 additions and 198 deletions

View File

@ -405,6 +405,7 @@ SRCS_COMMON = asxparser.c \
libmpdemux/yuv4mpeg.c \
libmpdemux/yuv4mpeg_ratio.c \
libvo/osd.c \
libvo/eosd_packer.c \
osdep/numcores.c \
osdep/$(GETCH) \
osdep/$(TIMER) \

254
libvo/eosd_packer.c Normal file
View File

@ -0,0 +1,254 @@
/*
* Common code for packing EOSD images into larger surfaces.
*
* This file is part of mplayer2.
*
* mplayer2 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.
*
* mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <libavutil/common.h>
#include "talloc.h"
#include "mp_msg.h"
#include "eosd_packer.h"
// Initial size of EOSD surface in pixels (x*x)
#define EOSD_SURFACE_INITIAL_SIZE 256
// Allocate an eosd_packer, which can be used to layout and cache the list of
// EOSD images contained in a mp_eosd_images_t into a flat surface.
// It can be free'd with talloc_free().
// Don't forget to call eosd_init() before using it.
struct eosd_packer *eosd_packer_create(void *talloc_ctx) {
return talloc_zero(talloc_ctx, struct eosd_packer);
}
// Call this when you need to completely reinitialize the EOSD state, e.g. when
// when your EOSD surface was deleted.
// max_width and max_height are the maximum surface sizes that should be
// allowed.
void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width,
uint32_t max_height)
{
state->max_surface_width = max_width;
state->max_surface_height = max_height;
state->surface.w = 0;
state->surface.h = 0;
state->targets_count = 0;
}
#define HEIGHT_SORT_BITS 4
static int size_index(struct eosd_target *r)
{
unsigned int h = r->source.y1;
int n = av_log2_16bit(h);
return (n << HEIGHT_SORT_BITS)
+ (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
}
/* Pack the given rectangles into an area of size w * h.
* The size of each rectangle is read from .source.x1/.source.y1.
* The height of each rectangle must be at least 1 and less than 65536.
* The .source rectangle is then set corresponding to the packed position.
* 'scratch' must point to work memory for num_rects+16 ints.
* Return 0 on success, -1 if the rectangles did not fit in w*h.
*
* The rectangles are placed in rows in order approximately sorted by
* height (the approximate sorting is simpler than a full one would be,
* and allows the algorithm to work in linear time). Additionally, to
* reduce wasted space when there are a few tall rectangles, empty
* lower-right parts of rows are filled recursively when the size of
* rectangles in the row drops past a power-of-two threshold. So if a
* row starts with rectangles of size 3x50, 10x40 and 5x20 then the
* free rectangle with corners (13, 20)-(w, 50) is filled recursively.
*/
static int pack_rectangles(struct eosd_target *rects, int num_rects,
int w, int h, int *scratch)
{
int bins[16 << HEIGHT_SORT_BITS];
int sizes[16 << HEIGHT_SORT_BITS] = {};
for (int i = 0; i < num_rects; i++)
sizes[size_index(rects + i)]++;
int idx = 0;
for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
bins[i + j] = idx;
idx += sizes[i + j];
}
scratch[idx++] = -1;
}
for (int i = 0; i < num_rects; i++)
scratch[bins[size_index(rects + i)]++] = i;
for (int i = 0; i < 16; i++)
bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
struct {
int size, x, bottom;
} stack[16] = {{15, 0, h}}, s = {};
int stackpos = 1;
int y;
while (stackpos) {
y = s.bottom;
s = stack[--stackpos];
s.size++;
while (s.size--) {
int maxy = -1;
int obj;
while ((obj = scratch[bins[s.size]]) >= 0) {
int bottom = y + rects[obj].source.y1;
if (bottom > s.bottom)
break;
int right = s.x + rects[obj].source.x1;
if (right > w)
break;
bins[s.size]++;
rects[obj].source.x0 = s.x;
rects[obj].source.x1 += s.x;
rects[obj].source.y0 = y;
rects[obj].source.y1 += y;
num_rects--;
if (maxy <= 0)
stack[stackpos++] = s;
s.x = right;
maxy = FFMAX(maxy, bottom);
}
if (maxy > 0)
s.bottom = maxy;
}
}
return num_rects ? -1 : 0;
}
// padding to reduce interpolation artifacts when doing scaling & filtering
#define EOSD_PADDING 0
// Release all previous images, and packs the images in imgs into state. The
// caller must check the change variables:
// *out_need_reposition == true: sub-image positions changed
// *out_need_upload == true: upload all sub-images again
// *out_need_reallocate == true: resize the EOSD texture to state->surface.w/h
// Logical implications: need_reallocate => need_upload => need_reposition
void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs,
bool *out_need_reposition, bool *out_need_upload,
bool *out_need_reallocate)
{
int i;
ASS_Image *img = imgs->imgs;
ASS_Image *p;
struct eosd_surface *sfc = &state->surface;
*out_need_reposition = false;
*out_need_upload = false;
*out_need_reallocate = false;
int change_state = imgs->changed;
// eosd_reinit() was probably called, force full reupload.
if (state->targets_count == 0 && img)
change_state = 2;
if (change_state == 0)
return; // Nothing changed, no need to redraw
state->targets_count = 0;
*out_need_reposition = true;
if (!img)
return; // There's nothing to render!
if (change_state == 1)
goto eosd_skip_upload;
*out_need_upload = true;
while (1) {
for (p = img, i = 0; p; p = p->next) {
if (p->w <= 0 || p->h <= 0)
continue;
// Allocate new space for surface/target arrays
if (i >= state->targets_size) {
state->targets_size = FFMAX(state->targets_size * 2, 512);
state->targets =
talloc_realloc_size(state, state->targets,
state->targets_size
* sizeof(*state->targets));
state->scratch =
talloc_realloc_size(state, state->scratch,
(state->targets_size + 16)
* sizeof(*state->scratch));
}
state->targets[i].source.x1 = p->w + EOSD_PADDING;
state->targets[i].source.y1 = p->h + EOSD_PADDING;
i++;
}
if (pack_rectangles(state->targets, i, sfc->w, sfc->h,
state->scratch) >= 0)
break;
int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
state->max_surface_width);
int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
state->max_surface_height);
if (w == sfc->w && h == sfc->h) {
mp_msg(MSGT_VO, MSGL_ERR, "[eosd] EOSD bitmaps do not fit on "
"a surface with the maximum supported size\n");
return;
}
sfc->w = w;
sfc->h = h;
*out_need_reallocate = true;
}
if (*out_need_reallocate) {
mp_msg(MSGT_VO, MSGL_V, "[eosd] Allocate a %dx%d surface for "
"EOSD bitmaps.\n", sfc->w, sfc->h);
}
eosd_skip_upload:
for (p = img; p; p = p->next) {
if (p->w <= 0 || p->h <= 0)
continue;
struct eosd_target *target = &state->targets[state->targets_count];
target->source.x1 -= EOSD_PADDING;
target->source.y1 -= EOSD_PADDING;
target->dest.x0 = p->dst_x;
target->dest.y0 = p->dst_y;
target->dest.x1 = p->w + p->dst_x;
target->dest.y1 = p->h + p->dst_y;
target->color = p->color;
target->ass_img = p;
state->targets_count++;
}
}
// Calculate the bounding box of all sub-rectangles in the EOSD surface that
// will be used for EOSD rendering.
// If the bounding box is empty, return false.
bool eosd_packer_calculate_source_bb(struct eosd_packer *state,
struct eosd_rect *out_bb)
{
struct eosd_rect bb = { state->surface.w, state->surface.h, 0, 0 };
for (int n = 0; n < state->targets_count; n++) {
struct eosd_rect s = state->targets[n].source;
bb.x0 = FFMIN(bb.x0, s.x0);
bb.y0 = FFMIN(bb.y0, s.y0);
bb.x1 = FFMAX(bb.x1, s.x1);
bb.y1 = FFMAX(bb.y1, s.y1);
}
// avoid degenerate bounding box if empty
bb.x0 = FFMIN(bb.x0, bb.x1);
bb.y0 = FFMIN(bb.y0, bb.y1);
*out_bb = bb;
return state->targets_count > 0;
}

71
libvo/eosd_packer.h Normal file
View File

@ -0,0 +1,71 @@
/*
* This file is part of mplayer2.
*
* mplayer2 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.
*
* mplayer2 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_EOSD_PACKER_H
#define MPLAYER_EOSD_PACKER_H
#include <inttypes.h>
#include <stdbool.h>
#include "sub/ass_mp.h"
// Pool of surfaces
struct eosd_surface {
//void *native_surface;
int w;
int h;
};
struct eosd_rect {
int x0, y0, x1, y1;
};
// List of surfaces to be rendered
struct eosd_target {
struct eosd_rect source; // position in EOSD surface
struct eosd_rect dest; // position on screen
uint32_t color; // libass-style color of the image
// NOTE: This must not be accessed after you return from your VO's
// VOCTRL_DRAW_EOSD call - libass will free or reuse the associated
// memory. Feel free to set this to NULL to make erroneous accesses to
// this member fail early.
ASS_Image *ass_img;
};
struct eosd_packer {
struct eosd_surface surface;
struct eosd_target *targets;
int targets_count; // number of valid elements in targets
int targets_size; // number of allocated elements in targets
uint32_t max_surface_width;
uint32_t max_surface_height;
int *scratch;
};
struct eosd_packer *eosd_packer_create(void *talloc_ctx);
void eosd_packer_reinit(struct eosd_packer *state, uint32_t max_width,
uint32_t max_height);
void eosd_packer_generate(struct eosd_packer *state, mp_eosd_images_t *imgs,
bool *out_need_reposition, bool *out_need_upload,
bool *out_need_reallocate);
bool eosd_packer_calculate_source_bb(struct eosd_packer *state,
struct eosd_rect *out_bb);
#endif /* MPLAYER_EOSD_PACKER_H */

View File

@ -56,6 +56,7 @@
#include "libavutil/mathematics.h"
#include "sub/ass_mp.h"
#include "eosd_packer.h"
#define WRAP_ADD(x, a, m) ((a) < 0 \
? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
@ -85,9 +86,6 @@
/* number of palette entries */
#define PALETTE_SIZE 256
/* Initial size of EOSD surface in pixels (x*x) */
#define EOSD_SURFACE_INITIAL_SIZE 256
/* Pixelformat used for output surfaces */
#define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
@ -173,24 +171,8 @@ struct vdpctx {
// EOSD
// Pool of surfaces
struct eosd_bitmap_surface {
VdpBitmapSurface surface;
int w;
int h;
uint32_t max_width;
uint32_t max_height;
} eosd_surface;
// List of surfaces to be rendered
struct eosd_target {
VdpRect source;
VdpRect dest;
VdpColor color;
} *eosd_targets;
int eosd_targets_size;
int *eosd_scratch;
int eosd_render_count;
VdpBitmapSurface eosd_surface;
struct eosd_packer *eosd_packer;
// Video equalizer
struct mp_csp_equalizer video_eq;
@ -792,13 +774,16 @@ static int initialize_vdpau_objects(struct vo *vo)
if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
return -1;
int max_width = 0, max_height = 0;
vdp_st = vdp->
bitmap_surface_query_capabilities(vc->vdp_device,
VDP_RGBA_FORMAT_A8,
&(VdpBool){0},
&vc->eosd_surface.max_width,
&vc->eosd_surface.max_height);
&max_width,
&max_height);
CHECK_ST_WARNING("Query to get max EOSD surface size failed");
eosd_packer_reinit(vc->eosd_packer, max_width, max_height);
forget_frames(vo);
resize(vo);
return 0;
@ -818,11 +803,9 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
for (int i = 0; i <= MAX_OUTPUT_SURFACES; i++)
vc->output_surfaces[i] = VDP_INVALID_HANDLE;
vc->vdp_device = VDP_INVALID_HANDLE;
vc->eosd_surface = (struct eosd_bitmap_surface){
.surface = VDP_INVALID_HANDLE,
};
vc->eosd_surface = VDP_INVALID_HANDLE;
eosd_packer_reinit(vc->eosd_packer, 0, 0);
vc->output_surface_width = vc->output_surface_height = -1;
vc->eosd_render_count = 0;
}
static int handle_preemption(struct vo *vo)
@ -1018,6 +1001,8 @@ static void draw_osd_I8A8(void *ctx, int x0, int y0, int w, int h,
"vdp_output_surface_render_output_surface");
}
#define EOSD_VDP_RC(r) &(VdpRect){r.x0, r.y0, r.x1, r.y1}
static void draw_eosd(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
@ -1043,200 +1028,66 @@ static void draw_eosd(struct vo *vo)
.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
};
for (i = 0; i < vc->eosd_render_count; i++) {
for (i = 0; i < vc->eosd_packer->targets_count; i++) {
struct eosd_target *target = &vc->eosd_packer->targets[i];
VdpColor color = {
.alpha = 1.0 - ((target->color >> 0) & 0xff) / 255.0,
.blue = ((target->color >> 8) & 0xff) / 255.0,
.green = ((target->color >> 16) & 0xff) / 255.0,
.red = ((target->color >> 24) & 0xff) / 255.0,
};
vdp_st = vdp->
output_surface_render_bitmap_surface(output_surface,
&vc->eosd_targets[i].dest,
vc->eosd_surface.surface,
&vc->eosd_targets[i].source,
&vc->eosd_targets[i].color,
EOSD_VDP_RC(target->dest),
vc->eosd_surface,
EOSD_VDP_RC(target->source),
&color,
&blend_state,
VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
CHECK_ST_WARNING("EOSD: Error when rendering");
}
}
#define HEIGHT_SORT_BITS 4
static int size_index(struct eosd_target *r)
{
unsigned int h = r->source.y1;
int n = av_log2_16bit(h);
return (n << HEIGHT_SORT_BITS)
+ (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
}
/* Pack the given rectangles into an area of size w * h.
* The size of each rectangle is read from .source.x1/.source.y1.
* The height of each rectangle must be at least 1 and less than 65536.
* The .source rectangle is then set corresponding to the packed position.
* 'scratch' must point to work memory for num_rects+16 ints.
* Return 0 on success, -1 if the rectangles did not fit in w*h.
*
* The rectangles are placed in rows in order approximately sorted by
* height (the approximate sorting is simpler than a full one would be,
* and allows the algorithm to work in linear time). Additionally, to
* reduce wasted space when there are a few tall rectangles, empty
* lower-right parts of rows are filled recursively when the size of
* rectangles in the row drops past a power-of-two threshold. So if a
* row starts with rectangles of size 3x50, 10x40 and 5x20 then the
* free rectangle with corners (13, 20)-(w, 50) is filled recursively.
*/
static int pack_rectangles(struct eosd_target *rects, int num_rects,
int w, int h, int *scratch)
{
int bins[16 << HEIGHT_SORT_BITS];
int sizes[16 << HEIGHT_SORT_BITS] = {};
for (int i = 0; i < num_rects; i++)
sizes[size_index(rects + i)]++;
int idx = 0;
for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
bins[i + j] = idx;
idx += sizes[i + j];
}
scratch[idx++] = -1;
}
for (int i = 0; i < num_rects; i++)
scratch[bins[size_index(rects + i)]++] = i;
for (int i = 0; i < 16; i++)
bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
struct {
int size, x, bottom;
} stack[16] = {{15, 0, h}}, s = {};
int stackpos = 1;
int y;
while (stackpos) {
y = s.bottom;
s = stack[--stackpos];
s.size++;
while (s.size--) {
int maxy = -1;
int obj;
while ((obj = scratch[bins[s.size]]) >= 0) {
int bottom = y + rects[obj].source.y1;
if (bottom > s.bottom)
break;
int right = s.x + rects[obj].source.x1;
if (right > w)
break;
bins[s.size]++;
rects[obj].source.x0 = s.x;
rects[obj].source.x1 += s.x;
rects[obj].source.y0 = y;
rects[obj].source.y1 += y;
num_rects--;
if (maxy <= 0)
stack[stackpos++] = s;
s.x = right;
maxy = FFMAX(maxy, bottom);
}
if (maxy > 0)
s.bottom = maxy;
}
}
return num_rects ? -1 : 0;
}
static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
struct eosd_packer *packer = vc->eosd_packer;
int i;
ASS_Image *img = imgs->imgs;
ASS_Image *p;
struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
bool need_upload = false;
if (imgs->changed == 0)
return; // Nothing changed, no need to redraw
bool need_repos, need_upload, need_resize;
eosd_packer_generate(packer, imgs, &need_repos, &need_upload, &need_resize);
vc->eosd_render_count = 0;
if (!need_upload)
return;
if (!img)
return; // There's nothing to render!
if (imgs->changed == 1)
goto eosd_skip_upload;
need_upload = true;
bool reallocate = false;
while (1) {
for (p = img, i = 0; p; p = p->next) {
if (p->w <= 0 || p->h <= 0)
continue;
// Allocate new space for surface/target arrays
if (i >= vc->eosd_targets_size) {
vc->eosd_targets_size = FFMAX(vc->eosd_targets_size * 2, 512);
vc->eosd_targets =
talloc_realloc_size(vc, vc->eosd_targets,
vc->eosd_targets_size
* sizeof(*vc->eosd_targets));
vc->eosd_scratch =
talloc_realloc_size(vc, vc->eosd_scratch,
(vc->eosd_targets_size + 16)
* sizeof(*vc->eosd_scratch));
}
vc->eosd_targets[i].source.x1 = p->w;
vc->eosd_targets[i].source.y1 = p->h;
i++;
}
if (pack_rectangles(vc->eosd_targets, i, sfc->w, sfc->h,
vc->eosd_scratch) >= 0)
break;
int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
sfc->max_width);
int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
sfc->max_height);
if (w == sfc->w && h == sfc->h) {
mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
"a surface with the maximum supported size\n");
return;
} else {
sfc->w = w;
sfc->h = h;
}
reallocate = true;
}
if (reallocate) {
if (sfc->surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
if (need_resize) {
if (vc->eosd_surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
"EOSD bitmaps.\n", sfc->w, sfc->h);
vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
sfc->w, sfc->h, true,
&sfc->surface);
packer->surface.w, packer->surface.h,
true, &vc->eosd_surface);
if (vdp_st != VDP_STATUS_OK)
sfc->surface = VDP_INVALID_HANDLE;
vc->eosd_surface = VDP_INVALID_HANDLE;
CHECK_ST_WARNING("EOSD: error when creating surface");
}
eosd_skip_upload:
if (sfc->surface == VDP_INVALID_HANDLE)
if (vc->eosd_surface == VDP_INVALID_HANDLE)
return;
for (p = img; p; p = p->next) {
if (p->w <= 0 || p->h <= 0)
continue;
struct eosd_target *target = &vc->eosd_targets[vc->eosd_render_count];
if (need_upload) {
vdp_st = vdp->
bitmap_surface_put_bits_native(sfc->surface,
(const void *) &p->bitmap,
&p->stride, &target->source);
CHECK_ST_WARNING("EOSD: putbits failed");
}
// Render dest, color, etc.
target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
target->color.green = ((p->color >> 16) & 0xff) / 255.0;
target->color.red = ((p->color >> 24) & 0xff) / 255.0;
target->dest.x0 = p->dst_x;
target->dest.y0 = p->dst_y;
target->dest.x1 = p->w + p->dst_x;
target->dest.y1 = p->h + p->dst_y;
vc->eosd_render_count++;
for (i = 0; i < vc->eosd_packer->targets_count; i++) {
struct eosd_target *target = &vc->eosd_packer->targets[i];
ASS_Image *p = target->ass_img;
vdp_st = vdp->
bitmap_surface_put_bits_native(vc->eosd_surface,
(const void *) &p->bitmap,
&p->stride,
EOSD_VDP_RC(target->source));
CHECK_ST_WARNING("EOSD: putbits failed");
target->ass_img = NULL;
}
}
@ -1607,8 +1458,8 @@ static void destroy_vdpau_objects(struct vo *vo)
CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
}
if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
if (vc->eosd_surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface);
CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
@ -1643,6 +1494,8 @@ static int preinit(struct vo *vo, const char *arg)
struct vdpctx *vc = talloc_zero(vo, struct vdpctx);
vo->priv = vc;
vc->eosd_packer = eosd_packer_create(vo);
// Mark everything as invalid first so uninit() can tell what has been
// allocated
mark_vdpau_objects_uninitialized(vo);