Skip to content

Commit 8b18345

Browse files
only allow quantizer and tileRows/Cols to change
1 parent bcc47ea commit 8b18345

6 files changed

Lines changed: 184 additions & 116 deletions

File tree

include/avif/avif.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,8 +1058,8 @@ struct avifCodecSpecificOptions;
10581058
// image in less bytes. AVIF_SPEED_DEFAULT means "Leave the AV1 codec to its default speed settings"./
10591059
// If avifEncoder uses rav1e, the speed value is directly passed through (0-10). If libaom is used,
10601060
// a combination of settings are tweaked to simulate this speed range.
1061-
// * AV1 encoder settings and codec specific options set by avifEncoderSetCodecSpecificOption()
1062-
// will be applied / updated to AV1 encoder before each call to avifEncoderAddImage().
1061+
// * Some encoder settings can be changed. Changes will take effect from next call to
1062+
// avifEncoderAddImage().
10631063
typedef struct avifEncoder
10641064
{
10651065
// Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c)
@@ -1068,15 +1068,15 @@ typedef struct avifEncoder
10681068
// settings (see Notes above)
10691069
int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default).
10701070
uint64_t timescale; // timescale of the media (Hz)
1071-
// AV1 encoder settings.
10721071
int maxThreads;
1072+
int speed;
1073+
// changeable encoder settings.
10731074
int minQuantizer;
10741075
int maxQuantizer;
10751076
int minQuantizerAlpha;
10761077
int maxQuantizerAlpha;
10771078
int tileRowsLog2;
10781079
int tileColsLog2;
1079-
int speed;
10801080

10811081
// stats from the most recent write
10821082
avifIOStats ioStats;
@@ -1129,9 +1129,9 @@ AVIF_API avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
11291129
AVIF_API avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output);
11301130

11311131
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
1132-
// to be sent to codec at the next avifEncoderAddImage() call.
1133-
// See the codec documentation to know if a setting is permanent or applied only to the next frame.
1134-
// key must be non-NULL, but passing a NULL value will delete that pending key, if it exists.
1132+
// to be consumed by the codec at the next avifEncoderAddImage() call.
1133+
// See the codec documentation to know if a setting is persistent or applied only to the next frame.
1134+
// key must be non-NULL, but passing a NULL value will delete the pending key, if it exists.
11351135
// Setting an incorrect or unknown option for the current codec will cause errors of type
11361136
// AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION from avifEncoderWrite() or avifEncoderAddImage().
11371137
AVIF_API void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value);

include/avif/internal.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,18 @@ void avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const cha
254254
struct avifCodec;
255255
struct avifCodecInternal;
256256

257+
typedef enum avifEncoderConfig
258+
{
259+
AVIF_ENCODER_CONFIG_MIN_QUANTIZER = (1 << 0),
260+
AVIF_ENCODER_CONFIG_MAX_QUANTIZER = (1 << 1),
261+
AVIF_ENCODER_CONFIG_MIN_QUANTIZER_ALPHA = (1 << 2),
262+
AVIF_ENCODER_CONFIG_MAX_QUANTIZER_ALPHA = (1 << 3),
263+
AVIF_ENCODER_CONFIG_TILE_ROWS_LOG_2 = (1 << 4),
264+
AVIF_ENCODER_CONFIG_TILE_COLS_LOG_2 = (1 << 5),
265+
266+
AVIF_ENCODER_CONFIG_CODEC_SPECIFIC = (1 << 31)
267+
} avifEncoderConfig;
268+
257269
typedef avifBool (*avifCodecGetNextImageFunc)(struct avifCodec * codec,
258270
struct avifDecoder * decoder,
259271
const avifDecodeSample * sample,
@@ -268,7 +280,7 @@ typedef avifResult (*avifCodecEncodeImageFunc)(struct avifCodec * codec,
268280
avifEncoder * encoder,
269281
const avifImage * image,
270282
avifBool alpha,
271-
avifBool updateConfig,
283+
avifEncoderConfig updateConfig,
272284
avifAddImageFlags addImageFlags,
273285
avifCodecEncodeOutput * output);
274286
typedef avifBool (*avifCodecEncodeFinishFunc)(struct avifCodec * codec, avifCodecEncodeOutput * output);

src/codec_aom.c

Lines changed: 117 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,17 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
527527
avifEncoder * encoder,
528528
const avifImage * image,
529529
avifBool alpha,
530-
avifBool updateConfig,
530+
avifEncoderConfig updatedConfig,
531531
avifAddImageFlags addImageFlags,
532532
avifCodecEncodeOutput * output)
533533
{
534-
if (!codec->internal->encoderInitialized || updateConfig) {
534+
struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
535+
aom_codec_iface_t * encoderInterface = NULL;
536+
unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY;
537+
int aomCpuUsed = -1;
538+
avifBool lossless = AVIF_FALSE;
539+
540+
if (!codec->internal->encoderInitialized) {
535541
// Map encoder speed to AOM usage + CpuUsed:
536542
// Speed 0: GoodQuality CpuUsed 0
537543
// Speed 1: GoodQuality CpuUsed 1
@@ -544,15 +550,13 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
544550
// Speed 8: RealTime CpuUsed 8
545551
// Speed 9: RealTime CpuUsed 9
546552
// Speed 10: RealTime CpuUsed 9
547-
unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY;
548553
// Use the new AOM_USAGE_ALL_INTRA (added in https://crbug.com/aomedia/2959) for still
549554
// image encoding if it is available.
550555
#if defined(AOM_USAGE_ALL_INTRA)
551556
if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) {
552557
aomUsage = AOM_USAGE_ALL_INTRA;
553558
}
554559
#endif
555-
int aomCpuUsed = -1;
556560
if (encoder->speed != AVIF_SPEED_DEFAULT) {
557561
aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9);
558562
if (aomCpuUsed >= 7) {
@@ -589,50 +593,19 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
589593
}
590594
}
591595

592-
struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
593-
594-
aom_codec_iface_t * encoderInterface = NULL;
595-
if (!codec->internal->encoderInitialized) {
596-
codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha);
597-
if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) {
598-
return AVIF_RESULT_UNKNOWN_ERROR;
599-
}
600-
601-
avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo);
602-
603-
encoderInterface = aom_codec_av1_cx();
604-
aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage);
605-
if (err != AOM_CODEC_OK) {
606-
avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err));
607-
return AVIF_RESULT_UNKNOWN_ERROR;
608-
}
609-
} else {
610-
// aomUsage was taken into account by aom_codec_enc_config_default() but it can only be manually updated afterwards.
611-
cfg->g_usage = aomUsage;
596+
codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha);
597+
if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) {
598+
return AVIF_RESULT_UNKNOWN_ERROR;
612599
}
613600

614-
if (!codec->internal->endUsageSet) {
615-
// Set our own default cfg->rc_end_usage value, which may differ from libaom's default.
616-
switch (aomUsage) {
617-
case AOM_USAGE_GOOD_QUALITY:
618-
// libaom's default is AOM_VBR. Change the default to AOM_Q since we don't need to
619-
// hit a certain target bit rate. It's easier to control the worst quality in Q
620-
// mode.
621-
cfg->rc_end_usage = AOM_Q;
622-
break;
623-
case AOM_USAGE_REALTIME:
624-
// For real-time mode we need to use CBR rate control mode. AOM_Q doesn't fit the
625-
// rate control requirements for real-time mode. CBR does.
626-
cfg->rc_end_usage = AOM_CBR;
627-
break;
628-
#if defined(AOM_USAGE_ALL_INTRA)
629-
case AOM_USAGE_ALL_INTRA:
630-
cfg->rc_end_usage = AOM_Q;
631-
break;
632-
#endif
633-
}
634-
}
601+
avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo);
635602

603+
encoderInterface = aom_codec_av1_cx();
604+
aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage);
605+
if (err != AOM_CODEC_OK) {
606+
avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err));
607+
return AVIF_RESULT_UNKNOWN_ERROR;
608+
}
636609
// Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only.
637610
// Profile 1. 8-bit and 10-bit 4:4:4
638611
// Profile 2. 8-bit and 10-bit 4:2:2
@@ -671,8 +644,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
671644
cfg->g_profile = seqProfile;
672645
cfg->g_bit_depth = image->depth;
673646
cfg->g_input_bit_depth = image->depth;
674-
cfg->g_w = image->width;
675-
cfg->g_h = image->height;
647+
676648
if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) {
677649
// Set the maximum number of frames to encode to 1. This instructs
678650
// libaom to set still_picture and reduced_still_picture_header to
@@ -695,16 +667,6 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
695667
cfg->g_threads = encoder->maxThreads;
696668
}
697669

698-
int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
699-
int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
700-
if (alpha) {
701-
minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63);
702-
maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
703-
}
704-
avifBool lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS));
705-
cfg->rc_min_quantizer = minQuantizer;
706-
cfg->rc_max_quantizer = maxQuantizer;
707-
708670
codec->internal->monochromeEnabled = AVIF_FALSE;
709671
if (aomVersion > aomVersion_2_0_0) {
710672
// There exists a bug in libaom's chroma_check() function where it will attempt to
@@ -720,60 +682,95 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
720682
cfg->monochrome = 1;
721683
}
722684
}
685+
}
686+
687+
avifBool dimensionChanged = AVIF_FALSE;
688+
if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) {
689+
cfg->g_w = image->width;
690+
cfg->g_h = image->height;
691+
if (codec->internal->encoderInitialized) {
692+
// We are not ready for dimension change for now.
693+
return AVIF_RESULT_NOT_IMPLEMENTED;
694+
}
695+
}
696+
697+
if (!codec->internal->encoderInitialized || updatedConfig) {
698+
int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
699+
int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
700+
if (alpha) {
701+
minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63);
702+
maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
703+
}
704+
lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS));
705+
cfg->rc_min_quantizer = minQuantizer;
706+
cfg->rc_max_quantizer = maxQuantizer;
723707

724708
if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) {
725709
return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
726710
}
711+
}
727712

728-
avifBool initPhase = AVIF_FALSE;
729-
if (!codec->internal->encoderInitialized) {
730-
aom_codec_flags_t encoderFlags = 0;
731-
if (image->depth > 8) {
732-
encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH;
733-
}
734-
735-
if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) {
736-
avifDiagnosticsPrintf(codec->diag,
737-
"aom_codec_enc_init() failed: %s: %s",
738-
aom_codec_error(&codec->internal->encoder),
739-
aom_codec_error_detail(&codec->internal->encoder));
740-
return AVIF_RESULT_UNKNOWN_ERROR;
741-
}
742-
codec->internal->encoderInitialized = AVIF_TRUE;
743-
initPhase = AVIF_TRUE;
744-
} else {
745-
if (aom_codec_enc_config_set(&codec->internal->encoder, cfg) != AOM_CODEC_OK) {
746-
avifDiagnosticsPrintf(codec->diag,
747-
"aom_codec_enc_config_set() failed: %s: %s",
748-
aom_codec_error(&codec->internal->encoder),
749-
aom_codec_error_detail(&codec->internal->encoder));
750-
return AVIF_RESULT_UNKNOWN_ERROR;
713+
if (!codec->internal->encoderInitialized) {
714+
if (!codec->internal->endUsageSet) {
715+
// Set our own default cfg->rc_end_usage value, which may differ from libaom's default.
716+
switch (aomUsage) {
717+
case AOM_USAGE_GOOD_QUALITY:
718+
// libaom's default is AOM_VBR. Change the default to AOM_Q since we don't need to
719+
// hit a certain target bit rate. It's easier to control the worst quality in Q
720+
// mode.
721+
cfg->rc_end_usage = AOM_Q;
722+
break;
723+
case AOM_USAGE_REALTIME:
724+
// For real-time mode we need to use CBR rate control mode. AOM_Q doesn't fit the
725+
// rate control requirements for real-time mode. CBR does.
726+
cfg->rc_end_usage = AOM_CBR;
727+
break;
728+
#if defined(AOM_USAGE_ALL_INTRA)
729+
case AOM_USAGE_ALL_INTRA:
730+
cfg->rc_end_usage = AOM_Q;
731+
break;
732+
#endif
751733
}
752734
}
753735

754-
if (!initPhase || lossless) {
755-
aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
756-
}
757-
int rowMT = encoder->maxThreads > 1;
758-
if (!initPhase || rowMT) {
759-
aom_codec_control(&codec->internal->encoder, AV1E_SET_ROW_MT, rowMT);
736+
aom_codec_flags_t encoderFlags = 0;
737+
if (image->depth > 8) {
738+
encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH;
760739
}
761-
int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
762-
if (!initPhase || (tileRowsLog2 > 0)) {
763-
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2);
740+
741+
if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) {
742+
avifDiagnosticsPrintf(codec->diag,
743+
"aom_codec_enc_init() failed: %s: %s",
744+
aom_codec_error(&codec->internal->encoder),
745+
aom_codec_error_detail(&codec->internal->encoder));
746+
return AVIF_RESULT_UNKNOWN_ERROR;
764747
}
765-
int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6);
766-
if (!initPhase || (tileColsLog2 > 0)) {
767-
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2);
748+
749+
if (encoder->maxThreads > 1) {
750+
aom_codec_control(&codec->internal->encoder, AV1E_SET_ROW_MT, 1);
768751
}
769752
if (aomCpuUsed != -1) {
770753
if (aom_codec_control(&codec->internal->encoder, AOME_SET_CPUUSED, aomCpuUsed) != AOM_CODEC_OK) {
771754
return AVIF_RESULT_UNKNOWN_ERROR;
772755
}
773756
}
757+
} else if ((updatedConfig &~ AVIF_ENCODER_CONFIG_CODEC_SPECIFIC) || dimensionChanged) {
758+
// Codec specific options does not change cfg, so no need to update it.
759+
aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg);
760+
if (err != AOM_CODEC_OK) {
761+
avifDiagnosticsPrintf(codec->diag,
762+
"aom_codec_enc_config_set() failed: %s: %s",
763+
aom_codec_error(&codec->internal->encoder),
764+
aom_codec_error_detail(&codec->internal->encoder));
765+
return AVIF_RESULT_UNKNOWN_ERROR;
766+
}
767+
}
768+
769+
if (!codec->internal->encoderInitialized || (updatedConfig & AVIF_ENCODER_CONFIG_CODEC_SPECIFIC)) {
774770
if (!avifProcessAOMOptionsPostInit(codec, alpha)) {
775771
return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
776772
}
773+
777774
#if defined(AOM_USAGE_ALL_INTRA)
778775
if (aomUsage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet) {
779776
// The default rc_end_usage in all intra mode is AOM_Q, which requires cq-level to
@@ -786,14 +783,39 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
786783
aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel);
787784
}
788785
#endif
786+
}
787+
788+
if (!codec->internal->encoderInitialized) {
789+
if (lossless) {
790+
aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
791+
}
792+
int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
793+
if (tileRowsLog2 > 0) {
794+
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2);
795+
}
796+
int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6);
797+
if (tileColsLog2 > 0) {
798+
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2);
799+
}
800+
789801
if (!codec->internal->tuningSet) {
790802
if (aom_codec_control(&codec->internal->encoder, AOME_SET_TUNING, AOM_TUNE_SSIM) != AOM_CODEC_OK) {
791803
return AVIF_RESULT_UNKNOWN_ERROR;
792804
}
793805
}
794-
795-
// Sync our copy with updated config in AOM encoder.
796-
*cfg = *codec->internal->encoder.config.enc;
806+
codec->internal->encoderInitialized = AVIF_TRUE;
807+
} else if (updatedConfig) {
808+
if (((!alpha) && ((updatedConfig & AVIF_ENCODER_CONFIG_MIN_QUANTIZER) || (updatedConfig & AVIF_ENCODER_CONFIG_MAX_QUANTIZER))) ||
809+
(alpha && ((updatedConfig & AVIF_ENCODER_CONFIG_MIN_QUANTIZER_ALPHA) ||
810+
(updatedConfig & AVIF_ENCODER_CONFIG_MAX_QUANTIZER_ALPHA)))) {
811+
aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
812+
}
813+
if (updatedConfig & AVIF_ENCODER_CONFIG_TILE_ROWS_LOG_2) {
814+
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, AVIF_CLAMP(encoder->tileRowsLog2, 0, 6));
815+
}
816+
if (updatedConfig & AVIF_ENCODER_CONFIG_TILE_COLS_LOG_2) {
817+
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, AVIF_CLAMP(encoder->tileColsLog2, 0, 6));
818+
}
797819
}
798820

799821
aom_image_t aomImage;

src/codec_rav1e.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct avifCodecInternal
1212
RaContext * rav1eContext;
1313
RaChromaSampling chromaSampling;
1414
int yShift;
15+
uint32_t encodeWidth;
16+
uint32_t encodeHeight;
1517
};
1618

1719
static void rav1eCodecDestroyInternal(avifCodec * codec)
@@ -51,11 +53,20 @@ static avifResult rav1eCodecEncodeImage(avifCodec * codec,
5153
avifEncoder * encoder,
5254
const avifImage * image,
5355
avifBool alpha,
54-
avifBool updateConfig,
56+
avifEncoderConfig updatedConfig,
5557
uint32_t addImageFlags,
5658
avifCodecEncodeOutput * output)
5759
{
58-
if (updateConfig) {
60+
// rav1e does not support changing config.
61+
if (updatedConfig) {
62+
return AVIF_RESULT_NOT_IMPLEMENTED;
63+
}
64+
65+
// rav1e does not support changing encoding dimension.
66+
if (!codec->internal->rav1eContext) {
67+
codec->internal->encodeWidth = image->width;
68+
codec->internal->encodeHeight = image->height;
69+
} else if ((codec->internal->encodeWidth != image->width) || (codec->internal->encodeHeight != image->height)) {
5970
return AVIF_RESULT_NOT_IMPLEMENTED;
6071
}
6172

0 commit comments

Comments
 (0)