@@ -69,6 +69,7 @@ struct avifCodecInternal
6969 aom_img_fmt_t aomFormat ;
7070 uint32_t currentLayer ;
7171 int qualityFirstLayer ;
72+ avifBool previousFrameUsedTuneIq ;
7273#endif
7374};
7475
@@ -403,16 +404,21 @@ static avifBool avifProcessAOMOptionsPreInit(avifCodec * codec, avifBool alpha,
403404 return AVIF_TRUE ;
404405}
405406
406- static avifBool avifImageUsesTuneIq (const avifCodec * codec , avifBool alpha )
407+ static avifBool avifImageUsesTuneIq (const avifCodec * codec , avifBool alpha , avifBool useLibavifDefaultTuneMetric , aom_tune_metric libavifDefaultTuneMetric )
407408{
408- avifBool ret = AVIF_FALSE ;
409-
410409#if !defined(AOM_HAVE_TUNE_IQ )
411410// Define the tune IQ value here if libaom doesn't define it. The enum value is guaranteed to never change
412411// in libaom, so this definition won't ever get out of sync.
413412#define AOM_TUNE_IQ 10
414413#endif
415414
415+ if (useLibavifDefaultTuneMetric && libavifDefaultTuneMetric == AOM_TUNE_IQ ) {
416+ return AVIF_TRUE ;
417+ }
418+
419+ avifBool useTuneIq = AVIF_FALSE ;
420+ avifBool isAnyTuneDefined = AVIF_FALSE ;
421+
416422 // Tune IQ string -> enum mapping
417423 static const struct aomOptionEnumList tuneIqEnum [] = { { "iq" , AOM_TUNE_IQ }, // Image Quality (IQ) mode
418424 { NULL , 0 } };
@@ -424,15 +430,20 @@ static avifBool avifImageUsesTuneIq(const avifCodec * codec, avifBool alpha)
424430 // For consistent behavior, handle both cases where tune IQ was either specified as a string (tune=iq),
425431 // or as an enum value (tune=10).
426432 if (avifKeyEqualsName (entry -> key , "tune" , alpha ) && aomOptionParseEnum (entry -> value , tuneIqEnum , & val )) {
427- ret = (val == AOM_TUNE_IQ );
433+ isAnyTuneDefined = AVIF_TRUE ;
434+ useTuneIq = (val == AOM_TUNE_IQ );
428435 }
429436 }
430437
431- // In practice this function should also return true if avifEncoderSetCodecSpecificOption("tune", "iq")
432- // was called for a previous frame and not called (or called with NULL) for this frame, because the tune
433- // option persists across frames in libaom. However AOM_TUNE_IQ is only supported with still images in
434- // libavif and libaom as of today, so there is no need to remember this option across frames.
435- return ret ;
438+ if (!isAnyTuneDefined && codec -> internal -> previousFrameUsedTuneIq ) {
439+ // Handle the case where the encoder was called with avifEncoderSetCodecSpecificOption("tune", "iq")
440+ // for a previous frame and not called (or called with NULL) for this frame, because the tune
441+ // option persists across frames in libaom.
442+ // In this case, we know libaom will also use tune=iq for this frame.
443+ return AVIF_TRUE ;
444+ }
445+
446+ return useTuneIq ;
436447}
437448
438449#if !defined(HAVE_AOM_CODEC_SET_OPTION )
@@ -725,23 +736,32 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
725736
726737 avifBool useLibavifDefaultTuneMetric = AVIF_FALSE ; // If true, override libaom's default tune option.
727738 aom_tune_metric libavifDefaultTuneMetric = AOM_TUNE_PSNR ; // Meaningless unless useLibavifDefaultTuneMetric.
728- if (quality != AVIF_QUALITY_LOSSLESS && !avifAOMOptionsContainExplicitTuning (codec , alpha )) {
729- useLibavifDefaultTuneMetric = AVIF_TRUE ;
730- if (alpha ) {
731- // Minimize ringing for alpha.
732- libavifDefaultTuneMetric = AOM_TUNE_PSNR ;
733- } else {
734- libavifDefaultTuneMetric = AOM_TUNE_SSIM ;
739+
740+ // libavif only needs to set the default tune metric for the first frame
741+ if (!codec -> internal -> encoderInitialized ) {
742+ if (quality != AVIF_QUALITY_LOSSLESS && !avifAOMOptionsContainExplicitTuning (codec , alpha )) {
743+ useLibavifDefaultTuneMetric = AVIF_TRUE ;
744+ if (alpha ) {
745+ // Minimize ringing for alpha.
746+ libavifDefaultTuneMetric = AOM_TUNE_PSNR ;
747+ } else {
748+ libavifDefaultTuneMetric = AOM_TUNE_SSIM ;
749+ }
735750 }
751+ codec -> internal -> previousFrameUsedTuneIq = AVIF_FALSE ;
736752 }
737753
738754 struct aom_codec_enc_cfg * cfg = & codec -> internal -> cfg ;
739755 avifBool quantizerUpdated = AVIF_FALSE ;
740756 // True if libavif knows that tune=iq is used, either by default by libavif, or explicitly set by the user.
741757 // False otherwise (including if libaom uses tune=iq by default, which is not the case as of v3.13.1 and earlier versions).
742- const avifBool useTuneIq = useLibavifDefaultTuneMetric ? libavifDefaultTuneMetric == AOM_TUNE_IQ : avifImageUsesTuneIq (codec , alpha );
758+ const avifBool useTuneIq = avifImageUsesTuneIq (codec , alpha , useLibavifDefaultTuneMetric , libavifDefaultTuneMetric );
743759 const int quantizer = aomQualityToQuantizer (quality , useTuneIq );
744760
761+ // libavif needs to know whether the current frame uses tune=iq for the next frame, as libaom persists
762+ // tuning modes across frames
763+ codec -> internal -> previousFrameUsedTuneIq = useTuneIq ;
764+
745765 // For encoder->scalingMode.horizontal and encoder->scalingMode.vertical to take effect in AOM
746766 // encoder, config should be applied for each frame, so we don't care about changes on these
747767 // two fields.
0 commit comments