diff mbox

[FFmpeg-devel,v9,1/2] lavc/svt_hevc: add libsvt hevc encoder wrapper

Message ID 1553658370-15207-1-git-send-email-jing.a.sun@intel.com
State Superseded
Headers show

Commit Message

Jing SUN March 27, 2019, 3:46 a.m. UTC
Signed-off-by: Zhengxu Huang <zhengxu.huang@intel.com>
Signed-off-by: Hassene Tmar <hassene.tmar@intel.com>
Signed-off-by: Jun Zhao <jun.zhao@intel.com>
Signed-off-by: Jing Sun <jing.a.sun@intel.com>
---
 configure                |   4 +
 libavcodec/Makefile      |   1 +
 libavcodec/allcodecs.c   |   1 +
 libavcodec/libsvt_hevc.c | 502 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 508 insertions(+)
 create mode 100644 libavcodec/libsvt_hevc.c

Comments

Vittorio Giovara March 27, 2019, 5:13 p.m. UTC | #1
On Tue, Mar 26, 2019 at 10:47 PM Jing Sun <jing.a.sun@intel.com> wrote:

> Signed-off-by: Zhengxu Huang <zhengxu.huang@intel.com>
> Signed-off-by: Hassene Tmar <hassene.tmar@intel.com>
> Signed-off-by: Jun Zhao <jun.zhao@intel.com>
> Signed-off-by: Jing Sun <jing.a.sun@intel.com>
> ---
>  configure                |   4 +
>  libavcodec/Makefile      |   1 +
>  libavcodec/allcodecs.c   |   1 +
>  libavcodec/libsvt_hevc.c | 502
> +++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 508 insertions(+)
>  create mode 100644 libavcodec/libsvt_hevc.c
>
> +
> +    if (avctx->pix_fmt == AV_PIX_FMT_YUV420P10LE) {
> +        av_log(avctx, AV_LOG_DEBUG , "Encoder 10 bits depth input\n");
> +
> +        // SVT-HEVC's compressed 10-bit format is to supported,
> +        // without which it works well functionally but with a
> +        // slight performance penalty caused by the extra conv
> +        // step from yuv420p10le to that format.
> +
> +        param->compressedTenBitFormat = 0;
> +        param->encoderBitDepth        = 10;
> +    }
>

So what is happening in this case? The encoder is slower because it
converts from 10 to 8 bit internally? And then the output encode is 8 bit
right now? If that is the case, I'd rather have this functionality removed
since the conversion can happen directly within ffmpeg. Having the
conversion performed by the encoder is guaranteed to be slower and less
precise, and if the output is not 10 bit it is very surprising too.

At the same time the comment in the code is useless because users will
never read something buried deep in the code, I'd suggest printing
something at the warning level so that it will be shown during the
conversion (and please have it proofread by a native English-speaking
person).


> +    param->encoderColorFormat = EB_YUV420;
> +
> +    // Update param from options
> +    param->hierarchicalLevels     = svt_enc->hierarchical_level - 1;
> +    param->encMode                = svt_enc->enc_mode;
> +    param->profile                = svt_enc->profile;
> +    param->tier                   = svt_enc->tier;
> +    param->level                  = svt_enc->level;
> +    param->rateControlMode        = svt_enc->rc_mode;
> +    param->sceneChangeDetection   = svt_enc->scd;
> +    param->tune                   = svt_enc->tune;
> +    param->baseLayerSwitchMode    = svt_enc->base_layer_switch_mode;
> +    param->qp                     = svt_enc->qp;
> +    param->accessUnitDelimiter    = svt_enc->aud;
> +
> +    param->targetBitRate          = avctx->bit_rate;
> +    if (avctx->gop_size > 0)
> +        param->intraPeriodLength  = avctx->gop_size - 1;
> +
> +    if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
> +        param->frameRateNumerator     = avctx->framerate.num;
> +        param->frameRateDenominator   = avctx->framerate.den *
> avctx->ticks_per_frame;
> +    } else {
> +        param->frameRateNumerator     = avctx->time_base.den;
> +        param->frameRateDenominator   = avctx->time_base.num *
> avctx->ticks_per_frame;
> +    }
> +
> +    if (param->rateControlMode) {
> +        param->maxQpAllowed       = avctx->qmax;
> +        param->minQpAllowed       = avctx->qmin;
> +    }
> +
> +    param->intraRefreshType       =
> +        !!(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP) + 1;
> +
> +    // is it repeat headers for MP4 or Annex-b
> +    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
> +        param->codeVpsSpsPps          = 0;
> +    else
> +        param->codeVpsSpsPps          = 1;
> +
> +    param->codeEosNal             = 1;
>

nit: excessive whitespace alignment

+
> +    if (svt_enc->hdr) {
> +        svt_enc->vui_info = 1;
> +        param->highDynamicRangeInput = svt_enc->hdr;
> +    }
>

Where is the warning that notifies the lack of color properties support?


> +
> +        avctx->extradata_size = header_ptr->nFilledLen;
> +        avctx->extradata = av_mallocz(avctx->extradata_size +
> AV_INPUT_BUFFER_PADDING_SIZE);
> +        if (!avctx->extradata) {
> +            av_log(avctx, AV_LOG_ERROR,
> +                   "Cannot allocate HEVC header of size %d.\n",
> avctx->extradata_size);
> +            svt_ret = EB_ErrorInsufficientResources;
> +            goto failed_init_enc;
> +        }
>

initialize extradata_size only in case of success, some code may rely on it

+#define OFFSET(x) offsetof(SvtContext, x)
> +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +static const AVOption options[] = {
> +    { "vui", "Enable vui info", OFFSET(vui_info),
> +      AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE },
> +
> +    { "aud", "Include AUD", OFFSET(aud),
> +      AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
>

IMO these options help text could be improved.
Mark Thompson March 27, 2019, 9:19 p.m. UTC | #2
On 27/03/2019 17:13, Vittorio Giovara wrote:
> On Tue, Mar 26, 2019 at 10:47 PM Jing Sun <jing.a.sun@intel.com> wrote:
> 
>> Signed-off-by: Zhengxu Huang <zhengxu.huang@intel.com>
>> Signed-off-by: Hassene Tmar <hassene.tmar@intel.com>
>> Signed-off-by: Jun Zhao <jun.zhao@intel.com>
>> Signed-off-by: Jing Sun <jing.a.sun@intel.com>
>> ---
>>  configure                |   4 +
>>  libavcodec/Makefile      |   1 +
>>  libavcodec/allcodecs.c   |   1 +
>>  libavcodec/libsvt_hevc.c | 502
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 508 insertions(+)
>>  create mode 100644 libavcodec/libsvt_hevc.c
>>
>> ...
>> +    if (svt_enc->hdr) {
>> +        svt_enc->vui_info = 1;
>> +        param->highDynamicRangeInput = svt_enc->hdr;
>> +    }
>>
> 
> Where is the warning that notifies the lack of color properties support?

Looking at what the highDynamicRangeInput field actually does <https://github.com/OpenVisualCloud/SVT-HEVC/blob/master/Source/Lib/Codec/EbResourceCoordinationProcess.c#L550-L566>, I don't think it makes sense for this external "hdr" option at exist at all.

From the point of view of a user looking at the external options, they might expect that on setting this option some conversion takes place to actually create an HDR output.  In fact, that's not what it does - it just marks the output with some very specific colour properties, and any stream which doesn't actually have exactly those properties will then have incorrect metadata for display (possibly conflicting with container metadata, if the container has better support for colour properties than this encoder).

Perhaps to avoid confusion about what is actually happening the option could be removed and this check replaced with something like:

if (avctx->colorspace == AVCOL_SPC_BT2020_NCL &&
    avctx->color_primaries == AVCOL_PRI_BT2020 &&
    avctx->color_trc == AVCOL_TRC_SMPTE2084 &&
    avctx->color_range == AVCOL_RANGE_MPEG &&
    avctx->chroma_sample_location == AVCHROMA_LOC_TOPLEFT) {
    param->highDynamicRangeInput = 1;
} else {
    param->highDynamicRangeInput = 0;
    // Maybe also a warning here in some cases?
}

That would then do the right thing for all streams which actually have the given properties, while not forcing incorrect display of anything else.

- Mark
Jing SUN March 29, 2019, 9:32 a.m. UTC | #3
On Thursday, March 28, 2019 5:20 AM, Mark Thompson wrote:

>Looking at what the highDynamicRangeInput field actually does <https://github.com/OpenVisualCloud/SVT-HEVC/blob/master/Source/Lib/Codec/EbResourceCoordinationProcess.c#L550-L566>, I don't think it makes sense for this external "hdr" option at exist at all.


>From the point of view of a user looking at the external options, they might expect that on setting this option some conversion takes place to actually create an HDR output.  In fact, that's not what it does - it just marks the output with some very specific colour properties, and any stream which doesn't actually have exactly those properties will then have incorrect metadata for display (possibly conflicting with container metadata, if the container has better support for colour properties than this encoder).


>Perhaps to avoid confusion about what is actually happening the option could be removed and this check replaced with something like:


>if (avctx->colorspace == AVCOL_SPC_BT2020_NCL &&

>    avctx->color_primaries == AVCOL_PRI_BT2020 &&

>    avctx->color_trc == AVCOL_TRC_SMPTE2084 &&

>    avctx->color_range == AVCOL_RANGE_MPEG &&

>    avctx->chroma_sample_location == AVCHROMA_LOC_TOPLEFT) {

>    param->highDynamicRangeInput = 1;

>} else {

>    param->highDynamicRangeInput = 0;

>    // Maybe also a warning here in some cases?

>}


>- Mark


Since SVT-HEVC's HDR is not ready, I just removed the interface and I will add it back when it's fully supported with color properties preserving. Please review v10 and thanks a lot.

- Jing
diff mbox

Patch

diff --git a/configure b/configure
index 938ff10..2aabac4 100755
--- a/configure
+++ b/configure
@@ -264,6 +264,7 @@  External library support:
   --enable-libspeex        enable Speex de/encoding via libspeex [no]
   --enable-libsrt          enable Haivision SRT protocol via libsrt [no]
   --enable-libssh          enable SFTP protocol via libssh [no]
+  --enable-libsvthevc      enable HEVC encoding via svt [no]
   --enable-libtensorflow   enable TensorFlow as a DNN module backend
                            for DNN based filters like sr [no]
   --enable-libtesseract    enable Tesseract, needed for ocr filter [no]
@@ -1784,6 +1785,7 @@  EXTERNAL_LIBRARY_LIST="
     libspeex
     libsrt
     libssh
+    libsvthevc
     libtensorflow
     libtesseract
     libtheora
@@ -3173,6 +3175,7 @@  libshine_encoder_select="audio_frame_queue"
 libspeex_decoder_deps="libspeex"
 libspeex_encoder_deps="libspeex"
 libspeex_encoder_select="audio_frame_queue"
+libsvt_hevc_encoder_deps="libsvthevc"
 libtheora_encoder_deps="libtheora"
 libtwolame_encoder_deps="libtwolame"
 libvo_amrwbenc_encoder_deps="libvo_amrwbenc"
@@ -6209,6 +6212,7 @@  enabled libsoxr           && require libsoxr soxr.h soxr_create -lsoxr
 enabled libssh            && require_pkg_config libssh libssh libssh/sftp.h sftp_init
 enabled libspeex          && require_pkg_config libspeex speex speex/speex.h speex_decoder_init
 enabled libsrt            && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket
+enabled libsvthevc        && require_pkg_config libsvthevc SvtHevcEnc EbApi.h EbInitHandle
 enabled libtensorflow     && require libtensorflow tensorflow/c/c_api.h TF_Version -ltensorflow
 enabled libtesseract      && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate
 enabled libtheora         && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 15c43a8..c93e545 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -987,6 +987,7 @@  OBJS-$(CONFIG_LIBOPUS_ENCODER)            += libopusenc.o libopus.o     \
 OBJS-$(CONFIG_LIBSHINE_ENCODER)           += libshine.o
 OBJS-$(CONFIG_LIBSPEEX_DECODER)           += libspeexdec.o
 OBJS-$(CONFIG_LIBSPEEX_ENCODER)           += libspeexenc.o
+OBJS-$(CONFIG_LIBSVT_HEVC_ENCODER)        += libsvt_hevc.o
 OBJS-$(CONFIG_LIBTHEORA_ENCODER)          += libtheoraenc.o
 OBJS-$(CONFIG_LIBTWOLAME_ENCODER)         += libtwolame.o
 OBJS-$(CONFIG_LIBVO_AMRWBENC_ENCODER)     += libvo-amrwbenc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index b26aeca..e93f66f 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -703,6 +703,7 @@  extern AVCodec ff_librsvg_decoder;
 extern AVCodec ff_libshine_encoder;
 extern AVCodec ff_libspeex_encoder;
 extern AVCodec ff_libspeex_decoder;
+extern AVCodec ff_libsvt_hevc_encoder;
 extern AVCodec ff_libtheora_encoder;
 extern AVCodec ff_libtwolame_encoder;
 extern AVCodec ff_libvo_amrwbenc_encoder;
diff --git a/libavcodec/libsvt_hevc.c b/libavcodec/libsvt_hevc.c
new file mode 100644
index 0000000..ca6e37b
--- /dev/null
+++ b/libavcodec/libsvt_hevc.c
@@ -0,0 +1,502 @@ 
+/*
+* Scalable Video Technology for HEVC encoder library plugin
+*
+* Copyright (c) 2018 Intel Corporation
+*
+* 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 this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "EbErrorCodes.h"
+#include "EbTime.h"
+#include "EbApi.h"
+
+#include "libavutil/common.h"
+#include "libavutil/frame.h"
+#include "libavutil/opt.h"
+
+#include "internal.h"
+#include "avcodec.h"
+
+typedef enum eos_status {
+    EOS_NOT_REACHED = 0,
+    EOS_REACHED,
+    EOS_TOTRIGGER
+}EOS_STATUS;
+
+typedef struct SvtContext {
+    AVClass *class;
+
+    EB_H265_ENC_CONFIGURATION enc_params;
+    EB_COMPONENTTYPE *svt_handle;
+    EB_BUFFERHEADERTYPE in_buf;
+    EOS_STATUS eos_flag;
+
+    // User options.
+    int vui_info;
+    int hierarchical_level;
+    int la_depth;
+    int enc_mode;
+    int rc_mode;
+    int scd;
+    int tune;
+    int qp;
+    int hdr;
+
+    int forced_idr;
+
+    int aud;
+
+    int profile;
+    int tier;
+    int level;
+
+    int base_layer_switch_mode;
+} SvtContext;
+
+static int error_mapping(EB_ERRORTYPE svt_ret)
+{
+    int err;
+
+    switch (svt_ret) {
+    case EB_ErrorInsufficientResources:
+        err = AVERROR(ENOMEM);
+        break;
+
+    case EB_ErrorUndefined:
+    case EB_ErrorInvalidComponent:
+    case EB_ErrorBadParameter:
+        err = AVERROR(EINVAL);
+        break;
+
+    case EB_ErrorDestroyThreadFailed:
+    case EB_ErrorSemaphoreUnresponsive:
+    case EB_ErrorDestroySemaphoreFailed:
+    case EB_ErrorCreateMutexFailed:
+    case EB_ErrorMutexUnresponsive:
+    case EB_ErrorDestroyMutexFailed:
+        err = AVERROR_EXTERNAL;
+            break;
+
+    case EB_NoErrorEmptyQueue:
+        err = AVERROR(EAGAIN);
+
+    case EB_ErrorNone:
+        err = 0;
+        break;
+
+    default:
+        err = AVERROR_UNKNOWN;
+    }
+
+    return err;
+}
+
+static void free_buffer(SvtContext *svt_enc)
+{
+    uint8_t *in_data = svt_enc->in_buf.pBuffer;
+
+    av_freep(&in_data);
+}
+
+static int alloc_buffer(EB_H265_ENC_CONFIGURATION *config, SvtContext *svt_enc)
+{
+    EB_H265_ENC_INPUT *in_data;
+
+    // allocate buffer for in and out
+    in_data  = av_mallocz(sizeof(*in_data));
+    if (!in_data)
+        goto failed;
+    svt_enc->in_buf.pBuffer  = (unsigned char *)in_data;
+
+    svt_enc->in_buf.nSize        = sizeof(svt_enc->in_buf);
+    svt_enc->in_buf.pAppPrivate  = NULL;
+
+    return 0;
+
+failed:
+    free_buffer(svt_enc);
+    return AVERROR(ENOMEM);
+}
+
+static int config_enc_params(EB_H265_ENC_CONFIGURATION *param,
+                             AVCodecContext *avctx)
+{
+    SvtContext *svt_enc = avctx->priv_data;
+    int ret;
+
+    param->sourceWidth     = avctx->width;
+    param->sourceHeight    = avctx->height;
+    param->encoderBitDepth = 8;
+
+    if (avctx->pix_fmt == AV_PIX_FMT_YUV420P10LE) {
+        av_log(avctx, AV_LOG_DEBUG , "Encoder 10 bits depth input\n");
+
+        // SVT-HEVC's compressed 10-bit format is to supported,
+        // without which it works well functionally but with a
+        // slight performance penalty caused by the extra conv
+        // step from yuv420p10le to that format.
+
+        param->compressedTenBitFormat = 0;
+        param->encoderBitDepth        = 10;
+    }
+    param->encoderColorFormat = EB_YUV420;
+
+    // Update param from options
+    param->hierarchicalLevels     = svt_enc->hierarchical_level - 1;
+    param->encMode                = svt_enc->enc_mode;
+    param->profile                = svt_enc->profile;
+    param->tier                   = svt_enc->tier;
+    param->level                  = svt_enc->level;
+    param->rateControlMode        = svt_enc->rc_mode;
+    param->sceneChangeDetection   = svt_enc->scd;
+    param->tune                   = svt_enc->tune;
+    param->baseLayerSwitchMode    = svt_enc->base_layer_switch_mode;
+    param->qp                     = svt_enc->qp;
+    param->accessUnitDelimiter    = svt_enc->aud;
+
+    param->targetBitRate          = avctx->bit_rate;
+    if (avctx->gop_size > 0)
+        param->intraPeriodLength  = avctx->gop_size - 1;
+
+    if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
+        param->frameRateNumerator     = avctx->framerate.num;
+        param->frameRateDenominator   = avctx->framerate.den * avctx->ticks_per_frame;
+    } else {
+        param->frameRateNumerator     = avctx->time_base.den;
+        param->frameRateDenominator   = avctx->time_base.num * avctx->ticks_per_frame;
+    }
+
+    if (param->rateControlMode) {
+        param->maxQpAllowed       = avctx->qmax;
+        param->minQpAllowed       = avctx->qmin;
+    }
+
+    param->intraRefreshType       =
+        !!(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP) + 1;
+
+    // is it repeat headers for MP4 or Annex-b
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
+        param->codeVpsSpsPps          = 0;
+    else
+        param->codeVpsSpsPps          = 1;
+
+    param->codeEosNal             = 1;
+
+    if (svt_enc->hdr) {
+        svt_enc->vui_info = 1;
+        param->highDynamicRangeInput = svt_enc->hdr;
+    }
+
+    if (svt_enc->vui_info)
+        param->videoUsabilityInfo = svt_enc->vui_info;
+
+    if (svt_enc->la_depth != -1)
+        param->lookAheadDistance  = svt_enc->la_depth;
+
+    ret = alloc_buffer(param, svt_enc);
+
+    return ret;
+}
+
+static void read_in_data(EB_H265_ENC_CONFIGURATION *config,
+                         const AVFrame *frame,
+                         EB_BUFFERHEADERTYPE *header_ptr)
+{
+    uint8_t is16bit = config->encoderBitDepth > 8;
+    uint64_t luma_size =
+        (uint64_t)config->sourceWidth * config->sourceHeight<< is16bit;
+    EB_H265_ENC_INPUT *in_data = (EB_H265_ENC_INPUT *)header_ptr->pBuffer;
+
+    // support yuv420p and yuv420p010
+    in_data->luma = frame->data[0];
+    in_data->cb   = frame->data[1];
+    in_data->cr   = frame->data[2];
+
+    // stride info
+    in_data->yStride  = frame->linesize[0] >> is16bit;
+    in_data->cbStride = frame->linesize[1] >> is16bit;
+    in_data->crStride = frame->linesize[2] >> is16bit;
+
+    header_ptr->nFilledLen   += luma_size * 3/2u;
+}
+
+static av_cold int eb_enc_init(AVCodecContext *avctx)
+{
+    SvtContext   *svt_enc = avctx->priv_data;
+    EB_ERRORTYPE svt_ret;
+
+    svt_enc->eos_flag = EOS_NOT_REACHED;
+
+    svt_ret = EbInitHandle(&svt_enc->svt_handle, svt_enc, &svt_enc->enc_params);
+    if (svt_ret != EB_ErrorNone) {
+        av_log(avctx, AV_LOG_ERROR, "Error init encoder handle\n");
+        goto failed;
+    }
+
+    svt_ret = config_enc_params(&svt_enc->enc_params, avctx);
+    if (svt_ret != EB_ErrorNone) {
+        av_log(avctx, AV_LOG_ERROR, "Error configure encoder parameters\n");
+        goto failed_init_handle;
+    }
+
+    svt_ret = EbH265EncSetParameter(svt_enc->svt_handle, &svt_enc->enc_params);
+    if (svt_ret != EB_ErrorNone) {
+        av_log(avctx, AV_LOG_ERROR, "Error setting encoder parameters\n");
+        goto failed_init_handle;
+    }
+
+    svt_ret = EbInitEncoder(svt_enc->svt_handle);
+    if (svt_ret != EB_ErrorNone) {
+        av_log(avctx, AV_LOG_ERROR, "Error init encoder\n");
+        goto failed_init_handle;
+    }
+
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+        EB_BUFFERHEADERTYPE *header_ptr = NULL;
+
+        svt_ret = EbH265EncStreamHeader(svt_enc->svt_handle, &header_ptr);
+        if (svt_ret != EB_ErrorNone) {
+            av_log(avctx, AV_LOG_ERROR, "Error when build stream header.\n");
+            goto failed_init_enc;
+        }
+
+        avctx->extradata_size = header_ptr->nFilledLen;
+        avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
+        if (!avctx->extradata) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "Cannot allocate HEVC header of size %d.\n", avctx->extradata_size);
+            svt_ret = EB_ErrorInsufficientResources;
+            goto failed_init_enc;
+        }
+        memcpy(avctx->extradata, header_ptr->pBuffer, avctx->extradata_size);
+    }
+
+    return 0;
+
+failed_init_enc:
+    EbDeinitEncoder(svt_enc->svt_handle);
+failed_init_handle:
+    EbDeinitHandle(svt_enc->svt_handle);
+failed:
+    free_buffer(svt_enc);
+    svt_enc->svt_handle = NULL;
+    svt_enc = NULL;
+    return error_mapping(svt_ret);
+}
+
+static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame)
+{
+    SvtContext           *svt_enc = avctx->priv_data;
+    EB_BUFFERHEADERTYPE  *header_ptr = &svt_enc->in_buf;
+
+    if (!frame) {
+        EB_BUFFERHEADERTYPE header_ptr_last;
+        header_ptr_last.nAllocLen   = 0;
+        header_ptr_last.nFilledLen  = 0;
+        header_ptr_last.nTickCount  = 0;
+        header_ptr_last.pAppPrivate = NULL;
+        header_ptr_last.pBuffer     = NULL;
+        header_ptr_last.nFlags      = EB_BUFFERFLAG_EOS;
+
+        EbH265EncSendPicture(svt_enc->svt_handle, &header_ptr_last);
+        svt_enc->eos_flag = EOS_REACHED;
+        av_log(avctx, AV_LOG_DEBUG, "Finish sending frames!!!\n");
+        return 0;
+    }
+
+    read_in_data(&svt_enc->enc_params, frame, header_ptr);
+
+    header_ptr->nFlags       = 0;
+    header_ptr->pAppPrivate  = NULL;
+    header_ptr->pts          = frame->pts;
+    switch (frame->pict_type) {
+    case AV_PICTURE_TYPE_I:
+        header_ptr->sliceType = svt_enc->forced_idr > 0 ? EB_IDR_PICTURE : EB_I_PICTURE;
+        break;
+    case AV_PICTURE_TYPE_P:
+        header_ptr->sliceType = EB_P_PICTURE;
+        break;
+    case AV_PICTURE_TYPE_B:
+        header_ptr->sliceType = EB_B_PICTURE;
+        break;
+    default:
+        header_ptr->sliceType = EB_INVALID_PICTURE;
+        break;
+    }
+    EbH265EncSendPicture(svt_enc->svt_handle, header_ptr);
+
+    return 0;
+}
+
+static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt)
+{
+    SvtContext  *svt_enc = avctx->priv_data;
+    EB_BUFFERHEADERTYPE   *header_ptr = NULL;
+    EB_ERRORTYPE          svt_ret;
+    int ret = 0;
+
+    if (EOS_TOTRIGGER == svt_enc->eos_flag) {
+        pkt = NULL;
+        return AVERROR_EOF;
+    }
+
+    svt_ret = EbH265GetPacket(svt_enc->svt_handle, &header_ptr, svt_enc->eos_flag);
+    if (svt_ret == EB_NoErrorEmptyQueue)
+        return AVERROR(EAGAIN);
+
+    if ((ret = ff_alloc_packet2(avctx, pkt, header_ptr->nFilledLen, 0)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");
+        EbH265ReleaseOutBuffer(&header_ptr);
+        return ret;
+    }
+
+    memcpy(pkt->data, header_ptr->pBuffer, header_ptr->nFilledLen);
+
+    pkt->size = header_ptr->nFilledLen;
+    pkt->pts  = header_ptr->pts;
+    pkt->dts  = header_ptr->dts;
+    if ((header_ptr->sliceType == EB_IDR_PICTURE) ||
+        (header_ptr->sliceType == EB_I_PICTURE))
+        pkt->flags |= AV_PKT_FLAG_KEY;
+    if (header_ptr->sliceType == EB_NON_REF_PICTURE)
+        pkt->flags |= AV_PKT_FLAG_DISPOSABLE;
+
+    EbH265ReleaseOutBuffer(&header_ptr);
+
+    if (EB_BUFFERFLAG_EOS == header_ptr->nFlags)
+        svt_enc->eos_flag = EOS_TOTRIGGER;
+
+    return 0;
+}
+
+static av_cold int eb_enc_close(AVCodecContext *avctx)
+{
+    SvtContext *svt_enc = avctx->priv_data;
+
+    if (svt_enc) {
+        if (svt_enc->svt_handle) {
+            EbDeinitEncoder(svt_enc->svt_handle);
+            EbDeinitHandle(svt_enc->svt_handle);
+            svt_enc->svt_handle = NULL;
+        }
+
+        free_buffer(svt_enc);
+        svt_enc = NULL;
+    }
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(SvtContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+    { "vui", "Enable vui info", OFFSET(vui_info),
+      AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE },
+
+    { "aud", "Include AUD", OFFSET(aud),
+      AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
+
+    { "hielevel", "Hierarchical prediction levels setting", OFFSET(hierarchical_level),
+      AV_OPT_TYPE_INT, { .i64 = 4 }, 1, 4, VE , "hielevel"},
+        { "flat",   NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 },  INT_MIN, INT_MAX, VE, "hielevel" },
+        { "2level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 },  INT_MIN, INT_MAX, VE, "hielevel" },
+        { "3level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 },  INT_MIN, INT_MAX, VE, "hielevel" },
+        { "4level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4 },  INT_MIN, INT_MAX, VE, "hielevel" },
+
+    { "la_depth", "Look ahead distance [0, 256]", OFFSET(la_depth),
+      AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 256, VE },
+
+    { "preset", "Encoding preset [0, 12] (e,g, for subjective quality tuning mode and >=4k resolution), [0, 10] (for >= 1080p resolution), [0, 9] (for all resolution and modes)",
+      OFFSET(enc_mode), AV_OPT_TYPE_INT, { .i64 = 9 }, 0, 12, VE },
+
+    { "profile", "Profile setting, Main Still Picture Profile not supported", OFFSET(profile),
+      AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_HEVC_MAIN_10 }, FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10, VE, "profile"},
+
+    { "tier", "Set tier (general_tier_flag)", OFFSET(tier),
+      AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, "tier" },
+        { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "tier" },
+        { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, "tier" },
+
+    { "level", "Set level (level_idc)", OFFSET(level),
+      AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 0xff, VE, "level" },
+
+    { "rc", "Bit rate control mode", OFFSET(rc_mode),
+      AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE , "rc"},
+        { "cqp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 },  INT_MIN, INT_MAX, VE, "rc" },
+        { "vbr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 },  INT_MIN, INT_MAX, VE, "rc" },
+
+    { "qp", "QP value for intra frames", OFFSET(qp),
+      AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 51, VE },
+
+    { "sc_detection", "Scene change detection", OFFSET(scd),
+      AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE },
+
+    { "tune", "Quality tuning mode", OFFSET(tune), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 2, VE, "tune" },
+        { "sq", "Visually optimized mode", 0,
+          AV_OPT_TYPE_CONST, { .i64 = 0 },  INT_MIN, INT_MAX, VE, "tune" },
+        { "oq",  "PSNR / SSIM optimized mode",  0,
+          AV_OPT_TYPE_CONST, { .i64 = 1 },  INT_MIN, INT_MAX, VE, "tune" },
+        { "vmaf", "VMAF optimized mode", 0,
+          AV_OPT_TYPE_CONST, { .i64 = 2 },  INT_MIN, INT_MAX, VE, "tune" },
+
+    { "bl_mode", "Random Access Prediction Structure type setting", OFFSET(base_layer_switch_mode),
+      AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
+
+    { "forced-idr", "If forcing keyframes, force them as IDR frames.", OFFSET(forced_idr),
+      AV_OPT_TYPE_BOOL,   { .i64 = 0 }, -1, 1, VE },
+
+    { "hdr", "High dynamic range input", OFFSET(hdr),
+      AV_OPT_TYPE_BOOL,   { .i64 = 0 }, 0, 1, VE },
+
+    {NULL},
+};
+
+static const AVClass class = {
+    .class_name = "libsvt_hevc",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVCodecDefault eb_enc_defaults[] = {
+    { "b",         "7M"    },
+    { "flags",     "+cgop" },
+    { "qmin",      "10"    },
+    { "qmax",      "48"    },
+    { "g",         "-2"    },
+    { NULL },
+};
+
+AVCodec ff_libsvt_hevc_encoder = {
+    .name           = "libsvt_hevc",
+    .long_name      = NULL_IF_CONFIG_SMALL("SVT-HEVC(Scalable Video Technology for HEVC) encoder"),
+    .priv_data_size = sizeof(SvtContext),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_HEVC,
+    .init           = eb_enc_init,
+    .send_frame     = eb_send_frame,
+    .receive_packet = eb_receive_packet,
+    .close          = eb_enc_close,
+    .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS,
+    .pix_fmts       = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P,
+                                                    AV_PIX_FMT_YUV420P10,
+                                                    AV_PIX_FMT_NONE },
+    .priv_class     = &class,
+    .defaults       = eb_enc_defaults,
+    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
+    .wrapper_name   = "libsvt_hevc",
+};