[vlc-devel] [PATCH 2/2] vdpau: VDPAU hardware decoding with avcodec
Rémi Denis-Courmont
remi at remlab.net
Tue Jan 8 21:36:46 CET 2013
Note that due to a design bug in libavcodec, the appropriate VDPAU
decoder must be enforced. VDPAU support does not follow the normal
hwaccel system. For example:
# vlc --avcodec-codec h264_vdpau --avcodec-hw vdpau --noaudio --nospu
MPEG-4 ASP & AVC seem to work. MPEG2 requires a hack (#if 0) to work
due to another design bug in libavcodec. WMV3, VC-1 and MPEG1 were not
tested.
TBD: Remove static variable.
TBD: Set draw_horiz_bands correctly when VDPAU is not used.
---
modules/codec/Modules.am | 10 +
modules/codec/avcodec/vdpau.c | 436 +++++++++++++++++++++++++++++++++++++++++
modules/codec/avcodec/video.c | 8 +-
3 files changed, 452 insertions(+), 2 deletions(-)
create mode 100644 modules/codec/avcodec/vdpau.c
diff --git a/modules/codec/Modules.am b/modules/codec/Modules.am
index d65f20a..3babcca 100644
--- a/modules/codec/Modules.am
+++ b/modules/codec/Modules.am
@@ -153,6 +153,16 @@ if HAVE_AVCODEC_VDA
libvlc_LTLIBRARIES += libvda_plugin.la
endif
+libvdpau_plugin_la_SOURCES = avcodec/vdpau.c
+libvdpau_plugin_la_CFLAGS = $(AM_CFLAGS) $(VDPAU_CFLAGS) \
+ $(X_CFLAGS) $(CFLAGS_avcodec)
+libvdpau_plugin_la_LIBADD = $(AM_LIBADD) $(VDPAU_LIBS) \
+ $(X_LIBS) $(X_PRE_LIBS) -lX11 $(LIBS_avcodec)
+libvdpau_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_avcodec)
+### XXX
+VDPAU_LIBS = -lvdpau
+EXTRA_LTLIBRARIES += libvdpau_plugin.la
+
### XWD ###
libxwd_plugin_la_SOURCES = xwd.c
libxwd_plugin_la_CFLAGS = $(AM_CFLAGS) $(XPROTO_CFLAGS)
diff --git a/modules/codec/avcodec/vdpau.c b/modules/codec/avcodec/vdpau.c
new file mode 100644
index 0000000..954d4e4
--- /dev/null
+++ b/modules/codec/avcodec/vdpau.c
@@ -0,0 +1,436 @@
+/*****************************************************************************
+ * vdpau.c: VDPAU decoder for libav
+ *****************************************************************************
+ * Copyright (C) 2012 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <libavutil/mem.h>
+#include <libavcodec/avcodec.h>
+#include <libavcodec/vdpau.h>
+#include <vdpau/vdpau.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_fourcc.h>
+#include <vlc_picture.h>
+
+#include <X11/Xlib.h>
+#include <vdpau/vdpau_x11.h>
+#include <vlc_xlib.h>
+
+#include "avcodec.h"
+#include "va.h"
+
+static int Open (vlc_va_t *, int, const es_format_t *);
+static void Close (vlc_va_t *);
+
+vlc_module_begin ()
+ set_description (N_("Video Decode and Presentation API for Unix (VDPAU)"))
+ set_capability ("hw decoder", 100)
+ set_category (CAT_INPUT)
+ set_subcategory (SUBCAT_INPUT_VCODEC)
+ set_callbacks (Open, Close)
+vlc_module_end ()
+
+#define MAX_SURFACES (16+1)
+
+typedef struct vdpau_render_state vlc_va_surface_t;
+
+struct vlc_va_sys_t
+{
+ VdpDevice device;
+ VdpDecoder decoder;
+ VdpDecoderProfile profile;
+ vlc_va_surface_t surfaces[MAX_SURFACES];
+ uint32_t locked;
+ uint16_t width;
+ uint16_t height;
+ void *display;
+
+ VdpGetErrorString *GetErrorString;
+ VdpGetInformationString *GetInformationString;
+ VdpDeviceDestroy *DeviceDestroy;
+ VdpVideoSurfaceCreate *VideoSurfaceCreate;
+ VdpVideoSurfaceDestroy *VideoSurfaceDestroy;
+ VdpVideoSurfaceGetBitsYCbCr *VideoSurfaceGetBitsYCbCr;
+ VdpDecoderCreate *DecoderCreate;
+ VdpDecoderDestroy *DecoderDestroy;
+ VdpDecoderRender *DecoderRender;
+};
+
+static vlc_va_surface_t *GetSurface (vlc_va_t *va)
+{
+ vlc_va_sys_t *sys = va->sys;
+
+ for (unsigned i = 0; i < MAX_SURFACES; i++)
+ {
+ vlc_va_surface_t *surface = sys->surfaces + i;
+
+ if (surface->surface == VDP_INVALID_HANDLE)
+ continue;
+ if (sys->locked & (1 << i))
+ continue;
+ sys->locked |= 1 << i;
+ return surface;
+ }
+ return NULL;
+}
+
+static void PutSurface (vlc_va_t *va, vlc_va_surface_t *surface)
+{
+ vlc_va_sys_t *sys = va->sys;
+ unsigned i = surface - sys->surfaces;
+
+ assert (surface->surface != VDP_INVALID_HANDLE);
+ assert (sys->locked & (1 << i));
+ sys->locked &= ~(1 << i);
+}
+
+static int Lock (vlc_va_t *va, AVFrame *ff)
+{
+ vlc_va_surface_t *surface = GetSurface (va);
+
+ if (surface == NULL)
+ {
+ msg_Err (va, "no free surfaces");
+ return VLC_EGENERIC;
+ }
+
+ ff->data[0] = (void *)surface;
+ ff->linesize[0] = 0;
+ for (unsigned i = 1; i < AV_NUM_DATA_POINTERS; i++)
+ {
+ ff->data[i] = NULL;
+ ff->linesize[i] = 0;
+ }
+ msg_Dbg (va, "%2ju got buffer (surface %u)", surface - va->sys->surfaces,
+ surface->surface);
+ return VLC_SUCCESS;
+}
+
+static void Unlock (vlc_va_t *va, AVFrame *ff)
+{
+ vlc_va_surface_t *surface = (void *)(ff->data[0]);
+
+ PutSurface (va, surface);
+ ff->data[0] = NULL;
+ msg_Dbg (va, "%2ju released buffer (surface %u)",
+ surface - va->sys->surfaces, surface->surface);
+}
+
+static int Render (vlc_va_t *va, const AVFrame *ff)
+{
+ vlc_va_sys_t *sys = va->sys;
+ vlc_va_surface_t *surface = (void *)(ff->data[0]);
+ VdpStatus err;
+
+ msg_Dbg (va, "%2ju drawing buffer (surface %u): %u/%u @ %p",
+ surface - sys->surfaces, surface->surface,
+ surface->bitstream_buffers_used,
+ surface->bitstream_buffers_allocated,
+ surface->bitstream_buffers);
+ assert (sys->locked & (1 << (surface - sys->surfaces)));
+
+ err = sys->DecoderRender (sys->decoder, surface->surface,
+ (void *)&surface->info,
+ surface->bitstream_buffers_used,
+ surface->bitstream_buffers);
+ if (err != VDP_STATUS_OK)
+ {
+ msg_Err (va, "decoder render failure: %s", sys->GetErrorString (err));
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+static vlc_va_t *ugly_hack;
+
+static void DrawHorizBand (struct AVCodecContext *ctx, const AVFrame *ff,
+ int offset[AV_NUM_DATA_POINTERS],
+ int y, int type, int height)
+{
+ Render (ugly_hack, ff);
+ (void)ctx;
+ (void) offset; (void) y; (void) type; (void) height;
+}
+
+static int Copy (vlc_va_t *va, picture_t *pic, AVFrame *ff)
+{
+ vlc_va_sys_t *sys = va->sys;
+ vlc_va_surface_t *surface = (void *)(ff->data[0]);
+ void *planes[3];
+ uint32_t pitches[3];
+ VdpStatus err;
+
+ for (unsigned i = 0; i < 3; i++)
+ {
+ planes[i] = pic->p[i].p_pixels;
+ pitches[i] = pic->p[i].i_pitch;
+ }
+
+ msg_Dbg (va, "%2ju copying buffer (surface %u)", surface - sys->surfaces,
+ surface->surface);
+ assert (sys->locked & (1 << (surface - sys->surfaces)));
+
+ err = sys->VideoSurfaceGetBitsYCbCr (surface->surface,
+ VDP_YCBCR_FORMAT_YV12,
+ planes, pitches);
+ if (err != VDP_STATUS_OK)
+ {
+ msg_Err (va, "surface copy failure: %s", sys->GetErrorString (err));
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+static int Init (vlc_va_t *va, AVCodecContext *ctx, vlc_fourcc_t *chromap,
+ int width, int height)
+{
+ vlc_va_sys_t *sys = va->sys;
+ VdpStatus err;
+
+ width = (width + 3) & ~3;
+ height = (height + 3) & ~3;
+
+ unsigned surfaces = 2;
+ if (sys->profile == VDP_DECODER_PROFILE_H264_HIGH)
+ surfaces = 16;
+
+ err = sys->DecoderCreate (sys->device, sys->profile, width, height,
+ surfaces, &sys->decoder);
+ if (err != VDP_STATUS_OK)
+ {
+ msg_Err (va, "decoder creation failure: %s",
+ sys->GetErrorString (err));
+ sys->decoder = VDP_INVALID_HANDLE;
+ return VLC_EGENERIC;
+ }
+
+ assert (width > 0 && height > 0);
+ surfaces++;
+ assert (surfaces <= MAX_SURFACES);
+ /* TODO: select better chromas when appropriate */
+ for (unsigned i = 0; i < surfaces; i++)
+ {
+ vlc_va_surface_t *surface = sys->surfaces + i;
+
+ err = sys->VideoSurfaceCreate (sys->device, VDP_CHROMA_TYPE_420, width,
+ height, &surface->surface);
+ if (err != VDP_STATUS_OK)
+ {
+ msg_Err (va, "surface creation failure: %s",
+ sys->GetErrorString (err));
+ surface->surface = VDP_INVALID_HANDLE;
+ break;
+ }
+ surface->state = 0;
+ surface->bitstream_buffers_used =
+ surface->bitstream_buffers_allocated = 0;
+ surface->bitstream_buffers = NULL;
+ }
+
+ sys->locked = 0;
+ ctx->draw_horiz_band = DrawHorizBand;
+ ctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
+ ctx->hwaccel_context = NULL;
+ *chromap = VLC_CODEC_YV12;
+ return VLC_SUCCESS;
+}
+
+static void Deinit (vlc_va_t *va)
+{
+ vlc_va_sys_t *sys = va->sys;
+
+ for (unsigned i = 0; i < MAX_SURFACES; i++)
+ {
+ vlc_va_surface_t *surface = sys->surfaces + i;
+
+ if (surface->surface != VDP_INVALID_HANDLE)
+ {
+ av_freep (&surface->bitstream_buffers);
+ sys->VideoSurfaceDestroy (surface->surface);
+ surface->surface = VDP_INVALID_HANDLE;
+ }
+ }
+
+ assert (sys->decoder != VDP_INVALID_HANDLE);
+ sys->DecoderDestroy (sys->device);
+}
+
+static int Setup (vlc_va_t *va, AVCodecContext *ctx, vlc_fourcc_t *chromap,
+ int width, int height)
+{
+ vlc_va_sys_t *sys = va->sys;
+
+ if (sys->decoder != VDP_INVALID_HANDLE)
+ {
+ if (sys->width == width && sys->height == height)
+ return VLC_SUCCESS;
+ Deinit (va);
+ sys->decoder = VDP_INVALID_HANDLE;
+ }
+
+ sys->width = width;
+ sys->height = height;
+ ugly_hack = va;
+ return Init (va, ctx, chromap, width, height);
+}
+
+static int vdp_device_Create (vlc_object_t *obj, void **sysp, VdpDevice *devp,
+ VdpGetProcAddress **gpap)
+{
+ VdpStatus err;
+
+ if (!vlc_xlib_init (obj))
+ {
+ msg_Err (obj, "Xlib is required for VDPAU");
+ return VLC_EGENERIC;
+ }
+
+ Display *x11 = XOpenDisplay (NULL);
+ if (x11 == NULL)
+ {
+ msg_Err (obj, "windowing system failure failure");
+ return VLC_EGENERIC;
+ }
+
+ err = vdp_device_create_x11 (x11, XDefaultScreen (x11), devp, gpap);
+ if (err)
+ {
+ msg_Err (obj, "device creation failure: error %d", (int)err);
+ XCloseDisplay (x11);
+ return VLC_EGENERIC;
+ }
+ *sysp = x11;
+ return VLC_SUCCESS;
+}
+
+static int Open (vlc_va_t *va, int codec, const es_format_t *fmt)
+{
+ VdpStatus err;
+ int pixfmt;
+ int profile;
+
+ switch (codec)
+ {
+ case CODEC_ID_H264:
+ pixfmt = PIX_FMT_VDPAU_H264;
+ profile = VDP_DECODER_PROFILE_H264_HIGH;
+ break;
+ case CODEC_ID_MPEG1VIDEO:
+ pixfmt = PIX_FMT_VDPAU_MPEG1;
+ profile = VDP_DECODER_PROFILE_MPEG1;
+ break;
+ case CODEC_ID_MPEG2VIDEO:
+ pixfmt = PIX_FMT_VDPAU_MPEG2;
+ profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
+ break;
+ /*case CODEC_ID_WMV3:
+ pixfmt = PIX_FMT_VDPAU_WMV3;
+ profile = ???;
+ break;*/
+ case CODEC_ID_VC1:
+ pixfmt = PIX_FMT_VDPAU_VC1;
+ profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
+ break;
+ case CODEC_ID_MPEG4:
+ pixfmt = PIX_FMT_VDPAU_MPEG4;
+ profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
+ break;
+ default:
+ return VLC_EGENERIC;
+ }
+
+ vlc_va_sys_t *sys = malloc (sizeof (*sys));
+ if (unlikely(sys == NULL))
+ return VLC_ENOMEM;
+
+ VdpDevice device;
+ VdpGetProcAddress *GetProcAddress;
+ if (vdp_device_Create (VLC_OBJECT(va), &sys->display, &device,
+ &GetProcAddress))
+ {
+ free (sys);
+ return VLC_EGENERIC;
+ }
+
+ sys->device = device;
+ sys->decoder = VDP_INVALID_HANDLE;
+ sys->profile = profile;
+
+ for (unsigned i = 0; i < MAX_SURFACES; i++)
+ sys->surfaces[i].surface = VDP_INVALID_HANDLE;
+
+#define PROC(id,name) \
+ do { \
+ void *ptr; \
+ err = GetProcAddress (sys->device, VDP_FUNC_ID_##id, &ptr); \
+ if (unlikely(err)) \
+ abort (); \
+ sys->name = ptr; \
+ } while (0)
+
+ /* We are really screwed if any function fails. We cannot even delete the
+ * already allocated device. */
+ PROC(GET_ERROR_STRING, GetErrorString);
+ PROC(GET_INFORMATION_STRING, GetInformationString);
+ PROC(DEVICE_DESTROY, DeviceDestroy);
+ PROC(VIDEO_SURFACE_CREATE, VideoSurfaceCreate);
+ PROC(VIDEO_SURFACE_DESTROY, VideoSurfaceDestroy);
+ PROC(VIDEO_SURFACE_GET_BITS_Y_CB_CR, VideoSurfaceGetBitsYCbCr);
+ PROC(DECODER_CREATE, DecoderCreate);
+ PROC(DECODER_DESTROY, DecoderDestroy);
+ PROC(DECODER_RENDER, DecoderRender);
+
+ /* TODO: Query cap */
+
+ const char *infos;
+ if (sys->GetInformationString (&infos) != VDP_STATUS_OK)
+ infos = "VDPAU";
+
+ va->sys = sys;
+ va->description = (char *)infos;
+ va->pix_fmt = pixfmt;
+ va->setup = Setup;
+ va->get = Lock;
+ va->release = Unlock;
+ va->extract = Copy;
+ (void) fmt;
+ return VLC_SUCCESS;
+
+/*error:
+ XCloseDisplay (sys->display);
+ free (sys);
+ return VLC_EGENERIC;*/
+}
+
+static void Close (vlc_va_t *va)
+{
+ vlc_va_sys_t *sys = va->sys;
+
+ if (sys->decoder != VDP_INVALID_HANDLE)
+ Deinit (va);
+ sys->DeviceDestroy (sys->device);
+ XCloseDisplay (sys->display);
+ free (sys);
+}
diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
index 81b14c1..076078d 100644
--- a/modules/codec/avcodec/video.c
+++ b/modules/codec/avcodec/video.c
@@ -426,7 +426,11 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context,
free( p_sys );
return VLC_EGENERIC;
}
-
+#if 0
+ // XXX: bug-to-bug for mpegvideo_vdpau decoder
+ enum PixelFormat fmts[2] = { PIX_FMT_VDPAU_MPEG2, PIX_FMT_NONE };
+ ffmpeg_GetFormat( p_sys->p_context, fmts );
+#endif
return VLC_SUCCESS;
}
@@ -1193,7 +1197,7 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
*/
p_sys->b_direct_rendering = false;
p_sys->p_va = p_va;
- p_context->draw_horiz_band = NULL;
+ //p_context->draw_horiz_band = NULL; FIXME
return pi_fmt[i];
}
--
1.7.10.4
More information about the vlc-devel
mailing list