1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * AVS2 encoding using the xavs2 library
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * Copyright (C) 2018 Yiqun Xu,   <yiqun.xu@vipl.ict.ac.cn>
5cabdff1aSopenharmony_ci *                    Falei Luo,  <falei.luo@gmail.com>
6cabdff1aSopenharmony_ci *                    Huiwen Ren, <hwrenx@gmail.com>
7cabdff1aSopenharmony_ci *
8cabdff1aSopenharmony_ci * This file is part of FFmpeg.
9cabdff1aSopenharmony_ci *
10cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
11cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
12cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
13cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
14cabdff1aSopenharmony_ci *
15cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
16cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
17cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18cabdff1aSopenharmony_ci * Lesser General Public License for more details.
19cabdff1aSopenharmony_ci *
20cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
21cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
22cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23cabdff1aSopenharmony_ci */
24cabdff1aSopenharmony_ci
25cabdff1aSopenharmony_ci#include "xavs2.h"
26cabdff1aSopenharmony_ci#include "codec_internal.h"
27cabdff1aSopenharmony_ci#include "encode.h"
28cabdff1aSopenharmony_ci#include "mpeg12.h"
29cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
30cabdff1aSopenharmony_ci#include "libavutil/opt.h"
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_ci#define xavs2_opt_set2(name, format, ...) do{ \
33cabdff1aSopenharmony_ci    char opt_str[16] = {0}; \
34cabdff1aSopenharmony_ci    int err; \
35cabdff1aSopenharmony_ci    av_strlcatf(opt_str, sizeof(opt_str), format, __VA_ARGS__); \
36cabdff1aSopenharmony_ci    err = cae->api->opt_set2(cae->param, name, opt_str); \
37cabdff1aSopenharmony_ci    if (err < 0) {\
38cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING, "Invalid value for %s: %s\n", name, opt_str);\
39cabdff1aSopenharmony_ci    }\
40cabdff1aSopenharmony_ci} while(0);
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_citypedef struct XAVS2EContext {
43cabdff1aSopenharmony_ci    AVClass *class;
44cabdff1aSopenharmony_ci
45cabdff1aSopenharmony_ci    int lcu_row_threads;
46cabdff1aSopenharmony_ci    int initial_qp;
47cabdff1aSopenharmony_ci    int qp;
48cabdff1aSopenharmony_ci    int max_qp;
49cabdff1aSopenharmony_ci    int min_qp;
50cabdff1aSopenharmony_ci    int preset_level;
51cabdff1aSopenharmony_ci    int log_level;
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_ci    void *encoder;
54cabdff1aSopenharmony_ci    AVDictionary *xavs2_opts;
55cabdff1aSopenharmony_ci
56cabdff1aSopenharmony_ci    xavs2_outpacket_t packet;
57cabdff1aSopenharmony_ci    xavs2_param_t *param;
58cabdff1aSopenharmony_ci
59cabdff1aSopenharmony_ci    const xavs2_api_t *api;
60cabdff1aSopenharmony_ci
61cabdff1aSopenharmony_ci} XAVS2EContext;
62cabdff1aSopenharmony_ci
63cabdff1aSopenharmony_cistatic av_cold int xavs2_init(AVCodecContext *avctx)
64cabdff1aSopenharmony_ci{
65cabdff1aSopenharmony_ci    XAVS2EContext *cae = avctx->priv_data;
66cabdff1aSopenharmony_ci    int bit_depth, code;
67cabdff1aSopenharmony_ci
68cabdff1aSopenharmony_ci    bit_depth = avctx->pix_fmt == AV_PIX_FMT_YUV420P ? 8 : 10;
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_ci    /* get API handler */
71cabdff1aSopenharmony_ci    cae->api = xavs2_api_get(bit_depth);
72cabdff1aSopenharmony_ci    if (!cae->api) {
73cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Failed to get xavs2 api context\n");
74cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
75cabdff1aSopenharmony_ci    }
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci    cae->param = cae->api->opt_alloc();
78cabdff1aSopenharmony_ci    if (!cae->param) {
79cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Failed to alloc xavs2 parameters\n");
80cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
81cabdff1aSopenharmony_ci    }
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_ci    xavs2_opt_set2("Width",     "%d", avctx->width);
84cabdff1aSopenharmony_ci    xavs2_opt_set2("Height",    "%d", avctx->height);
85cabdff1aSopenharmony_ci    xavs2_opt_set2("BFrames",   "%d", avctx->max_b_frames);
86cabdff1aSopenharmony_ci    xavs2_opt_set2("BitDepth",  "%d", bit_depth);
87cabdff1aSopenharmony_ci    xavs2_opt_set2("Log",       "%d", cae->log_level);
88cabdff1aSopenharmony_ci    xavs2_opt_set2("Preset",    "%d", cae->preset_level);
89cabdff1aSopenharmony_ci
90cabdff1aSopenharmony_ci    xavs2_opt_set2("IntraPeriodMax",    "%d", avctx->gop_size);
91cabdff1aSopenharmony_ci    xavs2_opt_set2("IntraPeriodMin",    "%d", avctx->gop_size);
92cabdff1aSopenharmony_ci
93cabdff1aSopenharmony_ci    xavs2_opt_set2("ThreadFrames",      "%d", avctx->thread_count);
94cabdff1aSopenharmony_ci    xavs2_opt_set2("ThreadRows",        "%d", cae->lcu_row_threads);
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_ci    xavs2_opt_set2("OpenGOP",  "%d", !(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP));
97cabdff1aSopenharmony_ci
98cabdff1aSopenharmony_ci    {
99cabdff1aSopenharmony_ci        AVDictionaryEntry *en = NULL;
100cabdff1aSopenharmony_ci        while ((en = av_dict_get(cae->xavs2_opts, "", en, AV_DICT_IGNORE_SUFFIX)))
101cabdff1aSopenharmony_ci            xavs2_opt_set2(en->key, "%s", en->value);
102cabdff1aSopenharmony_ci    }
103cabdff1aSopenharmony_ci
104cabdff1aSopenharmony_ci    /* Rate control */
105cabdff1aSopenharmony_ci    if (avctx->bit_rate > 0) {
106cabdff1aSopenharmony_ci        xavs2_opt_set2("RateControl",   "%d", 1);
107cabdff1aSopenharmony_ci        xavs2_opt_set2("TargetBitRate", "%"PRId64"", avctx->bit_rate);
108cabdff1aSopenharmony_ci        xavs2_opt_set2("InitialQP",     "%d", cae->initial_qp);
109cabdff1aSopenharmony_ci        xavs2_opt_set2("MaxQP",         "%d", avctx->qmax >= 0 ? avctx->qmax : cae->max_qp);
110cabdff1aSopenharmony_ci        xavs2_opt_set2("MinQP",         "%d", avctx->qmin >= 0 ? avctx->qmin : cae->min_qp);
111cabdff1aSopenharmony_ci    } else {
112cabdff1aSopenharmony_ci        xavs2_opt_set2("InitialQP",     "%d", cae->qp);
113cabdff1aSopenharmony_ci    }
114cabdff1aSopenharmony_ci
115cabdff1aSopenharmony_ci    ff_mpeg12_find_best_frame_rate(avctx->framerate, &code, NULL, NULL, 0);
116cabdff1aSopenharmony_ci    xavs2_opt_set2("FrameRate",   "%d", code);
117cabdff1aSopenharmony_ci
118cabdff1aSopenharmony_ci    cae->encoder = cae->api->encoder_create(cae->param);
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci    if (!cae->encoder) {
121cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Failed to create xavs2 encoder instance.\n");
122cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
123cabdff1aSopenharmony_ci    }
124cabdff1aSopenharmony_ci
125cabdff1aSopenharmony_ci    return 0;
126cabdff1aSopenharmony_ci}
127cabdff1aSopenharmony_ci
128cabdff1aSopenharmony_cistatic void xavs2_copy_frame_with_shift(xavs2_picture_t *pic, const AVFrame *frame, const int shift_in)
129cabdff1aSopenharmony_ci{
130cabdff1aSopenharmony_ci    uint16_t *p_plane;
131cabdff1aSopenharmony_ci    uint8_t *p_buffer;
132cabdff1aSopenharmony_ci    int plane;
133cabdff1aSopenharmony_ci    int hIdx;
134cabdff1aSopenharmony_ci    int wIdx;
135cabdff1aSopenharmony_ci
136cabdff1aSopenharmony_ci    for (plane = 0; plane < 3; plane++) {
137cabdff1aSopenharmony_ci        p_plane = (uint16_t *)pic->img.img_planes[plane];
138cabdff1aSopenharmony_ci        p_buffer = frame->data[plane];
139cabdff1aSopenharmony_ci        for (hIdx = 0; hIdx < pic->img.i_lines[plane]; hIdx++) {
140cabdff1aSopenharmony_ci            memset(p_plane, 0, pic->img.i_stride[plane]);
141cabdff1aSopenharmony_ci            for (wIdx = 0; wIdx < pic->img.i_width[plane]; wIdx++) {
142cabdff1aSopenharmony_ci                p_plane[wIdx] = p_buffer[wIdx] << shift_in;
143cabdff1aSopenharmony_ci            }
144cabdff1aSopenharmony_ci            p_plane += pic->img.i_stride[plane];
145cabdff1aSopenharmony_ci            p_buffer += frame->linesize[plane];
146cabdff1aSopenharmony_ci        }
147cabdff1aSopenharmony_ci    }
148cabdff1aSopenharmony_ci}
149cabdff1aSopenharmony_ci
150cabdff1aSopenharmony_cistatic void xavs2_copy_frame(xavs2_picture_t *pic, const AVFrame *frame)
151cabdff1aSopenharmony_ci{
152cabdff1aSopenharmony_ci    uint8_t *p_plane;
153cabdff1aSopenharmony_ci    uint8_t *p_buffer;
154cabdff1aSopenharmony_ci    int plane;
155cabdff1aSopenharmony_ci    int hIdx;
156cabdff1aSopenharmony_ci    int stride;
157cabdff1aSopenharmony_ci
158cabdff1aSopenharmony_ci    for (plane = 0; plane < 3; plane++) {
159cabdff1aSopenharmony_ci        p_plane = pic->img.img_planes[plane];
160cabdff1aSopenharmony_ci        p_buffer = frame->data[plane];
161cabdff1aSopenharmony_ci        stride = pic->img.i_width[plane] * pic->img.in_sample_size;
162cabdff1aSopenharmony_ci        for (hIdx = 0; hIdx < pic->img.i_lines[plane]; hIdx++) {
163cabdff1aSopenharmony_ci            memcpy(p_plane, p_buffer, stride);
164cabdff1aSopenharmony_ci            p_plane += pic->img.i_stride[plane];
165cabdff1aSopenharmony_ci            p_buffer += frame->linesize[plane];
166cabdff1aSopenharmony_ci        }
167cabdff1aSopenharmony_ci    }
168cabdff1aSopenharmony_ci}
169cabdff1aSopenharmony_ci
170cabdff1aSopenharmony_cistatic int xavs2_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
171cabdff1aSopenharmony_ci                              const AVFrame *frame, int *got_packet)
172cabdff1aSopenharmony_ci{
173cabdff1aSopenharmony_ci    XAVS2EContext *cae = avctx->priv_data;
174cabdff1aSopenharmony_ci    xavs2_picture_t pic;
175cabdff1aSopenharmony_ci    int ret;
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_ci    /* create the XAVS2 video encoder */
178cabdff1aSopenharmony_ci    /* read frame data and send to the XAVS2 video encoder */
179cabdff1aSopenharmony_ci    if (cae->api->encoder_get_buffer(cae->encoder, &pic) < 0) {
180cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Failed to get xavs2 frame buffer\n");
181cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
182cabdff1aSopenharmony_ci    }
183cabdff1aSopenharmony_ci    if (frame) {
184cabdff1aSopenharmony_ci        switch (frame->format) {
185cabdff1aSopenharmony_ci        case AV_PIX_FMT_YUV420P:
186cabdff1aSopenharmony_ci            if (pic.img.in_sample_size == pic.img.enc_sample_size) {
187cabdff1aSopenharmony_ci                xavs2_copy_frame(&pic, frame);
188cabdff1aSopenharmony_ci            } else {
189cabdff1aSopenharmony_ci                const int shift_in = atoi(cae->api->opt_get(cae->param, "SampleShift"));
190cabdff1aSopenharmony_ci                xavs2_copy_frame_with_shift(&pic, frame, shift_in);
191cabdff1aSopenharmony_ci            }
192cabdff1aSopenharmony_ci            break;
193cabdff1aSopenharmony_ci        case AV_PIX_FMT_YUV420P10:
194cabdff1aSopenharmony_ci            if (pic.img.in_sample_size == pic.img.enc_sample_size) {
195cabdff1aSopenharmony_ci                xavs2_copy_frame(&pic, frame);
196cabdff1aSopenharmony_ci                break;
197cabdff1aSopenharmony_ci            }
198cabdff1aSopenharmony_ci        default:
199cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n");
200cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
201cabdff1aSopenharmony_ci            break;
202cabdff1aSopenharmony_ci        }
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_ci        pic.i_state = 0;
205cabdff1aSopenharmony_ci        pic.i_pts   = frame->pts;
206cabdff1aSopenharmony_ci        pic.i_type  = XAVS2_TYPE_AUTO;
207cabdff1aSopenharmony_ci
208cabdff1aSopenharmony_ci        ret = cae->api->encoder_encode(cae->encoder, &pic, &cae->packet);
209cabdff1aSopenharmony_ci
210cabdff1aSopenharmony_ci        if (ret) {
211cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_ERROR, "Encoding error occurred.\n");
212cabdff1aSopenharmony_ci            return AVERROR_EXTERNAL;
213cabdff1aSopenharmony_ci        }
214cabdff1aSopenharmony_ci
215cabdff1aSopenharmony_ci    } else {
216cabdff1aSopenharmony_ci        cae->api->encoder_encode(cae->encoder, NULL, &cae->packet);
217cabdff1aSopenharmony_ci    }
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_ci    if ((cae->packet.len) && (cae->packet.state != XAVS2_STATE_FLUSH_END)) {
220cabdff1aSopenharmony_ci        if ((ret = ff_get_encode_buffer(avctx, pkt, cae->packet.len, 0)) < 0) {
221cabdff1aSopenharmony_ci            cae->api->encoder_packet_unref(cae->encoder, &cae->packet);
222cabdff1aSopenharmony_ci            return ret;
223cabdff1aSopenharmony_ci        }
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_ci        pkt->pts = cae->packet.pts;
226cabdff1aSopenharmony_ci        pkt->dts = cae->packet.dts;
227cabdff1aSopenharmony_ci
228cabdff1aSopenharmony_ci        if (cae->packet.type == XAVS2_TYPE_IDR ||
229cabdff1aSopenharmony_ci            cae->packet.type == XAVS2_TYPE_I ||
230cabdff1aSopenharmony_ci            cae->packet.type == XAVS2_TYPE_KEYFRAME) {
231cabdff1aSopenharmony_ci            pkt->flags |= AV_PKT_FLAG_KEY;
232cabdff1aSopenharmony_ci        }
233cabdff1aSopenharmony_ci
234cabdff1aSopenharmony_ci        memcpy(pkt->data, cae->packet.stream, cae->packet.len);
235cabdff1aSopenharmony_ci
236cabdff1aSopenharmony_ci        cae->api->encoder_packet_unref(cae->encoder, &cae->packet);
237cabdff1aSopenharmony_ci
238cabdff1aSopenharmony_ci        *got_packet = 1;
239cabdff1aSopenharmony_ci    } else {
240cabdff1aSopenharmony_ci        *got_packet = 0;
241cabdff1aSopenharmony_ci    }
242cabdff1aSopenharmony_ci
243cabdff1aSopenharmony_ci    return 0;
244cabdff1aSopenharmony_ci}
245cabdff1aSopenharmony_ci
246cabdff1aSopenharmony_cistatic av_cold int xavs2_close(AVCodecContext *avctx)
247cabdff1aSopenharmony_ci{
248cabdff1aSopenharmony_ci    XAVS2EContext *cae = avctx->priv_data;
249cabdff1aSopenharmony_ci    /* destroy the encoder */
250cabdff1aSopenharmony_ci    if (cae->api) {
251cabdff1aSopenharmony_ci        cae->api->encoder_destroy(cae->encoder);
252cabdff1aSopenharmony_ci
253cabdff1aSopenharmony_ci        if (cae->param) {
254cabdff1aSopenharmony_ci            cae->api->opt_destroy(cae->param);
255cabdff1aSopenharmony_ci        }
256cabdff1aSopenharmony_ci    }
257cabdff1aSopenharmony_ci    return 0;
258cabdff1aSopenharmony_ci}
259cabdff1aSopenharmony_ci
260cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(XAVS2EContext, x)
261cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
262cabdff1aSopenharmony_ci
263cabdff1aSopenharmony_cistatic const AVOption options[] = {
264cabdff1aSopenharmony_ci    { "lcu_row_threads" ,   "number of parallel threads for rows" ,     OFFSET(lcu_row_threads) , AV_OPT_TYPE_INT, {.i64 =  0 },  0, INT_MAX,  VE },
265cabdff1aSopenharmony_ci    { "initial_qp"      ,   "Quantization initial parameter"      ,     OFFSET(initial_qp)      , AV_OPT_TYPE_INT, {.i64 = 34 },  1,      63,  VE },
266cabdff1aSopenharmony_ci    { "qp"              ,   "Quantization parameter"  ,                 OFFSET(qp)              , AV_OPT_TYPE_INT, {.i64 = 34 },  1,      63,  VE },
267cabdff1aSopenharmony_ci    { "max_qp"          ,   "max qp for rate control" ,                 OFFSET(max_qp)          , AV_OPT_TYPE_INT, {.i64 = 55 },  0,      63,  VE },
268cabdff1aSopenharmony_ci    { "min_qp"          ,   "min qp for rate control" ,                 OFFSET(min_qp)          , AV_OPT_TYPE_INT, {.i64 = 20 },  0,      63,  VE },
269cabdff1aSopenharmony_ci    { "speed_level"     ,   "Speed level, higher is better but slower", OFFSET(preset_level)    , AV_OPT_TYPE_INT, {.i64 =  0 },  0,       9,  VE },
270cabdff1aSopenharmony_ci    { "log_level"       ,   "log level: -1: none, 0: error, 1: warning, 2: info, 3: debug", OFFSET(log_level)    , AV_OPT_TYPE_INT, {.i64 =  0 },  -1,       3,  VE },
271cabdff1aSopenharmony_ci    { "xavs2-params"    ,   "set the xavs2 configuration using a :-separated list of key=value parameters", OFFSET(xavs2_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE },
272cabdff1aSopenharmony_ci    { NULL },
273cabdff1aSopenharmony_ci};
274cabdff1aSopenharmony_ci
275cabdff1aSopenharmony_cistatic const AVClass libxavs2 = {
276cabdff1aSopenharmony_ci    .class_name = "XAVS2EContext",
277cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
278cabdff1aSopenharmony_ci    .option     = options,
279cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
280cabdff1aSopenharmony_ci};
281cabdff1aSopenharmony_ci
282cabdff1aSopenharmony_cistatic const FFCodecDefault xavs2_defaults[] = {
283cabdff1aSopenharmony_ci    { "b",                "0" },
284cabdff1aSopenharmony_ci    { "g",                "48"},
285cabdff1aSopenharmony_ci    { "bf",               "7" },
286cabdff1aSopenharmony_ci    { NULL },
287cabdff1aSopenharmony_ci};
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ciconst FFCodec ff_libxavs2_encoder = {
290cabdff1aSopenharmony_ci    .p.name         = "libxavs2",
291cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("libxavs2 AVS2-P2/IEEE1857.4"),
292cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_VIDEO,
293cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_AVS2,
294cabdff1aSopenharmony_ci    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
295cabdff1aSopenharmony_ci                      AV_CODEC_CAP_OTHER_THREADS,
296cabdff1aSopenharmony_ci    .priv_data_size = sizeof(XAVS2EContext),
297cabdff1aSopenharmony_ci    .init           = xavs2_init,
298cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_CB(xavs2_encode_frame),
299cabdff1aSopenharmony_ci    .close          = xavs2_close,
300cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_AUTO_THREADS,
301cabdff1aSopenharmony_ci    .p.pix_fmts     = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P,
302cabdff1aSopenharmony_ci                                                     AV_PIX_FMT_NONE },
303cabdff1aSopenharmony_ci    .p.priv_class   = &libxavs2,
304cabdff1aSopenharmony_ci    .defaults       = xavs2_defaults,
305cabdff1aSopenharmony_ci    .p.wrapper_name = "libxavs2",
306cabdff1aSopenharmony_ci} ;
307