[FFmpeg-devel] Added HW H.264 and HEVC encoding for AMD GPUs based on AMF SDK
Mark Thompson
sw at jkqxz.net
Sat Nov 4 22:15:14 EET 2017
On 31/10/17 19:39, mmironov wrote:
> From 8640b995634f827eb39ae87bcbe2c1992d8140f2 Mon Sep 17 00:00:00 2001
> From: mmironov <mikhail.mironov at amd.com>
> Date: Fri, 27 Oct 2017 13:03:15 -0400
> Subject: [PATCH] Added: HW accelerated H.264 and HEVC encoding for AMD GPUs
> based on AMF SDK
>
> Signed-off-by: mmironov <mikhail.mironov at amd.com>
> ---
> Changelog | 3 +-
> compat/amd/amfsdkenc.h | 1753 ++++++++++++++++++++++++++++++++++++++++++++++
> configure | 25 +
> libavcodec/Makefile | 4 +
> libavcodec/allcodecs.c | 2 +
> libavcodec/amfenc.c | 513 ++++++++++++++
> libavcodec/amfenc.h | 136 ++++
> libavcodec/amfenc_h264.c | 368 ++++++++++
> libavcodec/amfenc_hevc.c | 296 ++++++++
> libavcodec/version.h | 4 +-
> 10 files changed, 3101 insertions(+), 3 deletions(-)
> 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
>
> diff --git a/Changelog b/Changelog
> index 6592d86..f0d22fa 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -6,7 +6,8 @@ version <next>:
> - Dropped support for OpenJPEG versions 2.0 and below. Using OpenJPEG now
> requires 2.1 (or later) and pkg-config.
> - VDA dropped (use VideoToolbox instead)
> -
> +- AMF H.264 encoder
> +- AMF HEVC encoder
>
> version 3.4:
> - deflicker video filter
> diff --git a/compat/amd/amfsdkenc.h b/compat/amd/amfsdkenc.h
> new file mode 100644
> index 0000000..0d48451
> --- /dev/null
> +++ b/compat/amd/amfsdkenc.h> @@ -0,0 +1,1753 @@
> +//
> +// MIT license
> +//
> +// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.
> +//
> +// Permission is hereby granted, free of charge, to any person obtaining a copy
> +// of this software and associated documentation files (the "Software"), to deal
> +// in the Software without restriction, including without limitation the rights
> +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> +// copies of the Software, and to permit persons to whom the Software is
> +// furnished to do so, subject to the following conditions:
> +//
> +// The above copyright notice and this permission notice shall be included in
> +// all copies or substantial portions of the Software.
> +//
> +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> +// THE SOFTWARE.
> +//
> +
> +// Reduced AMF API
> +//
> +// Full version of AMF SDK and the latest version of this file
> +// can be found at https://github.com/GPUOpen-LibrariesAndSDKs/AMF
On further consideration I am against including this header. Just ask the user to get it from this link you are including anyway. (Also maybe make that packagable by providing some means to install it, preferably including pkg-config support.)
> diff --git a/configure b/configure
> index 0e1ccaa..403e010 100755
> --- a/configure
> +++ b/configure
> @@ -304,6 +304,7 @@ External library support:
>
> The following libraries provide various hardware acceleration features:
> --disable-audiotoolbox disable Apple AudioToolbox code [autodetect]
> + --disable-amf disable AMF video encoding code [autodetect]
m < u
> --disable-cuda disable dynamically linked Nvidia CUDA code [autodetect]
> --enable-cuda-sdk enable CUDA features that require the CUDA SDK [no]
> --disable-cuvid disable Nvidia CUVID support [autodetect]
> @@ -1641,6 +1642,7 @@ EXTERNAL_LIBRARY_LIST="
> "
>
> HWACCEL_AUTODETECT_LIBRARY_LIST="
> + amf
> audiotoolbox
> crystalhd
> cuda
> @@ -2785,12 +2787,15 @@ scale_npp_filter_deps="cuda libnpp"
> scale_cuda_filter_deps="cuda_sdk"
> thumbnail_cuda_filter_deps="cuda_sdk"
>
> +amf_deps_any="dlopen 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="cuda cuvid"
> h264_cuvid_decoder_select="h264_mp4toannexb_bsf"
> @@ -2809,6 +2814,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="cuda cuvid"
> hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf"
> hevc_mediacodec_decoder_deps="mediacodec"
> @@ -6305,6 +6311,18 @@ else
> disable cuda cuvid nvenc
> fi
>
> +if enabled x86; then
> + case $target_os in
> + mingw32*|mingw64*|win32|win64|cygwin*)
> + ;;
> + *)
> + disable amf
> + ;;
> + esac
> +else
> + disable amf
> +fi
> +
> enabled nvenc &&
> check_cc -I$source_path <<EOF || disable nvenc
> #include "compat/nvenc/nvEncodeAPI.h"
> @@ -6313,6 +6331,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 bc4d7da..cbf45ac 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -50,6 +50,7 @@ OBJS = allcodecs.o \
> # subsystems
> OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o
> OBJS-$(CONFIG_AC3DSP) += ac3dsp.o ac3.o ac3tab.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
> @@ -334,6 +335,7 @@ OBJS-$(CONFIG_H264_DECODER) += h264dec.o h264_cabac.o h264_cavlc.o \
> OBJS-$(CONFIG_H264_CUVID_DECODER) += cuvid.o
> OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o
> OBJS-$(CONFIG_H264_MMAL_DECODER) += mmaldec.o
> +OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o
A < M
> OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o
> OBJS-$(CONFIG_NVENC_ENCODER) += nvenc_h264.o
> OBJS-$(CONFIG_NVENC_H264_ENCODER) += nvenc_h264.o
> @@ -352,6 +354,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o \
> hevcdsp.o hevc_filter.o hevc_data.o
> OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuvid.o
> OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o
> +OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o
A < M
> OBJS-$(CONFIG_HEVC_NVENC_ENCODER) += nvenc_hevc.o
> OBJS-$(CONFIG_NVENC_HEVC_ENCODER) += nvenc_hevc.o
> OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec_h2645.o
> @@ -1056,6 +1059,7 @@ SKIPHEADERS-$(CONFIG_JNI) += ffjni.h
> SKIPHEADERS-$(CONFIG_LIBVPX) += libvpx.h
> SKIPHEADERS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.h
> SKIPHEADERS-$(CONFIG_MEDIACODEC) += mediacodecdec_common.h mediacodec_surface.h mediacodec_wrapper.h mediacodec_sw_buffer.h
> +SKIPHEADERS-$(CONFIG_AMF) += amfenc.h
> SKIPHEADERS-$(CONFIG_NVENC) += nvenc.h
> SKIPHEADERS-$(CONFIG_QSV) += qsv.h qsv_internal.h
> SKIPHEADERS-$(CONFIG_QSVDEC) += qsvdec.h
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 8369126..3d2299f 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -649,6 +649,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);
> @@ -661,6 +662,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..fafe8da
> --- /dev/null
> +++ b/libavcodec/amfenc.c
> @@ -0,0 +1,513 @@
> +/*
> + * 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 "amfenc.h"
This is an lavc header, it should be after the lavu headers.
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/hwcontext.h"
> +#include "libavutil/hwcontext_d3d11va.h"
> +#include "libavutil/mem.h"
> +#include "libavutil/pixdesc.h"
> +#include "libavutil/time.h"
> +#include "internal.h"
> +
> +#include <d3d11.h>
> +
> +#ifdef _WIN32
> +#include "compat/w32dlfcn.h"
> +#else
> +#include <dlfcn.h>
> +#endif
> +
> +#define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf"
> +
> +#define PTS_PROP L"PtsProp"
Who is defining this key? (Does the AMF code look inside it?)
> +
> +const enum AVPixelFormat ff_amf_pix_fmts[] = {
> + AV_PIX_FMT_NV12,
> + AV_PIX_FMT_RGB0,
> + AV_PIX_FMT_BGR0,
I still think including RGB formats here is a bad idea without any colourspace support.
> + 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 =
const
> +{
> + .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->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 (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);
> + }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);
> + } else {
> + av_log(avctx, AV_LOG_INFO, "amf_shared: avctx->hw_device_ctx has non-AMD device, switching to default\n");
> + }
> + }
> + }
> + }
> + if (!ctx->hw_frames_ctx) {
This runs if you have hw_device_ctx set and you call InitDX11 twice? Is that intended?
> + 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);
> +
> + ctx->eof = 0;
> + ctx->delayed_drain = 0;
> + ctx->delayed_surface = NULL;
> +
> + return 0;
> +}
> +
> +static int amf_terminate(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;
> +
> + 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);
> + 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++) {
> + 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 int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buffer)
> +{
> + int ret;
> + AMFVariantStruct var = {0};
> + 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
> + pkt->dts = buffer->pVtbl->GetPts(buffer); // in monotonic order
Does the GetPts function really return the DTS?
> +
> + return 0;
> +}
> +
> +// amfenc API implmentation
> +int ff_amf_encode_init(AVCodecContext *avctx)
> +{
> + AmfContext *ctx = avctx->priv_data;
> + int ret;
> +
> + ctx->factory = NULL;
> + ctx->debug = NULL;
> + ctx->trace = NULL;
> + ctx->context = NULL;
> + ctx->encoder = NULL;
> + ctx->library = NULL;
> + ctx->version = 0;
> + ctx->eof = 0;
> + ctx->format = 0;
> + ctx->tracer.vtbl = NULL;
> + ctx->tracer.avctx = NULL;
> +
> + if ((ret = amf_load_library(avctx)) == 0) {
> + if ((ret = amf_init_context(avctx)) == 0) {
> + if ((ret = amf_init_encoder(avctx)) == 0) {
> + return 0;
> + }
> + }
> + }
> + amf_terminate(avctx);
> + return ret;
> +}
> +
> +int av_cold ff_amf_encode_close(AVCodecContext *avctx)
> +{
> + int ret;
> + ret = amf_terminate(avctx);
> + return ret;
Why bother with this function at all? Just rename amf_terminate as the close function.
> +}
> +
> +int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
> +{
> + AMF_RESULT res = AMF_OK;
> + AmfContext *ctx = avctx->priv_data;
> + AMFSurface *surface = NULL;
> +
> + if (!ctx->encoder)
> + return AVERROR(EINVAL);
> +
> + if (!frame) { // submit drain
> + if (!ctx->eof) { // submit drain one time only
> + if (ctx->delayed_surface != NULL) {
> + ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
> + } else if(!ctx->delayed_drain){
> + res = ctx->encoder->pVtbl->Drain(ctx->encoder);
> + if (res == AMF_INPUT_FULL) {
> + ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
> + }else {
> + if (res == AMF_OK) {
> + ctx->eof = 1; // drain started
> + }
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Drain() failed with error %d\n", res);
> + }
> + }
> + }else{
> + return AVERROR_EOF;
> + }
> + } else { // submit frame
> + if (ctx->delayed_surface != NULL) {
> + return AVERROR(EAGAIN); // should not happen when called from ffmpeg, other clients may resubmit
> + }
> + // prepare surface from frame
> + if (frame->hw_frames_ctx && ( // HW frame detected
> + // check if the same hw_frames_ctx as used in initialization
> + (ctx->hw_frames_ctx && frame->hw_frames_ctx->data == ctx->hw_frames_ctx->data) ||
> + // check if the same hw_device_ctx as used in initialization
> + (ctx->hw_device_ctx && ((AVHWFramesContext*)frame->hw_frames_ctx->data)->device_ctx ==
> + (AVHWDeviceContext*)ctx->hw_device_ctx->data)
> + )) {
> + GUID AMFTextureArrayIndexGUID = AMFTextureArrayIndexGUIDDef;
> + ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; // actual texture
> + int index = (int)(size_t)frame->data[1]; // index is a slice in texture array is - set to tell AMF which slice to use
> + texture->lpVtbl->SetPrivateData(texture, &AMFTextureArrayIndexGUID, sizeof(index), &index);
> +
> + res = ctx->context->pVtbl->CreateSurfaceFromDX11Native(ctx->context, texture, &surface, NULL); // wrap to AMF surface
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "CreateSurfaceFromDX11Native() failed with error %d\n", res);
I think you will need to hold a reference to the input frame while it's in the encoder here, assuming it doesn't get copied away immediately. If not, it will be returned to the frame pool and might be reused by someone else - it can't be freed because you are holding a reference to its frames context, but I think it can be written to.
(I could believe that testing with ffmpeg (something like "./ffmpeg_g -hwaccel d3d11va -hwaccel_output_format d3d11 input.mp4 -c:v h264_amf ... output.mp4", presumably?) is not sufficient to show any problems here. Not sure what would be.)
> +
> + // input HW surfaces can be vertically aligned by 16; tell AMF the real size
> + surface->pVtbl->SetCrop(surface, 0, 0, frame->width, frame->height);
> + } else {
> + res = ctx->context->pVtbl->AllocSurface(ctx->context, AMF_MEMORY_HOST, ctx->format, avctx->width, avctx->height, &surface);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "AllocSurface() failed with error %d\n", res);
> + amf_copy_surface(avctx, frame, surface);
> + }
> + surface->pVtbl->SetPts(surface, frame->pts);
> + AMF_ASSIGN_PROPERTY_INT64(res, surface, PTS_PROP, frame->pts);
> +
> + // submit surface
> + res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)surface);
> + if (res == AMF_INPUT_FULL) { // handle full queue
> + //store surface for later submission
> + ctx->delayed_surface = surface;
> + }
> + else {
> + surface->pVtbl->Release(surface);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "SubmitInput() failed with error %d\n", res);
> + }
> + }
> + return 0;
> +}
> +int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
> +{
> + int ret;
> + AMF_RESULT res;
> + AMF_RESULT res_query;
> + AmfContext *ctx = avctx->priv_data;
> + AMFData *data = NULL;
> + int block_and_wait;
> +
> + if (!ctx->encoder)
> + return AVERROR(EINVAL);
> +
> + do {
> + block_and_wait = 0;
> + // poll data
> + res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data);
> + if (data) {
> + // copy data to packet
> + AMFBuffer* buffer;
> + AMFGuid guid = IID_AMFBuffer();
> + data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface
> + ret = amf_copy_buffer(avctx, avpkt, buffer);
> +
> + buffer->pVtbl->Release(buffer);
> + data->pVtbl->Release(data);
> +
> + AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret);
> +
> + if (ctx->delayed_surface != NULL) { // try to resubmit frame
> + res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)ctx->delayed_surface);
> + if (res != AMF_INPUT_FULL) {
> + ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface);
> + ctx->delayed_surface = NULL;
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res);
> + }else {
> + av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed frame submission got AMF_INPUT_FULL- should not happen\n");
> + }
> + }else if (ctx->delayed_drain) { // try to resubmit drain
> + res = ctx->encoder->pVtbl->Drain(ctx->encoder);
> + if (res != AMF_INPUT_FULL) {
> + ctx->delayed_drain = 0;
> + ctx->eof = 1; // drain started
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated Drain() failed with error %d\n", res);
> + }else {
> + av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n");
> + }
> + }
Just to confirm, the encoder will /always/ be able to consume the new surface after returning a packet?
> + }else if (ctx->delayed_surface != NULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF)) {
> + block_and_wait = 1;
> + av_usleep(1000); // wait and poll again
:(
> + }
> + } while (block_and_wait);
> +
> + if (res_query == AMF_EOF) {
> + ret = AVERROR_EOF;
> + }else if (data == NULL) {
> + ret = AVERROR(EAGAIN);
> + }else {
> + ret = 0;
> + }
> + return ret;
> +}
That looks more sensible to me now. Thanks for changing this.
> diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
> new file mode 100644
> index 0000000..381242a
> --- /dev/null
> +++ b/libavcodec/amfenc.h
> @@ -0,0 +1,136 @@
> +/*
> +* 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
> +*/
> +
> +#ifndef AVCODEC_AMFENC_H
> +#define AVCODEC_AMFENC_H
> +
> +#include "config.h"
> +#include "avcodec.h"
> +#include "compat/amd/amfsdkenc.h"
> +
> +
> +/**
> +* AMF trace writer callback class
> +* Used to capture all AMF logging
> +*/
> +
> +typedef struct AmfTraceWriter {
> + AMFTraceWriterVtbl* vtbl;
> + AVCodecContext *avctx;
> +} AmfTraceWriter;
> +
> +/**
> +* AMF encoder context
> +*/
> +
> +typedef struct AmfContext {
> + AVClass *avclass;
> + // access to AMF runtime
> + amf_handle library; ///< handle to DLL library
> + AMFFactory *factory; ///< pointer to AMF factory
> + AMFDebug* debug; ///< pointer to AMF debug interface
> + AMFTrace* trace; ///< pointer to AMF trace interface
> +
> + amf_uint64 version; ///< version of AMF runtime
> + AmfTraceWriter tracer; ///< AMF writer registered with AMF
> + AMFContext *context; ///< AMF context
> + //encoder
> + AMFComponent* encoder; ///< AMF encoder object
> + amf_bool eof; ///< flag indicating EOF happened
> + AMF_SURFACE_FORMAT format; ///< AMF surface format
> +
> + AVBufferRef *hw_device_ctx; ///< pointer to HW accelerator (decoder)
> + AVBufferRef *hw_frames_ctx; ///< pointer to HW accelerator (frame allocator)
> +
> + // helpers to handle async calls
> + int delayed_drain;
> + AMFSurface *delayed_surface;
> +
> + // common encoder option options
> +
> + int log_to_dbg;
> +
> + // Static options, have to be set before Init() call
> + int usage;
> + int profile;
> + int level;
> + int preanalysis;
> + int quality;
> + int b_frame_delta_qp;
> + int ref_b_frame_delta_qp;
> +
> + // Dynamic options, can be set after Init() call
> +
> + int rate_control_mode;
> + int enforce_hrd;
> + int filler_data;
> + int enable_vbaq;
> + int skip_frame;
> + int qp_i;
> + int qp_p;
> + int qp_b;
> + int max_au_size;
> + int header_spacing;
> + int b_frame_ref;
> + int intra_refresh_mb;
> + int coding_mode;
> + int me_half_pel;
> + int me_quarter_pel;
> +
> + // HEVC - specific options
> +
> + int gops_per_idr;
> + int header_insertion_mode;
> + int min_qp_i;
> + int max_qp_i;
> + int min_qp_p;
> + int max_qp_p;
> + int tier;
> +} AmfContext;
> +
> +/**
> +* Common encoder initization function
> +*/
> +int ff_amf_encode_init(AVCodecContext *avctx);
> +/**
> +* Common encoder termination function
> +*/
> +int ff_amf_encode_close(AVCodecContext *avctx);
> +
> +/**
> +* Ecoding one frame - common function for all AMF encoders
> +*/
> +
> +int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame);
> +int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
> +
> +/**
> +* Supported formats
> +*/
> +extern const enum AVPixelFormat ff_amf_pix_fmts[];
> +
> +/**
> +* Error handling helper
> +*/
> +#define AMF_RETURN_IF_FALSE(avctx, exp, ret_value, /*message,*/ ...) \
> + if (!(exp)) { \
> + av_log(avctx, AV_LOG_ERROR, __VA_ARGS__); \
> + return AVERROR(ret_value); \
> + }
> +
> +#endif //AVCODEC_AMFENC_H
> diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
> new file mode 100644
> index 0000000..dc212bc
> --- /dev/null
> +++ b/libavcodec/amfenc_h264.c
> @@ -0,0 +1,368 @@
> +/*
> + * 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 "amfenc.h"
> +
> +#include "libavutil/internal.h"
> +#include "libavutil/opt.h"
> +#include "internal.h"
> +
> +#define OFFSET(x) offsetof(AmfContext, x)
> +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +
> +static const AVOption options[] = {
> + // Static
> + /// Usage
> + { "usage", "Encoder Usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING }, AMF_VIDEO_ENCODER_USAGE_TRANSCONDING, AMF_VIDEO_ENCODER_USAGE_WEBCAM, VE, "usage" },
> + { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING }, 0, 0, VE, "usage" },
> + { "ultralowlatency","", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" },
> + { "lowlatency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" },
> + { "webcam", "Webcam", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_WEBCAM }, 0, 0, VE, "usage" },
> +
> + /// Profile,
> + { "profile", "Profile", OFFSET(profile),AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, AMF_VIDEO_ENCODER_PROFILE_BASELINE, AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH, VE, "profile" },
> + { "baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_BASELINE }, 0, 0, VE, "profile" },
I still very much doubt you support baseline profile. Please remove it to avoid confusion.
> + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, 0, 0, VE, "profile" },
> + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_HIGH }, 0, 0, VE, "profile" },
> + { "constrained_baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_BASELINE }, 0, 0, VE, "profile" },
> + { "constrained_high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH }, 0, 0, VE, "profile" },
> +
> + /// Profile Level
> + { "level", "Profile Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 62, VE, "level" },
> + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "level" },
> + { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 10 }, 0, 0, VE, "level" },
> + { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 11 }, 0, 0, VE, "level" },
> + { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 12 }, 0, 0, VE, "level" },
> + { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, 0, 0, VE, "level" },
> + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 20 }, 0, 0, VE, "level" },
> + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 21 }, 0, 0, VE, "level" },
> + { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 22 }, 0, 0, VE, "level" },
> + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, 0, 0, VE, "level" },
> + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, 0, 0, VE, "level" },
> + { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, VE, "level" },
> + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, 0, 0, VE, "level" },
> + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, 0, 0, VE, "level" },
> + { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, 0, 0, VE, "level" },
> + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, 0, 0, VE, "level" },
> + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, 0, 0, VE, "level" },
> + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, 0, 0, VE, "level" },
> + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 60 }, 0, 0, VE, "level" },
> + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 61 }, 0, 0, VE, "level" },
> + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 62 }, 0, 0, VE, "level" },
> +
> +
> + /// Quality Preset
> + { "quality", "Quality Preference", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED, AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY, VE, "quality" },
> + { "speed", "Prefer Speed", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" },
> + { "balanced", "Balanced", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED }, 0, 0, VE, "quality" },
> + { "quality", "Prefer Quality", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" },
> +
> + // Dynamic
> + /// Rate Control Method
> + { "rc", "Rate Control Method", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, VE, "rc" },
> + { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" },
> + { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" },
> + { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" },
> + { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" },
> +
> + /// Enforce HRD, Filler Data, VBAQ, Frame Skipping
> + { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "filler_data", "Filler Data Enable", OFFSET(filler_data), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "vbaq", "Enable VBAQ", OFFSET(enable_vbaq), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "frame_skipping", "Rate Control Based Frame Skip", OFFSET(skip_frame), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE, NULL },
> +
> + /// QP Values
> + { "qp_i", "Quantization Parameter for I-Frame", OFFSET(qp_i), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE },
> + { "qp_p", "Quantization Parameter for P-Frame", OFFSET(qp_p), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE },
> + { "qp_b", "Quantization Parameter for B-Frame", OFFSET(qp_b), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE },
> +
> + /// Pre-Pass, Pre-Analysis, Two-Pass
> + { "preanalysis", "Pre-Analysis Mode", OFFSET(preanalysis), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> +
> + /// Maximum Access Unit Size
> + { "max_au_size", "Maximum Access Unit Size for rate control (in bits)", OFFSET(max_au_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE, NULL },
> +
> + /// Header Insertion Spacing
> + { "header_spacing", "Header Insertion Spacing", OFFSET(header_spacing), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1000, VE },
> +
> + /// B-Frames
> + // BPicturesPattern=bf
> + { "bf_delta_qp", "B-Picture Delta QP", OFFSET(b_frame_delta_qp), AV_OPT_TYPE_INT, { .i64 = 4 }, -10, 10, VE, NULL },
> + { "bf_ref", "Enable Reference to B-Frames", OFFSET(b_frame_ref), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE, NULL },
> + { "bf_ref_delta_qp","Reference B-Picture Delta QP", OFFSET(ref_b_frame_delta_qp), AV_OPT_TYPE_INT, { .i64 = 4 }, -10, 10, VE, NULL },
> +
> + /// Intra-Refresh
> + { "intra_refresh_mb","Intra Refresh MBs Number Per Slot in Macroblocks", OFFSET(intra_refresh_mb), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },
> +
> + /// coder
> + { "coder", "Coding Type", OFFSET(coding_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_UNDEFINED }, AMF_VIDEO_ENCODER_UNDEFINED, AMF_VIDEO_ENCODER_CALV, VE, "coding" },
You've still got "coding" as the unit name.
> + { "auto", "Automatic", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_UNDEFINED }, 0, 0, VE, "coder" },
> + { "cavlc", "Context Adaptive Variable-Length Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CALV }, 0, 0, VE, "coder" },
> + { "cabac", "Context Adaptive Binary Arithmetic Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CABAC }, 0, 0, VE, "coder" },
> +
> + { "me_half_pel", "Enable ME Half Pixel", OFFSET(me_half_pel), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE, NULL },
> + { "me_quarter_pel", "Enable ME Quarter Pixel", OFFSET(me_quarter_pel), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE, NULL },
> +
> + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
I would consistently omit the redundant final NULL from all ungrouped options.
> + { NULL }
> +};
> +
> +static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
> +{
> + int ret = 0;
> + AMF_RESULT res = AMF_OK;
> + AmfContext *ctx = avctx->priv_data;
> + AMFVariantStruct var = {0};
> + amf_int64 profile = 0;
> + amf_int64 profile_level = 0;
> + AMFBuffer *buffer;
> + AMFGuid guid;
> + AMFRate framerate;
> + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height);
> + int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0;
> +
> + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
> + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den);
> + }else {
> + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame);
> + }
> +
> + if ((ret = ff_amf_encode_init(avctx)) != 0)
> + return ret;
> +
> + // Static parameters
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_USAGE, ctx->usage);
> +
> + AMF_ASSIGN_PROPERTY_SIZE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMESIZE, framesize);
> +
> + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate);
> +
> + switch(avctx->profile){
> + case FF_PROFILE_H264_BASELINE:
> + profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE;
> + break;
> + case FF_PROFILE_H264_MAIN:
> + profile = AMF_VIDEO_ENCODER_PROFILE_MAIN;
> + break;
> + case FF_PROFILE_H264_HIGH:
> + profile = AMF_VIDEO_ENCODER_PROFILE_HIGH;
> + break;
> + case FF_PROFILE_H264_CONSTRAINED_BASELINE:
> + profile = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_BASELINE;
> + break;
> + case (FF_PROFILE_H264_HIGH | FF_PROFILE_H264_CONSTRAINED):
> + profile = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH;
> + break;
> + }
> + if (profile == 0) {
> + profile = ctx->profile;
> + }
> +
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PROFILE, profile);
> +
> + profile_level = avctx->level;
> + if (profile_level == FF_LEVEL_UNKNOWN) {
> + profile_level = ctx->level;
> + }
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PROFILE_LEVEL, profile_level);
> +
> + // Maximum Reference Frames
> + if (avctx->refs != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MAX_NUM_REFRAMES, avctx->refs);
> + }
> + if (avctx->sample_aspect_ratio.den && avctx->sample_aspect_ratio.num) {
> + AMFRatio ratio = AMFConstructRatio(avctx->sample_aspect_ratio.num, avctx->sample_aspect_ratio.den);
> + AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio);
> + }
> +
> + /// Color Range (Partial/TV/MPEG or Full/PC/JPEG)
> + if (avctx->color_range == AVCOL_RANGE_JPEG) {
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 1);
> + }
> +
> + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE, AMF_VIDEO_ENCODER_PREENCODE_DISABLED);
> + if (ctx->preanalysis)
> + av_log(ctx, AV_LOG_WARNING, "Pre-Analysis is not supported by cqp Rate Control Method, automatically disabled\n");
> + } else {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE, ctx->preanalysis);
> + }
> +
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUALITY_PRESET, ctx->quality);
> +
> + // Initialize Encoder
> + res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res);
> +
> + // Dynamic parmaters
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD, ctx->rate_control_mode);
> +
> + /// VBV Buffer
> + if (avctx->rc_buffer_size != 0) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_VBV_BUFFER_SIZE, avctx->rc_buffer_size);
> + if (avctx->rc_initial_buffer_occupancy != 0) {
> + int amf_buffer_fullness = avctx->rc_initial_buffer_occupancy * 64 / avctx->rc_buffer_size;
> + if (amf_buffer_fullness > 64)
> + amf_buffer_fullness = 64;
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_INITIAL_VBV_BUFFER_FULLNESS, amf_buffer_fullness);
> + }
> + }
> + /// Maximum Access Unit Size
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MAX_AU_SIZE, ctx->max_au_size);
> +
> + // QP Minimum / Maximum
> + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MIN_QP, 0);
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MAX_QP, 51);
> + } else {
> + if (avctx->qmin != -1) {
> + int qval = avctx->qmin > 51 ? 51 : avctx->qmin;
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MIN_QP, qval);
> + }
> + if (avctx->qmax != -1) {
> + int qval = avctx->qmax > 51 ? 51 : avctx->qmax;
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MAX_QP, qval);
> + }
> + }
> + // QP Values
> + if (ctx->qp_i != -1)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QP_I, ctx->qp_i);
> + if (ctx->qp_p != -1)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QP_P, ctx->qp_p);
> + if (ctx->qp_b != -1)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QP_B, ctx->qp_b);
> +
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_TARGET_BITRATE, avctx->bit_rate);
> +
> + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PEAK_BITRATE, avctx->bit_rate);
> + }
> + if (avctx->rc_max_rate) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PEAK_BITRATE, avctx->rc_max_rate);
> + }else if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR) {
> + av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n");
> + }
> + // Enforce HRD, Filler Data, VBAQ, Frame Skipping, Deblocking Filter
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_ENFORCE_HRD, !!ctx->enforce_hrd);
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FILLER_DATA_ENABLE, !!ctx->filler_data);
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_SKIP_FRAME_ENABLE, !!ctx->skip_frame);
> + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP) {
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_ENABLE_VBAQ, 0);
> + if (ctx->enable_vbaq)
> + av_log(ctx, AV_LOG_WARNING, "VBAQ is not supported by cqp Rate Control Method, automatically disabled\n");
> + } else {
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_ENABLE_VBAQ, !!ctx->enable_vbaq);
> + }
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_DE_BLOCKING_FILTER, !!deblocking_filter);
> +
> + // B-Frames
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, avctx->max_b_frames);
> + if (avctx->max_b_frames && res == AMF_OK) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_DELTA_QP, ctx->b_frame_delta_qp);
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_B_REFERENCE_ENABLE, !!ctx->b_frame_ref);
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP, ctx->ref_b_frame_delta_qp);
> + }
> +
> + // Keyframe Interval
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_IDR_PERIOD, avctx->gop_size);
> +
> + // Header Insertion Spacing
> + if (ctx->header_spacing >= 0)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEADER_INSERTION_SPACING, ctx->header_spacing);
> +
> + // Intra-Refresh, Slicing
> + if (ctx->intra_refresh_mb > 0)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_INTRA_REFRESH_NUM_MBS_PER_SLOT, ctx->intra_refresh_mb);
> + if (avctx->slices > 1)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_SLICES_PER_FRAME, avctx->slices);
> +
> + // Coding
> + if (ctx->coding_mode != 0)
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_CABAC_ENABLE, ctx->coding_mode);
> +
> + // Motion Estimation
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_MOTION_HALF_PIXEL, !!ctx->me_half_pel);
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_MOTION_QUARTERPIXEL, !!ctx->me_quarter_pel);
> +
> + // fill extradata
> + res = AMFVariantInit(&var);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "AMFVariantInit() failed with error %d\n", res);
> +
> + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_EXTRADATA, &var);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "GetProperty(AMF_VIDEO_ENCODER_EXTRADATA) failed with error %d\n", res);
> + AMF_RETURN_IF_FALSE(ctx, var.pInterface != NULL, AVERROR_BUG, "GetProperty(AMF_VIDEO_ENCODER_EXTRADATA) returned NULL\n");
> +
> + guid = IID_AMFBuffer();
> +
> + res = var.pInterface->pVtbl->QueryInterface(var.pInterface, &guid, (void**)&buffer); // query for buffer interface
> + if (res != AMF_OK) {
> + var.pInterface->pVtbl->Release(var.pInterface);
> + }
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "QueryInterface(IID_AMFBuffer) failed with error %d\n", res);
> +
> + avctx->extradata_size = (int)buffer->pVtbl->GetSize(buffer);
> + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
> + if (!avctx->extradata) {
> + buffer->pVtbl->Release(buffer);
> + var.pInterface->pVtbl->Release(var.pInterface);
> + return AVERROR(ENOMEM);
> + }
> + memcpy(avctx->extradata, buffer->pVtbl->GetNative(buffer), avctx->extradata_size);
> +
> + buffer->pVtbl->Release(buffer);
> + var.pInterface->pVtbl->Release(var.pInterface);
> +
> + return 0;
> +}
> +
> +static const AVCodecDefault defaults[] = {
> + { "refs", "-1" },
> + { "aspect", "0" },
> + { "sar", "0" },
> + { "qmin", "-1" },
> + { "qmax", "-1" },
> + { "b", "2M" },
> + { "maxrate", "3M" },
This default maxrate will apply if the user doesn't set anything. I don't think that will do what you want - e.g. "-b:v 10M" in isolation will set TARGET_BITRATE = 10M, PEAK_BITRATE = 3M.
> + { "g", "250" },
> + { "slices", "1" },
> + { NULL },
> +};
> +
> +static const AVClass h264_amf_class = {
> + .class_name = "h264_amf",
> + .item_name = av_default_item_name,
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +//TODO declare as HW encoder when available
> +AVCodec ff_h264_amf_encoder = {
> + .name = "h264_amf",
> + .long_name = NULL_IF_CONFIG_SMALL("AMD AMF H.264 Encoder"),
> + .type = AVMEDIA_TYPE_VIDEO,
> + .id = AV_CODEC_ID_H264,
> + .init = amf_encode_init_h264,
> + .send_frame = ff_amf_send_frame,
> + .receive_packet = ff_amf_receive_packet,
> + .close = ff_amf_encode_close,
> + .priv_data_size = sizeof(AmfContext),
> + .priv_class = &h264_amf_class,
> + .defaults = defaults,
> + .capabilities = AV_CODEC_CAP_DELAY,
> + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
> + .pix_fmts = ff_amf_pix_fmts,
> +};
> diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
> new file mode 100644
> index 0000000..8c5dd03
> --- /dev/null
> +++ b/libavcodec/amfenc_hevc.c
> @@ -0,0 +1,296 @@
> +/*
> + * 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 "amfenc.h"
> +
> +#include "libavutil/internal.h"
> +#include "libavutil/opt.h"
> +#include "internal.h"
> +
> +#define OFFSET(x) offsetof(AmfContext, x)
> +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +static const AVOption options[] = {
> + { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING }, AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING, AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM, VE, "usage" },
> + { "transcoding", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING }, 0, 0, VE, "usage" },
> + { "ultralowlatency","", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" },
> + { "lowlatency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" },
> + { "webcam", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM }, 0, 0, VE, "usage" },
> +
> + { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, VE, "profile" },
> + { "main", "", 0, AV_OPT_TYPE_CONST,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, 0, 0, VE, "profile" },
> +
> + { "profile_tier", "Set the profile tier (default main)", OFFSET(tier), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN }, AMF_VIDEO_ENCODER_HEVC_TIER_MAIN, AMF_VIDEO_ENCODER_HEVC_TIER_HIGH, VE, "tier" },
> + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN }, 0, 0, VE, "tier" },
> + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_HIGH }, 0, 0, VE, "tier" },
> +
> + { "level", "Set the encoding level (default auto)", OFFSET(level), AV_OPT_TYPE_INT,{ .i64 = 0 }, 0, AMF_LEVEL_6_2, VE, "level" },
> + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "level" },
> + { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_1 }, 0, 0, VE, "level" },
> + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_2 }, 0, 0, VE, "level" },
> + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_2_1 }, 0, 0, VE, "level" },
> + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_3 }, 0, 0, VE, "level" },
> + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_3_1 }, 0, 0, VE, "level" },
> + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_4 }, 0, 0, VE, "level" },
> + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_4_1 }, 0, 0, VE, "level" },
> + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5 }, 0, 0, VE, "level" },
> + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5_1 }, 0, 0, VE, "level" },
> + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5_2 }, 0, 0, VE, "level" },
> + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6 }, 0, 0, VE, "level" },
> + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6_1 }, 0, 0, VE, "level" },
> + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6_2 }, 0, 0, VE, "level" },
> +
> + { "quality", "Set the encoding quality", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED, VE, "quality" },
> + { "balanced", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED }, 0, 0, VE, "quality" },
> + { "speed", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" },
> + { "quality", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" },
These are 0, 5, 10. Do the intermediate values work? Should they be exposed?
> +
> + { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR, VE, "rc" },
> + { "cqp", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" },
> + { "cbr", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" },
> + { "vbr_peak", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" },
> + { "vbr_latency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" },
> +
> + { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED, VE, "hdrmode" },
> + { "none", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, "hdrmode" },
> + { "gop", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, "hdrmode" },
> + { "idr", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED }, 0, 0, VE, "hdrmode" },
> +
> + { "gops_per_idr", "GOPs per IDR 0-no IDR will be inserted", OFFSET(gops_per_idr), AV_OPT_TYPE_INT,{ .i64 = 60 }, 0, INT_MAX, VE },
> + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "vbaq", "Enable VBAQ", OFFSET(enable_vbaq), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "filler_data", "Filler Data Enable", OFFSET(filler_data), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "max_au_size", "Maximum Access Unit Size for rate control (in bits)", OFFSET(max_au_size), AV_OPT_TYPE_INT,{ .i64 = 0 }, 0, INT_MAX, VE, NULL },
> + { "min_qp_i", "min quantization parameter for I-frame", OFFSET(min_qp_i), AV_OPT_TYPE_INT,{ .i64 = -1 }, -1, 51, VE },
> + { "max_qp_i", "max quantization parameter for I-frame", OFFSET(max_qp_i), AV_OPT_TYPE_INT,{ .i64 = -1 }, -1, 51, VE },
> + { "min_qp_p", "min quantization parameter for P-frame", OFFSET(min_qp_p), AV_OPT_TYPE_INT,{ .i64 = -1 }, -1, 51, VE },
> + { "max_qp_p", "max quantization parameter for P-frame", OFFSET(max_qp_p), AV_OPT_TYPE_INT,{ .i64 = -1 }, -1, 51, VE },
> + { "qp_p", "quantization parameter for P-frame", OFFSET(qp_p), AV_OPT_TYPE_INT,{ .i64 = -1 }, -1, 51, VE },
> + { "qp_i", "quantization parameter for I-frame", OFFSET(qp_i), AV_OPT_TYPE_INT,{ .i64 = -1 }, -1, 51, VE },
> + { "skip_frame", "Rate Control Based Frame Skip", OFFSET(skip_frame), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { "me_half_pel", "Enable ME Half Pixel", OFFSET(me_half_pel), AV_OPT_TYPE_BOOL,{ .i64 = 1 }, 0, 1, VE, NULL },
> + { "me_quarter_pel", "Enable ME Quarter Pixel ", OFFSET(me_quarter_pel), AV_OPT_TYPE_BOOL,{ .i64 = 1 }, 0, 1, VE, NULL },
> +
> + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL },
> + { NULL }
> +};
> +
> +static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
> +{
> + int ret = 0;
> + AMF_RESULT res = AMF_OK;
> + AmfContext *ctx = avctx->priv_data;
> + AMFVariantStruct var = {0};
> + amf_int64 profile = 0;
> + amf_int64 profile_level = 0;
> + AMFBuffer *buffer;
> + AMFGuid guid;
> + AMFRate framerate;
> + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height);
> + int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0;
> +
> + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
> + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den);
> + }else {
> + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame);
> + }
> +
> + if ((ret = ff_amf_encode_init(avctx)) < 0)
> + return ret;
> +
> + // init static parameters
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_USAGE, ctx->usage);
> +
> + AMF_ASSIGN_PROPERTY_SIZE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMESIZE, framesize);
> +
> + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate);
> +
> + switch (avctx->profile) {
> + case FF_PROFILE_HEVC_MAIN:
> + profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN;
> + break;
> + default:
> + break;
> + }
> + if (profile == 0) {
> + profile = ctx->profile;
> + }
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PROFILE, profile);
> +
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TIER, ctx->tier);
> +
> + profile_level = avctx->level;
> + if (profile_level == 0) {
> + profile_level = ctx->level;
> + }
> + if (profile_level != 0) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PROFILE_LEVEL, profile_level);
> + }
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, ctx->quality);
> + // Maximum Reference Frames
> + if (avctx->refs != 0) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MAX_NUM_REFRAMES, avctx->refs);
> + }
> + // Aspect Ratio
> + if (avctx->sample_aspect_ratio.den && avctx->sample_aspect_ratio.num) {
> + AMFRatio ratio = AMFConstructRatio(avctx->sample_aspect_ratio.num, avctx->sample_aspect_ratio.den);
> + AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, ratio);
> + }
> +
> + // Picture control properties
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, ctx->gops_per_idr);
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, avctx->gop_size);
> + if (avctx->slices > 1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_SLICES_PER_FRAME, avctx->slices);
> + }
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_DE_BLOCKING_FILTER_DISABLE, deblocking_filter);
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE, ctx->header_insertion_mode);
> +
> + // Rate control
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, ctx->rate_control_mode);
> + if (avctx->rc_buffer_size) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_VBV_BUFFER_SIZE, avctx->rc_buffer_size);
> +
> + if (avctx->rc_initial_buffer_occupancy != 0) {
> + int amf_buffer_fullness = avctx->rc_initial_buffer_occupancy * 64 / avctx->rc_buffer_size;
> + if (amf_buffer_fullness > 64)
> + amf_buffer_fullness = 64;
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_INITIAL_VBV_BUFFER_FULLNESS, amf_buffer_fullness);
> + }
> + }
> + // Pre-Pass, Pre-Analysis, Two-Pass
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_PREANALYSIS_ENABLE, ctx->preanalysis);
> +
> + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP) {
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, false);
> + if (ctx->enable_vbaq)
> + av_log(ctx, AV_LOG_WARNING, "VBAQ is not supported by cqp Rate Control Method, automatically disabled\n");
> + } else {
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, !!ctx->enable_vbaq);
> + }
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MOTION_HALF_PIXEL, ctx->me_half_pel);
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MOTION_QUARTERPIXEL, ctx->me_quarter_pel);
> +
> + // init encoder
> + res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res);
> +
> + // init dynamic rate control params
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ENFORCE_HRD, ctx->enforce_hrd);
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FILLER_DATA_ENABLE, ctx->filler_data);
> +
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, avctx->bit_rate);
> +
> + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, avctx->bit_rate);
> + }
> + if(avctx->rc_max_rate){
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, avctx->rc_max_rate);
> + }else if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR) {
> + av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n");
> + }
> +
> + // init dynamic picture control params
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MAX_AU_SIZE, ctx->max_au_size);
> +
> +
> + if (ctx->min_qp_i != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MIN_QP_I, ctx->min_qp_i);
> + }
> + if (ctx->max_qp_i != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MAX_QP_I, ctx->max_qp_i);
> + }
> + if (ctx->min_qp_p != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MIN_QP_P, ctx->min_qp_p);
> + }
> + if (ctx->max_qp_p != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MAX_QP_P, ctx->max_qp_p);
> + }
> +
> + if (ctx->qp_p != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_I, ctx->qp_p);
> + }
> + if (ctx->qp_i != -1) {
> + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_P, ctx->qp_i);
> + }
> + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE, ctx->skip_frame);
> +
> +
> + // fill extradata
> + res = AMFVariantInit(&var);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "AMFVariantInit() failed with error %d\n", res);
> +
> + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_HEVC_EXTRADATA, &var);
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "GetProperty(AMF_VIDEO_ENCODER_EXTRADATA) failed with error %d\n", res);
> + AMF_RETURN_IF_FALSE(ctx, var.pInterface != NULL, AVERROR_BUG, "GetProperty(AMF_VIDEO_ENCODER_EXTRADATA) returned NULL\n");
> +
> + guid = IID_AMFBuffer();
> +
> + res = var.pInterface->pVtbl->QueryInterface(var.pInterface, &guid, (void**)&buffer); // query for buffer interface
> + if (res != AMF_OK) {
> + var.pInterface->pVtbl->Release(var.pInterface);
> + }
> + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "QueryInterface(IID_AMFBuffer) failed with error %d\n", res);
> +
> + avctx->extradata_size = (int)buffer->pVtbl->GetSize(buffer);
> + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
> + if (!avctx->extradata) {
> + buffer->pVtbl->Release(buffer);
> + var.pInterface->pVtbl->Release(var.pInterface);
> + return AVERROR(ENOMEM);
> + }
> + memcpy(avctx->extradata, buffer->pVtbl->GetNative(buffer), avctx->extradata_size);
> +
> + buffer->pVtbl->Release(buffer);
> + var.pInterface->pVtbl->Release(var.pInterface);
> +
> + return 0;
> +}
> +static const AVCodecDefault defaults[] = {
> + { "refs", "-1" },
> + { "aspect", "0" },
> + { "sar", "0" },
> + { "b", "2M" },
> + { "maxrate", "3M" },
> + { "g", "250" },
> + { "slices", "1" },
> + { NULL },
> +};
> +static const AVClass hevc_amf_class = {
> + .class_name = "hevc_amf",
> + .item_name = av_default_item_name,
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +//TODO declare as HW encoder when available
> +AVCodec ff_hevc_amf_encoder = {
> + .name = "hevc_amf",
> + .long_name = NULL_IF_CONFIG_SMALL("AMD AMF HEVC encoder"),
> + .type = AVMEDIA_TYPE_VIDEO,
> + .id = AV_CODEC_ID_HEVC,
> + .init = amf_encode_init_hevc,
> + .send_frame = ff_amf_send_frame,
> + .receive_packet = ff_amf_receive_packet,
> + .close = ff_amf_encode_close,
> + .priv_data_size = sizeof(AmfContext),
> + .priv_class = &hevc_amf_class,
> + .defaults = defaults,
> + .capabilities = AV_CODEC_CAP_DELAY,
> + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
> + .pix_fmts = ff_amf_pix_fmts,
> +};
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 226da19..6c0d7a8 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -28,8 +28,8 @@
> #include "libavutil/version.h"
>
> #define LIBAVCODEC_VERSION_MAJOR 58
> -#define LIBAVCODEC_VERSION_MINOR 0
> -#define LIBAVCODEC_VERSION_MICRO 101
> +#define LIBAVCODEC_VERSION_MINOR 1
> +#define LIBAVCODEC_VERSION_MICRO 100
>
> #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> LIBAVCODEC_VERSION_MINOR, \
>
More information about the ffmpeg-devel
mailing list