Redesign AVIF_DECODER_SOURCE_AUTO to honor the FileTypeBox's major brand
When using AVIF_DECODER_SOURCE_AUTO, use the major_brand field in the FileTypeBox ('ftyp') in the
beginning of the file as a hint to whether or not to treat this file as an AVIF sequence or an
items-based single image.
In practice (and currently in the field), this won't change pre-existing behavior as the vast
majority of AVIFs in existence right now are single images and signal the major brand 'avif'
anyway, and sequences generated by avifenc have always used the major brand 'avis'.
This design is more in the spirit of ISO-BMFF Section 4.3's explanation of major_brand:
Files would normally be externally identified (e.g. with a file extension or mime type) that
identifies the ‘best use’ (major brand), or the brand that the author believes will provide the
greatest compatibility.
This also would allow for currently unexercised use-cases such as "live photos", where an AVIF can
internally hold an image sequence recorded by a camera, but suggest (via the major brand) to only
display it as a single image, specifying one of the frame samples as an item.
diff --git a/src/read.c b/src/read.c
index e35bcb2..68784fe 100644
--- a/src/read.c
+++ b/src/read.c
@@ -721,6 +721,7 @@
avifImageGrid colorGrid;
avifImageGrid alphaGrid;
avifDecoderSource source;
+ uint8_t majorBrand[4]; // From the file's ftyp, used by AVIF_DECODER_SOURCE_AUTO
avifDiagnostics * diag; // Shallow copy; owned by avifDecoder
const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
avifBool cicpSet; // True if avifDecoder's image has had its CICP set correctly yet.
@@ -2761,6 +2762,7 @@
return 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");
} else if (!memcmp(header.type, "meta", 4)) {
@@ -3146,7 +3148,12 @@
data->sourceSampleTable = NULL; // Reset
if (decoder->requestedSource == AVIF_DECODER_SOURCE_AUTO) {
- if (data->tracks.count > 0) {
+ // Honor the major brand (avif or avis) if present, otherwise prefer avis (tracks) if possible.
+ if (!memcmp(data->majorBrand, "avis", 4)) {
+ data->source = AVIF_DECODER_SOURCE_TRACKS;
+ } else if (!memcmp(data->majorBrand, "avif", 4)) {
+ data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;
+ } else if (data->tracks.count > 0) {
data->source = AVIF_DECODER_SOURCE_TRACKS;
} else {
data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;