Showing with 75 additions and 56 deletions.
  1. +1 −0 video/decode/lavc.h
  2. +34 −27 video/decode/vd_lavc.c
  3. +40 −29 video/out/gl_video.c
@@ -20,6 +20,7 @@ typedef struct lavc_ctx {
enum AVDiscard skip_frame;
const char *software_fallback_decoder;
bool hwdec_failed;
bool hwdec_notified;

// From VO
struct mp_hwdec_info *hwdec_info;
@@ -243,6 +243,7 @@ static struct vd_lavc_hwdec *probe_hwdec(struct dec_video *vd, bool autoprobe,
enum hwdec_type api,
const char *decoder)
{
MP_VERBOSE(vd, "Probing '%s'...\n", m_opt_choice_str(mp_hwdec_names, api));
struct vd_lavc_hwdec *hwdec = find_hwcodec(api);
if (!hwdec) {
MP_VERBOSE(vd, "Requested hardware decoder not compiled.\n");
@@ -274,6 +275,21 @@ static void uninit(struct dec_video *vd)
talloc_free(vd->priv);
}

static bool force_fallback(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
if (!ctx->software_fallback_decoder)
return false;

uninit_avctx(vd);
int lev = ctx->hwdec_notified ? MSGL_WARN : MSGL_V;
mp_msg(vd->log, lev, "Falling back to software decoding.\n");
const char *decoder = ctx->software_fallback_decoder;
ctx->software_fallback_decoder = NULL;
init_avctx(vd, decoder, NULL);
return true;
}

static int init(struct dec_video *vd, const char *decoder)
{
vd_ffmpeg_ctx *ctx;
@@ -314,20 +330,14 @@ static int init(struct dec_video *vd, const char *decoder)
ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
if (hwdec->get_codec)
decoder = hwdec->get_codec(ctx);
MP_INFO(vd, "Using hardware decoding.\n");
} else if (vd->opts->hwdec_api != HWDEC_NONE) {
MP_INFO(vd, "Using software decoding.\n");
MP_VERBOSE(vd, "Trying hardware decoding.\n");
} else {
MP_VERBOSE(vd, "Using software decoding.\n");
}

init_avctx(vd, decoder, hwdec);
if (!ctx->avctx) {
if (ctx->software_fallback_decoder) {
MP_ERR(vd, "Error initializing hardware decoding, "
"falling back to software decoding.\n");
decoder = ctx->software_fallback_decoder;
ctx->software_fallback_decoder = NULL;
init_avctx(vd, decoder, NULL);
}
force_fallback(vd);
if (!ctx->avctx) {
uninit(vd);
return 0;
@@ -655,21 +665,6 @@ static int decode(struct dec_video *vd, struct demux_packet *packet,
return 1;
}

static int force_fallback(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
if (ctx->software_fallback_decoder) {
uninit_avctx(vd);
MP_WARN(vd, "Hardware decoding failed,"
" falling back to software decoding.\n");
const char *decoder = ctx->software_fallback_decoder;
ctx->software_fallback_decoder = NULL;
init_avctx(vd, decoder, NULL);
return ctx->avctx ? CONTROL_OK : CONTROL_ERROR;
}
return CONTROL_FALSE;
}

static struct mp_image *decode_with_fallback(struct dec_video *vd,
struct demux_packet *packet, int flags)
{
@@ -681,10 +676,20 @@ static struct mp_image *decode_with_fallback(struct dec_video *vd,
int res = decode(vd, packet, flags, &mpi);
if (res < 0) {
// Failed hardware decoding? Try again in software.
if (force_fallback(vd) == CONTROL_OK)
if (force_fallback(vd) && ctx->avctx)
decode(vd, packet, flags, &mpi);
}

if (mpi && !ctx->hwdec_notified && vd->opts->hwdec_api != HWDEC_NONE) {
if (ctx->hwdec) {
MP_INFO(vd, "Using hardware decoding (%s).\n",
m_opt_choice_str(mp_hwdec_names, ctx->hwdec->type));
} else {
MP_INFO(vd, "Using software decoding.\n");
}
ctx->hwdec_notified = true;
}

return mpi;
}

@@ -711,7 +716,9 @@ static int control(struct dec_video *vd, int cmd, void *arg)
return CONTROL_TRUE;
}
case VDCTRL_FORCE_HWDEC_FALLBACK:
return force_fallback(vd);
if (force_fallback(vd))
return ctx->avctx ? CONTROL_OK : CONTROL_ERROR;
return CONTROL_FALSE;
}
return CONTROL_UNKNOWN;
}
@@ -102,8 +102,6 @@ struct texplane {
GLenum gl_type;
GLuint gl_texture;
int gl_buffer;
int buffer_size;
void *buffer_ptr;
};

struct video_image {
@@ -762,8 +760,6 @@ static void uninit_video(struct gl_video *p)
plane->gl_texture = 0;
gl->DeleteBuffers(1, &plane->gl_buffer);
plane->gl_buffer = 0;
plane->buffer_ptr = NULL;
plane->buffer_size = 0;
}
mp_image_unrefp(&vimg->mpi);

@@ -2254,6 +2250,21 @@ void gl_video_resize(struct gl_video *p, int vp_w, int vp_h,
gl_video_reset_surfaces(p);
}

static bool unmap_image(struct gl_video *p, struct mp_image *mpi)
{
GL *gl = p->gl;
bool ok = true;
struct video_image *vimg = &p->image;
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
ok = gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER) && ok;
mpi->planes[n] = NULL; // PBO offset 0
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
return ok;
}

static bool map_image(struct gl_video *p, struct mp_image *mpi)
{
GL *gl = p->gl;
@@ -2266,20 +2277,20 @@ static bool map_image(struct gl_video *p, struct mp_image *mpi)
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
mpi->stride[n] = mp_image_plane_w(mpi, n) * p->image_desc.bytes[n];
int needed_size = mp_image_plane_h(mpi, n) * mpi->stride[n];
if (!plane->gl_buffer)
if (!plane->gl_buffer) {
gl->GenBuffers(1, &plane->gl_buffer);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
if (needed_size > plane->buffer_size) {
plane->buffer_size = needed_size;
gl->BufferData(GL_PIXEL_UNPACK_BUFFER, plane->buffer_size,
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
size_t buffer_size = mp_image_plane_h(mpi, n) * mpi->stride[n];
gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size,
NULL, GL_DYNAMIC_DRAW);
}
if (!plane->buffer_ptr)
plane->buffer_ptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER,
GL_WRITE_ONLY);
mpi->planes[n] = plane->buffer_ptr;
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
mpi->planes[n] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
if (!mpi->planes[n]) {
unmap_image(p, mpi);
return false;
}
}
memset(mpi->bufs, 0, sizeof(mpi->bufs));
return true;
@@ -2309,28 +2320,28 @@ static void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)

assert(mpi->num_planes == p->plane_count);

mp_image_t mpi2 = *mpi;
bool pbo = false;
if (!vimg->planes[0].buffer_ptr && map_image(p, &mpi2)) {
mp_image_copy(&mpi2, mpi);
pbo = true;
mp_image_t pbo_mpi = *mpi;
bool pbo = map_image(p, &pbo_mpi);
if (pbo) {
mp_image_copy(&pbo_mpi, mpi);
if (unmap_image(p, &pbo_mpi)) {
mpi = &pbo_mpi;
} else {
MP_FATAL(p, "Video PBO upload failed. Disabling PBOs.\n");
pbo = false;
p->opts.pbo = 0;
}
}
vimg->image_flipped = mpi2.stride[0] < 0;

vimg->image_flipped = mpi->stride[0] < 0;
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
void *plane_ptr = mpi2.planes[n];
if (pbo) {
if (pbo)
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
MP_FATAL(p, "Video PBO upload failed. "
"Remove the 'pbo' suboption.\n");
plane->buffer_ptr = NULL;
plane_ptr = NULL; // PBO offset 0
}
gl->ActiveTexture(GL_TEXTURE0 + n);
gl->BindTexture(p->gl_target, plane->gl_texture);
glUploadTex(gl, p->gl_target, plane->gl_format, plane->gl_type,
plane_ptr, mpi2.stride[n], 0, 0, plane->w, plane->h, 0);
mpi->planes[n], mpi->stride[n], 0, 0, plane->w, plane->h, 0);
}
gl->ActiveTexture(GL_TEXTURE0);
if (pbo)