3 changed files with 443 additions and 0 deletions
@ -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); |
||||
|
} |
||||
Loading…
Reference in new issue