On 05/09/18 14:38, hwren wrote: > Signed-off-by: hwren <hwr...@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...@vipl.ict.ac.cn> > + * Falei Luo, <falei....@gmail.com> > + * Huiwen Ren, <hwr...@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel