Skip to content

Commit 14d8e3c

Browse files
authored
Improve 'mini' read and write of float and brand (#2400)
Check minor_version field of FileTypeBox. Return proper error for samples in floating point format. Verify codec config length.
1 parent 56512dd commit 14d8e3c

File tree

2 files changed

+47
-27
lines changed

2 files changed

+47
-27
lines changed

src/read.c

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ static const char * avifGetConfigurationPropertyName(avifCodecType codecType)
7575
typedef struct avifFileType
7676
{
7777
uint8_t majorBrand[4];
78-
uint32_t minorVersion;
78+
uint8_t minorVersion[4];
7979
// If not null, points to a memory block of 4 * compatibleBrandsCount bytes.
8080
const uint8_t * compatibleBrands;
8181
int compatibleBrandsCount;
@@ -2254,6 +2254,8 @@ static avifBool avifParseContentLightLevelInformationBox(avifProperty * prop, co
22542254
// See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax.
22552255
static avifBool avifParseCodecConfiguration(avifROStream * s, avifCodecConfigurationBox * config, const char * configPropName, avifDiagnostics * diag)
22562256
{
2257+
const size_t av1COffset = s->offset;
2258+
22572259
uint32_t marker, version;
22582260
AVIF_CHECK(avifROStreamReadBits(s, &marker, /*bitCount=*/1)); // unsigned int (1) marker = 1;
22592261
if (!marker) {
@@ -2295,6 +2297,8 @@ static avifBool avifParseCodecConfiguration(avifROStream * s, avifCodecConfigura
22952297
// For simplicity, the constraints above are not enforced.
22962298
// The following is skipped by avifParseItemPropertyContainerBox().
22972299
// unsigned int (8) configOBUs[];
2300+
2301+
AVIF_CHECK(s->offset - av1COffset == 4); // Make sure avifParseCodecConfiguration() reads exactly 4 bytes.
22982302
return AVIF_TRUE;
22992303
}
23002304

@@ -3565,7 +3569,12 @@ static avifProperty * avifDecoderItemAddProperty(avifDecoderItem * item, const a
35653569
return itemProperty;
35663570
}
35673571

3568-
static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
3572+
static avifResult avifParseMinimizedImageBox(avifMeta * meta,
3573+
uint64_t rawOffset,
3574+
const uint8_t * raw,
3575+
size_t rawLen,
3576+
avifBool isAvifAccordingToMinorVersion,
3577+
avifDiagnostics * diag)
35693578
{
35703579
BEGIN_STREAM(s, raw, rawLen, diag, "Box[mini]");
35713580

@@ -3614,7 +3623,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
36143623
uint32_t bitDepth;
36153624
if (floatFlag) {
36163625
// bit(2) bit_depth_log2_minus4;
3617-
return AVIF_RESULT_NOT_IMPLEMENTED;
3626+
return AVIF_RESULT_BMFF_PARSE_FAILED; // Either invalid AVIF or unsupported non-AVIF.
36183627
} else {
36193628
uint32_t highBitDepthFlag;
36203629
AVIF_CHECKERR(avifROStreamReadBits(&s, &highBitDepthFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) high_bit_depth_flag;
@@ -3652,7 +3661,6 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
36523661
: AVIF_MATRIX_COEFFICIENTS_BT601; // 6
36533662
}
36543663

3655-
// Optional unless minor_version of FileTypeBox is a brand defining these
36563664
uint8_t infeType[4];
36573665
uint8_t codecConfigType[4];
36583666
if (hasExplicitCodecTypes) {
@@ -3669,9 +3677,9 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
36693677
return AVIF_RESULT_NOT_IMPLEMENTED;
36703678
}
36713679
#endif
3672-
AVIF_CHECKERR(!memcmp(infeType, "av01", 4), AVIF_RESULT_BMFF_PARSE_FAILED);
3673-
AVIF_CHECKERR(!memcmp(codecConfigType, "av1C", 4), AVIF_RESULT_BMFF_PARSE_FAILED);
3680+
AVIF_CHECKERR(!memcmp(infeType, "av01", 4) && !memcmp(codecConfigType, "av1C", 4), AVIF_RESULT_BMFF_PARSE_FAILED);
36743681
} else {
3682+
AVIF_CHECKERR(isAvifAccordingToMinorVersion, AVIF_RESULT_BMFF_PARSE_FAILED);
36753683
memcpy(infeType, "av01", 4);
36763684
memcpy(codecConfigType, "av1C", 4);
36773685
}
@@ -3825,7 +3833,7 @@ static avifResult avifParseMinimizedImageBox(avifMeta * meta, uint64_t rawOffset
38253833
}
38263834
// if (hdr_flag && gainmap_flag && gainmap_item_codec_config_size > 0)
38273835
// unsigned int(8) gainmap_item_codec_config[gainmap_item_codec_config_size];
3828-
avifCodecConfigurationBox mainItemCodecConfig = { 0 };
3836+
avifCodecConfigurationBox mainItemCodecConfig;
38293837
// 'av1C' always uses 4 bytes.
38303838
AVIF_CHECKERR(mainItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED);
38313839
AVIF_CHECKERR(avifParseCodecConfiguration(&s, &mainItemCodecConfig, (const char *)codecConfigType, diag),
@@ -4051,7 +4059,7 @@ static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, s
40514059
BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]");
40524060

40534061
AVIF_CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
4054-
AVIF_CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
4062+
AVIF_CHECK(avifROStreamRead(&s, ftyp->minorVersion, 4));
40554063

40564064
size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
40574065
if ((compatibleBrandsBytes % 4) != 0) {
@@ -4085,6 +4093,7 @@ static avifResult avifParse(avifDecoder * decoder)
40854093
avifBool miniSeen = AVIF_FALSE;
40864094
avifBool needsMini = AVIF_FALSE;
40874095
#endif
4096+
avifFileType ftyp = {};
40884097

40894098
for (;;) {
40904099
// Read just enough to get the next box header (a max of 32 bytes)
@@ -4173,19 +4182,25 @@ static avifResult avifParse(avifDecoder * decoder)
41734182

41744183
if (isFtyp) {
41754184
AVIF_CHECKERR(!ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
4176-
avifFileType ftyp;
41774185
AVIF_CHECKERR(avifParseFileTypeBox(&ftyp, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
4178-
if (!avifFileTypeIsCompatible(&ftyp)) {
4179-
return AVIF_RESULT_INVALID_FTYP;
4180-
}
4186+
AVIF_CHECKERR(avifFileTypeIsCompatible(&ftyp), AVIF_RESULT_INVALID_FTYP);
41814187
ftypSeen = AVIF_TRUE;
41824188
memcpy(data->majorBrand, ftyp.majorBrand, 4); // Remember the major brand for future AVIF_DECODER_SOURCE_AUTO decisions
41834189
needsMeta = avifFileTypeHasBrand(&ftyp, "avif");
41844190
needsMoov = avifFileTypeHasBrand(&ftyp, "avis");
41854191
#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI)
41864192
needsMini = avifFileTypeHasBrand(&ftyp, "mif3");
4187-
if (needsMini && needsMeta) {
4188-
return AVIF_RESULT_INVALID_FTYP;
4193+
if (needsMini) {
4194+
AVIF_CHECKERR(!needsMeta, AVIF_RESULT_INVALID_FTYP);
4195+
// Section O.2.1.2 of ISO/IEC 23008-12:2014, CDAM 2:
4196+
// When the 'mif3' brand is present as the major_brand of the FileTypeBox,
4197+
// the minor_version of the FileTypeBox shall be 0 or a brand that is either
4198+
// structurally compatible with the 'mif3' brand, such as a codec brand
4199+
// complying with the 'mif3' structural brand, or a brand to which the file
4200+
// conforms after the equivalent MetaBox has been transformed from
4201+
// MinimizedImageBox as specified in Clause O.4.
4202+
AVIF_CHECKERR(!memcmp(ftyp.minorVersion, "\0\0\0\0", 4) || !memcmp(ftyp.minorVersion, "avif", 4),
4203+
AVIF_RESULT_BMFF_PARSE_FAILED);
41894204
}
41904205
#endif // AVIF_ENABLE_EXPERIMENTAL_MINI
41914206
} else if (isMeta) {
@@ -4199,7 +4214,9 @@ static avifResult avifParse(avifDecoder * decoder)
41994214
} else if (isMini) {
42004215
AVIF_CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
42014216
AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
4202-
AVIF_CHECKRES(avifParseMinimizedImageBox(data->meta, boxOffset, boxContents.data, boxContents.size, data->diag));
4217+
const avifBool isAvifAccordingToMinorVersion = !memcmp(ftyp.minorVersion, "avif", 4);
4218+
AVIF_CHECKRES(
4219+
avifParseMinimizedImageBox(data->meta, boxOffset, boxContents.data, boxContents.size, isAvifAccordingToMinorVersion, data->diag));
42034220
miniSeen = AVIF_TRUE;
42044221
#endif
42054222
} else if (isMoov) {

src/write.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,15 +2472,15 @@ static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream *
24722472
AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 2)); // bit(2) version = 0;
24732473

24742474
// flags
2475-
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCodecTypes, 1)); // bit(1) explicit_codec_types_flag;
2476-
AVIF_CHECKRES(avifRWStreamWriteBits(s, floatFlag, 1)); // bit(1) float_flag;
2477-
AVIF_CHECKRES(avifRWStreamWriteBits(s, fullRange, 1)); // bit(1) full_range_flag;
2478-
AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaItem ? 1 : 0, 1)); // bit(1) alpha_flag;
2479-
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCicp, 1)); // bit(1) explicit_cicp_flag;
2480-
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasHdr, 1)); // bit(1) hdr_flag;
2481-
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasIcc, 1)); // bit(1) icc_flag;
2482-
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->exif.size ? 1 : 0, 1)); // bit(1) exif_flag;
2483-
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->xmp.size ? 1 : 0, 1)); // bit(1) xmp_flag;
2475+
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCodecTypes, 1)); // bit(1) explicit_codec_types_flag;
2476+
AVIF_CHECKRES(avifRWStreamWriteBits(s, floatFlag, 1)); // bit(1) float_flag;
2477+
AVIF_CHECKRES(avifRWStreamWriteBits(s, fullRange, 1)); // bit(1) full_range_flag;
2478+
AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaItem != 0, 1)); // bit(1) alpha_flag;
2479+
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCicp, 1)); // bit(1) explicit_cicp_flag;
2480+
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasHdr, 1)); // bit(1) hdr_flag;
2481+
AVIF_CHECKRES(avifRWStreamWriteBits(s, hasIcc, 1)); // bit(1) icc_flag;
2482+
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->exif.size != 0, 1)); // bit(1) exif_flag;
2483+
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->xmp.size != 0, 1)); // bit(1) xmp_flag;
24842484

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

25012501
if (floatFlag) {
25022502
// bit(2) bit_depth_log2_minus4;
2503-
return AVIF_RESULT_NOT_IMPLEMENTED;
2503+
AVIF_ASSERT_OR_RETURN(AVIF_FALSE);
25042504
} else {
25052505
AVIF_CHECKRES(avifRWStreamWriteBits(s, image->depth > 8, 1)); // bit(1) high_bit_depth_flag;
25062506
if (image->depth > 8) {
@@ -2523,11 +2523,10 @@ static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream *
25232523
}
25242524
}
25252525

2526-
// Optional unless minor_version of FileTypeBox is a brand defining these
25272526
if (hasExplicitCodecTypes) {
25282527
// bit(32) infe_type;
25292528
// bit(32) codec_config_type;
2530-
return AVIF_RESULT_NOT_IMPLEMENTED;
2529+
AVIF_ASSERT_OR_RETURN(AVIF_FALSE);
25312530
}
25322531

25332532
// High Dynamic Range properties
@@ -3654,6 +3653,8 @@ avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avif
36543653
// See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax.
36553654
static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfigurationBox * cfg)
36563655
{
3656+
const size_t av1COffset = s->offset;
3657+
36573658
AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/1)); // unsigned int (1) marker = 1;
36583659
AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/7)); // unsigned int (7) version = 1;
36593660

@@ -3676,6 +3677,8 @@ static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfiguratio
36763677
// there is no need to write any OBU here.
36773678
// See https://aomediacodec.github.io/av1-avif/v1.1.0.html#av1-configuration-item-property.
36783679
// unsigned int (8) configOBUs[];
3680+
3681+
AVIF_ASSERT_OR_RETURN(s->offset - av1COffset == 4); // Make sure writeCodecConfig() writes exactly 4 bytes.
36793682
return AVIF_RESULT_OK;
36803683
}
36813684

0 commit comments

Comments
 (0)