summaryrefslogtreecommitdiffstats
path: root/video/decode/vdpau.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/decode/vdpau.c')
-rw-r--r--video/decode/vdpau.c228
1 files changed, 228 insertions, 0 deletions
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,
+};