diff --git a/media/ffvpx/libavcodec/libaomenc.c b/media/ffvpx/libavcodec/libaomenc.c index 0f7571ee7a1ba..a116989b1f4bc 100644 --- a/media/ffvpx/libavcodec/libaomenc.c +++ b/media/ffvpx/libavcodec/libaomenc.c @@ -30,6 +30,7 @@ #include #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "libavutil/base64.h" #include "libavutil/common.h" #include "libavutil/cpu.h" @@ -137,6 +138,7 @@ typedef struct AOMEncoderContext { int enable_diff_wtd_comp; int enable_dist_wtd_comp; int enable_dual_filter; + AVDictionary *svc_parameters; AVDictionary *aom_params; } AOMContext; @@ -201,6 +203,7 @@ static const char *const ctlidstr[] = { [AV1E_GET_TARGET_SEQ_LEVEL_IDX] = "AV1E_GET_TARGET_SEQ_LEVEL_IDX", #endif [AV1_GET_NEW_FRAME_IMAGE] = "AV1_GET_NEW_FRAME_IMAGE", + [AV1E_SET_SVC_PARAMS] = "AV1E_SET_SVC_PARAMS", }; static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc) @@ -382,6 +385,31 @@ static av_cold int codecctl_imgp(AVCodecContext *avctx, return 0; } +static av_cold int codecctl_svcp(AVCodecContext *avctx, +#ifdef UENUM1BYTE + aome_enc_control_id id, +#else + enum aome_enc_control_id id, +#endif + aom_svc_params_t *svc_params) +{ + AOMContext *ctx = avctx->priv_data; + char buf[80]; + int res; + + snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]); + + res = aom_codec_control(&ctx->encoder, id, svc_params); + if (res != AOM_CODEC_OK) { + snprintf(buf, sizeof(buf), "Failed to get %s codec control", + ctlidstr[id]); + log_encoder_error(avctx, buf); + return AVERROR(EINVAL); + } + + return 0; +} + static av_cold int aom_free(AVCodecContext *avctx) { AOMContext *ctx = avctx->priv_data; @@ -428,6 +456,18 @@ static av_cold int aom_free(AVCodecContext *avctx) return 0; } +static void aom_svc_parse_int_array(int *dest, char *value, int max_entries) +{ + int dest_idx = 0; + char *saveptr = NULL; + char *token = av_strtok(value, ",", &saveptr); + + while (token && dest_idx < max_entries) { + dest[dest_idx++] = strtoul(token, NULL, 10); + token = av_strtok(NULL, ",", &saveptr); + } +} + static int set_pix_fmt(AVCodecContext *avctx, aom_codec_caps_t codec_caps, struct aom_codec_enc_cfg *enccfg, aom_codec_flags_t *flags, aom_img_fmt_t *img_fmt) @@ -968,6 +1008,41 @@ static av_cold int aom_init(AVCodecContext *avctx, if (ctx->enable_intrabc >= 0) codecctl_int(avctx, AV1E_SET_ENABLE_INTRABC, ctx->enable_intrabc); + if (enccfg.rc_end_usage == AOM_CBR) { + aom_svc_params_t svc_params = {}; + svc_params.framerate_factor[0] = 1; + svc_params.number_spatial_layers = 1; + svc_params.number_temporal_layers = 1; + + AVDictionaryEntry *en = NULL; + while ((en = av_dict_get(ctx->svc_parameters, "", en, AV_DICT_IGNORE_SUFFIX))) { + if (!strlen(en->value)) + return AVERROR(EINVAL); + + if (!strcmp(en->key, "number_spatial_layers")) + svc_params.number_spatial_layers = strtoul(en->value, NULL, 10); + else if (!strcmp(en->key, "number_temporal_layers")) + svc_params.number_temporal_layers = strtoul(en->value, NULL, 10); + else if (!strcmp(en->key, "max_quantizers")) + aom_svc_parse_int_array(svc_params.max_quantizers, en->value, AOM_MAX_LAYERS); + else if (!strcmp(en->key, "min_quantizers")) + aom_svc_parse_int_array(svc_params.min_quantizers, en->value, AOM_MAX_LAYERS); + else if (!strcmp(en->key, "scaling_factor_num")) + aom_svc_parse_int_array(svc_params.scaling_factor_num, en->value, AOM_MAX_SS_LAYERS); + else if (!strcmp(en->key, "scaling_factor_den")) + aom_svc_parse_int_array(svc_params.scaling_factor_den, en->value, AOM_MAX_SS_LAYERS); + else if (!strcmp(en->key, "layer_target_bitrate")) + aom_svc_parse_int_array(svc_params.layer_target_bitrate, en->value, AOM_MAX_LAYERS); + else if (!strcmp(en->key, "framerate_factor")) + aom_svc_parse_int_array(svc_params.framerate_factor, en->value, AOM_MAX_TS_LAYERS); + } + + res = codecctl_svcp(avctx, AV1E_SET_SVC_PARAMS, &svc_params); + if (res < 0) + return res; + } + + #if AOM_ENCODER_ABI_VERSION >= 23 { const AVDictionaryEntry *en = NULL; @@ -1310,6 +1385,25 @@ FF_ENABLE_DEPRECATION_WARNINGS if (frame->pict_type == AV_PICTURE_TYPE_I) flags |= AOM_EFLAG_FORCE_KF; + + AVDictionaryEntry* en = av_dict_get(frame->metadata, "temporal_id", NULL, 0); + if (en) { + aom_svc_layer_id_t layer_id = {}; + layer_id.temporal_layer_id = strtoul(en->value, NULL, 10); + av_log(avctx, AV_LOG_DEBUG, "Frame pts % " PRId64 " temporal layer id %d", + frame->pts, layer_id.temporal_layer_id); + if (!ctx->svc_parameters) { + av_log(avctx, AV_LOG_WARNING, + "Temporal SVC not enabled, but temporal layer id received."); + } + res = aom_codec_control(&ctx->encoder, AV1E_SET_SVC_LAYER_ID, &layer_id); + if (res != AOM_CODEC_OK) { + av_log(avctx, AV_LOG_ERROR, + "Error setting temporal layer id %d on frame pts % " PRId64 "\n", + layer_id.temporal_layer_id, frame->pts); + return res; + } + } } res = aom_codec_encode(&ctx->encoder, rawimg, timestamp, duration, flags); @@ -1538,6 +1632,7 @@ static const AVOption options[] = { { "enable-masked-comp", "Enable masked compound", OFFSET(enable_masked_comp), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, { "enable-interintra-comp", "Enable interintra compound", OFFSET(enable_interintra_comp), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, { "enable-smooth-interintra", "Enable smooth interintra mode", OFFSET(enable_smooth_interintra), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, + { "svc-parameters", "SVC configuration using a :-separated list of key=value parameters (only applied in CBR mode)", OFFSET(svc_parameters), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE}, #if AOM_ENCODER_ABI_VERSION >= 23 { "aom-params", "Set libaom options using a :-separated list of key=value pairs", OFFSET(aom_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, #endif