Skip to content

Conversation

@tongyuantongyu
Copy link
Contributor

This is a follow up of #1033.

The init logic was skipping some calls if the option is the default value. Now it no longer skip calls so that encoder settings can be reverted to default.

Switch to discard csOptions after being applied to encoder.

@tongyuantongyu tongyuantongyu force-pushed the enc-update-settings-2 branch 2 times, most recently from f90ec00 to 331c5aa Compare August 21, 2022 06:46
@tongyuantongyu
Copy link
Contributor Author

@y-guyon , @wantehchang : Please review, thanks.


@wantehchang :

the third option (quoted below) seems the best to me

After rethink I agree this is better.

I think the original comment at line 863 "Another frame in an image sequence" should be updated to "Another frame in an image sequence, or another layer in a layered image" or simply "Another frame in an image sequence or layered image".

Maybe "the first image" and "subsequent images" is enough?

the following new comment added at lines 866-870 should be removed

Done. Also removed one of the two conditions that effectively checking the same thing.

I believe we are dealing with an image sequence or layered image here. So we should allow the frames to have different widths and heights

Done. I wonder if libavif decoder handles frames with different widths and heights correctly.

Copy link
Contributor

@y-guyon y-guyon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems tests/gtest/avifchangesettingtest.cc passes as is, even with the csOptions behavior change in this PR. Ideally the coverage should be increased to catch future any csOptions behavior change. It can be done in another PR.

src/codec_aom.c Outdated
return AVIF_RESULT_UNKNOWN_ERROR;
}
} else {
cfg->g_usage = aomUsage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see where does this come from. A comment would be useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. encoder->speed decides aomUsage and aomCpuUsed, so updating g_usage here. In "init" code path this field is set in aom_codec_enc_config_default().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Your message helped me more than the comment though, so maybe consider using the former as the latter. Something like // aomUsage was taken into account by aom_codec_enc_config_default() but it can only be manually updated afterwards.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing cfg->g_usage like this is risky, because some other fields in cfg may be tightly coupled to cfg->g_usage and need to be changed accordinly. It is best to avoid this potential issue, by not allowing encoder->speed to change.

Similarly, it is best to not allow encoder->maxThreads to change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was imagine some time-constrained encoding application that starts with better quality and tries to speed up if it's too slow. But if changing speed is risky, then I agree we'd better avoid that.

Copy link
Collaborator

@wantehchang wantehchang Aug 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into this. I found that libaom's test/datarate_test.h changes AOME_SET_CPUUSED dynamically in the middle of a video sequence (search for speed_change_test_ in the file), and I also saw webrtc's libaom_av1_encoder.cc file do this. So I believe libaom supports this. However, libavif may need to change cfg->g_usage at the same time it changes AOME_SET_CPUUSED, so it is best to avoid this until it's really needed.

Copy link
Contributor Author

@tongyuantongyu tongyuantongyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the coverage should be increased to catch future any csOptions behavior change.

csOptions field is internal so we can't directly check it. And given that options that can be set in this way are probably all persistent, it's also hard to tell from the encoded file. Maybe we need a test encoder.

src/codec_aom.c Outdated
return AVIF_RESULT_UNKNOWN_ERROR;
}
} else {
cfg->g_usage = aomUsage;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. encoder->speed decides aomUsage and aomCpuUsed, so updating g_usage here. In "init" code path this field is set in aom_codec_enc_config_default().

src/codec_aom.c Outdated
int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2);
int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6);
aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make these aom_codec_control() calls only when necessary. Is it possible to determine which calls are necessary when updateConfig is true?

The reason is that aom_codec_control() is not as cheap as it looks, if you trace its code. It will cause libaom to validate all of its configuration settings. See https://crbug.com/aomedia/2732.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These options can also be set using aom_codec_set_option().
If we only sync when these value changes, we can compare with previous value to find out which value needs to be updated; If these values should always be reflected in encoder, then we have to update them each time, as we can't know the real current value.

Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Yuan,

I suggest some changes. I will need to carefully review the current code in src/codec_aom.c after this pull request is merged.

// key must be non-NULL, but passing a NULL value will delete that key, if it exists.
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
// to be sent to codec at the next avifEncoderAddImage() call.
// See the codec documentation to know if a setting is permanent or applied only to the next frame.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: change "permanent" to "persistent" or "sticky"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs.
// key must be non-NULL, but passing a NULL value will delete that key, if it exists.
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
// to be sent to codec at the next avifEncoderAddImage() call.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add "the" before "codec"

Optional: Perhaps we can say "to be consumed by the codec ..." The word "consume" more strongly implies that the key/value pairs will be cleared after they are sent to the codec.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
// to be sent to codec at the next avifEncoderAddImage() call.
// See the codec documentation to know if a setting is permanent or applied only to the next frame.
// key must be non-NULL, but passing a NULL value will delete that pending key, if it exists.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: change "that" to "the"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

src/codec_aom.c Outdated
return AVIF_RESULT_UNKNOWN_ERROR;
}
} else {
cfg->g_usage = aomUsage;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing cfg->g_usage like this is risky, because some other fields in cfg may be tightly coupled to cfg->g_usage and need to be changed accordinly. It is best to avoid this potential issue, by not allowing encoder->speed to change.

Similarly, it is best to not allow encoder->maxThreads to change.

// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs.
// key must be non-NULL, but passing a NULL value will delete that key, if it exists.
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
// to be sent to codec at the next avifEncoderAddImage() call.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// key must be non-NULL, but passing a NULL value will delete that key, if it exists.
// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
// to be sent to codec at the next avifEncoderAddImage() call.
// See the codec documentation to know if a setting is permanent or applied only to the next frame.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs,
// to be sent to codec at the next avifEncoderAddImage() call.
// See the codec documentation to know if a setting is permanent or applied only to the next frame.
// key must be non-NULL, but passing a NULL value will delete that pending key, if it exists.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

src/codec_aom.c Outdated
return AVIF_RESULT_UNKNOWN_ERROR;
}
} else {
cfg->g_usage = aomUsage;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was imagine some time-constrained encoding application that starts with better quality and tries to speed up if it's too slow. But if changing speed is risky, then I agree we'd better avoid that.

avifBool dimensionChanged = AVIF_FALSE;
if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) {
cfg->g_w = image->width;
cfg->g_h = image->height;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems just changing g_w and g_h here only works when sending smaller frames, due to aom writing first frame's dimension to stream header.

To support larger frames, seems we need user to provide the expect dimension before starting encoding, and set g_forced_max_frame_width and g_forced_max_frame_height. I'm going to implement this in another PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct. An AV1 bitstream begins with a sequence header OBU, which advertises the maximum frame width and maximum frame height. Then each frame has a frame header OBU that may optionally specify a frame width and height smaller than the maximum. So if we are encoding multiple frames with increasing frame widths and heights, we will need a way to specify the maximum frame width and maximum frame height before encoding the first frame.

Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yuan: Please be careful when making this round of suggested changes. For some reason I find it difficult to review the changes to src/codec_aom.c. So we should refrain from making more big changes and try to stabilize this pull request. Thank you!

src/codec_aom.c Outdated
if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) {
cfg->g_w = image->width;
cfg->g_h = image->height;
if (codec->internal->encoderInitialized) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do this check first, immediately after line 688.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// a combination of settings are tweaked to simulate this speed range.
// * AV1 encoder settings and codec specific options set by avifEncoderSetCodecSpecificOption()
// will be applied / updated to AV1 encoder before each call to avifEncoderAddImage().
// * Some encoder settings can be changed. Changes will take effect from next call to
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add "after encoding starts" to the end of the first sentence. Change "from next call" to "in the next call".

// * Some encoder settings can be changed after encoding starts. Changes will take effect in the next call to

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

AVIF_ENCODER_CONFIG_TILE_COLS_LOG_2 = (1 << 5),

AVIF_ENCODER_CONFIG_CODEC_SPECIFIC = (1 << 31)
} avifEncoderConfig;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, if the avifEncoderConfig enum type is defined like this, it cannot take values that are the bitwise-OR of AVIF_ENCODER_CONFIG_xxx. You can find examples of one way to do this properly in avif.h.

I suggest the following:

typedef enum avifEncoderChange
{
    AVIF_ENCODER_CHANGE_MIN_QUANTIZER = (1 << 0),
    AVIF_ENCODER_CHANGE_MAX_QUANTIZER = (1 << 1),
    AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA = (1 << 2),
    AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA = (1 << 3),
    AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2 = (1 << 4),
    AVIF_ENCODER_CHANGE_TILE_COLS_LOG2 = (1 << 5),

    AVIF_ENCODER_CHANGE_CODEC_SPECIFIC = (1 << 31)
} avifEncoderChange;
typedef uint32_t avifEncoderChanges;

and then change the function parameter avifEncoderConfig updateConfig at line 283 to avifEncoderChanges encoderChanges.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

(alpha && ((updatedConfig & AVIF_ENCODER_CONFIG_MIN_QUANTIZER_ALPHA) ||
(updatedConfig & AVIF_ENCODER_CONFIG_MAX_QUANTIZER_ALPHA)))) {
aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This conditional expression is very complicated. I suggest we aim for clarify and rewrite it as follows:

        if (!alpha) {
            if ((updatedConfig & AVIF_ENCODER_CONFIG_MIN_QUANTIZER) || (updatedConfig & AVIF_ENCODER_CONFIG_MAX_QUANTIZER)) {
                aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
            }
        } else {
            if ((updatedConfig & AVIF_ENCODER_CONFIG_MIN_QUANTIZER_ALPHA) || (updatedConfig & AVIF_ENCODER_CONFIG_MAX_QUANTIZER_ALPHA)) {
                aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
            }
        }

Optional: We can simplify the conditionals even further:

        if (!alpha) {
            if (updatedConfig & (AVIF_ENCODER_CONFIG_MIN_QUANTIZER | AVIF_ENCODER_CONFIG_MAX_QUANTIZER)) {
                aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
            }
        } else {
            if (updatedConfig & (AVIF_ENCODER_CONFIG_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CONFIG_MAX_QUANTIZER_ALPHA)) {
                aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless);
            }
        }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// It reports if the change is valid, i.e. if any setting that can't change was changed.
// It also sets needUpdate to true if valid changes are detected.
static avifBool avifEncoderSettingsChanged(const avifEncoder * encoder, avifBool * needUpdate)
// It also reports detected changes in updatedConfig.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should sets *updatedConfig to 0 on entry. Otherwise, we need to document that it expects the caller to initialize *updatedConfig to 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

src/codec_aom.c Outdated
}
}

avifBool dimensionChanged = AVIF_FALSE;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: dimensionChanged => dimensionsChanged

"Dimensions" should be plural.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) {
return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we do not allow "end-usage" to change, we only need to call avifProcessAOMOptionsPreInit() before the aom_codec_enc_init() call. Please move lines 708-710 right before line 736, inside the if (!codec->internal->encoderInitialized) block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel);
}
#endif
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 774-785 should only be done when initializing the encoder or when cfg->rc_min_quantizer or cfg->rc_max_quantizer changed. If it takes more than five lines to implement this, just add a TODO comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@wantehchang wantehchang merged commit 47df37c into AOMediaCodec:main Aug 26, 2022
@tongyuantongyu tongyuantongyu deleted the enc-update-settings-2 branch August 26, 2022 04:18
wantehchang added a commit to wantehchang/libavif that referenced this pull request Aug 27, 2022
Clean up the changes made in the following two pull requests to support
updating encoder settings during encoding:
AOMediaCodec#1033
AOMediaCodec#1058

In particular, restore the aomCodecEncodeImage() function in
src/codec_aom.c to its original structure, plus a new block of code to
handle encoder changes.

Rename some functions and data members. Edit some comments and messages.

In the avifEncoderChange enum, left-shift the unsigned int constant 1u
because if we left-shift the signed int constant 1 by 31 bits, it will
be shifted into the sign bit.

Other miscellaneous cosmetic changes.

AOMediaCodec#761
wantehchang added a commit to wantehchang/libavif that referenced this pull request Aug 27, 2022
Clean up the changes made in the following two pull requests to support
updating encoder settings during encoding:
AOMediaCodec#1033
AOMediaCodec#1058

In particular, restore the aomCodecEncodeImage() function in
src/codec_aom.c to its original structure, plus a new block of code to
handle encoder changes.

Rename some functions and data members. Edit some comments and messages.

In the avifEncoderChange enum, left-shift the unsigned int constant 1u
because if we left-shift the signed int constant 1 by 31 bits, it will
be shifted into the sign bit.

Other miscellaneous cosmetic changes.

AOMediaCodec#761
wantehchang added a commit to wantehchang/libavif that referenced this pull request Aug 27, 2022
Clean up the changes made in the following two pull requests to support
updating encoder settings during encoding:
AOMediaCodec#1033
AOMediaCodec#1058

In particular, restore the aomCodecEncodeImage() function in
src/codec_aom.c to its original structure, plus a new block of code to
handle encoder changes.

Rename some functions and data members. Edit some comments and messages.

In the avifEncoderChange enum, left-shift the unsigned int constant 1u
because if we left-shift the signed int constant 1 by 31 bits, it will
be shifted into the sign bit.

Other miscellaneous cosmetic changes.

AOMediaCodec#761
wantehchang added a commit that referenced this pull request Aug 29, 2022
Clean up the changes made in the following two pull requests to support
updating encoder settings during encoding:
#1033
#1058

In particular, restore the aomCodecEncodeImage() function in
src/codec_aom.c to its original structure, plus a new block of code to
handle encoder changes.

Rename some functions and data members. Edit some comments and messages.

In the avifEncoderChange enum, left-shift the unsigned int constant 1u
because if we left-shift the signed int constant 1 by 31 bits, it will
be shifted into the sign bit.

Other miscellaneous cosmetic changes.

#761
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants