diff mbox

[FFmpeg-devel] lavc: Add libsvt-av1 encoder wrapper

Message ID 1562927539-18233-1-git-send-email-zhong.li@intel.com
State New
Headers show

Commit Message

Zhong Li July 12, 2019, 10:32 a.m. UTC
From: Daryl Seah <daryl.seah@intel.com>

Signed-off-by: Daryl Seah
Signed-off-by: Jing SUN <jing.a.sun@intel.com>
Signed-off-by: ZhiZhen Tang <zhizhen.tang@intel.com>
Signed-off-by: Zhong Li <zhong.li@intel.com>
---
It is part of requirement from ticket #7914.
Since SVT-AV1 library is not stable, this patch is not ready to be merged but just call for comments of this wrapper and SVT library.
Fixes and improvements based on https://github.com/OpenVisualCloud/SVT-AV1/blob/master/ffmpeg_plugin with changes:
* Add 10bits support
* Add high/professional profiles setting
* Add 422 and 444 chroma support functions (though SVT library can't support well)
* Add more detailed error message report
* Expose threads setting since AV_CODEC_CAP_AUTO_THREADS declared
* Remove deinitialized funtion in eb_enc_init() since FF_CODEC_CAP_INIT_CLEANUP declared
* Add ff_side_data_set_encoder_stats() to report quality and piture type
* Add FFmpeg documentation.
* Rename to libsvtav1enc.c from libsvt_av1.c since SVT-AV1 can also support decoding.
* Other misc changes:
    * Use function name with prefix "svt_" instead of "eb_"
    * call ff_alloc_packet2() after eb_svt_get_packet() to avoid "header_ptr->n_filled_len" uninitialized
    * Remove aud option since it isn't used
    * Fix some error values, such as max look_ahead depth, vbr value, max preset value
    * address some other comments from libsvt-hevc patch review
Changelog and version bump is missed to aviod rebase confict in the future when patch update

 configure                 |   4 +
 doc/encoders.texi         | 139 ++++++++++++++
 doc/general.texi          |   8 +
 libavcodec/Makefile       |   1 +
 libavcodec/allcodecs.c    |   1 +
 libavcodec/libsvtav1enc.c | 453 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 606 insertions(+)
 create mode 100644 libavcodec/libsvtav1enc.c

Comments

Jing SUN July 12, 2019, 2:15 p.m. UTC | #1
-----Original Message-----
From: Li, Zhong 
Sent: Friday, July 12, 2019 6:32 PM
To: ffmpeg-devel@ffmpeg.org
Cc: Daryl Seah <daryl.seah@intel.com>; Li, Zhong <zhong.li@intel.com>; Daryl; Seah; Sun, Jing A <jing.a.sun@intel.com>; Tang, Zhizhen <zhizhen.tang@intel.com>
Subject: [PATCH] lavc: Add libsvt-av1 encoder wrapper

From: Daryl Seah <daryl.seah@intel.com>

Signed-off-by: Daryl Seah
Signed-off-by: Jing SUN <jing.a.sun@intel.com>
Signed-off-by: ZhiZhen Tang <zhizhen.tang@intel.com>
Signed-off-by: Zhong Li <zhong.li@intel.com>
---
It is part of requirement from ticket #7914.

...

Hi Zhong,

I have not submitted such FFmpeg SVT-AV1 plugin patch to the community, like the similar one for SVT-HEVC, not just because SVT-AV1 is under development and not officially released, but also because the patch is NOT fully functional and many severe blocking issues exist, such as this https://github.com/OpenVisualCloud/SVT-AV1/issues/268  (~50% frames coded by SVT-AV1 fail to get decoded for the pkts generated by it don't fully compliant with AV1 spec and have confliction with the av1_metadata_bsf filter).

And in the SVT-AV1 GitHub, it's an open message that it is not ready: https://github.com/OpenVisualCloud/SVT-AV1/issues/259.

You (or anyone) are welcome to contribute to the SVT-AV1 project, and submit the FFmpeg plugin changes you'd like to make here: https://github.com/OpenVisualCloud/SVT-AV1/tree/master/ffmpeg_plugin, and we will take your commits seriously. But I have not got any code changes made by you in our project GitHub and it seems you haven't made any contribution to the project yet.

To be honest, I felt more than embarrassed that this internal work in progress of ours, which is only half-done, got exported to the real world, which is to get read and judged by all the experts on FFmpeg community.

Please do ignore this review request. It's NOT at least basically working yet. I am going to submit one when it is.

-- Jing
Zhong Li July 12, 2019, 5:23 p.m. UTC | #2
> You (or anyone) are welcome to contribute to the SVT-AV1 project, and
> submit the FFmpeg plugin changes you'd like to make here:
> https://github.com/OpenVisualCloud/SVT-AV1/tree/master/ffmpeg_plugin,
> and we will take your commits seriously. But I have not got any code
> changes made by you in our project GitHub and it seems you haven't made
> any contribution to the project yet.

I prefer to contribute on FFmpeg mainline instead of a forked branch. However, once get aligned in the community, of course I can contribute to the branch as well.
(Many SVT core library and API patches hold on my hand will also be sent out after my leave of next 2~3 weeks)

And let me provide more detail about the purposes:
1. SVT encoders (01.org/svt ) show very impressive performance benefit and get much attention . 
  Let's collect some feedback as early as possible, this patch is not call for merge bug call for feedback, comments. Not only collect feedback of SVT FFmpeg wrapper, but also feedback of SVT core libraries and APIs.

  e.g : SVT-AV1 reused libaom. If you enable both libsvt-av1 and libaom as static link, then will have name conflict. 
  Such issue is not easily found if you keep wrapper patch on https://github.com/OpenVisualCloud/SVT-AV1/tree/master/ffmpeg_plugin ,
  But will easily found if works on FFmpeg mainline. 

2. Many review comments from community (including mine) haven't been well addressed in SVT-HEVC wrapper patch. 
  This patch is just to address them, probably it can be a reference of SVT-HEVC wrapper too. 

3. As mentioned in the patch annotation, this wrapper patch is a response of ticket #7914, though it is not quite mature.
Jing SUN July 15, 2019, 2:40 a.m. UTC | #3
-----Original Message-----
From: Li, Zhong 
Sent: Saturday, July 13, 2019 1:23 AM
To: Sun, Jing A <jing.a.sun@intel.com>; ffmpeg-devel@ffmpeg.org
Cc: Daryl Seah <daryl.seah@intel.com>; Tang, Zhizhen <zhizhen.tang@intel.com>
Subject: RE: [PATCH] lavc: Add libsvt-av1 encoder wrapper

> I prefer to contribute on FFmpeg mainline instead of a forked branch. However, once get aligned in the community, of course I can contribute to the branch as well.
> (Many SVT core library and API patches hold on my hand will also be sent out after my leave of next 2~3 weeks)
> And let me provide more detail about the purposes:
> 1. SVT encoders (01.org/svt ) show very impressive performance benefit and get much attention . 
  Let's collect some feedback as early as possible, this patch is not call for merge bug call for feedback, comments. Not only collect feedback of SVT FFmpeg wrapper, but also feedback of SVT core libraries and APIs.

SVT AV1 GitHub is not a forked branch, but a project every SVT developer is working together on. When you have any changes you would like to make to its FFmpeg plugin, you'd better submit those to the SVT AV1 Github first, so that the SVT maintainers and other plugin authors could review them before making them public.

>  e.g : SVT-AV1 reused libaom. If you enable both libsvt-av1 and libaom as static link, then will have name conflict. 
>  Such issue is not easily found if you keep wrapper patch on https://github.com/OpenVisualCloud/SVT-AV1/tree/master/ffmpeg_plugin ,
> But will easily found if works on FFmpeg mainline. 

That's a known issue: https://github.com/OpenVisualCloud/SVT-AV1/issues/119, which was submitted by me at Feb. Please look into it.

> 2. Many review comments from community (including mine) haven't been well addressed in SVT-HEVC wrapper patch. 
  This patch is just to address them, probably it can be a reference of SVT-HEVC wrapper too. 

Which one is not addressed?

> 3. As mentioned in the patch annotation, this wrapper patch is a response of ticket #7914, though it is not quite mature.
diff mbox

Patch

diff --git a/configure b/configure
index 5a4f507..09f9ba3 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-libsvtav1enc    enable AV1 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]
@@ -1788,6 +1789,7 @@  EXTERNAL_LIBRARY_LIST="
     libspeex
     libsrt
     libssh
+    libsvtav1enc
     libtensorflow
     libtesseract
     libtheora
@@ -3183,6 +3185,7 @@  libshine_encoder_select="audio_frame_queue"
 libspeex_decoder_deps="libspeex"
 libspeex_encoder_deps="libspeex"
 libspeex_encoder_select="audio_frame_queue"
+libsvt_av1_encoder_select="libsvtav1enc"
 libtheora_encoder_deps="libtheora"
 libtwolame_encoder_deps="libtwolame"
 libvo_amrwbenc_encoder_deps="libvo_amrwbenc"
@@ -6230,6 +6233,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 libsvtav1enc      && require_pkg_config libsvtav1enc SvtAv1Enc EbSvtAv1Enc.h eb_init_handle
 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/doc/encoders.texi b/doc/encoders.texi
index eefd124..dd7ad1c 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -1574,6 +1574,145 @@  by commas (,). See kvazaar documentation for a list of options.
 
 @end table
 
+@section libsvt-av1
+
+Scalable Video Technology for AV1 (SVT-AV1) encoder wrapper.
+
+This encoder requires the presence of the headers and
+library during configuration. You need to explicitly configure the
+build with @code{--enable-libsvtav1enc}. The library is detected using
+@command{pkg-config}.
+
+For more information about the library see
+@url{https://github.com/OpenVisualCloud/SVT-AV1}.
+
+@subsection Options
+
+The following FFmpeg global options affect the configurations of the
+libsvt-av1 encoder:
+
+@table @option
+@item b  (@emph{bitrate})
+Set the bitrate (as a number of bits per second). Default is 7M.
+
+@item g  / @option{gop_size}
+Set the GOP size. Default is -2 (unspecified).
+
+@item qmin (@emph{min-q})
+Default is 0
+
+@item qmax (@emph{max-q})
+Default is 63
+
+Set minimum/maximum quantisation values.  Valid range is from 0 to 63
+(Only used when bit rate control mode @option{rc} is set to 2(vbr) mode.
+It is required that qmax >= qmin).
+
+@item threads
+Set the number of threads to use while encoding. if not set, threads will be
+managed by OS thread scheduler.
+
+@item profile (@emph{profile})
+Set profile restrictions. Can assume one of the following possible values:
+
+@table @samp
+@item main
+main profile
+@item high
+high profile
+@item professional
+professional profile
+@end table
+
+Default is 0 (main), but may be modified as pixel format to follow AV1 specification
+
+@item level (@emph{level})
+
+@option{level} sets the value of @emph{level}.
+Set level (level_idc). Default is 0 (to be determined by libsvt av1 library).
+
+@end table
+
+The encoder also has its own specific options:
+
+@table @option
+@item hielevel
+Set hierarchical levels. Can assume one of the following possible values:
+
+@table @samp
+@item 3 level
+Minigop size is 2^3
+@item 4 level
+Minigop size is 2^4
+@end table
+
+Default is 4 level.
+
+@item la_depth
+Set look-ahead depth, depending on @option{rc}: for @var{vbr}, it's recommended
+to unset it and use the default value (the intra period); for @var{cqp}, better
+specify the look-ahead depth.
+
+The range is @var{-1-120}. Default is -1 (unset and the default value to be used).
+
+@item preset
+Set the quality vs density tradeoff point at which the encoding is to be performed.
+Higher perset value, higher density and lower quality.
+
+The range is @var{0-8}. Default is 8.
+
+@item tier
+Set @emph{general_tier_flag}.  This may affect the level chosen for the stream
+if it is not explicitly specified. Can assume one of the following possible values:
+
+@table @samp
+@item main
+main tier
+@item high
+high tier
+@end table
+
+Default is 0 (main).
+
+@item rc
+Set bit rate control mode. Can assume one of the following possible values:
+
+@table @samp
+@item cqp
+Constant QP (CQP) mode
+@item vbr
+Variable Bit Rate (VBR) mode
+@end table
+
+Default is 2 (vbr).
+
+@item qp
+Initial quantization parameter for the intra pictures used when
+@option{rc} is cqp mode. The range is from @var{0-63}. Default is 50.
+
+@item sc_detection
+Enables or disables the scene change detection algorithm. Default is 0 (disabled).
+
+@item bl_mode
+Enables or disables Random Access Prediction. Default is 0 (disabled).
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Set VBR mode with 5Mbps with @command{ffmpeg}:
+@example
+ffmpeg -i INPUT -codec:v libsvt-av1 -rc vbr -b:v 5M OUTPUT.mp4
+@end example
+
+@item
+Set CQP mode with qp equal to 30 with @command{ffmpeg}:
+@example
+ffmpeg -i INPUT -codec:v libsvt-av1 -rc cqp -qp 30 OUTPUT.mp4
+@end example
+@end itemize
+
 @section libopenh264
 
 Cisco libopenh264 H.264/MPEG-4 AVC encoder wrapper.
diff --git a/doc/general.texi b/doc/general.texi
index 3c0c803..9f19950 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -25,6 +25,14 @@  Go to @url{http://aomedia.org/} and follow the instructions for
 installing the library. Then pass @code{--enable-libaom} to configure to
 enable it.
 
+@section  Scalable Video Technology for AV1 encoding
+
+FFmpeg can make use of the SVT-AV1 library for AV1 encoding.
+
+Go to @url{https://github.com/OpenVisualCloud/SVT-AV1/} and follow the instructions
+for installing the library. Then pass @code{--enable-libsvtav1enc} to configure to
+enable it.
+
 @section AMD AMF/VCE
 
 FFmpeg can use the AMD Advanced Media Framework library under Windows
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3cd73fb..b5f6637 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -991,6 +991,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_AV1_ENCODER)         += libsvtav1enc.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 d2f9a39..75759c7 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -707,6 +707,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_av1_encoder;
 extern AVCodec ff_libtheora_encoder;
 extern AVCodec ff_libtwolame_encoder;
 extern AVCodec ff_libvo_amrwbenc_encoder;
diff --git a/libavcodec/libsvtav1enc.c b/libavcodec/libsvtav1enc.c
new file mode 100644
index 0000000..bb96d07
--- /dev/null
+++ b/libavcodec/libsvtav1enc.c
@@ -0,0 +1,453 @@ 
+/*
+* Scalable Video Technology for AV1 encoder library plugin
+*
+* Copyright (c) 2019 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 <stdint.h>
+#include "EbSvtAv1ErrorCodes.h"
+#include "EbSvtAv1Enc.h"
+
+#include "libavutil/common.h"
+#include "libavutil/frame.h"
+#include "libavutil/opt.h"
+#include "libavutil/avassert.h"
+#include "libavutil/pixdesc.h"
+
+#include "internal.h"
+#include "avcodec.h"
+
+typedef struct SvtContext {
+    AVClass     *class;
+
+    EbSvtAv1EncConfiguration    enc_params;
+    EbComponentType            *svt_handle;
+    EbBufferHeaderType         *in_buf;
+    int         eos_flag;
+
+    // User options.
+    int hierarchical_level;
+    int la_depth;
+    int enc_mode;
+    int rc_mode;
+    int scd;
+    int qp;
+    int tier;
+    int level;
+    int profile;
+    int base_layer_switch_mode;
+} SvtContext;
+
+static const struct {
+    EbErrorType    eb_err;
+    int            av_err;
+    const char     *desc;
+} svt_errors[] = {
+    { EB_ErrorNone,                             0,              "success"                   },
+    { EB_ErrorInsufficientResources,      AVERROR(ENOMEM),      "insufficient resources"    },
+    { EB_ErrorUndefined,                  AVERROR(EINVAL),      "undefined error"           },
+    { EB_ErrorInvalidComponent,           AVERROR(EINVAL),      "invalid component"         },
+    { EB_ErrorBadParameter,               AVERROR(EINVAL),      "bad parameter"             },
+    { EB_ErrorDestroyThreadFailed,        AVERROR_EXTERNAL,     "failed to destory thread"  },
+    { EB_ErrorSemaphoreUnresponsive,      AVERROR_EXTERNAL,     "semaphore unresponsive"    },
+    { EB_ErrorDestroySemaphoreFailed,     AVERROR_EXTERNAL,     "semaphore unresponsive"    },
+    { EB_ErrorCreateMutexFailed,          AVERROR_EXTERNAL,     "failed to creat mutex"     },
+    { EB_ErrorMutexUnresponsive,          AVERROR_EXTERNAL,     "mutex unresponsive"        },
+    { EB_ErrorDestroyMutexFailed,         AVERROR_EXTERNAL,     "failed to destory muxtex"  },
+    { EB_NoErrorEmptyQueue,               AVERROR(EAGAIN),      "empty queue"               },
+};
+
+static int svt_map_error(EbErrorType eb_err, const char **desc)
+{
+    int i;
+    av_assert0(desc);
+    for (i = 0; i < FF_ARRAY_ELEMS(svt_errors); i++) {
+        if (svt_errors[i].eb_err == eb_err) {
+            *desc = svt_errors[i].desc;
+            return svt_errors[i].av_err;
+        }
+    }
+    *desc = "unknown error";
+    return AVERROR_UNKNOWN;
+}
+
+static int svt_print_error(void *log_ctx, EbErrorType err,
+                       const char *error_string)
+{
+    const char *desc;
+    int ret;
+    ret = svt_map_error(err, &desc);
+    av_log(log_ctx, AV_LOG_ERROR, "%s: %s (%d)\n", error_string, desc, err);
+    return ret;
+}
+
+static void free_buffer(SvtContext *svt_enc)
+{
+    if (svt_enc->in_buf) {
+        uint8_t *in_data = svt_enc->in_buf->p_buffer;
+        av_freep(&in_data);
+        av_freep(&svt_enc->in_buf);
+    }
+}
+
+static int alloc_buffer(SvtContext *svt_enc)
+{
+    EbSvtIOFormat *in_data;
+
+    // allocate buffer for in and out
+    svt_enc->in_buf           = av_mallocz(sizeof(*svt_enc->in_buf));
+    if (!svt_enc->in_buf)
+        return AVERROR(ENOMEM);
+
+    in_data  = av_mallocz(sizeof(*in_data));
+    if (!in_data)
+        return AVERROR(ENOMEM);
+    svt_enc->in_buf->p_buffer  = (unsigned char *)in_data;
+    svt_enc->in_buf->size      = sizeof(*svt_enc->in_buf);
+    svt_enc->in_buf->p_app_private  = NULL;
+
+    return 0;
+}
+
+static int config_enc_params(EbSvtAv1EncConfiguration *param,
+                             AVCodecContext *avctx)
+{
+    SvtContext *svt_enc = avctx->priv_data;
+    const AVPixFmtDescriptor *desc;
+
+    param->source_width      = avctx->width;
+    param->source_height     = avctx->height;
+
+    desc = av_pix_fmt_desc_get(avctx->pix_fmt);
+    param->encoder_bit_depth = desc->comp[0].depth;
+    av_log(avctx, AV_LOG_DEBUG , "Encoder %d bits depth input\n", param->encoder_bit_depth);
+
+    if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1)
+        param->encoder_color_format   = EB_YUV420;
+    else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0)
+        param->encoder_color_format   = EB_YUV422;
+    else if (!desc->log2_chroma_w && !desc->log2_chroma_h)
+        param->encoder_color_format   = EB_YUV444;
+    else {
+        av_log(avctx, AV_LOG_ERROR , "Unsupported pixel format\n");
+        return AVERROR(EINVAL);
+    }
+    av_log(avctx, AV_LOG_DEBUG , "Encoder color format is %d \n", param->encoder_color_format);
+
+    if (param->encoder_color_format == EB_YUV422 || param->encoder_bit_depth > 10) {
+        av_log(avctx, AV_LOG_WARNING, "Force to be professional profile \n");
+        param->profile = PROFESSIONAL_PROFILE;
+    } else if (param->encoder_color_format == EB_YUV444) {
+        av_log(avctx, AV_LOG_WARNING, "Force to be high profile \n");
+        param->profile = HIGH_PROFILE;
+    }
+
+    // Update param from options
+    param->hierarchical_levels      = svt_enc->hierarchical_level;
+    param->enc_mode                 = svt_enc->enc_mode;
+    param->tier                     = svt_enc->tier;
+    param->level                    = svt_enc->level;
+    param->rate_control_mode        = svt_enc->rc_mode;
+    param->scene_change_detection   = svt_enc->scd;
+    param->base_layer_switch_mode   = svt_enc->base_layer_switch_mode;
+    param->qp                       = svt_enc->qp;
+
+    param->target_bit_rate          = avctx->bit_rate;
+    if (avctx->gop_size > 0)
+        param->intra_period_length  = avctx->gop_size - 1;
+
+    if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
+        param->frame_rate_numerator     = avctx->framerate.num;
+        param->frame_rate_denominator   = avctx->framerate.den * avctx->ticks_per_frame;
+    } else {
+        param->frame_rate_numerator     = avctx->time_base.den;
+        param->frame_rate_denominator   = avctx->time_base.num * avctx->ticks_per_frame;
+    }
+
+    if (param->rate_control_mode) {
+        param->max_qp_allowed       = avctx->qmax;
+        param->min_qp_allowed       = avctx->qmin;
+    }
+
+    param->intra_refresh_type       = !!(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP) + 1;
+
+    if (svt_enc->la_depth != -1)
+        param->look_ahead_distance  = svt_enc->la_depth;
+
+    param->logical_processors       = avctx->thread_count;
+
+    return 0;
+}
+
+static void read_in_data(const AVFrame *frame,
+                         EbBufferHeaderType *header_ptr)
+{
+    EbSvtIOFormat *in_data = (EbSvtIOFormat *)header_ptr->p_buffer;
+    const AVPixFmtDescriptor *desc;
+    int i, bytes_shift, plane_h;
+
+    desc = av_pix_fmt_desc_get(frame->format);
+    bytes_shift = desc->comp[0].depth > 8 ? 1 : 0;
+
+    in_data->luma = frame->data[0];
+    in_data->cb   = frame->data[1];
+    in_data->cr   = frame->data[2];
+
+    in_data->y_stride  = AV_CEIL_RSHIFT(frame->linesize[0], bytes_shift);
+    in_data->cb_stride = AV_CEIL_RSHIFT(frame->linesize[1], bytes_shift);
+    in_data->cr_stride = AV_CEIL_RSHIFT(frame->linesize[2], bytes_shift);
+
+    for (i = 0; i < desc->nb_components; i++) {
+        plane_h = frame->height;
+        if (i > 0)
+            plane_h = AV_CEIL_RSHIFT(plane_h, desc->log2_chroma_h);
+        header_ptr->n_filled_len += frame->linesize[i] * plane_h;
+    }
+}
+
+static av_cold int svt_enc_init(AVCodecContext *avctx)
+{
+    SvtContext   *svt_enc = avctx->priv_data;
+    EbErrorType svt_ret;
+
+    svt_enc->eos_flag = 0;
+
+    svt_ret = eb_init_handle(&svt_enc->svt_handle, svt_enc, &svt_enc->enc_params);
+    if (svt_ret != EB_ErrorNone) {
+        return svt_print_error(avctx, svt_ret, "Error init encoder handle");
+    }
+
+    svt_ret = config_enc_params(&svt_enc->enc_params, avctx);
+    if (svt_ret < 0) {
+        return svt_ret;
+    }
+
+    svt_ret = eb_svt_enc_set_parameter(svt_enc->svt_handle, &svt_enc->enc_params);
+    if (svt_ret != EB_ErrorNone) {
+        return svt_print_error(avctx, svt_ret, "Error setting encoder parameters");
+    }
+
+    svt_ret = eb_init_encoder(svt_enc->svt_handle);
+    if (svt_ret != EB_ErrorNone) {
+        return svt_print_error(avctx, svt_ret, "Error init encoder");
+    }
+
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+        EbBufferHeaderType *header_ptr = NULL;
+
+        svt_ret = eb_svt_enc_stream_header(svt_enc->svt_handle, &header_ptr);
+        if (svt_ret != EB_ErrorNone) {
+            return svt_print_error(avctx, svt_ret, "Error when build stream header");
+        }
+
+        avctx->extradata_size = header_ptr->n_filled_len;
+        avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
+        if (!avctx->extradata) {
+            return AVERROR(ENOMEM);
+        }
+
+        memcpy(avctx->extradata, header_ptr->p_buffer, avctx->extradata_size);
+
+        svt_ret = eb_svt_release_enc_stream_header(header_ptr);
+        if (svt_ret != EB_ErrorNone) {
+            return svt_print_error(avctx, svt_ret, "Error when destroy stream header");
+        }
+    }
+
+    svt_ret = alloc_buffer(svt_enc);
+    if (svt_ret < 0)
+        return svt_ret;
+
+    return 0;
+}
+
+static int svt_send_frame(AVCodecContext *avctx, const AVFrame *frame)
+{
+    SvtContext          *svt_enc = avctx->priv_data;
+    EbBufferHeaderType  *header_ptr = svt_enc->in_buf;
+
+    if (!frame) {
+        EbBufferHeaderType header_ptrLast;
+        header_ptrLast.n_alloc_len   = 0;
+        header_ptrLast.n_filled_len  = 0;
+        header_ptrLast.n_tick_count  = 0;
+        header_ptrLast.p_app_private = NULL;
+        header_ptrLast.p_buffer      = NULL;
+        header_ptrLast.flags         = EB_BUFFERFLAG_EOS;
+
+        eb_svt_enc_send_picture(svt_enc->svt_handle, &header_ptrLast);
+        svt_enc->eos_flag = 1;
+        av_log(avctx, AV_LOG_DEBUG, "Finish sending frames!!!\n");
+        return 0;
+    }
+
+    read_in_data(frame, header_ptr);
+
+    header_ptr->flags        = 0;
+    header_ptr->p_app_private  = NULL;
+    header_ptr->pts          = frame->pts;
+
+    eb_svt_enc_send_picture(svt_enc->svt_handle, header_ptr);
+
+    return 0;
+}
+
+static int svt_receive_packet(AVCodecContext *avctx, AVPacket *pkt)
+{
+    SvtContext  *svt_enc = avctx->priv_data;
+    EbBufferHeaderType   *header_ptr;
+    EbErrorType          svt_ret;
+    int ret;
+
+    svt_ret = eb_svt_get_packet(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->n_filled_len, 0)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");
+        return ret;
+    }
+
+    memcpy(pkt->data, header_ptr->p_buffer, header_ptr->n_filled_len);
+    pkt->size = header_ptr->n_filled_len;
+    pkt->pts  = header_ptr->pts;
+    pkt->dts  = header_ptr->dts;
+    if (header_ptr->pic_type == EB_AV1_KEY_PICTURE)
+        pkt->flags |= AV_PKT_FLAG_KEY;
+
+    ff_side_data_set_encoder_stats(pkt,  header_ptr->qp * FF_QP2LAMBDA, NULL, 0, header_ptr->pic_type);
+
+    ret = (header_ptr->flags & EB_BUFFERFLAG_EOS) ? AVERROR_EOF : 0;
+
+    eb_svt_release_out_buffer(&header_ptr);
+
+    return ret;
+}
+
+static av_cold int svt_enc_close(AVCodecContext *avctx)
+{
+    SvtContext *svt_enc = avctx->priv_data;
+
+    if (svt_enc) {
+        if (svt_enc->svt_handle) {
+            eb_deinit_encoder(svt_enc->svt_handle);
+            eb_deinit_handle(svt_enc->svt_handle);
+        }
+
+        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[] = {
+    { "hielevel", "Hierarchical prediction levels setting", OFFSET(hierarchical_level),
+      AV_OPT_TYPE_INT, { .i64 = 4 }, 3, 4, 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, 120]", OFFSET(la_depth),
+      AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 120, VE },
+
+    { "preset", "Encoding preset [0, 8]",
+      OFFSET(enc_mode), AV_OPT_TYPE_INT, { .i64 = MAX_ENC_PRESET }, 0, MAX_ENC_PRESET, VE },
+
+    { "profile", "Set profile restrictions", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = MAIN_PROFILE}, MAIN_PROFILE, PROFESSIONAL_PROFILE, VE, "profile" },
+    { "main" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MAIN_PROFILE}, INT_MIN, INT_MAX,     VE, "profile" },
+    { "high" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HIGH_PROFILE}, INT_MIN, INT_MAX,     VE, "profile" },
+    { "professional", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PROFESSIONAL_PROFILE }, INT_MIN, INT_MAX,     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" },
+
+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
+    { .i64 = value }, 0, 0, VE, "level"
+    { LEVEL("1",   10) },
+    { LEVEL("2",   20) },
+    { LEVEL("2.1", 21) },
+    { LEVEL("3",   30) },
+    { LEVEL("3.1", 31) },
+    { LEVEL("4",   40) },
+    { LEVEL("4.1", 41) },
+    { LEVEL("5",   50) },
+    { LEVEL("5.1", 51) },
+    { LEVEL("5.2", 52) },
+    { LEVEL("6",   60) },
+    { LEVEL("6.1", 61) },
+    { LEVEL("6.2", 62) },
+#undef LEVEL
+
+    { "rc", "Bit rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, 3, 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 = 2 },  INT_MIN, INT_MAX, VE, "rc" },
+
+    { "qp", "QP value for intra frames", OFFSET(qp),
+      AV_OPT_TYPE_INT, { .i64 = 50 }, 0, 63, VE },
+
+    { "sc_detection", "Scene change detection", OFFSET(scd),
+      AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
+
+    { "bl_mode", "Random Access Prediction Structure type setting", OFFSET(base_layer_switch_mode),
+      AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
+
+    {NULL},
+};
+
+static const AVClass class = {
+    .class_name = "libsvt-av1 encoder",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVCodecDefault eb_enc_defaults[] = {
+    { "b",         "7M"    },
+    { "g",         "-2"    },
+    { "qmin",      "0"     },
+    { "qmax",      "63"    },
+    { NULL },
+};
+
+AVCodec ff_libsvt_av1_encoder = {
+    .name           = "libsvt-av1",
+    .long_name      = NULL_IF_CONFIG_SMALL("SVT-AV1(Scalable Video Technology for AV1)"),
+    .priv_data_size = sizeof(SvtContext),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_AV1,
+    .init           = svt_enc_init,
+    .send_frame     = svt_send_frame,
+    .receive_packet = svt_receive_packet,
+    .close          = svt_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-av1",
+};