diff options
Diffstat (limited to 'video/decode')
-rw-r--r-- | video/decode/dec_video.h | 6 | ||||
-rw-r--r-- | video/decode/lavc.h | 19 | ||||
-rw-r--r-- | video/decode/lavc_dr1.c | 2 | ||||
-rw-r--r-- | video/decode/vd_lavc.c | 138 | ||||
-rw-r--r-- | video/decode/vdpau.c | 228 | ||||
-rw-r--r-- | video/decode/vdpau_old.c | 267 |
6 files changed, 587 insertions, 73 deletions
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h index 141442fa19..4ba052afd1 100644 --- a/video/decode/dec_video.h +++ b/video/decode/dec_video.h @@ -47,4 +47,10 @@ int vd_control(struct sh_video *sh_video, int cmd, void *arg); extern int divx_quality; +// Used to communicate hardware decoder API handles from VO to video decoder. +// The VO can set the context pointer for supported APIs. +struct mp_hwdec_info { + struct mp_vdpau_ctx *vdpau_ctx; +}; + #endif /* MPLAYER_DEC_VIDEO_H */ diff --git a/video/decode/lavc.h b/video/decode/lavc.h index 25ed2b8ac5..3611530400 100644 --- a/video/decode/lavc.h +++ b/video/decode/lavc.h @@ -10,7 +10,7 @@ #include "demux/stheader.h" #include "video/mp_image.h" -typedef struct ffmpeg_ctx { +typedef struct lavc_ctx { AVCodecContext *avctx; AVFrame *pic; struct hwdec *hwdec; @@ -23,11 +23,28 @@ typedef struct ffmpeg_ctx { enum AVDiscard skip_frame; const char *software_fallback_decoder; + // From VO + struct mp_hwdec_info *hwdec_info; + + // For free use by hwdec implementation + void *hwdec_priv; + + // Legacy bool do_dr1; struct FramePool *dr1_buffer_pool; struct mp_image_pool *non_dr1_pool; } vd_ffmpeg_ctx; +struct vd_lavc_hwdec_functions { + // If not-NULL, a 0 terminated list of IMGFMT_ formats. Only one of these + // formats is accepted when handling the libavcodec get_format callback. + const int *image_formats; + int (*init)(struct lavc_ctx *ctx); + void (*uninit)(struct lavc_ctx *ctx); + struct mp_image *(*allocate_image)(struct lavc_ctx *ctx, AVFrame *frame); + void (*fix_image)(struct lavc_ctx *ctx, struct mp_image *img); +}; + // lavc_dr1.c int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame); void mp_codec_release_buffer(AVCodecContext *s, AVFrame *frame); diff --git a/video/decode/lavc_dr1.c b/video/decode/lavc_dr1.c index 5dc73c3ea8..15fc44a445 100644 --- a/video/decode/lavc_dr1.c +++ b/video/decode/lavc_dr1.c @@ -137,7 +137,7 @@ static int alloc_buffer(FramePool *pool, AVCodecContext *s) int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame) { sh_video_t *sh = s->opaque; - struct ffmpeg_ctx *ctx = sh->context; + struct lavc_ctx *ctx = sh->context; if (!ctx->dr1_buffer_pool) { ctx->dr1_buffer_pool = av_mallocz(sizeof(*ctx->dr1_buffer_pool)); diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index 3ee1b40bb8..ea12126ffb 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -34,6 +34,7 @@ #include "config.h" #include "core/mp_msg.h" #include "core/options.h" +#include "core/bstr.h" #include "core/av_opts.h" #include "core/av_common.h" #include "core/codecs.h" @@ -50,7 +51,6 @@ #include "osdep/numcores.h" #include "video/csputils.h" -#include "libavcodec/avcodec.h" #include "lavc.h" #if AVPALETTE_SIZE != MP_PALETTE_SIZE @@ -62,8 +62,6 @@ static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec); static void uninit_avctx(sh_video_t *sh); static void setup_refcounting_hw(struct AVCodecContext *s); -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_hwdec(struct AVCodecContext *avctx, const enum PixelFormat *pix_fmt); @@ -94,17 +92,32 @@ enum hwdec_type { struct hwdec { enum hwdec_type api; const char *codec, *hw_codec; + const struct vd_lavc_hwdec_functions *fns; }; +const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau; +const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau_old; + static const struct hwdec hwdec_list[] = { - {HWDEC_VDPAU, "h264", "h264_vdpau"}, - {HWDEC_VDPAU, "wmv3", "wmv3_vdpau"}, - {HWDEC_VDPAU, "vc1", "vc1_vdpau"}, - {HWDEC_VDPAU, "mpegvideo", "mpegvideo_vdpau"}, - {HWDEC_VDPAU, "mpeg1video", "mpeg1video_vdpau"}, - {HWDEC_VDPAU, "mpeg2video", "mpegvideo_vdpau"}, - {HWDEC_VDPAU, "mpeg2", "mpeg2_vdpau"}, - {HWDEC_VDPAU, "mpeg4", "mpeg4_vdpau"}, +#ifdef CONFIG_VDPAU +#if HAVE_AV_CODEC_NEW_VDPAU_API + {HWDEC_VDPAU, "h264", NULL, &mp_vd_lavc_vdpau}, + {HWDEC_VDPAU, "wmv3", NULL, &mp_vd_lavc_vdpau}, + {HWDEC_VDPAU, "vc1", NULL, &mp_vd_lavc_vdpau}, + {HWDEC_VDPAU, "mpeg1video", NULL, &mp_vd_lavc_vdpau}, + {HWDEC_VDPAU, "mpeg2video", NULL, &mp_vd_lavc_vdpau}, + {HWDEC_VDPAU, "mpeg4", NULL, &mp_vd_lavc_vdpau}, +#else + {HWDEC_VDPAU, "h264", "h264_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "wmv3", "wmv3_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "vc1", "vc1_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "mpegvideo", "mpegvideo_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "mpeg1video", "mpeg1video_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "mpeg2video", "mpegvideo_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "mpeg2", "mpeg2_vdpau", &mp_vd_lavc_vdpau_old}, + {HWDEC_VDPAU, "mpeg4", "mpeg4_vdpau", &mp_vd_lavc_vdpau_old}, +#endif +#endif // CONFIG_VDPAU {HWDEC_VDA, "h264", "h264_vda"}, @@ -158,17 +171,33 @@ static int init(sh_video_t *sh, const char *decoder) ctx = sh->context = talloc_zero(NULL, vd_ffmpeg_ctx); ctx->non_dr1_pool = talloc_steal(ctx, mp_image_pool_new(16)); + if (bstr_endswith0(bstr0(decoder), "_vdpau")) { + mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "VDPAU decoder '%s' was requested. " + "This way of enabling hardware\ndecoding is not supported " + "anymore. Use --hwdec=vdpau instead.\nThe --hwdec-codec=... " + "option can be used to restrict which codecs are\nenabled, " + "otherwise all hardware decoding is tried for all codecs.\n", + decoder); + uninit(sh); + return 0; + } + struct hwdec *hwdec = find_hwcodec(sh->opts->hwdec_api, decoder); struct hwdec *use_hwdec = NULL; if (hwdec && hwdec_codec_allowed(sh, hwdec)) { - AVCodec *lavc_hwcodec = avcodec_find_decoder_by_name(hwdec->hw_codec); - if (lavc_hwcodec) { + if (hwdec->hw_codec) { + AVCodec *lavc_hwcodec = avcodec_find_decoder_by_name(hwdec->hw_codec); + if (lavc_hwcodec) { + ctx->software_fallback_decoder = talloc_strdup(ctx, decoder); + decoder = lavc_hwcodec->name; + use_hwdec = hwdec; + } else { + mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "Decoder '%s' not found in " + "libavcodec, using software decoding.\n", hwdec->hw_codec); + } + } else { ctx->software_fallback_decoder = talloc_strdup(ctx, decoder); - decoder = lavc_hwcodec->name; use_hwdec = hwdec; - } else { - mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "Decoder '%s' not found in " - "libavcodec, using software decoding.\n", hwdec->hw_codec); } } @@ -264,6 +293,8 @@ static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec) if (!lavc_codec) return; + ctx->hwdec_info = sh->hwdec_info; + ctx->do_dr1 = ctx->do_hw_dr1 = 0; ctx->pix_fmt = PIX_FMT_NONE; ctx->vo_initialized = 0; @@ -277,26 +308,15 @@ static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec) avctx->thread_count = lavc_param->threads; - // Hack to allow explicitly selecting vdpau hw decoders - if (!hwdec && (lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU)) { - ctx->hwdec = talloc(ctx, struct hwdec); - *ctx->hwdec = (struct hwdec) { - .api = HWDEC_VDPAU, - .codec = sh->gsh->codec, - .hw_codec = decoder, - }; - } - - if (ctx->hwdec && ctx->hwdec->api == HWDEC_VDPAU) { - assert(lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU); + if (ctx->hwdec && ctx->hwdec->fns) { ctx->do_hw_dr1 = true; avctx->thread_count = 1; - avctx->get_format = get_format_hwdec; + if (ctx->hwdec->fns->image_formats) + avctx->get_format = get_format_hwdec; setup_refcounting_hw(avctx); - if (ctx->hwdec->api == HWDEC_VDPAU) { - avctx->draw_horiz_band = draw_slice_hwdec; - avctx->slice_flags = - SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; + if (ctx->hwdec->fns->init(ctx) < 0) { + uninit_avctx(sh); + return; } } else { #if HAVE_AVUTIL_REFCOUNTING @@ -381,6 +401,9 @@ static void uninit_avctx(sh_video_t *sh) av_freep(&ctx->avctx); avcodec_free_frame(&ctx->pic); + if (ctx->hwdec && ctx->hwdec->fns) + ctx->hwdec->fns->uninit(ctx); + #if !HAVE_AVUTIL_REFCOUNTING mp_buffer_pool_free(&ctx->dr1_buffer_pool); #endif @@ -406,11 +429,6 @@ static int init_vo(sh_video_t *sh, AVFrame *frame) pix_fmt = ctx->avctx->pix_fmt; #endif - /* Reconfiguring filter/VO chain may invalidate direct rendering buffers - * we have allocated for libavcodec (including the VDPAU HW decoding - * case). Is it guaranteed that the code below only triggers in a situation - * with no busy direct rendering buffers for reference frames? - */ if (av_cmp_q(frame->sample_aspect_ratio, ctx->last_sample_aspect_ratio) || width != sh->disp_w || height != sh->disp_h || pix_fmt != ctx->pix_fmt || !ctx->vo_initialized) @@ -466,27 +484,19 @@ static enum PixelFormat get_format_hwdec(struct AVCodecContext *avctx, mp_msg(MSGT_DECVIDEO, MSGL_V, " %s", av_get_pix_fmt_name(fmt[i])); mp_msg(MSGT_DECVIDEO, MSGL_V, "\n"); - assert(ctx->hwdec); + assert(ctx->hwdec && ctx->hwdec->fns); for (int i = 0; fmt[i] != PIX_FMT_NONE; i++) { - int imgfmt = pixfmt2imgfmt(fmt[i]); - if (ctx->hwdec->api == HWDEC_VDPAU && IMGFMT_IS_VDPAU(imgfmt)) - return fmt[i]; + const int *okfmt = ctx->hwdec->fns->image_formats; + for (int n = 0; okfmt && okfmt[n]; n++) { + if (imgfmt2pixfmt(okfmt[n]) == fmt[i]) + return fmt[i]; + } } return PIX_FMT_NONE; } -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_surface_hwdec(struct sh_video *sh, AVFrame *pic) { vd_ffmpeg_ctx *ctx = sh->context; @@ -506,24 +516,7 @@ static struct mp_image *get_surface_hwdec(struct sh_video *sh, AVFrame *pic) if (!IMGFMT_IS_HWACCEL(imgfmt)) return NULL; - // Video with non mod-16 width/height will have allocation sizes that are - // rounded up. This conflicts with our video size change detection and - // leads to an endless loop. On the other hand, vdpau seems to round up - // frame allocations internally. So use the original video resolution - // instead. - AVFrame pic_resized = *pic; - pic_resized.width = ctx->avctx->width; - pic_resized.height = ctx->avctx->height; - - if (init_vo(sh, &pic_resized) < 0) - return NULL; - - assert(IMGFMT_IS_HWACCEL(ctx->best_csp)); - - struct mp_image *mpi = NULL; - - struct vf_instance *vf = sh->vfilter; - vf->control(vf, VFCTRL_HWDEC_ALLOC_SURFACE, &mpi); + struct mp_image *mpi = ctx->hwdec->fns->allocate_image(ctx, pic); if (mpi) { for (int i = 0; i < 4; i++) @@ -698,6 +691,9 @@ static int decode(struct sh_video *sh, struct demux_packet *packet, struct mp_image *mpi = image_from_decoder(sh); assert(mpi->planes[0]); + if (ctx->hwdec && ctx->hwdec->fns && ctx->hwdec->fns->fix_image) + ctx->hwdec->fns->fix_image(ctx, mpi); + mpi->colorspace = ctx->image_params.colorspace; mpi->levels = ctx->image_params.colorlevels; mpi->chroma_location = ctx->image_params.chroma_location; diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c new file mode 100644 index 0000000000..512f9170a3 --- /dev/null +++ b/video/decode/vdpau.c @@ -0,0 +1,228 @@ +/* + * This file is part of mpv. + * + * mpv 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. + * + * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <assert.h> + +#include <libavcodec/vdpau.h> +#include <libavutil/common.h> + +#include "lavc.h" +#include "core/mp_common.h" +#include "video/fmt-conversion.h" +#include "video/vdpau.h" +#include "video/decode/dec_video.h" + +struct priv { + struct mp_vdpau_ctx *mpvdp; + struct vdp_functions *vdp; + VdpDevice vdp_device; + uint64_t preemption_counter; + + AVVDPAUContext context; + + int vid_width; + int vid_height; +}; + +struct profile_entry { + enum AVCodecID av_codec; + int ff_profile; + VdpDecoderProfile vdp_profile; + int maxrefs; +}; + +#define PE(av_codec_id, ff_profile, vdp_dcoder_profile, maxrefs) \ + {AV_CODEC_ID_ ## av_codec_id, \ + FF_PROFILE_ ## ff_profile, \ + VDP_DECODER_PROFILE_ ## vdp_dcoder_profile, \ + maxrefs} + +static const struct profile_entry profiles[] = { + PE(MPEG1VIDEO, UNKNOWN, MPEG1, 2), + PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2_SIMPLE, 2), + PE(MPEG2VIDEO, UNKNOWN, MPEG2_MAIN, 2), + PE(H264, H264_BASELINE, H264_BASELINE, 16), + PE(H264, H264_CONSTRAINED_BASELINE, H264_BASELINE, 16), + PE(H264, H264_MAIN, H264_MAIN, 16), + PE(H264, UNKNOWN, H264_HIGH, 16), + PE(WMV3, VC1_SIMPLE, VC1_SIMPLE, 2), + PE(WMV3, VC1_MAIN, VC1_MAIN, 2), + PE(WMV3, UNKNOWN, VC1_ADVANCED, 2), + PE(VC1, VC1_SIMPLE, VC1_SIMPLE, 2), + PE(VC1, VC1_MAIN, VC1_MAIN, 2), + PE(VC1, UNKNOWN, VC1_ADVANCED, 2), + PE(MPEG4, MPEG4_SIMPLE, MPEG4_PART2_SP, 2), + PE(MPEG4, UNKNOWN, MPEG4_PART2_ASP,2), +}; + +// libavcodec absolutely wants a non-NULL render callback +static VdpStatus dummy_render( + VdpDecoder decoder, + VdpVideoSurface target, + VdpPictureInfo const * picture_info, + uint32_t bitstream_buffer_count, + VdpBitstreamBuffer const * bitstream_buffers) +{ + return VDP_STATUS_DISPLAY_PREEMPTED; +} + +static void mark_uninitialized(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + p->vdp_device = VDP_INVALID_HANDLE; + p->context.decoder = VDP_INVALID_HANDLE; + p->context.render = dummy_render; +} + +static int handle_preemption(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + if (!mp_vdpau_status_ok(p->mpvdp)) + return -1; + + // Mark objects as destroyed if preemption+reinit occured + if (p->preemption_counter < p->mpvdp->preemption_counter) { + p->preemption_counter = p->mpvdp->preemption_counter; + mark_uninitialized(ctx); + } + + p->vdp_device = p->mpvdp->vdp_device; + p->vdp = p->mpvdp->vdp; + + return 0; +} + +static bool create_vdp_decoder(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + struct vdp_functions *vdp = p->mpvdp->vdp; + VdpStatus vdp_st; + + if (handle_preemption(ctx) < 0) + return false; + + if (p->context.decoder != VDP_INVALID_HANDLE) + vdp->decoder_destroy(p->context.decoder); + + const struct profile_entry *pe = NULL; + for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) { + if (profiles[n].av_codec == ctx->avctx->codec_id && + (profiles[n].ff_profile == ctx->avctx->profile || + profiles[n].ff_profile == FF_PROFILE_UNKNOWN)) + { + pe = &profiles[n]; + break; + } + } + + if (!pe) { + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unknown codec!\n"); + goto fail; + } + + vdp_st = vdp->decoder_create(p->vdp_device, pe->vdp_profile, + p->vid_width, p->vid_height, pe->maxrefs, + &p->context.decoder); + CHECK_ST_WARNING("Failed creating VDPAU decoder"); + p->context.render = p->vdp->decoder_render; + if (vdp_st != VDP_STATUS_OK) + goto fail; + return true; + +fail: + p->context.decoder = VDP_INVALID_HANDLE; + p->context.render = dummy_render; + return false; +} + +static struct mp_image *allocate_image(struct lavc_ctx *ctx, AVFrame *frame) +{ + struct priv *p = ctx->hwdec_priv; + + if (frame->format != AV_PIX_FMT_VDPAU) + return NULL; + + // frame->width/height lie. Using them breaks with non-mod 16 video. + int w = ctx->avctx->width; + int h = ctx->avctx->height; + + handle_preemption(ctx); + + if (w != p->vid_width || h != p->vid_height || + p->context.decoder == VDP_INVALID_HANDLE) + { + p->vid_width = w; + p->vid_height = h; + if (!create_vdp_decoder(ctx)) + return NULL; + } + + VdpChromaType chroma; + mp_vdpau_get_format(IMGFMT_VDPAU, &chroma, NULL); + + return mp_vdpau_get_video_surface(p->mpvdp, IMGFMT_VDPAU, chroma, w, h); +} + +static void uninit(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + if (!p) + return; + + if (p->context.decoder != VDP_INVALID_HANDLE) + p->vdp->decoder_destroy(p->context.decoder); + + // Free bitstream buffers allocated by libavcodec + av_freep(&p->context.bitstream_buffers); + + talloc_free(p); + + ctx->hwdec_priv = NULL; +} + +static int init(struct lavc_ctx *ctx) +{ + if (!ctx->hwdec_info || !ctx->hwdec_info->vdpau_ctx) + return -1; + + struct priv *p = talloc_ptrtype(NULL, p); + *p = (struct priv) { + .mpvdp = ctx->hwdec_info->vdpau_ctx, + }; + ctx->hwdec_priv = p; + + p->preemption_counter = p->mpvdp->preemption_counter; + mark_uninitialized(ctx); + + if (handle_preemption(ctx) < 0) + return -1; + + ctx->avctx->hwaccel_context = &p->context; + + return 0; +} + +const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau = { + .image_formats = (const int[]) {IMGFMT_VDPAU, 0}, + .init = init, + .uninit = uninit, + .allocate_image = allocate_image, +}; diff --git a/video/decode/vdpau_old.c b/video/decode/vdpau_old.c new file mode 100644 index 0000000000..e9c88b69ea --- /dev/null +++ b/video/decode/vdpau_old.c @@ -0,0 +1,267 @@ +/* + * VDPAU video output driver + * + * Copyright (C) 2008 NVIDIA + * Copyright (C) 2009 Uoti Urpala + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stddef.h> +#include <assert.h> + +#include <libavcodec/vdpau.h> +#include <libavutil/common.h> + +#include "lavc.h" +#include "video/fmt-conversion.h" +#include "video/vdpau.h" +#include "video/decode/dec_video.h" + +struct priv { + struct mp_vdpau_ctx *mpvdp; + struct vdp_functions *vdp; + VdpDevice vdp_device; + uint64_t preemption_counter; + + int image_format; + int vid_width; + int vid_height; + + VdpDecoder decoder; + int decoder_max_refs; +}; + +static void mark_uninitialized(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + p->vdp_device = VDP_INVALID_HANDLE; + p->decoder = VDP_INVALID_HANDLE; +} + +static int handle_preemption(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + if (!mp_vdpau_status_ok(p->mpvdp)) + return -1; + + // Mark objects as destroyed if preemption+reinit occured + if (p->preemption_counter < p->mpvdp->preemption_counter) { + p->preemption_counter = p->mpvdp->preemption_counter; + mark_uninitialized(ctx); + } + + p->vdp_device = p->mpvdp->vdp_device; + p->vdp = p->mpvdp->vdp; + + return 0; +} + +static bool create_vdp_decoder(struct lavc_ctx *ctx, int max_refs) +{ + struct priv *p = ctx->hwdec_priv; + struct vdp_functions *vdp = p->mpvdp->vdp; + VdpStatus vdp_st; + VdpDecoderProfile vdp_decoder_profile; + + if (handle_preemption(ctx) < 0) + return false; + + if (p->decoder != VDP_INVALID_HANDLE) + vdp->decoder_destroy(p->decoder); + + switch (p->image_format) { + case IMGFMT_VDPAU_MPEG1: + vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1; + break; + case IMGFMT_VDPAU_MPEG2: + vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN; + break; + case IMGFMT_VDPAU_H264: + vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH; + mp_msg(MSGT_VO, MSGL_V, "[vdpau] Creating H264 hardware decoder " + "for %d reference frames.\n", max_refs); + break; + case IMGFMT_VDPAU_WMV3: + vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN; + break; + case IMGFMT_VDPAU_VC1: + vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED; + break; + case IMGFMT_VDPAU_MPEG4: + vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP; + break; + default: + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unknown image format!\n"); + goto fail; + } + vdp_st = vdp->decoder_create(p->vdp_device, vdp_decoder_profile, + p->vid_width, p->vid_height, max_refs, + &p->decoder); + CHECK_ST_WARNING("Failed creating VDPAU decoder"); + if (vdp_st != VDP_STATUS_OK) + goto fail; + p->decoder_max_refs = max_refs; + return true; + +fail: + p->decoder = VDP_INVALID_HANDLE; + p->decoder_max_refs = 0; + return false; +} + +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 lavc_ctx *ctx = sh->context; + struct priv *p = ctx->hwdec_priv; + struct vdp_functions *vdp = p->vdp; + VdpStatus vdp_st; + + if (handle_preemption(ctx) < 0) + return; + + struct vdpau_render_state *rndr = (void *)src->data[0]; + + int max_refs = p->image_format == IMGFMT_VDPAU_H264 ? + rndr->info.h264.num_ref_frames : 2; + if ((p->decoder == VDP_INVALID_HANDLE || p->decoder_max_refs < max_refs) + && !create_vdp_decoder(ctx, max_refs)) + return; + + vdp_st = vdp->decoder_render(p->decoder, rndr->surface, + (void *)&rndr->info, + rndr->bitstream_buffers_used, + rndr->bitstream_buffers); + CHECK_ST_WARNING("Failed VDPAU decoder rendering"); +} + +static void release_surface(void *ptr) +{ + struct vdpau_render_state *state = ptr; + // Free bitstream buffers allocated by libavcodec + av_freep(&state->bitstream_buffers); + talloc_free(state); +} + +static struct mp_image *allocate_image(struct lavc_ctx *ctx, AVFrame *frame) +{ + struct priv *p = ctx->hwdec_priv; + int imgfmt = pixfmt2imgfmt(frame->format); + + if (!IMGFMT_IS_VDPAU(imgfmt)) + return NULL; + + // frame->width/height lie. Using them breaks with non-mod 16 video. + int w = ctx->avctx->width; + int h = ctx->avctx->height; + + if (w != p->vid_width || h != p->vid_height || imgfmt != p->image_format) { + p->vid_width = w; + p->vid_height = h; + p->image_format = imgfmt; + if (!create_vdp_decoder(ctx, 2)) + return NULL; + } + + VdpChromaType chroma; + mp_vdpau_get_format(p->image_format, &chroma, NULL); + + struct mp_image *img = + mp_vdpau_get_video_surface(p->mpvdp, imgfmt, chroma, w, h); + + if (!img) + return NULL; + + // Create chained reference for vdpau_render_state. This will track the + // lifetime of the actual reference too. + // This is quite roundabout, but at least it allows us to share the + // surface allocator in vo_vdpau.c with the new vdpau code. + + struct vdpau_render_state *state = talloc_ptrtype(NULL, state); + memset(state, 0, sizeof(*state)); + state->surface = (VdpVideoSurface)(intptr_t)img->planes[3]; + + talloc_steal(state, img); + + struct mp_image *new = mp_image_new_custom_ref(img, state, release_surface); + new->planes[0] = (void *)state; + return new; +} + +static void uninit(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + if (!p) + return; + + if (p->decoder != VDP_INVALID_HANDLE) + p->vdp->decoder_destroy(p->decoder); + + talloc_free(p); + ctx->hwdec_priv = NULL; +} + +static int init(struct lavc_ctx *ctx) +{ + if (!ctx->hwdec_info || !ctx->hwdec_info->vdpau_ctx) + return -1; + + struct priv *p = talloc_ptrtype(NULL, p); + *p = (struct priv) { + .mpvdp = ctx->hwdec_info->vdpau_ctx, + }; + ctx->hwdec_priv = p; + + p->preemption_counter = p->mpvdp->preemption_counter; + mark_uninitialized(ctx); + + if (handle_preemption(ctx) < 0) + return -1; + + AVCodecContext *avctx = ctx->avctx; + + avctx->draw_horiz_band = draw_slice_hwdec; + avctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; + + return 0; +} + +static void fix_image(struct lavc_ctx *ctx, struct mp_image *img) +{ + // Make it follow the convention of the "new" vdpau decoder + struct vdpau_render_state *rndr = (void *)img->planes[0]; + img->planes[0] = (void *)"dummy"; // must be non-NULL, otherwise arbitrary + img->planes[3] = (void *)(intptr_t)rndr->surface; +} + +const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau_old = { + .image_formats = (const int[]) { + IMGFMT_VDPAU_MPEG1, IMGFMT_VDPAU_MPEG2, IMGFMT_VDPAU_H264, + IMGFMT_VDPAU_WMV3, IMGFMT_VDPAU_VC1, IMGFMT_VDPAU_MPEG4, + 0 + }, + .init = init, + .uninit = uninit, + .allocate_image = allocate_image, + .fix_image = fix_image, +}; |