- lavc/aomenc: Force default qmax of 0 if crf was set to 0 - avcodec/libaomenc: Avoid copying data, allow user-supplied buffers - lavc/libaomenc: Show encoder config as a warning in case of failed initialization - avcodec/libaomenc: use ctx->usage to get default cfg - avcodec/libaomenc: remove the redundant initialization - avcodec/libaomenc: Add unmet target level warning - avcodec/libaomenc: Expose the allintra usage mode - avcodec/libaomenc: Get number of operating points Index: libavcodec/libaomenc.c --- libavcodec/libaomenc.c.orig +++ libavcodec/libaomenc.c @@ -36,6 +36,7 @@ #include "av1.h" #include "avcodec.h" +#include "encode.h" #include "internal.h" #include "packet_internal.h" #include "profiles.h" @@ -194,6 +195,15 @@ static const char *const ctlidstr[] = { [AV1E_SET_ENABLE_SMOOTH_INTERINTRA] = "AV1E_SET_ENABLE_SMOOTH_INTERINTRA", [AV1E_SET_ENABLE_REF_FRAME_MVS] = "AV1E_SET_ENABLE_REF_FRAME_MVS", #endif +#ifdef AOM_CTRL_AV1E_GET_NUM_OPERATING_POINTS + [AV1E_GET_NUM_OPERATING_POINTS] = "AV1E_GET_NUM_OPERATING_POINTS", +#endif +#ifdef AOM_CTRL_AV1E_GET_SEQ_LEVEL_IDX + [AV1E_GET_SEQ_LEVEL_IDX] = "AV1E_GET_SEQ_LEVEL_IDX", +#endif +#ifdef AOM_CTRL_AV1E_GET_TARGET_SEQ_LEVEL_IDX + [AV1E_GET_TARGET_SEQ_LEVEL_IDX] = "AV1E_GET_TARGET_SEQ_LEVEL_IDX", +#endif }; static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc) @@ -208,10 +218,10 @@ static av_cold void log_encoder_error(AVCodecContext * } static av_cold void dump_enc_cfg(AVCodecContext *avctx, - const struct aom_codec_enc_cfg *cfg) + const struct aom_codec_enc_cfg *cfg, + int level) { int width = -30; - int level = AV_LOG_DEBUG; av_log(avctx, level, "aom_codec_enc_cfg\n"); av_log(avctx, level, "generic settings\n" @@ -319,10 +329,73 @@ static av_cold int codecctl_int(AVCodecContext *avctx, return 0; } +#if defined(AOM_CTRL_AV1E_GET_NUM_OPERATING_POINTS) && \ + defined(AOM_CTRL_AV1E_GET_SEQ_LEVEL_IDX) && \ + defined(AOM_CTRL_AV1E_GET_TARGET_SEQ_LEVEL_IDX) +static av_cold int codecctl_intp(AVCodecContext *avctx, +#ifdef UENUM1BYTE + aome_enc_control_id id, +#else + enum aome_enc_control_id id, +#endif + int* ptr) +{ + AOMContext *ctx = avctx->priv_data; + char buf[80]; + int width = -30; + int res; + + snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]); + av_log(avctx, AV_LOG_DEBUG, " %*s%d\n", width, buf, *ptr); + + res = aom_codec_control(&ctx->encoder, id, ptr); + if (res != AOM_CODEC_OK) { + snprintf(buf, sizeof(buf), "Failed to set %s codec control", + ctlidstr[id]); + log_encoder_error(avctx, buf); + return AVERROR(EINVAL); + } + + return 0; +} +#endif + static av_cold int aom_free(AVCodecContext *avctx) { AOMContext *ctx = avctx->priv_data; +#if defined(AOM_CTRL_AV1E_GET_NUM_OPERATING_POINTS) && \ + defined(AOM_CTRL_AV1E_GET_SEQ_LEVEL_IDX) && \ + defined(AOM_CTRL_AV1E_GET_TARGET_SEQ_LEVEL_IDX) + if (!(avctx->flags & AV_CODEC_FLAG_PASS1)) { + int num_operating_points; + int levels[32]; + int target_levels[32]; + + if (!codecctl_intp(avctx, AV1E_GET_NUM_OPERATING_POINTS, + &num_operating_points) && + !codecctl_intp(avctx, AV1E_GET_SEQ_LEVEL_IDX, levels) && + !codecctl_intp(avctx, AV1E_GET_TARGET_SEQ_LEVEL_IDX, + target_levels)) { + for (int i = 0; i < num_operating_points; i++) { + if (levels[i] > target_levels[i]) { + // Warn when the target level was not met + av_log(avctx, AV_LOG_WARNING, + "Could not encode to target level %d.%d for " + "operating point %d. The output level is %d.%d.\n", + 2 + (target_levels[i] >> 2), target_levels[i] & 3, + i, 2 + (levels[i] >> 2), levels[i] & 3); + } else if (target_levels[i] < 31) { + // Log the encoded level if a target level was given + av_log(avctx, AV_LOG_INFO, + "Output level for operating point %d is %d.%d.\n", + i, 2 + (levels[i] >> 2), levels[i] & 3); + } + } + } + } +#endif + aom_codec_destroy(&ctx->encoder); av_freep(&ctx->twopass_stats.buf); av_freep(&avctx->stats_out); @@ -596,7 +669,7 @@ static av_cold int aom_init(AVCodecContext *avctx, av_log(avctx, AV_LOG_INFO, "%s\n", aom_codec_version_str()); av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_build_config()); - if ((res = aom_codec_enc_config_default(iface, &enccfg, 0)) != AOM_CODEC_OK) { + if ((res = aom_codec_enc_config_default(iface, &enccfg, ctx->usage)) != AOM_CODEC_OK) { av_log(avctx, AV_LOG_ERROR, "Failed to get config: %s\n", aom_codec_err_to_string(res)); return AVERROR(EINVAL); @@ -611,7 +684,7 @@ static av_cold int aom_init(AVCodecContext *avctx, return AVERROR(EINVAL); } - dump_enc_cfg(avctx, &enccfg); + dump_enc_cfg(avctx, &enccfg, AV_LOG_DEBUG); enccfg.g_w = avctx->width; enccfg.g_h = avctx->height; @@ -620,8 +693,6 @@ static av_cold int aom_init(AVCodecContext *avctx, enccfg.g_threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 64); - enccfg.g_usage = ctx->usage; - if (ctx->lag_in_frames >= 0) enccfg.g_lag_in_frames = ctx->lag_in_frames; @@ -654,8 +725,11 @@ static av_cold int aom_init(AVCodecContext *avctx, if (avctx->qmin >= 0) enccfg.rc_min_quantizer = avctx->qmin; - if (avctx->qmax >= 0) + if (avctx->qmax >= 0) { enccfg.rc_max_quantizer = avctx->qmax; + } else if (!ctx->crf) { + enccfg.rc_max_quantizer = 0; + } if (enccfg.rc_end_usage == AOM_CQ || enccfg.rc_end_usage == AOM_Q) { if (ctx->crf < enccfg.rc_min_quantizer || ctx->crf > enccfg.rc_max_quantizer) { @@ -742,13 +816,14 @@ static av_cold int aom_init(AVCodecContext *avctx, if (res < 0) return res; - dump_enc_cfg(avctx, &enccfg); /* Construct Encoder Context */ res = aom_codec_enc_init(&ctx->encoder, iface, &enccfg, flags); if (res != AOM_CODEC_OK) { + dump_enc_cfg(avctx, &enccfg, AV_LOG_WARNING); log_encoder_error(avctx, "Failed to initialize encoder"); return AVERROR(EINVAL); } + dump_enc_cfg(avctx, &enccfg, AV_LOG_DEBUG); // codec control failures are currently treated only as warnings av_log(avctx, AV_LOG_DEBUG, "aom_codec_control\n"); @@ -943,7 +1018,6 @@ static inline void cx_pktcpy(AOMContext *ctx, dst->sz = src->data.frame.sz; dst->buf = src->data.frame.buf; #ifdef AOM_FRAME_IS_INTRAONLY - dst->have_sse = 0; dst->frame_number = ++ctx->frame_number; dst->have_sse = ctx->have_sse; if (ctx->have_sse) { @@ -967,7 +1041,7 @@ static int storeframe(AVCodecContext *avctx, struct Fr { AOMContext *ctx = avctx->priv_data; int av_unused pict_type; - int ret = ff_alloc_packet2(avctx, pkt, cx_frame->sz, 0); + int ret = ff_get_encode_buffer(avctx, pkt, cx_frame->sz, 0); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error getting output packet of size %"SIZE_SPECIFIER".\n", cx_frame->sz); @@ -1282,6 +1356,7 @@ static const AVOption options[] = { { "usage", "Quality and compression efficiency vs speed trade-off", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "usage"}, { "good", "Good quality", 0, AV_OPT_TYPE_CONST, {.i64 = 0 /* AOM_USAGE_GOOD_QUALITY */}, 0, 0, VE, "usage"}, { "realtime", "Realtime encoding", 0, AV_OPT_TYPE_CONST, {.i64 = 1 /* AOM_USAGE_REALTIME */}, 0, 0, VE, "usage"}, + { "allintra", "All Intra encoding", 0, AV_OPT_TYPE_CONST, {.i64 = 2 /* AOM_USAGE_ALL_INTRA */}, 0, 0, VE, "usage"}, { "tune", "The metric that the encoder tunes for. Automatically chosen by the encoder by default", OFFSET(tune), AV_OPT_TYPE_INT, {.i64 = -1}, -1, AOM_TUNE_SSIM, VE, "tune"}, { "psnr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_PSNR}, 0, 0, VE, "tune"}, { "ssim", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_SSIM}, 0, 0, VE, "tune"}, @@ -1341,11 +1416,12 @@ AVCodec ff_libaom_av1_encoder = { .long_name = NULL_IF_CONFIG_SMALL("libaom AV1"), .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_AV1, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_OTHER_THREADS, .priv_data_size = sizeof(AOMContext), .init = av1_init, .encode2 = aom_encode, .close = aom_free, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, .caps_internal = FF_CODEC_CAP_AUTO_THREADS, .profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), .priv_class = &class_aom,