Skip to content
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

Improve 'mini' read and write of float and brand #2400

Merged
merged 3 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions src/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static const char * avifGetConfigurationPropertyName(avifCodecType codecType)
typedef struct avifFileType
{
uint8_t majorBrand[4];
uint32_t minorVersion;
uint8_t minorVersion[4];
// If not null, points to a memory block of 4 * compatibleBrandsCount bytes.
const uint8_t * compatibleBrands;
int compatibleBrandsCount;
Expand Down Expand Up @@ -2254,6 +2254,8 @@ static avifBool avifParseContentLightLevelInformationBox(avifProperty * prop, co
// See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax.
static avifBool avifParseCodecConfiguration(avifROStream * s, avifCodecConfigurationBox * config, const char * configPropName, avifDiagnostics * diag)
{
const size_t av1COffset = s->offset;

uint32_t marker, version;
AVIF_CHECK(avifROStreamReadBits(s, &marker, /*bitCount=*/1)); // unsigned int (1) marker = 1;
if (!marker) {
Expand Down Expand Up @@ -2295,6 +2297,8 @@ static avifBool avifParseCodecConfiguration(avifROStream * s, avifCodecConfigura
// For simplicity, the constraints above are not enforced.
// The following is skipped by avifParseItemPropertyContainerBox().
// unsigned int (8) configOBUs[];

AVIF_CHECK(s->offset - av1COffset == 4); // Make sure avifParseCodecConfiguration() reads exactly 4 bytes.
return AVIF_TRUE;
}

Expand Down Expand Up @@ -3565,7 +3569,12 @@ static avifProperty * avifDecoderItemAddProperty(avifDecoderItem * item, const a
return itemProperty;
}

static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
static avifResult avifParseMinimizedImageBox(avifMeta * meta,
uint64_t rawOffset,
const uint8_t * raw,
size_t rawLen,
avifBool isAvifAccordingToMinorVersion,
avifDiagnostics * diag)
{
BEGIN_STREAM(s, raw, rawLen, diag, "Box[mini]");

Expand Down Expand Up @@ -3614,7 +3623,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
uint32_t bitDepth;
if (floatFlag) {
// bit(2) bit_depth_log2_minus4;
return AVIF_RESULT_NOT_IMPLEMENTED;
return AVIF_RESULT_BMFF_PARSE_FAILED; // Either invalid AVIF or unsupported non-AVIF.
} else {
uint32_t highBitDepthFlag;
AVIF_CHECKERR(avifROStreamReadBits(&s, &highBitDepthFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) high_bit_depth_flag;
Expand Down Expand Up @@ -3652,7 +3661,6 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
: AVIF_MATRIX_COEFFICIENTS_BT601; // 6
}

// Optional unless minor_version of FileTypeBox is a brand defining these
uint8_t infeType[4];
uint8_t codecConfigType[4];
if (hasExplicitCodecTypes) {
Expand All @@ -3669,9 +3677,9 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
return AVIF_RESULT_NOT_IMPLEMENTED;
}
#endif
AVIF_CHECKERR(!memcmp(infeType, "av01", 4), AVIF_RESULT_BMFF_PARSE_FAILED);
AVIF_CHECKERR(!memcmp(codecConfigType, "av1C", 4), AVIF_RESULT_BMFF_PARSE_FAILED);
AVIF_CHECKERR(!memcmp(infeType, "av01", 4) && !memcmp(codecConfigType, "av1C", 4), AVIF_RESULT_BMFF_PARSE_FAILED);
} else {
AVIF_CHECKERR(isAvifAccordingToMinorVersion, AVIF_RESULT_BMFF_PARSE_FAILED);
memcpy(infeType, "av01", 4);
memcpy(codecConfigType, "av1C", 4);
}
Expand Down Expand Up @@ -3825,7 +3833,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
}
// if (hdr_flag && gainmap_flag && gainmap_item_codec_config_size > 0)
// unsigned int(8) gainmap_item_codec_config[gainmap_item_codec_config_size];
avifCodecConfigurationBox mainItemCodecConfig = { 0 };
avifCodecConfigurationBox mainItemCodecConfig;
// 'av1C' always uses 4 bytes.
AVIF_CHECKERR(mainItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED);
AVIF_CHECKERR(avifParseCodecConfiguration(&s, &mainItemCodecConfig, (const char *)codecConfigType, diag),
Expand Down Expand Up @@ -4051,7 +4059,7 @@ static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, s
BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]");

AVIF_CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
AVIF_CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
AVIF_CHECK(avifROStreamRead(&s, ftyp->minorVersion, 4));

size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
if ((compatibleBrandsBytes % 4) != 0) {
Expand Down Expand Up @@ -4085,6 +4093,7 @@ static avifResult avifParse(avifDecoder * decoder)
avifBool miniSeen = AVIF_FALSE;
avifBool needsMini = AVIF_FALSE;
#endif
avifFileType ftyp = {};

for (;;) {
// Read just enough to get the next box header (a max of 32 bytes)
Expand Down Expand Up @@ -4153,19 +4162,25 @@ static avifResult avifParse(avifDecoder * decoder)

if (!memcmp(header.type, "ftyp", 4)) {
AVIF_CHECKERR(!ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
avifFileType ftyp;
AVIF_CHECKERR(avifParseFileTypeBox(&ftyp, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
if (!avifFileTypeIsCompatible(&ftyp)) {
return AVIF_RESULT_INVALID_FTYP;
}
AVIF_CHECKERR(avifFileTypeIsCompatible(&ftyp), AVIF_RESULT_INVALID_FTYP);
ftypSeen = AVIF_TRUE;
memcpy(data->majorBrand, ftyp.majorBrand, 4); // Remember the major brand for future AVIF_DECODER_SOURCE_AUTO decisions
needsMeta = avifFileTypeHasBrand(&ftyp, "avif");
needsMoov = avifFileTypeHasBrand(&ftyp, "avis");
#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI)
needsMini = avifFileTypeHasBrand(&ftyp, "mif3");
if (needsMini && needsMeta) {
return AVIF_RESULT_INVALID_FTYP;
if (needsMini) {
AVIF_CHECKERR(!needsMeta, AVIF_RESULT_INVALID_FTYP);
// Section O.2.1.2 of ISO/IEC 23008-12:2014, CDAM 2:
// When the 'mif3' brand is present as the major_brand of the FileTypeBox,
// the minor_version of the FileTypeBox shall be 0 or a brand that is either
// structurally compatible with the 'mif3' brand, such as a codec brand
// complying with the 'mif3' structural brand, or a brand to which the file
// conforms after the equivalent MetaBox has been transformed from
// MinimizedImageBox as specified in Clause O.4.
AVIF_CHECKERR(!memcmp(ftyp.minorVersion, "\0\0\0\0", 4) || !memcmp(ftyp.minorVersion, "avif", 4),
AVIF_RESULT_BMFF_PARSE_FAILED);
}
#endif // AVIF_ENABLE_EXPERIMENTAL_MINI
} else if (!memcmp(header.type, "meta", 4)) {
Expand All @@ -4179,7 +4194,9 @@ static avifResult avifParse(avifDecoder * decoder)
} else if (!memcmp(header.type, "mini", 4)) {
AVIF_CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
AVIF_CHECKRES(avifParseMinimizedImageBox(data->meta, boxOffset, boxContents.data, boxContents.size, data->diag));
const avifBool isAvifAccordingToMinorVersion = !memcmp(ftyp.minorVersion, "avif", 4);
AVIF_CHECKRES(
avifParseMinimizedImageBox(data->meta, boxOffset, boxContents.data, boxContents.size, isAvifAccordingToMinorVersion, data->diag));
miniSeen = AVIF_TRUE;
#endif
} else if (!memcmp(header.type, "moov", 4)) {
Expand Down
27 changes: 15 additions & 12 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -2472,15 +2472,15 @@ static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream *
AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 2)); // bit(2) version = 0;

// flags
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCodecTypes, 1)); // bit(1) explicit_codec_types_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, floatFlag, 1)); // bit(1) float_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, fullRange, 1)); // bit(1) full_range_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaItem ? 1 : 0, 1)); // bit(1) alpha_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCicp, 1)); // bit(1) explicit_cicp_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasHdr, 1)); // bit(1) hdr_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasIcc, 1)); // bit(1) icc_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->exif.size ? 1 : 0, 1)); // bit(1) exif_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->xmp.size ? 1 : 0, 1)); // bit(1) xmp_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCodecTypes, 1)); // bit(1) explicit_codec_types_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, floatFlag, 1)); // bit(1) float_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, fullRange, 1)); // bit(1) full_range_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaItem != 0, 1)); // bit(1) alpha_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCicp, 1)); // bit(1) explicit_cicp_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasHdr, 1)); // bit(1) hdr_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasIcc, 1)); // bit(1) icc_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->exif.size != 0, 1)); // bit(1) exif_flag;
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->xmp.size != 0, 1)); // bit(1) xmp_flag;

AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaSubsampling, 2)); // bit(2) chroma_subsampling;
AVIF_CHECKRES(avifRWStreamWriteBits(s, orientationMinus1, 3)); // bit(3) orientation_minus1;
Expand All @@ -2500,7 +2500,7 @@ static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream *

if (floatFlag) {
// bit(2) bit_depth_log2_minus4;
return AVIF_RESULT_NOT_IMPLEMENTED;
AVIF_ASSERT_OR_RETURN(AVIF_FALSE);
} else {
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->depth > 8, 1)); // bit(1) high_bit_depth_flag;
if (image->depth > 8) {
Expand All @@ -2523,11 +2523,10 @@ static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream *
}
}

// Optional unless minor_version of FileTypeBox is a brand defining these
if (hasExplicitCodecTypes) {
// bit(32) infe_type;
// bit(32) codec_config_type;
return AVIF_RESULT_NOT_IMPLEMENTED;
AVIF_ASSERT_OR_RETURN(AVIF_FALSE);
}

// High Dynamic Range properties
Expand Down Expand Up @@ -3654,6 +3653,8 @@ avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avif
// See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax.
static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfigurationBox * cfg)
{
const size_t av1COffset = s->offset;

AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/1)); // unsigned int (1) marker = 1;
AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/7)); // unsigned int (7) version = 1;

Expand All @@ -3676,6 +3677,8 @@ static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfiguratio
// there is no need to write any OBU here.
// See https://aomediacodec.github.io/av1-avif/v1.1.0.html#av1-configuration-item-property.
// unsigned int (8) configOBUs[];

AVIF_ASSERT_OR_RETURN(s->offset - av1COffset == 4); // Make sure writeCodecConfig() writes exactly 4 bytes.
return AVIF_RESULT_OK;
}

Expand Down
Loading