/*****************************************************************************
* 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 <stdatomic.h>
#include <stdlib.h>
#include <stdio.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_codec.h>
#include "vlc_vdpau.h"
#include "../../codec/avcodec/va.h"
typedef struct
{
VdpChromaType type;
void *hwaccel_context;
uint32_t width;
uint32_t height;
vlc_video_context *vctx;
vlc_vdp_video_field_t *pool[];
} vlc_va_sys_t;
static vlc_vdp_video_field_t *CreateSurface(vlc_va_t *va, vdpau_decoder_device_t *vdpau_decoder)
{
vlc_va_sys_t *sys = va->sys;
VdpVideoSurface surface;
VdpStatus err;
err = vdp_video_surface_create(vdpau_decoder->vdp, vdpau_decoder->device, sys->type,
sys->width, sys->height, &surface);
if (err != VDP_STATUS_OK)
{
msg_Err(va, "%s creation failure: %s", "video surface",
vdp_get_error_string(vdpau_decoder->vdp, err));
return NULL;
}
vlc_vdp_video_field_t *field = vlc_vdp_video_create(sys->vctx, surface);
if (unlikely(field == NULL))
vdp_video_surface_destroy(vdpau_decoder->vdp, surface);
return field;
}
static vlc_vdp_video_field_t *GetSurface(vlc_va_sys_t *sys)
{
vlc_vdp_video_field_t *f;
for (unsigned i = 0; (f = sys->pool[i]) != NULL; i++)
{
uintptr_t expected = 1;
if (atomic_compare_exchange_strong(&f->frame->refs, &expected, 2))
{
vlc_vdp_video_field_t *field = vlc_vdp_video_copy(f);
atomic_fetch_sub(&f->frame->refs, 1);
return field;
}
}
return NULL;
}
static vlc_vdp_video_field_t *Get(vlc_va_sys_t *sys)
{
vlc_vdp_video_field_t *field;
while ((field = GetSurface(sys)) == NULL)
{
/* Pool empty. Wait for some time as in src/input/decoder.c.
* XXX: Both this and the core should use a semaphore or a CV. */
vlc_tick_sleep(VOUT_OUTMEM_SLEEP);
}
return field;
}
static int Lock(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx, AVFrame *frame)
{
(void) ctx;
vlc_va_sys_t *sys = va->sys;
vlc_vdp_video_field_t *field = Get(sys);
if (field == NULL)
return VLC_ENOMEM;
pic->context = &field->context;
frame->data[3] = (void *)(uintptr_t)field->frame->surface;
return VLC_SUCCESS;
}
static void Close(vlc_va_t *va, AVCodecContext* ctx)
{
vlc_va_sys_t *sys = va->sys;
for (size_t i = 0; sys->pool[i] != NULL; i++)
vlc_vdp_video_destroy(sys->pool[i]);
vlc_video_context_Release(sys->vctx);
if (sys->hwaccel_context)
av_free(sys->hwaccel_context);
if (ctx)
ctx->hwaccel_context = NULL;
free(sys);
}
static const struct vlc_va_operations ops =
{
.get = Lock,
.close = Close,
};
const struct vlc_video_context_operations vdpau_vctx_ops = {
NULL,
};
static int Open(vlc_va_t *va, struct vlc_va_cfg *cfg)
{
AVCodecContext *avctx = cfg->avctx;
enum AVPixelFormat hwfmt = cfg->hwfmt;
vlc_decoder_device *dec_device = cfg->dec_device;
video_format_t *fmt_out = cfg->video_fmt_out;
if ( hwfmt != AV_PIX_FMT_VDPAU || GetVDPAUOpaqueDevice(dec_device) == NULL)
return VLC_EGENERIC;
void *func;
VdpStatus err;
VdpChromaType type, *chroma;
uint32_t width, height;
if (av_vdpau_get_surface_parameters(avctx, &type, &width, &height))
return VLC_EGENERIC;
fmt_out->i_chroma = VLC_CODEC_VDPAU_VIDEO;
unsigned codec_refs;
switch (avctx->codec_id)
{
case AV_CODEC_ID_H264:
codec_refs = avctx->refs; // we can rely on this
break;
case AV_CODEC_ID_HEVC:
codec_refs = 16;
break;
case AV_CODEC_ID_VP9:
codec_refs = 8;
break;
default:
codec_refs = 2;
break;
}
const unsigned refs = codec_refs + 2 * avctx->thread_count + 5;
size_t extra = (refs + 1) * sizeof (vlc_vdp_video_field_t *);
vlc_va_sys_t *sys = malloc(sizeof (*sys) + extra);
if (unlikely(sys == NULL))
return VLC_ENOMEM;
sys->vctx = vlc_video_context_Create(dec_device, VLC_VIDEO_CONTEXT_VDPAU,
sizeof (VdpChromaType),
&vdpau_vctx_ops);
if (sys->vctx == NULL)
{
free(sys);
return VLC_ENOMEM;
}
chroma = vlc_video_context_GetPrivate(sys->vctx, VLC_VIDEO_CONTEXT_VDPAU);
*chroma = type;
sys->type = type;
sys->width = width;
sys->height = height;
sys->hwaccel_context = NULL;
vdpau_decoder_device_t *vdpau_decoder = GetVDPAUOpaqueDevice(dec_device);
unsigned flags = AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH;
err = vdp_get_proc_address(vdpau_decoder->vdp, vdpau_decoder->device,
VDP_FUNC_ID_GET_PROC_ADDRESS, &func);
if (err != VDP_STATUS_OK)
goto error;
if (av_vdpau_bind_context(avctx, vdpau_decoder->device, func, flags))
goto error;
sys->hwaccel_context = avctx->hwaccel_context;
va->sys = sys;
unsigned i = 0;
while (i < refs)
{
sys->pool[i] = CreateSurface(va, vdpau_decoder);
if (sys->pool[i] == NULL)
break;
i++;
}
sys->pool[i] = NULL;
if (i < refs)
{
msg_Err(va, "not enough video RAM");
Close(va, avctx);
return VLC_ENOMEM;
}
cfg->vctx_out = sys->vctx;
va->ops = &ops;
return VLC_SUCCESS;
error:
if (sys->vctx)
vlc_video_context_Release(sys->vctx);
if (sys->hwaccel_context)
av_free(sys->hwaccel_context);
free(sys);
return VLC_EGENERIC;
}
vlc_module_begin()
set_description(N_("VDPAU video decoder"))
set_subcategory(SUBCAT_INPUT_VCODEC)
set_va_callback(Open, 100)
add_shortcut("vdpau")
vlc_module_end()
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|