[FFmpeg-devel] Added HW H.264 and HEVC encoding for AMD GPUs based on AMF SDK
Mark Thompson
sw at jkqxz.net
Mon Nov 27 02:21:27 EET 2017
On 22/11/17 23:28, mmironov wrote:
> From c669277afd764903d3da09d92a263d0fb58e24b1 Mon Sep 17 00:00:00 2001
> From: mmironov <mikhail.mironov at amd.com>
> Date: Tue, 14 Nov 2017 17:54:24 -0500
> Subject: [PATCH] Added HW H.264 and HEVC encoding for AMD GPUs based on AMF
> SDK
>
> Signed-off-by: mmironov <mikhail.mironov at amd.com>
> ---
> Changelog | 1 +
> compat/amd/amfsdkenc.h | 1755 ++++++++++++++++++++++++++++++++++++++++++++++
> configure | 18 +-
> libavcodec/Makefile | 4 +
> libavcodec/allcodecs.c | 2 +
> libavcodec/amfenc.c | 596 ++++++++++++++++
> libavcodec/amfenc.h | 143 ++++
> libavcodec/amfenc_h264.c | 397 +++++++++++
> libavcodec/amfenc_hevc.c | 327 +++++++++
> 9 files changed, 3242 insertions(+), 1 deletion(-)
> create mode 100644 compat/amd/amfsdkenc.h
> create mode 100644 libavcodec/amfenc.c
> create mode 100644 libavcodec/amfenc.h
> create mode 100644 libavcodec/amfenc_h264.c
> create mode 100644 libavcodec/amfenc_hevc.c
A few minor fixups below. I would be happy to apply this if it didn't contain the external header.
Thanks,
- Mark
> diff --git a/Changelog b/Changelog
> index 68829f2..e5e5ffd 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -15,6 +15,7 @@ version <next>:
> - Raw aptX muxer and demuxer
> - NVIDIA NVDEC-accelerated H.264, HEVC and VP9 hwaccel decoding
> - Intel QSV-accelerated overlay filter
> +- AMD NW H.264 and HEVC encoders
NW?
>
>
> version 3.4:
> diff --git a/compat/amd/amfsdkenc.h b/compat/amd/amfsdkenc.h
> new file mode 100644
> index 0000000..282656d
> --- /dev/null
> +++ b/compat/amd/amfsdkenc.h
> @@ -0,0 +1,1755 @@
> ...
> diff --git a/configure b/configure
> index 3788f26..a562a2a 100755
> --- a/configure
> +++ b/configure
> @@ -303,6 +303,7 @@ External library support:
> --disable-zlib disable zlib [autodetect]
>
> The following libraries provide various hardware acceleration features:
> + --disable-amf disable AMF video encoding code [autodetect]
> --disable-audiotoolbox disable Apple AudioToolbox code [autodetect]
> --disable-cuda disable dynamically linked Nvidia CUDA code [autodetect]
> --enable-cuda-sdk enable CUDA features that require the CUDA SDK [no]
> @@ -1639,6 +1640,7 @@ EXTERNAL_LIBRARY_LIST="
> "
>
> HWACCEL_AUTODETECT_LIBRARY_LIST="
> + amf
> audiotoolbox
> crystalhd
> cuda
> @@ -2781,12 +2783,15 @@ scale_npp_filter_deps="cuda libnpp"
> scale_cuda_filter_deps="cuda_sdk"
> thumbnail_cuda_filter_deps="cuda_sdk"
>
> +amf_deps_any="libdl LoadLibrary"
> +
> nvenc_deps="cuda"
> nvenc_deps_any="libdl LoadLibrary"
> nvenc_encoder_deps="nvenc"
>
> h263_v4l2m2m_decoder_deps="v4l2_m2m h263_v4l2_m2m"
> h263_v4l2m2m_encoder_deps="v4l2_m2m h263_v4l2_m2m"
> +h264_amf_encoder_deps="amf"
> h264_crystalhd_decoder_select="crystalhd h264_mp4toannexb_bsf h264_parser"
> h264_cuvid_decoder_deps="cuvid"
> h264_cuvid_decoder_select="h264_mp4toannexb_bsf"
> @@ -2803,6 +2808,7 @@ h264_vaapi_encoder_deps="VAEncPictureParameterBufferH264"
> h264_vaapi_encoder_select="cbs_h264 vaapi_encode"
> h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m"
> h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m"
> +hevc_amf_encoder_deps="amf"
> hevc_cuvid_decoder_deps="cuvid"
> hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf"
> hevc_mediacodec_decoder_deps="mediacodec"
> @@ -6164,9 +6170,12 @@ if enabled x86; then
> mingw32*|mingw64*|win32|win64|linux|cygwin*)
> ;;
> *)
> - disable cuda cuvid nvdec nvenc
> + disable cuda cuvid nvdec nvenc amf
> ;;
> esac
> + if test $target_os = "linux"; then
> + disable amf
> + fi
> else
> disable cuda cuvid nvdec nvenc
amf here too?
> fi
> @@ -6179,6 +6188,13 @@ void f(void) { struct { const GUID guid; } s[] = { { NV_ENC_PRESET_HQ_GUID } };
> int main(void) { return 0; }
> EOF
>
> +enabled amf &&
> + check_cc -I$source_path <<EOF || disable amf
> +#include "compat/amd/amfsdkenc.h"
> +AMFFactory *factory;
> +int main(void) { return 0; }
> +EOF
> +
> # Funny iconv installations are not unusual, so check it after all flags have been set
> if enabled libc_iconv; then
> check_func_headers iconv.h iconv
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 2476aec..9bbb60e 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -55,6 +55,7 @@ OBJS = ac3_parser.o \
> OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o
> OBJS-$(CONFIG_AC3DSP) += ac3dsp.o ac3.o ac3tab.o
> OBJS-$(CONFIG_ADTS_HEADER) += adts_header.o mpeg4audio.o
> +OBJS-$(CONFIG_AMF) += amfenc.o
> OBJS-$(CONFIG_AUDIO_FRAME_QUEUE) += audio_frame_queue.o
> OBJS-$(CONFIG_AUDIODSP) += audiodsp.o
> OBJS-$(CONFIG_BLOCKDSP) += blockdsp.o
> @@ -332,6 +333,7 @@ OBJS-$(CONFIG_H263_ENCODER) += mpeg4videoenc.o mpeg4video.o \
> h263.o ituh263enc.o flvenc.o h263data.o
> OBJS-$(CONFIG_H263_V4L2M2M_DECODER) += v4l2_m2m_dec.o
> OBJS-$(CONFIG_H263_V4L2M2M_ENCODER) += v4l2_m2m_enc.o
> +OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o
> OBJS-$(CONFIG_H264_DECODER) += h264dec.o h264_cabac.o h264_cavlc.o \
> h264_direct.o h264_loopfilter.o \
> h264_mb.o h264_picture.o \
> @@ -353,6 +355,7 @@ OBJS-$(CONFIG_H264_V4L2M2M_DECODER) += v4l2_m2m_dec.o
> OBJS-$(CONFIG_H264_V4L2M2M_ENCODER) += v4l2_m2m_enc.o
> OBJS-$(CONFIG_HAP_DECODER) += hapdec.o hap.o
> OBJS-$(CONFIG_HAP_ENCODER) += hapenc.o hap.o
> +OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o
> OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o \
> hevc_cabac.o hevc_refs.o hevcpred.o \
> hevcdsp.o hevc_filter.o hevc_data.o
> @@ -1059,6 +1062,7 @@ SKIPHEADERS += %_tablegen.h \
> aacenc_quantization_misc.h \
> $(ARCH)/vp56_arith.h \
>
> +SKIPHEADERS-$(CONFIG_AMF) += amfenc.h
> SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h
> SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h
> SKIPHEADERS-$(CONFIG_JNI) += ffjni.h
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 0781862..20c19ec 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -653,6 +653,7 @@ static void register_all(void)
> * above is available */
> REGISTER_ENCODER(H263_V4L2M2M, h263_v4l2m2m);
> REGISTER_ENCDEC (LIBOPENH264, libopenh264);
> + REGISTER_ENCODER(H264_AMF, h264_amf);
> REGISTER_DECODER(H264_CUVID, h264_cuvid);
> REGISTER_ENCODER(H264_NVENC, h264_nvenc);
> REGISTER_ENCODER(H264_OMX, h264_omx);
> @@ -665,6 +666,7 @@ static void register_all(void)
> REGISTER_ENCODER(NVENC_H264, nvenc_h264);
> REGISTER_ENCODER(NVENC_HEVC, nvenc_hevc);
> #endif
> + REGISTER_ENCODER(HEVC_AMF, hevc_amf);
> REGISTER_DECODER(HEVC_CUVID, hevc_cuvid);
> REGISTER_DECODER(HEVC_MEDIACODEC, hevc_mediacodec);
> REGISTER_ENCODER(HEVC_NVENC, hevc_nvenc);
> diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
> new file mode 100644
> index 0000000..6b23f64
> --- /dev/null
> +++ b/libavcodec/amfenc.c
> @@ -0,0 +1,596 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg 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.
> + *
> + * FFmpeg 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 FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/hwcontext.h"
> +#if CONFIG_D3D11VA
> +#include "libavutil/hwcontext_d3d11va.h"
> +#endif
> +#include "libavutil/mem.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/time.h"
> +
> +#include "amfenc.h"
> +#include "internal.h"
> +
> +#if CONFIG_D3D11VA
> +#include <d3d11.h>
> +#endif
> +
> +#ifdef _WIN32
> +#include "compat/w32dlfcn.h"
> +#else
> +#include <dlfcn.h>
> +#endif
> +
> +#define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf"
> +
> +#define PTS_PROP L"PtsProp"
> +
> +const enum AVPixelFormat ff_amf_pix_fmts[] = {
> + AV_PIX_FMT_NV12,
> + AV_PIX_FMT_YUV420P,
> + AV_PIX_FMT_D3D11,
> + AV_PIX_FMT_NONE
> +};
> +
> +typedef struct FormatMap {
> + enum AVPixelFormat av_format;
> + enum AMF_SURFACE_FORMAT amf_format;
> +} FormatMap;
> +
> +static const FormatMap format_map[] =
> +{
> + { AV_PIX_FMT_NONE, AMF_SURFACE_UNKNOWN },
> + { AV_PIX_FMT_NV12, AMF_SURFACE_NV12 },
> + { AV_PIX_FMT_BGR0, AMF_SURFACE_BGRA },
> + { AV_PIX_FMT_RGB0, AMF_SURFACE_RGBA },
> + { AV_PIX_FMT_GRAY8, AMF_SURFACE_GRAY8 },
> + { AV_PIX_FMT_YUV420P, AMF_SURFACE_YUV420P },
> + { AV_PIX_FMT_YUYV422, AMF_SURFACE_YUY2 },
> + { AV_PIX_FMT_D3D11, AMF_SURFACE_NV12 },
> +};
> +
> +
> +static int is_hwaccel_pix_fmt(enum AVPixelFormat pix_fmt)
> +{
> + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
> + return desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
> +}
> +
> +
> +static enum AMF_SURFACE_FORMAT amf_av_to_amf_format(enum AVPixelFormat fmt)
> +{
> + int i;
> + for (i = 0; i < amf_countof(format_map); i++) {
> + if (format_map[i].av_format == fmt) {
> + return format_map[i].amf_format;
> + }
> + }
> + return AMF_SURFACE_UNKNOWN;
> +}
> +
> +static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
> + const wchar_t *scope, const wchar_t *message)
> +{
> + AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
> + av_log(tracer->avctx, AV_LOG_DEBUG, "%ls: %ls", scope, message); // \n is provided from AMF
> +}
> +
> +static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
> +{
> +}
> +
> +static AMFTraceWriterVtbl tracer_vtbl =
> +{
> + .Write = AMFTraceWriter_Write,
> + .Flush = AMFTraceWriter_Flush,
> +};
> +
> +static int amf_load_library(AVCodecContext *avctx)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + AMFInit_Fn init_fun = NULL;
> + AMFQueryVersion_Fn version_fun = NULL;
> + AMF_RESULT res = AMF_OK;
> +
> + ctx->eof = 0;
> + ctx->delayed_drain = 0;
> + ctx->hw_frames_ctx = NULL;
> + ctx->hw_device_ctx = NULL;
> + ctx->delayed_surface = NULL;
> + ctx->delayed_frame = av_frame_alloc();
> + if (!ctx->delayed_frame) {
> + return AVERROR(ENOMEM);
> + }
> + // hardcoded to current HW queue size - will realloc in timestamp_queue_enqueue() if too small
> + ctx->timestamp_list = av_fifo_alloc((avctx->max_b_frames + 16) * sizeof(int64_t));
> + if (!ctx->timestamp_list) {
> + return AVERROR(ENOMEM);
> + }
> + ctx->dts_delay = 0;
> +
> +
> + ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
> + AMF_RETURN_IF_FALSE(ctx, ctx->library != NULL,
> + AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
> +
> + init_fun = (AMFInit_Fn)dlsym(ctx->library, AMF_INIT_FUNCTION_NAME);
> + AMF_RETURN_IF_FALSE(ctx, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME);
> +
> + version_fun = (AMFQueryVersion_Fn)dlsym(ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
> + AMF_RETURN_IF_FALSE(ctx, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME);
> +
> + res = version_fun(&ctx->version);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);
> + res = init_fun(AMF_FULL_VERSION, &ctx->factory);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
> + res = ctx->factory->pVtbl->GetTrace(ctx->factory, &ctx->trace);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
> + res = ctx->factory->pVtbl->GetDebug(ctx->factory, &ctx->debug);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
> + return 0;
> +}
> +
> +static int amf_init_context(AVCodecContext *avctx)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + AMF_RESULT res = AMF_OK;
> +
> + // confugure AMF logger
> + // the return of these functions indicates old state and do not affect behaviour
> + ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, ctx->log_to_dbg != 0 );
> + if (ctx->log_to_dbg)
> + ctx->trace->pVtbl->SetWriterLevel(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, AMF_TRACE_TRACE);
> + ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_CONSOLE, 0);
> + ctx->trace->pVtbl->SetGlobalLevel(ctx->trace, AMF_TRACE_TRACE);
> +
> + // connect AMF logger to av_log
> + ctx->tracer.vtbl = &tracer_vtbl;
> + ctx->tracer.avctx = avctx;
> + ctx->trace->pVtbl->RegisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID,(AMFTraceWriter*)&ctx->tracer, 1);
> + ctx->trace->pVtbl->SetWriterLevel(ctx->trace, FFMPEG_AMF_WRITER_ID, AMF_TRACE_TRACE);
> +
> + res = ctx->factory->pVtbl->CreateContext(ctx->factory, &ctx->context);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
> + // try to reuse existing DX device
> +#if CONFIG_D3D11VA
> + if (avctx->hw_frames_ctx) {
> + AVHWFramesContext *device_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
> + if (device_ctx->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA){
> + if (amf_av_to_amf_format(device_ctx->sw_format) != AMF_SURFACE_UNKNOWN) {
> + if (device_ctx->device_ctx->hwctx) {
> + AVD3D11VADeviceContext *device_d3d11 = (AVD3D11VADeviceContext *)device_ctx->device_ctx->hwctx;
> + res = ctx->context->pVtbl->InitDX11(ctx->context, device_d3d11->device, AMF_DX11_1);
> + if (res == AMF_OK) {
> + ctx->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
Return value should be checked.
> + }else {
> + if(res == AMF_NOT_SUPPORTED)
> + av_log(avctx, AV_LOG_INFO, "amf_shared: avctx->hw_frames_ctx has D3D11 device which doesn't have D3D11VA interface, switching to default\n");
> + else
> + av_log(avctx, AV_LOG_INFO, "amf_shared: avctx->hw_frames_ctx has non-AMD device, switching to default\n");
> + }
> + }
> + }else {
> + av_log(avctx, AV_LOG_INFO, "amf_shared: avctx->hw_frames_ctx has format not uspported by AMF, switching to default\n");
> + }
> + }
> + } else if (avctx->hw_device_ctx) {
> + AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)(avctx->hw_device_ctx->data);
> + if (device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
> + if (device_ctx->hwctx) {
> + AVD3D11VADeviceContext *device_d3d11 = (AVD3D11VADeviceContext *)device_ctx->hwctx;
> + res = ctx->context->pVtbl->InitDX11(ctx->context, device_d3d11->device, AMF_DX11_1);
> + if (res == AMF_OK) {
> + ctx->hw_device_ctx = av_buffer_ref(avctx->hw_device_ctx);
And here.
> + } else {
> + if (res == AMF_NOT_SUPPORTED)
> + av_log(avctx, AV_LOG_INFO, "amf_shared: avctx->hw_device_ctx has D3D11 device which doesn't have D3D11VA interface, switching to default\n");
> + else
> + av_log(avctx, AV_LOG_INFO, "amf_shared: avctx->hw_device_ctx has non-AMD device, switching to default\n");
I didn't notice this before, but the "amf_shared" tags probably aren't wanted - the logging context already carries where the message is coming from.
> + }
> + }
> + }
> + }
> +#endif
> + if (!ctx->hw_frames_ctx && !ctx->hw_device_ctx) {
> + res = ctx->context->pVtbl->InitDX11(ctx->context, NULL, AMF_DX11_1);
> + if (res != AMF_OK) {
> + res = ctx->context->pVtbl->InitDX9(ctx->context, NULL);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "InitDX9() failed with error %d\n", res);
> + }
> + }
> + return 0;
> +}
> +
> +static int amf_init_encoder(AVCodecContext *avctx)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + const wchar_t *codec_id = NULL;
> + AMF_RESULT res = AMF_OK;
> +
> + switch (avctx->codec->id) {
> + case AV_CODEC_ID_H264:
> + codec_id = AMFVideoEncoderVCE_AVC;
> + break;
> + case AV_CODEC_ID_HEVC:
> + codec_id = AMFVideoEncoder_HEVC;
> + break;
> + default:
> + break;
> + }
> + AMF_RETURN_IF_FALSE(ctx, codec_id != NULL, AVERROR(EINVAL), "Codec %d is not supported\n", avctx->codec->id);
> +
> + ctx->format = amf_av_to_amf_format(avctx->pix_fmt);
> + AMF_RETURN_IF_FALSE(ctx, ctx->format != AMF_SURFACE_UNKNOWN, AVERROR(EINVAL), "Format %d is not supported\n", avctx->pix_fmt);
> +
> + res = ctx->factory->pVtbl->CreateComponent(ctx->factory, ctx->context, codec_id, &ctx->encoder);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_ENCODER_NOT_FOUND, "CreateComponent(%ls) failed with error %d\n", codec_id, res);
> +
> + return 0;
> +}
> +
> +int av_cold ff_amf_encode_close(AVCodecContext *avctx)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + if (ctx->delayed_surface)
> + {
> + ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface);
> + ctx->delayed_surface = NULL;
> + }
> +
> + if (ctx->encoder) {
> + ctx->encoder->pVtbl->Terminate(ctx->encoder);
> + ctx->encoder->pVtbl->Release(ctx->encoder);
> + ctx->encoder = NULL;
> + }
> +
> + if (ctx->context) {
> + ctx->context->pVtbl->Terminate(ctx->context);
> + ctx->context->pVtbl->Release(ctx->context);
> + ctx->context = NULL;
> + }
> + av_buffer_unref(&ctx->hw_device_ctx);
> + av_buffer_unref(&ctx->hw_frames_ctx);
> +
> + if (ctx->trace) {
> + ctx->trace->pVtbl->UnregisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID);
> + }
> + if (ctx->library) {
> + dlclose(ctx->library);
> + ctx->library = NULL;
> + }
> + ctx->trace = NULL;
> + ctx->debug = NULL;
> + ctx->factory = NULL;
> + ctx->version = 0;
> + ctx->delayed_drain = 0;
> + av_frame_free(&ctx->delayed_frame);
> + av_fifo_freep(&ctx->timestamp_list);
> +
> + return 0;
> +}
> +
> +static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame,
> + AMFSurface* surface)
> +{
> + AVFrame *sw_frame = NULL;
> + AMFPlane *plane = NULL;
> + uint8_t *dst_data[4];
> + int dst_linesize[4];
> + int ret = 0;
> + int planes;
> +
> + if (frame->hw_frames_ctx && is_hwaccel_pix_fmt(frame->format)) {
> + if (!(sw_frame = av_frame_alloc())) {
> + av_log(avctx, AV_LOG_ERROR, "Can not alloc frame\n");
> + ret = AVERROR(ENOMEM);
> + goto fail;
> + }
> + if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Error transferring the data to system memory\n");
> + ret = AVERROR(EINVAL);
ret is already set, no need to overwrite it.
> + goto fail;
> + }
> + frame = sw_frame;
> + }
> + planes = (int)surface->pVtbl->GetPlanesCount(surface);
> + if (planes > amf_countof(dst_data)) {
> + av_log(avctx, AV_LOG_ERROR, "Invalid number of planes %d in surface\n", planes);
> + ret = AVERROR(EINVAL);
> + goto fail;
> + }
> +
> + for (int i = 0; i < planes; i++) {
Declare at the start of the block.
> + plane = surface->pVtbl->GetPlaneAt(surface, i);
> + dst_data[i] = plane->pVtbl->GetNative(plane);
> + dst_linesize[i] = plane->pVtbl->GetHPitch(plane);
> + }
> + av_image_copy(dst_data, dst_linesize,
> + (const uint8_t**)frame->data, frame->linesize, frame->format,
> + avctx->width, avctx->height);
> +
> +fail:
> + if (sw_frame){
> + av_frame_free(&sw_frame);
> + }
> + return ret;
> +}
> +
> +static inline int timestamp_queue_enqueue(AVCodecContext *avctx, int64_t timestamp)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + if (av_fifo_space(ctx->timestamp_list) < sizeof(timestamp)){
> + if (av_fifo_grow(ctx->timestamp_list, sizeof(timestamp)) < 0) {
> + return AVERROR(ENOMEM);
> + }
> + }
> + av_fifo_generic_write(ctx->timestamp_list, ×tamp, sizeof(timestamp), NULL);
> + return 0;
> +}
> +
> +static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buffer)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + int ret;
> + AMFVariantStruct var = {0};
> + int64_t timestamp = AV_NOPTS_VALUE;
> + int64_t size = buffer->pVtbl->GetSize(buffer);
> +
> + if ((ret = ff_alloc_packet2(avctx, pkt, size, 0)) < 0) {
> + return ret;
> + }
> + memcpy(pkt->data, buffer->pVtbl->GetNative(buffer), size);
> +
> + switch (avctx->codec->id) {
> + case AV_CODEC_ID_H264:
> + buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &var);
> + if(var.int64Value == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR) {
> + pkt->flags = AV_PKT_FLAG_KEY;
> + }
> + break;
> + case AV_CODEC_ID_HEVC:
> + buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, &var);
> + if (var.int64Value == AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR) {
> + pkt->flags = AV_PKT_FLAG_KEY;
> + }
> + break;
> + default:
> + break;
> + }
> +
> + buffer->pVtbl->GetProperty(buffer, PTS_PROP, &var);
> +
> + pkt->pts = var.int64Value; // original pts
> +
> +
> + AMF_RETURN_IF_FALSE(ctx, av_fifo_size(ctx->timestamp_list) > 0, AVERROR_UNKNOWN, "timestamp_list is empty\n");
> +
> + av_fifo_generic_read(ctx->timestamp_list, ×tamp, sizeof(timestamp), NULL);
> +
> + // calc dts shift if max_b_frames > 0
> + if (avctx->max_b_frames > 0 && ctx->dts_delay == 0){
> + int64_t timestamp_last = AV_NOPTS_VALUE;
> + AMF_RETURN_IF_FALSE(ctx, av_fifo_size(ctx->timestamp_list) > 0, AVERROR_UNKNOWN,
> + "timestamp_list is empty while max_b_frames = %d\n", avctx->max_b_frames);
> + av_fifo_generic_peek_at(
> + ctx->timestamp_list,
> + ×tamp_last,
> + (av_fifo_size(ctx->timestamp_list) / sizeof(timestamp) - 1) * sizeof(timestamp_last),
> + sizeof(timestamp_last),
Some trailing spaces here.
> + NULL);
> + if (timestamp < 0 || timestamp_last < AV_NOPTS_VALUE) {
> + return AVERROR(ERANGE);
> + }
> + ctx->dts_delay = timestamp_last - timestamp;
> + }
> + pkt->dts = timestamp - ctx->dts_delay;
> + return 0;
> +}
> ...
More information about the ffmpeg-devel
mailing list