Codec-specific options plumbing for advanced encoding settings (unused, first pass)
diff --git a/src/avif.c b/src/avif.c index 969accc..9c9fe04 100644 --- a/src/avif.c +++ b/src/avif.c
@@ -68,24 +68,25 @@ { // clang-format off switch (result) { - case AVIF_RESULT_OK: return "OK"; - case AVIF_RESULT_INVALID_FTYP: return "Invalid ftyp"; - case AVIF_RESULT_NO_CONTENT: return "No content"; - case AVIF_RESULT_NO_YUV_FORMAT_SELECTED: return "No YUV format selected"; - case AVIF_RESULT_REFORMAT_FAILED: return "Reformat failed"; - case AVIF_RESULT_UNSUPPORTED_DEPTH: return "Unsupported depth"; - case AVIF_RESULT_ENCODE_COLOR_FAILED: return "Encoding of color planes failed"; - case AVIF_RESULT_ENCODE_ALPHA_FAILED: return "Encoding of alpha plane failed"; - case AVIF_RESULT_BMFF_PARSE_FAILED: return "BMFF parsing failed"; - case AVIF_RESULT_NO_AV1_ITEMS_FOUND: return "No AV1 items found"; - case AVIF_RESULT_DECODE_COLOR_FAILED: return "Decoding of color planes failed"; - case AVIF_RESULT_DECODE_ALPHA_FAILED: return "Decoding of alpha plane failed"; - case AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH: return "Color and alpha planes size mismatch"; - case AVIF_RESULT_ISPE_SIZE_MISMATCH: return "Plane sizes don't match ispe values"; - case AVIF_RESULT_NO_CODEC_AVAILABLE: return "No codec available"; - case AVIF_RESULT_NO_IMAGES_REMAINING: return "No images remaining"; - case AVIF_RESULT_INVALID_EXIF_PAYLOAD: return "Invalid Exif payload"; - case AVIF_RESULT_INVALID_IMAGE_GRID: return "Invalid image grid"; + case AVIF_RESULT_OK: return "OK"; + case AVIF_RESULT_INVALID_FTYP: return "Invalid ftyp"; + case AVIF_RESULT_NO_CONTENT: return "No content"; + case AVIF_RESULT_NO_YUV_FORMAT_SELECTED: return "No YUV format selected"; + case AVIF_RESULT_REFORMAT_FAILED: return "Reformat failed"; + case AVIF_RESULT_UNSUPPORTED_DEPTH: return "Unsupported depth"; + case AVIF_RESULT_ENCODE_COLOR_FAILED: return "Encoding of color planes failed"; + case AVIF_RESULT_ENCODE_ALPHA_FAILED: return "Encoding of alpha plane failed"; + case AVIF_RESULT_BMFF_PARSE_FAILED: return "BMFF parsing failed"; + case AVIF_RESULT_NO_AV1_ITEMS_FOUND: return "No AV1 items found"; + case AVIF_RESULT_DECODE_COLOR_FAILED: return "Decoding of color planes failed"; + case AVIF_RESULT_DECODE_ALPHA_FAILED: return "Decoding of alpha plane failed"; + case AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH: return "Color and alpha planes size mismatch"; + case AVIF_RESULT_ISPE_SIZE_MISMATCH: return "Plane sizes don't match ispe values"; + case AVIF_RESULT_NO_CODEC_AVAILABLE: return "No codec available"; + case AVIF_RESULT_NO_IMAGES_REMAINING: return "No images remaining"; + case AVIF_RESULT_INVALID_EXIF_PAYLOAD: return "Invalid Exif payload"; + case AVIF_RESULT_INVALID_IMAGE_GRID: return "Invalid image grid"; + case AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION: return "Invalid codec-specific option"; case AVIF_RESULT_UNKNOWN_ERROR: default: break; @@ -376,6 +377,79 @@ } // --------------------------------------------------------------------------- +// avifCodecSpecificOption + +static char * avifStrdup(const char * str) +{ + size_t len = strlen(str); + char * dup = avifAlloc(len + 1); + memcpy(dup, str, len + 1); + return dup; +} + +avifCodecSpecificOptions * avifCodecSpecificOptionsCreate(void) +{ + avifCodecSpecificOptions * ava = avifAlloc(sizeof(avifCodecSpecificOptions)); + avifArrayCreate(ava, sizeof(avifCodecSpecificOption), 4); + return ava; +} + +void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions) +{ + if (!csOptions) { + return; + } + + for (uint32_t i = 0; i < csOptions->count; ++i) { + avifCodecSpecificOption * entry = &csOptions->entries[i]; + avifFree(entry->key); + avifFree(entry->value); + } + avifArrayDestroy(csOptions); + avifFree(csOptions); +} + +void avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const char * key, const char * value) +{ + // Check to see if a key must be replaced + for (uint32_t i = 0; i < csOptions->count; ++i) { + avifCodecSpecificOption * entry = &csOptions->entries[i]; + if (!strcmp(entry->key, key)) { + if (value) { + // Update the value + avifFree(entry->value); + entry->value = avifStrdup(value); + } else { + // Delete the value + avifFree(entry->key); + avifFree(entry->value); + --csOptions->count; + if (csOptions->count > 0) { + memmove(&csOptions->entries[i], &csOptions->entries[i + 1], (csOptions->count - i) * csOptions->elementSize); + } + } + return; + } + } + + // Add a new key + avifCodecSpecificOption * entry = (avifCodecSpecificOption *)avifArrayPushPtr(csOptions); + entry->key = avifStrdup(key); + entry->value = avifStrdup(value); +} + +avifCodecSpecificOption * avifCodecSpecificOptionsGet(avifCodecSpecificOptions * csOptions, const char * key) +{ + for (uint32_t i = 0; i < csOptions->count; ++i) { + avifCodecSpecificOption * entry = &csOptions->entries[i]; + if (!strcmp(entry->key, key)) { + return entry; + } + } + return NULL; +} + +// --------------------------------------------------------------------------- // Codec availability and versions typedef const char * (*versionFunc)(void);
diff --git a/src/codec_aom.c b/src/codec_aom.c index 10bc8e8..4e602e3 100644 --- a/src/codec_aom.c +++ b/src/codec_aom.c
@@ -216,12 +216,12 @@ return fmt; } -static avifBool aomCodecEncodeImage(avifCodec * codec, - avifEncoder * encoder, - const avifImage * image, - avifBool alpha, - uint32_t addImageFlags, - avifCodecEncodeOutput * output) +static avifResult aomCodecEncodeImage(avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + uint32_t addImageFlags, + avifCodecEncodeOutput * output) { if (!codec->internal->encoderInitialized) { // Map encoder speed to AOM usage + CpuUsed: @@ -271,9 +271,14 @@ } } + if (codec->csOptions->count > 0) { + // None are currently supported! + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha); if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) { - return AVIF_FALSE; + return AVIF_RESULT_UNKNOWN_ERROR; } avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); @@ -436,7 +441,7 @@ } aom_img_free(aomImage); - return AVIF_TRUE; + return AVIF_RESULT_OK; } static avifBool aomCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output)
diff --git a/src/codec_rav1e.c b/src/codec_rav1e.c index 6a7fb22..f7605ef 100644 --- a/src/codec_rav1e.c +++ b/src/codec_rav1e.c
@@ -55,19 +55,24 @@ return minorVersion >= 4; } -static avifBool rav1eCodecEncodeImage(avifCodec * codec, - avifEncoder * encoder, - const avifImage * image, - avifBool alpha, - uint32_t addImageFlags, - avifCodecEncodeOutput * output) +static avifResult rav1eCodecEncodeImage(avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + uint32_t addImageFlags, + avifCodecEncodeOutput * output) { - avifBool success = AVIF_FALSE; + avifResult result = AVIF_RESULT_UNKNOWN_ERROR; RaConfig * rav1eConfig = NULL; RaFrame * rav1eFrame = NULL; if (!codec->internal->rav1eContext) { + if (codec->csOptions->count > 0) { + // None are currently supported! + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + const avifBool supports400 = rav1eSupports400(); RaPixelRange rav1eRange; if (alpha) { @@ -94,7 +99,7 @@ break; case AVIF_PIXEL_FORMAT_NONE: default: - return AVIF_FALSE; + return AVIF_RESULT_UNKNOWN_ERROR; } } @@ -209,7 +214,7 @@ break; } } - success = AVIF_TRUE; + result = AVIF_RESULT_OK; cleanup: if (rav1eFrame) { rav1e_frame_unref(rav1eFrame); @@ -219,7 +224,7 @@ rav1e_config_unref(rav1eConfig); rav1eConfig = NULL; } - return success; + return result; } static avifBool rav1eCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output)
diff --git a/src/write.c b/src/write.c index d3ac127..3b8493d 100644 --- a/src/write.c +++ b/src/write.c
@@ -178,15 +178,22 @@ encoder->data = avifEncoderDataCreate(); encoder->timescale = 1; encoder->keyframeInterval = 0; + encoder->csOptions = avifCodecSpecificOptionsCreate(); return encoder; } void avifEncoderDestroy(avifEncoder * encoder) { + avifCodecSpecificOptionsDestroy(encoder->csOptions); avifEncoderDataDestroy(encoder->data); avifFree(encoder); } +void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value) +{ + avifCodecSpecificOptionsSet(encoder->csOptions, key, value); +} + static void avifEncoderWriteColorProperties(avifRWStream * s, const avifImage * imageMetadata, struct ipmaArray * ipma, uint8_t * itemPropertyIndex) { if (imageMetadata->icc.size > 0) { @@ -359,6 +366,7 @@ encoder->data->colorItem = avifEncoderDataCreateItem(encoder->data, "av01", "Color", 6); encoder->data->colorItem->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE); + encoder->data->colorItem->codec->csOptions = encoder->csOptions; if (!encoder->data->colorItem->codec) { // Just bail out early, we're not surviving this function without an encoder compiled in return AVIF_RESULT_NO_CODEC_AVAILABLE; @@ -381,6 +389,7 @@ if (needsAlpha) { encoder->data->alphaItem = avifEncoderDataCreateItem(encoder->data, "av01", "Alpha", 6); encoder->data->alphaItem->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE); + encoder->data->alphaItem->codec->csOptions = encoder->csOptions; if (!encoder->data->alphaItem->codec) { return AVIF_RESULT_NO_CODEC_AVAILABLE; } @@ -465,8 +474,13 @@ for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { avifEncoderItem * item = &encoder->data->items.item[itemIndex]; if (item->codec) { - if (!item->codec->encodeImage(item->codec, encoder, image, item->alpha, addImageFlags, item->encodeOutput)) { - return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; + avifResult encodeResult = + item->codec->encodeImage(item->codec, encoder, image, item->alpha, addImageFlags, item->encodeOutput); + if (encodeResult == AVIF_RESULT_UNKNOWN_ERROR) { + encodeResult = item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; + } + if (encodeResult != AVIF_RESULT_OK) { + return encodeResult; } } }