Commit 61bdd53f for libheif
commit 61bdd53f6d9779acdd88b2c25139730923671a11
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri May 15 18:18:16 2026 +0200
aom encoder: auto-select IQ mode if possible (#1797, #1725)
diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc
index 6e58a8aa..0fa47fb6 100644
--- a/libheif/plugins/encoder_aom.cc
+++ b/libheif/plugins/encoder_aom.cc
@@ -93,6 +93,7 @@ struct encoder_struct_aom
#endif
aom_tune_metric tune;
+ bool tune_auto = true;
heif_chroma chroma = heif_chroma_420;
@@ -193,7 +194,7 @@ static const char* const kParam_chroma_valid_values[] = {
static const char* kParam_tune = "tune";
static const char* const kParam_tune_valid_values[] = {
- "psnr", "ssim", "iq", nullptr
+ "auto", "psnr", "ssim", "iq", nullptr
};
static const int AOM_PLUGIN_PRIORITY = 60;
@@ -310,7 +311,7 @@ static void aom_init_parameters()
p->version = 2;
p->name = kParam_tune;
p->type = heif_encoder_parameter_type_string;
- p->string.default_value = "ssim";
+ p->string.default_value = "auto";
p->has_default = true;
p->string.valid_values = kParam_tune_valid_values;
d[i++] = p++;
@@ -663,17 +664,24 @@ heif_error aom_set_parameter_string(void* encoder_raw, const char* name, const c
}
if (strcmp(name, kParam_tune) == 0) {
- if (strcmp(value, "psnr") == 0) {
+ if (strcmp(value, "auto") == 0) {
+ encoder->tune_auto = true;
+ return heif_error_ok;
+ }
+ else if (strcmp(value, "psnr") == 0) {
encoder->tune = AOM_TUNE_PSNR;
+ encoder->tune_auto = false;
return heif_error_ok;
}
else if (strcmp(value, "ssim") == 0) {
encoder->tune = AOM_TUNE_SSIM;
+ encoder->tune_auto = false;
return heif_error_ok;
}
#if defined(AOM_HAVE_TUNE_IQ)
else if (strcmp(value, "iq") == 0) {
encoder->tune = AOM_TUNE_IQ;
+ encoder->tune_auto = false;
return heif_error_ok;
}
#endif
@@ -723,6 +731,10 @@ heif_error aom_get_parameter_string(void* encoder_raw, const char* name,
return heif_error_ok;
}
else if (strcmp(name, kParam_tune) == 0) {
+ if (encoder->tune_auto) {
+ save_strcpy(value, value_size, "auto");
+ return heif_error_ok;
+ }
switch (encoder->tune) {
case AOM_TUNE_PSNR:
save_strcpy(value, value_size, "psnr");
@@ -1077,7 +1089,37 @@ static heif_error aom_start_sequence_encoding_intern(void* encoder_raw, const he
aom_error = aom_codec_control(&codec, AV1E_SET_TRANSFER_CHARACTERISTICS, nclx->transfer_characteristics); CHECK_ERROR;
}
- aom_error = aom_codec_control(&codec, AOME_SET_TUNING, encoder->tune); CHECK_ERROR;
+ aom_tune_metric effective_tune = encoder->tune;
+ if (encoder->tune_auto) {
+ if (input_class == heif_image_input_class_alpha) {
+ // AOM_TUNE_SSIM causes ringing on alpha; PSNR avoids that.
+ effective_tune = AOM_TUNE_PSNR;
+ }
+ else {
+ effective_tune = AOM_TUNE_SSIM;
+#if defined(AOM_HAVE_TUNE_IQ)
+ // AOM_TUNE_IQ is tuned for the YCbCr family of color spaces (and other YUV-like
+ // spaces such as YCgCo, ICtCp, including monochrome). It does NOT generalize to
+ // GBR samples (matrix_coefficients = IDENTITY), so we keep SSIM for that case.
+ // AOM_TUNE_IQ stabilized in libaom v3.13.0 (all-intra only); v3.14.0 added
+ // support for the good-quality and realtime inter-frame modes.
+
+ static const int aom_version_3_13_0 = (3 << 16) | (13 << 8);
+ static const int aom_version_3_14_0 = (3 << 16) | (14 << 8);
+
+ bool is_identity_matrix = nclx && (nclx->matrix_coefficients == heif_matrix_coefficients_RGB_GBR);
+ int aom_version = aom_codec_version();
+ bool iq_supports_inter = (aom_version >= aom_version_3_14_0);
+
+ if (!is_identity_matrix &&
+ (cfg.g_usage == AOM_USAGE_ALL_INTRA || iq_supports_inter) &&
+ aom_version >= aom_version_3_13_0) {
+ effective_tune = AOM_TUNE_IQ;
+ }
+#endif
+ }
+ }
+ aom_error = aom_codec_control(&codec, AOME_SET_TUNING, effective_tune); CHECK_ERROR;
if (encoder->lossless || (input_class == heif_image_input_class_alpha && encoder->lossless_alpha)) {
aom_error = aom_codec_control(&codec, AV1E_SET_LOSSLESS, 1); CHECK_ERROR;