diff mbox

[FFmpeg-devel,v6,1/2] lavc, doc, configure: add libxavs2 video encoder wrapper

Message ID 1536154712-22593-1-git-send-email-hwrenx@126.com
State Superseded
Headers show

Commit Message

hwren Sept. 5, 2018, 1:38 p.m. UTC
Signed-off-by: hwren <hwrenx@126.com>
---
 Changelog              |   1 +
 configure              |   4 +
 doc/encoders.texi      |  34 ++++++
 doc/general.texi       |  14 +++
 libavcodec/Makefile    |   1 +
 libavcodec/allcodecs.c |   1 +
 libavcodec/libxavs2.c  | 290 +++++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h   |   4 +-
 8 files changed, 347 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/libxavs2.c

Comments

Mark Thompson Sept. 6, 2018, 12:43 a.m. UTC | #1
On 05/09/18 14:38, hwren wrote:
> Signed-off-by: hwren <hwrenx@126.com>
> ---
>  Changelog              |   1 +
>  configure              |   4 +
>  doc/encoders.texi      |  34 ++++++
>  doc/general.texi       |  14 +++
>  libavcodec/Makefile    |   1 +
>  libavcodec/allcodecs.c |   1 +
>  libavcodec/libxavs2.c  | 290 +++++++++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/version.h   |   4 +-
>  8 files changed, 347 insertions(+), 2 deletions(-)
>  create mode 100644 libavcodec/libxavs2.c
> 
> diff --git a/Changelog b/Changelog
> index 0975fee..8377956 100644
> ...
> diff --git a/doc/encoders.texi b/doc/encoders.texi
> index 7b09575..6998493 100644
> --- a/doc/encoders.texi
> +++ b/doc/encoders.texi
> @@ -2726,6 +2726,40 @@ Reduces detail but attempts to preserve color at extremely low bitrates.
>  
>  @end table
>  
> +@section libxavs2
> +
> +xavs2 AVS2-P2/IEEE1857.4 encoder wrapper.
> +
> +This encoder requires the presence of the libxavs2 headers and library
> +during configuration. You need to explicitly configure the build with
> +@option{--enable-libxavs2}.
> +
> +@subsection Options
> +
> +@table @option
> +@item i_lcurow_threads
> +Set the number of parallel threads for rows from 1 to 8 (default 5).
> +
> +@item i_initial_qp
> +Set the xavs2 quantization parameter from 1 to 63 (default 34).
> +
> +@item speed_level
> +Set the Speed level from 0 to 9 (default 0).

I think mention that larger numbers mean slower / higher quality here as well.

> +
> +@item hierarchical_ref
> +Set the hierarchical reference or not (default true).
> +
> +@item xavs2-params
> +Set xavs2 options using a list of @var{key}=@var{value} couples separated
> +by ":".
> +
> +For example to specify libxavs2 encoding options with @option{-xavs2-params}:
> +
> +@example
> +ffmpeg -i input -c:v libxavs2 -xavs2-params preset_level=5 output.avs2
> +@end example
> +@end table
> +
>  @c man end VIDEO ENCODERS
>  
>  @chapter Subtitles Encoders
> ...
> diff --git a/libavcodec/libxavs2.c b/libavcodec/libxavs2.c
> new file mode 100644
> index 0000000..ce92efb
> --- /dev/null
> +++ b/libavcodec/libxavs2.c
> @@ -0,0 +1,290 @@
> +/*
> + * AVS2 encoding using the xavs2 library
> + *
> + * Copyright (C) 2018 Yiqun Xu,   <yiqun.xu@vipl.ict.ac.cn>
> + *                    Falei Luo,  <falei.luo@gmail.com>
> + *                    Huiwen Ren, <hwrenx@gmail.com>
> + *
> + * 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 <ctype.h>
> +
> +#include "xavs2.h"
> +#include "avcodec.h"
> +#include "mpeg12.h"
> +#include "internal.h"
> +#include "libavutil/internal.h"
> +#include "libavutil/mem.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/common.h"
> +#include "libavutil/avutil.h"
> +
> +#define xavs2_opt_set2(name, format, ...) do{ \
> +    char opt_str[16] = {0}; \
> +    av_strlcatf(opt_str, sizeof(opt_str), format, __VA_ARGS__); \
> +    cae->api->opt_set2(cae->param, name, opt_str); \
> +} while(0);
> +
> +typedef struct XAVS2EContext {
> +    AVClass *class;
> +
> +    int i_lcurow_threads;
> +    int i_initial_qp;
> +    int preset_level;
> +    int intra_period;

This field isn't used any more.

> +
> +    void *encoder;
> +    char *xavs2_opts;
> +
> +    int b_hierarchical_reference;
> +
> +    xavs2_outpacket_t packet;
> +    xavs2_param_t *param;
> +
> +    const xavs2_api_t *api;
> +
> +} XAVS2EContext;
> +
> +static av_cold int xavs2_init(AVCodecContext *avctx)
> +{
> +    XAVS2EContext *cae= avctx->priv_data;
> +    int bit_depth, code;
> +
> +    bit_depth = avctx->pix_fmt == AV_PIX_FMT_YUV420P ? 8 : 10;
> +
> +    /* get API handler */
> +    cae->api = xavs2_api_get(bit_depth);
> +
> +    if (!cae->api) {
> +        av_log(avctx, AV_LOG_ERROR, "api get failed\n");
> +        return AVERROR(EINVAL);

AVERROR_EXTERNAL?  I don't think this indicates that the user did something invalid.

> +    }
> +
> +    cae->param = cae->api->opt_alloc();
> +
> +    if (!cae->param) {
> +        av_log(avctx, AV_LOG_ERROR, "param alloc failed\n");
> +        return AVERROR(EINVAL);

Maybe AVERROR(ENOMEM)?

> +    }
> +
> +    xavs2_opt_set2("rec",   "%d", 0);
> +    xavs2_opt_set2("log",   "%d", 2);
> +
> +    xavs2_opt_set2("width",     "%d", avctx->width);
> +    xavs2_opt_set2("height",    "%d", avctx->height);
> +    xavs2_opt_set2("bframes",   "%d", avctx->max_b_frames);
> +    xavs2_opt_set2("bitdepth",  "%d", bit_depth);
> +    xavs2_opt_set2("preset",    "%d", cae->preset_level);
> +
> +    xavs2_opt_set2("thread_frames",     "%d", avctx->thread_count);
> +    xavs2_opt_set2("intraperiod",       "%d", avctx->gop_size);
> +    xavs2_opt_set2("thread_rows",       "%d", cae->i_lcurow_threads);
> +    xavs2_opt_set2("initial_qp",        "%d", cae->i_initial_qp);
> +    xavs2_opt_set2("hierarchical_ref",  "%d", cae->b_hierarchical_reference);

This code looks much nicer, thank you!

> +
> +    if (cae->xavs2_opts) {
> +        AVDictionary *dict    = NULL;
> +        AVDictionaryEntry *en = NULL;
> +
> +        if (!av_dict_parse_string(&dict, cae->xavs2_opts, "=", ":", 0)) {
> +            while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) {
> +                xavs2_opt_set2(en->key, "%s", en->value);

Should you check the result of this one?  The user might pass soemthing completely invalid, which probably wants a warning at least.

> +            }
> +            av_dict_free(&dict);
> +        }
> +    }
> +
> +    /* Rate control */
> +    if (avctx->bit_rate > 0) {
> +        xavs2_opt_set2("RateControl",   "%d", 1);
> +        xavs2_opt_set2("TargetBitRate", "%d", avctx->bit_rate);
> +    }
> +
> +
> +    ff_mpeg12_find_best_frame_rate(avctx->framerate, &code, NULL, NULL, 0);
> +
> +    xavs2_opt_set2("FrameRate",   "%d", code);
> +
> +    cae->encoder = cae->api->encoder_create(cae->param);
> +
> +    if (!cae->encoder) {
> +        av_log(avctx,AV_LOG_ERROR, "Can not create encoder. Null pointer returned\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    return 0;
> +}
> +
> ...
> +
> +static int xavs2_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
> +                      const AVFrame *frame, int *got_packet)
> +{
> +    XAVS2EContext *cae = avctx->priv_data;
> +    xavs2_picture_t pic;
> +    int ret;
> +
> +    /* create the XAVS2 video encoder */
> +    /* read frame data and send to the XAVS2 video encoder */
> +    if (cae->api->encoder_get_buffer(cae->encoder, &pic) < 0) {
> +        av_log(avctx,AV_LOG_ERROR, "failed to get frame buffer\n");
> +        return AVERROR(AVERROR_EXTERNAL);

Just AVERROR_EXTERNAL - AVERROR(EFOO) is only used for the errno.h EFOO codes.

> +    }
> +    if (frame) {
> +        switch (frame->format) {
> +            case AV_PIX_FMT_YUV420P:
> +                if (pic.img.in_sample_size == pic.img.enc_sample_size) {
> +                    xavs2_copy_frame(&pic, frame);
> +                } else {
> +                    const int shift_in = atoi(cae->api->opt_get(cae->param, "SampleShift"));
> +                    xavs2_copy_frame_with_shift(&pic, frame, shift_in);
> +                }
> +            break;
> +            case AV_PIX_FMT_YUV420P10:
> +                if (pic.img.in_sample_size == pic.img.enc_sample_size) {
> +                    xavs2_copy_frame(&pic, frame);
> +                    break;
> +                }
> +            default:
> +                av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n");
> +                return AVERROR(EINVAL);
> +            break;
> +        }
> +
> +        pic.i_state = 0;
> +        pic.i_pts  = frame->pts;
> +        pic.i_type = XAVS2_TYPE_AUTO;
> +
> +        ret = cae->api->encoder_encode(cae->encoder, &pic, &cae->packet);
> +
> +        if (ret) {
> +            av_log(avctx, AV_LOG_ERROR, "encode failed\n");
> +            return AVERROR(AVERROR_EXTERNAL);

Just AVERROR_EXTERNAL.

> +        }
> +
> +    } else {
> +        cae->api->encoder_encode(cae->encoder, NULL, &cae->packet);
> +    }
> +
> +    if ((cae->packet.len) && (cae->packet.state != XAVS2_STATE_FLUSH_END)){
> +
> +        if (av_new_packet(pkt, cae->packet.len) < 0){
> +            av_log(avctx, AV_LOG_ERROR, "packet alloc failed\n");
> +            cae->api->encoder_packet_unref(cae->encoder, &cae->packet);
> +            return AVERROR(ENOMEM);
> +        }
> +
> +        pkt->pts = cae->packet.pts;
> +        pkt->dts = cae->packet.dts;
> +
> +        memcpy(pkt->data, cae->packet.stream, cae->packet.len);
> +        pkt->size = cae->packet.len;
> +
> +        cae->api->encoder_packet_unref(cae->encoder, &cae->packet);
> +
> +        *got_packet = 1;
> +    } else {
> +        *got_packet = 0;
> +    }
> +
> +    return 0;
> +}
> +
> ...
> +
> +#define OFFSET(x) offsetof(XAVS2EContext, x)
> +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +
> +static const AVOption options[] = {
> +    { "i_lcurow_threads",   "number of parallel threads for rows" ,     OFFSET(i_lcurow_threads), AV_OPT_TYPE_INT, {.i64 =  5 }, 1,   8,  VE },

This name could probably be clearer - "row_threads" might be enough, or if the fact that they are LCU rows is important then "lcu_row_threads".

Where does the default of 5 and the upper limit of 8 come from?  Is it desirable that this matches the number of CPUs, or are there some codec constraints to take into account as well?

> +    { "i_initial_qp"    ,   "Quantization parameter" ,                  OFFSET(i_initial_qp)    , AV_OPT_TYPE_INT, {.i64 = 34 }, 1,  63,  VE },

If I understand what you said previously correctly, this is only used in constant-QP mode, and there it is used as the QP for every frame (not just the initial one)?

If that's the case then it should probably not say "initial" - I would read "initial_qp" as meaning the QP used for the first frame only, so probably in modes with a bitrate target.  Maybe change it to just be "qp"?  That name is used by several other encoders, including libx264 and libxavs.

> +    { "speed_level"     ,   "Speed level, higher is slower and better", OFFSET(preset_level)    , AV_OPT_TYPE_INT, {.i64 =  0 }, 0,   9,  VE },
> +    { "hierarchical_ref",   "hierarchical reference" ,                  OFFSET(b_hierarchical_reference)    , AV_OPT_TYPE_BOOL,    {.i64 =  1 }, 0, 1,  VE },
> +    { "xavs2-params"    ,   "set the xavs2 configuration using a :-separated list of key=value parameters", OFFSET(xavs2_opts), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
> +    { NULL },
> +};
> +
> +static const AVClass libxavs2_class = {
> +    .class_name = "XAVS2EContext",

Use the class name "libxavs2", to match other encoders.

> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +static const AVCodecDefault xavs2_defaults[] = {
> +    { "b",                "0" },

You might want consider setting a default gop_size ("g") here as well - the global default of 12 is generally a bit low for most applications.

Similarly, you might want a value for max_b_frames ("bf"), since that defaults to zero.

> +    { NULL },
> +};
> +
> +AVCodec ff_libxavs2_encoder = {
> +    .name           = "libxavs2",
> +    .long_name      = NULL_IF_CONFIG_SMALL("libxavs2 AVS2-P2/IEEE1857.4"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_AVS2,
> +    .priv_data_size = sizeof(XAVS2EContext),
> +    .init           = xavs2_init,
> +    .encode2        = xavs2_encode_frame,
> +    .close          = xavs2_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     = &libxavs2_class,
> +    .defaults       = xavs2_defaults,
> +    .wrapper_name   = "libxavs2",
> +} ;
Thanks,

- Mark
hwren Sept. 6, 2018, 1:46 p.m. UTC | #2
At 2018-09-06 08:43:05, "Mark Thompson" <sw@jkqxz.net> wrote:
>On 05/09/18 14:38, hwren wrote:
>> Signed-off-by: hwren <hwrenx@126.com>
[...]
>> +    if (cae->xavs2_opts) {
>> +        AVDictionary *dict    = NULL;
>> +        AVDictionaryEntry *en = NULL;
>> +
>> +        if (!av_dict_parse_string(&dict, cae->xavs2_opts, "=", ":", 0)) {
>> +            while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) {
>> +                xavs2_opt_set2(en->key, "%s", en->value);
>
>Should you check the result of this one?  The user might pass soemthing completely invalid, which probably wants a warning at least.

Added into the macro function :)

[...]
>> +    { "i_initial_qp"    ,   "Quantization parameter" ,                  OFFSET(i_initial_qp)    , AV_OPT_TYPE_INT, {.i64 = 34 }, 1,  63,  VE },
>
>If I understand what you said previously correctly, this is only used in constant-QP mode, and there it is used as the QP for every frame (not just the initial one)?
>
>If that's the case then it should probably not say "initial" - I would read "initial_qp" as meaning the QP used for the first frame only, so probably in modes with a bitrate target.  Maybe change it to just be "qp"?  That name is used by several other encoders, including libx264 and libxavs.

If "RateControl" is opened, the "initial_qp" will be used for the first frame and kept for all the other frames (constant-QP) and if there is no rate control, the initial_qp will only work for the first frame (xavs2 will always initial the qp for the first frame). So...maybe better with "initial"?

Thanks,
Huiwen Ren
Mark Thompson Sept. 9, 2018, 5:50 p.m. UTC | #3
On 06/09/18 14:46, Huiwen Ren wrote:
> At 2018-09-06 08:43:05, "Mark Thompson" <sw@jkqxz.net> wrote:
>> On 05/09/18 14:38, hwren wrote:
>>> +    { "i_initial_qp"    ,   "Quantization parameter" ,                  OFFSET(i_initial_qp)    , AV_OPT_TYPE_INT, {.i64 = 34 }, 1,  63,  VE },
>>
>> If I understand what you said previously correctly, this is only used in constant-QP mode, and there it is used as the QP for every frame (not just the initial one)?
>>
>> If that's the case then it should probably not say "initial" - I would read "initial_qp" as meaning the QP used for the first frame only, so probably in modes with a bitrate target.  Maybe change it to just be "qp"?  That name is used by several other encoders, including libx264 and libxavs.
> 
> If "RateControl" is opened, the "initial_qp" will be used for the first frame and kept for all the other frames (constant-QP) and if there is no rate control, the initial_qp will only work for the first frame (xavs2 will always initial the qp for the first frame). So...maybe better with "initial"?

Oh, so it's actually being used for both cases here?  Then I think it should be two separate options to match other encoders ("initial_qp" for the bitrate-target case, "qp" or AVCodecContext.global_quality for the constant-quality case).

Relatedly, the min-QP default value seems to be applied in constant-quality mode as well, where it probably shouldn't be:

$ for i in $(seq 1 63) ; do ./ffmpeg_g -y -i in.mp4 -an -c:v libxavs2 -frames:v 1000 -initial_qp $i out-$i.avs ; done
...
$ rename 's/-(..avs)/-0$1/' out-*
$ du -b out-*
18464881        out-01.avs
18464878        out-02.avs
18464872        out-03.avs
18464872        out-04.avs
18464875        out-05.avs
18464872        out-06.avs
18464875        out-07.avs
18464873        out-08.avs
18464878        out-09.avs
18464878        out-10.avs
18464875        out-11.avs
18464878        out-12.avs
18464875        out-13.avs
18464872        out-14.avs
18464878        out-15.avs
18464875        out-16.avs
18464875        out-17.avs
18464878        out-18.avs
18464872        out-19.avs
18464878        out-20.avs
17015783        out-21.avs
15555727        out-22.avs
14176171        out-23.avs
12707758        out-24.avs
11603156        out-25.avs
10401092        out-26.avs
9392228 out-27.avs
8371627 out-28.avs
7476957 out-29.avs
6706333 out-30.avs
6025691 out-31.avs
5402430 out-32.avs
4877929 out-33.avs
4563285 out-34.avs
4081752 out-35.avs
3672218 out-36.avs
3290184 out-37.avs
2945455 out-38.avs
2637767 out-39.avs
2362608 out-40.avs
2119294 out-41.avs
1902747 out-42.avs
1710491 out-43.avs
1536269 out-44.avs
1390060 out-45.avs
1238536 out-46.avs
1122929 out-47.avs
1005188 out-48.avs
906167  out-49.avs
807448  out-50.avs
729962  out-51.avs
647092  out-52.avs
583707  out-53.avs
520264  out-54.avs
469991  out-55.avs
421588  out-56.avs
380653  out-57.avs
347411  out-58.avs
313181  out-59.avs
287456  out-60.avs
268281  out-61.avs
243882  out-62.avs
232726  out-63.avs


Thanks,

- Mark
diff mbox

Patch

diff --git a/Changelog b/Changelog
index 0975fee..8377956 100644
--- a/Changelog
+++ b/Changelog
@@ -21,6 +21,7 @@  version <next>:
 - Brooktree ProSumer video decoder
 - MatchWare Screen Capture Codec decoder
 - WinCam Motion Video decoder
+- AVS2 video encoder via libxavs2
 
 
 version 4.0:
diff --git a/configure b/configure
index 0d6ee0a..c8dc1a8 100755
--- a/configure
+++ b/configure
@@ -280,6 +280,7 @@  External library support:
   --enable-libx264         enable H.264 encoding via x264 [no]
   --enable-libx265         enable HEVC encoding via x265 [no]
   --enable-libxavs         enable AVS encoding via xavs [no]
+  --enable-libxavs2        enable AVS2 encoding via xavs2 [no]
   --enable-libxcb          enable X11 grabbing using XCB [autodetect]
   --enable-libxcb-shm      enable X11 grabbing shm communication [autodetect]
   --enable-libxcb-xfixes   enable X11 grabbing mouse rendering [autodetect]
@@ -1666,6 +1667,7 @@  EXTERNAL_LIBRARY_GPL_LIST="
     libx264
     libx265
     libxavs
+    libxavs2
     libxvid
 "
 
@@ -3131,6 +3133,7 @@  libx264rgb_encoder_deps="libx264 x264_csp_bgr"
 libx264rgb_encoder_select="libx264_encoder"
 libx265_encoder_deps="libx265"
 libxavs_encoder_deps="libxavs"
+libxavs2_encoder_deps="libxavs2"
 libxvid_encoder_deps="libxvid"
 libzvbi_teletext_decoder_deps="libzvbi"
 vapoursynth_demuxer_deps="vapoursynth"
@@ -6165,6 +6168,7 @@  enabled libx264           && { check_pkg_config libx264 x264 "stdint.h x264.h" x
 enabled libx265           && require_pkg_config libx265 x265 x265.h x265_api_get &&
                              require_cpp_condition libx265 x265.h "X265_BUILD >= 68"
 enabled libxavs           && require libxavs "stdint.h xavs.h" xavs_encoder_encode "-lxavs $pthreads_extralibs $libm_extralibs"
+enabled libxavs2          && require_pkg_config libxavs2 "xavs2 >= 1.2.77" "stdint.h xavs2.h" xavs2_api_get
 enabled libxvid           && require libxvid xvid.h xvid_global -lxvidcore
 enabled libzimg           && require_pkg_config libzimg "zimg >= 2.7.0" zimg.h zimg_get_api_version
 enabled libzmq            && require_pkg_config libzmq libzmq zmq.h zmq_ctx_new
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7b09575..6998493 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2726,6 +2726,40 @@  Reduces detail but attempts to preserve color at extremely low bitrates.
 
 @end table
 
+@section libxavs2
+
+xavs2 AVS2-P2/IEEE1857.4 encoder wrapper.
+
+This encoder requires the presence of the libxavs2 headers and library
+during configuration. You need to explicitly configure the build with
+@option{--enable-libxavs2}.
+
+@subsection Options
+
+@table @option
+@item i_lcurow_threads
+Set the number of parallel threads for rows from 1 to 8 (default 5).
+
+@item i_initial_qp
+Set the xavs2 quantization parameter from 1 to 63 (default 34).
+
+@item speed_level
+Set the Speed level from 0 to 9 (default 0).
+
+@item hierarchical_ref
+Set the hierarchical reference or not (default true).
+
+@item xavs2-params
+Set xavs2 options using a list of @var{key}=@var{value} couples separated
+by ":".
+
+For example to specify libxavs2 encoding options with @option{-xavs2-params}:
+
+@example
+ffmpeg -i input -c:v libxavs2 -xavs2-params preset_level=5 output.avs2
+@end example
+@end table
+
 @c man end VIDEO ENCODERS
 
 @chapter Subtitles Encoders
diff --git a/doc/general.texi b/doc/general.texi
index 06f7a78..05f7bcd9 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -17,6 +17,20 @@  for more formats. None of them are used by default, their use has to be
 explicitly requested by passing the appropriate flags to
 @command{./configure}.
 
+@section libxavs2
+
+FFmpeg can make use of the xavs2 library for AVS2-P2/IEEE1857.4 video encoding.
+
+Go to @url{https://github.com/pkuvcl/xavs2} and follow the instructions for
+installing the library. Then pass @code{--enable-libxavs2} to configure to
+enable it.
+
+@float NOTE
+libxavs2 is under the GNU Public License Version 2 or later
+(see @url{http://www.gnu.org/licenses/old-licenses/gpl-2.0.html} for
+details), you must upgrade FFmpeg's license to GPL in order to use it.
+@end float
+
 @section libdavs2
 
 FFmpeg can make use of the davs2 library for AVS2-P2/IEEE1857.4 video decoding.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index f8673f0..bf17bf7 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -992,6 +992,7 @@  OBJS-$(CONFIG_LIBX262_ENCODER)            += libx264.o
 OBJS-$(CONFIG_LIBX264_ENCODER)            += libx264.o
 OBJS-$(CONFIG_LIBX265_ENCODER)            += libx265.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
+OBJS-$(CONFIG_LIBXAVS2_ENCODER)           += libxavs2.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
 OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER)   += libzvbi-teletextdec.o ass.o
 
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index a461131..493ff8f 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -711,6 +711,7 @@  extern AVCodec ff_libx264_encoder;
 extern AVCodec ff_libx264rgb_encoder;
 extern AVCodec ff_libx265_encoder;
 extern AVCodec ff_libxavs_encoder;
+extern AVCodec ff_libxavs2_encoder;
 extern AVCodec ff_libxvid_encoder;
 extern AVCodec ff_libzvbi_teletext_decoder;
 
diff --git a/libavcodec/libxavs2.c b/libavcodec/libxavs2.c
new file mode 100644
index 0000000..ce92efb
--- /dev/null
+++ b/libavcodec/libxavs2.c
@@ -0,0 +1,290 @@ 
+/*
+ * AVS2 encoding using the xavs2 library
+ *
+ * Copyright (C) 2018 Yiqun Xu,   <yiqun.xu@vipl.ict.ac.cn>
+ *                    Falei Luo,  <falei.luo@gmail.com>
+ *                    Huiwen Ren, <hwrenx@gmail.com>
+ *
+ * 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 <ctype.h>
+
+#include "xavs2.h"
+#include "avcodec.h"
+#include "mpeg12.h"
+#include "internal.h"
+#include "libavutil/internal.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/common.h"
+#include "libavutil/avutil.h"
+
+#define xavs2_opt_set2(name, format, ...) do{ \
+    char opt_str[16] = {0}; \
+    av_strlcatf(opt_str, sizeof(opt_str), format, __VA_ARGS__); \
+    cae->api->opt_set2(cae->param, name, opt_str); \
+} while(0);
+
+typedef struct XAVS2EContext {
+    AVClass *class;
+
+    int i_lcurow_threads;
+    int i_initial_qp;
+    int preset_level;
+    int intra_period;
+
+    void *encoder;
+    char *xavs2_opts;
+
+    int b_hierarchical_reference;
+
+    xavs2_outpacket_t packet;
+    xavs2_param_t *param;
+
+    const xavs2_api_t *api;
+
+} XAVS2EContext;
+
+static av_cold int xavs2_init(AVCodecContext *avctx)
+{
+    XAVS2EContext *cae= avctx->priv_data;
+    int bit_depth, code;
+
+    bit_depth = avctx->pix_fmt == AV_PIX_FMT_YUV420P ? 8 : 10;
+
+    /* get API handler */
+    cae->api = xavs2_api_get(bit_depth);
+
+    if (!cae->api) {
+        av_log(avctx, AV_LOG_ERROR, "api get failed\n");
+        return AVERROR(EINVAL);
+    }
+
+    cae->param = cae->api->opt_alloc();
+
+    if (!cae->param) {
+        av_log(avctx, AV_LOG_ERROR, "param alloc failed\n");
+        return AVERROR(EINVAL);
+    }
+
+    xavs2_opt_set2("rec",   "%d", 0);
+    xavs2_opt_set2("log",   "%d", 2);
+
+    xavs2_opt_set2("width",     "%d", avctx->width);
+    xavs2_opt_set2("height",    "%d", avctx->height);
+    xavs2_opt_set2("bframes",   "%d", avctx->max_b_frames);
+    xavs2_opt_set2("bitdepth",  "%d", bit_depth);
+    xavs2_opt_set2("preset",    "%d", cae->preset_level);
+
+    xavs2_opt_set2("thread_frames",     "%d", avctx->thread_count);
+    xavs2_opt_set2("intraperiod",       "%d", avctx->gop_size);
+    xavs2_opt_set2("thread_rows",       "%d", cae->i_lcurow_threads);
+    xavs2_opt_set2("initial_qp",        "%d", cae->i_initial_qp);
+    xavs2_opt_set2("hierarchical_ref",  "%d", cae->b_hierarchical_reference);
+
+    if (cae->xavs2_opts) {
+        AVDictionary *dict    = NULL;
+        AVDictionaryEntry *en = NULL;
+
+        if (!av_dict_parse_string(&dict, cae->xavs2_opts, "=", ":", 0)) {
+            while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) {
+                xavs2_opt_set2(en->key, "%s", en->value);
+            }
+            av_dict_free(&dict);
+        }
+    }
+
+    /* Rate control */
+    if (avctx->bit_rate > 0) {
+        xavs2_opt_set2("RateControl",   "%d", 1);
+        xavs2_opt_set2("TargetBitRate", "%d", avctx->bit_rate);
+    }
+
+
+    ff_mpeg12_find_best_frame_rate(avctx->framerate, &code, NULL, NULL, 0);
+
+    xavs2_opt_set2("FrameRate",   "%d", code);
+
+    cae->encoder = cae->api->encoder_create(cae->param);
+
+    if (!cae->encoder) {
+        av_log(avctx,AV_LOG_ERROR, "Can not create encoder. Null pointer returned\n");
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static void xavs2_copy_frame_with_shift(xavs2_picture_t *pic, AVFrame *frame, const int shift_in)
+{
+    int j, k;
+    for (k = 0; k < 3; k++) {
+        int i_stride = pic->img.i_stride[k];
+        for (j = 0; j < pic->img.i_lines[k]; j++) {
+            uint16_t *p_plane = (uint16_t *)&pic->img.img_planes[k][j * i_stride];
+            int i;
+            uint8_t *p_buffer = frame->data[k] + frame->linesize[k] * j;
+            memset(p_plane, 0, i_stride);
+            for (i = 0; i < pic->img.i_width[k]; i++) {
+                p_plane[i] = p_buffer[i] << shift_in;
+            }
+        }
+    }
+}
+
+static void xavs2_copy_frame(xavs2_picture_t *pic, AVFrame *frame)
+{
+    int j, k;
+    for (k = 0; k < 3; k++) {
+        for (j = 0; j < pic->img.i_lines[k]; j++) {
+            memcpy( pic->img.img_planes[k] + pic->img.i_stride[k] * j,
+                    frame->data[k]+frame->linesize[k] * j,
+                    pic->img.i_width[k] * pic->img.in_sample_size);
+        }
+    }
+}
+
+static int xavs2_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                      const AVFrame *frame, int *got_packet)
+{
+    XAVS2EContext *cae = avctx->priv_data;
+    xavs2_picture_t pic;
+    int ret;
+
+    /* create the XAVS2 video encoder */
+    /* read frame data and send to the XAVS2 video encoder */
+    if (cae->api->encoder_get_buffer(cae->encoder, &pic) < 0) {
+        av_log(avctx,AV_LOG_ERROR, "failed to get frame buffer\n");
+        return AVERROR(AVERROR_EXTERNAL);
+    }
+    if (frame) {
+        switch (frame->format) {
+            case AV_PIX_FMT_YUV420P:
+                if (pic.img.in_sample_size == pic.img.enc_sample_size) {
+                    xavs2_copy_frame(&pic, frame);
+                } else {
+                    const int shift_in = atoi(cae->api->opt_get(cae->param, "SampleShift"));
+                    xavs2_copy_frame_with_shift(&pic, frame, shift_in);
+                }
+            break;
+            case AV_PIX_FMT_YUV420P10:
+                if (pic.img.in_sample_size == pic.img.enc_sample_size) {
+                    xavs2_copy_frame(&pic, frame);
+                    break;
+                }
+            default:
+                av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n");
+                return AVERROR(EINVAL);
+            break;
+        }
+
+        pic.i_state = 0;
+        pic.i_pts  = frame->pts;
+        pic.i_type = XAVS2_TYPE_AUTO;
+
+        ret = cae->api->encoder_encode(cae->encoder, &pic, &cae->packet);
+
+        if (ret) {
+            av_log(avctx, AV_LOG_ERROR, "encode failed\n");
+            return AVERROR(AVERROR_EXTERNAL);
+        }
+
+    } else {
+        cae->api->encoder_encode(cae->encoder, NULL, &cae->packet);
+    }
+
+    if ((cae->packet.len) && (cae->packet.state != XAVS2_STATE_FLUSH_END)){
+
+        if (av_new_packet(pkt, cae->packet.len) < 0){
+            av_log(avctx, AV_LOG_ERROR, "packet alloc failed\n");
+            cae->api->encoder_packet_unref(cae->encoder, &cae->packet);
+            return AVERROR(ENOMEM);
+        }
+
+        pkt->pts = cae->packet.pts;
+        pkt->dts = cae->packet.dts;
+
+        memcpy(pkt->data, cae->packet.stream, cae->packet.len);
+        pkt->size = cae->packet.len;
+
+        cae->api->encoder_packet_unref(cae->encoder, &cae->packet);
+
+        *got_packet = 1;
+    } else {
+        *got_packet = 0;
+    }
+
+    return 0;
+}
+
+static av_cold int xavs2_close(AVCodecContext *avctx)
+{
+    XAVS2EContext *cae = avctx->priv_data;
+    /* destroy the encoder */
+    if (cae->api) {
+        cae->api->encoder_destroy(cae->encoder);
+
+        if (cae->param) {
+            cae->api->opt_destroy(cae->param);
+        }
+    }
+    return 0;
+}
+
+#define OFFSET(x) offsetof(XAVS2EContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+
+static const AVOption options[] = {
+    { "i_lcurow_threads",   "number of parallel threads for rows" ,     OFFSET(i_lcurow_threads), AV_OPT_TYPE_INT, {.i64 =  5 }, 1,   8,  VE },
+    { "i_initial_qp"    ,   "Quantization parameter" ,                  OFFSET(i_initial_qp)    , AV_OPT_TYPE_INT, {.i64 = 34 }, 1,  63,  VE },
+    { "speed_level"     ,   "Speed level, higher is slower and better", OFFSET(preset_level)    , AV_OPT_TYPE_INT, {.i64 =  0 }, 0,   9,  VE },
+    { "hierarchical_ref",   "hierarchical reference" ,                  OFFSET(b_hierarchical_reference)    , AV_OPT_TYPE_BOOL,    {.i64 =  1 }, 0, 1,  VE },
+    { "xavs2-params"    ,   "set the xavs2 configuration using a :-separated list of key=value parameters", OFFSET(xavs2_opts), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
+    { NULL },
+};
+
+static const AVClass libxavs2_class = {
+    .class_name = "XAVS2EContext",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVCodecDefault xavs2_defaults[] = {
+    { "b",                "0" },
+    { NULL },
+};
+
+AVCodec ff_libxavs2_encoder = {
+    .name           = "libxavs2",
+    .long_name      = NULL_IF_CONFIG_SMALL("libxavs2 AVS2-P2/IEEE1857.4"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_AVS2,
+    .priv_data_size = sizeof(XAVS2EContext),
+    .init           = xavs2_init,
+    .encode2        = xavs2_encode_frame,
+    .close          = xavs2_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     = &libxavs2_class,
+    .defaults       = xavs2_defaults,
+    .wrapper_name   = "libxavs2",
+} ;
diff --git a/libavcodec/version.h b/libavcodec/version.h
index ce33490..c092491 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  27
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MINOR  28
+#define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \