blob: 379b95061d17927200b83d1f6456d197e2216152 [file] [log] [blame]
Joe Drago444f0512019-01-23 17:03:24 -08001// Copyright 2019 Joe Drago. All rights reserved.
2// SPDX-License-Identifier: BSD-2-Clause
3
4#include "avif/internal.h"
5
Wan-Teh Changb856dc22020-09-28 13:00:56 -07006#include <assert.h>
Joe Drago4d5f4a42021-04-28 13:11:45 -07007#include <inttypes.h>
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07008#include <limits.h>
Joe Drago444f0512019-01-23 17:03:24 -08009#include <string.h>
10
Joe Dragocd1e4c32019-02-08 11:26:31 -080011#define AUXTYPE_SIZE 64
Joe Dragof6a42272019-11-21 15:21:41 -080012#define CONTENTTYPE_SIZE 64
Joe Drago8f7a3002019-02-07 19:35:37 -080013
Joe Drago6500fd62019-10-08 17:17:34 -070014// class VisualSampleEntry(codingname) extends SampleEntry(codingname) {
15// unsigned int(16) pre_defined = 0;
16// const unsigned int(16) reserved = 0;
17// unsigned int(32)[3] pre_defined = 0;
18// unsigned int(16) width;
19// unsigned int(16) height;
20// template unsigned int(32) horizresolution = 0x00480000; // 72 dpi
21// template unsigned int(32) vertresolution = 0x00480000; // 72 dpi
22// const unsigned int(32) reserved = 0;
23// template unsigned int(16) frame_count = 1;
24// string[32] compressorname;
25// template unsigned int(16) depth = 0x0018;
26// int(16) pre_defined = -1;
27// // other boxes from derived specifications
28// CleanApertureBox clap; // optional
29// PixelAspectRatioBox pasp; // optional
30// }
31static const size_t VISUALSAMPLEENTRY_SIZE = 78;
32
Joe Dragof6a42272019-11-21 15:21:41 -080033static const char xmpContentType[] = CONTENT_TYPE_XMP;
34static const size_t xmpContentTypeSize = sizeof(xmpContentType);
35
Joe Dragoc60ccae2021-03-31 10:30:48 -070036// The only supported ipma box values for both version and flags are [0,1], so there technically
37// can't be more than 4 unique tuples right now.
38#define MAX_IPMA_VERSION_AND_FLAGS_SEEN 4
39
Joe Dragobffba3b2021-05-26 15:46:10 -070040#define MAX_AV1_LAYER_COUNT 4
41
Joe Dragob13e5722019-02-08 19:07:25 -080042// ---------------------------------------------------------------------------
43// Box data structures
44
45// ftyp
46typedef struct avifFileType
47{
48 uint8_t majorBrand[4];
49 uint32_t minorVersion;
Wan-Teh Chang6da0a882020-07-01 12:19:31 -070050 // If not null, points to a memory block of 4 * compatibleBrandsCount bytes.
51 const uint8_t * compatibleBrands;
Joe Dragob13e5722019-02-08 19:07:25 -080052 int compatibleBrandsCount;
53} avifFileType;
54
55// ispe
Joe Drago8f7a3002019-02-07 19:35:37 -080056typedef struct avifImageSpatialExtents
57{
58 uint32_t width;
59 uint32_t height;
60} avifImageSpatialExtents;
Joe Drago444f0512019-01-23 17:03:24 -080061
Joe Dragob13e5722019-02-08 19:07:25 -080062// auxC
Joe Dragocd1e4c32019-02-08 11:26:31 -080063typedef struct avifAuxiliaryType
64{
65 char auxType[AUXTYPE_SIZE];
66} avifAuxiliaryType;
67
Joe Dragof6a42272019-11-21 15:21:41 -080068// infe mime content_type
69typedef struct avifContentType
70{
71 char contentType[CONTENTTYPE_SIZE];
72} avifContentType;
73
Joe Dragob13e5722019-02-08 19:07:25 -080074// colr
Joe Drago41eb62b2019-02-08 15:38:18 -080075typedef struct avifColourInformationBox
76{
Joe Dragoa0da4a42020-05-08 14:27:40 -070077 avifBool hasICC;
Joe Drago345aaa12019-09-25 13:42:12 -070078 const uint8_t * icc;
Joe Drago41eb62b2019-02-08 15:38:18 -080079 size_t iccSize;
Joe Dragoa0da4a42020-05-08 14:27:40 -070080
81 avifBool hasNCLX;
Wan-Teh Chang559def52021-02-01 14:25:31 -080082 avifColorPrimaries colorPrimaries;
83 avifTransferCharacteristics transferCharacteristics;
84 avifMatrixCoefficients matrixCoefficients;
Joe Dragoa0da4a42020-05-08 14:27:40 -070085 avifRange range;
Joe Drago41eb62b2019-02-08 15:38:18 -080086} avifColourInformationBox;
87
Joe Drago60421562020-04-23 11:32:26 -070088#define MAX_PIXI_PLANE_DEPTHS 4
89typedef struct avifPixelInformationProperty
90{
91 uint8_t planeDepths[MAX_PIXI_PLANE_DEPTHS];
92 uint8_t planeCount;
93} avifPixelInformationProperty;
94
Joe Dragobffba3b2021-05-26 15:46:10 -070095typedef struct avifOperatingPointSelectorProperty
96{
97 uint8_t opIndex;
98} avifOperatingPointSelectorProperty;
99
100typedef struct avifLayerSelectorProperty
101{
102 uint16_t layerID;
103} avifLayerSelectorProperty;
104
105typedef struct avifAV1LayeredImageIndexingProperty
106{
107 uint32_t layerSize[3];
108} avifAV1LayeredImageIndexingProperty;
109
Joe Dragob13e5722019-02-08 19:07:25 -0800110// ---------------------------------------------------------------------------
111// Top-level structures
112
Joe Drago9f2b87b2020-06-03 19:36:38 -0700113struct avifMeta;
114
Joe Dragoa72da5b2020-06-15 19:40:17 -0700115// Temporary storage for ipco/stsd contents until they can be associated and memcpy'd to an avifDecoderItem
Joe Drago8f7a3002019-02-07 19:35:37 -0800116typedef struct avifProperty
117{
118 uint8_t type[4];
Wan-Teh Chang2031dc12020-05-22 09:24:46 -0700119 union
120 {
121 avifImageSpatialExtents ispe;
122 avifAuxiliaryType auxC;
123 avifColourInformationBox colr;
124 avifCodecConfigurationBox av1C;
125 avifPixelAspectRatioBox pasp;
126 avifCleanApertureBox clap;
127 avifImageRotation irot;
128 avifImageMirror imir;
129 avifPixelInformationProperty pixi;
Joe Dragobffba3b2021-05-26 15:46:10 -0700130 avifOperatingPointSelectorProperty a1op;
131 avifLayerSelectorProperty lsel;
132 avifAV1LayeredImageIndexingProperty a1lx;
Wan-Teh Chang2031dc12020-05-22 09:24:46 -0700133 } u;
Joe Drago8f7a3002019-02-07 19:35:37 -0800134} avifProperty;
Joe Drago05559c92019-07-17 16:33:38 -0700135AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop);
Joe Drago8f7a3002019-02-07 19:35:37 -0800136
Joe Dragoa72da5b2020-06-15 19:40:17 -0700137static const avifProperty * avifPropertyArrayFind(const avifPropertyArray * properties, const char * type)
138{
139 for (uint32_t propertyIndex = 0; propertyIndex < properties->count; ++propertyIndex) {
140 avifProperty * prop = &properties->prop[propertyIndex];
141 if (!memcmp(prop->type, type, 4)) {
142 return prop;
143 }
144 }
145 return NULL;
146}
147
Joe Drago4bcdfde2020-11-13 17:50:55 -0800148AVIF_ARRAY_DECLARE(avifExtentArray, avifExtent, extent);
Joe Dragoa4956902020-08-28 01:24:35 -0700149
Joe Dragoa72da5b2020-06-15 19:40:17 -0700150// one "item" worth for decoding (all iref, iloc, iprp, etc refer to one of these)
151typedef struct avifDecoderItem
152{
153 uint32_t id;
Joe Dragoba1eb492020-06-22 17:05:04 -0700154 struct avifMeta * meta; // Unowned; A back-pointer for convenience
Joe Dragoa72da5b2020-06-15 19:40:17 -0700155 uint8_t type[4];
Joe Drago217056b2020-11-13 16:19:35 -0800156 size_t size;
Joe Drago1dea33e2021-09-14 17:12:39 -0700157 avifBool idatStored; // If true, offset is relative to the associated meta box's idat box (iloc construction_method==1)
158 uint32_t width; // Set from this item's ispe property, if present
159 uint32_t height; // Set from this item's ispe property, if present
Joe Dragoa72da5b2020-06-15 19:40:17 -0700160 avifContentType contentType;
161 avifPropertyArray properties;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800162 avifExtentArray extents; // All extent offsets/sizes
Joe Dragobe4cbb92020-09-21 12:14:05 -0700163 avifRWData mergedExtents; // if set, is a single contiguous block of this item's extents (unused when extents.count == 1)
164 avifBool ownsMergedExtents; // if true, mergedExtents must be freed when this item is destroyed
165 avifBool partialMergedExtents; // If true, mergedExtents doesn't have all of the item data yet
166 uint32_t thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
167 uint32_t auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID}
168 uint32_t descForID; // if non-zero, this item is a content description for Item #{descForID}
169 uint32_t dimgForID; // if non-zero, this item is a derived image for Item #{dimgForID}
Yuan Tonge4850be2021-01-22 14:21:25 +0800170 uint32_t premByID; // if non-zero, this item is premultiplied by Item #{premByID}
Joe Dragoa72da5b2020-06-15 19:40:17 -0700171 avifBool hasUnsupportedEssentialProperty; // If true, this item cites a property flagged as 'essential' that libavif doesn't support (yet). Ignore the item, if so.
Joe Dragobffba3b2021-05-26 15:46:10 -0700172 avifBool ipmaSeen; // if true, this item already received a property association
173 avifBool progressive; // if true, this item has progressive layers (a1lx), but does not select a specific layer (lsel)
Joe Dragoa72da5b2020-06-15 19:40:17 -0700174} avifDecoderItem;
175AVIF_ARRAY_DECLARE(avifDecoderItemArray, avifDecoderItem, item);
176
Joe Drago060d5342020-03-03 10:53:49 -0800177// grid storage
178typedef struct avifImageGrid
179{
Joe Drago79ebcf32020-11-18 22:37:10 -0800180 uint32_t rows; // Legal range: [1-256]
181 uint32_t columns; // Legal range: [1-256]
Joe Drago060d5342020-03-03 10:53:49 -0800182 uint32_t outputWidth;
183 uint32_t outputHeight;
184} avifImageGrid;
185
Joe Dragoae7e2c32019-07-18 15:22:25 -0700186// ---------------------------------------------------------------------------
187// avifTrack
188
189typedef struct avifSampleTableChunk
190{
191 uint64_t offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700192} avifSampleTableChunk;
193AVIF_ARRAY_DECLARE(avifSampleTableChunkArray, avifSampleTableChunk, chunk);
194
195typedef struct avifSampleTableSampleToChunk
196{
197 uint32_t firstChunk;
198 uint32_t samplesPerChunk;
199 uint32_t sampleDescriptionIndex;
200} avifSampleTableSampleToChunk;
201AVIF_ARRAY_DECLARE(avifSampleTableSampleToChunkArray, avifSampleTableSampleToChunk, sampleToChunk);
202
203typedef struct avifSampleTableSampleSize
204{
205 uint32_t size;
206} avifSampleTableSampleSize;
207AVIF_ARRAY_DECLARE(avifSampleTableSampleSizeArray, avifSampleTableSampleSize, sampleSize);
208
209typedef struct avifSampleTableTimeToSample
210{
211 uint32_t sampleCount;
212 uint32_t sampleDelta;
213} avifSampleTableTimeToSample;
214AVIF_ARRAY_DECLARE(avifSampleTableTimeToSampleArray, avifSampleTableTimeToSample, timeToSample);
215
Joe Drago22c1ad92019-09-26 12:46:50 -0700216typedef struct avifSyncSample
217{
218 uint32_t sampleNumber;
219} avifSyncSample;
220AVIF_ARRAY_DECLARE(avifSyncSampleArray, avifSyncSample, syncSample);
221
Joe Drago2c0924c2019-09-26 17:41:01 -0700222typedef struct avifSampleDescription
223{
224 uint8_t format[4];
Joe Dragoa72da5b2020-06-15 19:40:17 -0700225 avifPropertyArray properties;
Joe Drago2c0924c2019-09-26 17:41:01 -0700226} avifSampleDescription;
227AVIF_ARRAY_DECLARE(avifSampleDescriptionArray, avifSampleDescription, description);
228
Joe Dragoae7e2c32019-07-18 15:22:25 -0700229typedef struct avifSampleTable
230{
231 avifSampleTableChunkArray chunks;
Joe Drago2c0924c2019-09-26 17:41:01 -0700232 avifSampleDescriptionArray sampleDescriptions;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700233 avifSampleTableSampleToChunkArray sampleToChunks;
234 avifSampleTableSampleSizeArray sampleSizes;
235 avifSampleTableTimeToSampleArray timeToSamples;
Joe Drago22c1ad92019-09-26 12:46:50 -0700236 avifSyncSampleArray syncSamples;
Joe Drago370be3f2020-02-07 15:59:42 -0800237 uint32_t allSamplesSize; // If this is non-zero, sampleSizes will be empty and all samples will be this size
Joe Dragoae7e2c32019-07-18 15:22:25 -0700238} avifSampleTable;
239
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800240static void avifSampleTableDestroy(avifSampleTable * sampleTable);
241
Joe Drago46ea0582019-07-22 15:55:47 -0700242static avifSampleTable * avifSampleTableCreate()
Joe Dragoae7e2c32019-07-18 15:22:25 -0700243{
244 avifSampleTable * sampleTable = (avifSampleTable *)avifAlloc(sizeof(avifSampleTable));
245 memset(sampleTable, 0, sizeof(avifSampleTable));
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800246 if (!avifArrayCreate(&sampleTable->chunks, sizeof(avifSampleTableChunk), 16)) {
247 goto error;
248 }
249 if (!avifArrayCreate(&sampleTable->sampleDescriptions, sizeof(avifSampleDescription), 2)) {
250 goto error;
251 }
252 if (!avifArrayCreate(&sampleTable->sampleToChunks, sizeof(avifSampleTableSampleToChunk), 16)) {
253 goto error;
254 }
255 if (!avifArrayCreate(&sampleTable->sampleSizes, sizeof(avifSampleTableSampleSize), 16)) {
256 goto error;
257 }
258 if (!avifArrayCreate(&sampleTable->timeToSamples, sizeof(avifSampleTableTimeToSample), 16)) {
259 goto error;
260 }
261 if (!avifArrayCreate(&sampleTable->syncSamples, sizeof(avifSyncSample), 16)) {
262 goto error;
263 }
Joe Dragoae7e2c32019-07-18 15:22:25 -0700264 return sampleTable;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800265
266error:
267 avifSampleTableDestroy(sampleTable);
268 return NULL;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700269}
270
Joe Drago46ea0582019-07-22 15:55:47 -0700271static void avifSampleTableDestroy(avifSampleTable * sampleTable)
Joe Dragoae7e2c32019-07-18 15:22:25 -0700272{
273 avifArrayDestroy(&sampleTable->chunks);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700274 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
275 avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
276 avifArrayDestroy(&description->properties);
277 }
Joe Drago2c0924c2019-09-26 17:41:01 -0700278 avifArrayDestroy(&sampleTable->sampleDescriptions);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700279 avifArrayDestroy(&sampleTable->sampleToChunks);
280 avifArrayDestroy(&sampleTable->sampleSizes);
281 avifArrayDestroy(&sampleTable->timeToSamples);
Joe Drago22c1ad92019-09-26 12:46:50 -0700282 avifArrayDestroy(&sampleTable->syncSamples);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700283 avifFree(sampleTable);
284}
285
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700286static uint32_t avifSampleTableGetImageDelta(const avifSampleTable * sampleTable, int imageIndex)
Joe Drago46ea0582019-07-22 15:55:47 -0700287{
288 int maxSampleIndex = 0;
289 for (uint32_t i = 0; i < sampleTable->timeToSamples.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700290 const avifSampleTableTimeToSample * timeToSample = &sampleTable->timeToSamples.timeToSample[i];
Joe Drago46ea0582019-07-22 15:55:47 -0700291 maxSampleIndex += timeToSample->sampleCount;
292 if ((imageIndex < maxSampleIndex) || (i == (sampleTable->timeToSamples.count - 1))) {
293 return timeToSample->sampleDelta;
294 }
295 }
296
297 // TODO: fail here?
298 return 1;
299}
300
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700301static avifBool avifSampleTableHasFormat(const avifSampleTable * sampleTable, const char * format)
Joe Drago2c0924c2019-09-26 17:41:01 -0700302{
303 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
304 if (!memcmp(sampleTable->sampleDescriptions.description[i].format, format, 4)) {
305 return AVIF_TRUE;
306 }
307 }
308 return AVIF_FALSE;
309}
310
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700311static uint32_t avifCodecConfigurationBoxGetDepth(const avifCodecConfigurationBox * av1C)
Joe Drago6500fd62019-10-08 17:17:34 -0700312{
313 if (av1C->twelveBit) {
314 return 12;
315 } else if (av1C->highBitdepth) {
316 return 10;
317 }
318 return 8;
319}
320
Joe Drago4a70b472021-05-06 17:21:33 -0700321// This is used as a hint to validating the clap box in avifDecoderItemValidateAV1.
322static avifPixelFormat avifCodecConfigurationBoxGetFormat(const avifCodecConfigurationBox * av1C)
323{
324 if (av1C->monochrome) {
325 return AVIF_PIXEL_FORMAT_YUV400;
Wan-Teh Changa1aa19c2021-05-28 14:29:25 -0700326 } else if (av1C->chromaSubsamplingY == 1) {
Joe Drago4a70b472021-05-06 17:21:33 -0700327 return AVIF_PIXEL_FORMAT_YUV420;
Wan-Teh Changa1aa19c2021-05-28 14:29:25 -0700328 } else if (av1C->chromaSubsamplingX == 1) {
Joe Drago4a70b472021-05-06 17:21:33 -0700329 return AVIF_PIXEL_FORMAT_YUV422;
330 }
331 return AVIF_PIXEL_FORMAT_YUV444;
332}
333
Joe Dragoa72da5b2020-06-15 19:40:17 -0700334static const avifPropertyArray * avifSampleTableGetProperties(const avifSampleTable * sampleTable)
Joe Drago6500fd62019-10-08 17:17:34 -0700335{
336 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700337 const avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
Joe Dragoa72da5b2020-06-15 19:40:17 -0700338 if (!memcmp(description->format, "av01", 4)) {
339 return &description->properties;
Joe Drago6500fd62019-10-08 17:17:34 -0700340 }
341 }
Joe Drago00bcaaf2020-06-05 15:29:38 -0700342 return NULL;
Joe Drago6500fd62019-10-08 17:17:34 -0700343}
344
Joe Dragoae7e2c32019-07-18 15:22:25 -0700345// one video track ("trak" contents)
346typedef struct avifTrack
347{
348 uint32_t id;
Wan-Teh Chang53adb6a2021-02-20 16:58:43 -0800349 uint32_t auxForID; // if non-zero, this track is an auxC plane for Track #{auxForID}
350 uint32_t premByID; // if non-zero, this track is premultiplied by Track #{premByID}
Joe Dragoae7e2c32019-07-18 15:22:25 -0700351 uint32_t mediaTimescale;
352 uint64_t mediaDuration;
Joe Dragofc4144e2019-09-27 20:35:06 -0700353 uint32_t width;
354 uint32_t height;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700355 avifSampleTable * sampleTable;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700356 struct avifMeta * meta;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700357} avifTrack;
358AVIF_ARRAY_DECLARE(avifTrackArray, avifTrack, track);
359
360// ---------------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -0700361// avifCodecDecodeInput
362
Joe Drago399df4f2019-07-23 16:45:14 -0700363avifCodecDecodeInput * avifCodecDecodeInputCreate(void)
Joe Drago46ea0582019-07-22 15:55:47 -0700364{
365 avifCodecDecodeInput * decodeInput = (avifCodecDecodeInput *)avifAlloc(sizeof(avifCodecDecodeInput));
366 memset(decodeInput, 0, sizeof(avifCodecDecodeInput));
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800367 if (!avifArrayCreate(&decodeInput->samples, sizeof(avifDecodeSample), 1)) {
368 goto error;
369 }
Joe Drago46ea0582019-07-22 15:55:47 -0700370 return decodeInput;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800371
372error:
373 avifFree(decodeInput);
374 return NULL;
Joe Drago46ea0582019-07-22 15:55:47 -0700375}
376
Joe Drago8b34ad72019-07-22 16:56:32 -0700377void avifCodecDecodeInputDestroy(avifCodecDecodeInput * decodeInput)
Joe Drago46ea0582019-07-22 15:55:47 -0700378{
Joe Dragobe4cbb92020-09-21 12:14:05 -0700379 for (uint32_t sampleIndex = 0; sampleIndex < decodeInput->samples.count; ++sampleIndex) {
380 avifDecodeSample * sample = &decodeInput->samples.sample[sampleIndex];
381 if (sample->ownsData) {
382 avifRWDataFree((avifRWData *)&sample->data);
383 }
384 }
Joe Drago46ea0582019-07-22 15:55:47 -0700385 avifArrayDestroy(&decodeInput->samples);
386 avifFree(decodeInput);
387}
388
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700389// Returns how many samples are in the chunk.
390static uint32_t avifGetSampleCountOfChunk(const avifSampleTableSampleToChunkArray * sampleToChunks, uint32_t chunkIndex)
391{
392 uint32_t sampleCount = 0;
393 for (int sampleToChunkIndex = sampleToChunks->count - 1; sampleToChunkIndex >= 0; --sampleToChunkIndex) {
394 const avifSampleTableSampleToChunk * sampleToChunk = &sampleToChunks->sampleToChunk[sampleToChunkIndex];
395 if (sampleToChunk->firstChunk <= (chunkIndex + 1)) {
396 sampleCount = sampleToChunk->samplesPerChunk;
397 break;
398 }
399 }
400 return sampleCount;
401}
402
Joe Dragobffba3b2021-05-26 15:46:10 -0700403static avifBool avifCodecDecodeInputFillFromSampleTable(avifCodecDecodeInput * decodeInput,
404 avifSampleTable * sampleTable,
405 const uint32_t imageCountLimit,
406 const uint64_t sizeHint,
407 avifDiagnostics * diag)
Joe Drago46ea0582019-07-22 15:55:47 -0700408{
Joe Dragod2340b42021-03-14 13:20:02 -0700409 if (imageCountLimit) {
410 // Verify that the we're not about to exceed the frame count limit.
411
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700412 uint32_t imageCountLeft = imageCountLimit;
Joe Dragod2340b42021-03-14 13:20:02 -0700413 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
414 // First, figure out how many samples are in this chunk
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700415 uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex);
Joe Dragod2340b42021-03-14 13:20:02 -0700416 if (sampleCount == 0) {
417 // chunks with 0 samples are invalid
Joe Drago4d5f4a42021-04-28 13:11:45 -0700418 avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples");
Joe Dragod2340b42021-03-14 13:20:02 -0700419 return AVIF_FALSE;
420 }
421
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700422 if (sampleCount > imageCountLeft) {
Joe Dragod2340b42021-03-14 13:20:02 -0700423 // This file exceeds the imageCountLimit, bail out
Joe Drago4d5f4a42021-04-28 13:11:45 -0700424 avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit");
Joe Dragod2340b42021-03-14 13:20:02 -0700425 return AVIF_FALSE;
426 }
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700427 imageCountLeft -= sampleCount;
Joe Dragod2340b42021-03-14 13:20:02 -0700428 }
429 }
430
Joe Drago46ea0582019-07-22 15:55:47 -0700431 uint32_t sampleSizeIndex = 0;
432 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
433 avifSampleTableChunk * chunk = &sampleTable->chunks.chunk[chunkIndex];
434
435 // First, figure out how many samples are in this chunk
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700436 uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex);
Joe Drago46ea0582019-07-22 15:55:47 -0700437 if (sampleCount == 0) {
438 // chunks with 0 samples are invalid
Joe Drago4d5f4a42021-04-28 13:11:45 -0700439 avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples");
Joe Drago46ea0582019-07-22 15:55:47 -0700440 return AVIF_FALSE;
441 }
442
443 uint64_t sampleOffset = chunk->offset;
444 for (uint32_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
Joe Drago370be3f2020-02-07 15:59:42 -0800445 uint32_t sampleSize = sampleTable->allSamplesSize;
446 if (sampleSize == 0) {
447 if (sampleSizeIndex >= sampleTable->sampleSizes.count) {
448 // We've run out of samples to sum
Joe Drago4d5f4a42021-04-28 13:11:45 -0700449 avifDiagnosticsPrintf(diag, "Truncated sample table");
Joe Drago370be3f2020-02-07 15:59:42 -0800450 return AVIF_FALSE;
451 }
452 avifSampleTableSampleSize * sampleSizePtr = &sampleTable->sampleSizes.sampleSize[sampleSizeIndex];
453 sampleSize = sampleSizePtr->size;
Joe Drago46ea0582019-07-22 15:55:47 -0700454 }
455
Joe Dragoe3e3bfa2020-06-02 16:33:53 -0700456 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700457 sample->offset = sampleOffset;
458 sample->size = sampleSize;
Joe Drago9d195462021-06-14 14:14:35 -0700459 sample->spatialID = AVIF_SPATIAL_ID_UNSET; // Not filtering by spatial_id
460 sample->sync = AVIF_FALSE; // to potentially be set to true following the outer loop
Joe Drago46ea0582019-07-22 15:55:47 -0700461
Wan-Teh Chang3ca14242020-09-30 16:39:38 -0700462 if (sampleSize > UINT64_MAX - sampleOffset) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700463 avifDiagnosticsPrintf(diag,
Joe Drago48867292021-05-04 13:23:08 -0700464 "Sample table contains an offset/size pair which overflows: [%" PRIu64 " / %u]",
Joe Drago4d5f4a42021-04-28 13:11:45 -0700465 sampleOffset,
466 sampleSize);
Wan-Teh Chang3ca14242020-09-30 16:39:38 -0700467 return AVIF_FALSE;
468 }
Wan-Teh Chang136d7572020-10-08 15:13:42 -0700469 if (sizeHint && ((sampleOffset + sampleSize) > sizeHint)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700470 avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data");
Joe Drago34c0d312020-04-30 15:23:03 -0700471 return AVIF_FALSE;
472 }
Joe Drago46ea0582019-07-22 15:55:47 -0700473
Joe Drago370be3f2020-02-07 15:59:42 -0800474 sampleOffset += sampleSize;
Joe Drago46ea0582019-07-22 15:55:47 -0700475 ++sampleSizeIndex;
476 }
477 }
Joe Drago22c1ad92019-09-26 12:46:50 -0700478
479 // Mark appropriate samples as sync
480 for (uint32_t syncSampleIndex = 0; syncSampleIndex < sampleTable->syncSamples.count; ++syncSampleIndex) {
481 uint32_t frameIndex = sampleTable->syncSamples.syncSample[syncSampleIndex].sampleNumber - 1; // sampleNumber is 1-based
482 if (frameIndex < decodeInput->samples.count) {
483 decodeInput->samples.sample[frameIndex].sync = AVIF_TRUE;
484 }
485 }
486
487 // Assume frame 0 is sync, just in case the stss box is absent in the BMFF. (Unnecessary?)
488 if (decodeInput->samples.count > 0) {
489 decodeInput->samples.sample[0].sync = AVIF_TRUE;
490 }
Joe Drago46ea0582019-07-22 15:55:47 -0700491 return AVIF_TRUE;
492}
493
Joe Dragobffba3b2021-05-26 15:46:10 -0700494static avifBool avifCodecDecodeInputFillFromDecoderItem(avifCodecDecodeInput * decodeInput,
495 avifDecoderItem * item,
496 avifBool allowProgressive,
497 const uint32_t imageCountLimit,
498 const uint64_t sizeHint,
499 avifDiagnostics * diag)
500{
501 if (sizeHint && (item->size > sizeHint)) {
502 avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data");
503 return AVIF_FALSE;
504 }
505
506 uint8_t layerCount = 0;
507 size_t layerSizes[4] = { 0 };
508 const avifProperty * a1lxProp = avifPropertyArrayFind(&item->properties, "a1lx");
509 if (a1lxProp) {
510 // Calculate layer count and all layer sizes from the a1lx box, and then validate
511
512 size_t remainingSize = item->size;
513 for (int i = 0; i < 3; ++i) {
514 ++layerCount;
515
516 const size_t layerSize = (size_t)a1lxProp->u.a1lx.layerSize[i];
517 if (layerSize) {
518 if (layerSize >= remainingSize) { // >= instead of > because there must be room for the last layer
519 avifDiagnosticsPrintf(diag, "a1lx layer index [%d] does not fit in item size", i);
520 return AVIF_FALSE;
521 }
522 layerSizes[i] = layerSize;
523 remainingSize -= layerSize;
524 } else {
525 layerSizes[i] = remainingSize;
526 remainingSize = 0;
527 break;
528 }
529 }
530 if (remainingSize > 0) {
531 assert(layerCount == 3);
532 ++layerCount;
533 layerSizes[3] = remainingSize;
534 }
535 }
536
537 const avifProperty * lselProp = avifPropertyArrayFind(&item->properties, "lsel");
538 item->progressive = (a1lxProp && !lselProp); // Progressive images offer layers via the a1lxProp, but don't specify a layer selection with lsel.
539 if (lselProp) {
540 // Layer selection. This requires that the underlying AV1 codec decodes all layers,
541 // and then only returns the requested layer as a single frame. To the user of libavif,
542 // this appears to be a single frame.
543
544 decodeInput->allLayers = AVIF_TRUE;
545
546 size_t sampleSize = 0;
547 if (layerCount > 0) {
548 // Optimization: If we're selecting a layer that doesn't require the entire image's payload (hinted via the a1lx box)
549
550 if (lselProp->u.lsel.layerID >= layerCount) {
551 avifDiagnosticsPrintf(diag,
552 "lsel property requests layer index [%u] which isn't present in a1lx property ([%u] layers)",
553 lselProp->u.lsel.layerID,
554 layerCount);
555 return AVIF_FALSE;
556 }
557
558 for (uint8_t i = 0; i <= lselProp->u.lsel.layerID; ++i) {
559 sampleSize += layerSizes[i];
560 }
561 } else {
562 // This layer's payload subsection is unknown, just use the whole payload
563 sampleSize = item->size;
564 }
565
566 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
567 sample->itemID = item->id;
Joe Dragobffba3b2021-05-26 15:46:10 -0700568 sample->offset = 0;
569 sample->size = sampleSize;
Wan-Teh Chang6ff9c4e2021-07-13 15:22:42 -0700570 assert(lselProp->u.lsel.layerID < MAX_AV1_LAYER_COUNT);
Joe Drago9d195462021-06-14 14:14:35 -0700571 sample->spatialID = (uint8_t)lselProp->u.lsel.layerID;
Joe Dragobffba3b2021-05-26 15:46:10 -0700572 sample->sync = AVIF_TRUE;
Joe Drago8df04be2021-06-14 14:22:18 -0700573 } else if (allowProgressive && item->progressive) {
Joe Dragobffba3b2021-05-26 15:46:10 -0700574 // Progressive image. Decode all layers and expose them all to the user.
575
576 if (imageCountLimit && (layerCount > imageCountLimit)) {
577 avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit (progressive)");
578 return AVIF_FALSE;
579 }
580
581 decodeInput->allLayers = AVIF_TRUE;
582
583 size_t offset = 0;
584 for (int i = 0; i < layerCount; ++i) {
585 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
586 sample->itemID = item->id;
587 sample->offset = offset;
588 sample->size = layerSizes[i];
Joe Drago9d195462021-06-14 14:14:35 -0700589 sample->spatialID = AVIF_SPATIAL_ID_UNSET;
Joe Dragobffba3b2021-05-26 15:46:10 -0700590 sample->sync = (i == 0); // Assume all layers depend on the first layer
591
592 offset += layerSizes[i];
593 }
594 } else {
595 // Typical case: Use the entire item's payload for a single frame output
596
597 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
598 sample->itemID = item->id;
599 sample->offset = 0;
600 sample->size = item->size;
Joe Drago9d195462021-06-14 14:14:35 -0700601 sample->spatialID = AVIF_SPATIAL_ID_UNSET;
Joe Dragobffba3b2021-05-26 15:46:10 -0700602 sample->sync = AVIF_TRUE;
603 }
604 return AVIF_TRUE;
605}
606
Joe Drago46ea0582019-07-22 15:55:47 -0700607// ---------------------------------------------------------------------------
Joe Drago8f439092020-08-28 15:05:17 -0700608// Helper macros / functions
Joe Dragoa72da5b2020-06-15 19:40:17 -0700609
Joe Drago618ed5f2021-05-04 12:13:24 -0700610#define BEGIN_STREAM(VARNAME, PTR, SIZE, DIAG, CONTEXT) \
611 avifROStream VARNAME; \
612 avifROData VARNAME##_roData; \
613 VARNAME##_roData.data = PTR; \
614 VARNAME##_roData.size = SIZE; \
615 avifROStreamStart(&VARNAME, &VARNAME##_roData, DIAG, CONTEXT)
Joe Dragoa72da5b2020-06-15 19:40:17 -0700616
Joe Drago8f439092020-08-28 15:05:17 -0700617// Use this to keep track of whether or not a child box that must be unique (0 or 1 present) has
618// been seen yet, when parsing a parent box. If the "seen" bit is already set for a given box when
619// it is encountered during parse, an error is thrown. Which bit corresponds to which box is
620// dictated entirely by the calling function.
Joe Drago4d5f4a42021-04-28 13:11:45 -0700621static avifBool uniqueBoxSeen(uint32_t * uniqueBoxFlags, uint32_t whichFlag, const char * parentBoxType, const char * boxType, avifDiagnostics * diagnostics)
Joe Drago8f439092020-08-28 15:05:17 -0700622{
623 const uint32_t flag = 1 << whichFlag;
624 if (*uniqueBoxFlags & flag) {
625 // This box has already been seen. Error!
Joe Drago618ed5f2021-05-04 12:13:24 -0700626 avifDiagnosticsPrintf(diagnostics, "Box[%s] contains a duplicate unique box of type '%s'", parentBoxType, boxType);
Joe Drago8f439092020-08-28 15:05:17 -0700627 return AVIF_FALSE;
628 }
629
630 // Mark this box as seen.
631 *uniqueBoxFlags |= flag;
632 return AVIF_TRUE;
633}
634
Joe Dragoa72da5b2020-06-15 19:40:17 -0700635// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700636// avifDecoderData
Joe Dragoae7e2c32019-07-18 15:22:25 -0700637
Joe Drago060d5342020-03-03 10:53:49 -0800638typedef struct avifTile
639{
640 avifCodecDecodeInput * input;
641 struct avifCodec * codec;
642 avifImage * image;
Joe Drago46104d62021-05-27 18:17:29 -0700643 uint32_t width; // Either avifTrack.width or avifDecoderItem.width
644 uint32_t height; // Either avifTrack.height or avifDecoderItem.height
Joe Dragoe79bc372021-06-09 17:54:08 -0700645 uint8_t operatingPoint;
Joe Drago060d5342020-03-03 10:53:49 -0800646} avifTile;
647AVIF_ARRAY_DECLARE(avifTileArray, avifTile, tile);
648
Joe Dragoba1eb492020-06-22 17:05:04 -0700649// This holds one "meta" box (from the BMFF and HEIF standards) worth of relevant-to-AVIF information.
650// * If a meta box is parsed from the root level of the BMFF, it can contain the information about
651// "items" which might be color planes, alpha planes, or EXIF or XMP metadata.
652// * If a meta box is parsed from inside of a track ("trak") box, any metadata (EXIF/XMP) items inside
653// of that box are implicitly associated with that track.
Joe Drago9f2b87b2020-06-03 19:36:38 -0700654typedef struct avifMeta
Joe Drago444f0512019-01-23 17:03:24 -0800655{
Joe Dragoba1eb492020-06-22 17:05:04 -0700656 // Items (from HEIF) are the generic storage for any data that does not require timed processing
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700657 // (single image color planes, alpha planes, EXIF, XMP, etc). Each item has a unique integer ID >1,
658 // and is defined by a series of child boxes in a meta box:
Joe Dragoba1eb492020-06-22 17:05:04 -0700659 // * iloc - location: byte offset to item data, item size in bytes
660 // * iinf - information: type of item (color planes, alpha plane, EXIF, XMP)
661 // * ipco - properties: dimensions, aspect ratio, image transformations, references to other items
662 // * ipma - associations: Attaches an item in the properties list to a given item
663 //
664 // Items are lazily created in this array when any of the above boxes refer to one by a new (unseen) ID,
665 // and are then further modified/updated as new information for an item's ID is parsed.
Joe Drago800b47f2020-03-18 16:22:37 -0700666 avifDecoderItemArray items;
Joe Dragoba1eb492020-06-22 17:05:04 -0700667
668 // Any ipco boxes explained above are populated into this array as a staging area, which are
669 // then duplicated into the appropriate items upon encountering an item property association
670 // (ipma) box.
Joe Drago05559c92019-07-17 16:33:38 -0700671 avifPropertyArray properties;
Joe Dragoba1eb492020-06-22 17:05:04 -0700672
Joe Drago1dea33e2021-09-14 17:12:39 -0700673 // Filled with the contents of this meta box's "idat" box, which is raw data that an item can
674 // directly refer to in its item location box (iloc) instead of just giving an offset into the
675 // overall file. If all items' iloc boxes simply point at an offset/length in the file itself,
676 // this buffer will likely be empty.
677 avifRWData idat;
Joe Dragoba1eb492020-06-22 17:05:04 -0700678
679 // Ever-incrementing ID for uniquely identifying which 'meta' box contains an idat (when
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700680 // multiple meta boxes exist as BMFF siblings). Each time avifParseMetaBox() is called on an
Joe Dragoba1eb492020-06-22 17:05:04 -0700681 // avifMeta struct, this value is incremented. Any time an additional meta box is detected at
682 // the same "level" (root level, trak level, etc), this ID helps distinguish which meta box's
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700683 // "idat" is which, as items implicitly reference idat boxes that exist in the same meta
Joe Dragoba1eb492020-06-22 17:05:04 -0700684 // box.
685 uint32_t idatID;
686
687 // Contents of a pitm box, which signal which of the items in this file is the main image. For
688 // AVIF, this should point at an av01 type item containing color planes, and all other items
689 // are ignored unless they refer to this item in some way (alpha plane, EXIF/XMP metadata).
Joe Drago9f2b87b2020-06-03 19:36:38 -0700690 uint32_t primaryItemID;
691} avifMeta;
692
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800693static void avifMetaDestroy(avifMeta * meta);
694
Joe Drago9f2b87b2020-06-03 19:36:38 -0700695static avifMeta * avifMetaCreate()
696{
697 avifMeta * meta = (avifMeta *)avifAlloc(sizeof(avifMeta));
698 memset(meta, 0, sizeof(avifMeta));
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800699 if (!avifArrayCreate(&meta->items, sizeof(avifDecoderItem), 8)) {
700 goto error;
701 }
702 if (!avifArrayCreate(&meta->properties, sizeof(avifProperty), 16)) {
703 goto error;
704 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700705 return meta;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800706
707error:
708 avifMetaDestroy(meta);
709 return NULL;
Joe Drago9f2b87b2020-06-03 19:36:38 -0700710}
711
712static void avifMetaDestroy(avifMeta * meta)
713{
Joe Dragoa72da5b2020-06-15 19:40:17 -0700714 for (uint32_t i = 0; i < meta->items.count; ++i) {
715 avifDecoderItem * item = &meta->items.item[i];
716 avifArrayDestroy(&item->properties);
Joe Dragoa4956902020-08-28 01:24:35 -0700717 avifArrayDestroy(&item->extents);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700718 if (item->ownsMergedExtents) {
719 avifRWDataFree(&item->mergedExtents);
720 }
Joe Dragoa72da5b2020-06-15 19:40:17 -0700721 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700722 avifArrayDestroy(&meta->items);
723 avifArrayDestroy(&meta->properties);
Joe Drago1dea33e2021-09-14 17:12:39 -0700724 avifRWDataFree(&meta->idat);
Joe Drago9f2b87b2020-06-03 19:36:38 -0700725 avifFree(meta);
726}
727
728static avifDecoderItem * avifMetaFindItem(avifMeta * meta, uint32_t itemID)
729{
730 if (itemID == 0) {
731 return NULL;
732 }
733
734 for (uint32_t i = 0; i < meta->items.count; ++i) {
735 if (meta->items.item[i].id == itemID) {
736 return &meta->items.item[i];
737 }
738 }
739
740 avifDecoderItem * item = (avifDecoderItem *)avifArrayPushPtr(&meta->items);
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800741 if (!avifArrayCreate(&item->properties, sizeof(avifProperty), 16)) {
742 goto error;
743 }
744 if (!avifArrayCreate(&item->extents, sizeof(avifExtent), 1)) {
745 goto error;
746 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700747 item->id = itemID;
748 item->meta = meta;
749 return item;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800750
751error:
752 avifArrayDestroy(&item->extents);
753 avifArrayDestroy(&item->properties);
754 avifArrayPop(&meta->items);
755 return NULL;
Joe Drago9f2b87b2020-06-03 19:36:38 -0700756}
757
758typedef struct avifDecoderData
759{
Wan-Teh Chang6fc17582020-09-24 15:16:37 -0700760 avifMeta * meta; // The root-level meta box
Joe Dragoae7e2c32019-07-18 15:22:25 -0700761 avifTrackArray tracks;
Joe Drago060d5342020-03-03 10:53:49 -0800762 avifTileArray tiles;
763 unsigned int colorTileCount;
764 unsigned int alphaTileCount;
Yannis Guyond3e85ff2022-03-17 05:10:29 +0000765 unsigned int decodedColorTileCount;
766 unsigned int decodedAlphaTileCount;
Joe Drago060d5342020-03-03 10:53:49 -0800767 avifImageGrid colorGrid;
768 avifImageGrid alphaGrid;
Joe Drago46ea0582019-07-22 15:55:47 -0700769 avifDecoderSource source;
Joe Dragof131b782021-09-21 16:52:39 -0700770 uint8_t majorBrand[4]; // From the file's ftyp, used by AVIF_DECODER_SOURCE_AUTO
Joe Drago4d5f4a42021-04-28 13:11:45 -0700771 avifDiagnostics * diag; // Shallow copy; owned by avifDecoder
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700772 const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
Joe Dragoc00d5832020-08-13 16:03:28 -0700773 avifBool cicpSet; // True if avifDecoder's image has had its CICP set correctly yet.
774 // This allows nclx colr boxes to override AV1 CICP, as specified in the MIAF
775 // standard (ISO/IEC 23000-22:2019), section 7.3.6.4:
776 //
777 // "The colour information property takes precedence over any colour information in the image
778 // bitstream, i.e. if the property is present, colour information in the bitstream shall be ignored."
Joe Drago800b47f2020-03-18 16:22:37 -0700779} avifDecoderData;
Joe Drago444f0512019-01-23 17:03:24 -0800780
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800781static void avifDecoderDataDestroy(avifDecoderData * data);
782
Joe Drago800b47f2020-03-18 16:22:37 -0700783static avifDecoderData * avifDecoderDataCreate()
Joe Drago05559c92019-07-17 16:33:38 -0700784{
Joe Drago800b47f2020-03-18 16:22:37 -0700785 avifDecoderData * data = (avifDecoderData *)avifAlloc(sizeof(avifDecoderData));
786 memset(data, 0, sizeof(avifDecoderData));
Joe Drago9f2b87b2020-06-03 19:36:38 -0700787 data->meta = avifMetaCreate();
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800788 if (!avifArrayCreate(&data->tracks, sizeof(avifTrack), 2)) {
789 goto error;
790 }
791 if (!avifArrayCreate(&data->tiles, sizeof(avifTile), 8)) {
792 goto error;
793 }
Joe Drago05559c92019-07-17 16:33:38 -0700794 return data;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800795
796error:
797 avifDecoderDataDestroy(data);
798 return NULL;
Joe Drago05559c92019-07-17 16:33:38 -0700799}
800
Joe Drago800b47f2020-03-18 16:22:37 -0700801static void avifDecoderDataResetCodec(avifDecoderData * data)
Joe Drago05559c92019-07-17 16:33:38 -0700802{
Joe Drago060d5342020-03-03 10:53:49 -0800803 for (unsigned int i = 0; i < data->tiles.count; ++i) {
804 avifTile * tile = &data->tiles.tile[i];
Joe Drago9d368782020-03-04 17:53:17 -0800805 if (tile->image) {
806 avifImageFreePlanes(tile->image, AVIF_PLANES_ALL); // forget any pointers into codec image buffers
807 }
Joe Drago060d5342020-03-03 10:53:49 -0800808 if (tile->codec) {
809 avifCodecDestroy(tile->codec);
810 tile->codec = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -0700811 }
812 }
Yannis Guyond3e85ff2022-03-17 05:10:29 +0000813 data->decodedColorTileCount = 0;
814 data->decodedAlphaTileCount = 0;
Joe Drago46ea0582019-07-22 15:55:47 -0700815}
816
Joe Dragoe79bc372021-06-09 17:54:08 -0700817static avifTile * avifDecoderDataCreateTile(avifDecoderData * data, uint32_t width, uint32_t height, uint8_t operatingPoint)
Joe Drago060d5342020-03-03 10:53:49 -0800818{
819 avifTile * tile = (avifTile *)avifArrayPushPtr(&data->tiles);
820 tile->image = avifImageCreateEmpty();
Wan-Teh Changd8911872022-02-14 14:15:00 -0800821 if (!tile->image) {
822 goto error;
823 }
Joe Drago060d5342020-03-03 10:53:49 -0800824 tile->input = avifCodecDecodeInputCreate();
Wan-Teh Changd8911872022-02-14 14:15:00 -0800825 if (!tile->input) {
826 goto error;
827 }
Joe Drago46104d62021-05-27 18:17:29 -0700828 tile->width = width;
829 tile->height = height;
Joe Dragoe79bc372021-06-09 17:54:08 -0700830 tile->operatingPoint = operatingPoint;
Joe Drago060d5342020-03-03 10:53:49 -0800831 return tile;
Wan-Teh Changd8911872022-02-14 14:15:00 -0800832
833error:
834 if (tile->input) {
835 avifCodecDecodeInputDestroy(tile->input);
836 }
837 if (tile->image) {
838 avifImageDestroy(tile->image);
839 }
840 avifArrayPop(&data->tiles);
841 return NULL;
Joe Drago060d5342020-03-03 10:53:49 -0800842}
843
Joe Dragoa72da5b2020-06-15 19:40:17 -0700844static avifTrack * avifDecoderDataCreateTrack(avifDecoderData * data)
845{
846 avifTrack * track = (avifTrack *)avifArrayPushPtr(&data->tracks);
847 track->meta = avifMetaCreate();
848 return track;
849}
850
Joe Drago800b47f2020-03-18 16:22:37 -0700851static void avifDecoderDataClearTiles(avifDecoderData * data)
Joe Drago060d5342020-03-03 10:53:49 -0800852{
853 for (unsigned int i = 0; i < data->tiles.count; ++i) {
854 avifTile * tile = &data->tiles.tile[i];
855 if (tile->input) {
856 avifCodecDecodeInputDestroy(tile->input);
857 tile->input = NULL;
858 }
859 if (tile->codec) {
860 avifCodecDestroy(tile->codec);
861 tile->codec = NULL;
862 }
863 if (tile->image) {
864 avifImageDestroy(tile->image);
865 tile->image = NULL;
866 }
867 }
868 data->tiles.count = 0;
869 data->colorTileCount = 0;
870 data->alphaTileCount = 0;
Yannis Guyond3e85ff2022-03-17 05:10:29 +0000871 data->decodedColorTileCount = 0;
872 data->decodedAlphaTileCount = 0;
Joe Drago060d5342020-03-03 10:53:49 -0800873}
874
Joe Drago800b47f2020-03-18 16:22:37 -0700875static void avifDecoderDataDestroy(avifDecoderData * data)
Joe Drago46ea0582019-07-22 15:55:47 -0700876{
Joe Drago9f2b87b2020-06-03 19:36:38 -0700877 avifMetaDestroy(data->meta);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700878 for (uint32_t i = 0; i < data->tracks.count; ++i) {
Joe Dragoa72da5b2020-06-15 19:40:17 -0700879 avifTrack * track = &data->tracks.track[i];
880 if (track->sampleTable) {
881 avifSampleTableDestroy(track->sampleTable);
882 }
883 if (track->meta) {
884 avifMetaDestroy(track->meta);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700885 }
886 }
887 avifArrayDestroy(&data->tracks);
Joe Drago800b47f2020-03-18 16:22:37 -0700888 avifDecoderDataClearTiles(data);
Joe Drago060d5342020-03-03 10:53:49 -0800889 avifArrayDestroy(&data->tiles);
Joe Drago05559c92019-07-17 16:33:38 -0700890 avifFree(data);
891}
892
Joe Drago4bcdfde2020-11-13 17:50:55 -0800893// This returns the max extent that has to be read in order to decode this item. If
894// the item is stored in an idat, the data has already been read during Parse() and
895// this function will return AVIF_RESULT_OK with a 0-byte extent.
Joe Dragoc0758eb2021-06-23 17:52:47 -0700896static avifResult avifDecoderItemMaxExtent(const avifDecoderItem * item, const avifDecodeSample * sample, avifExtent * outExtent)
Joe Drago4bcdfde2020-11-13 17:50:55 -0800897{
898 if (item->extents.count == 0) {
899 return AVIF_RESULT_TRUNCATED_DATA;
900 }
901
Joe Drago1dea33e2021-09-14 17:12:39 -0700902 if (item->idatStored) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800903 // construction_method: idat(1)
904
Joe Drago1dea33e2021-09-14 17:12:39 -0700905 if (item->meta->idat.size > 0) {
906 // Already read from a meta box during Parse()
907 memset(outExtent, 0, sizeof(avifExtent));
908 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800909 }
910
Wan-Teh Chang80275392020-11-17 12:35:11 -0800911 // no associated idat box was found in the meta box, bail out
Joe Drago4bcdfde2020-11-13 17:50:55 -0800912 return AVIF_RESULT_NO_CONTENT;
913 }
914
915 // construction_method: file(0)
916
Joe Dragoc0758eb2021-06-23 17:52:47 -0700917 if (sample->size == 0) {
918 return AVIF_RESULT_TRUNCATED_DATA;
919 }
Joe Drago2dde71d2021-06-24 15:40:22 -0700920 uint64_t remainingOffset = sample->offset;
Joe Dragoc0758eb2021-06-23 17:52:47 -0700921 size_t remainingBytes = sample->size; // This may be smaller than item->size if the item is progressive
922
Wan-Teh Chang15584522020-11-17 14:11:12 -0800923 // Assert that the for loop below will execute at least one iteration.
924 assert(item->extents.count != 0);
925 uint64_t minOffset = UINT64_MAX;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800926 uint64_t maxOffset = 0;
927 for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) {
928 avifExtent * extent = &item->extents.extent[extentIter];
929
Joe Drago2dde71d2021-06-24 15:40:22 -0700930 // Make local copies of extent->offset and extent->size as they might need to be adjusted
931 // due to the sample's offset.
932 uint64_t startOffset = extent->offset;
933 size_t extentSize = extent->size;
934 if (remainingOffset) {
935 if (remainingOffset >= extentSize) {
936 remainingOffset -= extentSize;
937 continue;
938 } else {
939 if (remainingOffset > UINT64_MAX - startOffset) {
940 return AVIF_RESULT_BMFF_PARSE_FAILED;
941 }
942 startOffset += remainingOffset;
943 extentSize -= remainingOffset;
944 remainingOffset = 0;
945 }
946 }
947
948 const size_t usedExtentSize = (extentSize < remainingBytes) ? extentSize : remainingBytes;
949
950 if (usedExtentSize > UINT64_MAX - startOffset) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800951 return AVIF_RESULT_BMFF_PARSE_FAILED;
952 }
Joe Drago2dde71d2021-06-24 15:40:22 -0700953 const uint64_t endOffset = startOffset + usedExtentSize;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800954
Joe Drago2dde71d2021-06-24 15:40:22 -0700955 if (minOffset > startOffset) {
956 minOffset = startOffset;
Wan-Teh Chang15584522020-11-17 14:11:12 -0800957 }
958 if (maxOffset < endOffset) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800959 maxOffset = endOffset;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800960 }
Joe Dragoc0758eb2021-06-23 17:52:47 -0700961
962 remainingBytes -= usedExtentSize;
963 if (remainingBytes == 0) {
964 // We've got enough bytes for this sample.
965 break;
966 }
967 }
968
969 if (remainingBytes != 0) {
970 return AVIF_RESULT_TRUNCATED_DATA;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800971 }
972
973 outExtent->offset = minOffset;
Wan-Teh Changd69958e2020-11-17 12:14:27 -0800974 const uint64_t extentLength = maxOffset - minOffset;
975 if (extentLength > SIZE_MAX) {
976 return AVIF_RESULT_BMFF_PARSE_FAILED;
977 }
978 outExtent->size = (size_t)extentLength;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800979 return AVIF_RESULT_OK;
980}
981
Joe Dragobffba3b2021-05-26 15:46:10 -0700982static uint8_t avifDecoderItemOperatingPoint(const avifDecoderItem * item)
983{
984 const avifProperty * a1opProp = avifPropertyArrayFind(&item->properties, "a1op");
985 if (a1opProp) {
986 return a1opProp->u.a1op.opIndex;
987 }
988 return 0; // default
989}
990
Joe Drago9b942c22021-05-06 12:33:12 -0700991static avifResult avifDecoderItemValidateAV1(const avifDecoderItem * item, avifDiagnostics * diag, const avifStrictFlags strictFlags)
Joe Dragoc10c6be2021-03-31 08:55:19 -0700992{
993 const avifProperty * av1CProp = avifPropertyArrayFind(&item->properties, "av1C");
994 if (!av1CProp) {
995 // An av1C box is mandatory in all valid AVIF configurations. Bail out.
Wan-Teh Chang455c9d62021-10-20 12:13:20 -0700996 avifDiagnosticsPrintf(diag, "Item ID %u of type '%.4s' is missing mandatory av1C property", item->id, (const char *)item->type);
Joe Dragoc10c6be2021-03-31 08:55:19 -0700997 return AVIF_RESULT_BMFF_PARSE_FAILED;
998 }
Joe Dragoc10c6be2021-03-31 08:55:19 -0700999
1000 const avifProperty * pixiProp = avifPropertyArrayFind(&item->properties, "pixi");
Joe Drago9b942c22021-05-06 12:33:12 -07001001 if (!pixiProp && (strictFlags & AVIF_STRICT_PIXI_REQUIRED)) {
Joe Dragoc10c6be2021-03-31 08:55:19 -07001002 // A pixi box is mandatory in all valid AVIF configurations. Bail out.
Wan-Teh Chang455c9d62021-10-20 12:13:20 -07001003 avifDiagnosticsPrintf(diag,
1004 "[Strict] Item ID %u of type '%.4s' is missing mandatory pixi property",
1005 item->id,
1006 (const char *)item->type);
Joe Dragoc10c6be2021-03-31 08:55:19 -07001007 return AVIF_RESULT_BMFF_PARSE_FAILED;
1008 }
1009
Joe Drago9b942c22021-05-06 12:33:12 -07001010 if (pixiProp) {
Joe Drago4a70b472021-05-06 17:21:33 -07001011 const uint32_t av1CDepth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
Joe Drago9b942c22021-05-06 12:33:12 -07001012 for (uint8_t i = 0; i < pixiProp->u.pixi.planeCount; ++i) {
1013 if (pixiProp->u.pixi.planeDepths[i] != av1CDepth) {
1014 // pixi depth must match av1C depth
1015 avifDiagnosticsPrintf(diag,
1016 "Item ID %u depth specified by pixi property [%u] does not match av1C property depth [%u]",
1017 item->id,
1018 pixiProp->u.pixi.planeDepths[i],
1019 av1CDepth);
1020 return AVIF_RESULT_BMFF_PARSE_FAILED;
1021 }
Joe Dragoc10c6be2021-03-31 08:55:19 -07001022 }
1023 }
Joe Drago4a70b472021-05-06 17:21:33 -07001024
1025 if (strictFlags & AVIF_STRICT_CLAP_VALID) {
1026 const avifProperty * clapProp = avifPropertyArrayFind(&item->properties, "clap");
1027 if (clapProp) {
1028 const avifProperty * ispeProp = avifPropertyArrayFind(&item->properties, "ispe");
1029 if (!ispeProp) {
Joe Dragof5b98a62021-05-10 12:25:29 -07001030 avifDiagnosticsPrintf(diag,
1031 "[Strict] Item ID %u is missing an ispe property, so its clap property cannot be validated",
1032 item->id);
Joe Drago4a70b472021-05-06 17:21:33 -07001033 return AVIF_RESULT_BMFF_PARSE_FAILED;
1034 }
1035
1036 avifCropRect cropRect;
1037 const uint32_t imageW = ispeProp->u.ispe.width;
1038 const uint32_t imageH = ispeProp->u.ispe.height;
1039 const avifPixelFormat av1CFormat = avifCodecConfigurationBoxGetFormat(&av1CProp->u.av1C);
1040 avifBool validClap = avifCropRectConvertCleanApertureBox(&cropRect, &clapProp->u.clap, imageW, imageH, av1CFormat, diag);
1041 if (!validClap) {
1042 return AVIF_RESULT_BMFF_PARSE_FAILED;
1043 }
1044 }
1045 }
Joe Dragoc10c6be2021-03-31 08:55:19 -07001046 return AVIF_RESULT_OK;
1047}
1048
Joe Dragobffba3b2021-05-26 15:46:10 -07001049static avifResult avifDecoderItemRead(avifDecoderItem * item,
1050 avifIO * io,
1051 avifROData * outData,
1052 size_t offset,
1053 size_t partialByteCount,
1054 avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08001055{
Joe Dragobe4cbb92020-09-21 12:14:05 -07001056 if (item->mergedExtents.data && !item->partialMergedExtents) {
Joe Dragoa4956902020-08-28 01:24:35 -07001057 // Multiple extents have already been concatenated for this item, just return it
Joe Drago8df04be2021-06-14 14:22:18 -07001058 if (offset >= item->mergedExtents.size) {
1059 avifDiagnosticsPrintf(diag, "Item ID %u read has overflowing offset", item->id);
1060 return AVIF_RESULT_TRUNCATED_DATA;
1061 }
1062 outData->data = item->mergedExtents.data + offset;
1063 outData->size = item->mergedExtents.size - offset;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001064 return AVIF_RESULT_OK;
1065 }
1066
1067 if (item->extents.count == 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001068 avifDiagnosticsPrintf(diag, "Item ID %u has zero extents", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001069 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragoa4956902020-08-28 01:24:35 -07001070 }
1071
1072 // Find this item's source of all extents' data, based on the construction method
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -07001073 const avifRWData * idatBuffer = NULL;
Joe Drago1dea33e2021-09-14 17:12:39 -07001074 if (item->idatStored) {
Joe Dragof6a42272019-11-21 15:21:41 -08001075 // construction_method: idat(1)
1076
Joe Drago1dea33e2021-09-14 17:12:39 -07001077 if (item->meta->idat.size > 0) {
1078 idatBuffer = &item->meta->idat;
1079 } else {
Wan-Teh Chang80275392020-11-17 12:35:11 -08001080 // no associated idat box was found in the meta box, bail out
Joe Drago618ed5f2021-05-04 12:13:24 -07001081 avifDiagnosticsPrintf(diag, "Item ID %u is stored in an idat, but no associated idat box was found", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001082 return AVIF_RESULT_NO_CONTENT;
Joe Dragof6a42272019-11-21 15:21:41 -08001083 }
1084 }
1085
Joe Dragobe4cbb92020-09-21 12:14:05 -07001086 // Merge extents into a single contiguous buffer
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001087 if ((io->sizeHint > 0) && (item->size > io->sizeHint)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001088 // Sanity check: somehow the sum of extents exceeds the entire file or idat size!
1089 avifDiagnosticsPrintf(diag, "Item ID %u reported size failed size hint sanity check. Truncated data?", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001090 return AVIF_RESULT_TRUNCATED_DATA;
Joe Drago8f439092020-08-28 15:05:17 -07001091 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001092
Wan-Teh Chang905296e2021-07-13 16:55:45 -07001093 if (offset >= item->size) {
Joe Dragobffba3b2021-05-26 15:46:10 -07001094 avifDiagnosticsPrintf(diag, "Item ID %u read has overflowing offset", item->id);
1095 return AVIF_RESULT_TRUNCATED_DATA;
1096 }
Wan-Teh Chang905296e2021-07-13 16:55:45 -07001097 const size_t maxOutputSize = item->size - offset;
1098 const size_t readOutputSize = (partialByteCount && (partialByteCount < maxOutputSize)) ? partialByteCount : maxOutputSize;
Joe Dragobffba3b2021-05-26 15:46:10 -07001099 const size_t totalBytesToRead = offset + readOutputSize;
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001100
Joe Dragobe4cbb92020-09-21 12:14:05 -07001101 // If there is a single extent for this item and the source of the read buffer is going to be
1102 // persistent for the lifetime of the avifDecoder (whether it comes from its own internal
1103 // idatBuffer or from a known-persistent IO), we can avoid buffer duplication and just use the
1104 // preexisting buffer.
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001105 avifBool singlePersistentBuffer = ((item->extents.count == 1) && (idatBuffer || io->persistent));
Joe Dragobe4cbb92020-09-21 12:14:05 -07001106 if (!singlePersistentBuffer) {
Joe Dragocb2660e2021-08-19 02:24:31 -07001107 // Always allocate the item's full size here, as progressive image decodes will do partial
1108 // reads into this buffer and begin feeding the buffer to the underlying AV1 decoder, but
1109 // will then write more into this buffer without flushing the AV1 decoder (which is still
1110 // holding the address of the previous allocation of this buffer). This strategy avoids
1111 // use-after-free issues in the AV1 decoder and unnecessary reallocs as a typical
1112 // progressive decode use case will eventually decode the final layer anyway.
1113 avifRWDataRealloc(&item->mergedExtents, item->size);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001114 item->ownsMergedExtents = AVIF_TRUE;
1115 }
1116
1117 // Set this until we manage to fill the entire mergedExtents buffer
1118 item->partialMergedExtents = AVIF_TRUE;
Joe Drago8f439092020-08-28 15:05:17 -07001119
Joe Dragoa4956902020-08-28 01:24:35 -07001120 uint8_t * front = item->mergedExtents.data;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001121 size_t remainingBytes = totalBytesToRead;
Joe Dragoa4956902020-08-28 01:24:35 -07001122 for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) {
Joe Drago4bcdfde2020-11-13 17:50:55 -08001123 avifExtent * extent = &item->extents.extent[extentIter];
Joe Dragobe4cbb92020-09-21 12:14:05 -07001124
1125 size_t bytesToRead = extent->size;
1126 if (bytesToRead > remainingBytes) {
1127 bytesToRead = remainingBytes;
Joe Dragoa4956902020-08-28 01:24:35 -07001128 }
1129
Joe Dragobe4cbb92020-09-21 12:14:05 -07001130 avifROData offsetBuffer;
1131 if (idatBuffer) {
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001132 if (extent->offset > idatBuffer->size) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001133 avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent offset in idat buffer", item->id);
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001134 return AVIF_RESULT_BMFF_PARSE_FAILED;
1135 }
Wan-Teh Changa17693d2021-06-02 14:57:18 -07001136 // Since extent->offset (a uint64_t) is not bigger than idatBuffer->size (a size_t),
1137 // it is safe to cast extent->offset to size_t.
1138 const size_t extentOffset = (size_t)extent->offset;
1139 if (extent->size > idatBuffer->size - extentOffset) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001140 avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent size in idat buffer", item->id);
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001141 return AVIF_RESULT_BMFF_PARSE_FAILED;
1142 }
Wan-Teh Changa17693d2021-06-02 14:57:18 -07001143 offsetBuffer.data = idatBuffer->data + extentOffset;
1144 offsetBuffer.size = idatBuffer->size - extentOffset;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001145 } else {
1146 // construction_method: file(0)
1147
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001148 if ((io->sizeHint > 0) && (extent->offset > io->sizeHint)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001149 avifDiagnosticsPrintf(diag, "Item ID %u extent offset failed size hint sanity check. Truncated data?", item->id);
Wan-Teh Changb856dc22020-09-28 13:00:56 -07001150 return AVIF_RESULT_BMFF_PARSE_FAILED;
1151 }
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001152 avifResult readResult = io->read(io, 0, extent->offset, bytesToRead, &offsetBuffer);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001153 if (readResult != AVIF_RESULT_OK) {
1154 return readResult;
1155 }
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -07001156 if (bytesToRead != offsetBuffer.size) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001157 avifDiagnosticsPrintf(diag,
1158 "Item ID %u tried to read %zu bytes, but only received %zu bytes",
Joe Drago48867292021-05-04 13:23:08 -07001159 item->id,
Joe Drago618ed5f2021-05-04 12:13:24 -07001160 bytesToRead,
1161 offsetBuffer.size);
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -07001162 return AVIF_RESULT_TRUNCATED_DATA;
1163 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001164 }
1165
1166 if (singlePersistentBuffer) {
1167 memcpy(&item->mergedExtents, &offsetBuffer, sizeof(avifRWData));
1168 item->mergedExtents.size = bytesToRead;
1169 } else {
Joe Dragodb1009a2021-03-16 15:26:14 -07001170 assert(item->ownsMergedExtents);
1171 assert(front);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001172 memcpy(front, offsetBuffer.data, bytesToRead);
1173 front += bytesToRead;
1174 }
1175
1176 remainingBytes -= bytesToRead;
1177 if (remainingBytes == 0) {
1178 // This happens when partialByteCount is set
1179 break;
1180 }
Joe Dragoa4956902020-08-28 01:24:35 -07001181 }
1182 if (remainingBytes != 0) {
1183 // This should be impossible?
Joe Drago618ed5f2021-05-04 12:13:24 -07001184 avifDiagnosticsPrintf(diag, "Item ID %u has %zu unexpected trailing bytes", item->id, remainingBytes);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001185 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragof6a42272019-11-21 15:21:41 -08001186 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001187
Joe Dragobffba3b2021-05-26 15:46:10 -07001188 outData->data = item->mergedExtents.data + offset;
1189 outData->size = readOutputSize;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001190 item->partialMergedExtents = (item->size != totalBytesToRead);
1191 return AVIF_RESULT_OK;
Joe Dragof6a42272019-11-21 15:21:41 -08001192}
1193
Joe Dragobffba3b2021-05-26 15:46:10 -07001194static avifBool avifDecoderGenerateImageGridTiles(avifDecoder * decoder, avifImageGrid * grid, avifDecoderItem * gridItem, avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -08001195{
Wan-Teh Chang7ca3dd92020-11-20 12:50:44 -08001196 unsigned int tilesRequested = grid->rows * grid->columns;
Joe Drago060d5342020-03-03 10:53:49 -08001197
1198 // Count number of dimg for this item, bail out if it doesn't match perfectly
1199 unsigned int tilesAvailable = 0;
Joe Drago9f2b87b2020-06-03 19:36:38 -07001200 for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) {
1201 avifDecoderItem * item = &gridItem->meta->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -08001202 if (item->dimgForID == gridItem->id) {
1203 if (memcmp(item->type, "av01", 4)) {
1204 continue;
1205 }
Joe Drago3320e5f2020-04-21 17:36:27 -07001206 if (item->hasUnsupportedEssentialProperty) {
Wan-Teh Chang29aaade2020-08-10 16:14:16 -07001207 // An essential property isn't supported by libavif; can't
1208 // decode a grid image if any tile in the grid isn't supported.
Joe Dragobffba3b2021-05-26 15:46:10 -07001209 avifDiagnosticsPrintf(&decoder->diag, "Grid image contains tile with an unsupported property marked as essential");
Wan-Teh Chang29aaade2020-08-10 16:14:16 -07001210 return AVIF_FALSE;
Joe Drago3320e5f2020-04-21 17:36:27 -07001211 }
Joe Drago060d5342020-03-03 10:53:49 -08001212
1213 ++tilesAvailable;
1214 }
1215 }
1216
1217 if (tilesRequested != tilesAvailable) {
Joe Dragobffba3b2021-05-26 15:46:10 -07001218 avifDiagnosticsPrintf(&decoder->diag,
Wan-Teh Changf01e2252021-06-10 17:40:21 -07001219 "Grid image of dimensions %ux%u requires %u tiles, and only %u were found",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001220 grid->columns,
1221 grid->rows,
1222 tilesRequested,
1223 tilesAvailable);
Joe Drago060d5342020-03-03 10:53:49 -08001224 return AVIF_FALSE;
1225 }
1226
Joe Drago9c5f5652020-06-29 15:13:44 -07001227 avifBool firstTile = AVIF_TRUE;
Joe Drago9f2b87b2020-06-03 19:36:38 -07001228 for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) {
1229 avifDecoderItem * item = &gridItem->meta->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -08001230 if (item->dimgForID == gridItem->id) {
1231 if (memcmp(item->type, "av01", 4)) {
1232 continue;
1233 }
1234
Joe Drago46104d62021-05-27 18:17:29 -07001235 avifTile * tile = avifDecoderDataCreateTile(decoder->data, item->width, item->height, avifDecoderItemOperatingPoint(item));
Wan-Teh Changd8911872022-02-14 14:15:00 -08001236 if (!tile) {
1237 return AVIF_FALSE;
1238 }
Joe Dragobffba3b2021-05-26 15:46:10 -07001239 if (!avifCodecDecodeInputFillFromDecoderItem(tile->input,
1240 item,
1241 decoder->allowProgressive,
1242 decoder->imageCountLimit,
1243 decoder->io->sizeHint,
1244 &decoder->diag)) {
1245 return AVIF_FALSE;
1246 }
Joe Drago060d5342020-03-03 10:53:49 -08001247 tile->input->alpha = alpha;
Joe Drago9c5f5652020-06-29 15:13:44 -07001248
1249 if (firstTile) {
1250 firstTile = AVIF_FALSE;
1251
1252 // Adopt the av1C property of the first av01 tile, so that it can be queried from
1253 // the top-level color/alpha item during avifDecoderReset().
1254 const avifProperty * srcProp = avifPropertyArrayFind(&item->properties, "av1C");
1255 if (!srcProp) {
Joe Dragobffba3b2021-05-26 15:46:10 -07001256 avifDiagnosticsPrintf(&decoder->diag, "Grid image's first tile is missing an av1C property");
Joe Drago9c5f5652020-06-29 15:13:44 -07001257 return AVIF_FALSE;
1258 }
1259 avifProperty * dstProp = (avifProperty *)avifArrayPushPtr(&gridItem->properties);
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07001260 *dstProp = *srcProp;
Joe Dragobffba3b2021-05-26 15:46:10 -07001261
Wan-Teh Chang34615242021-07-13 17:26:31 -07001262 if (!alpha && item->progressive) {
1263 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_AVAILABLE;
1264 if (tile->input->samples.count > 1) {
1265 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_ACTIVE;
1266 decoder->imageCount = tile->input->samples.count;
Joe Dragobffba3b2021-05-26 15:46:10 -07001267 }
1268 }
Joe Drago9c5f5652020-06-29 15:13:44 -07001269 }
Joe Drago060d5342020-03-03 10:53:49 -08001270 }
1271 }
1272 return AVIF_TRUE;
1273}
1274
Yannis Guyond3e85ff2022-03-17 05:10:29 +00001275// Checks the grid consistency and copies the pixels from the tiles to the
1276// dstImage. Only the freshly decoded tiles are considered, skipping the already
1277// copied or not-yet-decoded tiles.
Joe Drago800b47f2020-03-18 16:22:37 -07001278static avifBool avifDecoderDataFillImageGrid(avifDecoderData * data,
1279 avifImageGrid * grid,
1280 avifImage * dstImage,
1281 unsigned int firstTileIndex,
Yannis Guyond3e85ff2022-03-17 05:10:29 +00001282 unsigned int oldDecodedTileCount,
1283 unsigned int decodedTileCount,
Joe Drago800b47f2020-03-18 16:22:37 -07001284 avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -08001285{
Yannis Guyond3e85ff2022-03-17 05:10:29 +00001286 assert(decodedTileCount > oldDecodedTileCount);
Joe Drago060d5342020-03-03 10:53:49 -08001287
1288 avifTile * firstTile = &data->tiles.tile[firstTileIndex];
Joe Dragoa0da4a42020-05-08 14:27:40 -07001289 avifBool firstTileUVPresent = (firstTile->image->yuvPlanes[AVIF_CHAN_U] && firstTile->image->yuvPlanes[AVIF_CHAN_V]);
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09001290
Joe Dragoa0da4a42020-05-08 14:27:40 -07001291 // Check for tile consistency: All tiles in a grid image should match in the properties checked below.
Yannis Guyond3e85ff2022-03-17 05:10:29 +00001292 for (unsigned int i = AVIF_MAX(1, oldDecodedTileCount); i < decodedTileCount; ++i) {
Joe Drago060d5342020-03-03 10:53:49 -08001293 avifTile * tile = &data->tiles.tile[firstTileIndex + i];
Joe Drago951a0022020-03-09 16:19:44 -07001294 avifBool uvPresent = (tile->image->yuvPlanes[AVIF_CHAN_U] && tile->image->yuvPlanes[AVIF_CHAN_V]);
Joe Dragoa0da4a42020-05-08 14:27:40 -07001295 if ((tile->image->width != firstTile->image->width) || (tile->image->height != firstTile->image->height) ||
1296 (tile->image->depth != firstTile->image->depth) || (tile->image->yuvFormat != firstTile->image->yuvFormat) ||
1297 (tile->image->yuvRange != firstTile->image->yuvRange) || (uvPresent != firstTileUVPresent) ||
wantehchangbc35a5f2020-08-12 15:27:39 -07001298 (tile->image->colorPrimaries != firstTile->image->colorPrimaries) ||
1299 (tile->image->transferCharacteristics != firstTile->image->transferCharacteristics) ||
Wan-Teh Chang5bdead52020-08-12 17:02:22 -07001300 (tile->image->matrixCoefficients != firstTile->image->matrixCoefficients) ||
1301 (tile->image->alphaRange != firstTile->image->alphaRange)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001302 avifDiagnosticsPrintf(data->diag, "Grid image contains mismatched tiles");
Joe Drago060d5342020-03-03 10:53:49 -08001303 return AVIF_FALSE;
1304 }
1305 }
1306
wantehchangdf586a82020-08-12 13:06:08 -07001307 // Validate grid image size and tile size.
1308 //
1309 // HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1:
TYTYb587c592020-12-08 17:42:18 +08001310 // The tiled input images shall completely "cover" the reconstructed image grid canvas, ...
wantehchangbc35a5f2020-08-12 15:27:39 -07001311 if (((firstTile->image->width * grid->columns) < grid->outputWidth) ||
1312 ((firstTile->image->height * grid->rows) < grid->outputHeight)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001313 avifDiagnosticsPrintf(data->diag,
1314 "Grid image tiles do not completely cover the image (HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1)");
wantehchangdf586a82020-08-12 13:06:08 -07001315 return AVIF_FALSE;
1316 }
1317 // Tiles in the rightmost column and bottommost row must overlap the reconstructed image grid canvas. See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2, Figure 2.
wantehchangbc35a5f2020-08-12 15:27:39 -07001318 if (((firstTile->image->width * (grid->columns - 1)) >= grid->outputWidth) ||
1319 ((firstTile->image->height * (grid->rows - 1)) >= grid->outputHeight)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001320 avifDiagnosticsPrintf(data->diag,
1321 "Grid image tiles in the rightmost column and bottommost row do not overlap the reconstructed image grid canvas. See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2, Figure 2");
wantehchangdf586a82020-08-12 13:06:08 -07001322 return AVIF_FALSE;
1323 }
Yannis Guyonbf28a922022-02-09 23:19:32 +01001324
1325 if (alpha) {
Yannis Guyon7a6d13b2022-02-22 20:15:07 +00001326 // An alpha tile does not contain any YUV pixels.
1327 assert(firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_NONE);
wantehchangdf586a82020-08-12 13:06:08 -07001328 }
Yannis Guyonbf28a922022-02-09 23:19:32 +01001329 if (!avifAreGridDimensionsValid(firstTile->image->yuvFormat,
1330 grid->outputWidth,
1331 grid->outputHeight,
1332 firstTile->image->width,
1333 firstTile->image->height,
1334 data->diag)) {
1335 return AVIF_FALSE;
wantehchangdf586a82020-08-12 13:06:08 -07001336 }
1337
Joe Dragoa0da4a42020-05-08 14:27:40 -07001338 // Lazily populate dstImage with the new frame's properties. If we're decoding alpha,
1339 // these values must already match.
1340 if ((dstImage->width != grid->outputWidth) || (dstImage->height != grid->outputHeight) ||
Wan-Teh Chang5bdead52020-08-12 17:02:22 -07001341 (dstImage->depth != firstTile->image->depth) || (!alpha && (dstImage->yuvFormat != firstTile->image->yuvFormat))) {
Joe Drago060d5342020-03-03 10:53:49 -08001342 if (alpha) {
1343 // Alpha doesn't match size, just bail out
Joe Drago4d5f4a42021-04-28 13:11:45 -07001344 avifDiagnosticsPrintf(data->diag, "Alpha plane dimensions do not match color plane dimensions");
Joe Drago060d5342020-03-03 10:53:49 -08001345 return AVIF_FALSE;
1346 }
1347
1348 avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
1349 dstImage->width = grid->outputWidth;
1350 dstImage->height = grid->outputHeight;
Joe Dragoa0da4a42020-05-08 14:27:40 -07001351 dstImage->depth = firstTile->image->depth;
1352 dstImage->yuvFormat = firstTile->image->yuvFormat;
Joe Dragoc00d5832020-08-13 16:03:28 -07001353 dstImage->yuvRange = firstTile->image->yuvRange;
Joe Dragoa0da4a42020-05-08 14:27:40 -07001354 if (!data->cicpSet) {
1355 data->cicpSet = AVIF_TRUE;
Joe Dragoc00d5832020-08-13 16:03:28 -07001356 dstImage->colorPrimaries = firstTile->image->colorPrimaries;
1357 dstImage->transferCharacteristics = firstTile->image->transferCharacteristics;
1358 dstImage->matrixCoefficients = firstTile->image->matrixCoefficients;
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09001359 }
Joe Drago060d5342020-03-03 10:53:49 -08001360 }
Joe Dragod0eeb182020-05-18 17:23:48 -07001361 if (alpha) {
1362 dstImage->alphaRange = firstTile->image->alphaRange;
1363 }
Joe Drago060d5342020-03-03 10:53:49 -08001364
1365 avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV);
1366
1367 avifPixelFormatInfo formatInfo;
Joe Drago7a249f52020-08-13 12:58:03 -07001368 avifGetPixelFormatInfo(firstTile->image->yuvFormat, &formatInfo);
Joe Drago060d5342020-03-03 10:53:49 -08001369
Yannis Guyond3e85ff2022-03-17 05:10:29 +00001370 unsigned int tileIndex = oldDecodedTileCount;
Joe Drago060d5342020-03-03 10:53:49 -08001371 size_t pixelBytes = avifImageUsesU16(dstImage) ? 2 : 1;
Yannis Guyond3e85ff2022-03-17 05:10:29 +00001372 unsigned int rowIndex = oldDecodedTileCount / grid->columns;
1373 unsigned int colIndex = oldDecodedTileCount % grid->columns;
1374 // Only the first iteration of the outer for loop uses this initial value of colIndex.
1375 // Subsequent iterations of the outer for loop initializes colIndex to 0.
1376 for (; rowIndex < grid->rows; ++rowIndex, colIndex = 0) {
1377 for (; colIndex < grid->columns; ++colIndex, ++tileIndex) {
1378 if (tileIndex >= decodedTileCount) {
1379 // Tile is not ready yet.
1380 return AVIF_TRUE;
1381 }
1382 avifTile * tile = &data->tiles.tile[firstTileIndex + tileIndex];
Joe Drago060d5342020-03-03 10:53:49 -08001383
Joe Dragoa0da4a42020-05-08 14:27:40 -07001384 unsigned int widthToCopy = firstTile->image->width;
1385 unsigned int maxX = firstTile->image->width * (colIndex + 1);
Joe Drago060d5342020-03-03 10:53:49 -08001386 if (maxX > grid->outputWidth) {
1387 widthToCopy -= maxX - grid->outputWidth;
1388 }
1389
Joe Dragoa0da4a42020-05-08 14:27:40 -07001390 unsigned int heightToCopy = firstTile->image->height;
1391 unsigned int maxY = firstTile->image->height * (rowIndex + 1);
Joe Drago060d5342020-03-03 10:53:49 -08001392 if (maxY > grid->outputHeight) {
1393 heightToCopy -= maxY - grid->outputHeight;
1394 }
1395
1396 // Y and A channels
Joe Dragodb1009a2021-03-16 15:26:14 -07001397 size_t yaColOffset = (size_t)colIndex * firstTile->image->width;
1398 size_t yaRowOffset = (size_t)rowIndex * firstTile->image->height;
Joe Drago060d5342020-03-03 10:53:49 -08001399 size_t yaRowBytes = widthToCopy * pixelBytes;
1400
1401 if (alpha) {
1402 // A
1403 for (unsigned int j = 0; j < heightToCopy; ++j) {
1404 uint8_t * src = &tile->image->alphaPlane[j * tile->image->alphaRowBytes];
1405 uint8_t * dst = &dstImage->alphaPlane[(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->alphaRowBytes)];
1406 memcpy(dst, src, yaRowBytes);
1407 }
1408 } else {
1409 // Y
1410 for (unsigned int j = 0; j < heightToCopy; ++j) {
1411 uint8_t * src = &tile->image->yuvPlanes[AVIF_CHAN_Y][j * tile->image->yuvRowBytes[AVIF_CHAN_Y]];
1412 uint8_t * dst =
1413 &dstImage->yuvPlanes[AVIF_CHAN_Y][(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_Y])];
1414 memcpy(dst, src, yaRowBytes);
1415 }
1416
Joe Dragoa0da4a42020-05-08 14:27:40 -07001417 if (!firstTileUVPresent) {
Joe Drago060d5342020-03-03 10:53:49 -08001418 continue;
1419 }
1420
1421 // UV
Joe Drago060d5342020-03-03 10:53:49 -08001422 heightToCopy >>= formatInfo.chromaShiftY;
1423 size_t uvColOffset = yaColOffset >> formatInfo.chromaShiftX;
1424 size_t uvRowOffset = yaRowOffset >> formatInfo.chromaShiftY;
1425 size_t uvRowBytes = yaRowBytes >> formatInfo.chromaShiftX;
1426 for (unsigned int j = 0; j < heightToCopy; ++j) {
1427 uint8_t * srcU = &tile->image->yuvPlanes[AVIF_CHAN_U][j * tile->image->yuvRowBytes[AVIF_CHAN_U]];
1428 uint8_t * dstU =
1429 &dstImage->yuvPlanes[AVIF_CHAN_U][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_U])];
1430 memcpy(dstU, srcU, uvRowBytes);
1431
1432 uint8_t * srcV = &tile->image->yuvPlanes[AVIF_CHAN_V][j * tile->image->yuvRowBytes[AVIF_CHAN_V]];
1433 uint8_t * dstV =
1434 &dstImage->yuvPlanes[AVIF_CHAN_V][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_V])];
1435 memcpy(dstV, srcV, uvRowBytes);
1436 }
1437 }
1438 }
1439 }
1440
1441 return AVIF_TRUE;
1442}
1443
Joe Drago39267fd2020-06-19 15:21:14 -07001444// If colorId == 0 (a sentinel value as item IDs must be nonzero), accept any found EXIF/XMP metadata. Passing in 0
1445// is used when finding metadata in a meta box embedded in a trak box, as any items inside of a meta box that is
Joe Dragofa990822020-06-19 15:23:55 -07001446// inside of a trak box are implicitly associated to the track.
Joe Dragobe4cbb92020-09-21 12:14:05 -07001447static avifResult avifDecoderFindMetadata(avifDecoder * decoder, avifMeta * meta, avifImage * image, uint32_t colorId)
Joe Dragoa72da5b2020-06-15 19:40:17 -07001448{
Joe Dragobe4cbb92020-09-21 12:14:05 -07001449 if (decoder->ignoreExif && decoder->ignoreXMP) {
1450 // Nothing to do!
1451 return AVIF_RESULT_OK;
1452 }
Joe Dragoa72da5b2020-06-15 19:40:17 -07001453
1454 for (uint32_t itemIndex = 0; itemIndex < meta->items.count; ++itemIndex) {
1455 avifDecoderItem * item = &meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07001456 if (!item->size) {
1457 continue;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001458 }
1459 if (item->hasUnsupportedEssentialProperty) {
1460 // An essential property isn't supported by libavif; ignore the item.
1461 continue;
1462 }
1463
1464 if ((colorId > 0) && (item->descForID != colorId)) {
1465 // Not a content description (metadata) for the colorOBU, skip it
1466 continue;
1467 }
1468
Joe Dragobe4cbb92020-09-21 12:14:05 -07001469 if (!decoder->ignoreExif && !memcmp(item->type, "Exif", 4)) {
1470 avifROData exifContents;
Joe Dragobffba3b2021-05-26 15:46:10 -07001471 avifResult readResult = avifDecoderItemRead(item, decoder->io, &exifContents, 0, 0, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001472 if (readResult != AVIF_RESULT_OK) {
1473 return readResult;
1474 }
1475
Joe Dragoa72da5b2020-06-15 19:40:17 -07001476 // Advance past Annex A.2.1's header
Joe Drago618ed5f2021-05-04 12:13:24 -07001477 BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size, &decoder->diag, "Exif header");
Joe Dragoa72da5b2020-06-15 19:40:17 -07001478 uint32_t exifTiffHeaderOffset;
Wan-Teh Changf1d60192020-10-07 16:23:08 -07001479 CHECKERR(avifROStreamReadU32(&exifBoxStream, &exifTiffHeaderOffset), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) exif_tiff_header_offset;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001480
Joe Dragobe4cbb92020-09-21 12:14:05 -07001481 avifImageSetMetadataExif(image, avifROStreamCurrent(&exifBoxStream), avifROStreamRemainingBytes(&exifBoxStream));
1482 } else if (!decoder->ignoreXMP && !memcmp(item->type, "mime", 4) &&
1483 !memcmp(item->contentType.contentType, xmpContentType, xmpContentTypeSize)) {
1484 avifROData xmpContents;
Joe Dragobffba3b2021-05-26 15:46:10 -07001485 avifResult readResult = avifDecoderItemRead(item, decoder->io, &xmpContents, 0, 0, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001486 if (readResult != AVIF_RESULT_OK) {
1487 return readResult;
1488 }
1489
1490 avifImageSetMetadataXMP(image, xmpContents.data, xmpContents.size);
Joe Dragoa72da5b2020-06-15 19:40:17 -07001491 }
1492 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001493 return AVIF_RESULT_OK;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001494}
1495
Joe Drago8f7a3002019-02-07 19:35:37 -08001496// ---------------------------------------------------------------------------
Joe Dragocd1e4c32019-02-08 11:26:31 -08001497// URN
1498
Joe Dragoa72da5b2020-06-15 19:40:17 -07001499static avifBool isAlphaURN(const char * urn)
Joe Dragocd1e4c32019-02-08 11:26:31 -08001500{
wantehchang3fde0d02020-03-10 23:58:32 -07001501 return !strcmp(urn, URN_ALPHA0) || !strcmp(urn, URN_ALPHA1);
Joe Dragocd1e4c32019-02-08 11:26:31 -08001502}
1503
1504// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08001505// BMFF Parsing
1506
Joe Drago4d5f4a42021-04-28 13:11:45 -07001507static avifBool avifParseHandlerBox(const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoe0185182021-03-31 08:14:51 -07001508{
Joe Drago618ed5f2021-05-04 12:13:24 -07001509 BEGIN_STREAM(s, raw, rawLen, diag, "Box[hdlr]");
Joe Dragoe0185182021-03-31 08:14:51 -07001510
1511 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1512
1513 uint32_t predefined;
1514 CHECK(avifROStreamReadU32(&s, &predefined)); // unsigned int(32) pre_defined = 0;
1515 if (predefined != 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001516 avifDiagnosticsPrintf(diag, "Box[hdlr] contains a pre_defined value that is nonzero");
Joe Dragoe0185182021-03-31 08:14:51 -07001517 return AVIF_FALSE;
1518 }
1519
1520 uint8_t handlerType[4];
1521 CHECK(avifROStreamRead(&s, handlerType, 4)); // unsigned int(32) handler_type;
1522 if (memcmp(handlerType, "pict", 4) != 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001523 avifDiagnosticsPrintf(diag, "Box[hdlr] handler_type is not 'pict'");
Joe Dragoe0185182021-03-31 08:14:51 -07001524 return AVIF_FALSE;
1525 }
1526
1527 for (int i = 0; i < 3; ++i) {
1528 uint32_t reserved;
1529 CHECK(avifROStreamReadU32(&s, &reserved)); // const unsigned int(32)[3] reserved = 0;
1530 }
1531
1532 // Verify that a valid string is here, but don't bother to store it
1533 CHECK(avifROStreamReadString(&s, NULL, 0)); // string name;
1534 return AVIF_TRUE;
1535}
1536
Joe Drago4d5f4a42021-04-28 13:11:45 -07001537static avifBool avifParseItemLocationBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago444f0512019-01-23 17:03:24 -08001538{
Joe Drago618ed5f2021-05-04 12:13:24 -07001539 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iloc]");
Joe Drago444f0512019-01-23 17:03:24 -08001540
Joe Dragof6a42272019-11-21 15:21:41 -08001541 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001542 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragof6a42272019-11-21 15:21:41 -08001543 if (version > 2) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001544 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported version [%u]", version);
Joe Dragof6a42272019-11-21 15:21:41 -08001545 return AVIF_FALSE;
1546 }
Joe Drago444f0512019-01-23 17:03:24 -08001547
Joe Drago8f7a3002019-02-07 19:35:37 -08001548 uint8_t offsetSizeAndLengthSize;
Joe Drago345aaa12019-09-25 13:42:12 -07001549 CHECK(avifROStreamRead(&s, &offsetSizeAndLengthSize, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001550 uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
1551 uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
Joe Drago444f0512019-01-23 17:03:24 -08001552
Joe Dragof6a42272019-11-21 15:21:41 -08001553 uint8_t baseOffsetSizeAndIndexSize;
1554 CHECK(avifROStreamRead(&s, &baseOffsetSizeAndIndexSize, 1));
1555 uint8_t baseOffsetSize = (baseOffsetSizeAndIndexSize >> 4) & 0xf; // unsigned int(4) base_offset_size;
1556 uint8_t indexSize = 0;
1557 if ((version == 1) || (version == 2)) {
1558 indexSize = baseOffsetSizeAndIndexSize & 0xf; // unsigned int(4) index_size;
1559 if (indexSize != 0) {
1560 // extent_index unsupported
Joe Drago618ed5f2021-05-04 12:13:24 -07001561 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported extent_index");
Joe Dragof6a42272019-11-21 15:21:41 -08001562 return AVIF_FALSE;
1563 }
1564 }
Joe Drago444f0512019-01-23 17:03:24 -08001565
Joe Dragof6a42272019-11-21 15:21:41 -08001566 uint16_t tmp16;
1567 uint32_t itemCount;
1568 if (version < 2) {
1569 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_count;
1570 itemCount = tmp16;
1571 } else {
1572 CHECK(avifROStreamReadU32(&s, &itemCount)); // unsigned int(32) item_count;
1573 }
1574 for (uint32_t i = 0; i < itemCount; ++i) {
1575 uint32_t itemID;
Joe Dragof6a42272019-11-21 15:21:41 -08001576 if (version < 2) {
1577 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
1578 itemID = tmp16;
1579 } else {
1580 CHECK(avifROStreamReadU32(&s, &itemID)); // unsigned int(32) item_ID;
1581 }
1582
Joe Drago1dea33e2021-09-14 17:12:39 -07001583 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
1584 if (!item) {
1585 avifDiagnosticsPrintf(diag, "Box[iloc] has an invalid item ID [%u]", itemID);
1586 return AVIF_FALSE;
1587 }
1588 if (item->extents.count > 0) {
1589 // This item has already been given extents via this iloc box. This is invalid.
1590 avifDiagnosticsPrintf(diag, "Item ID [%u] contains duplicate sets of extents", itemID);
1591 return AVIF_FALSE;
1592 }
1593
Joe Dragof6a42272019-11-21 15:21:41 -08001594 if ((version == 1) || (version == 2)) {
1595 uint8_t ignored;
1596 uint8_t constructionMethod;
1597 CHECK(avifROStreamRead(&s, &ignored, 1)); // unsigned int(12) reserved = 0;
1598 CHECK(avifROStreamRead(&s, &constructionMethod, 1)); // unsigned int(4) construction_method;
1599 constructionMethod = constructionMethod & 0xf;
1600 if ((constructionMethod != 0 /* file */) && (constructionMethod != 1 /* idat */)) {
1601 // construction method item(2) unsupported
Joe Drago618ed5f2021-05-04 12:13:24 -07001602 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported construction method [%u]", constructionMethod);
Joe Dragof6a42272019-11-21 15:21:41 -08001603 return AVIF_FALSE;
1604 }
1605 if (constructionMethod == 1) {
Joe Drago1dea33e2021-09-14 17:12:39 -07001606 item->idatStored = AVIF_TRUE;
Joe Dragof6a42272019-11-21 15:21:41 -08001607 }
1608 }
1609
Joe Drago345aaa12019-09-25 13:42:12 -07001610 uint16_t dataReferenceIndex; // unsigned int(16) data_ref rence_index;
1611 CHECK(avifROStreamReadU16(&s, &dataReferenceIndex)); //
1612 uint64_t baseOffset; // unsigned int(base_offset_size*8) base_offset;
1613 CHECK(avifROStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
1614 uint16_t extentCount; // unsigned int(16) extent_count;
1615 CHECK(avifROStreamReadU16(&s, &extentCount)); //
Joe Dragoa4956902020-08-28 01:24:35 -07001616 for (int extentIter = 0; extentIter < extentCount; ++extentIter) {
Joe Dragof6a42272019-11-21 15:21:41 -08001617 // If extent_index is ever supported, this spec must be implemented here:
1618 // :: if (((version == 1) || (version == 2)) && (index_size > 0)) {
1619 // :: unsigned int(index_size*8) extent_index;
1620 // :: }
1621
Joe Drago8f7a3002019-02-07 19:35:37 -08001622 uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
Joe Drago345aaa12019-09-25 13:42:12 -07001623 CHECK(avifROStreamReadUX8(&s, &extentOffset, offsetSize));
Joe Drago8f7a3002019-02-07 19:35:37 -08001624 uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
Joe Drago345aaa12019-09-25 13:42:12 -07001625 CHECK(avifROStreamReadUX8(&s, &extentLength, lengthSize));
Joe Drago444f0512019-01-23 17:03:24 -08001626
Joe Drago4bcdfde2020-11-13 17:50:55 -08001627 avifExtent * extent = (avifExtent *)avifArrayPushPtr(&item->extents);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001628 if (extentOffset > UINT64_MAX - baseOffset) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001629 avifDiagnosticsPrintf(diag,
1630 "Item ID [%u] contains an extent offset which overflows: [base: %" PRIu64 " offset:%" PRIu64 "]",
1631 itemID,
1632 baseOffset,
1633 extentOffset);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001634 return AVIF_FALSE;
1635 }
1636 uint64_t offset = baseOffset + extentOffset;
Joe Drago217056b2020-11-13 16:19:35 -08001637 extent->offset = offset;
1638 if (extentLength > SIZE_MAX) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001639 avifDiagnosticsPrintf(diag, "Item ID [%u] contains an extent length which overflows: [%" PRIu64 "]", itemID, extentLength);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001640 return AVIF_FALSE;
1641 }
Joe Drago217056b2020-11-13 16:19:35 -08001642 extent->size = (size_t)extentLength;
1643 if (extent->size > SIZE_MAX - item->size) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001644 avifDiagnosticsPrintf(diag,
Joe Drago48867292021-05-04 13:23:08 -07001645 "Item ID [%u] contains an extent length which overflows the item size: [%zu, %zu]",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001646 itemID,
1647 extent->size,
1648 item->size);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001649 return AVIF_FALSE;
1650 }
Joe Dragoa4956902020-08-28 01:24:35 -07001651 item->size += extent->size;
Joe Drago444f0512019-01-23 17:03:24 -08001652 }
1653 }
1654 return AVIF_TRUE;
1655}
1656
Wan-Teh Chang980d5852021-08-03 20:02:38 -07001657static avifBool avifParseImageGridBox(avifImageGrid * grid, const uint8_t * raw, size_t rawLen, uint32_t imageSizeLimit, avifDiagnostics * diag)
Joe Drago060d5342020-03-03 10:53:49 -08001658{
Joe Drago618ed5f2021-05-04 12:13:24 -07001659 BEGIN_STREAM(s, raw, rawLen, diag, "Box[grid]");
Joe Drago060d5342020-03-03 10:53:49 -08001660
1661 uint8_t version, flags;
1662 CHECK(avifROStreamRead(&s, &version, 1)); // unsigned int(8) version = 0;
1663 if (version != 0) {
Joe Dragoba4d67d2021-05-04 12:34:37 -07001664 avifDiagnosticsPrintf(diag, "Box[grid] has unsupported version [%u]", version);
Joe Drago060d5342020-03-03 10:53:49 -08001665 return AVIF_FALSE;
1666 }
Joe Drago79ebcf32020-11-18 22:37:10 -08001667 uint8_t rowsMinusOne, columnsMinusOne;
1668 CHECK(avifROStreamRead(&s, &flags, 1)); // unsigned int(8) flags;
1669 CHECK(avifROStreamRead(&s, &rowsMinusOne, 1)); // unsigned int(8) rows_minus_one;
1670 CHECK(avifROStreamRead(&s, &columnsMinusOne, 1)); // unsigned int(8) columns_minus_one;
1671 grid->rows = (uint32_t)rowsMinusOne + 1;
1672 grid->columns = (uint32_t)columnsMinusOne + 1;
Joe Drago060d5342020-03-03 10:53:49 -08001673
1674 uint32_t fieldLength = ((flags & 1) + 1) * 16;
1675 if (fieldLength == 16) {
1676 uint16_t outputWidth16, outputHeight16;
1677 CHECK(avifROStreamReadU16(&s, &outputWidth16)); // unsigned int(FieldLength) output_width;
1678 CHECK(avifROStreamReadU16(&s, &outputHeight16)); // unsigned int(FieldLength) output_height;
1679 grid->outputWidth = outputWidth16;
1680 grid->outputHeight = outputHeight16;
1681 } else {
1682 if (fieldLength != 32) {
1683 // This should be impossible
Joe Drago4d5f4a42021-04-28 13:11:45 -07001684 avifDiagnosticsPrintf(diag, "Grid box contains illegal field length: [%u]", fieldLength);
Joe Drago060d5342020-03-03 10:53:49 -08001685 return AVIF_FALSE;
1686 }
1687 CHECK(avifROStreamReadU32(&s, &grid->outputWidth)); // unsigned int(FieldLength) output_width;
1688 CHECK(avifROStreamReadU32(&s, &grid->outputHeight)); // unsigned int(FieldLength) output_height;
1689 }
Wan-Teh Changbd1492e2021-08-06 16:08:14 -07001690 if ((grid->outputWidth == 0) || (grid->outputHeight == 0)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001691 avifDiagnosticsPrintf(diag, "Grid box contains illegal dimensions: [%u x %u]", grid->outputWidth, grid->outputHeight);
Wan-Teh Chang0a8e7242020-08-10 13:24:59 -07001692 return AVIF_FALSE;
1693 }
Wan-Teh Changbd1492e2021-08-06 16:08:14 -07001694 if (grid->outputWidth > (imageSizeLimit / grid->outputHeight)) {
1695 avifDiagnosticsPrintf(diag, "Grid box dimensions are too large: [%u x %u]", grid->outputWidth, grid->outputHeight);
1696 return AVIF_FALSE;
1697 }
wantehchangae2074b2020-08-12 13:02:27 -07001698 return avifROStreamRemainingBytes(&s) == 0;
Joe Drago060d5342020-03-03 10:53:49 -08001699}
1700
Joe Drago618ed5f2021-05-04 12:13:24 -07001701static avifBool avifParseImageSpatialExtentsProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001702{
Joe Drago618ed5f2021-05-04 12:13:24 -07001703 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ispe]");
Joe Drago345aaa12019-09-25 13:42:12 -07001704 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08001705
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001706 avifImageSpatialExtents * ispe = &prop->u.ispe;
1707 CHECK(avifROStreamReadU32(&s, &ispe->width));
1708 CHECK(avifROStreamReadU32(&s, &ispe->height));
Joe Drago8f7a3002019-02-07 19:35:37 -08001709 return AVIF_TRUE;
1710}
1711
Joe Drago618ed5f2021-05-04 12:13:24 -07001712static avifBool avifParseAuxiliaryTypeProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragocd1e4c32019-02-08 11:26:31 -08001713{
Joe Drago618ed5f2021-05-04 12:13:24 -07001714 BEGIN_STREAM(s, raw, rawLen, diag, "Box[auxC]");
Joe Drago345aaa12019-09-25 13:42:12 -07001715 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001716
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001717 CHECK(avifROStreamReadString(&s, prop->u.auxC.auxType, AUXTYPE_SIZE));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001718 return AVIF_TRUE;
1719}
1720
Joe Drago618ed5f2021-05-04 12:13:24 -07001721static avifBool avifParseColourInformationBox(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago41eb62b2019-02-08 15:38:18 -08001722{
Joe Drago618ed5f2021-05-04 12:13:24 -07001723 BEGIN_STREAM(s, raw, rawLen, diag, "Box[colr]");
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001724
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001725 avifColourInformationBox * colr = &prop->u.colr;
1726 colr->hasICC = AVIF_FALSE;
1727 colr->hasNCLX = AVIF_FALSE;
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001728
Joe Dragoa0da4a42020-05-08 14:27:40 -07001729 uint8_t colorType[4]; // unsigned int(32) colour_type;
1730 CHECK(avifROStreamRead(&s, colorType, 4));
1731 if (!memcmp(colorType, "rICC", 4) || !memcmp(colorType, "prof", 4)) {
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001732 colr->hasICC = AVIF_TRUE;
1733 colr->icc = avifROStreamCurrent(&s);
1734 colr->iccSize = avifROStreamRemainingBytes(&s);
Joe Dragoa0da4a42020-05-08 14:27:40 -07001735 } else if (!memcmp(colorType, "nclx", 4)) {
Joe Dragofb5a5f02021-01-31 20:43:57 -08001736 CHECK(avifROStreamReadU16(&s, &colr->colorPrimaries)); // unsigned int(16) colour_primaries;
1737 CHECK(avifROStreamReadU16(&s, &colr->transferCharacteristics)); // unsigned int(16) transfer_characteristics;
1738 CHECK(avifROStreamReadU16(&s, &colr->matrixCoefficients)); // unsigned int(16) matrix_coefficients;
Joe Drago97b071c2019-07-17 14:24:56 -07001739 // unsigned int(1) full_range_flag;
1740 // unsigned int(7) reserved = 0;
Joe Drago74cd1c92020-04-16 12:17:11 -07001741 uint8_t tmp8;
1742 CHECK(avifROStreamRead(&s, &tmp8, 1));
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001743 colr->range = (tmp8 & 0x80) ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED;
1744 colr->hasNCLX = AVIF_TRUE;
Joe Drago41eb62b2019-02-08 15:38:18 -08001745 }
1746 return AVIF_TRUE;
1747}
1748
Joe Drago4d5f4a42021-04-28 13:11:45 -07001749static avifBool avifParseAV1CodecConfigurationBox(const uint8_t * raw, size_t rawLen, avifCodecConfigurationBox * av1C, avifDiagnostics * diag)
Joe Drago6500fd62019-10-08 17:17:34 -07001750{
Joe Drago618ed5f2021-05-04 12:13:24 -07001751 BEGIN_STREAM(s, raw, rawLen, diag, "Box[av1C]");
Joe Drago6500fd62019-10-08 17:17:34 -07001752
1753 uint8_t markerAndVersion = 0;
1754 CHECK(avifROStreamRead(&s, &markerAndVersion, 1));
1755 uint8_t seqProfileAndIndex = 0;
1756 CHECK(avifROStreamRead(&s, &seqProfileAndIndex, 1));
1757 uint8_t rawFlags = 0;
1758 CHECK(avifROStreamRead(&s, &rawFlags, 1));
1759
1760 if (markerAndVersion != 0x81) {
1761 // Marker and version must both == 1
Joe Drago4d5f4a42021-04-28 13:11:45 -07001762 avifDiagnosticsPrintf(diag, "av1C contains illegal marker and version pair: [%u]", markerAndVersion);
Joe Drago6500fd62019-10-08 17:17:34 -07001763 return AVIF_FALSE;
1764 }
1765
1766 av1C->seqProfile = (seqProfileAndIndex >> 5) & 0x7; // unsigned int (3) seq_profile;
1767 av1C->seqLevelIdx0 = (seqProfileAndIndex >> 0) & 0x1f; // unsigned int (5) seq_level_idx_0;
1768 av1C->seqTier0 = (rawFlags >> 7) & 0x1; // unsigned int (1) seq_tier_0;
1769 av1C->highBitdepth = (rawFlags >> 6) & 0x1; // unsigned int (1) high_bitdepth;
1770 av1C->twelveBit = (rawFlags >> 5) & 0x1; // unsigned int (1) twelve_bit;
1771 av1C->monochrome = (rawFlags >> 4) & 0x1; // unsigned int (1) monochrome;
1772 av1C->chromaSubsamplingX = (rawFlags >> 3) & 0x1; // unsigned int (1) chroma_subsampling_x;
1773 av1C->chromaSubsamplingY = (rawFlags >> 2) & 0x1; // unsigned int (1) chroma_subsampling_y;
1774 av1C->chromaSamplePosition = (rawFlags >> 0) & 0x3; // unsigned int (2) chroma_sample_position;
1775 return AVIF_TRUE;
1776}
1777
Joe Drago4d5f4a42021-04-28 13:11:45 -07001778static avifBool avifParseAV1CodecConfigurationBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago6500fd62019-10-08 17:17:34 -07001779{
Joe Drago4d5f4a42021-04-28 13:11:45 -07001780 return avifParseAV1CodecConfigurationBox(raw, rawLen, &prop->u.av1C, diag);
Joe Drago6500fd62019-10-08 17:17:34 -07001781}
1782
Joe Drago618ed5f2021-05-04 12:13:24 -07001783static avifBool avifParsePixelAspectRatioBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001784{
Joe Drago618ed5f2021-05-04 12:13:24 -07001785 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pasp]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001786
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001787 avifPixelAspectRatioBox * pasp = &prop->u.pasp;
Joe Drago89f0cc82020-03-09 16:13:27 -07001788 CHECK(avifROStreamReadU32(&s, &pasp->hSpacing)); // unsigned int(32) hSpacing;
1789 CHECK(avifROStreamReadU32(&s, &pasp->vSpacing)); // unsigned int(32) vSpacing;
1790 return AVIF_TRUE;
1791}
1792
Joe Drago618ed5f2021-05-04 12:13:24 -07001793static avifBool avifParseCleanApertureBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001794{
Joe Drago618ed5f2021-05-04 12:13:24 -07001795 BEGIN_STREAM(s, raw, rawLen, diag, "Box[clap]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001796
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001797 avifCleanApertureBox * clap = &prop->u.clap;
Joe Drago89f0cc82020-03-09 16:13:27 -07001798 CHECK(avifROStreamReadU32(&s, &clap->widthN)); // unsigned int(32) cleanApertureWidthN;
1799 CHECK(avifROStreamReadU32(&s, &clap->widthD)); // unsigned int(32) cleanApertureWidthD;
1800 CHECK(avifROStreamReadU32(&s, &clap->heightN)); // unsigned int(32) cleanApertureHeightN;
1801 CHECK(avifROStreamReadU32(&s, &clap->heightD)); // unsigned int(32) cleanApertureHeightD;
1802 CHECK(avifROStreamReadU32(&s, &clap->horizOffN)); // unsigned int(32) horizOffN;
1803 CHECK(avifROStreamReadU32(&s, &clap->horizOffD)); // unsigned int(32) horizOffD;
1804 CHECK(avifROStreamReadU32(&s, &clap->vertOffN)); // unsigned int(32) vertOffN;
1805 CHECK(avifROStreamReadU32(&s, &clap->vertOffD)); // unsigned int(32) vertOffD;
1806 return AVIF_TRUE;
1807}
1808
Joe Drago4d5f4a42021-04-28 13:11:45 -07001809static avifBool avifParseImageRotationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001810{
Joe Drago618ed5f2021-05-04 12:13:24 -07001811 BEGIN_STREAM(s, raw, rawLen, diag, "Box[irot]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001812
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001813 avifImageRotation * irot = &prop->u.irot;
Joe Drago89f0cc82020-03-09 16:13:27 -07001814 CHECK(avifROStreamRead(&s, &irot->angle, 1)); // unsigned int (6) reserved = 0; unsigned int (2) angle;
1815 if ((irot->angle & 0xfc) != 0) {
1816 // reserved bits must be 0
Joe Drago618ed5f2021-05-04 12:13:24 -07001817 avifDiagnosticsPrintf(diag, "Box[irot] contains nonzero reserved bits [%u]", irot->angle);
Joe Drago89f0cc82020-03-09 16:13:27 -07001818 return AVIF_FALSE;
1819 }
1820 return AVIF_TRUE;
1821}
1822
Joe Drago4d5f4a42021-04-28 13:11:45 -07001823static avifBool avifParseImageMirrorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001824{
Joe Drago618ed5f2021-05-04 12:13:24 -07001825 BEGIN_STREAM(s, raw, rawLen, diag, "Box[imir]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001826
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001827 avifImageMirror * imir = &prop->u.imir;
Joe Dragob551bb32021-06-03 15:22:05 -07001828 CHECK(avifROStreamRead(&s, &imir->mode, 1)); // unsigned int (7) reserved = 0; unsigned int (1) mode;
1829 if ((imir->mode & 0xfe) != 0) {
Joe Drago89f0cc82020-03-09 16:13:27 -07001830 // reserved bits must be 0
Joe Dragob551bb32021-06-03 15:22:05 -07001831 avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", imir->mode);
Joe Drago89f0cc82020-03-09 16:13:27 -07001832 return AVIF_FALSE;
1833 }
1834 return AVIF_TRUE;
1835}
1836
Joe Drago4d5f4a42021-04-28 13:11:45 -07001837static avifBool avifParsePixelInformationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago60421562020-04-23 11:32:26 -07001838{
Joe Drago618ed5f2021-05-04 12:13:24 -07001839 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pixi]");
Joe Drago60421562020-04-23 11:32:26 -07001840 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1841
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001842 avifPixelInformationProperty * pixi = &prop->u.pixi;
Joe Drago60421562020-04-23 11:32:26 -07001843 CHECK(avifROStreamRead(&s, &pixi->planeCount, 1)); // unsigned int (8) num_channels;
1844 if (pixi->planeCount > MAX_PIXI_PLANE_DEPTHS) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001845 avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported plane count [%u]", pixi->planeCount);
Joe Drago60421562020-04-23 11:32:26 -07001846 return AVIF_FALSE;
1847 }
1848 for (uint8_t i = 0; i < pixi->planeCount; ++i) {
1849 CHECK(avifROStreamRead(&s, &pixi->planeDepths[i], 1)); // unsigned int (8) bits_per_channel;
1850 }
1851 return AVIF_TRUE;
1852}
1853
Joe Dragobffba3b2021-05-26 15:46:10 -07001854static avifBool avifParseOperatingPointSelectorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
1855{
1856 BEGIN_STREAM(s, raw, rawLen, diag, "Box[a1op]");
1857
1858 avifOperatingPointSelectorProperty * a1op = &prop->u.a1op;
1859 CHECK(avifROStreamRead(&s, &a1op->opIndex, 1));
1860 if (a1op->opIndex > 31) { // 31 is AV1's max operating point value
1861 avifDiagnosticsPrintf(diag, "Box[a1op] contains an unsupported operating point [%u]", a1op->opIndex);
1862 return AVIF_FALSE;
1863 }
1864 return AVIF_TRUE;
1865}
1866
1867static avifBool avifParseLayerSelectorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
1868{
1869 BEGIN_STREAM(s, raw, rawLen, diag, "Box[lsel]");
1870
1871 avifLayerSelectorProperty * lsel = &prop->u.lsel;
1872 CHECK(avifROStreamReadU16(&s, &lsel->layerID));
1873 if (lsel->layerID >= MAX_AV1_LAYER_COUNT) {
1874 avifDiagnosticsPrintf(diag, "Box[lsel] contains an unsupported layer [%u]", lsel->layerID);
1875 return AVIF_FALSE;
1876 }
1877 return AVIF_TRUE;
1878}
1879
1880static avifBool avifParseAV1LayeredImageIndexingProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
1881{
1882 BEGIN_STREAM(s, raw, rawLen, diag, "Box[a1lx]");
1883
1884 avifAV1LayeredImageIndexingProperty * a1lx = &prop->u.a1lx;
1885
1886 uint8_t largeSize = 0;
1887 CHECK(avifROStreamRead(&s, &largeSize, 1));
1888 if (largeSize & 0xFE) {
1889 avifDiagnosticsPrintf(diag, "Box[a1lx] has bits set in the reserved section [%u]", largeSize);
1890 return AVIF_FALSE;
1891 }
1892
1893 for (int i = 0; i < 3; ++i) {
1894 if (largeSize) {
1895 CHECK(avifROStreamReadU32(&s, &a1lx->layerSize[i]));
1896 } else {
1897 uint16_t layerSize16;
1898 CHECK(avifROStreamReadU16(&s, &layerSize16));
1899 a1lx->layerSize[i] = (uint32_t)layerSize16;
1900 }
1901 }
1902
1903 // Layer sizes will be validated layer (when the item's size is known)
1904 return AVIF_TRUE;
1905}
1906
Joe Drago4d5f4a42021-04-28 13:11:45 -07001907static avifBool avifParseItemPropertyContainerBox(avifPropertyArray * properties, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001908{
Wan-Teh Changd8867d12021-05-27 16:08:55 -07001909 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipco]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001910
Joe Drago345aaa12019-09-25 13:42:12 -07001911 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001912 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001913 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08001914
Joe Dragoa72da5b2020-06-15 19:40:17 -07001915 int propertyIndex = avifArrayPushIndex(properties);
1916 avifProperty * prop = &properties->prop[propertyIndex];
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001917 memcpy(prop->type, header.type, 4);
Joe Drago8f7a3002019-02-07 19:35:37 -08001918 if (!memcmp(header.type, "ispe", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001919 CHECK(avifParseImageSpatialExtentsProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001920 } else if (!memcmp(header.type, "auxC", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001921 CHECK(avifParseAuxiliaryTypeProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001922 } else if (!memcmp(header.type, "colr", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001923 CHECK(avifParseColourInformationBox(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001924 } else if (!memcmp(header.type, "av1C", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001925 CHECK(avifParseAV1CodecConfigurationBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001926 } else if (!memcmp(header.type, "pasp", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001927 CHECK(avifParsePixelAspectRatioBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001928 } else if (!memcmp(header.type, "clap", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001929 CHECK(avifParseCleanApertureBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001930 } else if (!memcmp(header.type, "irot", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001931 CHECK(avifParseImageRotationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001932 } else if (!memcmp(header.type, "imir", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001933 CHECK(avifParseImageMirrorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001934 } else if (!memcmp(header.type, "pixi", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001935 CHECK(avifParsePixelInformationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragobffba3b2021-05-26 15:46:10 -07001936 } else if (!memcmp(header.type, "a1op", 4)) {
1937 CHECK(avifParseOperatingPointSelectorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
1938 } else if (!memcmp(header.type, "lsel", 4)) {
1939 CHECK(avifParseLayerSelectorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
1940 } else if (!memcmp(header.type, "a1lx", 4)) {
1941 CHECK(avifParseAV1LayeredImageIndexingProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Drago60421562020-04-23 11:32:26 -07001942 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001943
Joe Drago345aaa12019-09-25 13:42:12 -07001944 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001945 }
1946 return AVIF_TRUE;
1947}
1948
Joe Drago4d5f4a42021-04-28 13:11:45 -07001949static avifBool avifParseItemPropertyAssociation(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag, uint32_t * outVersionAndFlags)
Joe Drago8f7a3002019-02-07 19:35:37 -08001950{
Joe Dragoc60ccae2021-03-31 10:30:48 -07001951 // NOTE: If this function ever adds support for versions other than [0,1] or flags other than
1952 // [0,1], please increase the value of MAX_IPMA_VERSION_AND_FLAGS_SEEN accordingly.
1953
Joe Drago618ed5f2021-05-04 12:13:24 -07001954 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipma]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001955
1956 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001957 uint32_t flags;
1958 CHECK(avifROStreamReadVersionAndFlags(&s, &version, &flags));
1959 avifBool propertyIndexIsU16 = ((flags & 0x1) != 0);
Wan-Teh Chang33d64622021-04-08 08:49:22 -07001960 *outVersionAndFlags = ((uint32_t)version << 24) | flags;
Joe Drago8f7a3002019-02-07 19:35:37 -08001961
1962 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001963 CHECK(avifROStreamReadU32(&s, &entryCount));
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001964 unsigned int prevItemID = 0;
Joe Drago8f7a3002019-02-07 19:35:37 -08001965 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001966 // ISO/IEC 23008-12, First edition, 2017-12, Section 9.3.1:
1967 // Each ItemPropertyAssociation box shall be ordered by increasing item_ID, and there shall
1968 // be at most one association box for each item_ID, in any ItemPropertyAssociation box.
Joe Drago8f7a3002019-02-07 19:35:37 -08001969 unsigned int itemID;
1970 if (version < 1) {
1971 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001972 CHECK(avifROStreamReadU16(&s, &tmp));
Joe Drago8f7a3002019-02-07 19:35:37 -08001973 itemID = tmp;
1974 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07001975 CHECK(avifROStreamReadU32(&s, &itemID));
Joe Drago8f7a3002019-02-07 19:35:37 -08001976 }
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001977 if (itemID <= prevItemID) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001978 avifDiagnosticsPrintf(diag, "Box[ipma] item IDs are not ordered by increasing ID");
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001979 return AVIF_FALSE;
1980 }
1981 prevItemID = itemID;
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001982
1983 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
1984 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001985 avifDiagnosticsPrintf(diag, "Box[ipma] has an invalid item ID [%u]", itemID);
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001986 return AVIF_FALSE;
1987 }
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001988 if (item->ipmaSeen) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001989 avifDiagnosticsPrintf(diag, "Duplicate Box[ipma] for item ID [%u]", itemID);
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001990 return AVIF_FALSE;
1991 }
1992 item->ipmaSeen = AVIF_TRUE;
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001993
Joe Drago8f7a3002019-02-07 19:35:37 -08001994 uint8_t associationCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001995 CHECK(avifROStreamRead(&s, &associationCount, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001996 for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) {
Joe Drago3320e5f2020-04-21 17:36:27 -07001997 avifBool essential = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08001998 uint16_t propertyIndex = 0;
1999 if (propertyIndexIsU16) {
Joe Drago345aaa12019-09-25 13:42:12 -07002000 CHECK(avifROStreamReadU16(&s, &propertyIndex));
Joe Drago3320e5f2020-04-21 17:36:27 -07002001 essential = ((propertyIndex & 0x8000) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08002002 propertyIndex &= 0x7fff;
2003 } else {
2004 uint8_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002005 CHECK(avifROStreamRead(&s, &tmp, 1));
Joe Drago3320e5f2020-04-21 17:36:27 -07002006 essential = ((tmp & 0x80) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08002007 propertyIndex = tmp & 0x7f;
2008 }
2009
2010 if (propertyIndex == 0) {
2011 // Not associated with any item
2012 continue;
2013 }
2014 --propertyIndex; // 1-indexed
2015
Joe Drago9f2b87b2020-06-03 19:36:38 -07002016 if (propertyIndex >= meta->properties.count) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002017 avifDiagnosticsPrintf(diag,
Joe Drago618ed5f2021-05-04 12:13:24 -07002018 "Box[ipma] for item ID [%u] contains an illegal property index [%u] (out of [%u] properties)",
Joe Drago4d5f4a42021-04-28 13:11:45 -07002019 itemID,
2020 propertyIndex,
2021 meta->properties.count);
Joe Drago8f7a3002019-02-07 19:35:37 -08002022 return AVIF_FALSE;
2023 }
2024
Joe Dragoa72da5b2020-06-15 19:40:17 -07002025 // Copy property to item
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07002026 const avifProperty * srcProp = &meta->properties.prop[propertyIndex];
Joe Dragoa72da5b2020-06-15 19:40:17 -07002027
Joe Dragobffba3b2021-05-26 15:46:10 -07002028 static const char * supportedTypes[] = { "ispe", "auxC", "colr", "av1C", "pasp", "clap",
2029 "irot", "imir", "pixi", "a1op", "lsel", "a1lx" };
Joe Dragoa72da5b2020-06-15 19:40:17 -07002030 size_t supportedTypesCount = sizeof(supportedTypes) / sizeof(supportedTypes[0]);
2031 avifBool supportedType = AVIF_FALSE;
2032 for (size_t i = 0; i < supportedTypesCount; ++i) {
2033 if (!memcmp(srcProp->type, supportedTypes[i], 4)) {
2034 supportedType = AVIF_TRUE;
2035 break;
2036 }
2037 }
2038 if (supportedType) {
Wan-Teh Chang37eefca2021-07-14 16:10:34 -07002039 if (essential) {
2040 // Verify that it is legal for this property to be flagged as essential. Any
2041 // types in this list are *required* in the spec to not be flagged as essential
2042 // when associated with an item.
2043 static const char * const nonessentialTypes[] = {
2044
2045 // AVIF: Section 2.3.2.3.2: "If associated, it shall not be marked as essential."
2046 "a1lx"
2047
2048 };
2049 size_t nonessentialTypesCount = sizeof(nonessentialTypes) / sizeof(nonessentialTypes[0]);
2050 for (size_t i = 0; i < nonessentialTypesCount; ++i) {
2051 if (!memcmp(srcProp->type, nonessentialTypes[i], 4)) {
2052 avifDiagnosticsPrintf(diag,
2053 "Item ID [%u] has a %s property association which must not be marked essential, but is",
2054 itemID,
2055 nonessentialTypes[i]);
2056 return AVIF_FALSE;
2057 }
2058 }
2059 } else {
Joe Dragobffba3b2021-05-26 15:46:10 -07002060 // Verify that it is legal for this property to not be flagged as essential. Any
2061 // types in this list are *required* in the spec to be flagged as essential when
2062 // associated with an item.
2063 static const char * const essentialTypes[] = {
2064
2065 // AVIF: Section 2.3.2.1.1: "If associated, it shall be marked as essential."
2066 "a1op",
2067
2068 // HEIF: Section 6.5.11.1: "essential shall be equal to 1 for an 'lsel' item property."
2069 "lsel"
2070
2071 };
2072 size_t essentialTypesCount = sizeof(essentialTypes) / sizeof(essentialTypes[0]);
2073 for (size_t i = 0; i < essentialTypesCount; ++i) {
2074 if (!memcmp(srcProp->type, essentialTypes[i], 4)) {
2075 avifDiagnosticsPrintf(diag,
2076 "Item ID [%u] has a %s property association which must be marked essential, but is not",
2077 itemID,
2078 essentialTypes[i]);
2079 return AVIF_FALSE;
2080 }
2081 }
2082 }
2083
2084 // Supported and valid; associate it with this item.
Joe Dragoa72da5b2020-06-15 19:40:17 -07002085 avifProperty * dstProp = (avifProperty *)avifArrayPushPtr(&item->properties);
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07002086 *dstProp = *srcProp;
Joe Drago3320e5f2020-04-21 17:36:27 -07002087 } else {
2088 if (essential) {
2089 // Discovered an essential item property that libavif doesn't support!
2090 // Make a note to ignore this item later.
2091 item->hasUnsupportedEssentialProperty = AVIF_TRUE;
2092 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002093 }
2094 }
2095 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002096 return AVIF_TRUE;
2097}
2098
Joe Drago4d5f4a42021-04-28 13:11:45 -07002099static avifBool avifParsePrimaryItemBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08002100{
Joe Drago9f2b87b2020-06-03 19:36:38 -07002101 if (meta->primaryItemID > 0) {
Joe Dragof6a42272019-11-21 15:21:41 -08002102 // Illegal to have multiple pitm boxes, bail out
Joe Drago618ed5f2021-05-04 12:13:24 -07002103 avifDiagnosticsPrintf(diag, "Multiple boxes of unique Box[pitm] found");
Joe Dragof6a42272019-11-21 15:21:41 -08002104 return AVIF_FALSE;
2105 }
2106
Joe Drago618ed5f2021-05-04 12:13:24 -07002107 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pitm]");
Joe Dragof6a42272019-11-21 15:21:41 -08002108
2109 uint8_t version;
2110 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
2111
2112 if (version == 0) {
2113 uint16_t tmp16;
2114 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
Joe Drago9f2b87b2020-06-03 19:36:38 -07002115 meta->primaryItemID = tmp16;
Joe Dragof6a42272019-11-21 15:21:41 -08002116 } else {
Joe Drago9f2b87b2020-06-03 19:36:38 -07002117 CHECK(avifROStreamReadU32(&s, &meta->primaryItemID)); // unsigned int(32) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -08002118 }
2119 return AVIF_TRUE;
2120}
2121
Joe Drago4d5f4a42021-04-28 13:11:45 -07002122static avifBool avifParseItemDataBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08002123{
Joe Dragof6a42272019-11-21 15:21:41 -08002124 // Check to see if we've already seen an idat box for this meta box. If so, bail out
Joe Drago1dea33e2021-09-14 17:12:39 -07002125 if (meta->idat.size > 0) {
2126 avifDiagnosticsPrintf(diag, "Meta box contains multiple idat boxes");
2127 return AVIF_FALSE;
2128 }
2129 if (rawLen == 0) {
2130 avifDiagnosticsPrintf(diag, "idat box has a length of 0");
2131 return AVIF_FALSE;
Joe Dragof6a42272019-11-21 15:21:41 -08002132 }
2133
Joe Drago1dea33e2021-09-14 17:12:39 -07002134 avifRWDataSet(&meta->idat, raw, rawLen);
Joe Dragof6a42272019-11-21 15:21:41 -08002135 return AVIF_TRUE;
2136}
2137
Joe Drago4d5f4a42021-04-28 13:11:45 -07002138static avifBool avifParseItemPropertiesBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002139{
Joe Drago618ed5f2021-05-04 12:13:24 -07002140 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002141
2142 avifBoxHeader ipcoHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002143 CHECK(avifROStreamReadBoxHeader(&s, &ipcoHeader));
wantehchangbc35a5f2020-08-12 15:27:39 -07002144 if (memcmp(ipcoHeader.type, "ipco", 4)) {
Joe Dragoba4d67d2021-05-04 12:34:37 -07002145 avifDiagnosticsPrintf(diag, "Failed to find Box[ipco] as the first box in Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002146 return AVIF_FALSE;
2147 }
2148
2149 // Read all item properties inside of ItemPropertyContainerBox
Joe Drago4d5f4a42021-04-28 13:11:45 -07002150 CHECK(avifParseItemPropertyContainerBox(&meta->properties, avifROStreamCurrent(&s), ipcoHeader.size, diag));
Joe Drago345aaa12019-09-25 13:42:12 -07002151 CHECK(avifROStreamSkip(&s, ipcoHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002152
Joe Dragoc60ccae2021-03-31 10:30:48 -07002153 uint32_t versionAndFlagsSeen[MAX_IPMA_VERSION_AND_FLAGS_SEEN];
2154 uint32_t versionAndFlagsSeenCount = 0;
2155
Joe Drago8f7a3002019-02-07 19:35:37 -08002156 // Now read all ItemPropertyAssociation until the end of the box, and make associations
Joe Drago345aaa12019-09-25 13:42:12 -07002157 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002158 avifBoxHeader ipmaHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002159 CHECK(avifROStreamReadBoxHeader(&s, &ipmaHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08002160
2161 if (!memcmp(ipmaHeader.type, "ipma", 4)) {
Joe Dragoc60ccae2021-03-31 10:30:48 -07002162 uint32_t versionAndFlags;
Joe Drago4d5f4a42021-04-28 13:11:45 -07002163 CHECK(avifParseItemPropertyAssociation(meta, avifROStreamCurrent(&s), ipmaHeader.size, diag, &versionAndFlags));
Joe Dragoc60ccae2021-03-31 10:30:48 -07002164 for (uint32_t i = 0; i < versionAndFlagsSeenCount; ++i) {
2165 if (versionAndFlagsSeen[i] == versionAndFlags) {
2166 // HEIF (ISO 23008-12:2017) 9.3.1 - There shall be at most one
2167 // ItemPropertyAssociation box with a given pair of values of version and
2168 // flags.
Joe Drago618ed5f2021-05-04 12:13:24 -07002169 avifDiagnosticsPrintf(diag, "Multiple Box[ipma] with a given pair of values of version and flags. See HEIF (ISO 23008-12:2017) 9.3.1");
Joe Dragoc60ccae2021-03-31 10:30:48 -07002170 return AVIF_FALSE;
2171 }
2172 }
2173 if (versionAndFlagsSeenCount == MAX_IPMA_VERSION_AND_FLAGS_SEEN) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002174 avifDiagnosticsPrintf(diag, "Exceeded possible count of unique ipma version and flags tuples");
Joe Dragoc60ccae2021-03-31 10:30:48 -07002175 return AVIF_FALSE;
2176 }
2177 versionAndFlagsSeen[versionAndFlagsSeenCount] = versionAndFlags;
2178 ++versionAndFlagsSeenCount;
Joe Drago8f7a3002019-02-07 19:35:37 -08002179 } else {
2180 // These must all be type ipma
Joe Drago618ed5f2021-05-04 12:13:24 -07002181 avifDiagnosticsPrintf(diag, "Box[iprp] contains a box that isn't type 'ipma'");
Joe Drago8f7a3002019-02-07 19:35:37 -08002182 return AVIF_FALSE;
2183 }
2184
Joe Drago345aaa12019-09-25 13:42:12 -07002185 CHECK(avifROStreamSkip(&s, ipmaHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002186 }
2187 return AVIF_TRUE;
2188}
2189
Joe Drago4d5f4a42021-04-28 13:11:45 -07002190static avifBool avifParseItemInfoEntry(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002191{
Joe Drago618ed5f2021-05-04 12:13:24 -07002192 BEGIN_STREAM(s, raw, rawLen, diag, "Box[infe]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002193
Wan-Teh Chang35a4bc92022-02-17 09:14:49 -08002194 uint8_t version;
2195 uint32_t flags;
2196 CHECK(avifROStreamReadVersionAndFlags(&s, &version, &flags));
2197 // Version 2+ is required for item_type
2198 if (version != 2 && version != 3) {
2199 avifDiagnosticsPrintf(s.diag, "%s: Expecting box version 2 or 3, got version %u", s.diagContext, version);
2200 return AVIF_FALSE;
2201 }
2202 // TODO: check flags. ISO/IEC 23008-12:2017, Section 9.2 says:
2203 // The flags field of ItemInfoEntry with version greater than or equal to 2 is specified as
2204 // follows:
2205 //
2206 // (flags & 1) equal to 1 indicates that the item is not intended to be a part of the
2207 // presentation. For example, when (flags & 1) is equal to 1 for an image item, the image
2208 // item should not be displayed.
2209 // (flags & 1) equal to 0 indicates that the item is intended to be a part of the
2210 // presentation.
2211 //
2212 // See also Section 6.4.2.
Joe Drago8f7a3002019-02-07 19:35:37 -08002213
Wan-Teh Chang35a4bc92022-02-17 09:14:49 -08002214 uint32_t itemID;
2215 if (version == 2) {
2216 uint16_t tmp;
2217 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) item_ID;
2218 itemID = tmp;
2219 } else {
2220 assert(version == 3);
2221 CHECK(avifROStreamReadU32(&s, &itemID)); // unsigned int(32) item_ID;
2222 }
Joe Drago345aaa12019-09-25 13:42:12 -07002223 uint16_t itemProtectionIndex; // unsigned int(16) item_protection_index;
2224 CHECK(avifROStreamReadU16(&s, &itemProtectionIndex)); //
2225 uint8_t itemType[4]; // unsigned int(32) item_type;
2226 CHECK(avifROStreamRead(&s, itemType, 4)); //
Joe Drago8f7a3002019-02-07 19:35:37 -08002227
Joe Dragof6a42272019-11-21 15:21:41 -08002228 avifContentType contentType;
2229 if (!memcmp(itemType, "mime", 4)) {
2230 CHECK(avifROStreamReadString(&s, NULL, 0)); // string item_name; (skipped)
2231 CHECK(avifROStreamReadString(&s, contentType.contentType, CONTENTTYPE_SIZE)); // string content_type;
2232 } else {
2233 memset(&contentType, 0, sizeof(contentType));
2234 }
2235
Joe Drago9f2b87b2020-06-03 19:36:38 -07002236 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07002237 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002238 avifDiagnosticsPrintf(diag, "Box[infe] has an invalid item ID [%u]", itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07002239 return AVIF_FALSE;
2240 }
2241
Joe Drago05559c92019-07-17 16:33:38 -07002242 memcpy(item->type, itemType, sizeof(itemType));
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07002243 item->contentType = contentType;
Joe Drago8f7a3002019-02-07 19:35:37 -08002244 return AVIF_TRUE;
2245}
2246
Joe Drago4d5f4a42021-04-28 13:11:45 -07002247static avifBool avifParseItemInfoBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002248{
Joe Drago618ed5f2021-05-04 12:13:24 -07002249 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iinf]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002250
2251 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07002252 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08002253 uint32_t entryCount;
2254 if (version == 0) {
2255 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002256 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08002257 entryCount = tmp;
2258 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002259 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08002260 } else {
Joe Dragoba4d67d2021-05-04 12:34:37 -07002261 avifDiagnosticsPrintf(diag, "Box[iinf] has an unsupported version %u", version);
Joe Drago8f7a3002019-02-07 19:35:37 -08002262 return AVIF_FALSE;
2263 }
2264
Joe Drago678b9382019-02-09 03:17:47 -08002265 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002266 avifBoxHeader infeHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002267 CHECK(avifROStreamReadBoxHeader(&s, &infeHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08002268
2269 if (!memcmp(infeHeader.type, "infe", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002270 CHECK(avifParseItemInfoEntry(meta, avifROStreamCurrent(&s), infeHeader.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002271 } else {
Joe Drago618ed5f2021-05-04 12:13:24 -07002272 // These must all be type infe
2273 avifDiagnosticsPrintf(diag, "Box[iinf] contains a box that isn't type 'infe'");
Joe Drago8f7a3002019-02-07 19:35:37 -08002274 return AVIF_FALSE;
2275 }
2276
Joe Drago345aaa12019-09-25 13:42:12 -07002277 CHECK(avifROStreamSkip(&s, infeHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002278 }
2279
2280 return AVIF_TRUE;
2281}
2282
Joe Drago4d5f4a42021-04-28 13:11:45 -07002283static avifBool avifParseItemReferenceBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002284{
Joe Drago618ed5f2021-05-04 12:13:24 -07002285 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iref]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002286
2287 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07002288 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08002289
Joe Drago345aaa12019-09-25 13:42:12 -07002290 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002291 avifBoxHeader irefHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002292 CHECK(avifROStreamReadBoxHeader(&s, &irefHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08002293
2294 uint32_t fromID = 0;
2295 if (version == 0) {
2296 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002297 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002298 fromID = tmp;
2299 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002300 CHECK(avifROStreamReadU32(&s, &fromID)); // unsigned int(32) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002301 } else {
2302 // unsupported iref version, skip it
2303 break;
2304 }
2305
2306 uint16_t referenceCount = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07002307 CHECK(avifROStreamReadU16(&s, &referenceCount)); // unsigned int(16) reference_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08002308
2309 for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) {
2310 uint32_t toID = 0;
2311 if (version == 0) {
2312 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002313 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002314 toID = tmp;
2315 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002316 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002317 } else {
2318 // unsupported iref version, skip it
2319 break;
2320 }
2321
2322 // Read this reference as "{fromID} is a {irefType} for {toID}"
2323 if (fromID && toID) {
Joe Drago9f2b87b2020-06-03 19:36:38 -07002324 avifDecoderItem * item = avifMetaFindItem(meta, fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07002325 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002326 avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID [%u]", fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07002327 return AVIF_FALSE;
2328 }
2329
Joe Drago8f7a3002019-02-07 19:35:37 -08002330 if (!memcmp(irefHeader.type, "thmb", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07002331 item->thumbnailForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002332 } else if (!memcmp(irefHeader.type, "auxl", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07002333 item->auxForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002334 } else if (!memcmp(irefHeader.type, "cdsc", 4)) {
Joe Dragof6a42272019-11-21 15:21:41 -08002335 item->descForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002336 } else if (!memcmp(irefHeader.type, "dimg", 4)) {
Joe Drago060d5342020-03-03 10:53:49 -08002337 // derived images refer in the opposite direction
Joe Drago9f2b87b2020-06-03 19:36:38 -07002338 avifDecoderItem * dimg = avifMetaFindItem(meta, toID);
Joe Drago060d5342020-03-03 10:53:49 -08002339 if (!dimg) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002340 avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID dimg ref [%u]", toID);
Joe Drago060d5342020-03-03 10:53:49 -08002341 return AVIF_FALSE;
2342 }
2343
2344 dimg->dimgForID = fromID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002345 } else if (!memcmp(irefHeader.type, "prem", 4)) {
Yuan Tonge4850be2021-01-22 14:21:25 +08002346 item->premByID = toID;
2347 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002348 }
2349 }
2350 }
2351
2352 return AVIF_TRUE;
2353}
2354
Joe Drago4d5f4a42021-04-28 13:11:45 -07002355static avifBool avifParseMetaBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002356{
Joe Drago618ed5f2021-05-04 12:13:24 -07002357 BEGIN_STREAM(s, raw, rawLen, diag, "Box[meta]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002358
Joe Drago345aaa12019-09-25 13:42:12 -07002359 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08002360
Joe Dragoba1eb492020-06-22 17:05:04 -07002361 ++meta->idatID; // for tracking idat
Joe Dragof6a42272019-11-21 15:21:41 -08002362
Joe Dragoe0185182021-03-31 08:14:51 -07002363 avifBool firstBox = AVIF_TRUE;
Joe Drago8f439092020-08-28 15:05:17 -07002364 uint32_t uniqueBoxFlags = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07002365 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002366 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002367 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08002368
Joe Dragoe0185182021-03-31 08:14:51 -07002369 if (firstBox) {
2370 if (!memcmp(header.type, "hdlr", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002371 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 0, "meta", "hdlr", diag));
2372 CHECK(avifParseHandlerBox(avifROStreamCurrent(&s), header.size, diag));
Joe Dragoe0185182021-03-31 08:14:51 -07002373 firstBox = AVIF_FALSE;
2374 } else {
2375 // hdlr must be the first box!
Joe Dragoba4d67d2021-05-04 12:34:37 -07002376 avifDiagnosticsPrintf(diag, "Box[meta] does not have a Box[hdlr] as its first child box");
Joe Dragoe0185182021-03-31 08:14:51 -07002377 return AVIF_FALSE;
2378 }
2379 } else if (!memcmp(header.type, "iloc", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002380 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 1, "meta", "iloc", diag));
2381 CHECK(avifParseItemLocationBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Dragof6a42272019-11-21 15:21:41 -08002382 } else if (!memcmp(header.type, "pitm", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002383 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 2, "meta", "pitm", diag));
2384 CHECK(avifParsePrimaryItemBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Dragof6a42272019-11-21 15:21:41 -08002385 } else if (!memcmp(header.type, "idat", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002386 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 3, "meta", "idat", diag));
2387 CHECK(avifParseItemDataBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002388 } else if (!memcmp(header.type, "iprp", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002389 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 4, "meta", "iprp", diag));
2390 CHECK(avifParseItemPropertiesBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002391 } else if (!memcmp(header.type, "iinf", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002392 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 5, "meta", "iinf", diag));
2393 CHECK(avifParseItemInfoBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002394 } else if (!memcmp(header.type, "iref", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002395 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 6, "meta", "iref", diag));
2396 CHECK(avifParseItemReferenceBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002397 }
2398
Joe Drago345aaa12019-09-25 13:42:12 -07002399 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002400 }
Joe Dragoe0185182021-03-31 08:14:51 -07002401 if (firstBox) {
2402 // The meta box must not be empty (it must contain at least a hdlr box)
Joe Dragoba4d67d2021-05-04 12:34:37 -07002403 avifDiagnosticsPrintf(diag, "Box[meta] has no child boxes");
Joe Dragoe0185182021-03-31 08:14:51 -07002404 return AVIF_FALSE;
2405 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002406 return AVIF_TRUE;
2407}
2408
Wan-Teh Chang980d5852021-08-03 20:02:38 -07002409static avifBool avifParseTrackHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, uint32_t imageSizeLimit, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002410{
Joe Drago618ed5f2021-05-04 12:13:24 -07002411 BEGIN_STREAM(s, raw, rawLen, diag, "Box[tkhd]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002412
2413 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07002414 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002415
2416 uint32_t ignored32, trackID;
2417 uint64_t ignored64;
2418 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002419 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
2420 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
2421 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07002422 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
2423 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002424 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07002425 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
2426 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
2427 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07002428 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
2429 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002430 } else {
2431 // Unsupported version
Joe Drago618ed5f2021-05-04 12:13:24 -07002432 avifDiagnosticsPrintf(diag, "Box[tkhd] has an unsupported version [%u]", version);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002433 return AVIF_FALSE;
2434 }
2435
Joe Dragofc4144e2019-09-27 20:35:06 -07002436 // Skipping the following 52 bytes here:
2437 // ------------------------------------
2438 // const unsigned int(32)[2] reserved = 0;
2439 // template int(16) layer = 0;
2440 // template int(16) alternate_group = 0;
2441 // template int(16) volume = {if track_is_audio 0x0100 else 0};
2442 // const unsigned int(16) reserved = 0;
2443 // template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix
2444 CHECK(avifROStreamSkip(&s, 52));
2445
2446 uint32_t width, height;
2447 CHECK(avifROStreamReadU32(&s, &width)); // unsigned int(32) width;
2448 CHECK(avifROStreamReadU32(&s, &height)); // unsigned int(32) height;
2449 track->width = width >> 16;
2450 track->height = height >> 16;
2451
Wan-Teh Changbd1492e2021-08-06 16:08:14 -07002452 if ((track->width == 0) || (track->height == 0)) {
Joe Drago46104d62021-05-27 18:17:29 -07002453 avifDiagnosticsPrintf(diag, "Track ID [%u] has an invalid size [%ux%u]", track->id, track->width, track->height);
2454 return AVIF_FALSE;
2455 }
Wan-Teh Changbd1492e2021-08-06 16:08:14 -07002456 if (track->width > (imageSizeLimit / track->height)) {
2457 avifDiagnosticsPrintf(diag, "Track ID [%u] size is too large [%ux%u]", track->id, track->width, track->height);
2458 return AVIF_FALSE;
2459 }
Joe Drago46104d62021-05-27 18:17:29 -07002460
Joe Dragoae7e2c32019-07-18 15:22:25 -07002461 // TODO: support scaling based on width/height track header info?
2462
2463 track->id = trackID;
2464 return AVIF_TRUE;
2465}
2466
Joe Drago4d5f4a42021-04-28 13:11:45 -07002467static avifBool avifParseMediaHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002468{
Joe Drago618ed5f2021-05-04 12:13:24 -07002469 BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdhd]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002470
2471 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07002472 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002473
2474 uint32_t ignored32, mediaTimescale, mediaDuration32;
2475 uint64_t ignored64, mediaDuration64;
2476 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002477 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
2478 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
2479 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
2480 CHECK(avifROStreamReadU64(&s, &mediaDuration64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002481 track->mediaDuration = mediaDuration64;
2482 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07002483 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
2484 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
2485 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
2486 CHECK(avifROStreamReadU32(&s, &mediaDuration32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002487 track->mediaDuration = (uint64_t)mediaDuration32;
2488 } else {
2489 // Unsupported version
Joe Drago618ed5f2021-05-04 12:13:24 -07002490 avifDiagnosticsPrintf(diag, "Box[mdhd] has an unsupported version [%u]", version);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002491 return AVIF_FALSE;
2492 }
2493
2494 track->mediaTimescale = mediaTimescale;
2495 return AVIF_TRUE;
2496}
2497
Joe Drago618ed5f2021-05-04 12:13:24 -07002498static avifBool avifParseChunkOffsetBox(avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002499{
Wan-Teh Chang72de1662021-05-28 18:11:11 -07002500 BEGIN_STREAM(s, raw, rawLen, diag, largeOffsets ? "Box[co64]" : "Box[stco]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002501
Joe Drago345aaa12019-09-25 13:42:12 -07002502 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002503
2504 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002505 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002506 for (uint32_t i = 0; i < entryCount; ++i) {
2507 uint64_t offset;
2508 if (largeOffsets) {
Joe Drago345aaa12019-09-25 13:42:12 -07002509 CHECK(avifROStreamReadU64(&s, &offset)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002510 } else {
2511 uint32_t offset32;
Joe Drago345aaa12019-09-25 13:42:12 -07002512 CHECK(avifROStreamReadU32(&s, &offset32)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002513 offset = (uint64_t)offset32;
2514 }
2515
2516 avifSampleTableChunk * chunk = (avifSampleTableChunk *)avifArrayPushPtr(&sampleTable->chunks);
2517 chunk->offset = offset;
2518 }
2519 return AVIF_TRUE;
2520}
2521
Joe Drago4d5f4a42021-04-28 13:11:45 -07002522static avifBool avifParseSampleToChunkBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002523{
Joe Drago618ed5f2021-05-04 12:13:24 -07002524 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsc]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002525
Joe Drago345aaa12019-09-25 13:42:12 -07002526 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002527
2528 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002529 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago859c9102021-03-11 18:06:40 -08002530 uint32_t prevFirstChunk = 0;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002531 for (uint32_t i = 0; i < entryCount; ++i) {
2532 avifSampleTableSampleToChunk * sampleToChunk = (avifSampleTableSampleToChunk *)avifArrayPushPtr(&sampleTable->sampleToChunks);
Joe Drago345aaa12019-09-25 13:42:12 -07002533 CHECK(avifROStreamReadU32(&s, &sampleToChunk->firstChunk)); // unsigned int(32) first_chunk;
2534 CHECK(avifROStreamReadU32(&s, &sampleToChunk->samplesPerChunk)); // unsigned int(32) samples_per_chunk;
2535 CHECK(avifROStreamReadU32(&s, &sampleToChunk->sampleDescriptionIndex)); // unsigned int(32) sample_description_index;
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002536 // The first_chunk fields should start with 1 and be strictly increasing.
2537 if (i == 0) {
2538 if (sampleToChunk->firstChunk != 1) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002539 avifDiagnosticsPrintf(diag, "Box[stsc] does not begin with chunk 1 [%u]", sampleToChunk->firstChunk);
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002540 return AVIF_FALSE;
2541 }
2542 } else {
2543 if (sampleToChunk->firstChunk <= prevFirstChunk) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002544 avifDiagnosticsPrintf(diag, "Box[stsc] chunks are not strictly increasing");
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002545 return AVIF_FALSE;
2546 }
2547 }
2548 prevFirstChunk = sampleToChunk->firstChunk;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002549 }
2550 return AVIF_TRUE;
2551}
2552
Joe Drago618ed5f2021-05-04 12:13:24 -07002553static avifBool avifParseSampleSizeBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002554{
Joe Drago618ed5f2021-05-04 12:13:24 -07002555 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsz]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002556
Joe Drago345aaa12019-09-25 13:42:12 -07002557 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002558
Joe Drago370be3f2020-02-07 15:59:42 -08002559 uint32_t allSamplesSize, sampleCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002560 CHECK(avifROStreamReadU32(&s, &allSamplesSize)); // unsigned int(32) sample_size;
Joe Drago370be3f2020-02-07 15:59:42 -08002561 CHECK(avifROStreamReadU32(&s, &sampleCount)); // unsigned int(32) sample_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002562
Joe Drago370be3f2020-02-07 15:59:42 -08002563 if (allSamplesSize > 0) {
2564 sampleTable->allSamplesSize = allSamplesSize;
2565 } else {
2566 for (uint32_t i = 0; i < sampleCount; ++i) {
2567 avifSampleTableSampleSize * sampleSize = (avifSampleTableSampleSize *)avifArrayPushPtr(&sampleTable->sampleSizes);
Joe Drago345aaa12019-09-25 13:42:12 -07002568 CHECK(avifROStreamReadU32(&s, &sampleSize->size)); // unsigned int(32) entry_size;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002569 }
2570 }
2571 return AVIF_TRUE;
2572}
2573
Joe Drago618ed5f2021-05-04 12:13:24 -07002574static avifBool avifParseSyncSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago22c1ad92019-09-26 12:46:50 -07002575{
Joe Drago618ed5f2021-05-04 12:13:24 -07002576 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stss]");
Joe Drago22c1ad92019-09-26 12:46:50 -07002577
2578 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
2579
2580 uint32_t entryCount;
2581 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
2582
2583 for (uint32_t i = 0; i < entryCount; ++i) {
2584 uint32_t sampleNumber = 0;
2585 CHECK(avifROStreamReadU32(&s, &sampleNumber)); // unsigned int(32) sample_number;
2586 avifSyncSample * syncSample = (avifSyncSample *)avifArrayPushPtr(&sampleTable->syncSamples);
2587 syncSample->sampleNumber = sampleNumber;
2588 }
2589 return AVIF_TRUE;
2590}
2591
Joe Drago618ed5f2021-05-04 12:13:24 -07002592static avifBool avifParseTimeToSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002593{
Joe Drago618ed5f2021-05-04 12:13:24 -07002594 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stts]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002595
Joe Drago345aaa12019-09-25 13:42:12 -07002596 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002597
2598 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002599 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002600
2601 for (uint32_t i = 0; i < entryCount; ++i) {
2602 avifSampleTableTimeToSample * timeToSample = (avifSampleTableTimeToSample *)avifArrayPushPtr(&sampleTable->timeToSamples);
Joe Drago345aaa12019-09-25 13:42:12 -07002603 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleCount)); // unsigned int(32) sample_count;
2604 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleDelta)); // unsigned int(32) sample_delta;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002605 }
2606 return AVIF_TRUE;
2607}
2608
Joe Drago4d5f4a42021-04-28 13:11:45 -07002609static avifBool avifParseSampleDescriptionBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago2c0924c2019-09-26 17:41:01 -07002610{
Joe Drago618ed5f2021-05-04 12:13:24 -07002611 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsd]");
Joe Drago2c0924c2019-09-26 17:41:01 -07002612
2613 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
2614
2615 uint32_t entryCount;
2616 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
2617
2618 for (uint32_t i = 0; i < entryCount; ++i) {
2619 avifBoxHeader sampleEntryHeader;
2620 CHECK(avifROStreamReadBoxHeader(&s, &sampleEntryHeader));
2621
2622 avifSampleDescription * description = (avifSampleDescription *)avifArrayPushPtr(&sampleTable->sampleDescriptions);
Wan-Teh Changf732a4d2022-01-21 15:56:35 -08002623 if (!avifArrayCreate(&description->properties, sizeof(avifProperty), 16)) {
2624 avifArrayPop(&sampleTable->sampleDescriptions);
2625 return AVIF_FALSE;
2626 }
Joe Drago2c0924c2019-09-26 17:41:01 -07002627 memcpy(description->format, sampleEntryHeader.type, sizeof(description->format));
Joe Drago6500fd62019-10-08 17:17:34 -07002628 size_t remainingBytes = avifROStreamRemainingBytes(&s);
2629 if (!memcmp(description->format, "av01", 4) && (remainingBytes > VISUALSAMPLEENTRY_SIZE)) {
Joe Drago11d23592021-01-05 14:18:57 -08002630 CHECK(avifParseItemPropertyContainerBox(&description->properties,
2631 avifROStreamCurrent(&s) + VISUALSAMPLEENTRY_SIZE,
Joe Drago4d5f4a42021-04-28 13:11:45 -07002632 remainingBytes - VISUALSAMPLEENTRY_SIZE,
2633 diag));
Joe Drago6500fd62019-10-08 17:17:34 -07002634 }
Joe Drago2c0924c2019-09-26 17:41:01 -07002635
2636 CHECK(avifROStreamSkip(&s, sampleEntryHeader.size));
2637 }
2638 return AVIF_TRUE;
2639}
2640
Joe Drago4d5f4a42021-04-28 13:11:45 -07002641static avifBool avifParseSampleTableBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002642{
2643 if (track->sampleTable) {
2644 // A TrackBox may only have one SampleTable
Joe Dragoba4d67d2021-05-04 12:34:37 -07002645 avifDiagnosticsPrintf(diag, "Duplicate Box[stbl] for a single track detected");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002646 return AVIF_FALSE;
2647 }
2648 track->sampleTable = avifSampleTableCreate();
2649
Joe Drago618ed5f2021-05-04 12:13:24 -07002650 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stbl]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002651
Joe Drago345aaa12019-09-25 13:42:12 -07002652 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002653 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002654 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002655
2656 if (!memcmp(header.type, "stco", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002657 CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002658 } else if (!memcmp(header.type, "co64", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002659 CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002660 } else if (!memcmp(header.type, "stsc", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002661 CHECK(avifParseSampleToChunkBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002662 } else if (!memcmp(header.type, "stsz", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002663 CHECK(avifParseSampleSizeBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Drago22c1ad92019-09-26 12:46:50 -07002664 } else if (!memcmp(header.type, "stss", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002665 CHECK(avifParseSyncSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002666 } else if (!memcmp(header.type, "stts", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002667 CHECK(avifParseTimeToSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Drago2c0924c2019-09-26 17:41:01 -07002668 } else if (!memcmp(header.type, "stsd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002669 CHECK(avifParseSampleDescriptionBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002670 }
2671
Joe Drago345aaa12019-09-25 13:42:12 -07002672 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002673 }
Joe Dragoae7e2c32019-07-18 15:22:25 -07002674 return AVIF_TRUE;
2675}
2676
Joe Drago4d5f4a42021-04-28 13:11:45 -07002677static avifBool avifParseMediaInformationBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002678{
Joe Drago618ed5f2021-05-04 12:13:24 -07002679 BEGIN_STREAM(s, raw, rawLen, diag, "Box[minf]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002680
Joe Drago345aaa12019-09-25 13:42:12 -07002681 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002682 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002683 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002684
2685 if (!memcmp(header.type, "stbl", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002686 CHECK(avifParseSampleTableBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002687 }
2688
Joe Drago345aaa12019-09-25 13:42:12 -07002689 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002690 }
2691 return AVIF_TRUE;
2692}
2693
Joe Drago4d5f4a42021-04-28 13:11:45 -07002694static avifBool avifParseMediaBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002695{
Joe Drago618ed5f2021-05-04 12:13:24 -07002696 BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdia]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002697
Joe Drago345aaa12019-09-25 13:42:12 -07002698 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002699 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002700 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002701
2702 if (!memcmp(header.type, "mdhd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002703 CHECK(avifParseMediaHeaderBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002704 } else if (!memcmp(header.type, "minf", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002705 CHECK(avifParseMediaInformationBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002706 }
2707
Joe Drago345aaa12019-09-25 13:42:12 -07002708 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002709 }
2710 return AVIF_TRUE;
2711}
2712
Joe Drago618ed5f2021-05-04 12:13:24 -07002713static avifBool avifTrackReferenceBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago46ea0582019-07-22 15:55:47 -07002714{
Joe Drago618ed5f2021-05-04 12:13:24 -07002715 BEGIN_STREAM(s, raw, rawLen, diag, "Box[tref]");
Joe Drago46ea0582019-07-22 15:55:47 -07002716
Joe Drago345aaa12019-09-25 13:42:12 -07002717 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002718 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002719 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago46ea0582019-07-22 15:55:47 -07002720
2721 if (!memcmp(header.type, "auxl", 4)) {
2722 uint32_t toID;
Joe Dragoceb2fa02021-01-29 18:14:55 -08002723 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) track_IDs[];
Joe Drago345aaa12019-09-25 13:42:12 -07002724 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Joe Drago46ea0582019-07-22 15:55:47 -07002725 track->auxForID = toID;
Yuan Tonge4850be2021-01-22 14:21:25 +08002726 } else if (!memcmp(header.type, "prem", 4)) {
2727 uint32_t byID;
Joe Dragoceb2fa02021-01-29 18:14:55 -08002728 CHECK(avifROStreamReadU32(&s, &byID)); // unsigned int(32) track_IDs[];
2729 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Yuan Tonge4850be2021-01-22 14:21:25 +08002730 track->premByID = byID;
Joe Drago46ea0582019-07-22 15:55:47 -07002731 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07002732 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago46ea0582019-07-22 15:55:47 -07002733 }
2734 }
2735 return AVIF_TRUE;
2736}
2737
Wan-Teh Chang980d5852021-08-03 20:02:38 -07002738static avifBool avifParseTrackBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, uint32_t imageSizeLimit)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002739{
Wan-Teh Chang53121812021-05-28 18:02:59 -07002740 BEGIN_STREAM(s, raw, rawLen, data->diag, "Box[trak]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002741
Joe Dragoa72da5b2020-06-15 19:40:17 -07002742 avifTrack * track = avifDecoderDataCreateTrack(data);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002743
Joe Drago345aaa12019-09-25 13:42:12 -07002744 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002745 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002746 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002747
2748 if (!memcmp(header.type, "tkhd", 4)) {
Wan-Teh Chang980d5852021-08-03 20:02:38 -07002749 CHECK(avifParseTrackHeaderBox(track, avifROStreamCurrent(&s), header.size, imageSizeLimit, data->diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07002750 } else if (!memcmp(header.type, "meta", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002751 CHECK(avifParseMetaBox(track->meta, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002752 } else if (!memcmp(header.type, "mdia", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002753 CHECK(avifParseMediaBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Drago46ea0582019-07-22 15:55:47 -07002754 } else if (!memcmp(header.type, "tref", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002755 CHECK(avifTrackReferenceBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002756 }
2757
Joe Drago345aaa12019-09-25 13:42:12 -07002758 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002759 }
2760 return AVIF_TRUE;
2761}
2762
Joe Dragob314e492021-09-14 16:57:00 -07002763static avifBool avifParseMovieBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, uint32_t imageSizeLimit)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002764{
Wan-Teh Chang53121812021-05-28 18:02:59 -07002765 BEGIN_STREAM(s, raw, rawLen, data->diag, "Box[moov]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002766
Joe Drago345aaa12019-09-25 13:42:12 -07002767 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002768 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002769 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002770
2771 if (!memcmp(header.type, "trak", 4)) {
Wan-Teh Chang980d5852021-08-03 20:02:38 -07002772 CHECK(avifParseTrackBox(data, avifROStreamCurrent(&s), header.size, imageSizeLimit));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002773 }
2774
Joe Drago345aaa12019-09-25 13:42:12 -07002775 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002776 }
2777 return AVIF_TRUE;
2778}
2779
Joe Drago4d5f4a42021-04-28 13:11:45 -07002780static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002781{
Joe Drago618ed5f2021-05-04 12:13:24 -07002782 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002783
Joe Drago345aaa12019-09-25 13:42:12 -07002784 CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
2785 CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
Joe Drago8f7a3002019-02-07 19:35:37 -08002786
Joe Drago345aaa12019-09-25 13:42:12 -07002787 size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
Joe Drago8f7a3002019-02-07 19:35:37 -08002788 if ((compatibleBrandsBytes % 4) != 0) {
Joe Drago48867292021-05-04 13:23:08 -07002789 avifDiagnosticsPrintf(diag, "Box[ftyp] contains a compatible brands section that isn't divisible by 4 [%zu]", compatibleBrandsBytes);
Joe Drago8f7a3002019-02-07 19:35:37 -08002790 return AVIF_FALSE;
2791 }
Wan-Teh Chang6da0a882020-07-01 12:19:31 -07002792 ftyp->compatibleBrands = avifROStreamCurrent(&s);
2793 CHECK(avifROStreamSkip(&s, compatibleBrandsBytes));
Joe Drago7e37b972019-07-24 12:44:47 -07002794 ftyp->compatibleBrandsCount = (int)compatibleBrandsBytes / 4;
Joe Drago8f7a3002019-02-07 19:35:37 -08002795
2796 return AVIF_TRUE;
2797}
2798
Joe Dragobb39aab2020-11-03 19:23:40 -08002799static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand);
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002800static avifBool avifFileTypeIsCompatible(avifFileType * ftyp);
2801
Joe Dragobe4cbb92020-09-21 12:14:05 -07002802static avifResult avifParse(avifDecoder * decoder)
Joe Drago8f7a3002019-02-07 19:35:37 -08002803{
Joe Drago9aa931f2020-09-24 13:10:11 -07002804 // Note: this top-level function is the only avifParse*() function that returns avifResult instead of avifBool.
2805 // Be sure to use CHECKERR() in this function with an explicit error result instead of simply using CHECK().
2806
Joe Dragobe4cbb92020-09-21 12:14:05 -07002807 avifResult readResult;
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002808 uint64_t parseOffset = 0;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002809 avifDecoderData * data = decoder->data;
Joe Dragobb39aab2020-11-03 19:23:40 -08002810 avifBool ftypSeen = AVIF_FALSE;
2811 avifBool metaSeen = AVIF_FALSE;
2812 avifBool moovSeen = AVIF_FALSE;
2813 avifBool needsMeta = AVIF_FALSE;
2814 avifBool needsMoov = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08002815
Joe Dragobe4cbb92020-09-21 12:14:05 -07002816 for (;;) {
2817 // Read just enough to get the next box header (a max of 32 bytes)
2818 avifROData headerContents;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002819 if ((decoder->io->sizeHint > 0) && (parseOffset > decoder->io->sizeHint)) {
2820 return AVIF_RESULT_BMFF_PARSE_FAILED;
2821 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002822 readResult = decoder->io->read(decoder->io, 0, parseOffset, 32, &headerContents);
2823 if (readResult != AVIF_RESULT_OK) {
2824 return readResult;
2825 }
2826 if (!headerContents.size) {
2827 // If we got AVIF_RESULT_OK from the reader but received 0 bytes,
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07002828 // we've reached the end of the file with no errors. Hooray!
Joe Dragobe4cbb92020-09-21 12:14:05 -07002829 break;
Joe Drago8f7a3002019-02-07 19:35:37 -08002830 }
2831
Joe Dragobe4cbb92020-09-21 12:14:05 -07002832 // Parse the header, and find out how many bytes it actually was
Joe Drago618ed5f2021-05-04 12:13:24 -07002833 BEGIN_STREAM(headerStream, headerContents.data, headerContents.size, &decoder->diag, "File-level box header");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002834 avifBoxHeader header;
Joe Drago468ded82020-09-24 12:52:51 -07002835 CHECKERR(avifROStreamReadBoxHeaderPartial(&headerStream, &header), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002836 parseOffset += headerStream.offset;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002837 assert((decoder->io->sizeHint == 0) || (parseOffset <= decoder->io->sizeHint));
Joe Dragobe4cbb92020-09-21 12:14:05 -07002838
2839 // Try to get the remainder of the box, if necessary
2840 avifROData boxContents = AVIF_DATA_EMPTY;
2841
2842 // TODO: reorg this code to only do these memcmps once each
Wan-Teh Changb309e982021-02-20 16:59:36 -08002843 if (!memcmp(header.type, "ftyp", 4) || !memcmp(header.type, "meta", 4) || !memcmp(header.type, "moov", 4)) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002844 readResult = decoder->io->read(decoder->io, 0, parseOffset, header.size, &boxContents);
2845 if (readResult != AVIF_RESULT_OK) {
2846 return readResult;
2847 }
2848 if (boxContents.size != header.size) {
2849 // A truncated box, bail out
Joe Dragofe5d5e42020-09-24 13:07:58 -07002850 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002851 }
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002852 } else if (header.size > (UINT64_MAX - parseOffset)) {
2853 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002854 }
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002855 parseOffset += header.size;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002856
2857 if (!memcmp(header.type, "ftyp", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002858 CHECKERR(!ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002859 avifFileType ftyp;
Joe Drago4d5f4a42021-04-28 13:11:45 -07002860 CHECKERR(avifParseFileTypeBox(&ftyp, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002861 if (!avifFileTypeIsCompatible(&ftyp)) {
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002862 return AVIF_RESULT_INVALID_FTYP;
2863 }
Joe Dragobb39aab2020-11-03 19:23:40 -08002864 ftypSeen = AVIF_TRUE;
Joe Dragof131b782021-09-21 16:52:39 -07002865 memcpy(data->majorBrand, ftyp.majorBrand, 4); // Remember the major brand for future AVIF_DECODER_SOURCE_AUTO decisions
Joe Dragobb39aab2020-11-03 19:23:40 -08002866 needsMeta = avifFileTypeHasBrand(&ftyp, "avif");
2867 needsMoov = avifFileTypeHasBrand(&ftyp, "avis");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002868 } else if (!memcmp(header.type, "meta", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002869 CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Drago4d5f4a42021-04-28 13:11:45 -07002870 CHECKERR(avifParseMetaBox(data->meta, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002871 metaSeen = AVIF_TRUE;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002872 } else if (!memcmp(header.type, "moov", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002873 CHECKERR(!moovSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragob314e492021-09-14 16:57:00 -07002874 CHECKERR(avifParseMovieBox(data, boxContents.data, boxContents.size, decoder->imageSizeLimit), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002875 moovSeen = AVIF_TRUE;
2876 }
2877
2878 // See if there is enough information to consider Parse() a success and early-out:
2879 // * If the brand 'avif' is present, require a meta box
2880 // * If the brand 'avis' is present, require a moov box
2881 if (ftypSeen && (!needsMeta || metaSeen) && (!needsMoov || moovSeen)) {
2882 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002883 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002884 }
Joe Drago029375a2021-04-28 11:57:56 -07002885 if (!ftypSeen) {
2886 return AVIF_RESULT_INVALID_FTYP;
2887 }
2888 if ((needsMeta && !metaSeen) || (needsMoov && !moovSeen)) {
2889 return AVIF_RESULT_TRUNCATED_DATA;
2890 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002891 return AVIF_RESULT_OK;
Joe Drago8f7a3002019-02-07 19:35:37 -08002892}
2893
2894// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08002895
Joe Dragobb39aab2020-11-03 19:23:40 -08002896static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand)
Joe Drago7e37b972019-07-24 12:44:47 -07002897{
Joe Dragobb39aab2020-11-03 19:23:40 -08002898 if (!memcmp(ftyp->majorBrand, brand, 4)) {
2899 return AVIF_TRUE;
2900 }
2901
2902 for (int compatibleBrandIndex = 0; compatibleBrandIndex < ftyp->compatibleBrandsCount; ++compatibleBrandIndex) {
2903 const uint8_t * compatibleBrand = &ftyp->compatibleBrands[4 * compatibleBrandIndex];
2904 if (!memcmp(compatibleBrand, brand, 4)) {
2905 return AVIF_TRUE;
Joe Drago7e37b972019-07-24 12:44:47 -07002906 }
2907 }
Joe Dragobb39aab2020-11-03 19:23:40 -08002908 return AVIF_FALSE;
2909}
2910
2911static avifBool avifFileTypeIsCompatible(avifFileType * ftyp)
2912{
2913 return avifFileTypeHasBrand(ftyp, "avif") || avifFileTypeHasBrand(ftyp, "avis");
Joe Drago7e37b972019-07-24 12:44:47 -07002914}
2915
Wan-Teh Change184dc12020-05-11 12:47:21 -07002916avifBool avifPeekCompatibleFileType(const avifROData * input)
Joe Drago7e37b972019-07-24 12:44:47 -07002917{
Joe Drago618ed5f2021-05-04 12:13:24 -07002918 BEGIN_STREAM(s, input->data, input->size, NULL, NULL);
Joe Drago7e37b972019-07-24 12:44:47 -07002919
2920 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002921 CHECK(avifROStreamReadBoxHeader(&s, &header));
wantehchangbc35a5f2020-08-12 15:27:39 -07002922 if (memcmp(header.type, "ftyp", 4)) {
Joe Drago7e37b972019-07-24 12:44:47 -07002923 return AVIF_FALSE;
2924 }
2925
2926 avifFileType ftyp;
2927 memset(&ftyp, 0, sizeof(avifFileType));
Joe Drago4d5f4a42021-04-28 13:11:45 -07002928 avifBool parsed = avifParseFileTypeBox(&ftyp, avifROStreamCurrent(&s), header.size, NULL);
Joe Drago7e37b972019-07-24 12:44:47 -07002929 if (!parsed) {
2930 return AVIF_FALSE;
2931 }
2932 return avifFileTypeIsCompatible(&ftyp);
2933}
2934
2935// ---------------------------------------------------------------------------
2936
Joe Drago0b05eee2019-06-12 13:24:39 -07002937avifDecoder * avifDecoderCreate(void)
2938{
2939 avifDecoder * decoder = (avifDecoder *)avifAlloc(sizeof(avifDecoder));
2940 memset(decoder, 0, sizeof(avifDecoder));
Joe Dragoede5c202020-11-11 09:42:57 -08002941 decoder->maxThreads = 1;
Wan-Teh Chang980d5852021-08-03 20:02:38 -07002942 decoder->imageSizeLimit = AVIF_DEFAULT_IMAGE_SIZE_LIMIT;
Joe Dragod2340b42021-03-14 13:20:02 -07002943 decoder->imageCountLimit = AVIF_DEFAULT_IMAGE_COUNT_LIMIT;
Joe Drago4d102ed2021-05-07 14:27:19 -07002944 decoder->strictFlags = AVIF_STRICT_ENABLED;
Joe Drago0b05eee2019-06-12 13:24:39 -07002945 return decoder;
2946}
2947
Joe Drago46ea0582019-07-22 15:55:47 -07002948static void avifDecoderCleanup(avifDecoder * decoder)
2949{
2950 if (decoder->data) {
Joe Drago800b47f2020-03-18 16:22:37 -07002951 avifDecoderDataDestroy(decoder->data);
Joe Drago46ea0582019-07-22 15:55:47 -07002952 decoder->data = NULL;
2953 }
2954
2955 if (decoder->image) {
2956 avifImageDestroy(decoder->image);
2957 decoder->image = NULL;
2958 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07002959 avifDiagnosticsClearError(&decoder->diag);
Joe Drago46ea0582019-07-22 15:55:47 -07002960}
2961
Joe Drago0b05eee2019-06-12 13:24:39 -07002962void avifDecoderDestroy(avifDecoder * decoder)
2963{
Joe Drago46ea0582019-07-22 15:55:47 -07002964 avifDecoderCleanup(decoder);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002965 avifIODestroy(decoder->io);
Joe Drago0b05eee2019-06-12 13:24:39 -07002966 avifFree(decoder);
2967}
2968
Joe Drago46ea0582019-07-22 15:55:47 -07002969avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source)
Joe Drago444f0512019-01-23 17:03:24 -08002970{
Joe Drago46ea0582019-07-22 15:55:47 -07002971 decoder->requestedSource = source;
2972 return avifDecoderReset(decoder);
2973}
Joe Drago33f1d362019-02-13 16:46:22 -08002974
Wan-Teh Change67f9362020-10-12 16:07:57 -07002975void avifDecoderSetIO(avifDecoder * decoder, avifIO * io)
Joe Drago46ea0582019-07-22 15:55:47 -07002976{
Joe Dragobe4cbb92020-09-21 12:14:05 -07002977 avifIODestroy(decoder->io);
2978 decoder->io = io;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002979}
2980
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002981avifResult avifDecoderSetIOMemory(avifDecoder * decoder, const uint8_t * data, size_t size)
Joe Dragobe4cbb92020-09-21 12:14:05 -07002982{
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002983 avifIO * io = avifIOCreateMemoryReader(data, size);
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002984 assert(io);
Wan-Teh Change67f9362020-10-12 16:07:57 -07002985 avifDecoderSetIO(decoder, io);
2986 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002987}
2988
Joe Dragobe4cbb92020-09-21 12:14:05 -07002989avifResult avifDecoderSetIOFile(avifDecoder * decoder, const char * filename)
2990{
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002991 avifIO * io = avifIOCreateFileReader(filename);
2992 if (!io) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002993 return AVIF_RESULT_IO_ERROR;
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002994 }
Wan-Teh Change67f9362020-10-12 16:07:57 -07002995 avifDecoderSetIO(decoder, io);
2996 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002997}
2998
Joe Drago4bcdfde2020-11-13 17:50:55 -08002999// 0-byte extents are ignored/overwritten during the merge, as they are the signal from helper
3000// functions that no extent was necessary for this given sample. If both provided extents are
3001// >0 bytes, this will set dst to be an extent that bounds both supplied extents.
Wan-Teh Changd69958e2020-11-17 12:14:27 -08003002static avifResult avifExtentMerge(avifExtent * dst, const avifExtent * src)
Joe Drago4bcdfde2020-11-13 17:50:55 -08003003{
3004 if (!dst->size) {
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003005 *dst = *src;
Wan-Teh Changd69958e2020-11-17 12:14:27 -08003006 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08003007 }
3008 if (!src->size) {
Wan-Teh Changd69958e2020-11-17 12:14:27 -08003009 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08003010 }
3011
3012 const uint64_t minExtent1 = dst->offset;
3013 const uint64_t maxExtent1 = dst->offset + dst->size;
3014 const uint64_t minExtent2 = src->offset;
3015 const uint64_t maxExtent2 = src->offset + src->size;
3016 dst->offset = AVIF_MIN(minExtent1, minExtent2);
Wan-Teh Changd69958e2020-11-17 12:14:27 -08003017 const uint64_t extentLength = AVIF_MAX(maxExtent1, maxExtent2) - dst->offset;
3018 if (extentLength > SIZE_MAX) {
3019 return AVIF_RESULT_BMFF_PARSE_FAILED;
3020 }
3021 dst->size = (size_t)extentLength;
3022 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08003023}
3024
Joe Drago93d5bf92020-11-17 14:31:57 -08003025avifResult avifDecoderNthImageMaxExtent(const avifDecoder * decoder, uint32_t frameIndex, avifExtent * outExtent)
Joe Drago4bcdfde2020-11-13 17:50:55 -08003026{
3027 if (!decoder->data) {
3028 // Nothing has been parsed yet
3029 return AVIF_RESULT_NO_CONTENT;
3030 }
3031
3032 memset(outExtent, 0, sizeof(avifExtent));
3033
Joe Drago93d5bf92020-11-17 14:31:57 -08003034 uint32_t startFrameIndex = avifDecoderNearestKeyframe(decoder, frameIndex);
Joe Drago4bcdfde2020-11-13 17:50:55 -08003035 uint32_t endFrameIndex = frameIndex;
3036 for (uint32_t currentFrameIndex = startFrameIndex; currentFrameIndex <= endFrameIndex; ++currentFrameIndex) {
3037 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
3038 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
3039 if (currentFrameIndex >= tile->input->samples.count) {
3040 return AVIF_RESULT_NO_IMAGES_REMAINING;
3041 }
3042
3043 avifDecodeSample * sample = &tile->input->samples.sample[currentFrameIndex];
3044 avifExtent sampleExtent;
3045 if (sample->itemID) {
3046 // The data comes from an item. Let avifDecoderItemMaxExtent() do the heavy lifting.
3047
3048 avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
Joe Dragoc0758eb2021-06-23 17:52:47 -07003049 avifResult maxExtentResult = avifDecoderItemMaxExtent(item, sample, &sampleExtent);
Joe Drago4bcdfde2020-11-13 17:50:55 -08003050 if (maxExtentResult != AVIF_RESULT_OK) {
3051 return maxExtentResult;
3052 }
3053 } else {
3054 // The data likely comes from a sample table. Use the sample position directly.
3055
3056 sampleExtent.offset = sample->offset;
3057 sampleExtent.size = sample->size;
3058 }
3059
3060 if (sampleExtent.size > UINT64_MAX - sampleExtent.offset) {
3061 return AVIF_RESULT_BMFF_PARSE_FAILED;
3062 }
3063
Wan-Teh Changd69958e2020-11-17 12:14:27 -08003064 avifResult extentMergeResult = avifExtentMerge(outExtent, &sampleExtent);
3065 if (extentMergeResult != AVIF_RESULT_OK) {
3066 return extentMergeResult;
3067 }
Joe Drago4bcdfde2020-11-13 17:50:55 -08003068 }
3069 }
3070 return AVIF_RESULT_OK;
3071}
3072
Joe Dragobe4cbb92020-09-21 12:14:05 -07003073static avifResult avifDecoderPrepareSample(avifDecoder * decoder, avifDecodeSample * sample, size_t partialByteCount)
3074{
3075 if (!sample->data.size || sample->partialData) {
3076 // This sample hasn't been read from IO or had its extents fully merged yet.
3077
Joe Dragobffba3b2021-05-26 15:46:10 -07003078 size_t bytesToRead = sample->size;
3079 if (partialByteCount && (bytesToRead > partialByteCount)) {
3080 bytesToRead = partialByteCount;
3081 }
3082
Joe Dragobe4cbb92020-09-21 12:14:05 -07003083 if (sample->itemID) {
Wan-Teh Chang4548b162020-11-06 11:48:25 -08003084 // The data comes from an item. Let avifDecoderItemRead() do the heavy lifting.
Joe Dragobe4cbb92020-09-21 12:14:05 -07003085
3086 avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
3087 avifROData itemContents;
Joe Dragobffba3b2021-05-26 15:46:10 -07003088 avifResult readResult = avifDecoderItemRead(item, decoder->io, &itemContents, sample->offset, bytesToRead, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003089 if (readResult != AVIF_RESULT_OK) {
3090 return readResult;
3091 }
3092
Wan-Teh Chang4548b162020-11-06 11:48:25 -08003093 // avifDecoderItemRead is guaranteed to already be persisted by either the underlying IO
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07003094 // or by mergedExtents; just reuse the buffer here.
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003095 sample->data = itemContents;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003096 sample->ownsData = AVIF_FALSE;
3097 sample->partialData = item->partialMergedExtents;
3098 } else {
3099 // The data likely comes from a sample table. Pull the sample and make a copy if necessary.
3100
Joe Dragobe4cbb92020-09-21 12:14:05 -07003101 avifROData sampleContents;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07003102 if ((decoder->io->sizeHint > 0) && (sample->offset > decoder->io->sizeHint)) {
3103 return AVIF_RESULT_BMFF_PARSE_FAILED;
3104 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07003105 avifResult readResult = decoder->io->read(decoder->io, 0, sample->offset, bytesToRead, &sampleContents);
3106 if (readResult != AVIF_RESULT_OK) {
3107 return readResult;
3108 }
3109 if (sampleContents.size != bytesToRead) {
3110 return AVIF_RESULT_TRUNCATED_DATA;
3111 }
3112
3113 sample->ownsData = !decoder->io->persistent;
3114 sample->partialData = (bytesToRead != sample->size);
3115 if (decoder->io->persistent) {
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003116 sample->data = sampleContents;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003117 } else {
3118 avifRWDataSet((avifRWData *)&sample->data, sampleContents.data, sampleContents.size);
3119 }
3120 }
3121 }
3122 return AVIF_RESULT_OK;
3123}
3124
3125avifResult avifDecoderParse(avifDecoder * decoder)
3126{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003127 avifDiagnosticsClearError(&decoder->diag);
3128
Wan-Teh Chang980d5852021-08-03 20:02:38 -07003129 // An imageSizeLimit greater than AVIF_DEFAULT_IMAGE_SIZE_LIMIT and the special value of 0 to
3130 // disable the limit are not yet implemented.
3131 if ((decoder->imageSizeLimit > AVIF_DEFAULT_IMAGE_SIZE_LIMIT) || (decoder->imageSizeLimit == 0)) {
3132 return AVIF_RESULT_NOT_IMPLEMENTED;
3133 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07003134 if (!decoder->io || !decoder->io->read) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07003135 return AVIF_RESULT_IO_NOT_SET;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003136 }
3137
Joe Drago46ea0582019-07-22 15:55:47 -07003138 // Cleanup anything lingering in the decoder
3139 avifDecoderCleanup(decoder);
3140
Joe Drago444f0512019-01-23 17:03:24 -08003141 // -----------------------------------------------------------------------
3142 // Parse BMFF boxes
3143
Joe Drago800b47f2020-03-18 16:22:37 -07003144 decoder->data = avifDecoderDataCreate();
Joe Drago4d5f4a42021-04-28 13:11:45 -07003145 decoder->data->diag = &decoder->diag;
Joe Drago46ea0582019-07-22 15:55:47 -07003146
Joe Dragobe4cbb92020-09-21 12:14:05 -07003147 avifResult parseResult = avifParse(decoder);
3148 if (parseResult != AVIF_RESULT_OK) {
3149 return parseResult;
Joe Drago444f0512019-01-23 17:03:24 -08003150 }
3151
Joe Drago46104d62021-05-27 18:17:29 -07003152 // Walk the decoded items (if any) and harvest ispe
3153 avifDecoderData * data = decoder->data;
3154 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
3155 avifDecoderItem * item = &data->meta->items.item[itemIndex];
3156 if (!item->size) {
3157 continue;
3158 }
3159 if (item->hasUnsupportedEssentialProperty) {
3160 // An essential property isn't supported by libavif; ignore the item.
3161 continue;
3162 }
3163 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
3164 if (memcmp(item->type, "av01", 4) && !isGrid) {
3165 // probably exif or some other data
3166 continue;
3167 }
3168
3169 const avifProperty * ispeProp = avifPropertyArrayFind(&item->properties, "ispe");
3170 if (ispeProp) {
3171 item->width = ispeProp->u.ispe.width;
3172 item->height = ispeProp->u.ispe.height;
3173
Wan-Teh Changbd1492e2021-08-06 16:08:14 -07003174 if ((item->width == 0) || (item->height == 0)) {
Joe Drago46104d62021-05-27 18:17:29 -07003175 avifDiagnosticsPrintf(data->diag, "Item ID [%u] has an invalid size [%ux%u]", item->id, item->width, item->height);
3176 return AVIF_RESULT_BMFF_PARSE_FAILED;
3177 }
Wan-Teh Changbd1492e2021-08-06 16:08:14 -07003178 if (item->width > (decoder->imageSizeLimit / item->height)) {
3179 avifDiagnosticsPrintf(data->diag, "Item ID [%u] size is too large [%ux%u]", item->id, item->width, item->height);
3180 return AVIF_RESULT_BMFF_PARSE_FAILED;
3181 }
Wan-Teh Chang2c1f6272021-09-13 17:13:13 -07003182 } else {
3183 const avifProperty * auxCProp = avifPropertyArrayFind(&item->properties, "auxC");
3184 if (auxCProp && isAlphaURN(auxCProp->u.auxC.auxType)) {
3185 if (decoder->strictFlags & AVIF_STRICT_ALPHA_ISPE_REQUIRED) {
3186 avifDiagnosticsPrintf(data->diag,
3187 "[Strict] Alpha auxiliary image item ID [%u] is missing a mandatory ispe property",
3188 item->id);
3189 return AVIF_RESULT_BMFF_PARSE_FAILED;
3190 }
3191 } else {
3192 avifDiagnosticsPrintf(data->diag, "Item ID [%u] is missing a mandatory ispe property", item->id);
3193 return AVIF_RESULT_BMFF_PARSE_FAILED;
3194 }
Joe Drago46104d62021-05-27 18:17:29 -07003195 }
3196 }
Joe Drago46ea0582019-07-22 15:55:47 -07003197 return avifDecoderReset(decoder);
3198}
3199
Joe Dragobe4cbb92020-09-21 12:14:05 -07003200static avifCodec * avifCodecCreateInternal(avifCodecChoice choice)
Joe Drago46ea0582019-07-22 15:55:47 -07003201{
Joe Dragobe4cbb92020-09-21 12:14:05 -07003202 return avifCodecCreate(choice, AVIF_CODEC_FLAG_CAN_DECODE);
Joe Drago46ea0582019-07-22 15:55:47 -07003203}
3204
Joe Drago22c1ad92019-09-26 12:46:50 -07003205static avifResult avifDecoderFlush(avifDecoder * decoder)
3206{
Joe Drago800b47f2020-03-18 16:22:37 -07003207 avifDecoderDataResetCodec(decoder->data);
Joe Drago22c1ad92019-09-26 12:46:50 -07003208
Joe Drago060d5342020-03-03 10:53:49 -08003209 for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) {
3210 avifTile * tile = &decoder->data->tiles.tile[i];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003211 tile->codec = avifCodecCreateInternal(decoder->codecChoice);
Joe Drago411221a2021-11-10 11:12:56 -08003212 if (tile->codec) {
3213 tile->codec->diag = &decoder->diag;
3214 tile->codec->operatingPoint = tile->operatingPoint;
3215 tile->codec->allLayers = tile->input->allLayers;
Joe Drago53355352019-10-28 19:04:51 -07003216 }
Joe Drago22c1ad92019-09-26 12:46:50 -07003217 }
3218 return AVIF_RESULT_OK;
3219}
3220
Joe Drago46ea0582019-07-22 15:55:47 -07003221avifResult avifDecoderReset(avifDecoder * decoder)
3222{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003223 avifDiagnosticsClearError(&decoder->diag);
3224
Joe Drago800b47f2020-03-18 16:22:37 -07003225 avifDecoderData * data = decoder->data;
Joe Drago46ea0582019-07-22 15:55:47 -07003226 if (!data) {
3227 // Nothing to reset.
3228 return AVIF_RESULT_OK;
3229 }
3230
Joe Drago060d5342020-03-03 10:53:49 -08003231 memset(&data->colorGrid, 0, sizeof(data->colorGrid));
3232 memset(&data->alphaGrid, 0, sizeof(data->alphaGrid));
Joe Drago800b47f2020-03-18 16:22:37 -07003233 avifDecoderDataClearTiles(data);
Joe Drago89f0cc82020-03-09 16:13:27 -07003234
3235 // Prepare / cleanup decoded image state
Joe Dragoa0da4a42020-05-08 14:27:40 -07003236 if (decoder->image) {
3237 avifImageDestroy(decoder->image);
Joe Drago8f7a3002019-02-07 19:35:37 -08003238 }
Joe Dragoa0da4a42020-05-08 14:27:40 -07003239 decoder->image = avifImageCreateEmpty();
Joe Dragobffba3b2021-05-26 15:46:10 -07003240 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_UNAVAILABLE;
Joe Dragoa0da4a42020-05-08 14:27:40 -07003241 data->cicpSet = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08003242
Joe Drago70cbf602019-07-24 15:30:55 -07003243 memset(&decoder->ioStats, 0, sizeof(decoder->ioStats));
3244
Joe Drago444f0512019-01-23 17:03:24 -08003245 // -----------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -07003246 // Build decode input
Joe Drago444f0512019-01-23 17:03:24 -08003247
Joe Drago46ea0582019-07-22 15:55:47 -07003248 data->sourceSampleTable = NULL; // Reset
3249 if (decoder->requestedSource == AVIF_DECODER_SOURCE_AUTO) {
Joe Dragof131b782021-09-21 16:52:39 -07003250 // Honor the major brand (avif or avis) if present, otherwise prefer avis (tracks) if possible.
3251 if (!memcmp(data->majorBrand, "avis", 4)) {
3252 data->source = AVIF_DECODER_SOURCE_TRACKS;
3253 } else if (!memcmp(data->majorBrand, "avif", 4)) {
3254 data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;
3255 } else if (data->tracks.count > 0) {
Joe Drago46ea0582019-07-22 15:55:47 -07003256 data->source = AVIF_DECODER_SOURCE_TRACKS;
3257 } else {
3258 data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;
Joe Drago76370232019-07-16 11:00:52 -07003259 }
Joe Drago46ea0582019-07-22 15:55:47 -07003260 } else {
3261 data->source = decoder->requestedSource;
Joe Drago76370232019-07-16 11:00:52 -07003262 }
3263
Joe Dragoa72da5b2020-06-15 19:40:17 -07003264 const avifPropertyArray * colorProperties = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07003265 if (data->source == AVIF_DECODER_SOURCE_TRACKS) {
3266 avifTrack * colorTrack = NULL;
3267 avifTrack * alphaTrack = NULL;
3268
3269 // Find primary track - this probably needs some better detection
3270 uint32_t colorTrackIndex = 0;
wantehchangb207b4d2020-08-11 17:50:22 -07003271 for (; colorTrackIndex < data->tracks.count; ++colorTrackIndex) {
3272 avifTrack * track = &data->tracks.track[colorTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07003273 if (!track->sampleTable) {
3274 continue;
3275 }
Joe Dragoba1eb492020-06-22 17:05:04 -07003276 if (!track->id) { // trak box might be missing a tkhd box inside, skip it
Joe Drago4a25c192020-06-03 16:29:58 -07003277 continue;
3278 }
Joe Drago46ea0582019-07-22 15:55:47 -07003279 if (!track->sampleTable->chunks.count) {
3280 continue;
3281 }
Joe Drago2c0924c2019-09-26 17:41:01 -07003282 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
3283 continue;
3284 }
Joe Drago46ea0582019-07-22 15:55:47 -07003285 if (track->auxForID != 0) {
3286 continue;
3287 }
3288
3289 // Found one!
Joe Drago444f0512019-01-23 17:03:24 -08003290 break;
3291 }
wantehchangb207b4d2020-08-11 17:50:22 -07003292 if (colorTrackIndex == data->tracks.count) {
Joe Drago618ed5f2021-05-04 12:13:24 -07003293 avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track");
Joe Drago46ea0582019-07-22 15:55:47 -07003294 return AVIF_RESULT_NO_CONTENT;
Joe Drago444f0512019-01-23 17:03:24 -08003295 }
wantehchangb207b4d2020-08-11 17:50:22 -07003296 colorTrack = &data->tracks.track[colorTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07003297
Joe Dragoa72da5b2020-06-15 19:40:17 -07003298 colorProperties = avifSampleTableGetProperties(colorTrack->sampleTable);
3299 if (!colorProperties) {
Joe Drago618ed5f2021-05-04 12:13:24 -07003300 avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track's color properties");
Joe Dragoa72da5b2020-06-15 19:40:17 -07003301 return AVIF_RESULT_BMFF_PARSE_FAILED;
3302 }
3303
3304 // Find Exif and/or XMP metadata, if any
3305 if (colorTrack->meta) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003306 // See the comment above avifDecoderFindMetadata() for the explanation of using 0 here
3307 avifResult findResult = avifDecoderFindMetadata(decoder, colorTrack->meta, decoder->image, 0);
3308 if (findResult != AVIF_RESULT_OK) {
3309 return findResult;
Joe Dragoa72da5b2020-06-15 19:40:17 -07003310 }
3311 }
3312
Joe Drago46ea0582019-07-22 15:55:47 -07003313 uint32_t alphaTrackIndex = 0;
wantehchangb207b4d2020-08-11 17:50:22 -07003314 for (; alphaTrackIndex < data->tracks.count; ++alphaTrackIndex) {
3315 avifTrack * track = &data->tracks.track[alphaTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07003316 if (!track->sampleTable) {
3317 continue;
3318 }
Joe Drago4a25c192020-06-03 16:29:58 -07003319 if (!track->id) {
3320 continue;
3321 }
Joe Drago46ea0582019-07-22 15:55:47 -07003322 if (!track->sampleTable->chunks.count) {
3323 continue;
3324 }
Joe Drago2c0924c2019-09-26 17:41:01 -07003325 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
3326 continue;
3327 }
Joe Drago46ea0582019-07-22 15:55:47 -07003328 if (track->auxForID == colorTrack->id) {
3329 // Found it!
3330 break;
3331 }
3332 }
wantehchangb207b4d2020-08-11 17:50:22 -07003333 if (alphaTrackIndex != data->tracks.count) {
3334 alphaTrack = &data->tracks.track[alphaTrackIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -08003335 }
Joe Drago444f0512019-01-23 17:03:24 -08003336
Joe Drago46104d62021-05-27 18:17:29 -07003337 avifTile * colorTile = avifDecoderDataCreateTile(data, colorTrack->width, colorTrack->height, 0); // No way to set operating point via tracks
Wan-Teh Changd8911872022-02-14 14:15:00 -08003338 if (!colorTile) {
3339 return AVIF_RESULT_OUT_OF_MEMORY;
3340 }
Joe Dragobffba3b2021-05-26 15:46:10 -07003341 if (!avifCodecDecodeInputFillFromSampleTable(colorTile->input,
3342 colorTrack->sampleTable,
3343 decoder->imageCountLimit,
3344 decoder->io->sizeHint,
3345 data->diag)) {
Joe Drago46ea0582019-07-22 15:55:47 -07003346 return AVIF_RESULT_BMFF_PARSE_FAILED;
3347 }
wantehchangb207b4d2020-08-11 17:50:22 -07003348 data->colorTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07003349
3350 if (alphaTrack) {
Joe Drago46104d62021-05-27 18:17:29 -07003351 avifTile * alphaTile = avifDecoderDataCreateTile(data, alphaTrack->width, alphaTrack->height, 0); // No way to set operating point via tracks
Wan-Teh Changd8911872022-02-14 14:15:00 -08003352 if (!alphaTile) {
3353 return AVIF_RESULT_OUT_OF_MEMORY;
3354 }
Joe Dragobffba3b2021-05-26 15:46:10 -07003355 if (!avifCodecDecodeInputFillFromSampleTable(alphaTile->input,
3356 alphaTrack->sampleTable,
3357 decoder->imageCountLimit,
3358 decoder->io->sizeHint,
3359 data->diag)) {
Joe Drago46ea0582019-07-22 15:55:47 -07003360 return AVIF_RESULT_BMFF_PARSE_FAILED;
3361 }
Joe Drago060d5342020-03-03 10:53:49 -08003362 alphaTile->input->alpha = AVIF_TRUE;
wantehchangb207b4d2020-08-11 17:50:22 -07003363 data->alphaTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07003364 }
3365
3366 // Stash off sample table for future timing information
3367 data->sourceSampleTable = colorTrack->sampleTable;
3368
3369 // Image sequence timing
3370 decoder->imageIndex = -1;
Joe Drago060d5342020-03-03 10:53:49 -08003371 decoder->imageCount = colorTile->input->samples.count;
Joe Drago46ea0582019-07-22 15:55:47 -07003372 decoder->timescale = colorTrack->mediaTimescale;
3373 decoder->durationInTimescales = colorTrack->mediaDuration;
3374 if (colorTrack->mediaTimescale) {
3375 decoder->duration = (double)decoder->durationInTimescales / (double)colorTrack->mediaTimescale;
3376 } else {
3377 decoder->duration = 0;
3378 }
3379 memset(&decoder->imageTiming, 0, sizeof(decoder->imageTiming)); // to be set in avifDecoderNextImage()
Joe Drago41700852019-09-26 17:01:43 -07003380
Joe Dragoc554f5f2020-06-09 18:59:51 -07003381 decoder->image->width = colorTrack->width;
3382 decoder->image->height = colorTrack->height;
Joe Dragoc554f5f2020-06-09 18:59:51 -07003383 decoder->alphaPresent = (alphaTrack != NULL);
Joe Dragoceb2fa02021-01-29 18:14:55 -08003384 decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorTrack->premByID == alphaTrack->id);
Joe Drago46ea0582019-07-22 15:55:47 -07003385 } else {
3386 // Create from items
3387
Joe Dragobe4cbb92020-09-21 12:14:05 -07003388 avifDecoderItem * colorItem = NULL;
3389 avifDecoderItem * alphaItem = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07003390
Joe Drago51d11142021-03-31 07:46:30 -07003391 if (data->meta->primaryItemID == 0) {
3392 // A primary item is required
Joe Drago618ed5f2021-05-04 12:13:24 -07003393 avifDiagnosticsPrintf(&decoder->diag, "Primary item not specified");
Joe Drago51d11142021-03-31 07:46:30 -07003394 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3395 }
3396
Joe Dragof6a42272019-11-21 15:21:41 -08003397 // Find the colorOBU (primary) item
Joe Drago9f2b87b2020-06-03 19:36:38 -07003398 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
3399 avifDecoderItem * item = &data->meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07003400 if (!item->size) {
3401 continue;
Joe Drago8f7a3002019-02-07 19:35:37 -08003402 }
Joe Drago3320e5f2020-04-21 17:36:27 -07003403 if (item->hasUnsupportedEssentialProperty) {
3404 // An essential property isn't supported by libavif; ignore the item.
3405 continue;
3406 }
Joe Drago951a0022020-03-09 16:19:44 -07003407 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08003408 if (memcmp(item->type, "av01", 4) && !isGrid) {
Joe Drago8f7a3002019-02-07 19:35:37 -08003409 // probably exif or some other data
3410 continue;
3411 }
3412 if (item->thumbnailForID != 0) {
3413 // It's a thumbnail, skip it
3414 continue;
3415 }
Joe Drago618ed5f2021-05-04 12:13:24 -07003416 if (item->id != data->meta->primaryItemID) {
3417 // This is not the primary item, skip it
Joe Dragof6a42272019-11-21 15:21:41 -08003418 continue;
3419 }
Joe Drago8f7a3002019-02-07 19:35:37 -08003420
Joe Drago060d5342020-03-03 10:53:49 -08003421 if (isGrid) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003422 avifROData readData;
Joe Dragobffba3b2021-05-26 15:46:10 -07003423 avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, 0, data->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003424 if (readResult != AVIF_RESULT_OK) {
3425 return readResult;
3426 }
Wan-Teh Chang980d5852021-08-03 20:02:38 -07003427 if (!avifParseImageGridBox(&data->colorGrid, readData.data, readData.size, decoder->imageSizeLimit, data->diag)) {
Joe Drago060d5342020-03-03 10:53:49 -08003428 return AVIF_RESULT_INVALID_IMAGE_GRID;
3429 }
Joe Drago060d5342020-03-03 10:53:49 -08003430 }
3431
Joe Dragobe4cbb92020-09-21 12:14:05 -07003432 colorItem = item;
Joe Drago46ea0582019-07-22 15:55:47 -07003433 break;
3434 }
3435
Joe Dragobe4cbb92020-09-21 12:14:05 -07003436 if (!colorItem) {
Joe Drago618ed5f2021-05-04 12:13:24 -07003437 avifDiagnosticsPrintf(&decoder->diag, "Primary item not found");
Joe Drago060d5342020-03-03 10:53:49 -08003438 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3439 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07003440 colorProperties = &colorItem->properties;
Joe Drago46ea0582019-07-22 15:55:47 -07003441
Joe Drago060d5342020-03-03 10:53:49 -08003442 // Find the alphaOBU item, if any
Joe Drago9f2b87b2020-06-03 19:36:38 -07003443 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
3444 avifDecoderItem * item = &data->meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07003445 if (!item->size) {
3446 continue;
Joe Drago060d5342020-03-03 10:53:49 -08003447 }
Joe Drago3320e5f2020-04-21 17:36:27 -07003448 if (item->hasUnsupportedEssentialProperty) {
3449 // An essential property isn't supported by libavif; ignore the item.
3450 continue;
3451 }
Joe Drago951a0022020-03-09 16:19:44 -07003452 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08003453 if (memcmp(item->type, "av01", 4) && !isGrid) {
3454 // probably exif or some other data
3455 continue;
3456 }
Joe Dragof6a42272019-11-21 15:21:41 -08003457
Joe Dragobe4cbb92020-09-21 12:14:05 -07003458 // Is this an alpha auxiliary item of whatever we chose for colorItem?
Joe Dragoa72da5b2020-06-15 19:40:17 -07003459 const avifProperty * auxCProp = avifPropertyArrayFind(&item->properties, "auxC");
Joe Dragobe4cbb92020-09-21 12:14:05 -07003460 if (auxCProp && isAlphaURN(auxCProp->u.auxC.auxType) && (item->auxForID == colorItem->id)) {
Joe Drago060d5342020-03-03 10:53:49 -08003461 if (isGrid) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003462 avifROData readData;
Joe Dragobffba3b2021-05-26 15:46:10 -07003463 avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, 0, data->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003464 if (readResult != AVIF_RESULT_OK) {
3465 return readResult;
3466 }
Wan-Teh Chang980d5852021-08-03 20:02:38 -07003467 if (!avifParseImageGridBox(&data->alphaGrid, readData.data, readData.size, decoder->imageSizeLimit, data->diag)) {
Joe Drago060d5342020-03-03 10:53:49 -08003468 return AVIF_RESULT_INVALID_IMAGE_GRID;
3469 }
Joe Dragof6a42272019-11-21 15:21:41 -08003470 }
3471
Joe Dragobe4cbb92020-09-21 12:14:05 -07003472 alphaItem = item;
Joe Drago060d5342020-03-03 10:53:49 -08003473 break;
Joe Dragof6a42272019-11-21 15:21:41 -08003474 }
Joe Drago444f0512019-01-23 17:03:24 -08003475 }
Joe Drago444f0512019-01-23 17:03:24 -08003476
Joe Drago060d5342020-03-03 10:53:49 -08003477 // Find Exif and/or XMP metadata, if any
Joe Dragobe4cbb92020-09-21 12:14:05 -07003478 avifResult findResult = avifDecoderFindMetadata(decoder, data->meta, decoder->image, colorItem->id);
3479 if (findResult != AVIF_RESULT_OK) {
3480 return findResult;
Joe Drago060d5342020-03-03 10:53:49 -08003481 }
3482
Joe Drago46ea0582019-07-22 15:55:47 -07003483 // Set all counts and timing to safe-but-uninteresting values
3484 decoder->imageIndex = -1;
3485 decoder->imageCount = 1;
3486 decoder->imageTiming.timescale = 1;
3487 decoder->imageTiming.pts = 0;
3488 decoder->imageTiming.ptsInTimescales = 0;
3489 decoder->imageTiming.duration = 1;
3490 decoder->imageTiming.durationInTimescales = 1;
3491 decoder->timescale = 1;
3492 decoder->duration = 1;
3493 decoder->durationInTimescales = 1;
Joe Drago70cbf602019-07-24 15:30:55 -07003494
Joe Dragobffba3b2021-05-26 15:46:10 -07003495 if ((data->colorGrid.rows > 0) && (data->colorGrid.columns > 0)) {
3496 if (!avifDecoderGenerateImageGridTiles(decoder, &data->colorGrid, colorItem, AVIF_FALSE)) {
3497 return AVIF_RESULT_INVALID_IMAGE_GRID;
3498 }
3499 data->colorTileCount = data->tiles.count;
3500 } else {
3501 if (colorItem->size == 0) {
3502 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3503 }
3504
Joe Drago46104d62021-05-27 18:17:29 -07003505 avifTile * colorTile =
3506 avifDecoderDataCreateTile(data, colorItem->width, colorItem->height, avifDecoderItemOperatingPoint(colorItem));
Wan-Teh Changd8911872022-02-14 14:15:00 -08003507 if (!colorTile) {
3508 return AVIF_RESULT_OUT_OF_MEMORY;
3509 }
Joe Dragobffba3b2021-05-26 15:46:10 -07003510 if (!avifCodecDecodeInputFillFromDecoderItem(colorTile->input,
3511 colorItem,
3512 decoder->allowProgressive,
3513 decoder->imageCountLimit,
3514 decoder->io->sizeHint,
3515 &decoder->diag)) {
Wan-Teh Changa8cd4412022-02-10 14:30:48 -08003516 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Dragobffba3b2021-05-26 15:46:10 -07003517 }
3518 data->colorTileCount = 1;
3519
3520 if (colorItem->progressive) {
3521 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_AVAILABLE;
3522 if (colorTile->input->samples.count > 1) {
3523 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_ACTIVE;
3524 decoder->imageCount = colorTile->input->samples.count;
3525 }
3526 }
3527 }
3528
3529 if (alphaItem) {
Joe Drago9c90c062021-09-01 13:34:42 -07003530 if (!alphaItem->width && !alphaItem->height) {
3531 // NON-STANDARD: Alpha subimage does not have an ispe property; adopt width/height from color item
Wan-Teh Chang2c1f6272021-09-13 17:13:13 -07003532 assert(!(decoder->strictFlags & AVIF_STRICT_ALPHA_ISPE_REQUIRED));
Joe Drago9c90c062021-09-01 13:34:42 -07003533 alphaItem->width = colorItem->width;
3534 alphaItem->height = colorItem->height;
3535 }
3536
Joe Dragobffba3b2021-05-26 15:46:10 -07003537 if ((data->alphaGrid.rows > 0) && (data->alphaGrid.columns > 0)) {
3538 if (!avifDecoderGenerateImageGridTiles(decoder, &data->alphaGrid, alphaItem, AVIF_TRUE)) {
3539 return AVIF_RESULT_INVALID_IMAGE_GRID;
3540 }
3541 data->alphaTileCount = data->tiles.count - data->colorTileCount;
3542 } else {
3543 if (alphaItem->size == 0) {
3544 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3545 }
3546
Joe Drago46104d62021-05-27 18:17:29 -07003547 avifTile * alphaTile =
3548 avifDecoderDataCreateTile(data, alphaItem->width, alphaItem->height, avifDecoderItemOperatingPoint(alphaItem));
Wan-Teh Changd8911872022-02-14 14:15:00 -08003549 if (!alphaTile) {
3550 return AVIF_RESULT_OUT_OF_MEMORY;
3551 }
Joe Dragobffba3b2021-05-26 15:46:10 -07003552 if (!avifCodecDecodeInputFillFromDecoderItem(alphaTile->input,
3553 alphaItem,
3554 decoder->allowProgressive,
3555 decoder->imageCountLimit,
3556 decoder->io->sizeHint,
3557 &decoder->diag)) {
Wan-Teh Changa8cd4412022-02-10 14:30:48 -08003558 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Dragobffba3b2021-05-26 15:46:10 -07003559 }
3560 alphaTile->input->alpha = AVIF_TRUE;
3561 data->alphaTileCount = 1;
3562 }
3563 }
3564
Joe Dragobe4cbb92020-09-21 12:14:05 -07003565 decoder->ioStats.colorOBUSize = colorItem->size;
3566 decoder->ioStats.alphaOBUSize = alphaItem ? alphaItem->size : 0;
Joe Drago41700852019-09-26 17:01:43 -07003567
Joe Drago46104d62021-05-27 18:17:29 -07003568 decoder->image->width = colorItem->width;
3569 decoder->image->height = colorItem->height;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003570 decoder->alphaPresent = (alphaItem != NULL);
Joe Dragoceb2fa02021-01-29 18:14:55 -08003571 decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorItem->premByID == alphaItem->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003572
Joe Drago9b942c22021-05-06 12:33:12 -07003573 avifResult colorItemValidationResult = avifDecoderItemValidateAV1(colorItem, &decoder->diag, decoder->strictFlags);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003574 if (colorItemValidationResult != AVIF_RESULT_OK) {
3575 return colorItemValidationResult;
3576 }
3577 if (alphaItem) {
Joe Drago9b942c22021-05-06 12:33:12 -07003578 avifResult alphaItemValidationResult = avifDecoderItemValidateAV1(alphaItem, &decoder->diag, decoder->strictFlags);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003579 if (alphaItemValidationResult != AVIF_RESULT_OK) {
3580 return alphaItemValidationResult;
3581 }
3582 }
Joe Drago00bcaaf2020-06-05 15:29:38 -07003583 }
3584
Joe Drago11f2a5e2020-07-06 10:49:00 -07003585 // Sanity check tiles
3586 for (uint32_t tileIndex = 0; tileIndex < data->tiles.count; ++tileIndex) {
3587 avifTile * tile = &data->tiles.tile[tileIndex];
3588 for (uint32_t sampleIndex = 0; sampleIndex < tile->input->samples.count; ++sampleIndex) {
Joe Drago043311b2020-07-06 16:48:41 -07003589 avifDecodeSample * sample = &tile->input->samples.sample[sampleIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003590 if (!sample->size) {
Joe Drago11f2a5e2020-07-06 10:49:00 -07003591 // Every sample must have some data
3592 return AVIF_RESULT_BMFF_PARSE_FAILED;
3593 }
3594 }
3595 }
3596
Joe Dragobf58fe72020-11-05 13:25:14 -08003597 // Find and adopt all colr boxes "at most one for a given value of colour type" (HEIF 6.5.5.1, from Amendment 3)
3598 // Accept one of each type, and bail out if more than one of a given type is provided.
3599 avifBool colrICCSeen = AVIF_FALSE;
3600 avifBool colrNCLXSeen = AVIF_FALSE;
3601 for (uint32_t propertyIndex = 0; propertyIndex < colorProperties->count; ++propertyIndex) {
3602 avifProperty * prop = &colorProperties->prop[propertyIndex];
3603
3604 if (!memcmp(prop->type, "colr", 4)) {
3605 if (prop->u.colr.hasICC) {
3606 if (colrICCSeen) {
3607 return AVIF_RESULT_BMFF_PARSE_FAILED;
3608 }
3609 colrICCSeen = AVIF_TRUE;
3610 avifImageSetProfileICC(decoder->image, prop->u.colr.icc, prop->u.colr.iccSize);
3611 }
3612 if (prop->u.colr.hasNCLX) {
3613 if (colrNCLXSeen) {
3614 return AVIF_RESULT_BMFF_PARSE_FAILED;
3615 }
3616 colrNCLXSeen = AVIF_TRUE;
3617 data->cicpSet = AVIF_TRUE;
3618 decoder->image->colorPrimaries = prop->u.colr.colorPrimaries;
3619 decoder->image->transferCharacteristics = prop->u.colr.transferCharacteristics;
3620 decoder->image->matrixCoefficients = prop->u.colr.matrixCoefficients;
3621 decoder->image->yuvRange = prop->u.colr.range;
3622 }
Joe Dragoa72da5b2020-06-15 19:40:17 -07003623 }
3624 }
3625
3626 // Transformations
3627 const avifProperty * paspProp = avifPropertyArrayFind(colorProperties, "pasp");
3628 if (paspProp) {
3629 decoder->image->transformFlags |= AVIF_TRANSFORM_PASP;
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003630 decoder->image->pasp = paspProp->u.pasp;
Joe Dragoa72da5b2020-06-15 19:40:17 -07003631 }
3632 const avifProperty * clapProp = avifPropertyArrayFind(colorProperties, "clap");
3633 if (clapProp) {
3634 decoder->image->transformFlags |= AVIF_TRANSFORM_CLAP;
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003635 decoder->image->clap = clapProp->u.clap;
Joe Dragoa72da5b2020-06-15 19:40:17 -07003636 }
3637 const avifProperty * irotProp = avifPropertyArrayFind(colorProperties, "irot");
3638 if (irotProp) {
3639 decoder->image->transformFlags |= AVIF_TRANSFORM_IROT;
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003640 decoder->image->irot = irotProp->u.irot;
Joe Dragoa72da5b2020-06-15 19:40:17 -07003641 }
3642 const avifProperty * imirProp = avifPropertyArrayFind(colorProperties, "imir");
3643 if (imirProp) {
3644 decoder->image->transformFlags |= AVIF_TRANSFORM_IMIR;
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003645 decoder->image->imir = imirProp->u.imir;
Joe Dragoa72da5b2020-06-15 19:40:17 -07003646 }
3647
wantehchangb207b4d2020-08-11 17:50:22 -07003648 if (!data->cicpSet && (data->tiles.count > 0)) {
Joe Dragoda92a382020-06-09 17:08:45 -07003649 avifTile * firstTile = &data->tiles.tile[0];
3650 if (firstTile->input->samples.count > 0) {
3651 avifDecodeSample * sample = &firstTile->input->samples.sample[0];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003652
3653 // Harvest CICP from the AV1's sequence header, which should be very close to the front
3654 // of the first sample. Read in successively larger chunks until we successfully parse the sequence.
3655 static const size_t searchSampleChunkIncrement = 64;
Wan-Teh Changac145712021-02-08 15:53:44 -08003656 static const size_t searchSampleSizeMax = 4096;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003657 size_t searchSampleSize = 0;
Wan-Teh Changc6c2fe82020-10-08 10:44:04 -07003658 do {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003659 searchSampleSize += searchSampleChunkIncrement;
3660 if (searchSampleSize > sample->size) {
3661 searchSampleSize = sample->size;
3662 }
3663
3664 avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, searchSampleSize);
3665 if (prepareResult != AVIF_RESULT_OK) {
3666 return prepareResult;
3667 }
3668
3669 avifSequenceHeader sequenceHeader;
3670 if (avifSequenceHeaderParse(&sequenceHeader, &sample->data)) {
3671 data->cicpSet = AVIF_TRUE;
3672 decoder->image->colorPrimaries = sequenceHeader.colorPrimaries;
3673 decoder->image->transferCharacteristics = sequenceHeader.transferCharacteristics;
3674 decoder->image->matrixCoefficients = sequenceHeader.matrixCoefficients;
3675 decoder->image->yuvRange = sequenceHeader.range;
3676 break;
3677 }
Wan-Teh Changac145712021-02-08 15:53:44 -08003678 } while (searchSampleSize != sample->size && searchSampleSize < searchSampleSizeMax);
Joe Dragoda92a382020-06-09 17:08:45 -07003679 }
3680 }
3681
Joe Dragoa72da5b2020-06-15 19:40:17 -07003682 const avifProperty * av1CProp = avifPropertyArrayFind(colorProperties, "av1C");
3683 if (av1CProp) {
Joe Dragob8401122020-06-19 11:45:49 -07003684 decoder->image->depth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
3685 if (av1CProp->u.av1C.monochrome) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003686 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
Joe Drago7b2cf802020-06-09 17:57:23 -07003687 } else {
Joe Dragob8401122020-06-19 11:45:49 -07003688 if (av1CProp->u.av1C.chromaSubsamplingX && av1CProp->u.av1C.chromaSubsamplingY) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003689 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV420;
Joe Dragob8401122020-06-19 11:45:49 -07003690 } else if (av1CProp->u.av1C.chromaSubsamplingX) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003691 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV422;
Joe Drago7b2cf802020-06-09 17:57:23 -07003692
3693 } else {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003694 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
Joe Drago7b2cf802020-06-09 17:57:23 -07003695 }
3696 }
Joe Dragob8401122020-06-19 11:45:49 -07003697 decoder->image->yuvChromaSamplePosition = (avifChromaSamplePosition)av1CProp->u.av1C.chromaSamplePosition;
Joe Drago00bcaaf2020-06-05 15:29:38 -07003698 } else {
Joe Dragof48a3382020-06-19 14:13:44 -07003699 // An av1C box is mandatory in all valid AVIF configurations. Bail out.
3700 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Drago46ea0582019-07-22 15:55:47 -07003701 }
3702
Joe Drago22c1ad92019-09-26 12:46:50 -07003703 return avifDecoderFlush(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07003704}
Joe Drago444f0512019-01-23 17:03:24 -08003705
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003706static avifResult avifDecoderPrepareTiles(avifDecoder * decoder,
3707 uint32_t nextImageIndex,
3708 unsigned int firstTileIndex,
3709 unsigned int tileCount,
3710 unsigned int decodedTileCount)
Joe Drago46ea0582019-07-22 15:55:47 -07003711{
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003712 for (unsigned int tileIndex = decodedTileCount; tileIndex < tileCount; ++tileIndex) {
3713 avifTile * tile = &decoder->data->tiles.tile[firstTileIndex + tileIndex];
Joe Drago411221a2021-11-10 11:12:56 -08003714
3715 // Ensure there's an AV1 codec available before doing anything else
3716 if (!tile->codec) {
3717 return AVIF_RESULT_NO_CODEC_AVAILABLE;
3718 }
3719
Joe Drago5998f592020-11-13 15:38:20 -08003720 if (nextImageIndex >= tile->input->samples.count) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003721 return AVIF_RESULT_NO_IMAGES_REMAINING;
3722 }
3723
Joe Drago5998f592020-11-13 15:38:20 -08003724 avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003725 avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, 0);
3726 if (prepareResult != AVIF_RESULT_OK) {
3727 return prepareResult;
3728 }
3729 }
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003730 return AVIF_RESULT_OK;
3731}
Joe Dragobe4cbb92020-09-21 12:14:05 -07003732
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003733static avifResult avifDecoderDecodeTiles(avifDecoder * decoder,
3734 uint32_t nextImageIndex,
3735 unsigned int firstTileIndex,
3736 unsigned int tileCount,
Wan-Teh Changa7743c82022-03-24 16:30:47 -07003737 unsigned int * decodedTileCount)
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003738{
Wan-Teh Changa7743c82022-03-24 16:30:47 -07003739 const unsigned int oldDecodedTileCount = *decodedTileCount;
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003740 for (unsigned int tileIndex = oldDecodedTileCount; tileIndex < tileCount; ++tileIndex) {
3741 avifTile * tile = &decoder->data->tiles.tile[firstTileIndex + tileIndex];
Joe Drago41eb62b2019-02-08 15:38:18 -08003742
Wan-Teh Changb4977b32020-10-05 18:04:54 -07003743 const avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex];
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003744 if (!sample->data.data) {
3745 assert(decoder->allowIncremental);
3746 // Data is missing but there is no error yet. Output available pixel rows.
3747 return AVIF_RESULT_OK;
3748 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07003749
Joe Drago405e8722021-05-25 15:08:00 -07003750 if (!tile->codec->getNextImage(tile->codec, decoder, sample, tile->input->alpha, tile->image)) {
Wan-Teh Chang9b68bf62021-06-23 15:46:37 -07003751 avifDiagnosticsPrintf(&decoder->diag, "tile->codec->getNextImage() failed");
Wan-Teh Chang365bd5e2021-03-23 18:28:40 -07003752 return tile->input->alpha ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED;
Joe Drago46ea0582019-07-22 15:55:47 -07003753 }
Joe Drago46104d62021-05-27 18:17:29 -07003754
3755 // Scale the decoded image so that it corresponds to this tile's output dimensions
3756 if ((tile->width != tile->image->width) || (tile->height != tile->image->height)) {
Wan-Teh Chang980d5852021-08-03 20:02:38 -07003757 if (!avifImageScale(tile->image, tile->width, tile->height, decoder->imageSizeLimit, &decoder->diag)) {
Wan-Teh Chang9b68bf62021-06-23 15:46:37 -07003758 avifDiagnosticsPrintf(&decoder->diag, "avifImageScale() failed");
Joe Drago46104d62021-05-27 18:17:29 -07003759 return tile->input->alpha ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED;
3760 }
3761 }
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003762
3763 ++*decodedTileCount;
3764 }
3765 return AVIF_RESULT_OK;
3766}
3767
3768avifResult avifDecoderNextImage(avifDecoder * decoder)
3769{
3770 avifDiagnosticsClearError(&decoder->diag);
3771
3772 if (!decoder->data) {
3773 // Nothing has been parsed yet
3774 return AVIF_RESULT_NO_CONTENT;
3775 }
3776
3777 if (!decoder->io || !decoder->io->read) {
3778 return AVIF_RESULT_IO_NOT_SET;
3779 }
3780
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07003781 if ((decoder->data->decodedColorTileCount == decoder->data->colorTileCount) &&
3782 (decoder->data->decodedAlphaTileCount == decoder->data->alphaTileCount)) {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003783 // A frame was decoded during the last avifDecoderNextImage() call.
3784 decoder->data->decodedColorTileCount = 0;
3785 decoder->data->decodedAlphaTileCount = 0;
Joe Drago060d5342020-03-03 10:53:49 -08003786 }
3787
3788 if (decoder->data->tiles.count != (decoder->data->colorTileCount + decoder->data->alphaTileCount)) {
3789 // TODO: assert here? This should be impossible.
3790 return AVIF_RESULT_UNKNOWN_ERROR;
3791 }
3792
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003793 const uint32_t nextImageIndex = (uint32_t)(decoder->imageIndex + 1);
3794 const unsigned int firstColorTileIndex = 0;
3795 const unsigned int firstAlphaTileIndex = decoder->data->colorTileCount;
Joe Drago060d5342020-03-03 10:53:49 -08003796
Wan-Teh Changa7743c82022-03-24 16:30:47 -07003797 // Acquire all sample data for the current image first, allowing for any read call to bail out
3798 // with AVIF_RESULT_WAITING_ON_IO harmlessly / idempotently, unless decoder->allowIncremental.
3799 // Start with color tiles.
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003800 const avifResult prepareColorTileResult =
3801 avifDecoderPrepareTiles(decoder, nextImageIndex, firstColorTileIndex, decoder->data->colorTileCount, decoder->data->decodedColorTileCount);
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07003802 if ((prepareColorTileResult != AVIF_RESULT_OK) &&
3803 (!decoder->allowIncremental || (prepareColorTileResult != AVIF_RESULT_WAITING_ON_IO))) {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003804 return prepareColorTileResult;
3805 }
3806 // Do the same with alpha tiles. They are handled separately because their
3807 // order of appearance relative to the color tiles in the bitstream is left
3808 // to the encoder's choice, and decoding as many as possible of each
3809 // category in parallel is beneficial for incremental decoding, as pixel
3810 // rows need all channels to be decoded before being accessible to the user.
3811 const avifResult prepareAlphaTileResult =
3812 avifDecoderPrepareTiles(decoder, nextImageIndex, firstAlphaTileIndex, decoder->data->alphaTileCount, decoder->data->decodedAlphaTileCount);
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07003813 if ((prepareAlphaTileResult != AVIF_RESULT_OK) &&
3814 (!decoder->allowIncremental || (prepareAlphaTileResult != AVIF_RESULT_WAITING_ON_IO))) {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003815 return prepareAlphaTileResult;
Joe Drago060d5342020-03-03 10:53:49 -08003816 }
3817
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003818 // Decode all available color tiles now, then all available alpha tiles.
3819 const unsigned int oldDecodedColorTileCount = decoder->data->decodedColorTileCount;
3820 const avifResult decodeColorTileResult =
3821 avifDecoderDecodeTiles(decoder, nextImageIndex, firstColorTileIndex, decoder->data->colorTileCount, &decoder->data->decodedColorTileCount);
3822 if (decodeColorTileResult != AVIF_RESULT_OK) {
3823 return decodeColorTileResult;
3824 }
3825 const unsigned int oldDecodedAlphaTileCount = decoder->data->decodedAlphaTileCount;
3826 const avifResult decodeAlphaTileResult =
3827 avifDecoderDecodeTiles(decoder, nextImageIndex, firstAlphaTileIndex, decoder->data->alphaTileCount, &decoder->data->decodedAlphaTileCount);
3828 if (decodeAlphaTileResult != AVIF_RESULT_OK) {
3829 return decodeAlphaTileResult;
3830 }
Joe Drago060d5342020-03-03 10:53:49 -08003831
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003832 if (decoder->data->decodedColorTileCount > oldDecodedColorTileCount) {
3833 // There is at least one newly decoded color tile.
3834 if ((decoder->data->colorGrid.rows > 0) && (decoder->data->colorGrid.columns > 0)) {
3835 if (!avifDecoderDataFillImageGrid(decoder->data,
3836 &decoder->data->colorGrid,
3837 decoder->image,
3838 firstColorTileIndex,
3839 oldDecodedColorTileCount,
3840 decoder->data->decodedColorTileCount,
3841 AVIF_FALSE)) {
3842 return AVIF_RESULT_INVALID_IMAGE_GRID;
3843 }
Joe Drago060d5342020-03-03 10:53:49 -08003844 } else {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003845 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
3846
3847 if (decoder->data->colorTileCount != 1) {
3848 avifDiagnosticsPrintf(&decoder->diag, "decoder->data->colorTileCount should be 1 but is %u", decoder->data->colorTileCount);
3849 return AVIF_RESULT_DECODE_COLOR_FAILED;
3850 }
3851
3852 avifImage * srcColor = decoder->data->tiles.tile[0].image;
3853
3854 if ((decoder->image->width != srcColor->width) || (decoder->image->height != srcColor->height) ||
3855 (decoder->image->depth != srcColor->depth)) {
3856 avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL);
3857
3858 decoder->image->width = srcColor->width;
3859 decoder->image->height = srcColor->height;
3860 decoder->image->depth = srcColor->depth;
3861 }
3862
3863#if 0
3864 // This code is currently unnecessary as the CICP is always set by the end of avifDecoderParse().
3865 if (!decoder->data->cicpSet) {
3866 decoder->data->cicpSet = AVIF_TRUE;
3867 decoder->image->colorPrimaries = srcColor->colorPrimaries;
3868 decoder->image->transferCharacteristics = srcColor->transferCharacteristics;
3869 decoder->image->matrixCoefficients = srcColor->matrixCoefficients;
3870 }
3871#endif
3872
3873 avifImageStealPlanes(decoder->image, srcColor, AVIF_PLANES_YUV);
3874 }
3875 }
3876
3877 if (decoder->data->alphaTileCount == 0) {
3878 avifImageFreePlanes(decoder->image, AVIF_PLANES_A); // no alpha
3879 } else if (decoder->data->decodedAlphaTileCount > oldDecodedAlphaTileCount) {
3880 // There is at least one newly decoded alpha tile.
3881 if ((decoder->data->alphaGrid.rows > 0) && (decoder->data->alphaGrid.columns > 0)) {
3882 if (!avifDecoderDataFillImageGrid(decoder->data,
3883 &decoder->data->alphaGrid,
3884 decoder->image,
3885 firstAlphaTileIndex,
3886 oldDecodedAlphaTileCount,
3887 decoder->data->decodedAlphaTileCount,
3888 AVIF_TRUE)) {
3889 return AVIF_RESULT_INVALID_IMAGE_GRID;
3890 }
3891 } else {
3892 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
Joe Drago060d5342020-03-03 10:53:49 -08003893 if (decoder->data->alphaTileCount != 1) {
Wan-Teh Chang9b68bf62021-06-23 15:46:37 -07003894 avifDiagnosticsPrintf(&decoder->diag, "decoder->data->alphaTileCount should be 1 but is %u", decoder->data->alphaTileCount);
Joe Drago060d5342020-03-03 10:53:49 -08003895 return AVIF_RESULT_DECODE_ALPHA_FAILED;
3896 }
3897
3898 avifImage * srcAlpha = decoder->data->tiles.tile[decoder->data->colorTileCount].image;
3899 if ((decoder->image->width != srcAlpha->width) || (decoder->image->height != srcAlpha->height) ||
3900 (decoder->image->depth != srcAlpha->depth)) {
Wan-Teh Chang9b68bf62021-06-23 15:46:37 -07003901 avifDiagnosticsPrintf(&decoder->diag, "decoder->image does not match srcAlpha in width, height, or bit depth");
Joe Drago060d5342020-03-03 10:53:49 -08003902 return AVIF_RESULT_DECODE_ALPHA_FAILED;
3903 }
3904
3905 avifImageStealPlanes(decoder->image, srcAlpha, AVIF_PLANES_A);
Joe Drago3fd2db22020-08-13 16:07:24 -07003906 decoder->image->alphaRange = srcAlpha->alphaRange;
Joe Drago060d5342020-03-03 10:53:49 -08003907 }
3908 }
Joe Drago7ad3ad62019-02-07 11:17:34 -08003909
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003910 if (avifDecoderDecodedRowCount(decoder) < decoder->image->height) {
3911 assert(decoder->allowIncremental);
3912 // Rows are missing. There should be no error unrelated to missing bytes, and at least some missing bytes.
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07003913 assert((prepareColorTileResult == AVIF_RESULT_OK) || (prepareColorTileResult == AVIF_RESULT_WAITING_ON_IO));
3914 assert((prepareAlphaTileResult == AVIF_RESULT_OK) || (prepareAlphaTileResult == AVIF_RESULT_WAITING_ON_IO));
3915 assert((prepareColorTileResult != AVIF_RESULT_OK) || (prepareAlphaTileResult != AVIF_RESULT_OK));
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003916 // Return the "not enough bytes" status now instead of moving on to the next frame.
3917 return AVIF_RESULT_WAITING_ON_IO;
3918 }
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07003919 assert((prepareColorTileResult == AVIF_RESULT_OK) && (prepareAlphaTileResult == AVIF_RESULT_OK));
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003920
3921 // Only advance decoder->imageIndex once the image is completely decoded, so that
3922 // avifDecoderNthImage(decoder, decoder->imageIndex + 1) is equivalent to avifDecoderNextImage(decoder)
3923 // if the previous call to avifDecoderNextImage() returned AVIF_RESULT_WAITING_ON_IO.
Joe Dragobe4cbb92020-09-21 12:14:05 -07003924 decoder->imageIndex = nextImageIndex;
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003925 // The decoded tile counts will be reset to 0 the next time avifDecoderNextImage() is called,
3926 // for avifDecoderDecodedRowCount() to work until then.
3927 assert(decoder->data->decodedColorTileCount == decoder->data->colorTileCount);
3928 assert(decoder->data->decodedAlphaTileCount == decoder->data->alphaTileCount);
Joe Drago46ea0582019-07-22 15:55:47 -07003929 if (decoder->data->sourceSampleTable) {
3930 // Decoding from a track! Provide timing information.
Joe Drago05559c92019-07-17 16:33:38 -07003931
Joe Dragoe9c58602020-04-13 17:23:13 -07003932 avifResult timingResult = avifDecoderNthImageTiming(decoder, decoder->imageIndex, &decoder->imageTiming);
3933 if (timingResult != AVIF_RESULT_OK) {
3934 return timingResult;
Joe Drago22c1ad92019-09-26 12:46:50 -07003935 }
Joe Dragoe9c58602020-04-13 17:23:13 -07003936 }
3937 return AVIF_RESULT_OK;
3938}
Joe Drago46ea0582019-07-22 15:55:47 -07003939
Wan-Teh Change184dc12020-05-11 12:47:21 -07003940avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming)
Joe Dragoe9c58602020-04-13 17:23:13 -07003941{
3942 if (!decoder->data) {
3943 // Nothing has been parsed yet
3944 return AVIF_RESULT_NO_CONTENT;
3945 }
3946
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07003947 if ((frameIndex > INT_MAX) || ((int)frameIndex >= decoder->imageCount)) {
Joe Dragoe9c58602020-04-13 17:23:13 -07003948 // Impossible index
3949 return AVIF_RESULT_NO_IMAGES_REMAINING;
3950 }
3951
3952 if (!decoder->data->sourceSampleTable) {
3953 // There isn't any real timing associated with this decode, so
3954 // just hand back the defaults chosen in avifDecoderReset().
Wan-Teh Chang2f225b02022-03-16 22:30:38 -07003955 *outTiming = decoder->imageTiming;
Joe Dragoe9c58602020-04-13 17:23:13 -07003956 return AVIF_RESULT_OK;
3957 }
3958
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003959 outTiming->timescale = decoder->timescale;
3960 outTiming->ptsInTimescales = 0;
Joe Dragoe9c58602020-04-13 17:23:13 -07003961 for (int imageIndex = 0; imageIndex < (int)frameIndex; ++imageIndex) {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003962 outTiming->ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07003963 }
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003964 outTiming->durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, frameIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07003965
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003966 if (outTiming->timescale > 0) {
3967 outTiming->pts = (double)outTiming->ptsInTimescales / (double)outTiming->timescale;
3968 outTiming->duration = (double)outTiming->durationInTimescales / (double)outTiming->timescale;
Joe Dragoe9c58602020-04-13 17:23:13 -07003969 } else {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003970 outTiming->pts = 0.0;
3971 outTiming->duration = 0.0;
Joe Drago444f0512019-01-23 17:03:24 -08003972 }
Joe Drago46ea0582019-07-22 15:55:47 -07003973 return AVIF_RESULT_OK;
3974}
3975
Joe Drago22c1ad92019-09-26 12:46:50 -07003976avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex)
3977{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003978 avifDiagnosticsClearError(&decoder->diag);
3979
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003980 if (!decoder->data) {
3981 // Nothing has been parsed yet
3982 return AVIF_RESULT_NO_CONTENT;
3983 }
3984
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07003985 if (frameIndex > INT_MAX) {
3986 // Impossible index
3987 return AVIF_RESULT_NO_IMAGES_REMAINING;
3988 }
3989
Joe Drago22c1ad92019-09-26 12:46:50 -07003990 int requestedIndex = (int)frameIndex;
3991 if (requestedIndex == decoder->imageIndex) {
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07003992 if ((decoder->data->decodedColorTileCount == decoder->data->colorTileCount) &&
3993 (decoder->data->decodedAlphaTileCount == decoder->data->alphaTileCount)) {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00003994 // The current fully decoded image (decoder->imageIndex) is requested, nothing to do
3995 return AVIF_RESULT_OK;
3996 }
3997 // The next image (decoder->imageIndex + 1) is partially decoded but
3998 // the previous image (decoder->imageIndex) is requested.
3999 // Fall through to flush and start decoding from the nearest key frame.
Joe Drago22c1ad92019-09-26 12:46:50 -07004000 }
4001
4002 if (requestedIndex == (decoder->imageIndex + 1)) {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00004003 // It's just the next image (already partially decoded or not at all), nothing special here
Joe Drago22c1ad92019-09-26 12:46:50 -07004004 return avifDecoderNextImage(decoder);
4005 }
4006
4007 if (requestedIndex >= decoder->imageCount) {
4008 // Impossible index
4009 return AVIF_RESULT_NO_IMAGES_REMAINING;
4010 }
4011
Wan-Teh Chang722fb7e2020-11-06 12:39:58 -08004012 int nearestKeyFrame = (int)avifDecoderNearestKeyframe(decoder, frameIndex);
Yannis Guyond3e85ff2022-03-17 05:10:29 +00004013 if ((nearestKeyFrame > (decoder->imageIndex + 1)) || (requestedIndex <= decoder->imageIndex)) {
Wan-Teh Chang722fb7e2020-11-06 12:39:58 -08004014 // If we get here, a decoder flush is necessary
4015 decoder->imageIndex = nearestKeyFrame - 1; // prepare to read nearest keyframe
4016 avifDecoderFlush(decoder);
4017 }
Joe Drago22c1ad92019-09-26 12:46:50 -07004018 for (;;) {
4019 avifResult result = avifDecoderNextImage(decoder);
4020 if (result != AVIF_RESULT_OK) {
4021 return result;
4022 }
4023
4024 if (requestedIndex == decoder->imageIndex) {
4025 break;
4026 }
Joe Dragofc4144e2019-09-27 20:35:06 -07004027 }
Joe Drago22c1ad92019-09-26 12:46:50 -07004028 return AVIF_RESULT_OK;
4029}
4030
Wan-Teh Change184dc12020-05-11 12:47:21 -07004031avifBool avifDecoderIsKeyframe(const avifDecoder * decoder, uint32_t frameIndex)
Joe Drago22c1ad92019-09-26 12:46:50 -07004032{
Joe Drago91bc33a2022-02-08 17:02:30 -08004033 if (!decoder->data || (decoder->data->tiles.count == 0)) {
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08004034 // Nothing has been parsed yet
4035 return AVIF_FALSE;
4036 }
4037
Joe Drago91bc33a2022-02-08 17:02:30 -08004038 // *All* tiles for the requested frameIndex must be keyframes in order for
4039 // avifDecoderIsKeyframe() to return true, otherwise we may seek to a frame in which the color
4040 // planes are a keyframe but the alpha plane isn't a keyframe, which will cause an alpha plane
4041 // decode failure.
4042 for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) {
4043 const avifTile * tile = &decoder->data->tiles.tile[i];
4044 if ((frameIndex >= tile->input->samples.count) || !tile->input->samples.sample[frameIndex].sync) {
4045 return AVIF_FALSE;
Joe Drago22c1ad92019-09-26 12:46:50 -07004046 }
4047 }
Joe Drago91bc33a2022-02-08 17:02:30 -08004048 return AVIF_TRUE;
Joe Drago22c1ad92019-09-26 12:46:50 -07004049}
4050
Wan-Teh Change184dc12020-05-11 12:47:21 -07004051uint32_t avifDecoderNearestKeyframe(const avifDecoder * decoder, uint32_t frameIndex)
Joe Drago22c1ad92019-09-26 12:46:50 -07004052{
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08004053 if (!decoder->data) {
4054 // Nothing has been parsed yet
4055 return 0;
4056 }
4057
Joe Drago22c1ad92019-09-26 12:46:50 -07004058 for (; frameIndex != 0; --frameIndex) {
4059 if (avifDecoderIsKeyframe(decoder, frameIndex)) {
4060 break;
4061 }
4062 }
4063 return frameIndex;
4064}
4065
Yannis Guyond3e85ff2022-03-17 05:10:29 +00004066// Returns the number of available rows in decoder->image given a color or alpha subimage.
Wan-Teh Changca728832022-03-16 22:38:51 -07004067static uint32_t avifGetDecodedRowCount(const avifDecoder * decoder,
4068 const avifImageGrid * grid,
Wan-Teh Changa7743c82022-03-24 16:30:47 -07004069 unsigned int firstTileIndex,
4070 unsigned int tileCount,
4071 unsigned int decodedTileCount)
Yannis Guyond3e85ff2022-03-17 05:10:29 +00004072{
4073 if (decodedTileCount == tileCount) {
4074 return decoder->image->height;
4075 }
4076 if (decodedTileCount == 0) {
4077 return 0;
4078 }
4079
Wan-Teh Changadfa25b2022-03-17 15:33:18 -07004080 if ((grid->rows > 0) && (grid->columns > 0)) {
Yannis Guyond3e85ff2022-03-17 05:10:29 +00004081 // Grid of AVIF tiles (not to be confused with AV1 tiles).
4082 const uint32_t tileHeight = decoder->data->tiles.tile[firstTileIndex].height;
4083 return AVIF_MIN((decodedTileCount / grid->columns) * tileHeight, decoder->image->height);
4084 } else {
4085 // Non-grid image.
4086 return decoder->image->height;
4087 }
4088}
4089
4090uint32_t avifDecoderDecodedRowCount(const avifDecoder * decoder)
4091{
Wan-Teh Changca728832022-03-16 22:38:51 -07004092 const uint32_t colorRowCount = avifGetDecodedRowCount(decoder,
4093 &decoder->data->colorGrid,
4094 /*firstTileIndex=*/0,
4095 decoder->data->colorTileCount,
4096 decoder->data->decodedColorTileCount);
Yannis Guyond3e85ff2022-03-17 05:10:29 +00004097 const uint32_t alphaRowCount = avifGetDecodedRowCount(decoder,
4098 &decoder->data->alphaGrid,
4099 /*firstTileIndex=*/decoder->data->colorTileCount,
4100 decoder->data->alphaTileCount,
4101 decoder->data->decodedAlphaTileCount);
4102 return AVIF_MIN(colorRowCount, alphaRowCount);
4103}
4104
Joe Dragobe4cbb92020-09-21 12:14:05 -07004105avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image)
Joe Drago46ea0582019-07-22 15:55:47 -07004106{
Joe Dragobe4cbb92020-09-21 12:14:05 -07004107 avifResult result = avifDecoderParse(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07004108 if (result != AVIF_RESULT_OK) {
4109 return result;
Joe Drago05559c92019-07-17 16:33:38 -07004110 }
Joe Drago46ea0582019-07-22 15:55:47 -07004111 result = avifDecoderNextImage(decoder);
4112 if (result != AVIF_RESULT_OK) {
4113 return result;
4114 }
Joe Drago250221a2020-06-01 11:11:06 -07004115 avifImageCopy(image, decoder->image, AVIF_PLANES_ALL);
Joe Drago46ea0582019-07-22 15:55:47 -07004116 return AVIF_RESULT_OK;
Joe Drago444f0512019-01-23 17:03:24 -08004117}
Joe Dragobe4cbb92020-09-21 12:14:05 -07004118
Wan-Teh Chang7de44452020-10-08 11:43:18 -07004119avifResult avifDecoderReadMemory(avifDecoder * decoder, avifImage * image, const uint8_t * data, size_t size)
Joe Dragobe4cbb92020-09-21 12:14:05 -07004120{
Joe Drago4d5f4a42021-04-28 13:11:45 -07004121 avifDiagnosticsClearError(&decoder->diag);
Wan-Teh Chang7de44452020-10-08 11:43:18 -07004122 avifResult result = avifDecoderSetIOMemory(decoder, data, size);
Joe Dragobe4cbb92020-09-21 12:14:05 -07004123 if (result != AVIF_RESULT_OK) {
4124 return result;
4125 }
4126 return avifDecoderRead(decoder, image);
4127}
4128
4129avifResult avifDecoderReadFile(avifDecoder * decoder, avifImage * image, const char * filename)
4130{
Joe Drago4d5f4a42021-04-28 13:11:45 -07004131 avifDiagnosticsClearError(&decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07004132 avifResult result = avifDecoderSetIOFile(decoder, filename);
4133 if (result != AVIF_RESULT_OK) {
4134 return result;
4135 }
4136 return avifDecoderRead(decoder, image);
4137}