1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * libkvazaar encoder 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * Copyright (c) 2015 Tampere University of Technology 5cabdff1aSopenharmony_ci * 6cabdff1aSopenharmony_ci * This file is part of FFmpeg. 7cabdff1aSopenharmony_ci * 8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 12cabdff1aSopenharmony_ci * 13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16cabdff1aSopenharmony_ci * Lesser General Public License for more details. 17cabdff1aSopenharmony_ci * 18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21cabdff1aSopenharmony_ci */ 22cabdff1aSopenharmony_ci 23cabdff1aSopenharmony_ci#include <kvazaar.h> 24cabdff1aSopenharmony_ci#include <stdint.h> 25cabdff1aSopenharmony_ci#include <string.h> 26cabdff1aSopenharmony_ci 27cabdff1aSopenharmony_ci#include "libavutil/attributes.h" 28cabdff1aSopenharmony_ci#include "libavutil/avassert.h" 29cabdff1aSopenharmony_ci#include "libavutil/dict.h" 30cabdff1aSopenharmony_ci#include "libavutil/error.h" 31cabdff1aSopenharmony_ci#include "libavutil/imgutils.h" 32cabdff1aSopenharmony_ci#include "libavutil/internal.h" 33cabdff1aSopenharmony_ci#include "libavutil/log.h" 34cabdff1aSopenharmony_ci#include "libavutil/mem.h" 35cabdff1aSopenharmony_ci#include "libavutil/pixdesc.h" 36cabdff1aSopenharmony_ci#include "libavutil/opt.h" 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci#include "avcodec.h" 39cabdff1aSopenharmony_ci#include "codec_internal.h" 40cabdff1aSopenharmony_ci#include "encode.h" 41cabdff1aSopenharmony_ci#include "packet_internal.h" 42cabdff1aSopenharmony_ci 43cabdff1aSopenharmony_citypedef struct LibkvazaarContext { 44cabdff1aSopenharmony_ci const AVClass *class; 45cabdff1aSopenharmony_ci 46cabdff1aSopenharmony_ci const kvz_api *api; 47cabdff1aSopenharmony_ci kvz_encoder *encoder; 48cabdff1aSopenharmony_ci kvz_config *config; 49cabdff1aSopenharmony_ci 50cabdff1aSopenharmony_ci char *kvz_params; 51cabdff1aSopenharmony_ci} LibkvazaarContext; 52cabdff1aSopenharmony_ci 53cabdff1aSopenharmony_cistatic av_cold int libkvazaar_init(AVCodecContext *avctx) 54cabdff1aSopenharmony_ci{ 55cabdff1aSopenharmony_ci LibkvazaarContext *const ctx = avctx->priv_data; 56cabdff1aSopenharmony_ci const kvz_api *const api = ctx->api = kvz_api_get(8); 57cabdff1aSopenharmony_ci kvz_config *cfg = NULL; 58cabdff1aSopenharmony_ci kvz_encoder *enc = NULL; 59cabdff1aSopenharmony_ci 60cabdff1aSopenharmony_ci /* Kvazaar requires width and height to be multiples of eight. */ 61cabdff1aSopenharmony_ci if (avctx->width % 8 || avctx->height % 8) { 62cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 63cabdff1aSopenharmony_ci "Video dimensions are not a multiple of 8 (%dx%d).\n", 64cabdff1aSopenharmony_ci avctx->width, avctx->height); 65cabdff1aSopenharmony_ci return AVERROR(ENOSYS); 66cabdff1aSopenharmony_ci } 67cabdff1aSopenharmony_ci 68cabdff1aSopenharmony_ci ctx->config = cfg = api->config_alloc(); 69cabdff1aSopenharmony_ci if (!cfg) { 70cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 71cabdff1aSopenharmony_ci "Could not allocate kvazaar config structure.\n"); 72cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 73cabdff1aSopenharmony_ci } 74cabdff1aSopenharmony_ci 75cabdff1aSopenharmony_ci if (!api->config_init(cfg)) { 76cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 77cabdff1aSopenharmony_ci "Could not initialize kvazaar config structure.\n"); 78cabdff1aSopenharmony_ci return AVERROR_BUG; 79cabdff1aSopenharmony_ci } 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_ci cfg->width = avctx->width; 82cabdff1aSopenharmony_ci cfg->height = avctx->height; 83cabdff1aSopenharmony_ci 84cabdff1aSopenharmony_ci if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { 85cabdff1aSopenharmony_ci cfg->framerate_num = avctx->framerate.num; 86cabdff1aSopenharmony_ci cfg->framerate_denom = avctx->framerate.den; 87cabdff1aSopenharmony_ci } else { 88cabdff1aSopenharmony_ci if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { 89cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 90cabdff1aSopenharmony_ci "Could not set framerate for kvazaar: integer overflow\n"); 91cabdff1aSopenharmony_ci return AVERROR(EINVAL); 92cabdff1aSopenharmony_ci } 93cabdff1aSopenharmony_ci cfg->framerate_num = avctx->time_base.den; 94cabdff1aSopenharmony_ci cfg->framerate_denom = avctx->time_base.num * avctx->ticks_per_frame; 95cabdff1aSopenharmony_ci } 96cabdff1aSopenharmony_ci cfg->target_bitrate = avctx->bit_rate; 97cabdff1aSopenharmony_ci cfg->vui.sar_width = avctx->sample_aspect_ratio.num; 98cabdff1aSopenharmony_ci cfg->vui.sar_height = avctx->sample_aspect_ratio.den; 99cabdff1aSopenharmony_ci if (avctx->bit_rate) { 100cabdff1aSopenharmony_ci cfg->rc_algorithm = KVZ_LAMBDA; 101cabdff1aSopenharmony_ci } 102cabdff1aSopenharmony_ci 103cabdff1aSopenharmony_ci if (ctx->kvz_params) { 104cabdff1aSopenharmony_ci AVDictionary *dict = NULL; 105cabdff1aSopenharmony_ci if (!av_dict_parse_string(&dict, ctx->kvz_params, "=", ",", 0)) { 106cabdff1aSopenharmony_ci AVDictionaryEntry *entry = NULL; 107cabdff1aSopenharmony_ci while ((entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX))) { 108cabdff1aSopenharmony_ci if (!api->config_parse(cfg, entry->key, entry->value)) { 109cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Invalid option: %s=%s.\n", 110cabdff1aSopenharmony_ci entry->key, entry->value); 111cabdff1aSopenharmony_ci } 112cabdff1aSopenharmony_ci } 113cabdff1aSopenharmony_ci } 114cabdff1aSopenharmony_ci av_dict_free(&dict); 115cabdff1aSopenharmony_ci } 116cabdff1aSopenharmony_ci 117cabdff1aSopenharmony_ci ctx->encoder = enc = api->encoder_open(cfg); 118cabdff1aSopenharmony_ci if (!enc) { 119cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Could not open kvazaar encoder.\n"); 120cabdff1aSopenharmony_ci return AVERROR_BUG; 121cabdff1aSopenharmony_ci } 122cabdff1aSopenharmony_ci 123cabdff1aSopenharmony_ci if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { 124cabdff1aSopenharmony_ci kvz_data_chunk *data_out = NULL; 125cabdff1aSopenharmony_ci kvz_data_chunk *chunk = NULL; 126cabdff1aSopenharmony_ci uint32_t len_out; 127cabdff1aSopenharmony_ci uint8_t *p; 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_ci if (!api->encoder_headers(enc, &data_out, &len_out)) 130cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci avctx->extradata = p = av_mallocz(len_out + AV_INPUT_BUFFER_PADDING_SIZE); 133cabdff1aSopenharmony_ci if (!p) { 134cabdff1aSopenharmony_ci ctx->api->chunk_free(data_out); 135cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 136cabdff1aSopenharmony_ci } 137cabdff1aSopenharmony_ci 138cabdff1aSopenharmony_ci avctx->extradata_size = len_out; 139cabdff1aSopenharmony_ci 140cabdff1aSopenharmony_ci for (chunk = data_out; chunk != NULL; chunk = chunk->next) { 141cabdff1aSopenharmony_ci memcpy(p, chunk->data, chunk->len); 142cabdff1aSopenharmony_ci p += chunk->len; 143cabdff1aSopenharmony_ci } 144cabdff1aSopenharmony_ci 145cabdff1aSopenharmony_ci ctx->api->chunk_free(data_out); 146cabdff1aSopenharmony_ci } 147cabdff1aSopenharmony_ci 148cabdff1aSopenharmony_ci return 0; 149cabdff1aSopenharmony_ci} 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_cistatic av_cold int libkvazaar_close(AVCodecContext *avctx) 152cabdff1aSopenharmony_ci{ 153cabdff1aSopenharmony_ci LibkvazaarContext *ctx = avctx->priv_data; 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_ci if (ctx->api) { 156cabdff1aSopenharmony_ci ctx->api->encoder_close(ctx->encoder); 157cabdff1aSopenharmony_ci ctx->api->config_destroy(ctx->config); 158cabdff1aSopenharmony_ci } 159cabdff1aSopenharmony_ci 160cabdff1aSopenharmony_ci return 0; 161cabdff1aSopenharmony_ci} 162cabdff1aSopenharmony_ci 163cabdff1aSopenharmony_cistatic int libkvazaar_encode(AVCodecContext *avctx, 164cabdff1aSopenharmony_ci AVPacket *avpkt, 165cabdff1aSopenharmony_ci const AVFrame *frame, 166cabdff1aSopenharmony_ci int *got_packet_ptr) 167cabdff1aSopenharmony_ci{ 168cabdff1aSopenharmony_ci LibkvazaarContext *ctx = avctx->priv_data; 169cabdff1aSopenharmony_ci kvz_picture *input_pic = NULL; 170cabdff1aSopenharmony_ci kvz_picture *recon_pic = NULL; 171cabdff1aSopenharmony_ci kvz_frame_info frame_info; 172cabdff1aSopenharmony_ci kvz_data_chunk *data_out = NULL; 173cabdff1aSopenharmony_ci uint32_t len_out = 0; 174cabdff1aSopenharmony_ci int retval = 0; 175cabdff1aSopenharmony_ci int pict_type; 176cabdff1aSopenharmony_ci 177cabdff1aSopenharmony_ci *got_packet_ptr = 0; 178cabdff1aSopenharmony_ci 179cabdff1aSopenharmony_ci if (frame) { 180cabdff1aSopenharmony_ci if (frame->width != ctx->config->width || 181cabdff1aSopenharmony_ci frame->height != ctx->config->height) { 182cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 183cabdff1aSopenharmony_ci "Changing video dimensions during encoding is not supported. " 184cabdff1aSopenharmony_ci "(changed from %dx%d to %dx%d)\n", 185cabdff1aSopenharmony_ci ctx->config->width, ctx->config->height, 186cabdff1aSopenharmony_ci frame->width, frame->height); 187cabdff1aSopenharmony_ci retval = AVERROR_INVALIDDATA; 188cabdff1aSopenharmony_ci goto done; 189cabdff1aSopenharmony_ci } 190cabdff1aSopenharmony_ci 191cabdff1aSopenharmony_ci if (frame->format != avctx->pix_fmt) { 192cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 193cabdff1aSopenharmony_ci "Changing pixel format during encoding is not supported. " 194cabdff1aSopenharmony_ci "(changed from %s to %s)\n", 195cabdff1aSopenharmony_ci av_get_pix_fmt_name(avctx->pix_fmt), 196cabdff1aSopenharmony_ci av_get_pix_fmt_name(frame->format)); 197cabdff1aSopenharmony_ci retval = AVERROR_INVALIDDATA; 198cabdff1aSopenharmony_ci goto done; 199cabdff1aSopenharmony_ci } 200cabdff1aSopenharmony_ci 201cabdff1aSopenharmony_ci // Allocate input picture for kvazaar. 202cabdff1aSopenharmony_ci input_pic = ctx->api->picture_alloc(frame->width, frame->height); 203cabdff1aSopenharmony_ci if (!input_pic) { 204cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Failed to allocate picture.\n"); 205cabdff1aSopenharmony_ci retval = AVERROR(ENOMEM); 206cabdff1aSopenharmony_ci goto done; 207cabdff1aSopenharmony_ci } 208cabdff1aSopenharmony_ci 209cabdff1aSopenharmony_ci // Copy pixels from frame to input_pic. 210cabdff1aSopenharmony_ci { 211cabdff1aSopenharmony_ci uint8_t *dst[4] = { 212cabdff1aSopenharmony_ci input_pic->data[0], 213cabdff1aSopenharmony_ci input_pic->data[1], 214cabdff1aSopenharmony_ci input_pic->data[2], 215cabdff1aSopenharmony_ci NULL, 216cabdff1aSopenharmony_ci }; 217cabdff1aSopenharmony_ci int dst_linesizes[4] = { 218cabdff1aSopenharmony_ci frame->width, 219cabdff1aSopenharmony_ci frame->width / 2, 220cabdff1aSopenharmony_ci frame->width / 2, 221cabdff1aSopenharmony_ci 0 222cabdff1aSopenharmony_ci }; 223cabdff1aSopenharmony_ci av_image_copy(dst, dst_linesizes, 224cabdff1aSopenharmony_ci (const uint8_t **)frame->data, frame->linesize, 225cabdff1aSopenharmony_ci frame->format, frame->width, frame->height); 226cabdff1aSopenharmony_ci } 227cabdff1aSopenharmony_ci 228cabdff1aSopenharmony_ci input_pic->pts = frame->pts; 229cabdff1aSopenharmony_ci } 230cabdff1aSopenharmony_ci 231cabdff1aSopenharmony_ci retval = ctx->api->encoder_encode(ctx->encoder, 232cabdff1aSopenharmony_ci input_pic, 233cabdff1aSopenharmony_ci &data_out, &len_out, 234cabdff1aSopenharmony_ci &recon_pic, NULL, 235cabdff1aSopenharmony_ci &frame_info); 236cabdff1aSopenharmony_ci if (!retval) { 237cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Failed to encode frame.\n"); 238cabdff1aSopenharmony_ci retval = AVERROR_INVALIDDATA; 239cabdff1aSopenharmony_ci goto done; 240cabdff1aSopenharmony_ci } else 241cabdff1aSopenharmony_ci retval = 0; /* kvazaar returns 1 on success */ 242cabdff1aSopenharmony_ci 243cabdff1aSopenharmony_ci if (data_out) { 244cabdff1aSopenharmony_ci kvz_data_chunk *chunk = NULL; 245cabdff1aSopenharmony_ci uint64_t written = 0; 246cabdff1aSopenharmony_ci 247cabdff1aSopenharmony_ci retval = ff_get_encode_buffer(avctx, avpkt, len_out, 0); 248cabdff1aSopenharmony_ci if (retval < 0) { 249cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n"); 250cabdff1aSopenharmony_ci goto done; 251cabdff1aSopenharmony_ci } 252cabdff1aSopenharmony_ci 253cabdff1aSopenharmony_ci for (chunk = data_out; chunk != NULL; chunk = chunk->next) { 254cabdff1aSopenharmony_ci av_assert0(written + chunk->len <= len_out); 255cabdff1aSopenharmony_ci memcpy(avpkt->data + written, chunk->data, chunk->len); 256cabdff1aSopenharmony_ci written += chunk->len; 257cabdff1aSopenharmony_ci } 258cabdff1aSopenharmony_ci 259cabdff1aSopenharmony_ci avpkt->pts = recon_pic->pts; 260cabdff1aSopenharmony_ci avpkt->dts = recon_pic->dts; 261cabdff1aSopenharmony_ci avpkt->flags = 0; 262cabdff1aSopenharmony_ci // IRAP VCL NAL unit types span the range 263cabdff1aSopenharmony_ci // [BLA_W_LP (16), RSV_IRAP_VCL23 (23)]. 264cabdff1aSopenharmony_ci if (frame_info.nal_unit_type >= KVZ_NAL_BLA_W_LP && 265cabdff1aSopenharmony_ci frame_info.nal_unit_type <= KVZ_NAL_RSV_IRAP_VCL23) { 266cabdff1aSopenharmony_ci avpkt->flags |= AV_PKT_FLAG_KEY; 267cabdff1aSopenharmony_ci } 268cabdff1aSopenharmony_ci 269cabdff1aSopenharmony_ci switch (frame_info.slice_type) { 270cabdff1aSopenharmony_ci case KVZ_SLICE_I: 271cabdff1aSopenharmony_ci pict_type = AV_PICTURE_TYPE_I; 272cabdff1aSopenharmony_ci break; 273cabdff1aSopenharmony_ci case KVZ_SLICE_P: 274cabdff1aSopenharmony_ci pict_type = AV_PICTURE_TYPE_P; 275cabdff1aSopenharmony_ci break; 276cabdff1aSopenharmony_ci case KVZ_SLICE_B: 277cabdff1aSopenharmony_ci pict_type = AV_PICTURE_TYPE_B; 278cabdff1aSopenharmony_ci break; 279cabdff1aSopenharmony_ci default: 280cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Unknown picture type encountered.\n"); 281cabdff1aSopenharmony_ci return AVERROR_EXTERNAL; 282cabdff1aSopenharmony_ci } 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci ff_side_data_set_encoder_stats(avpkt, frame_info.qp * FF_QP2LAMBDA, NULL, 0, pict_type); 285cabdff1aSopenharmony_ci 286cabdff1aSopenharmony_ci *got_packet_ptr = 1; 287cabdff1aSopenharmony_ci } 288cabdff1aSopenharmony_ci 289cabdff1aSopenharmony_cidone: 290cabdff1aSopenharmony_ci ctx->api->picture_free(input_pic); 291cabdff1aSopenharmony_ci ctx->api->picture_free(recon_pic); 292cabdff1aSopenharmony_ci ctx->api->chunk_free(data_out); 293cabdff1aSopenharmony_ci return retval; 294cabdff1aSopenharmony_ci} 295cabdff1aSopenharmony_ci 296cabdff1aSopenharmony_cistatic const enum AVPixelFormat pix_fmts[] = { 297cabdff1aSopenharmony_ci AV_PIX_FMT_YUV420P, 298cabdff1aSopenharmony_ci AV_PIX_FMT_NONE 299cabdff1aSopenharmony_ci}; 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(LibkvazaarContext, x) 302cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM 303cabdff1aSopenharmony_cistatic const AVOption options[] = { 304cabdff1aSopenharmony_ci { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of key=value pairs.", 305cabdff1aSopenharmony_ci OFFSET(kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE }, 306cabdff1aSopenharmony_ci { NULL }, 307cabdff1aSopenharmony_ci}; 308cabdff1aSopenharmony_ci 309cabdff1aSopenharmony_cistatic const AVClass class = { 310cabdff1aSopenharmony_ci .class_name = "libkvazaar", 311cabdff1aSopenharmony_ci .item_name = av_default_item_name, 312cabdff1aSopenharmony_ci .option = options, 313cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 314cabdff1aSopenharmony_ci}; 315cabdff1aSopenharmony_ci 316cabdff1aSopenharmony_cistatic const FFCodecDefault defaults[] = { 317cabdff1aSopenharmony_ci { "b", "0" }, 318cabdff1aSopenharmony_ci { NULL }, 319cabdff1aSopenharmony_ci}; 320cabdff1aSopenharmony_ci 321cabdff1aSopenharmony_ciconst FFCodec ff_libkvazaar_encoder = { 322cabdff1aSopenharmony_ci .p.name = "libkvazaar", 323cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("libkvazaar H.265 / HEVC"), 324cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 325cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_HEVC, 326cabdff1aSopenharmony_ci .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | 327cabdff1aSopenharmony_ci AV_CODEC_CAP_OTHER_THREADS, 328cabdff1aSopenharmony_ci .p.pix_fmts = pix_fmts, 329cabdff1aSopenharmony_ci 330cabdff1aSopenharmony_ci .p.priv_class = &class, 331cabdff1aSopenharmony_ci .priv_data_size = sizeof(LibkvazaarContext), 332cabdff1aSopenharmony_ci .defaults = defaults, 333cabdff1aSopenharmony_ci 334cabdff1aSopenharmony_ci .init = libkvazaar_init, 335cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(libkvazaar_encode), 336cabdff1aSopenharmony_ci .close = libkvazaar_close, 337cabdff1aSopenharmony_ci 338cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | 339cabdff1aSopenharmony_ci FF_CODEC_CAP_AUTO_THREADS, 340cabdff1aSopenharmony_ci 341cabdff1aSopenharmony_ci .p.wrapper_name = "libkvazaar", 342cabdff1aSopenharmony_ci}; 343