Commit 365c137f authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont
Browse files

vdpau: libavcodec hardware decoder using new helper library

parent f756e0c9
......@@ -363,6 +363,7 @@ $Id$
* vcdx: input module for accessing Video CDs with navigation & stills
* vda: VDADecoder hardware-accelerated decoding
* vdpau: VDPAU hardware-accelerated decoding
* vdpau_avcodec: VDPAU hardware-accelerated decoding
* vdummy: dummy video display
* visual: visualisation system
* vmem: video memory output
......
......@@ -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
/*****************************************************************************
* 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);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment