[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, &timestamp, 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, &timestamp, 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, 
> +            &timestamp_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