Distribute out and share code populating av01 config box across codecs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e29c8b..5488552 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
## [Unreleased]
### Changed
- Fix QP range for rav1e encodes (rav1e uses [0-255], not [0-63])
+- Distribute out and share code populating av01 config box across codecs
## [0.4.3] - 2019-10-28
### Added
diff --git a/include/avif/internal.h b/include/avif/internal.h
index a8de61c..c0bee2d 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -129,19 +129,18 @@
typedef avifBool (*avifCodecGetNextImageFunc)(struct avifCodec * codec, avifImage * image);
// avifCodecEncodeImageFunc: if either OBU* is null, skip its encode. alpha should always be lossless
typedef avifBool (*avifCodecEncodeImageFunc)(struct avifCodec * codec, avifImage * image, avifEncoder * encoder, avifRWData * obu, avifBool alpha);
-typedef void (*avifCodecGetConfigurationBoxFunc)(struct avifCodec * codec, avifCodecConfigurationBox * outConfig);
typedef void (*avifCodecDestroyInternalFunc)(struct avifCodec * codec);
typedef struct avifCodec
{
avifCodecDecodeInput * decodeInput;
+ avifCodecConfigurationBox configBox; // Pre-populated by avifEncoderWrite(), available and overridable by codec impls
struct avifCodecInternal * internal; // up to each codec to use how it wants
avifCodecOpenFunc open;
avifCodecAlphaLimitedRangeFunc alphaLimitedRange;
avifCodecGetNextImageFunc getNextImage;
avifCodecEncodeImageFunc encodeImage;
- avifCodecGetConfigurationBoxFunc getConfigurationBox;
avifCodecDestroyInternalFunc destroyInternal;
} avifCodec;
diff --git a/src/codec_aom.c b/src/codec_aom.c
index ff9ff52..fee504f 100644
--- a/src/codec_aom.c
+++ b/src/codec_aom.c
@@ -34,8 +34,6 @@
aom_codec_iter_t iter;
uint32_t inputSampleIndex;
aom_image_t * image;
-
- avifCodecConfigurationBox config;
};
static void aomCodecDestroyInternal(avifCodec * codec)
@@ -193,12 +191,12 @@
return AVIF_TRUE;
}
-static aom_img_fmt_t avifImageCalcAOMFmt(avifImage * image, avifBool alphaOnly, int * yShift)
+static aom_img_fmt_t avifImageCalcAOMFmt(avifImage * image, avifBool alpha, int * yShift)
{
*yShift = 0;
aom_img_fmt_t fmt;
- if (alphaOnly) {
+ if (alpha) {
// We're going monochrome, who cares about chroma quality
fmt = AOM_IMG_FMT_I420;
*yShift = 1;
@@ -230,16 +228,14 @@
return fmt;
}
-static avifBool encodeOBU(avifImage * image, avifBool alphaOnly, avifEncoder * encoder, avifRWData * outputOBU, avifCodecConfigurationBox * outputConfig)
+static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEncoder * encoder, avifRWData * obu, avifBool alpha)
{
avifBool success = AVIF_FALSE;
aom_codec_iface_t * encoder_interface = aom_codec_av1_cx();
aom_codec_ctx_t aomEncoder;
- memset(outputConfig, 0, sizeof(avifCodecConfigurationBox));
-
int yShift = 0;
- aom_img_fmt_t aomFormat = avifImageCalcAOMFmt(image, alphaOnly, &yShift);
+ aom_img_fmt_t aomFormat = avifImageCalcAOMFmt(image, alpha, &yShift);
if (aomFormat == AOM_IMG_FMT_NONE) {
return AVIF_FALSE;
}
@@ -250,40 +246,7 @@
struct aom_codec_enc_cfg cfg;
aom_codec_enc_config_default(encoder_interface, &cfg, 0);
- // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only.
- // Profile 1. 8-bit and 10-bit 4:4:4
- // Profile 2. 8-bit and 10-bit 4:2:2
- // 12-bit 4:0:0, 4:2:2 and 4:4:4
- if (image->depth == 12) {
- // Only profile 2 can handle 12 bit
- cfg.g_profile = 2;
- } else {
- // 8-bit or 10-bit
-
- if (alphaOnly) {
- // Assuming aomImage->monochrome makes it 4:0:0
- cfg.g_profile = 0;
- } else {
- switch (image->yuvFormat) {
- case AVIF_PIXEL_FORMAT_YUV444:
- cfg.g_profile = 1;
- break;
- case AVIF_PIXEL_FORMAT_YUV422:
- cfg.g_profile = 2;
- break;
- case AVIF_PIXEL_FORMAT_YUV420:
- cfg.g_profile = 0;
- break;
- case AVIF_PIXEL_FORMAT_YV12:
- cfg.g_profile = 0;
- break;
- case AVIF_PIXEL_FORMAT_NONE:
- default:
- break;
- }
- }
- }
-
+ cfg.g_profile = codec->configBox.seqProfile;
cfg.g_bit_depth = image->depth;
cfg.g_input_bit_depth = image->depth;
cfg.g_w = image->width;
@@ -292,32 +255,9 @@
cfg.g_threads = encoder->maxThreads;
}
- // TODO: Choose correct value from Annex A.3 table: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
- uint8_t seqLevelIdx0 = 31;
- if ((image->width <= 8192) && (image->height <= 4352) && ((image->width * image->height) <= 8912896)) {
- // Image is 5.1 compatible
- seqLevelIdx0 = 13; // 5.1
- }
-
- outputConfig->seqProfile = (uint8_t)cfg.g_profile;
- outputConfig->seqLevelIdx0 = seqLevelIdx0;
- outputConfig->seqTier0 = 0;
- outputConfig->highBitdepth = (image->depth > 8) ? 1 : 0;
- outputConfig->twelveBit = (image->depth == 12) ? 1 : 0;
- outputConfig->monochrome = alphaOnly ? 1 : 0;
- outputConfig->chromaSubsamplingX = (uint8_t)formatInfo.chromaShiftX;
- outputConfig->chromaSubsamplingY = (uint8_t)formatInfo.chromaShiftY;
-
- // TODO: choose the correct one from below:
- // * 0 - CSP_UNKNOWN Unknown (in this case the source video transfer function must be signaled outside the AV1 bitstream)
- // * 1 - CSP_VERTICAL Horizontally co-located with (0, 0) luma sample, vertical position in the middle between two luma samples
- // * 2 - CSP_COLOCATED co-located with (0, 0) luma sample
- // * 3 - CSP_RESERVED
- outputConfig->chromaSamplePosition = 0;
-
int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63);
int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63);
- if (alphaOnly) {
+ if (alpha) {
minQuantizer = AVIF_QUANTIZER_LOSSLESS;
maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
}
@@ -351,7 +291,7 @@
uint32_t uvHeight = image->height >> yShift;
aom_image_t * aomImage = aom_img_alloc(NULL, aomFormat, image->width, image->height, 16);
- if (alphaOnly) {
+ if (alpha) {
aomImage->range = AOM_CR_FULL_RANGE; // Alpha is always full range
aom_codec_control(&aomEncoder, AV1E_SET_COLOR_RANGE, aomImage->range);
aomImage->monochrome = 1;
@@ -406,7 +346,7 @@
if (pkt == NULL)
break;
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
- avifRWDataSet(outputOBU, pkt->data.frame.buf, pkt->data.frame.sz);
+ avifRWDataSet(obu, pkt->data.frame.buf, pkt->data.frame.sz);
success = AVIF_TRUE;
break;
}
@@ -417,19 +357,6 @@
return success;
}
-static avifBool aomCodecEncodeImage(avifCodec * codec, avifImage * image, avifEncoder * encoder, avifRWData * obu, avifBool alpha)
-{
- if (!encodeOBU(image, alpha, encoder, obu, &codec->internal->config)) {
- return AVIF_FALSE;
- }
- return AVIF_TRUE;
-}
-
-static void aomCodecGetConfigurationBox(avifCodec * codec, avifCodecConfigurationBox * outConfig)
-{
- memcpy(outConfig, &codec->internal->config, sizeof(avifCodecConfigurationBox));
-}
-
const char * avifCodecVersionAOM(void)
{
return aom_codec_version_str();
@@ -443,7 +370,6 @@
codec->alphaLimitedRange = aomCodecAlphaLimitedRange;
codec->getNextImage = aomCodecGetNextImage;
codec->encodeImage = aomCodecEncodeImage;
- codec->getConfigurationBox = aomCodecGetConfigurationBox;
codec->destroyInternal = aomCodecDestroyInternal;
codec->internal = (struct avifCodecInternal *)avifAlloc(sizeof(struct avifCodecInternal));
diff --git a/src/codec_rav1e.c b/src/codec_rav1e.c
index 0d1c522..a27380d 100644
--- a/src/codec_rav1e.c
+++ b/src/codec_rav1e.c
@@ -9,7 +9,7 @@
struct avifCodecInternal
{
- avifCodecConfigurationBox config;
+ uint32_t unused; // rav1e codec has no state
};
static void rav1eCodecDestroyInternal(avifCodec * codec)
@@ -26,6 +26,8 @@
static avifBool rav1eCodecEncodeImage(avifCodec * codec, avifImage * image, avifEncoder * encoder, avifRWData * obu, avifBool alpha)
{
+ (void)codec; // unused
+
avifBool success = AVIF_FALSE;
RaConfig * rav1eConfig = NULL;
@@ -58,9 +60,6 @@
}
}
- avifPixelFormatInfo formatInfo;
- avifGetPixelFormatInfo(image->yuvFormat, &formatInfo);
-
rav1eConfig = rav1e_config_default();
if (rav1e_config_set_pixel_format(
rav1eConfig, (uint8_t)image->depth, chromaSampling, RA_CHROMA_SAMPLE_POSITION_UNKNOWN, rav1eRange) < 0) {
@@ -92,57 +91,6 @@
goto cleanup;
}
- // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only.
- // Profile 1. 8-bit and 10-bit 4:4:4
- // Profile 2. 8-bit and 10-bit 4:2:2
- // 12-bit 4:0:0, 4:2:2 and 4:4:4
- uint8_t seqProfile = 0;
- if (image->depth == 12) {
- // Only seqProfile 2 can handle 12 bit
- seqProfile = 2;
- } else {
- // 8-bit or 10-bit
-
- if (alpha) {
- seqProfile = 0;
- } else {
- switch (image->yuvFormat) {
- case AVIF_PIXEL_FORMAT_YUV444:
- seqProfile = 1;
- break;
- case AVIF_PIXEL_FORMAT_YUV422:
- seqProfile = 2;
- break;
- case AVIF_PIXEL_FORMAT_YUV420:
- seqProfile = 0;
- break;
- case AVIF_PIXEL_FORMAT_YV12:
- seqProfile = 0;
- break;
- case AVIF_PIXEL_FORMAT_NONE:
- default:
- break;
- }
- }
- }
-
- // TODO: Choose correct value from Annex A.3 table: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
- uint8_t seqLevelIdx0 = 31;
- if ((image->width <= 8192) && (image->height <= 4352) && ((image->width * image->height) <= 8912896)) {
- // Image is 5.1 compatible
- seqLevelIdx0 = 13; // 5.1
- }
-
- memset(&codec->internal->config, 0, sizeof(avifCodecConfigurationBox));
- codec->internal->config.seqProfile = seqProfile;
- codec->internal->config.seqLevelIdx0 = seqLevelIdx0;
- codec->internal->config.seqTier0 = 0;
- codec->internal->config.highBitdepth = (image->depth > 8) ? 1 : 0;
- codec->internal->config.twelveBit = (image->depth == 12) ? 1 : 0;
- codec->internal->config.monochrome = alpha ? 1 : 0;
- codec->internal->config.chromaSubsamplingX = (uint8_t)formatInfo.chromaShiftX;
- codec->internal->config.chromaSubsamplingY = (uint8_t)formatInfo.chromaShiftY;
-
if (encoder->tileRowsLog2 != 0) {
int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6);
if (rav1e_config_parse_int(rav1eConfig, "tile_rows", 1 << tileRowsLog2) == -1) {
@@ -217,11 +165,6 @@
return success;
}
-static void rav1eCodecGetConfigurationBox(avifCodec * codec, avifCodecConfigurationBox * outConfig)
-{
- memcpy(outConfig, &codec->internal->config, sizeof(avifCodecConfigurationBox));
-}
-
const char * avifCodecVersionRav1e(void)
{
return "0"; // https://github.com/xiph/rav1e/issues/1801
@@ -233,7 +176,6 @@
memset(codec, 0, sizeof(struct avifCodec));
codec->open = rav1eCodecOpen;
codec->encodeImage = rav1eCodecEncodeImage;
- codec->getConfigurationBox = rav1eCodecGetConfigurationBox;
codec->destroyInternal = rav1eCodecDestroyInternal;
codec->internal = (struct avifCodecInternal *)avifAlloc(sizeof(struct avifCodecInternal));
diff --git a/src/write.c b/src/write.c
index 23774a1..b9c6447 100644
--- a/src/write.c
+++ b/src/write.c
@@ -21,6 +21,7 @@
static const size_t alphaURNSize = sizeof(alphaURN);
static avifBool avifImageIsOpaque(avifImage * image);
+static void fillConfigBox(avifCodec * codec, avifImage * image, avifBool alpha);
static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg);
avifEncoder * avifEncoderCreate(void)
@@ -65,6 +66,17 @@
}
}
+ // -----------------------------------------------------------------------
+ // Pre-fill config boxes based on image (codec can query/update later)
+
+ fillConfigBox(codec[AVIF_CODEC_PLANES_COLOR], image, AVIF_FALSE);
+ if (codec[AVIF_CODEC_PLANES_ALPHA]) {
+ fillConfigBox(codec[AVIF_CODEC_PLANES_ALPHA], image, AVIF_TRUE);
+ }
+
+ // -----------------------------------------------------------------------
+ // Begin write stream
+
avifRWStream s;
avifRWStreamStart(&s, output);
@@ -273,9 +285,7 @@
++ipcoIndex;
ipmaPush(&ipmaColor, ipcoIndex);
- avifCodecConfigurationBox colorConfig;
- codec[AVIF_CODEC_PLANES_COLOR]->getConfigurationBox(codec[AVIF_CODEC_PLANES_COLOR], &colorConfig);
- writeConfigBox(&s, &colorConfig);
+ writeConfigBox(&s, &codec[AVIF_CODEC_PLANES_COLOR]->configBox);
++ipcoIndex;
ipmaPush(&ipmaColor, ipcoIndex);
@@ -287,9 +297,7 @@
++ipcoIndex;
ipmaPush(&ipmaAlpha, ipcoIndex);
- avifCodecConfigurationBox alphaConfig;
- codec[AVIF_CODEC_PLANES_ALPHA]->getConfigurationBox(codec[AVIF_CODEC_PLANES_ALPHA], &alphaConfig);
- writeConfigBox(&s, &alphaConfig);
+ writeConfigBox(&s, &codec[AVIF_CODEC_PLANES_ALPHA]->configBox);
++ipcoIndex;
ipmaPush(&ipmaAlpha, ipcoIndex);
@@ -409,6 +417,70 @@
return AVIF_TRUE;
}
+static void fillConfigBox(avifCodec * codec, avifImage * image, avifBool alpha)
+{
+ avifPixelFormatInfo formatInfo;
+ avifGetPixelFormatInfo(image->yuvFormat, &formatInfo);
+
+ // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only.
+ // Profile 1. 8-bit and 10-bit 4:4:4
+ // Profile 2. 8-bit and 10-bit 4:2:2
+ // 12-bit 4:0:0, 4:2:2 and 4:4:4
+ uint8_t seqProfile = 0;
+ if (image->depth == 12) {
+ // Only seqProfile 2 can handle 12 bit
+ seqProfile = 2;
+ } else {
+ // 8-bit or 10-bit
+
+ if (alpha) {
+ seqProfile = 0;
+ } else {
+ switch (image->yuvFormat) {
+ case AVIF_PIXEL_FORMAT_YUV444:
+ seqProfile = 1;
+ break;
+ case AVIF_PIXEL_FORMAT_YUV422:
+ seqProfile = 2;
+ break;
+ case AVIF_PIXEL_FORMAT_YUV420:
+ seqProfile = 0;
+ break;
+ case AVIF_PIXEL_FORMAT_YV12:
+ seqProfile = 0;
+ break;
+ case AVIF_PIXEL_FORMAT_NONE:
+ default:
+ break;
+ }
+ }
+ }
+
+ // TODO: Choose correct value from Annex A.3 table: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
+ uint8_t seqLevelIdx0 = 31;
+ if ((image->width <= 8192) && (image->height <= 4352) && ((image->width * image->height) <= 8912896)) {
+ // Image is 5.1 compatible
+ seqLevelIdx0 = 13; // 5.1
+ }
+
+ memset(&codec->configBox, 0, sizeof(avifCodecConfigurationBox));
+ codec->configBox.seqProfile = seqProfile;
+ codec->configBox.seqLevelIdx0 = seqLevelIdx0;
+ codec->configBox.seqTier0 = 0;
+ codec->configBox.highBitdepth = (image->depth > 8) ? 1 : 0;
+ codec->configBox.twelveBit = (image->depth == 12) ? 1 : 0;
+ codec->configBox.monochrome = alpha ? 1 : 0;
+ codec->configBox.chromaSubsamplingX = (uint8_t)formatInfo.chromaShiftX;
+ codec->configBox.chromaSubsamplingY = (uint8_t)formatInfo.chromaShiftY;
+
+ // TODO: choose the correct one from below:
+ // * 0 - CSP_UNKNOWN Unknown (in this case the source video transfer function must be signaled outside the AV1 bitstream)
+ // * 1 - CSP_VERTICAL Horizontally co-located with (0, 0) luma sample, vertical position in the middle between two luma samples
+ // * 2 - CSP_COLOCATED co-located with (0, 0) luma sample
+ // * 3 - CSP_RESERVED
+ codec->configBox.chromaSamplePosition = 0;
+}
+
static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg)
{
avifBoxMarker av1C = avifRWStreamWriteBox(s, "av1C", -1, 0);