Skip to content

Commit f38cd85

Browse files
Support update encoder settings during encoding
Detect changes made to avifEncoder between avifEncoderAddImage() calls.
1 parent 1798f0f commit f38cd85

File tree

11 files changed

+353
-56
lines changed

11 files changed

+353
-56
lines changed

include/avif/avif.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ typedef enum avifResult
149149
AVIF_RESULT_WAITING_ON_IO, // similar to EAGAIN/EWOULDBLOCK, this means the avifIO doesn't have necessary data available yet
150150
AVIF_RESULT_INVALID_ARGUMENT, // an argument passed into this function is invalid
151151
AVIF_RESULT_NOT_IMPLEMENTED, // a requested code path is not (yet) implemented
152-
AVIF_RESULT_OUT_OF_MEMORY
152+
AVIF_RESULT_OUT_OF_MEMORY,
153+
AVIF_RESULT_CANNOT_CHANGE_SETTING, // a setting that can't change is changed during encoding
154+
AVIF_RESULT_INCOMPATIBLE_IMAGE // given image is not compatible with already encoded image
153155
} avifResult;
154156

155157
AVIF_API const char * avifResultToString(avifResult result);
@@ -1056,12 +1058,17 @@ struct avifCodecSpecificOptions;
10561058
// image in less bytes. AVIF_SPEED_DEFAULT means "Leave the AV1 codec to its default speed settings"./
10571059
// If avifEncoder uses rav1e, the speed value is directly passed through (0-10). If libaom is used,
10581060
// 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().
10591063
typedef struct avifEncoder
10601064
{
10611065
// Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c)
10621066
avifCodecChoice codecChoice;
10631067

10641068
// settings (see Notes above)
1069+
int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default).
1070+
uint64_t timescale; // timescale of the media (Hz)
1071+
// AV1 encoder settings.
10651072
int maxThreads;
10661073
int minQuantizer;
10671074
int maxQuantizer;
@@ -1070,8 +1077,6 @@ typedef struct avifEncoder
10701077
int tileRowsLog2;
10711078
int tileColsLog2;
10721079
int speed;
1073-
int keyframeInterval; // How many frames between automatic forced keyframes; 0 to disable (default).
1074-
uint64_t timescale; // timescale of the media (Hz)
10751080

10761081
// stats from the most recent write
10771082
avifIOStats ioStats;
@@ -1123,9 +1128,7 @@ AVIF_API avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
11231128
avifAddImageFlags addImageFlags);
11241129
AVIF_API avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output);
11251130

1126-
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs. These
1127-
// should be set as early as possible, preferably just after creating avifEncoder but before
1128-
// performing any other actions.
1131+
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs.
11291132
// key must be non-NULL, but passing a NULL value will delete that key, if it exists.
11301133
// Setting an incorrect or unknown option for the current codec will cause errors of type
11311134
// AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION from avifEncoderWrite() or avifEncoderAddImage().

include/avif/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ typedef avifResult (*avifCodecEncodeImageFunc)(struct avifCodec * codec,
267267
avifEncoder * encoder,
268268
const avifImage * image,
269269
avifBool alpha,
270+
avifBool updateConfig,
270271
avifAddImageFlags addImageFlags,
271272
avifCodecEncodeOutput * output);
272273
typedef avifBool (*avifCodecEncodeFinishFunc)(struct avifCodec * codec, avifCodecEncodeOutput * output);

src/avif.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ const char * avifResultToString(avifResult result)
9898
case AVIF_RESULT_INVALID_ARGUMENT: return "Invalid argument";
9999
case AVIF_RESULT_NOT_IMPLEMENTED: return "Not implemented";
100100
case AVIF_RESULT_OUT_OF_MEMORY: return "Out of memory";
101+
case AVIF_RESULT_CANNOT_CHANGE_SETTING: return "Can not change some settings during encoding";
102+
case AVIF_RESULT_INCOMPATIBLE_IMAGE: return "This image is incompatible with already encoded image";
101103
case AVIF_RESULT_UNKNOWN_ERROR:
102104
default:
103105
break;

src/codec_aom.c

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ struct avifCodecInternal
6060
#if defined(AVIF_CODEC_AOM_ENCODE)
6161
avifBool encoderInitialized;
6262
aom_codec_ctx_t encoder;
63+
struct aom_codec_enc_cfg cfg;
6364
avifPixelFormatInfo formatInfo;
6465
aom_img_fmt_t aomFormat;
6566
avifBool monochromeEnabled;
@@ -526,10 +527,11 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
526527
avifEncoder * encoder,
527528
const avifImage * image,
528529
avifBool alpha,
530+
avifBool updateConfig,
529531
avifAddImageFlags addImageFlags,
530532
avifCodecEncodeOutput * output)
531533
{
532-
if (!codec->internal->encoderInitialized) {
534+
if (!codec->internal->encoderInitialized || updateConfig) {
533535
// Map encoder speed to AOM usage + CpuUsed:
534536
// Speed 0: GoodQuality CpuUsed 0
535537
// Speed 1: GoodQuality CpuUsed 1
@@ -587,37 +589,41 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
587589
}
588590
}
589591

590-
codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha);
591-
if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) {
592-
return AVIF_RESULT_UNKNOWN_ERROR;
593-
}
592+
struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
594593

595-
avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo);
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+
}
596600

597-
aom_codec_iface_t * encoderInterface = aom_codec_av1_cx();
598-
struct aom_codec_enc_cfg cfg;
599-
aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, &cfg, aomUsage);
600-
if (err != AOM_CODEC_OK) {
601-
avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err));
602-
return AVIF_RESULT_UNKNOWN_ERROR;
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+
}
603609
}
604610

605-
// Set our own default cfg.rc_end_usage value, which may differ from libaom's default.
611+
// Set our own default cfg->rc_end_usage value, which may differ from libaom's default.
606612
switch (aomUsage) {
607613
case AOM_USAGE_GOOD_QUALITY:
608614
// libaom's default is AOM_VBR. Change the default to AOM_Q since we don't need to
609615
// hit a certain target bit rate. It's easier to control the worst quality in Q
610616
// mode.
611-
cfg.rc_end_usage = AOM_Q;
617+
cfg->rc_end_usage = AOM_Q;
612618
break;
613619
case AOM_USAGE_REALTIME:
614620
// For real-time mode we need to use CBR rate control mode. AOM_Q doesn't fit the
615621
// rate control requirements for real-time mode. CBR does.
616-
cfg.rc_end_usage = AOM_CBR;
622+
cfg->rc_end_usage = AOM_CBR;
617623
break;
618624
#if defined(AOM_USAGE_ALL_INTRA)
619625
case AOM_USAGE_ALL_INTRA:
620-
cfg.rc_end_usage = AOM_Q;
626+
cfg->rc_end_usage = AOM_Q;
621627
break;
622628
#endif
623629
}
@@ -657,31 +663,31 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
657663
}
658664
}
659665

660-
cfg.g_profile = seqProfile;
661-
cfg.g_bit_depth = image->depth;
662-
cfg.g_input_bit_depth = image->depth;
663-
cfg.g_w = image->width;
664-
cfg.g_h = image->height;
666+
cfg->g_profile = seqProfile;
667+
cfg->g_bit_depth = image->depth;
668+
cfg->g_input_bit_depth = image->depth;
669+
cfg->g_w = image->width;
670+
cfg->g_h = image->height;
665671
if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) {
666672
// Set the maximum number of frames to encode to 1. This instructs
667673
// libaom to set still_picture and reduced_still_picture_header to
668674
// 1 in AV1 sequence headers.
669-
cfg.g_limit = 1;
675+
cfg->g_limit = 1;
670676

671677
// Use the default settings of the new AOM_USAGE_ALL_INTRA (added in
672678
// https://crbug.com/aomedia/2959).
673679
//
674680
// Set g_lag_in_frames to 0 to reduce the number of frame buffers
675681
// (from 20 to 2) in libaom's lookahead structure. This reduces
676682
// memory consumption when encoding a single image.
677-
cfg.g_lag_in_frames = 0;
683+
cfg->g_lag_in_frames = 0;
678684
// Disable automatic placement of key frames by the encoder.
679-
cfg.kf_mode = AOM_KF_DISABLED;
685+
cfg->kf_mode = AOM_KF_DISABLED;
680686
// Tell libaom that all frames will be key frames.
681-
cfg.kf_max_dist = 0;
687+
cfg->kf_max_dist = 0;
682688
}
683689
if (encoder->maxThreads > 1) {
684-
cfg.g_threads = encoder->maxThreads;
690+
cfg->g_threads = encoder->maxThreads;
685691
}
686692

687693
int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
@@ -691,41 +697,52 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
691697
maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63);
692698
}
693699
avifBool lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS));
694-
cfg.rc_min_quantizer = minQuantizer;
695-
cfg.rc_max_quantizer = maxQuantizer;
700+
cfg->rc_min_quantizer = minQuantizer;
701+
cfg->rc_max_quantizer = maxQuantizer;
696702

697703
codec->internal->monochromeEnabled = AVIF_FALSE;
698704
if (aomVersion > aomVersion_2_0_0) {
699705
// There exists a bug in libaom's chroma_check() function where it will attempt to
700706
// access nonexistent UV planes when encoding monochrome at faster libavif "speeds". It
701707
// was fixed shortly after the 2.0.0 libaom release, and the fix exists in both the
702708
// master and applejack branches. This ensures that the next version *after* 2.0.0 will
703-
// have the fix, and we must avoid cfg.monochrome until then.
709+
// have the fix, and we must avoid cfg->monochrome until then.
704710
//
705711
// Bugfix Change-Id: https://aomedia-review.googlesource.com/q/I26a39791f820b4d4e1d63ff7141f594c3c7181f5
706712

707713
if (alpha || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) {
708714
codec->internal->monochromeEnabled = AVIF_TRUE;
709-
cfg.monochrome = 1;
715+
cfg->monochrome = 1;
710716
}
711717
}
712718

713-
if (!avifProcessAOMOptionsPreInit(codec, alpha, &cfg)) {
719+
if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) {
714720
return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
715721
}
716722

717-
aom_codec_flags_t encoderFlags = 0;
718-
if (image->depth > 8) {
719-
encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH;
720-
}
721-
if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, &cfg, encoderFlags) != AOM_CODEC_OK) {
722-
avifDiagnosticsPrintf(codec->diag,
723-
"aom_codec_enc_init() failed: %s: %s",
724-
aom_codec_error(&codec->internal->encoder),
725-
aom_codec_error_detail(&codec->internal->encoder));
726-
return AVIF_RESULT_UNKNOWN_ERROR;
723+
if (!codec->internal->encoderInitialized) {
724+
aom_codec_flags_t encoderFlags = 0;
725+
if (image->depth > 8) {
726+
encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH;
727+
}
728+
729+
if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) {
730+
avifDiagnosticsPrintf(codec->diag,
731+
"aom_codec_enc_init() failed: %s: %s",
732+
aom_codec_error(&codec->internal->encoder),
733+
aom_codec_error_detail(&codec->internal->encoder));
734+
return AVIF_RESULT_UNKNOWN_ERROR;
735+
}
736+
codec->internal->encoderInitialized = AVIF_TRUE;
737+
} else {
738+
if (aom_codec_enc_config_set(&codec->internal->encoder, cfg) != AOM_CODEC_OK) {
739+
avifDiagnosticsPrintf(codec->diag,
740+
"aom_codec_enc_config_set() failed: %s: %s",
741+
aom_codec_error(&codec->internal->encoder),
742+
aom_codec_error_detail(&codec->internal->encoder));
743+
return AVIF_RESULT_UNKNOWN_ERROR;
744+
}
727745
}
728-
codec->internal->encoderInitialized = AVIF_TRUE;
729746

730747
if (lossless) {
731748
aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, 1);
@@ -756,8 +773,8 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
756773
// set the min and max quantizers in the avifEncoder struct. If this is the case, set
757774
// cq-level to a reasonable value for the user, otherwise the default cq-level
758775
// (currently 10) will be unknowingly used.
759-
assert(cfg.rc_end_usage == AOM_Q);
760-
unsigned int cqLevel = (cfg.rc_min_quantizer + cfg.rc_max_quantizer) / 2;
776+
assert(cfg->rc_end_usage == AOM_Q);
777+
unsigned int cqLevel = (cfg->rc_min_quantizer + cfg->rc_max_quantizer) / 2;
761778
aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel);
762779
}
763780
#endif

src/codec_rav1e.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ static avifResult rav1eCodecEncodeImage(avifCodec * codec,
5151
avifEncoder * encoder,
5252
const avifImage * image,
5353
avifBool alpha,
54+
avifBool updateConfig,
5455
uint32_t addImageFlags,
5556
avifCodecEncodeOutput * output)
5657
{
58+
if (updateConfig) {
59+
return AVIF_RESULT_NOT_IMPLEMENTED;
60+
}
61+
5762
avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
5863

5964
RaConfig * rav1eConfig = NULL;

src/codec_svt.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ static avifResult svtCodecEncodeImage(avifCodec * codec,
4646
avifEncoder * encoder,
4747
const avifImage * image,
4848
avifBool alpha,
49+
avifBool updateConfig,
4950
uint32_t addImageFlags,
5051
avifCodecEncodeOutput * output)
5152
{
53+
if (updateConfig) {
54+
return AVIF_RESULT_NOT_IMPLEMENTED;
55+
}
56+
5257
avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
5358
EbColorFormat color_format = EB_YUV420;
5459
EbBufferHeaderType * input_buffer = NULL;

0 commit comments

Comments
 (0)