[vlc-devel] [PATCH 05/48] vdpau: libavcodec hardware decoder using new helper library

Rémi Denis-Courmont remi at remlab.net
Tue Jul 2 19:51:31 CEST 2013


---
 modules/hw/vdpau/Makefile.am |  13 ++
 modules/hw/vdpau/avcodec.c   | 429 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 442 insertions(+)
 create mode 100644 modules/hw/vdpau/avcodec.c

diff --git a/modules/hw/vdpau/Makefile.am b/modules/hw/vdpau/Makefile.am
index f45f1b1..c51629f 100644
--- a/modules/hw/vdpau/Makefile.am
+++ b/modules/hw/vdpau/Makefile.am
@@ -2,6 +2,7 @@ basedir = vdpau
 include $(top_srcdir)/modules/common.am
 
 AM_CFLAGS += $(VDPAU_CFLAGS)
+AM_LIBADD += libvlc_vdpau.la
 
 libvlc_vdpau_la_SOURCES = vlc_vdpau.c vlc_vdpau.h instance.c
 libvlc_vdpau_la_CPPFLAGS =
@@ -12,3 +13,15 @@ libvlc_vdpau_la_LDFLAGS = \
 	-export-symbols-regex ^vdp_ \
 	-version-info 0:0:0
 pkglib_LTLIBRARIES = libvlc_vdpau.la
+
+libvlc_LTLIBRARIES =
+
+libvdpau_avcodec_plugin_la_SOURCES = avcodec.c
+libvdpau_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS) \
+	$(X_CFLAGS) $(CFLAGS_avcodec)
+libvdpau_avcodec_plugin_la_LIBADD = $(AM_LIBADD) \
+	$(X_LIBS) $(X_PRE_LIBS) -lX11 $(LIBS_avcodec)
+libvdpau_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_avcodec)
+if HAVE_AVCODEC_VDPAU
+libvlc_LTLIBRARIES += libvdpau_avcodec_plugin.la
+endif
diff --git a/modules/hw/vdpau/avcodec.c b/modules/hw/vdpau/avcodec.c
new file mode 100644
index 0000000..faa18e7
--- /dev/null
+++ b/modules/hw/vdpau/avcodec.c
@@ -0,0 +1,429 @@
+/*****************************************************************************
+ * avcodec.c: VDPAU decoder for libav
+ *****************************************************************************
+ * Copyright (C) 2012-2013 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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <libavutil/mem.h>
+#include <libavcodec/avcodec.h>
+#include <libavcodec/vdpau.h>
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_fourcc.h>
+#include <vlc_picture.h>
+#include <vlc_xlib.h>
+#include "vlc_vdpau.h"
+#include "../../codec/avcodec/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)
+    add_shortcut("vdpau")
+vlc_module_end ()
+
+#define MAX_SURFACES (16+1)
+
+struct vlc_va_sys_t
+{
+    vdp_t *vdp;
+    VdpDevice device;
+    VdpDecoderProfile profile;
+    VdpYCbCrFormat format;
+    AVVDPAUContext context;
+    VdpVideoSurface surfaces[MAX_SURFACES];
+    uint32_t available;
+    vlc_fourcc_t chroma;
+    uint16_t width;
+    uint16_t height;
+};
+
+static int Lock(vlc_va_t *va, AVFrame *ff)
+{
+    vlc_va_sys_t *sys = va->sys;
+
+    for (unsigned i = 0; i < AV_NUM_DATA_POINTERS; i++)
+    {
+        ff->data[i] = NULL;
+        ff->linesize[i] = 0;
+    }
+
+    if (!sys->available)
+    {
+        msg_Err(va, "no free surfaces");
+        return VLC_EGENERIC;
+    }
+
+    unsigned idx = ctz (sys->available);
+    sys->available &= ~(1 << idx);
+
+    VdpVideoSurface *surface = sys->surfaces + idx;
+    assert(*surface != VDP_INVALID_HANDLE);
+    ff->data[0] = (void *)surface; /* must be non-NULL */
+    ff->data[3] = (void *)(uintptr_t)*surface;
+    ff->opaque = surface;
+    return VLC_SUCCESS;
+}
+
+static void Unlock(vlc_va_t *va, AVFrame *ff)
+{
+    vlc_va_sys_t *sys = va->sys;
+    VdpVideoSurface *surface = ff->opaque;
+    unsigned idx = surface - sys->surfaces;
+
+    assert(idx < MAX_SURFACES);
+    sys->available |= (1 << idx);
+
+    ff->data[0] = ff->data[3] = NULL;
+    ff->opaque = NULL;
+}
+
+static int Copy(vlc_va_t *va, picture_t *pic, AVFrame *ff)
+{
+    vlc_va_sys_t *sys = va->sys;
+    VdpVideoSurface *surface = ff->opaque;
+    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;
+    }
+
+    err = vdp_video_surface_get_bits_y_cb_cr(sys->vdp, *surface, sys->format,
+                                             planes, pitches);
+    if (err != VDP_STATUS_OK)
+    {
+        msg_Err(va, "surface copy failure: %s",
+                vdp_get_error_string(sys->vdp, err));
+        return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int Init(vlc_va_t *va, void **ctxp, vlc_fourcc_t *chromap,
+                int width, int height)
+{
+    vlc_va_sys_t *sys = va->sys;
+    VdpStatus err;
+
+    width = (width + 1) & ~1;
+    height = (height + 3) & ~3;
+    sys->width = width;
+    sys->height = height;
+
+    unsigned surfaces = 2;
+    switch (sys->profile)
+    {
+      case VDP_DECODER_PROFILE_H264_BASELINE:
+      case VDP_DECODER_PROFILE_H264_MAIN:
+      case VDP_DECODER_PROFILE_H264_HIGH:
+        surfaces = 16;
+        break;
+    }
+
+    err = vdp_decoder_create(sys->vdp, sys->device, sys->profile, width,
+                             height, surfaces, &sys->context.decoder);
+    if (err != VDP_STATUS_OK)
+    {
+        msg_Err(va, "%s creation failure: %s", "decoder",
+                vdp_get_error_string(sys->vdp, err));
+        sys->context.decoder = VDP_INVALID_HANDLE;
+        return VLC_EGENERIC;
+    }
+
+    assert (width > 0 && height > 0);
+    surfaces++;
+    assert (surfaces <= MAX_SURFACES);
+    sys->available = 0;
+
+    /* TODO: select better chromas when appropriate */
+    for (unsigned i = 0; i < surfaces; i++)
+    {
+        err = vdp_video_surface_create(sys->vdp, sys->device,
+                        VDP_CHROMA_TYPE_420, width, height, sys->surfaces + i);
+        if (err != VDP_STATUS_OK)
+        {
+             msg_Err(va, "%s creation failure: %s", "video surface",
+                     vdp_get_error_string(sys->vdp, err));
+             sys->surfaces[i] = VDP_INVALID_HANDLE;
+             break;
+        }
+        sys->available |= (1 << i);
+    }
+
+    *ctxp = &sys->context;
+    *chromap = sys->chroma;
+    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++)
+    {
+        VdpVideoSurface *surface = sys->surfaces + i;
+
+        if (*surface != VDP_INVALID_HANDLE)
+        {
+            vdp_video_surface_destroy(sys->vdp, *surface);
+            *surface = VDP_INVALID_HANDLE;
+        }
+    }
+
+    assert(sys->context.decoder != VDP_INVALID_HANDLE);
+    vdp_decoder_destroy(sys->vdp, sys->context.decoder);
+    av_freep(&sys->context.bitstream_buffers);
+}
+
+static int Setup(vlc_va_t *va, void **ctxp, vlc_fourcc_t *chromap,
+                 int width, int height)
+{
+    vlc_va_sys_t *sys = va->sys;
+
+    if (sys->context.decoder != VDP_INVALID_HANDLE)
+    {
+        if (sys->width == width && sys->height == height)
+            return VLC_SUCCESS;
+        Deinit(va);
+        sys->context.decoder = VDP_INVALID_HANDLE;
+    }
+
+    return Init(va, ctxp, chromap, width, height);
+}
+
+static int Open(vlc_va_t *va, int codec, const es_format_t *fmt)
+{
+    VdpStatus err;
+    VdpDecoderProfile profile;
+    int level;
+
+    switch (codec)
+    {
+      case AV_CODEC_ID_MPEG1VIDEO:
+        profile = VDP_DECODER_PROFILE_MPEG1;
+        level = VDP_DECODER_LEVEL_MPEG1_NA;
+        break;
+
+      case AV_CODEC_ID_MPEG2VIDEO:
+        switch (fmt->i_profile)
+        {
+          case FF_PROFILE_MPEG2_MAIN:
+            profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
+            break;
+          case FF_PROFILE_MPEG2_SIMPLE:
+            profile = VDP_DECODER_PROFILE_MPEG2_SIMPLE;
+            break;
+          default:
+            msg_Err(va, "unsupported %s profile %d", "MPEG2", fmt->i_profile);
+            return VLC_EGENERIC;
+        }
+        level = VDP_DECODER_LEVEL_MPEG2_HL;
+        break;
+
+      case AV_CODEC_ID_H263:
+        profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
+        level = VDP_DECODER_LEVEL_MPEG4_PART2_ASP_L5;
+        break;
+      case AV_CODEC_ID_MPEG4:
+        switch (fmt->i_profile)
+        {
+          case FF_PROFILE_MPEG4_SIMPLE:
+            profile = VDP_DECODER_PROFILE_MPEG4_PART2_SP;
+            break;
+          case FF_PROFILE_MPEG4_SIMPLE_STUDIO:
+            msg_Err(va, "unsupported %s profile %d", "MPEG4", fmt->i_profile);
+            return VLC_EGENERIC;
+          default:
+            profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
+            break;
+        }
+        level = fmt->i_level;
+        break;
+
+      case AV_CODEC_ID_H264:
+        switch (fmt->i_profile
+                        & ~(FF_PROFILE_H264_CONSTRAINED|FF_PROFILE_H264_INTRA))
+        {
+          case FF_PROFILE_H264_BASELINE:
+            profile = VDP_DECODER_PROFILE_H264_BASELINE;
+            break;
+          case FF_PROFILE_H264_MAIN:
+            profile = VDP_DECODER_PROFILE_H264_MAIN;
+            break;
+          case FF_PROFILE_H264_HIGH:
+            profile = VDP_DECODER_PROFILE_H264_HIGH;
+            break;
+          case FF_PROFILE_H264_EXTENDED:
+          default:
+            msg_Err(va, "unsupported %s profile %d", "H.264", fmt->i_profile);
+            return VLC_EGENERIC;
+        }
+        level = fmt->i_level;
+        if ((fmt->i_profile & FF_PROFILE_H264_INTRA) && (fmt->i_level == 11))
+            level = VDP_DECODER_LEVEL_H264_1b;
+        break;
+
+      case AV_CODEC_ID_WMV3:
+      case AV_CODEC_ID_VC1:
+        switch (fmt->i_profile)
+        {
+          case FF_PROFILE_VC1_SIMPLE:
+            profile = VDP_DECODER_PROFILE_VC1_SIMPLE;
+            break;
+          case FF_PROFILE_VC1_MAIN:
+            profile = VDP_DECODER_PROFILE_VC1_MAIN;
+            break;
+          case FF_PROFILE_VC1_ADVANCED:
+            profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
+            break;
+          default:
+            msg_Err(va, "unsupported %s profile %d", "VC-1", fmt->i_profile);
+            return VLC_EGENERIC;
+        }
+        level = fmt->i_level;
+        break;
+
+      default:
+        msg_Err(va, "unknown codec %d", codec);
+        return VLC_EGENERIC;
+    }
+
+    if (!vlc_xlib_init(VLC_OBJECT(va)))
+    {
+        msg_Err(va, "Xlib is required for VDPAU");
+        return VLC_EGENERIC;
+    }
+
+    vlc_va_sys_t *sys = malloc(sizeof (*sys));
+    if (unlikely(sys == NULL))
+       return VLC_ENOMEM;
+
+    sys->profile = profile;
+    memset(&sys->context, 0, sizeof (sys->context));
+    sys->context.decoder = VDP_INVALID_HANDLE;
+    for (unsigned i = 0; i < MAX_SURFACES; i++)
+        sys->surfaces[i] = VDP_INVALID_HANDLE;
+
+    err = vdp_get_x11(NULL, -1, &sys->vdp, &sys->device);
+    if (err != VDP_STATUS_OK)
+    {
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    void *func;
+    err = vdp_get_proc_address(sys->vdp, sys->device,
+                               VDP_FUNC_ID_DECODER_RENDER, &func);
+    if (err != VDP_STATUS_OK)
+        goto error;
+    sys->context.render = func;
+
+    /* Check capabilities */
+    VdpBool support;
+    uint32_t lvl, mb, width, height;
+
+    if (vdp_video_surface_query_capabilities(sys->vdp, sys->device,
+              VDP_CHROMA_TYPE_420, &support, &width, &height) != VDP_STATUS_OK)
+        support = VDP_FALSE;
+    if (!support || width < fmt->video.i_width || height < fmt->video.i_height)
+    {
+        msg_Err(va, "video surface not supported: %s %ux%u",
+                 "YUV 4:2:0", fmt->video.i_width, fmt->video.i_height);
+        goto error;
+    }
+    msg_Dbg(va, "video surface supported maximum: %s %"PRIu32"x%"PRIu32,
+            "YUV 4:2:0", width, height);
+
+    if (vdp_video_surface_query_get_put_bits_y_cb_cr_capabilities(sys->vdp,
+            sys->device, VDP_CHROMA_TYPE_420, VDP_YCBCR_FORMAT_YV12, &support)
+                                       == VDP_STATUS_OK && support == VDP_TRUE)
+    {
+        sys->format = VDP_YCBCR_FORMAT_YV12;
+        sys->chroma = VLC_CODEC_YV12;
+    }
+    else
+    if (vdp_video_surface_query_get_put_bits_y_cb_cr_capabilities(sys->vdp,
+            sys->device, VDP_CHROMA_TYPE_420, VDP_YCBCR_FORMAT_NV12, &support)
+                                       == VDP_STATUS_OK && support == VDP_TRUE)
+    {
+        sys->format = VDP_YCBCR_FORMAT_NV12;
+        sys->chroma = VLC_CODEC_NV12;
+    }
+    else
+    {
+        msg_Err(va, "video surface reading not supported: %s", "YUV 4:2:0");
+        goto error;
+    }
+
+    if (vdp_decoder_query_capabilities(sys->vdp, sys->device, profile,
+                        &support, &lvl, &mb, &width, &height) != VDP_STATUS_OK)
+        support = VDP_FALSE;
+    if (!support || (int)lvl < level
+     || width < fmt->video.i_width || height < fmt->video.i_height)
+    {
+        msg_Err(va, "decoding profile not supported: %"PRIu32".%d %ux%u",
+                profile, lvl, fmt->video.i_width, fmt->video.i_height);
+        goto error;
+    }
+    msg_Dbg(va, "decoding profile supported maximum: %"PRIu32".%"PRIu32" mb %"
+            PRIu32", %"PRIu32"x%"PRIu32, profile, lvl, mb, width, height);
+
+    const char *infos;
+    if (vdp_get_information_string(sys->vdp, &infos) != VDP_STATUS_OK)
+        infos = "VDPAU";
+
+    va->sys = sys;
+    va->description = (char *)infos;
+    va->pix_fmt = AV_PIX_FMT_VDPAU;
+    va->setup = Setup;
+    va->get = Lock;
+    va->release = Unlock;
+    va->extract = Copy;
+    return VLC_SUCCESS;
+
+error:
+    vdp_release_x11(sys->vdp);
+    free(sys);
+    return VLC_EGENERIC;
+}
+
+static void Close(vlc_va_t *va)
+{
+    vlc_va_sys_t *sys = va->sys;
+
+    if (sys->context.decoder != VDP_INVALID_HANDLE)
+        Deinit(va);
+    vdp_release_x11(sys->vdp);
+    free(sys);
+}
-- 
1.8.3.2




More information about the vlc-devel mailing list