-
Notifications
You must be signed in to change notification settings - Fork 282
Correctly update encoder settings, clear csOptions after avifEncoderAddImage() #1058
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9a6758c
4ed2425
bcc47ea
9fec472
f2db7ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -527,11 +527,17 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, | |
| avifEncoder * encoder, | ||
| const avifImage * image, | ||
| avifBool alpha, | ||
| avifBool updateConfig, | ||
| avifEncoderChanges encoderChanges, | ||
| avifAddImageFlags addImageFlags, | ||
| avifCodecEncodeOutput * output) | ||
| { | ||
| if (!codec->internal->encoderInitialized || updateConfig) { | ||
| struct aom_codec_enc_cfg * cfg = &codec->internal->cfg; | ||
| aom_codec_iface_t * encoderInterface = NULL; | ||
| unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY; | ||
| int aomCpuUsed = -1; | ||
| avifBool lossless = AVIF_FALSE; | ||
|
|
||
| if (!codec->internal->encoderInitialized) { | ||
| // Map encoder speed to AOM usage + CpuUsed: | ||
| // Speed 0: GoodQuality CpuUsed 0 | ||
| // Speed 1: GoodQuality CpuUsed 1 | ||
|
|
@@ -544,15 +550,13 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, | |
| // Speed 8: RealTime CpuUsed 8 | ||
| // Speed 9: RealTime CpuUsed 9 | ||
| // Speed 10: RealTime CpuUsed 9 | ||
| unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY; | ||
| // Use the new AOM_USAGE_ALL_INTRA (added in https://crbug.com/aomedia/2959) for still | ||
| // image encoding if it is available. | ||
| #if defined(AOM_USAGE_ALL_INTRA) | ||
| if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { | ||
| aomUsage = AOM_USAGE_ALL_INTRA; | ||
| } | ||
| #endif | ||
| int aomCpuUsed = -1; | ||
| if (encoder->speed != AVIF_SPEED_DEFAULT) { | ||
| aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9); | ||
| if (aomCpuUsed >= 7) { | ||
|
|
@@ -589,23 +593,18 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, | |
| } | ||
| } | ||
|
|
||
| struct aom_codec_enc_cfg * cfg = &codec->internal->cfg; | ||
|
|
||
| aom_codec_iface_t * encoderInterface = NULL; | ||
| if (!codec->internal->encoderInitialized) { | ||
| codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha); | ||
| if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) { | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha); | ||
| if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) { | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
|
|
||
| avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); | ||
| avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); | ||
|
|
||
| encoderInterface = aom_codec_av1_cx(); | ||
| aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage); | ||
| if (err != AOM_CODEC_OK) { | ||
| avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err)); | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| encoderInterface = aom_codec_av1_cx(); | ||
| aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage); | ||
| if (err != AOM_CODEC_OK) { | ||
| avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err)); | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the following code after this line: This code changes some default values from libaom's defaults to ours, so it should immediately follow a successful call to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
|
||
| // Set our own default cfg->rc_end_usage value, which may differ from libaom's default. | ||
|
|
@@ -666,8 +665,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, | |
| cfg->g_profile = seqProfile; | ||
| cfg->g_bit_depth = image->depth; | ||
| cfg->g_input_bit_depth = image->depth; | ||
| cfg->g_w = image->width; | ||
| cfg->g_h = image->height; | ||
|
|
||
| if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { | ||
| // Set the maximum number of frames to encode to 1. This instructs | ||
| // libaom to set still_picture and reduced_still_picture_header to | ||
|
|
@@ -690,16 +688,6 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, | |
| cfg->g_threads = encoder->maxThreads; | ||
| } | ||
|
|
||
| int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); | ||
| int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); | ||
| if (alpha) { | ||
| minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); | ||
| maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); | ||
| } | ||
| avifBool lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS)); | ||
| cfg->rc_min_quantizer = minQuantizer; | ||
| cfg->rc_max_quantizer = maxQuantizer; | ||
|
|
||
| codec->internal->monochromeEnabled = AVIF_FALSE; | ||
| if (aomVersion > aomVersion_2_0_0) { | ||
| // There exists a bug in libaom's chroma_check() function where it will attempt to | ||
|
|
@@ -715,76 +703,126 @@ static avifResult aomCodecEncodeImage(avifCodec * codec, | |
| cfg->monochrome = 1; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) { | ||
| return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; | ||
| avifBool dimensionsChanged = AVIF_FALSE; | ||
| if (!codec->internal->encoderInitialized) { | ||
| cfg->g_w = image->width; | ||
| cfg->g_h = image->height; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems just changing To support larger frames, seems we need user to provide the expect dimension before starting encoding, and set
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| } else if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) { | ||
| // We are not ready for dimension change for now. | ||
| return AVIF_RESULT_NOT_IMPLEMENTED; | ||
| } | ||
|
|
||
| if (!codec->internal->encoderInitialized || encoderChanges) { | ||
| int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); | ||
| int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); | ||
| if (alpha) { | ||
| minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); | ||
| maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); | ||
| } | ||
| lossless = ((minQuantizer == AVIF_QUANTIZER_LOSSLESS) && (maxQuantizer == AVIF_QUANTIZER_LOSSLESS)); | ||
| cfg->rc_min_quantizer = minQuantizer; | ||
| cfg->rc_max_quantizer = maxQuantizer; | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
|
||
| if (!codec->internal->encoderInitialized) { | ||
| aom_codec_flags_t encoderFlags = 0; | ||
| if (image->depth > 8) { | ||
| encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH; | ||
| } | ||
| if (!codec->internal->encoderInitialized) { | ||
| aom_codec_flags_t encoderFlags = 0; | ||
| if (image->depth > 8) { | ||
| encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH; | ||
| } | ||
|
|
||
| if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) { | ||
| avifDiagnosticsPrintf(codec->diag, | ||
| "aom_codec_enc_init() failed: %s: %s", | ||
| aom_codec_error(&codec->internal->encoder), | ||
| aom_codec_error_detail(&codec->internal->encoder)); | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| codec->internal->encoderInitialized = AVIF_TRUE; | ||
| } else { | ||
| if (aom_codec_enc_config_set(&codec->internal->encoder, cfg) != AOM_CODEC_OK) { | ||
| avifDiagnosticsPrintf(codec->diag, | ||
| "aom_codec_enc_config_set() failed: %s: %s", | ||
| aom_codec_error(&codec->internal->encoder), | ||
| aom_codec_error_detail(&codec->internal->encoder)); | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) { | ||
| return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; | ||
| } | ||
|
|
||
| if (lossless) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, 1); | ||
| if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) { | ||
| avifDiagnosticsPrintf(codec->diag, | ||
| "aom_codec_enc_init() failed: %s: %s", | ||
| aom_codec_error(&codec->internal->encoder), | ||
| aom_codec_error_detail(&codec->internal->encoder)); | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
|
|
||
| if (encoder->maxThreads > 1) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_ROW_MT, 1); | ||
| } | ||
| if (encoder->tileRowsLog2 != 0) { | ||
| int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6); | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); | ||
| } | ||
| if (encoder->tileColsLog2 != 0) { | ||
| int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6); | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); | ||
| } | ||
| if (aomCpuUsed != -1) { | ||
| if (aom_codec_control(&codec->internal->encoder, AOME_SET_CPUUSED, aomCpuUsed) != AOM_CODEC_OK) { | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| } | ||
| } else if ((encoderChanges & ~AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) || dimensionsChanged) { | ||
| // Codec specific options does not change cfg, so no need to update it. | ||
| aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg); | ||
| if (err != AOM_CODEC_OK) { | ||
| avifDiagnosticsPrintf(codec->diag, | ||
| "aom_codec_enc_config_set() failed: %s: %s", | ||
| aom_codec_error(&codec->internal->encoder), | ||
| aom_codec_error_detail(&codec->internal->encoder)); | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| } | ||
|
|
||
| if (!codec->internal->encoderInitialized || (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC)) { | ||
| if (!avifProcessAOMOptionsPostInit(codec, alpha)) { | ||
| return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; | ||
| } | ||
| #if defined(AOM_USAGE_ALL_INTRA) | ||
| if (aomUsage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet) { | ||
| // The default rc_end_usage in all intra mode is AOM_Q, which requires cq-level to | ||
| // function. A libavif user may not know this internal detail and therefore may only | ||
| // set the min and max quantizers in the avifEncoder struct. If this is the case, set | ||
| // cq-level to a reasonable value for the user, otherwise the default cq-level | ||
| // (currently 10) will be unknowingly used. | ||
| assert(cfg->rc_end_usage == AOM_Q); | ||
| unsigned int cqLevel = (cfg->rc_min_quantizer + cfg->rc_max_quantizer) / 2; | ||
| aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
|
||
| avifBool quantizerUpdated = AVIF_FALSE; | ||
| if (!codec->internal->encoderInitialized) { | ||
| quantizerUpdated = AVIF_TRUE; | ||
| if (lossless) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); | ||
| } | ||
| int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6); | ||
| if (tileRowsLog2 > 0) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); | ||
| } | ||
| int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6); | ||
| if (tileColsLog2 > 0) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); | ||
| } | ||
| #endif | ||
| if (!codec->internal->tuningSet) { | ||
| if (aom_codec_control(&codec->internal->encoder, AOME_SET_TUNING, AOM_TUNE_SSIM) != AOM_CODEC_OK) { | ||
| return AVIF_RESULT_UNKNOWN_ERROR; | ||
| } | ||
| } | ||
| codec->internal->encoderInitialized = AVIF_TRUE; | ||
| } else if (encoderChanges) { | ||
| if (alpha) { | ||
| if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA)) { | ||
| quantizerUpdated = AVIF_TRUE; | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); | ||
| } | ||
| } else { | ||
| if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER | AVIF_ENCODER_CHANGE_MAX_QUANTIZER)) { | ||
| quantizerUpdated = AVIF_TRUE; | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); | ||
| } | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: Optional: We can simplify the conditionals even further:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, AVIF_CLAMP(encoder->tileRowsLog2, 0, 6)); | ||
| } | ||
| if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_COLS_LOG2) { | ||
| aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, AVIF_CLAMP(encoder->tileColsLog2, 0, 6)); | ||
| } | ||
| } | ||
|
|
||
| #if defined(AOM_USAGE_ALL_INTRA) | ||
| if (aomUsage == AOM_USAGE_ALL_INTRA && !codec->internal->endUsageSet && !codec->internal->cqLevelSet && quantizerUpdated) { | ||
| // The default rc_end_usage in all intra mode is AOM_Q, which requires cq-level to | ||
| // function. A libavif user may not know this internal detail and therefore may only | ||
| // set the min and max quantizers in the avifEncoder struct. If this is the case, set | ||
| // cq-level to a reasonable value for the user, otherwise the default cq-level | ||
| // (currently 10) will be unknowingly used. | ||
| assert(cfg->rc_end_usage == AOM_Q); | ||
| unsigned int cqLevel = (cfg->rc_min_quantizer + cfg->rc_max_quantizer) / 2; | ||
| aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, cqLevel); | ||
| } | ||
| #endif | ||
|
|
||
| aom_image_t aomImage; | ||
| // We prefer to simply set the aomImage.planes[] pointers to the plane buffers in 'image'. When | ||
| // doing this, we set aomImage.w equal to aomImage.d_w and aomImage.h equal to aomImage.d_h and | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.