Message ID | 1536154712-22593-1-git-send-email-hwrenx@126.com |
---|---|
State | Superseded |
Headers | show |
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
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
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 --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, \
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