video: make vdpau hardware decoding not use DR code path

vdpau hardware decoding used the DR (direct rendering) path to let the
decoder query a surface from the VO. Special-case the HW decoding path
instead, to make it separate from DR.
This commit is contained in:
wm4 2012-11-06 15:27:44 +01:00
parent 0d1aca1289
commit 2d8fb838d7
5 changed files with 117 additions and 66 deletions

View File

@ -62,9 +62,12 @@ static const vd_info_t info = {
#error palette too large, adapt libmpcodecs/vf.c:vf_get_image
#endif
#define MAX_NUM_MPI 50
typedef struct {
AVCodecContext *avctx;
AVFrame *pic;
struct mp_image hwdec_mpi[MAX_NUM_MPI];
enum PixelFormat pix_fmt;
int do_dr1;
int vo_initialized;
@ -82,11 +85,15 @@ typedef struct {
static int get_buffer(AVCodecContext *avctx, AVFrame *pic);
static void release_buffer(AVCodecContext *avctx, AVFrame *pic);
static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic);
static void release_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic);
static void draw_slice_hwdec(struct AVCodecContext *s, const AVFrame *src,
int offset[4], int y, int type, int height);
static enum PixelFormat get_format(struct AVCodecContext *avctx,
const enum PixelFormat *pix_fmt);
static enum PixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum PixelFormat *pix_fmt);
static void uninit(struct sh_video *sh);
const m_option_t lavc_decode_opts_conf[] = {
@ -267,10 +274,9 @@ static int init(sh_video_t *sh)
if (lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU) {
ctx->do_dr1 = true;
avctx->thread_count = 1;
avctx->get_format = get_format;
avctx->get_buffer = get_buffer;
avctx->release_buffer = release_buffer;
avctx->reget_buffer = get_buffer;
avctx->get_format = get_format_hwdec;
avctx->get_buffer = get_buffer_hwdec;
avctx->release_buffer = release_buffer_hwdec;
avctx->draw_horiz_band = draw_slice_hwdec;
if (lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU)
mp_msg(MSGT_DECVIDEO, MSGL_V, "[VD_FFMPEG] VDPAU hardware "
@ -296,7 +302,7 @@ static int init(sh_video_t *sh)
"%d threads if supported.\n", avctx->thread_count);
}
if (ctx->do_dr1) {
if (ctx->do_dr1 && avctx->get_buffer != get_buffer_hwdec) {
avctx->flags |= CODEC_FLAG_EMU_EDGE;
avctx->get_buffer = get_buffer;
avctx->release_buffer = release_buffer;
@ -444,16 +450,6 @@ static void uninit(sh_video_t *sh)
talloc_free(ctx);
}
static void draw_slice_hwdec(struct AVCodecContext *s,
const AVFrame *src, int offset[4],
int y, int type, int height)
{
sh_video_t *sh = s->opaque;
struct vf_instance *vf = sh->vfilter;
void *state_ptr = src->data[0];
vf->control(vf, VFCTRL_HWDEC_DECODER_RENDER, state_ptr);
}
static int init_vo(sh_video_t *sh, enum PixelFormat pix_fmt)
{
vd_ffmpeg_ctx *ctx = sh->context;
@ -499,6 +495,99 @@ static int init_vo(sh_video_t *sh, enum PixelFormat pix_fmt)
return 0;
}
static enum PixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum PixelFormat *fmt)
{
sh_video_t *sh = avctx->opaque;
int i;
for (i = 0; fmt[i] != PIX_FMT_NONE; i++) {
int imgfmt = pixfmt2imgfmt(fmt[i]);
if (!IMGFMT_IS_HWACCEL(imgfmt))
continue;
mp_msg(MSGT_DECVIDEO, MSGL_V, "[VD_FFMPEG] Trying pixfmt=%d.\n", i);
if (init_vo(sh, fmt[i]) >= 0)
break;
}
return fmt[i];
}
static void draw_slice_hwdec(struct AVCodecContext *s,
const AVFrame *src, int offset[4],
int y, int type, int height)
{
sh_video_t *sh = s->opaque;
struct vf_instance *vf = sh->vfilter;
void *state_ptr = src->data[0];
vf->control(vf, VFCTRL_HWDEC_DECODER_RENDER, state_ptr);
}
static struct mp_image *get_image_hwdec(vd_ffmpeg_ctx *ctx)
{
for (int n = 0; n < MAX_NUM_MPI; n++) {
struct mp_image *cur = &ctx->hwdec_mpi[n];
if (cur->usage_count == 0) {
*cur = (struct mp_image) {
.number = n,
.imgfmt = ctx->best_csp,
.usage_count = 1,
};
return cur;
}
}
return NULL;
}
static int get_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
{
sh_video_t *sh = avctx->opaque;
vd_ffmpeg_ctx *ctx = sh->context;
assert(IMGFMT_IS_HWACCEL(ctx->best_csp));
// Uncertain whether this is needed; at least deals with VO/filter failures
if (init_vo(sh, avctx->pix_fmt) < 0) {
avctx->release_buffer = avcodec_default_release_buffer;
avctx->get_buffer = avcodec_default_get_buffer;
avctx->reget_buffer = avcodec_default_reget_buffer;
return avctx->get_buffer(avctx, pic);
}
struct mp_image *mpi = get_image_hwdec(ctx);
if (!mpi)
return -1;
struct vf_instance *vf = sh->vfilter;
vf->control(vf, VFCTRL_HWDEC_GET_SURFACE, mpi);
for (int i = 0; i < 4; i++)
pic->data[i] = mpi->planes[i];
pic->opaque = mpi;
pic->type = FF_BUFFER_TYPE_USER;
/* The libavcodec reordered_opaque functionality is implemented by
* a similar copy in avcodec_default_get_buffer() and without a
* workaround like this it'd stop working when a custom buffer
* callback is used.
*/
pic->reordered_opaque = avctx->reordered_opaque;
return 0;
}
static void release_buffer_hwdec(AVCodecContext *avctx, AVFrame *pic)
{
mp_image_t *mpi = pic->opaque;
assert(pic->type == FF_BUFFER_TYPE_USER);
assert(mpi);
assert(mpi->usage_count > 0);
mpi->usage_count--;
for (int i = 0; i < 4; i++)
pic->data[i] = NULL;
}
static int get_buffer(AVCodecContext *avctx, AVFrame *pic)
{
sh_video_t *sh = avctx->opaque;
@ -652,12 +741,8 @@ static struct mp_image *decode(struct sh_video *sh, struct demux_packet *packet,
AVFrame *pic = ctx->pic;
AVCodecContext *avctx = ctx->avctx;
mp_image_t *mpi = NULL;
int dr1 = ctx->do_dr1;
AVPacket pkt;
if (!dr1)
avctx->draw_horiz_band = NULL;
if (flags & 2)
avctx->skip_frame = AVDISCARD_ALL;
else if (flags & 1)
@ -682,7 +767,7 @@ static struct mp_image *decode(struct sh_video *sh, struct demux_packet *packet,
ret = avcodec_decode_video2(avctx, pic, &got_picture, &pkt);
*reordered_pts = (union pts){.i = pic->reordered_opaque}.d;
dr1 = ctx->do_dr1;
int dr1 = ctx->do_dr1;
if (ret < 0)
mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Error while decoding frame!\n");
@ -751,23 +836,6 @@ static struct mp_image *decode(struct sh_video *sh, struct demux_packet *packet,
return mpi;
}
static enum PixelFormat get_format(struct AVCodecContext *avctx,
const enum PixelFormat *fmt)
{
sh_video_t *sh = avctx->opaque;
int i;
for (i = 0; fmt[i] != PIX_FMT_NONE; i++) {
int imgfmt = pixfmt2imgfmt(fmt[i]);
if (!IMGFMT_IS_HWACCEL(imgfmt))
continue;
mp_msg(MSGT_DECVIDEO, MSGL_V, "[VD_FFMPEG] Trying pixfmt=%d.\n", i);
if (init_vo(sh, fmt[i]) >= 0)
break;
}
return fmt[i];
}
static int control(sh_video_t *sh, int cmd, void *arg)
{
vd_ffmpeg_ctx *ctx = sh->context;

View File

@ -98,6 +98,7 @@ struct vf_ctrl_screenshot {
#define VFCTRL_SET_EQUALIZER 6 // set color options (brightness,contrast etc)
#define VFCTRL_GET_EQUALIZER 8 // get color options (brightness,contrast etc)
#define VFCTRL_HWDEC_DECODER_RENDER 9 // vdpau hw decoding
#define VFCTRL_HWDEC_GET_SURFACE 10 // vdpau hw decoding
#define VFCTRL_SCREENSHOT 14 // Take screenshot, arg is vf_ctrl_screenshot
#define VFCTRL_INIT_OSD 15 // Filter OSD renderer present?
#define VFCTRL_SET_DEINTERLACE 18 // Set deinterlacing status

View File

@ -105,6 +105,8 @@ static int control(struct vf_instance *vf, int request, void *data)
}
case VFCTRL_HWDEC_DECODER_RENDER:
return vo_control(video_out, VOCTRL_HWDEC_DECODER_RENDER, data);
case VFCTRL_HWDEC_GET_SURFACE:
return vo_control(video_out, VOCTRL_HWDEC_GET_SURFACE, data);
}
return CONTROL_UNKNOWN;
}
@ -119,16 +121,6 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
return flags;
}
static void get_image(struct vf_instance *vf,
mp_image_t *mpi)
{
if (!video_out->config_ok)
return;
// GET_IMAGE is required for hardware-accelerated formats
if (IMGFMT_IS_HWACCEL(mpi->imgfmt))
vo_control(video_out, VOCTRL_GET_IMAGE, mpi);
}
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
{
if (!video_out->config_ok)
@ -151,7 +143,6 @@ static int vf_open(vf_instance_t *vf, char *args)
vf->config = config;
vf->control = control;
vf->query_format = query_format;
vf->get_image = get_image;
vf->put_image = put_image;
vf->uninit = uninit;
vf->priv = calloc(1, sizeof(struct vf_priv_s));

View File

@ -44,8 +44,7 @@ enum mp_voctrl {
VOCTRL_PAUSE,
/* start/resume playback */
VOCTRL_RESUME,
/* libmpcodecs direct rendering */
VOCTRL_GET_IMAGE,
VOCTRL_GET_PANSCAN,
VOCTRL_SET_PANSCAN,
VOCTRL_SET_EQUALIZER, // struct voctrl_set_equalizer_args
@ -54,6 +53,7 @@ enum mp_voctrl {
/* for vdpau hardware decoding */
VOCTRL_HWDEC_DECODER_RENDER, // pointer to hw state
VOCTRL_HWDEC_GET_SURFACE, // struct mp_image
VOCTRL_NEWFRAME,
VOCTRL_SKIPFRAME,

View File

@ -1307,7 +1307,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
struct vdpau_render_state *rndr;
if (IMGFMT_IS_VDPAU(vc->image_format)) {
rndr = mpi->priv;
rndr = (struct vdpau_render_state *)mpi->planes[0];
reserved_mpi = mpi;
} else {
rndr = get_surface(vo, vc->deint_counter);
@ -1394,31 +1394,22 @@ static struct mp_image *get_window_screenshot(struct vo *vo)
return image;
}
static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
static uint32_t get_decoder_surface(struct vo *vo, mp_image_t *mpi)
{
struct vdpctx *vc = vo->priv;
struct vdpau_render_state *rndr;
// no dr for non-decoding for now
if (!IMGFMT_IS_VDPAU(vc->image_format))
return VO_FALSE;
if (mpi->type != MP_IMGTYPE_NUMBERED)
return VO_FALSE;
rndr = get_surface(vo, mpi->number);
if (!rndr) {
mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
"get_image\n");
"get_decoder_surface\n");
// TODO: this probably breaks things forever, provide a dummy buffer?
return VO_FALSE;
}
mpi->flags |= MP_IMGFLAG_DIRECT;
mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = 0;
mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = NULL;
// hack to get around a check and to avoid a special-case in vd_ffmpeg.c
mpi->planes[0] = (void *)rndr;
mpi->num_planes = 1;
mpi->priv = rndr;
return VO_TRUE;
}
@ -1598,8 +1589,8 @@ static int control(struct vo *vo, uint32_t request, void *data)
if (vc->dropped_frame)
vo->want_redraw = true;
return true;
case VOCTRL_GET_IMAGE:
return get_image(vo, data);
case VOCTRL_HWDEC_GET_SURFACE:
return get_decoder_surface(vo, data);
case VOCTRL_HWDEC_DECODER_RENDER:
return decoder_render(vo, data);
case VOCTRL_BORDER: