blob: 5426fc2fb8091dfde9952fcb1b41f30b38d6da96 [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 Dragob13e5722019-02-08 19:07:25 -080040// ---------------------------------------------------------------------------
41// Box data structures
42
43// ftyp
44typedef struct avifFileType
45{
46 uint8_t majorBrand[4];
47 uint32_t minorVersion;
Wan-Teh Chang6da0a882020-07-01 12:19:31 -070048 // If not null, points to a memory block of 4 * compatibleBrandsCount bytes.
49 const uint8_t * compatibleBrands;
Joe Dragob13e5722019-02-08 19:07:25 -080050 int compatibleBrandsCount;
51} avifFileType;
52
53// ispe
Joe Drago8f7a3002019-02-07 19:35:37 -080054typedef struct avifImageSpatialExtents
55{
56 uint32_t width;
57 uint32_t height;
58} avifImageSpatialExtents;
Joe Drago444f0512019-01-23 17:03:24 -080059
Joe Dragob13e5722019-02-08 19:07:25 -080060// auxC
Joe Dragocd1e4c32019-02-08 11:26:31 -080061typedef struct avifAuxiliaryType
62{
63 char auxType[AUXTYPE_SIZE];
64} avifAuxiliaryType;
65
Joe Dragof6a42272019-11-21 15:21:41 -080066// infe mime content_type
67typedef struct avifContentType
68{
69 char contentType[CONTENTTYPE_SIZE];
70} avifContentType;
71
Joe Dragob13e5722019-02-08 19:07:25 -080072// colr
Joe Drago41eb62b2019-02-08 15:38:18 -080073typedef struct avifColourInformationBox
74{
Joe Dragoa0da4a42020-05-08 14:27:40 -070075 avifBool hasICC;
Joe Drago345aaa12019-09-25 13:42:12 -070076 const uint8_t * icc;
Joe Drago41eb62b2019-02-08 15:38:18 -080077 size_t iccSize;
Joe Dragoa0da4a42020-05-08 14:27:40 -070078
79 avifBool hasNCLX;
Wan-Teh Chang559def52021-02-01 14:25:31 -080080 avifColorPrimaries colorPrimaries;
81 avifTransferCharacteristics transferCharacteristics;
82 avifMatrixCoefficients matrixCoefficients;
Joe Dragoa0da4a42020-05-08 14:27:40 -070083 avifRange range;
Joe Drago41eb62b2019-02-08 15:38:18 -080084} avifColourInformationBox;
85
Joe Drago60421562020-04-23 11:32:26 -070086#define MAX_PIXI_PLANE_DEPTHS 4
87typedef struct avifPixelInformationProperty
88{
89 uint8_t planeDepths[MAX_PIXI_PLANE_DEPTHS];
90 uint8_t planeCount;
91} avifPixelInformationProperty;
92
Joe Dragob13e5722019-02-08 19:07:25 -080093// ---------------------------------------------------------------------------
94// Top-level structures
95
Joe Drago9f2b87b2020-06-03 19:36:38 -070096struct avifMeta;
97
Joe Dragoa72da5b2020-06-15 19:40:17 -070098// Temporary storage for ipco/stsd contents until they can be associated and memcpy'd to an avifDecoderItem
Joe Drago8f7a3002019-02-07 19:35:37 -080099typedef struct avifProperty
100{
101 uint8_t type[4];
Wan-Teh Chang2031dc12020-05-22 09:24:46 -0700102 union
103 {
104 avifImageSpatialExtents ispe;
105 avifAuxiliaryType auxC;
106 avifColourInformationBox colr;
107 avifCodecConfigurationBox av1C;
108 avifPixelAspectRatioBox pasp;
109 avifCleanApertureBox clap;
110 avifImageRotation irot;
111 avifImageMirror imir;
112 avifPixelInformationProperty pixi;
113 } u;
Joe Drago8f7a3002019-02-07 19:35:37 -0800114} avifProperty;
Joe Drago05559c92019-07-17 16:33:38 -0700115AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop);
Joe Drago8f7a3002019-02-07 19:35:37 -0800116
Joe Dragoa72da5b2020-06-15 19:40:17 -0700117static const avifProperty * avifPropertyArrayFind(const avifPropertyArray * properties, const char * type)
118{
119 for (uint32_t propertyIndex = 0; propertyIndex < properties->count; ++propertyIndex) {
120 avifProperty * prop = &properties->prop[propertyIndex];
121 if (!memcmp(prop->type, type, 4)) {
122 return prop;
123 }
124 }
125 return NULL;
126}
127
Joe Drago4bcdfde2020-11-13 17:50:55 -0800128AVIF_ARRAY_DECLARE(avifExtentArray, avifExtent, extent);
Joe Dragoa4956902020-08-28 01:24:35 -0700129
Joe Dragoa72da5b2020-06-15 19:40:17 -0700130// one "item" worth for decoding (all iref, iloc, iprp, etc refer to one of these)
131typedef struct avifDecoderItem
132{
133 uint32_t id;
Joe Dragoba1eb492020-06-22 17:05:04 -0700134 struct avifMeta * meta; // Unowned; A back-pointer for convenience
Joe Dragoa72da5b2020-06-15 19:40:17 -0700135 uint8_t type[4];
Joe Drago217056b2020-11-13 16:19:35 -0800136 size_t size;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700137 uint32_t idatID; // If non-zero, offset is relative to this idat box (iloc construction_method==1)
138 avifContentType contentType;
139 avifPropertyArray properties;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800140 avifExtentArray extents; // All extent offsets/sizes
Joe Dragobe4cbb92020-09-21 12:14:05 -0700141 avifRWData mergedExtents; // if set, is a single contiguous block of this item's extents (unused when extents.count == 1)
142 avifBool ownsMergedExtents; // if true, mergedExtents must be freed when this item is destroyed
143 avifBool partialMergedExtents; // If true, mergedExtents doesn't have all of the item data yet
144 uint32_t thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
145 uint32_t auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID}
146 uint32_t descForID; // if non-zero, this item is a content description for Item #{descForID}
147 uint32_t dimgForID; // if non-zero, this item is a derived image for Item #{dimgForID}
Yuan Tonge4850be2021-01-22 14:21:25 +0800148 uint32_t premByID; // if non-zero, this item is premultiplied by Item #{premByID}
Joe Dragoa72da5b2020-06-15 19:40:17 -0700149 avifBool hasUnsupportedEssentialProperty; // If true, this item cites a property flagged as 'essential' that libavif doesn't support (yet). Ignore the item, if so.
Wan-Teh Changf4c65382020-11-03 14:27:45 -0800150 avifBool ipmaSeen; // if true, this item already received a property association
Joe Dragoa72da5b2020-06-15 19:40:17 -0700151} avifDecoderItem;
152AVIF_ARRAY_DECLARE(avifDecoderItemArray, avifDecoderItem, item);
153
Joe Dragof6a42272019-11-21 15:21:41 -0800154// idat storage
Joe Drago800b47f2020-03-18 16:22:37 -0700155typedef struct avifDecoderItemData
Joe Dragof6a42272019-11-21 15:21:41 -0800156{
157 uint32_t id;
Joe Dragobe4cbb92020-09-21 12:14:05 -0700158 avifRWData data;
Joe Drago800b47f2020-03-18 16:22:37 -0700159} avifDecoderItemData;
160AVIF_ARRAY_DECLARE(avifDecoderItemDataArray, avifDecoderItemData, idat);
Joe Dragof6a42272019-11-21 15:21:41 -0800161
Joe Drago060d5342020-03-03 10:53:49 -0800162// grid storage
163typedef struct avifImageGrid
164{
Joe Drago79ebcf32020-11-18 22:37:10 -0800165 uint32_t rows; // Legal range: [1-256]
166 uint32_t columns; // Legal range: [1-256]
Joe Drago060d5342020-03-03 10:53:49 -0800167 uint32_t outputWidth;
168 uint32_t outputHeight;
169} avifImageGrid;
170
Joe Dragoae7e2c32019-07-18 15:22:25 -0700171// ---------------------------------------------------------------------------
172// avifTrack
173
174typedef struct avifSampleTableChunk
175{
176 uint64_t offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700177} avifSampleTableChunk;
178AVIF_ARRAY_DECLARE(avifSampleTableChunkArray, avifSampleTableChunk, chunk);
179
180typedef struct avifSampleTableSampleToChunk
181{
182 uint32_t firstChunk;
183 uint32_t samplesPerChunk;
184 uint32_t sampleDescriptionIndex;
185} avifSampleTableSampleToChunk;
186AVIF_ARRAY_DECLARE(avifSampleTableSampleToChunkArray, avifSampleTableSampleToChunk, sampleToChunk);
187
188typedef struct avifSampleTableSampleSize
189{
190 uint32_t size;
191} avifSampleTableSampleSize;
192AVIF_ARRAY_DECLARE(avifSampleTableSampleSizeArray, avifSampleTableSampleSize, sampleSize);
193
194typedef struct avifSampleTableTimeToSample
195{
196 uint32_t sampleCount;
197 uint32_t sampleDelta;
198} avifSampleTableTimeToSample;
199AVIF_ARRAY_DECLARE(avifSampleTableTimeToSampleArray, avifSampleTableTimeToSample, timeToSample);
200
Joe Drago22c1ad92019-09-26 12:46:50 -0700201typedef struct avifSyncSample
202{
203 uint32_t sampleNumber;
204} avifSyncSample;
205AVIF_ARRAY_DECLARE(avifSyncSampleArray, avifSyncSample, syncSample);
206
Joe Drago2c0924c2019-09-26 17:41:01 -0700207typedef struct avifSampleDescription
208{
209 uint8_t format[4];
Joe Dragoa72da5b2020-06-15 19:40:17 -0700210 avifPropertyArray properties;
Joe Drago2c0924c2019-09-26 17:41:01 -0700211} avifSampleDescription;
212AVIF_ARRAY_DECLARE(avifSampleDescriptionArray, avifSampleDescription, description);
213
Joe Dragoae7e2c32019-07-18 15:22:25 -0700214typedef struct avifSampleTable
215{
216 avifSampleTableChunkArray chunks;
Joe Drago2c0924c2019-09-26 17:41:01 -0700217 avifSampleDescriptionArray sampleDescriptions;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700218 avifSampleTableSampleToChunkArray sampleToChunks;
219 avifSampleTableSampleSizeArray sampleSizes;
220 avifSampleTableTimeToSampleArray timeToSamples;
Joe Drago22c1ad92019-09-26 12:46:50 -0700221 avifSyncSampleArray syncSamples;
Joe Drago370be3f2020-02-07 15:59:42 -0800222 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 -0700223} avifSampleTable;
224
Joe Drago46ea0582019-07-22 15:55:47 -0700225static avifSampleTable * avifSampleTableCreate()
Joe Dragoae7e2c32019-07-18 15:22:25 -0700226{
227 avifSampleTable * sampleTable = (avifSampleTable *)avifAlloc(sizeof(avifSampleTable));
228 memset(sampleTable, 0, sizeof(avifSampleTable));
229 avifArrayCreate(&sampleTable->chunks, sizeof(avifSampleTableChunk), 16);
Joe Drago2c0924c2019-09-26 17:41:01 -0700230 avifArrayCreate(&sampleTable->sampleDescriptions, sizeof(avifSampleDescription), 2);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700231 avifArrayCreate(&sampleTable->sampleToChunks, sizeof(avifSampleTableSampleToChunk), 16);
232 avifArrayCreate(&sampleTable->sampleSizes, sizeof(avifSampleTableSampleSize), 16);
233 avifArrayCreate(&sampleTable->timeToSamples, sizeof(avifSampleTableTimeToSample), 16);
Joe Drago759e6742019-09-26 18:07:21 -0700234 avifArrayCreate(&sampleTable->syncSamples, sizeof(avifSyncSample), 16);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700235 return sampleTable;
236}
237
Joe Drago46ea0582019-07-22 15:55:47 -0700238static void avifSampleTableDestroy(avifSampleTable * sampleTable)
Joe Dragoae7e2c32019-07-18 15:22:25 -0700239{
240 avifArrayDestroy(&sampleTable->chunks);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700241 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
242 avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
243 avifArrayDestroy(&description->properties);
244 }
Joe Drago2c0924c2019-09-26 17:41:01 -0700245 avifArrayDestroy(&sampleTable->sampleDescriptions);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700246 avifArrayDestroy(&sampleTable->sampleToChunks);
247 avifArrayDestroy(&sampleTable->sampleSizes);
248 avifArrayDestroy(&sampleTable->timeToSamples);
Joe Drago22c1ad92019-09-26 12:46:50 -0700249 avifArrayDestroy(&sampleTable->syncSamples);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700250 avifFree(sampleTable);
251}
252
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700253static uint32_t avifSampleTableGetImageDelta(const avifSampleTable * sampleTable, int imageIndex)
Joe Drago46ea0582019-07-22 15:55:47 -0700254{
255 int maxSampleIndex = 0;
256 for (uint32_t i = 0; i < sampleTable->timeToSamples.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700257 const avifSampleTableTimeToSample * timeToSample = &sampleTable->timeToSamples.timeToSample[i];
Joe Drago46ea0582019-07-22 15:55:47 -0700258 maxSampleIndex += timeToSample->sampleCount;
259 if ((imageIndex < maxSampleIndex) || (i == (sampleTable->timeToSamples.count - 1))) {
260 return timeToSample->sampleDelta;
261 }
262 }
263
264 // TODO: fail here?
265 return 1;
266}
267
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700268static avifBool avifSampleTableHasFormat(const avifSampleTable * sampleTable, const char * format)
Joe Drago2c0924c2019-09-26 17:41:01 -0700269{
270 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
271 if (!memcmp(sampleTable->sampleDescriptions.description[i].format, format, 4)) {
272 return AVIF_TRUE;
273 }
274 }
275 return AVIF_FALSE;
276}
277
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700278static uint32_t avifCodecConfigurationBoxGetDepth(const avifCodecConfigurationBox * av1C)
Joe Drago6500fd62019-10-08 17:17:34 -0700279{
280 if (av1C->twelveBit) {
281 return 12;
282 } else if (av1C->highBitdepth) {
283 return 10;
284 }
285 return 8;
286}
287
Joe Dragoa72da5b2020-06-15 19:40:17 -0700288static const avifPropertyArray * avifSampleTableGetProperties(const avifSampleTable * sampleTable)
Joe Drago6500fd62019-10-08 17:17:34 -0700289{
290 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700291 const avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
Joe Dragoa72da5b2020-06-15 19:40:17 -0700292 if (!memcmp(description->format, "av01", 4)) {
293 return &description->properties;
Joe Drago6500fd62019-10-08 17:17:34 -0700294 }
295 }
Joe Drago00bcaaf2020-06-05 15:29:38 -0700296 return NULL;
Joe Drago6500fd62019-10-08 17:17:34 -0700297}
298
Joe Dragoae7e2c32019-07-18 15:22:25 -0700299// one video track ("trak" contents)
300typedef struct avifTrack
301{
302 uint32_t id;
Wan-Teh Chang53adb6a2021-02-20 16:58:43 -0800303 uint32_t auxForID; // if non-zero, this track is an auxC plane for Track #{auxForID}
304 uint32_t premByID; // if non-zero, this track is premultiplied by Track #{premByID}
Joe Dragoae7e2c32019-07-18 15:22:25 -0700305 uint32_t mediaTimescale;
306 uint64_t mediaDuration;
Joe Dragofc4144e2019-09-27 20:35:06 -0700307 uint32_t width;
308 uint32_t height;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700309 avifSampleTable * sampleTable;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700310 struct avifMeta * meta;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700311} avifTrack;
312AVIF_ARRAY_DECLARE(avifTrackArray, avifTrack, track);
313
314// ---------------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -0700315// avifCodecDecodeInput
316
Joe Drago399df4f2019-07-23 16:45:14 -0700317avifCodecDecodeInput * avifCodecDecodeInputCreate(void)
Joe Drago46ea0582019-07-22 15:55:47 -0700318{
319 avifCodecDecodeInput * decodeInput = (avifCodecDecodeInput *)avifAlloc(sizeof(avifCodecDecodeInput));
320 memset(decodeInput, 0, sizeof(avifCodecDecodeInput));
Joe Dragoe3e3bfa2020-06-02 16:33:53 -0700321 avifArrayCreate(&decodeInput->samples, sizeof(avifDecodeSample), 1);
Joe Drago46ea0582019-07-22 15:55:47 -0700322 return decodeInput;
323}
324
Joe Drago8b34ad72019-07-22 16:56:32 -0700325void avifCodecDecodeInputDestroy(avifCodecDecodeInput * decodeInput)
Joe Drago46ea0582019-07-22 15:55:47 -0700326{
Joe Dragobe4cbb92020-09-21 12:14:05 -0700327 for (uint32_t sampleIndex = 0; sampleIndex < decodeInput->samples.count; ++sampleIndex) {
328 avifDecodeSample * sample = &decodeInput->samples.sample[sampleIndex];
329 if (sample->ownsData) {
330 avifRWDataFree((avifRWData *)&sample->data);
331 }
332 }
Joe Drago46ea0582019-07-22 15:55:47 -0700333 avifArrayDestroy(&decodeInput->samples);
334 avifFree(decodeInput);
335}
336
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700337// Returns how many samples are in the chunk.
338static uint32_t avifGetSampleCountOfChunk(const avifSampleTableSampleToChunkArray * sampleToChunks, uint32_t chunkIndex)
339{
340 uint32_t sampleCount = 0;
341 for (int sampleToChunkIndex = sampleToChunks->count - 1; sampleToChunkIndex >= 0; --sampleToChunkIndex) {
342 const avifSampleTableSampleToChunk * sampleToChunk = &sampleToChunks->sampleToChunk[sampleToChunkIndex];
343 if (sampleToChunk->firstChunk <= (chunkIndex + 1)) {
344 sampleCount = sampleToChunk->samplesPerChunk;
345 break;
346 }
347 }
348 return sampleCount;
349}
350
Joe Dragod2340b42021-03-14 13:20:02 -0700351static avifBool avifCodecDecodeInputGetSamples(avifCodecDecodeInput * decodeInput,
352 avifSampleTable * sampleTable,
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700353 const uint32_t imageCountLimit,
Joe Drago4d5f4a42021-04-28 13:11:45 -0700354 const uint64_t sizeHint,
355 avifDiagnostics * diag)
Joe Drago46ea0582019-07-22 15:55:47 -0700356{
Joe Dragod2340b42021-03-14 13:20:02 -0700357 if (imageCountLimit) {
358 // Verify that the we're not about to exceed the frame count limit.
359
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700360 uint32_t imageCountLeft = imageCountLimit;
Joe Dragod2340b42021-03-14 13:20:02 -0700361 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
362 // First, figure out how many samples are in this chunk
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700363 uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex);
Joe Dragod2340b42021-03-14 13:20:02 -0700364 if (sampleCount == 0) {
365 // chunks with 0 samples are invalid
Joe Drago4d5f4a42021-04-28 13:11:45 -0700366 avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples");
Joe Dragod2340b42021-03-14 13:20:02 -0700367 return AVIF_FALSE;
368 }
369
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700370 if (sampleCount > imageCountLeft) {
Joe Dragod2340b42021-03-14 13:20:02 -0700371 // This file exceeds the imageCountLimit, bail out
Joe Drago4d5f4a42021-04-28 13:11:45 -0700372 avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit");
Joe Dragod2340b42021-03-14 13:20:02 -0700373 return AVIF_FALSE;
374 }
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700375 imageCountLeft -= sampleCount;
Joe Dragod2340b42021-03-14 13:20:02 -0700376 }
377 }
378
Joe Drago46ea0582019-07-22 15:55:47 -0700379 uint32_t sampleSizeIndex = 0;
380 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
381 avifSampleTableChunk * chunk = &sampleTable->chunks.chunk[chunkIndex];
382
383 // First, figure out how many samples are in this chunk
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700384 uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex);
Joe Drago46ea0582019-07-22 15:55:47 -0700385 if (sampleCount == 0) {
386 // chunks with 0 samples are invalid
Joe Drago4d5f4a42021-04-28 13:11:45 -0700387 avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples");
Joe Drago46ea0582019-07-22 15:55:47 -0700388 return AVIF_FALSE;
389 }
390
391 uint64_t sampleOffset = chunk->offset;
392 for (uint32_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
Joe Drago370be3f2020-02-07 15:59:42 -0800393 uint32_t sampleSize = sampleTable->allSamplesSize;
394 if (sampleSize == 0) {
395 if (sampleSizeIndex >= sampleTable->sampleSizes.count) {
396 // We've run out of samples to sum
Joe Drago4d5f4a42021-04-28 13:11:45 -0700397 avifDiagnosticsPrintf(diag, "Truncated sample table");
Joe Drago370be3f2020-02-07 15:59:42 -0800398 return AVIF_FALSE;
399 }
400 avifSampleTableSampleSize * sampleSizePtr = &sampleTable->sampleSizes.sampleSize[sampleSizeIndex];
401 sampleSize = sampleSizePtr->size;
Joe Drago46ea0582019-07-22 15:55:47 -0700402 }
403
Joe Dragoe3e3bfa2020-06-02 16:33:53 -0700404 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700405 sample->offset = sampleOffset;
406 sample->size = sampleSize;
Joe Drago22c1ad92019-09-26 12:46:50 -0700407 sample->sync = AVIF_FALSE; // to potentially be set to true following the outer loop
Joe Drago46ea0582019-07-22 15:55:47 -0700408
Wan-Teh Chang3ca14242020-09-30 16:39:38 -0700409 if (sampleSize > UINT64_MAX - sampleOffset) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700410 avifDiagnosticsPrintf(diag,
Joe Drago48867292021-05-04 13:23:08 -0700411 "Sample table contains an offset/size pair which overflows: [%" PRIu64 " / %u]",
Joe Drago4d5f4a42021-04-28 13:11:45 -0700412 sampleOffset,
413 sampleSize);
Wan-Teh Chang3ca14242020-09-30 16:39:38 -0700414 return AVIF_FALSE;
415 }
Wan-Teh Chang136d7572020-10-08 15:13:42 -0700416 if (sizeHint && ((sampleOffset + sampleSize) > sizeHint)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700417 avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data");
Joe Drago34c0d312020-04-30 15:23:03 -0700418 return AVIF_FALSE;
419 }
Joe Drago46ea0582019-07-22 15:55:47 -0700420
Joe Drago370be3f2020-02-07 15:59:42 -0800421 sampleOffset += sampleSize;
Joe Drago46ea0582019-07-22 15:55:47 -0700422 ++sampleSizeIndex;
423 }
424 }
Joe Drago22c1ad92019-09-26 12:46:50 -0700425
426 // Mark appropriate samples as sync
427 for (uint32_t syncSampleIndex = 0; syncSampleIndex < sampleTable->syncSamples.count; ++syncSampleIndex) {
428 uint32_t frameIndex = sampleTable->syncSamples.syncSample[syncSampleIndex].sampleNumber - 1; // sampleNumber is 1-based
429 if (frameIndex < decodeInput->samples.count) {
430 decodeInput->samples.sample[frameIndex].sync = AVIF_TRUE;
431 }
432 }
433
434 // Assume frame 0 is sync, just in case the stss box is absent in the BMFF. (Unnecessary?)
435 if (decodeInput->samples.count > 0) {
436 decodeInput->samples.sample[0].sync = AVIF_TRUE;
437 }
Joe Drago46ea0582019-07-22 15:55:47 -0700438 return AVIF_TRUE;
439}
440
441// ---------------------------------------------------------------------------
Joe Drago8f439092020-08-28 15:05:17 -0700442// Helper macros / functions
Joe Dragoa72da5b2020-06-15 19:40:17 -0700443
Joe Drago618ed5f2021-05-04 12:13:24 -0700444#define BEGIN_STREAM(VARNAME, PTR, SIZE, DIAG, CONTEXT) \
445 avifROStream VARNAME; \
446 avifROData VARNAME##_roData; \
447 VARNAME##_roData.data = PTR; \
448 VARNAME##_roData.size = SIZE; \
449 avifROStreamStart(&VARNAME, &VARNAME##_roData, DIAG, CONTEXT)
Joe Dragoa72da5b2020-06-15 19:40:17 -0700450
Joe Drago8f439092020-08-28 15:05:17 -0700451// Use this to keep track of whether or not a child box that must be unique (0 or 1 present) has
452// been seen yet, when parsing a parent box. If the "seen" bit is already set for a given box when
453// it is encountered during parse, an error is thrown. Which bit corresponds to which box is
454// dictated entirely by the calling function.
Joe Drago4d5f4a42021-04-28 13:11:45 -0700455static avifBool uniqueBoxSeen(uint32_t * uniqueBoxFlags, uint32_t whichFlag, const char * parentBoxType, const char * boxType, avifDiagnostics * diagnostics)
Joe Drago8f439092020-08-28 15:05:17 -0700456{
457 const uint32_t flag = 1 << whichFlag;
458 if (*uniqueBoxFlags & flag) {
459 // This box has already been seen. Error!
Joe Drago618ed5f2021-05-04 12:13:24 -0700460 avifDiagnosticsPrintf(diagnostics, "Box[%s] contains a duplicate unique box of type '%s'", parentBoxType, boxType);
Joe Drago8f439092020-08-28 15:05:17 -0700461 return AVIF_FALSE;
462 }
463
464 // Mark this box as seen.
465 *uniqueBoxFlags |= flag;
466 return AVIF_TRUE;
467}
468
Joe Dragoa72da5b2020-06-15 19:40:17 -0700469// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700470// avifDecoderData
Joe Dragoae7e2c32019-07-18 15:22:25 -0700471
Joe Drago060d5342020-03-03 10:53:49 -0800472typedef struct avifTile
473{
474 avifCodecDecodeInput * input;
475 struct avifCodec * codec;
476 avifImage * image;
477} avifTile;
478AVIF_ARRAY_DECLARE(avifTileArray, avifTile, tile);
479
Joe Dragoba1eb492020-06-22 17:05:04 -0700480// This holds one "meta" box (from the BMFF and HEIF standards) worth of relevant-to-AVIF information.
481// * If a meta box is parsed from the root level of the BMFF, it can contain the information about
482// "items" which might be color planes, alpha planes, or EXIF or XMP metadata.
483// * If a meta box is parsed from inside of a track ("trak") box, any metadata (EXIF/XMP) items inside
484// of that box are implicitly associated with that track.
Joe Drago9f2b87b2020-06-03 19:36:38 -0700485typedef struct avifMeta
Joe Drago444f0512019-01-23 17:03:24 -0800486{
Joe Dragoba1eb492020-06-22 17:05:04 -0700487 // Items (from HEIF) are the generic storage for any data that does not require timed processing
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700488 // (single image color planes, alpha planes, EXIF, XMP, etc). Each item has a unique integer ID >1,
489 // and is defined by a series of child boxes in a meta box:
Joe Dragoba1eb492020-06-22 17:05:04 -0700490 // * iloc - location: byte offset to item data, item size in bytes
491 // * iinf - information: type of item (color planes, alpha plane, EXIF, XMP)
492 // * ipco - properties: dimensions, aspect ratio, image transformations, references to other items
493 // * ipma - associations: Attaches an item in the properties list to a given item
494 //
495 // Items are lazily created in this array when any of the above boxes refer to one by a new (unseen) ID,
496 // and are then further modified/updated as new information for an item's ID is parsed.
Joe Drago800b47f2020-03-18 16:22:37 -0700497 avifDecoderItemArray items;
Joe Dragoba1eb492020-06-22 17:05:04 -0700498
499 // Any ipco boxes explained above are populated into this array as a staging area, which are
500 // then duplicated into the appropriate items upon encountering an item property association
501 // (ipma) box.
Joe Drago05559c92019-07-17 16:33:38 -0700502 avifPropertyArray properties;
Joe Dragoba1eb492020-06-22 17:05:04 -0700503
504 // Filled with the contents of "idat" boxes, which are raw data that an item can directly refer to in its
505 // item location box (iloc) instead of just giving an offset into the overall file. If all items' iloc boxes
506 // simply point at an offset/length in the file itself, this array will likely be empty.
Joe Drago800b47f2020-03-18 16:22:37 -0700507 avifDecoderItemDataArray idats;
Joe Dragoba1eb492020-06-22 17:05:04 -0700508
509 // Ever-incrementing ID for uniquely identifying which 'meta' box contains an idat (when
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700510 // multiple meta boxes exist as BMFF siblings). Each time avifParseMetaBox() is called on an
Joe Dragoba1eb492020-06-22 17:05:04 -0700511 // avifMeta struct, this value is incremented. Any time an additional meta box is detected at
512 // the same "level" (root level, trak level, etc), this ID helps distinguish which meta box's
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700513 // "idat" is which, as items implicitly reference idat boxes that exist in the same meta
Joe Dragoba1eb492020-06-22 17:05:04 -0700514 // box.
515 uint32_t idatID;
516
517 // Contents of a pitm box, which signal which of the items in this file is the main image. For
518 // AVIF, this should point at an av01 type item containing color planes, and all other items
519 // are ignored unless they refer to this item in some way (alpha plane, EXIF/XMP metadata).
Joe Drago9f2b87b2020-06-03 19:36:38 -0700520 uint32_t primaryItemID;
521} avifMeta;
522
523static avifMeta * avifMetaCreate()
524{
525 avifMeta * meta = (avifMeta *)avifAlloc(sizeof(avifMeta));
526 memset(meta, 0, sizeof(avifMeta));
527 avifArrayCreate(&meta->items, sizeof(avifDecoderItem), 8);
528 avifArrayCreate(&meta->properties, sizeof(avifProperty), 16);
529 avifArrayCreate(&meta->idats, sizeof(avifDecoderItemData), 1);
530 return meta;
531}
532
533static void avifMetaDestroy(avifMeta * meta)
534{
Joe Dragoa72da5b2020-06-15 19:40:17 -0700535 for (uint32_t i = 0; i < meta->items.count; ++i) {
536 avifDecoderItem * item = &meta->items.item[i];
537 avifArrayDestroy(&item->properties);
Joe Dragoa4956902020-08-28 01:24:35 -0700538 avifArrayDestroy(&item->extents);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700539 if (item->ownsMergedExtents) {
540 avifRWDataFree(&item->mergedExtents);
541 }
Joe Dragoa72da5b2020-06-15 19:40:17 -0700542 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700543 avifArrayDestroy(&meta->items);
544 avifArrayDestroy(&meta->properties);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700545 for (uint32_t i = 0; i < meta->idats.count; ++i) {
546 avifDecoderItemData * idat = &meta->idats.idat[i];
547 avifRWDataFree(&idat->data);
548 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700549 avifArrayDestroy(&meta->idats);
550 avifFree(meta);
551}
552
553static avifDecoderItem * avifMetaFindItem(avifMeta * meta, uint32_t itemID)
554{
555 if (itemID == 0) {
556 return NULL;
557 }
558
559 for (uint32_t i = 0; i < meta->items.count; ++i) {
560 if (meta->items.item[i].id == itemID) {
561 return &meta->items.item[i];
562 }
563 }
564
565 avifDecoderItem * item = (avifDecoderItem *)avifArrayPushPtr(&meta->items);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700566 avifArrayCreate(&item->properties, sizeof(avifProperty), 16);
Joe Drago4bcdfde2020-11-13 17:50:55 -0800567 avifArrayCreate(&item->extents, sizeof(avifExtent), 1);
Joe Drago9f2b87b2020-06-03 19:36:38 -0700568 item->id = itemID;
569 item->meta = meta;
570 return item;
571}
572
573typedef struct avifDecoderData
574{
Wan-Teh Chang6fc17582020-09-24 15:16:37 -0700575 avifMeta * meta; // The root-level meta box
Joe Dragoae7e2c32019-07-18 15:22:25 -0700576 avifTrackArray tracks;
Joe Drago060d5342020-03-03 10:53:49 -0800577 avifTileArray tiles;
578 unsigned int colorTileCount;
579 unsigned int alphaTileCount;
580 avifImageGrid colorGrid;
581 avifImageGrid alphaGrid;
Joe Drago46ea0582019-07-22 15:55:47 -0700582 avifDecoderSource source;
Joe Drago4d5f4a42021-04-28 13:11:45 -0700583 avifDiagnostics * diag; // Shallow copy; owned by avifDecoder
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700584 const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
Joe Dragoc00d5832020-08-13 16:03:28 -0700585 avifBool cicpSet; // True if avifDecoder's image has had its CICP set correctly yet.
586 // This allows nclx colr boxes to override AV1 CICP, as specified in the MIAF
587 // standard (ISO/IEC 23000-22:2019), section 7.3.6.4:
588 //
589 // "The colour information property takes precedence over any colour information in the image
590 // bitstream, i.e. if the property is present, colour information in the bitstream shall be ignored."
Joe Drago800b47f2020-03-18 16:22:37 -0700591} avifDecoderData;
Joe Drago444f0512019-01-23 17:03:24 -0800592
Joe Drago800b47f2020-03-18 16:22:37 -0700593static avifDecoderData * avifDecoderDataCreate()
Joe Drago05559c92019-07-17 16:33:38 -0700594{
Joe Drago800b47f2020-03-18 16:22:37 -0700595 avifDecoderData * data = (avifDecoderData *)avifAlloc(sizeof(avifDecoderData));
596 memset(data, 0, sizeof(avifDecoderData));
Joe Drago9f2b87b2020-06-03 19:36:38 -0700597 data->meta = avifMetaCreate();
Joe Dragoae7e2c32019-07-18 15:22:25 -0700598 avifArrayCreate(&data->tracks, sizeof(avifTrack), 2);
Joe Drago060d5342020-03-03 10:53:49 -0800599 avifArrayCreate(&data->tiles, sizeof(avifTile), 8);
Joe Drago05559c92019-07-17 16:33:38 -0700600 return data;
601}
602
Joe Drago800b47f2020-03-18 16:22:37 -0700603static void avifDecoderDataResetCodec(avifDecoderData * data)
Joe Drago05559c92019-07-17 16:33:38 -0700604{
Joe Drago060d5342020-03-03 10:53:49 -0800605 for (unsigned int i = 0; i < data->tiles.count; ++i) {
606 avifTile * tile = &data->tiles.tile[i];
Joe Drago9d368782020-03-04 17:53:17 -0800607 if (tile->image) {
608 avifImageFreePlanes(tile->image, AVIF_PLANES_ALL); // forget any pointers into codec image buffers
609 }
Joe Drago060d5342020-03-03 10:53:49 -0800610 if (tile->codec) {
611 avifCodecDestroy(tile->codec);
612 tile->codec = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -0700613 }
614 }
615}
616
Joe Drago800b47f2020-03-18 16:22:37 -0700617static avifTile * avifDecoderDataCreateTile(avifDecoderData * data)
Joe Drago060d5342020-03-03 10:53:49 -0800618{
619 avifTile * tile = (avifTile *)avifArrayPushPtr(&data->tiles);
620 tile->image = avifImageCreateEmpty();
621 tile->input = avifCodecDecodeInputCreate();
622 return tile;
623}
624
Joe Dragoa72da5b2020-06-15 19:40:17 -0700625static avifTrack * avifDecoderDataCreateTrack(avifDecoderData * data)
626{
627 avifTrack * track = (avifTrack *)avifArrayPushPtr(&data->tracks);
628 track->meta = avifMetaCreate();
629 return track;
630}
631
Joe Drago800b47f2020-03-18 16:22:37 -0700632static void avifDecoderDataClearTiles(avifDecoderData * data)
Joe Drago060d5342020-03-03 10:53:49 -0800633{
634 for (unsigned int i = 0; i < data->tiles.count; ++i) {
635 avifTile * tile = &data->tiles.tile[i];
636 if (tile->input) {
637 avifCodecDecodeInputDestroy(tile->input);
638 tile->input = NULL;
639 }
640 if (tile->codec) {
641 avifCodecDestroy(tile->codec);
642 tile->codec = NULL;
643 }
644 if (tile->image) {
645 avifImageDestroy(tile->image);
646 tile->image = NULL;
647 }
648 }
649 data->tiles.count = 0;
650 data->colorTileCount = 0;
651 data->alphaTileCount = 0;
652}
653
Joe Drago800b47f2020-03-18 16:22:37 -0700654static void avifDecoderDataDestroy(avifDecoderData * data)
Joe Drago46ea0582019-07-22 15:55:47 -0700655{
Joe Drago9f2b87b2020-06-03 19:36:38 -0700656 avifMetaDestroy(data->meta);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700657 for (uint32_t i = 0; i < data->tracks.count; ++i) {
Joe Dragoa72da5b2020-06-15 19:40:17 -0700658 avifTrack * track = &data->tracks.track[i];
659 if (track->sampleTable) {
660 avifSampleTableDestroy(track->sampleTable);
661 }
662 if (track->meta) {
663 avifMetaDestroy(track->meta);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700664 }
665 }
666 avifArrayDestroy(&data->tracks);
Joe Drago800b47f2020-03-18 16:22:37 -0700667 avifDecoderDataClearTiles(data);
Joe Drago060d5342020-03-03 10:53:49 -0800668 avifArrayDestroy(&data->tiles);
Joe Drago05559c92019-07-17 16:33:38 -0700669 avifFree(data);
670}
671
Joe Drago4bcdfde2020-11-13 17:50:55 -0800672// This returns the max extent that has to be read in order to decode this item. If
673// the item is stored in an idat, the data has already been read during Parse() and
674// this function will return AVIF_RESULT_OK with a 0-byte extent.
675static avifResult avifDecoderItemMaxExtent(const avifDecoderItem * item, avifExtent * outExtent)
676{
677 if (item->extents.count == 0) {
678 return AVIF_RESULT_TRUNCATED_DATA;
679 }
680
681 if (item->idatID != 0) {
682 // construction_method: idat(1)
683
Wan-Teh Chang80275392020-11-17 12:35:11 -0800684 // Find associated idat box
Joe Drago4bcdfde2020-11-13 17:50:55 -0800685 for (uint32_t i = 0; i < item->meta->idats.count; ++i) {
686 if (item->meta->idats.idat[i].id == item->idatID) {
687 // Already read from a meta box during Parse()
688 memset(outExtent, 0, sizeof(avifExtent));
689 return AVIF_RESULT_OK;
690 }
691 }
692
Wan-Teh Chang80275392020-11-17 12:35:11 -0800693 // no associated idat box was found in the meta box, bail out
Joe Drago4bcdfde2020-11-13 17:50:55 -0800694 return AVIF_RESULT_NO_CONTENT;
695 }
696
697 // construction_method: file(0)
698
Wan-Teh Chang15584522020-11-17 14:11:12 -0800699 // Assert that the for loop below will execute at least one iteration.
700 assert(item->extents.count != 0);
701 uint64_t minOffset = UINT64_MAX;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800702 uint64_t maxOffset = 0;
703 for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) {
704 avifExtent * extent = &item->extents.extent[extentIter];
705
706 if (extent->size > UINT64_MAX - extent->offset) {
707 return AVIF_RESULT_BMFF_PARSE_FAILED;
708 }
709 const uint64_t endOffset = extent->offset + extent->size;
710
Wan-Teh Chang15584522020-11-17 14:11:12 -0800711 if (minOffset > extent->offset) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800712 minOffset = extent->offset;
Wan-Teh Chang15584522020-11-17 14:11:12 -0800713 }
714 if (maxOffset < endOffset) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800715 maxOffset = endOffset;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800716 }
717 }
718
719 outExtent->offset = minOffset;
Wan-Teh Changd69958e2020-11-17 12:14:27 -0800720 const uint64_t extentLength = maxOffset - minOffset;
721 if (extentLength > SIZE_MAX) {
722 return AVIF_RESULT_BMFF_PARSE_FAILED;
723 }
724 outExtent->size = (size_t)extentLength;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800725 return AVIF_RESULT_OK;
726}
727
Joe Drago9b942c22021-05-06 12:33:12 -0700728static avifResult avifDecoderItemValidateAV1(const avifDecoderItem * item, avifDiagnostics * diag, const avifStrictFlags strictFlags)
Joe Dragoc10c6be2021-03-31 08:55:19 -0700729{
730 const avifProperty * av1CProp = avifPropertyArrayFind(&item->properties, "av1C");
731 if (!av1CProp) {
732 // An av1C box is mandatory in all valid AVIF configurations. Bail out.
Joe Drago618ed5f2021-05-04 12:13:24 -0700733 avifDiagnosticsPrintf(diag, "Item ID %u is missing mandatory av1C property", item->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -0700734 return AVIF_RESULT_BMFF_PARSE_FAILED;
735 }
736 const uint32_t av1CDepth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
737
738 const avifProperty * pixiProp = avifPropertyArrayFind(&item->properties, "pixi");
Joe Drago9b942c22021-05-06 12:33:12 -0700739 if (!pixiProp && (strictFlags & AVIF_STRICT_PIXI_REQUIRED)) {
Joe Dragoc10c6be2021-03-31 08:55:19 -0700740 // A pixi box is mandatory in all valid AVIF configurations. Bail out.
Joe Drago618ed5f2021-05-04 12:13:24 -0700741 avifDiagnosticsPrintf(diag, "Item ID %u is missing mandatory pixi property", item->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -0700742 return AVIF_RESULT_BMFF_PARSE_FAILED;
743 }
744
Joe Drago9b942c22021-05-06 12:33:12 -0700745 if (pixiProp) {
746 for (uint8_t i = 0; i < pixiProp->u.pixi.planeCount; ++i) {
747 if (pixiProp->u.pixi.planeDepths[i] != av1CDepth) {
748 // pixi depth must match av1C depth
749 avifDiagnosticsPrintf(diag,
750 "Item ID %u depth specified by pixi property [%u] does not match av1C property depth [%u]",
751 item->id,
752 pixiProp->u.pixi.planeDepths[i],
753 av1CDepth);
754 return AVIF_RESULT_BMFF_PARSE_FAILED;
755 }
Joe Dragoc10c6be2021-03-31 08:55:19 -0700756 }
757 }
758 return AVIF_RESULT_OK;
759}
760
Joe Drago618ed5f2021-05-04 12:13:24 -0700761static avifResult avifDecoderItemRead(avifDecoderItem * item, avifIO * io, avifROData * outData, size_t partialByteCount, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -0800762{
Joe Drago618ed5f2021-05-04 12:13:24 -0700763 (void)diag;
Joe Dragobe4cbb92020-09-21 12:14:05 -0700764 if (item->mergedExtents.data && !item->partialMergedExtents) {
Joe Dragoa4956902020-08-28 01:24:35 -0700765 // Multiple extents have already been concatenated for this item, just return it
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700766 memcpy(outData, &item->mergedExtents, sizeof(avifROData));
Joe Dragobe4cbb92020-09-21 12:14:05 -0700767 return AVIF_RESULT_OK;
768 }
769
770 if (item->extents.count == 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700771 avifDiagnosticsPrintf(diag, "Item ID %u has zero extents", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700772 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragoa4956902020-08-28 01:24:35 -0700773 }
774
775 // Find this item's source of all extents' data, based on the construction method
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -0700776 const avifRWData * idatBuffer = NULL;
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700777 if (item->idatID != 0) {
Joe Dragof6a42272019-11-21 15:21:41 -0800778 // construction_method: idat(1)
779
Wan-Teh Chang80275392020-11-17 12:35:11 -0800780 // Find associated idat box
Joe Drago9f2b87b2020-06-03 19:36:38 -0700781 for (uint32_t i = 0; i < item->meta->idats.count; ++i) {
782 if (item->meta->idats.idat[i].id == item->idatID) {
Joe Dragobe4cbb92020-09-21 12:14:05 -0700783 idatBuffer = &item->meta->idats.idat[i].data;
Joe Dragof6a42272019-11-21 15:21:41 -0800784 break;
785 }
786 }
787
Joe Dragobe4cbb92020-09-21 12:14:05 -0700788 if (idatBuffer == NULL) {
Wan-Teh Chang80275392020-11-17 12:35:11 -0800789 // no associated idat box was found in the meta box, bail out
Joe Drago618ed5f2021-05-04 12:13:24 -0700790 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 -0700791 return AVIF_RESULT_NO_CONTENT;
Joe Dragof6a42272019-11-21 15:21:41 -0800792 }
793 }
794
Joe Dragobe4cbb92020-09-21 12:14:05 -0700795 // Merge extents into a single contiguous buffer
Wan-Teh Chang4548b162020-11-06 11:48:25 -0800796 if ((io->sizeHint > 0) && (item->size > io->sizeHint)) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700797 // Sanity check: somehow the sum of extents exceeds the entire file or idat size!
798 avifDiagnosticsPrintf(diag, "Item ID %u reported size failed size hint sanity check. Truncated data?", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700799 return AVIF_RESULT_TRUNCATED_DATA;
Joe Drago8f439092020-08-28 15:05:17 -0700800 }
Joe Dragobe4cbb92020-09-21 12:14:05 -0700801
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700802 size_t totalBytesToRead = item->size;
803 if (partialByteCount && (totalBytesToRead > partialByteCount)) {
804 totalBytesToRead = partialByteCount;
805 }
806
Joe Dragobe4cbb92020-09-21 12:14:05 -0700807 // If there is a single extent for this item and the source of the read buffer is going to be
808 // persistent for the lifetime of the avifDecoder (whether it comes from its own internal
809 // idatBuffer or from a known-persistent IO), we can avoid buffer duplication and just use the
810 // preexisting buffer.
Wan-Teh Chang4548b162020-11-06 11:48:25 -0800811 avifBool singlePersistentBuffer = ((item->extents.count == 1) && (idatBuffer || io->persistent));
Joe Dragobe4cbb92020-09-21 12:14:05 -0700812 if (!singlePersistentBuffer) {
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700813 avifRWDataRealloc(&item->mergedExtents, totalBytesToRead);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700814 item->ownsMergedExtents = AVIF_TRUE;
815 }
816
817 // Set this until we manage to fill the entire mergedExtents buffer
818 item->partialMergedExtents = AVIF_TRUE;
Joe Drago8f439092020-08-28 15:05:17 -0700819
Joe Dragoa4956902020-08-28 01:24:35 -0700820 uint8_t * front = item->mergedExtents.data;
Joe Dragobe4cbb92020-09-21 12:14:05 -0700821 size_t remainingBytes = totalBytesToRead;
Joe Dragoa4956902020-08-28 01:24:35 -0700822 for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800823 avifExtent * extent = &item->extents.extent[extentIter];
Joe Dragobe4cbb92020-09-21 12:14:05 -0700824
825 size_t bytesToRead = extent->size;
826 if (bytesToRead > remainingBytes) {
827 bytesToRead = remainingBytes;
Joe Dragoa4956902020-08-28 01:24:35 -0700828 }
829
Joe Dragobe4cbb92020-09-21 12:14:05 -0700830 avifROData offsetBuffer;
831 if (idatBuffer) {
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700832 if (extent->offset > idatBuffer->size) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700833 avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent offset in idat buffer", item->id);
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700834 return AVIF_RESULT_BMFF_PARSE_FAILED;
835 }
836 if (extent->size > idatBuffer->size - extent->offset) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700837 avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent size in idat buffer", item->id);
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700838 return AVIF_RESULT_BMFF_PARSE_FAILED;
839 }
840 offsetBuffer.data = idatBuffer->data + extent->offset;
841 offsetBuffer.size = idatBuffer->size - extent->offset;
Joe Dragobe4cbb92020-09-21 12:14:05 -0700842 } else {
843 // construction_method: file(0)
844
Wan-Teh Chang4548b162020-11-06 11:48:25 -0800845 if ((io->sizeHint > 0) && (extent->offset > io->sizeHint)) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700846 avifDiagnosticsPrintf(diag, "Item ID %u extent offset failed size hint sanity check. Truncated data?", item->id);
Wan-Teh Changb856dc22020-09-28 13:00:56 -0700847 return AVIF_RESULT_BMFF_PARSE_FAILED;
848 }
Wan-Teh Chang4548b162020-11-06 11:48:25 -0800849 avifResult readResult = io->read(io, 0, extent->offset, bytesToRead, &offsetBuffer);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700850 if (readResult != AVIF_RESULT_OK) {
851 return readResult;
852 }
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -0700853 if (bytesToRead != offsetBuffer.size) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700854 avifDiagnosticsPrintf(diag,
855 "Item ID %u tried to read %zu bytes, but only received %zu bytes",
Joe Drago48867292021-05-04 13:23:08 -0700856 item->id,
Joe Drago618ed5f2021-05-04 12:13:24 -0700857 bytesToRead,
858 offsetBuffer.size);
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -0700859 return AVIF_RESULT_TRUNCATED_DATA;
860 }
Joe Dragobe4cbb92020-09-21 12:14:05 -0700861 }
862
863 if (singlePersistentBuffer) {
864 memcpy(&item->mergedExtents, &offsetBuffer, sizeof(avifRWData));
865 item->mergedExtents.size = bytesToRead;
866 } else {
Joe Dragodb1009a2021-03-16 15:26:14 -0700867 assert(item->ownsMergedExtents);
868 assert(front);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700869 memcpy(front, offsetBuffer.data, bytesToRead);
870 front += bytesToRead;
871 }
872
873 remainingBytes -= bytesToRead;
874 if (remainingBytes == 0) {
875 // This happens when partialByteCount is set
876 break;
877 }
Joe Dragoa4956902020-08-28 01:24:35 -0700878 }
879 if (remainingBytes != 0) {
880 // This should be impossible?
Joe Drago618ed5f2021-05-04 12:13:24 -0700881 avifDiagnosticsPrintf(diag, "Item ID %u has %zu unexpected trailing bytes", item->id, remainingBytes);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700882 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragof6a42272019-11-21 15:21:41 -0800883 }
Joe Dragobe4cbb92020-09-21 12:14:05 -0700884
885 outData->data = item->mergedExtents.data;
886 outData->size = totalBytesToRead;
887 item->partialMergedExtents = (item->size != totalBytesToRead);
888 return AVIF_RESULT_OK;
Joe Dragof6a42272019-11-21 15:21:41 -0800889}
890
Joe Drago800b47f2020-03-18 16:22:37 -0700891static avifBool avifDecoderDataGenerateImageGridTiles(avifDecoderData * data, avifImageGrid * grid, avifDecoderItem * gridItem, avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -0800892{
Wan-Teh Chang7ca3dd92020-11-20 12:50:44 -0800893 unsigned int tilesRequested = grid->rows * grid->columns;
Joe Drago060d5342020-03-03 10:53:49 -0800894
895 // Count number of dimg for this item, bail out if it doesn't match perfectly
896 unsigned int tilesAvailable = 0;
Joe Drago9f2b87b2020-06-03 19:36:38 -0700897 for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) {
898 avifDecoderItem * item = &gridItem->meta->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -0800899 if (item->dimgForID == gridItem->id) {
900 if (memcmp(item->type, "av01", 4)) {
901 continue;
902 }
Joe Drago3320e5f2020-04-21 17:36:27 -0700903 if (item->hasUnsupportedEssentialProperty) {
Wan-Teh Chang29aaade2020-08-10 16:14:16 -0700904 // An essential property isn't supported by libavif; can't
905 // decode a grid image if any tile in the grid isn't supported.
Joe Drago4d5f4a42021-04-28 13:11:45 -0700906 avifDiagnosticsPrintf(data->diag, "Grid image contains tile with an unsupported property marked as essential");
Wan-Teh Chang29aaade2020-08-10 16:14:16 -0700907 return AVIF_FALSE;
Joe Drago3320e5f2020-04-21 17:36:27 -0700908 }
Joe Drago060d5342020-03-03 10:53:49 -0800909
910 ++tilesAvailable;
911 }
912 }
913
914 if (tilesRequested != tilesAvailable) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700915 avifDiagnosticsPrintf(data->diag,
916 "Grid image of dimensions %u/%u requires %u tiles, and only %u were found",
917 grid->columns,
918 grid->rows,
919 tilesRequested,
920 tilesAvailable);
Joe Drago060d5342020-03-03 10:53:49 -0800921 return AVIF_FALSE;
922 }
923
Joe Drago9c5f5652020-06-29 15:13:44 -0700924 avifBool firstTile = AVIF_TRUE;
Joe Drago9f2b87b2020-06-03 19:36:38 -0700925 for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) {
926 avifDecoderItem * item = &gridItem->meta->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -0800927 if (item->dimgForID == gridItem->id) {
928 if (memcmp(item->type, "av01", 4)) {
929 continue;
930 }
931
Joe Drago800b47f2020-03-18 16:22:37 -0700932 avifTile * tile = avifDecoderDataCreateTile(data);
Joe Dragoe3e3bfa2020-06-02 16:33:53 -0700933 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&tile->input->samples);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700934 sample->itemID = item->id;
935 sample->offset = 0;
936 sample->size = item->size;
Joe Drago060d5342020-03-03 10:53:49 -0800937 sample->sync = AVIF_TRUE;
938 tile->input->alpha = alpha;
Joe Drago9c5f5652020-06-29 15:13:44 -0700939
940 if (firstTile) {
941 firstTile = AVIF_FALSE;
942
943 // Adopt the av1C property of the first av01 tile, so that it can be queried from
944 // the top-level color/alpha item during avifDecoderReset().
945 const avifProperty * srcProp = avifPropertyArrayFind(&item->properties, "av1C");
946 if (!srcProp) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700947 avifDiagnosticsPrintf(data->diag, "Grid image's first tile is missing an av1C property");
Joe Drago9c5f5652020-06-29 15:13:44 -0700948 return AVIF_FALSE;
949 }
950 avifProperty * dstProp = (avifProperty *)avifArrayPushPtr(&gridItem->properties);
951 memcpy(dstProp, srcProp, sizeof(avifProperty));
952 }
Joe Drago060d5342020-03-03 10:53:49 -0800953 }
954 }
955 return AVIF_TRUE;
956}
957
Joe Drago800b47f2020-03-18 16:22:37 -0700958static avifBool avifDecoderDataFillImageGrid(avifDecoderData * data,
959 avifImageGrid * grid,
960 avifImage * dstImage,
961 unsigned int firstTileIndex,
962 unsigned int tileCount,
963 avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -0800964{
965 if (tileCount == 0) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700966 avifDiagnosticsPrintf(data->diag, "Cannot fill grid image, no tiles");
Joe Drago060d5342020-03-03 10:53:49 -0800967 return AVIF_FALSE;
968 }
969
970 avifTile * firstTile = &data->tiles.tile[firstTileIndex];
Joe Dragoa0da4a42020-05-08 14:27:40 -0700971 avifBool firstTileUVPresent = (firstTile->image->yuvPlanes[AVIF_CHAN_U] && firstTile->image->yuvPlanes[AVIF_CHAN_V]);
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +0900972
Joe Dragoa0da4a42020-05-08 14:27:40 -0700973 // Check for tile consistency: All tiles in a grid image should match in the properties checked below.
Joe Drago060d5342020-03-03 10:53:49 -0800974 for (unsigned int i = 1; i < tileCount; ++i) {
975 avifTile * tile = &data->tiles.tile[firstTileIndex + i];
Joe Drago951a0022020-03-09 16:19:44 -0700976 avifBool uvPresent = (tile->image->yuvPlanes[AVIF_CHAN_U] && tile->image->yuvPlanes[AVIF_CHAN_V]);
Joe Dragoa0da4a42020-05-08 14:27:40 -0700977 if ((tile->image->width != firstTile->image->width) || (tile->image->height != firstTile->image->height) ||
978 (tile->image->depth != firstTile->image->depth) || (tile->image->yuvFormat != firstTile->image->yuvFormat) ||
979 (tile->image->yuvRange != firstTile->image->yuvRange) || (uvPresent != firstTileUVPresent) ||
wantehchangbc35a5f2020-08-12 15:27:39 -0700980 (tile->image->colorPrimaries != firstTile->image->colorPrimaries) ||
981 (tile->image->transferCharacteristics != firstTile->image->transferCharacteristics) ||
Wan-Teh Chang5bdead52020-08-12 17:02:22 -0700982 (tile->image->matrixCoefficients != firstTile->image->matrixCoefficients) ||
983 (tile->image->alphaRange != firstTile->image->alphaRange)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700984 avifDiagnosticsPrintf(data->diag, "Grid image contains mismatched tiles");
Joe Drago060d5342020-03-03 10:53:49 -0800985 return AVIF_FALSE;
986 }
987 }
988
wantehchangdf586a82020-08-12 13:06:08 -0700989 // Validate grid image size and tile size.
990 //
991 // HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1:
TYTYb587c592020-12-08 17:42:18 +0800992 // The tiled input images shall completely "cover" the reconstructed image grid canvas, ...
wantehchangbc35a5f2020-08-12 15:27:39 -0700993 if (((firstTile->image->width * grid->columns) < grid->outputWidth) ||
994 ((firstTile->image->height * grid->rows) < grid->outputHeight)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700995 avifDiagnosticsPrintf(data->diag,
996 "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 -0700997 return AVIF_FALSE;
998 }
999 // 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 -07001000 if (((firstTile->image->width * (grid->columns - 1)) >= grid->outputWidth) ||
1001 ((firstTile->image->height * (grid->rows - 1)) >= grid->outputHeight)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001002 avifDiagnosticsPrintf(data->diag,
1003 "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 -07001004 return AVIF_FALSE;
1005 }
1006 // Check the restrictions in MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2.
1007 //
1008 // The tile_width shall be greater than or equal to 64, and the tile_height shall be greater than or equal to 64.
wantehchangbc35a5f2020-08-12 15:27:39 -07001009 if ((firstTile->image->width < 64) || (firstTile->image->height < 64)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001010 avifDiagnosticsPrintf(data->diag,
1011 "Grid image tiles are smaller than 64x64 (%u/%u). See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2",
1012 firstTile->image->width,
1013 firstTile->image->height);
wantehchangdf586a82020-08-12 13:06:08 -07001014 return AVIF_FALSE;
1015 }
1016 if (!alpha) {
wantehchangbc35a5f2020-08-12 15:27:39 -07001017 if ((firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) || (firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420)) {
wantehchangdf586a82020-08-12 13:06:08 -07001018 // The horizontal tile offsets and widths, and the output width, shall be even numbers.
wantehchangbc35a5f2020-08-12 15:27:39 -07001019 if (((firstTile->image->width & 1) != 0) || ((grid->outputWidth & 1) != 0)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001020 avifDiagnosticsPrintf(data->diag,
1021 "Grid image horizontal tile offsets and widths [%u], and the output width [%u], shall be even numbers.",
1022 firstTile->image->width,
1023 grid->outputWidth);
wantehchangdf586a82020-08-12 13:06:08 -07001024 return AVIF_FALSE;
1025 }
1026 }
1027 if (firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
1028 // The vertical tile offsets and heights, and the output height, shall be even numbers.
wantehchangbc35a5f2020-08-12 15:27:39 -07001029 if (((firstTile->image->height & 1) != 0) || ((grid->outputHeight & 1) != 0)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001030 avifDiagnosticsPrintf(data->diag,
1031 "Grid image vertical tile offsets and heights [%u], and the output height [%u], shall be even numbers.",
1032 firstTile->image->height,
1033 grid->outputHeight);
wantehchangdf586a82020-08-12 13:06:08 -07001034 return AVIF_FALSE;
1035 }
1036 }
1037 }
1038
Joe Dragoa0da4a42020-05-08 14:27:40 -07001039 // Lazily populate dstImage with the new frame's properties. If we're decoding alpha,
1040 // these values must already match.
1041 if ((dstImage->width != grid->outputWidth) || (dstImage->height != grid->outputHeight) ||
Wan-Teh Chang5bdead52020-08-12 17:02:22 -07001042 (dstImage->depth != firstTile->image->depth) || (!alpha && (dstImage->yuvFormat != firstTile->image->yuvFormat))) {
Joe Drago060d5342020-03-03 10:53:49 -08001043 if (alpha) {
1044 // Alpha doesn't match size, just bail out
Joe Drago4d5f4a42021-04-28 13:11:45 -07001045 avifDiagnosticsPrintf(data->diag, "Alpha plane dimensions do not match color plane dimensions");
Joe Drago060d5342020-03-03 10:53:49 -08001046 return AVIF_FALSE;
1047 }
1048
1049 avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
1050 dstImage->width = grid->outputWidth;
1051 dstImage->height = grid->outputHeight;
Joe Dragoa0da4a42020-05-08 14:27:40 -07001052 dstImage->depth = firstTile->image->depth;
1053 dstImage->yuvFormat = firstTile->image->yuvFormat;
Joe Dragoc00d5832020-08-13 16:03:28 -07001054 dstImage->yuvRange = firstTile->image->yuvRange;
Joe Dragoa0da4a42020-05-08 14:27:40 -07001055 if (!data->cicpSet) {
1056 data->cicpSet = AVIF_TRUE;
Joe Dragoc00d5832020-08-13 16:03:28 -07001057 dstImage->colorPrimaries = firstTile->image->colorPrimaries;
1058 dstImage->transferCharacteristics = firstTile->image->transferCharacteristics;
1059 dstImage->matrixCoefficients = firstTile->image->matrixCoefficients;
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09001060 }
Joe Drago060d5342020-03-03 10:53:49 -08001061 }
Joe Dragod0eeb182020-05-18 17:23:48 -07001062 if (alpha) {
1063 dstImage->alphaRange = firstTile->image->alphaRange;
1064 }
Joe Drago060d5342020-03-03 10:53:49 -08001065
1066 avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV);
1067
1068 avifPixelFormatInfo formatInfo;
Joe Drago7a249f52020-08-13 12:58:03 -07001069 avifGetPixelFormatInfo(firstTile->image->yuvFormat, &formatInfo);
Joe Drago060d5342020-03-03 10:53:49 -08001070
1071 unsigned int tileIndex = firstTileIndex;
1072 size_t pixelBytes = avifImageUsesU16(dstImage) ? 2 : 1;
1073 for (unsigned int rowIndex = 0; rowIndex < grid->rows; ++rowIndex) {
1074 for (unsigned int colIndex = 0; colIndex < grid->columns; ++colIndex, ++tileIndex) {
1075 avifTile * tile = &data->tiles.tile[tileIndex];
1076
Joe Dragoa0da4a42020-05-08 14:27:40 -07001077 unsigned int widthToCopy = firstTile->image->width;
1078 unsigned int maxX = firstTile->image->width * (colIndex + 1);
Joe Drago060d5342020-03-03 10:53:49 -08001079 if (maxX > grid->outputWidth) {
1080 widthToCopy -= maxX - grid->outputWidth;
1081 }
1082
Joe Dragoa0da4a42020-05-08 14:27:40 -07001083 unsigned int heightToCopy = firstTile->image->height;
1084 unsigned int maxY = firstTile->image->height * (rowIndex + 1);
Joe Drago060d5342020-03-03 10:53:49 -08001085 if (maxY > grid->outputHeight) {
1086 heightToCopy -= maxY - grid->outputHeight;
1087 }
1088
1089 // Y and A channels
Joe Dragodb1009a2021-03-16 15:26:14 -07001090 size_t yaColOffset = (size_t)colIndex * firstTile->image->width;
1091 size_t yaRowOffset = (size_t)rowIndex * firstTile->image->height;
Joe Drago060d5342020-03-03 10:53:49 -08001092 size_t yaRowBytes = widthToCopy * pixelBytes;
1093
1094 if (alpha) {
1095 // A
1096 for (unsigned int j = 0; j < heightToCopy; ++j) {
1097 uint8_t * src = &tile->image->alphaPlane[j * tile->image->alphaRowBytes];
1098 uint8_t * dst = &dstImage->alphaPlane[(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->alphaRowBytes)];
1099 memcpy(dst, src, yaRowBytes);
1100 }
1101 } else {
1102 // Y
1103 for (unsigned int j = 0; j < heightToCopy; ++j) {
1104 uint8_t * src = &tile->image->yuvPlanes[AVIF_CHAN_Y][j * tile->image->yuvRowBytes[AVIF_CHAN_Y]];
1105 uint8_t * dst =
1106 &dstImage->yuvPlanes[AVIF_CHAN_Y][(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_Y])];
1107 memcpy(dst, src, yaRowBytes);
1108 }
1109
Joe Dragoa0da4a42020-05-08 14:27:40 -07001110 if (!firstTileUVPresent) {
Joe Drago060d5342020-03-03 10:53:49 -08001111 continue;
1112 }
1113
1114 // UV
Joe Drago060d5342020-03-03 10:53:49 -08001115 heightToCopy >>= formatInfo.chromaShiftY;
1116 size_t uvColOffset = yaColOffset >> formatInfo.chromaShiftX;
1117 size_t uvRowOffset = yaRowOffset >> formatInfo.chromaShiftY;
1118 size_t uvRowBytes = yaRowBytes >> formatInfo.chromaShiftX;
1119 for (unsigned int j = 0; j < heightToCopy; ++j) {
1120 uint8_t * srcU = &tile->image->yuvPlanes[AVIF_CHAN_U][j * tile->image->yuvRowBytes[AVIF_CHAN_U]];
1121 uint8_t * dstU =
1122 &dstImage->yuvPlanes[AVIF_CHAN_U][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_U])];
1123 memcpy(dstU, srcU, uvRowBytes);
1124
1125 uint8_t * srcV = &tile->image->yuvPlanes[AVIF_CHAN_V][j * tile->image->yuvRowBytes[AVIF_CHAN_V]];
1126 uint8_t * dstV =
1127 &dstImage->yuvPlanes[AVIF_CHAN_V][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_V])];
1128 memcpy(dstV, srcV, uvRowBytes);
1129 }
1130 }
1131 }
1132 }
1133
1134 return AVIF_TRUE;
1135}
1136
Joe Drago39267fd2020-06-19 15:21:14 -07001137// If colorId == 0 (a sentinel value as item IDs must be nonzero), accept any found EXIF/XMP metadata. Passing in 0
1138// 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 -07001139// inside of a trak box are implicitly associated to the track.
Joe Dragobe4cbb92020-09-21 12:14:05 -07001140static avifResult avifDecoderFindMetadata(avifDecoder * decoder, avifMeta * meta, avifImage * image, uint32_t colorId)
Joe Dragoa72da5b2020-06-15 19:40:17 -07001141{
Joe Dragobe4cbb92020-09-21 12:14:05 -07001142 if (decoder->ignoreExif && decoder->ignoreXMP) {
1143 // Nothing to do!
1144 return AVIF_RESULT_OK;
1145 }
Joe Dragoa72da5b2020-06-15 19:40:17 -07001146
1147 for (uint32_t itemIndex = 0; itemIndex < meta->items.count; ++itemIndex) {
1148 avifDecoderItem * item = &meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07001149 if (!item->size) {
1150 continue;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001151 }
1152 if (item->hasUnsupportedEssentialProperty) {
1153 // An essential property isn't supported by libavif; ignore the item.
1154 continue;
1155 }
1156
1157 if ((colorId > 0) && (item->descForID != colorId)) {
1158 // Not a content description (metadata) for the colorOBU, skip it
1159 continue;
1160 }
1161
Joe Dragobe4cbb92020-09-21 12:14:05 -07001162 if (!decoder->ignoreExif && !memcmp(item->type, "Exif", 4)) {
1163 avifROData exifContents;
Joe Drago618ed5f2021-05-04 12:13:24 -07001164 avifResult readResult = avifDecoderItemRead(item, decoder->io, &exifContents, 0, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001165 if (readResult != AVIF_RESULT_OK) {
1166 return readResult;
1167 }
1168
Joe Dragoa72da5b2020-06-15 19:40:17 -07001169 // Advance past Annex A.2.1's header
Joe Drago618ed5f2021-05-04 12:13:24 -07001170 BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size, &decoder->diag, "Exif header");
Joe Dragoa72da5b2020-06-15 19:40:17 -07001171 uint32_t exifTiffHeaderOffset;
Wan-Teh Changf1d60192020-10-07 16:23:08 -07001172 CHECKERR(avifROStreamReadU32(&exifBoxStream, &exifTiffHeaderOffset), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) exif_tiff_header_offset;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001173
Joe Dragobe4cbb92020-09-21 12:14:05 -07001174 avifImageSetMetadataExif(image, avifROStreamCurrent(&exifBoxStream), avifROStreamRemainingBytes(&exifBoxStream));
1175 } else if (!decoder->ignoreXMP && !memcmp(item->type, "mime", 4) &&
1176 !memcmp(item->contentType.contentType, xmpContentType, xmpContentTypeSize)) {
1177 avifROData xmpContents;
Joe Drago618ed5f2021-05-04 12:13:24 -07001178 avifResult readResult = avifDecoderItemRead(item, decoder->io, &xmpContents, 0, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001179 if (readResult != AVIF_RESULT_OK) {
1180 return readResult;
1181 }
1182
1183 avifImageSetMetadataXMP(image, xmpContents.data, xmpContents.size);
Joe Dragoa72da5b2020-06-15 19:40:17 -07001184 }
1185 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001186 return AVIF_RESULT_OK;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001187}
1188
Joe Drago8f7a3002019-02-07 19:35:37 -08001189// ---------------------------------------------------------------------------
Joe Dragocd1e4c32019-02-08 11:26:31 -08001190// URN
1191
Joe Dragoa72da5b2020-06-15 19:40:17 -07001192static avifBool isAlphaURN(const char * urn)
Joe Dragocd1e4c32019-02-08 11:26:31 -08001193{
wantehchang3fde0d02020-03-10 23:58:32 -07001194 return !strcmp(urn, URN_ALPHA0) || !strcmp(urn, URN_ALPHA1);
Joe Dragocd1e4c32019-02-08 11:26:31 -08001195}
1196
1197// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08001198// BMFF Parsing
1199
Joe Drago4d5f4a42021-04-28 13:11:45 -07001200static avifBool avifParseHandlerBox(const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoe0185182021-03-31 08:14:51 -07001201{
Joe Drago618ed5f2021-05-04 12:13:24 -07001202 BEGIN_STREAM(s, raw, rawLen, diag, "Box[hdlr]");
Joe Dragoe0185182021-03-31 08:14:51 -07001203
1204 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1205
1206 uint32_t predefined;
1207 CHECK(avifROStreamReadU32(&s, &predefined)); // unsigned int(32) pre_defined = 0;
1208 if (predefined != 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001209 avifDiagnosticsPrintf(diag, "Box[hdlr] contains a pre_defined value that is nonzero");
Joe Dragoe0185182021-03-31 08:14:51 -07001210 return AVIF_FALSE;
1211 }
1212
1213 uint8_t handlerType[4];
1214 CHECK(avifROStreamRead(&s, handlerType, 4)); // unsigned int(32) handler_type;
1215 if (memcmp(handlerType, "pict", 4) != 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001216 avifDiagnosticsPrintf(diag, "Box[hdlr] handler_type is not 'pict'");
Joe Dragoe0185182021-03-31 08:14:51 -07001217 return AVIF_FALSE;
1218 }
1219
1220 for (int i = 0; i < 3; ++i) {
1221 uint32_t reserved;
1222 CHECK(avifROStreamReadU32(&s, &reserved)); // const unsigned int(32)[3] reserved = 0;
1223 }
1224
1225 // Verify that a valid string is here, but don't bother to store it
1226 CHECK(avifROStreamReadString(&s, NULL, 0)); // string name;
1227 return AVIF_TRUE;
1228}
1229
Joe Drago4d5f4a42021-04-28 13:11:45 -07001230static avifBool avifParseItemLocationBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago444f0512019-01-23 17:03:24 -08001231{
Joe Drago618ed5f2021-05-04 12:13:24 -07001232 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iloc]");
Joe Drago444f0512019-01-23 17:03:24 -08001233
Joe Dragof6a42272019-11-21 15:21:41 -08001234 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001235 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragof6a42272019-11-21 15:21:41 -08001236 if (version > 2) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001237 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported version [%u]", version);
Joe Dragof6a42272019-11-21 15:21:41 -08001238 return AVIF_FALSE;
1239 }
Joe Drago444f0512019-01-23 17:03:24 -08001240
Joe Drago8f7a3002019-02-07 19:35:37 -08001241 uint8_t offsetSizeAndLengthSize;
Joe Drago345aaa12019-09-25 13:42:12 -07001242 CHECK(avifROStreamRead(&s, &offsetSizeAndLengthSize, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001243 uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
1244 uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
Joe Drago444f0512019-01-23 17:03:24 -08001245
Joe Dragof6a42272019-11-21 15:21:41 -08001246 uint8_t baseOffsetSizeAndIndexSize;
1247 CHECK(avifROStreamRead(&s, &baseOffsetSizeAndIndexSize, 1));
1248 uint8_t baseOffsetSize = (baseOffsetSizeAndIndexSize >> 4) & 0xf; // unsigned int(4) base_offset_size;
1249 uint8_t indexSize = 0;
1250 if ((version == 1) || (version == 2)) {
1251 indexSize = baseOffsetSizeAndIndexSize & 0xf; // unsigned int(4) index_size;
1252 if (indexSize != 0) {
1253 // extent_index unsupported
Joe Drago618ed5f2021-05-04 12:13:24 -07001254 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported extent_index");
Joe Dragof6a42272019-11-21 15:21:41 -08001255 return AVIF_FALSE;
1256 }
1257 }
Joe Drago444f0512019-01-23 17:03:24 -08001258
Joe Dragof6a42272019-11-21 15:21:41 -08001259 uint16_t tmp16;
1260 uint32_t itemCount;
1261 if (version < 2) {
1262 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_count;
1263 itemCount = tmp16;
1264 } else {
1265 CHECK(avifROStreamReadU32(&s, &itemCount)); // unsigned int(32) item_count;
1266 }
1267 for (uint32_t i = 0; i < itemCount; ++i) {
1268 uint32_t itemID;
1269 uint32_t idatID = 0;
1270 if (version < 2) {
1271 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
1272 itemID = tmp16;
1273 } else {
1274 CHECK(avifROStreamReadU32(&s, &itemID)); // unsigned int(32) item_ID;
1275 }
1276
1277 if ((version == 1) || (version == 2)) {
1278 uint8_t ignored;
1279 uint8_t constructionMethod;
1280 CHECK(avifROStreamRead(&s, &ignored, 1)); // unsigned int(12) reserved = 0;
1281 CHECK(avifROStreamRead(&s, &constructionMethod, 1)); // unsigned int(4) construction_method;
1282 constructionMethod = constructionMethod & 0xf;
1283 if ((constructionMethod != 0 /* file */) && (constructionMethod != 1 /* idat */)) {
1284 // construction method item(2) unsupported
Joe Drago618ed5f2021-05-04 12:13:24 -07001285 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported construction method [%u]", constructionMethod);
Joe Dragof6a42272019-11-21 15:21:41 -08001286 return AVIF_FALSE;
1287 }
1288 if (constructionMethod == 1) {
Joe Dragoba1eb492020-06-22 17:05:04 -07001289 idatID = meta->idatID;
Joe Dragof6a42272019-11-21 15:21:41 -08001290 }
1291 }
1292
Joe Dragoa4956902020-08-28 01:24:35 -07001293 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
1294 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001295 avifDiagnosticsPrintf(diag, "Box[iloc] has an invalid item ID [%u]", itemID);
Joe Dragoa4956902020-08-28 01:24:35 -07001296 return AVIF_FALSE;
1297 }
Joe Drago8f439092020-08-28 15:05:17 -07001298 if (item->extents.count > 0) {
1299 // This item has already been given extents via this iloc box. This is invalid.
Joe Drago4d5f4a42021-04-28 13:11:45 -07001300 avifDiagnosticsPrintf(diag, "Item ID [%u] contains duplicate sets of extents", itemID);
Joe Drago8f439092020-08-28 15:05:17 -07001301 return AVIF_FALSE;
1302 }
Joe Dragoa4956902020-08-28 01:24:35 -07001303 item->idatID = idatID;
1304
Joe Drago345aaa12019-09-25 13:42:12 -07001305 uint16_t dataReferenceIndex; // unsigned int(16) data_ref rence_index;
1306 CHECK(avifROStreamReadU16(&s, &dataReferenceIndex)); //
1307 uint64_t baseOffset; // unsigned int(base_offset_size*8) base_offset;
1308 CHECK(avifROStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
1309 uint16_t extentCount; // unsigned int(16) extent_count;
1310 CHECK(avifROStreamReadU16(&s, &extentCount)); //
Joe Dragoa4956902020-08-28 01:24:35 -07001311 for (int extentIter = 0; extentIter < extentCount; ++extentIter) {
Joe Dragof6a42272019-11-21 15:21:41 -08001312 // If extent_index is ever supported, this spec must be implemented here:
1313 // :: if (((version == 1) || (version == 2)) && (index_size > 0)) {
1314 // :: unsigned int(index_size*8) extent_index;
1315 // :: }
1316
Joe Drago8f7a3002019-02-07 19:35:37 -08001317 uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
Joe Drago345aaa12019-09-25 13:42:12 -07001318 CHECK(avifROStreamReadUX8(&s, &extentOffset, offsetSize));
Joe Drago8f7a3002019-02-07 19:35:37 -08001319 uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
Joe Drago345aaa12019-09-25 13:42:12 -07001320 CHECK(avifROStreamReadUX8(&s, &extentLength, lengthSize));
Joe Drago444f0512019-01-23 17:03:24 -08001321
Joe Drago4bcdfde2020-11-13 17:50:55 -08001322 avifExtent * extent = (avifExtent *)avifArrayPushPtr(&item->extents);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001323 if (extentOffset > UINT64_MAX - baseOffset) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001324 avifDiagnosticsPrintf(diag,
1325 "Item ID [%u] contains an extent offset which overflows: [base: %" PRIu64 " offset:%" PRIu64 "]",
1326 itemID,
1327 baseOffset,
1328 extentOffset);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001329 return AVIF_FALSE;
1330 }
1331 uint64_t offset = baseOffset + extentOffset;
Joe Drago217056b2020-11-13 16:19:35 -08001332 extent->offset = offset;
1333 if (extentLength > SIZE_MAX) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001334 avifDiagnosticsPrintf(diag, "Item ID [%u] contains an extent length which overflows: [%" PRIu64 "]", itemID, extentLength);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001335 return AVIF_FALSE;
1336 }
Joe Drago217056b2020-11-13 16:19:35 -08001337 extent->size = (size_t)extentLength;
1338 if (extent->size > SIZE_MAX - item->size) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001339 avifDiagnosticsPrintf(diag,
Joe Drago48867292021-05-04 13:23:08 -07001340 "Item ID [%u] contains an extent length which overflows the item size: [%zu, %zu]",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001341 itemID,
1342 extent->size,
1343 item->size);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001344 return AVIF_FALSE;
1345 }
Joe Dragoa4956902020-08-28 01:24:35 -07001346 item->size += extent->size;
Joe Drago444f0512019-01-23 17:03:24 -08001347 }
1348 }
1349 return AVIF_TRUE;
1350}
1351
Joe Drago4d5f4a42021-04-28 13:11:45 -07001352static avifBool avifParseImageGridBox(avifImageGrid * grid, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago060d5342020-03-03 10:53:49 -08001353{
Joe Drago618ed5f2021-05-04 12:13:24 -07001354 BEGIN_STREAM(s, raw, rawLen, diag, "Box[grid]");
Joe Drago060d5342020-03-03 10:53:49 -08001355
1356 uint8_t version, flags;
1357 CHECK(avifROStreamRead(&s, &version, 1)); // unsigned int(8) version = 0;
1358 if (version != 0) {
Joe Dragoba4d67d2021-05-04 12:34:37 -07001359 avifDiagnosticsPrintf(diag, "Box[grid] has unsupported version [%u]", version);
Joe Drago060d5342020-03-03 10:53:49 -08001360 return AVIF_FALSE;
1361 }
Joe Drago79ebcf32020-11-18 22:37:10 -08001362 uint8_t rowsMinusOne, columnsMinusOne;
1363 CHECK(avifROStreamRead(&s, &flags, 1)); // unsigned int(8) flags;
1364 CHECK(avifROStreamRead(&s, &rowsMinusOne, 1)); // unsigned int(8) rows_minus_one;
1365 CHECK(avifROStreamRead(&s, &columnsMinusOne, 1)); // unsigned int(8) columns_minus_one;
1366 grid->rows = (uint32_t)rowsMinusOne + 1;
1367 grid->columns = (uint32_t)columnsMinusOne + 1;
Joe Drago060d5342020-03-03 10:53:49 -08001368
1369 uint32_t fieldLength = ((flags & 1) + 1) * 16;
1370 if (fieldLength == 16) {
1371 uint16_t outputWidth16, outputHeight16;
1372 CHECK(avifROStreamReadU16(&s, &outputWidth16)); // unsigned int(FieldLength) output_width;
1373 CHECK(avifROStreamReadU16(&s, &outputHeight16)); // unsigned int(FieldLength) output_height;
1374 grid->outputWidth = outputWidth16;
1375 grid->outputHeight = outputHeight16;
1376 } else {
1377 if (fieldLength != 32) {
1378 // This should be impossible
Joe Drago4d5f4a42021-04-28 13:11:45 -07001379 avifDiagnosticsPrintf(diag, "Grid box contains illegal field length: [%u]", fieldLength);
Joe Drago060d5342020-03-03 10:53:49 -08001380 return AVIF_FALSE;
1381 }
1382 CHECK(avifROStreamReadU32(&s, &grid->outputWidth)); // unsigned int(FieldLength) output_width;
1383 CHECK(avifROStreamReadU32(&s, &grid->outputHeight)); // unsigned int(FieldLength) output_height;
1384 }
wantehchangbc35a5f2020-08-12 15:27:39 -07001385 if ((grid->outputWidth == 0) || (grid->outputHeight == 0) || (grid->outputWidth > (AVIF_MAX_IMAGE_SIZE / grid->outputHeight))) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001386 avifDiagnosticsPrintf(diag, "Grid box contains illegal dimensions: [%u x %u]", grid->outputWidth, grid->outputHeight);
Wan-Teh Chang0a8e7242020-08-10 13:24:59 -07001387 return AVIF_FALSE;
1388 }
wantehchangae2074b2020-08-12 13:02:27 -07001389 return avifROStreamRemainingBytes(&s) == 0;
Joe Drago060d5342020-03-03 10:53:49 -08001390}
1391
Joe Drago618ed5f2021-05-04 12:13:24 -07001392static avifBool avifParseImageSpatialExtentsProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001393{
Joe Drago618ed5f2021-05-04 12:13:24 -07001394 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ispe]");
Joe Drago345aaa12019-09-25 13:42:12 -07001395 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08001396
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001397 avifImageSpatialExtents * ispe = &prop->u.ispe;
1398 CHECK(avifROStreamReadU32(&s, &ispe->width));
1399 CHECK(avifROStreamReadU32(&s, &ispe->height));
Joe Drago8f7a3002019-02-07 19:35:37 -08001400 return AVIF_TRUE;
1401}
1402
Joe Drago618ed5f2021-05-04 12:13:24 -07001403static avifBool avifParseAuxiliaryTypeProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragocd1e4c32019-02-08 11:26:31 -08001404{
Joe Drago618ed5f2021-05-04 12:13:24 -07001405 BEGIN_STREAM(s, raw, rawLen, diag, "Box[auxC]");
Joe Drago345aaa12019-09-25 13:42:12 -07001406 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001407
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001408 CHECK(avifROStreamReadString(&s, prop->u.auxC.auxType, AUXTYPE_SIZE));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001409 return AVIF_TRUE;
1410}
1411
Joe Drago618ed5f2021-05-04 12:13:24 -07001412static avifBool avifParseColourInformationBox(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago41eb62b2019-02-08 15:38:18 -08001413{
Joe Drago618ed5f2021-05-04 12:13:24 -07001414 BEGIN_STREAM(s, raw, rawLen, diag, "Box[colr]");
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001415
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001416 avifColourInformationBox * colr = &prop->u.colr;
1417 colr->hasICC = AVIF_FALSE;
1418 colr->hasNCLX = AVIF_FALSE;
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001419
Joe Dragoa0da4a42020-05-08 14:27:40 -07001420 uint8_t colorType[4]; // unsigned int(32) colour_type;
1421 CHECK(avifROStreamRead(&s, colorType, 4));
1422 if (!memcmp(colorType, "rICC", 4) || !memcmp(colorType, "prof", 4)) {
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001423 colr->hasICC = AVIF_TRUE;
1424 colr->icc = avifROStreamCurrent(&s);
1425 colr->iccSize = avifROStreamRemainingBytes(&s);
Joe Dragoa0da4a42020-05-08 14:27:40 -07001426 } else if (!memcmp(colorType, "nclx", 4)) {
Joe Dragofb5a5f02021-01-31 20:43:57 -08001427 CHECK(avifROStreamReadU16(&s, &colr->colorPrimaries)); // unsigned int(16) colour_primaries;
1428 CHECK(avifROStreamReadU16(&s, &colr->transferCharacteristics)); // unsigned int(16) transfer_characteristics;
1429 CHECK(avifROStreamReadU16(&s, &colr->matrixCoefficients)); // unsigned int(16) matrix_coefficients;
Joe Drago97b071c2019-07-17 14:24:56 -07001430 // unsigned int(1) full_range_flag;
1431 // unsigned int(7) reserved = 0;
Joe Drago74cd1c92020-04-16 12:17:11 -07001432 uint8_t tmp8;
1433 CHECK(avifROStreamRead(&s, &tmp8, 1));
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001434 colr->range = (tmp8 & 0x80) ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED;
1435 colr->hasNCLX = AVIF_TRUE;
Joe Drago41eb62b2019-02-08 15:38:18 -08001436 }
1437 return AVIF_TRUE;
1438}
1439
Joe Drago4d5f4a42021-04-28 13:11:45 -07001440static avifBool avifParseAV1CodecConfigurationBox(const uint8_t * raw, size_t rawLen, avifCodecConfigurationBox * av1C, avifDiagnostics * diag)
Joe Drago6500fd62019-10-08 17:17:34 -07001441{
Joe Drago618ed5f2021-05-04 12:13:24 -07001442 BEGIN_STREAM(s, raw, rawLen, diag, "Box[av1C]");
Joe Drago6500fd62019-10-08 17:17:34 -07001443
1444 uint8_t markerAndVersion = 0;
1445 CHECK(avifROStreamRead(&s, &markerAndVersion, 1));
1446 uint8_t seqProfileAndIndex = 0;
1447 CHECK(avifROStreamRead(&s, &seqProfileAndIndex, 1));
1448 uint8_t rawFlags = 0;
1449 CHECK(avifROStreamRead(&s, &rawFlags, 1));
1450
1451 if (markerAndVersion != 0x81) {
1452 // Marker and version must both == 1
Joe Drago4d5f4a42021-04-28 13:11:45 -07001453 avifDiagnosticsPrintf(diag, "av1C contains illegal marker and version pair: [%u]", markerAndVersion);
Joe Drago6500fd62019-10-08 17:17:34 -07001454 return AVIF_FALSE;
1455 }
1456
1457 av1C->seqProfile = (seqProfileAndIndex >> 5) & 0x7; // unsigned int (3) seq_profile;
1458 av1C->seqLevelIdx0 = (seqProfileAndIndex >> 0) & 0x1f; // unsigned int (5) seq_level_idx_0;
1459 av1C->seqTier0 = (rawFlags >> 7) & 0x1; // unsigned int (1) seq_tier_0;
1460 av1C->highBitdepth = (rawFlags >> 6) & 0x1; // unsigned int (1) high_bitdepth;
1461 av1C->twelveBit = (rawFlags >> 5) & 0x1; // unsigned int (1) twelve_bit;
1462 av1C->monochrome = (rawFlags >> 4) & 0x1; // unsigned int (1) monochrome;
1463 av1C->chromaSubsamplingX = (rawFlags >> 3) & 0x1; // unsigned int (1) chroma_subsampling_x;
1464 av1C->chromaSubsamplingY = (rawFlags >> 2) & 0x1; // unsigned int (1) chroma_subsampling_y;
1465 av1C->chromaSamplePosition = (rawFlags >> 0) & 0x3; // unsigned int (2) chroma_sample_position;
1466 return AVIF_TRUE;
1467}
1468
Joe Drago4d5f4a42021-04-28 13:11:45 -07001469static avifBool avifParseAV1CodecConfigurationBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago6500fd62019-10-08 17:17:34 -07001470{
Joe Drago4d5f4a42021-04-28 13:11:45 -07001471 return avifParseAV1CodecConfigurationBox(raw, rawLen, &prop->u.av1C, diag);
Joe Drago6500fd62019-10-08 17:17:34 -07001472}
1473
Joe Drago618ed5f2021-05-04 12:13:24 -07001474static avifBool avifParsePixelAspectRatioBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001475{
Joe Drago618ed5f2021-05-04 12:13:24 -07001476 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pasp]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001477
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001478 avifPixelAspectRatioBox * pasp = &prop->u.pasp;
Joe Drago89f0cc82020-03-09 16:13:27 -07001479 CHECK(avifROStreamReadU32(&s, &pasp->hSpacing)); // unsigned int(32) hSpacing;
1480 CHECK(avifROStreamReadU32(&s, &pasp->vSpacing)); // unsigned int(32) vSpacing;
1481 return AVIF_TRUE;
1482}
1483
Joe Drago618ed5f2021-05-04 12:13:24 -07001484static avifBool avifParseCleanApertureBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001485{
Joe Drago618ed5f2021-05-04 12:13:24 -07001486 BEGIN_STREAM(s, raw, rawLen, diag, "Box[clap]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001487
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001488 avifCleanApertureBox * clap = &prop->u.clap;
Joe Drago89f0cc82020-03-09 16:13:27 -07001489 CHECK(avifROStreamReadU32(&s, &clap->widthN)); // unsigned int(32) cleanApertureWidthN;
1490 CHECK(avifROStreamReadU32(&s, &clap->widthD)); // unsigned int(32) cleanApertureWidthD;
1491 CHECK(avifROStreamReadU32(&s, &clap->heightN)); // unsigned int(32) cleanApertureHeightN;
1492 CHECK(avifROStreamReadU32(&s, &clap->heightD)); // unsigned int(32) cleanApertureHeightD;
1493 CHECK(avifROStreamReadU32(&s, &clap->horizOffN)); // unsigned int(32) horizOffN;
1494 CHECK(avifROStreamReadU32(&s, &clap->horizOffD)); // unsigned int(32) horizOffD;
1495 CHECK(avifROStreamReadU32(&s, &clap->vertOffN)); // unsigned int(32) vertOffN;
1496 CHECK(avifROStreamReadU32(&s, &clap->vertOffD)); // unsigned int(32) vertOffD;
1497 return AVIF_TRUE;
1498}
1499
Joe Drago4d5f4a42021-04-28 13:11:45 -07001500static avifBool avifParseImageRotationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001501{
Joe Drago618ed5f2021-05-04 12:13:24 -07001502 BEGIN_STREAM(s, raw, rawLen, diag, "Box[irot]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001503
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001504 avifImageRotation * irot = &prop->u.irot;
Joe Drago89f0cc82020-03-09 16:13:27 -07001505 CHECK(avifROStreamRead(&s, &irot->angle, 1)); // unsigned int (6) reserved = 0; unsigned int (2) angle;
1506 if ((irot->angle & 0xfc) != 0) {
1507 // reserved bits must be 0
Joe Drago618ed5f2021-05-04 12:13:24 -07001508 avifDiagnosticsPrintf(diag, "Box[irot] contains nonzero reserved bits [%u]", irot->angle);
Joe Drago89f0cc82020-03-09 16:13:27 -07001509 return AVIF_FALSE;
1510 }
1511 return AVIF_TRUE;
1512}
1513
Joe Drago4d5f4a42021-04-28 13:11:45 -07001514static avifBool avifParseImageMirrorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001515{
Joe Drago618ed5f2021-05-04 12:13:24 -07001516 BEGIN_STREAM(s, raw, rawLen, diag, "Box[imir]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001517
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001518 avifImageMirror * imir = &prop->u.imir;
Joe Drago89f0cc82020-03-09 16:13:27 -07001519 CHECK(avifROStreamRead(&s, &imir->axis, 1)); // unsigned int (7) reserved = 0; unsigned int (1) axis;
1520 if ((imir->axis & 0xfe) != 0) {
1521 // reserved bits must be 0
Joe Drago618ed5f2021-05-04 12:13:24 -07001522 avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", imir->axis);
Joe Drago89f0cc82020-03-09 16:13:27 -07001523 return AVIF_FALSE;
1524 }
1525 return AVIF_TRUE;
1526}
1527
Joe Drago4d5f4a42021-04-28 13:11:45 -07001528static avifBool avifParsePixelInformationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago60421562020-04-23 11:32:26 -07001529{
Joe Drago618ed5f2021-05-04 12:13:24 -07001530 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pixi]");
Joe Drago60421562020-04-23 11:32:26 -07001531 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1532
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001533 avifPixelInformationProperty * pixi = &prop->u.pixi;
Joe Drago60421562020-04-23 11:32:26 -07001534 CHECK(avifROStreamRead(&s, &pixi->planeCount, 1)); // unsigned int (8) num_channels;
1535 if (pixi->planeCount > MAX_PIXI_PLANE_DEPTHS) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001536 avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported plane count [%u]", pixi->planeCount);
Joe Drago60421562020-04-23 11:32:26 -07001537 return AVIF_FALSE;
1538 }
1539 for (uint8_t i = 0; i < pixi->planeCount; ++i) {
1540 CHECK(avifROStreamRead(&s, &pixi->planeDepths[i], 1)); // unsigned int (8) bits_per_channel;
1541 }
1542 return AVIF_TRUE;
1543}
1544
Joe Drago4d5f4a42021-04-28 13:11:45 -07001545static avifBool avifParseItemPropertyContainerBox(avifPropertyArray * properties, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001546{
Joe Drago618ed5f2021-05-04 12:13:24 -07001547 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001548
Joe Drago345aaa12019-09-25 13:42:12 -07001549 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001550 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001551 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08001552
Joe Dragoa72da5b2020-06-15 19:40:17 -07001553 int propertyIndex = avifArrayPushIndex(properties);
1554 avifProperty * prop = &properties->prop[propertyIndex];
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001555 memcpy(prop->type, header.type, 4);
Joe Drago8f7a3002019-02-07 19:35:37 -08001556 if (!memcmp(header.type, "ispe", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001557 CHECK(avifParseImageSpatialExtentsProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001558 } else if (!memcmp(header.type, "auxC", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001559 CHECK(avifParseAuxiliaryTypeProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001560 } else if (!memcmp(header.type, "colr", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001561 CHECK(avifParseColourInformationBox(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001562 } else if (!memcmp(header.type, "av1C", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001563 CHECK(avifParseAV1CodecConfigurationBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001564 } else if (!memcmp(header.type, "pasp", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001565 CHECK(avifParsePixelAspectRatioBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001566 } else if (!memcmp(header.type, "clap", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001567 CHECK(avifParseCleanApertureBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001568 } else if (!memcmp(header.type, "irot", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001569 CHECK(avifParseImageRotationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001570 } else if (!memcmp(header.type, "imir", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001571 CHECK(avifParseImageMirrorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001572 } else if (!memcmp(header.type, "pixi", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001573 CHECK(avifParsePixelInformationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Drago60421562020-04-23 11:32:26 -07001574 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001575
Joe Drago345aaa12019-09-25 13:42:12 -07001576 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001577 }
1578 return AVIF_TRUE;
1579}
1580
Joe Drago4d5f4a42021-04-28 13:11:45 -07001581static avifBool avifParseItemPropertyAssociation(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag, uint32_t * outVersionAndFlags)
Joe Drago8f7a3002019-02-07 19:35:37 -08001582{
Joe Dragoc60ccae2021-03-31 10:30:48 -07001583 // NOTE: If this function ever adds support for versions other than [0,1] or flags other than
1584 // [0,1], please increase the value of MAX_IPMA_VERSION_AND_FLAGS_SEEN accordingly.
1585
Joe Drago618ed5f2021-05-04 12:13:24 -07001586 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipma]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001587
1588 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001589 uint32_t flags;
1590 CHECK(avifROStreamReadVersionAndFlags(&s, &version, &flags));
1591 avifBool propertyIndexIsU16 = ((flags & 0x1) != 0);
Wan-Teh Chang33d64622021-04-08 08:49:22 -07001592 *outVersionAndFlags = ((uint32_t)version << 24) | flags;
Joe Drago8f7a3002019-02-07 19:35:37 -08001593
1594 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001595 CHECK(avifROStreamReadU32(&s, &entryCount));
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001596 unsigned int prevItemID = 0;
Joe Drago8f7a3002019-02-07 19:35:37 -08001597 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001598 // ISO/IEC 23008-12, First edition, 2017-12, Section 9.3.1:
1599 // Each ItemPropertyAssociation box shall be ordered by increasing item_ID, and there shall
1600 // be at most one association box for each item_ID, in any ItemPropertyAssociation box.
Joe Drago8f7a3002019-02-07 19:35:37 -08001601 unsigned int itemID;
1602 if (version < 1) {
1603 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001604 CHECK(avifROStreamReadU16(&s, &tmp));
Joe Drago8f7a3002019-02-07 19:35:37 -08001605 itemID = tmp;
1606 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07001607 CHECK(avifROStreamReadU32(&s, &itemID));
Joe Drago8f7a3002019-02-07 19:35:37 -08001608 }
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001609 if (itemID <= prevItemID) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001610 avifDiagnosticsPrintf(diag, "Box[ipma] item IDs are not ordered by increasing ID");
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001611 return AVIF_FALSE;
1612 }
1613 prevItemID = itemID;
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001614
1615 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
1616 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001617 avifDiagnosticsPrintf(diag, "Box[ipma] has an invalid item ID [%u]", itemID);
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001618 return AVIF_FALSE;
1619 }
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001620 if (item->ipmaSeen) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001621 avifDiagnosticsPrintf(diag, "Duplicate Box[ipma] for item ID [%u]", itemID);
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001622 return AVIF_FALSE;
1623 }
1624 item->ipmaSeen = AVIF_TRUE;
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001625
Joe Drago8f7a3002019-02-07 19:35:37 -08001626 uint8_t associationCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001627 CHECK(avifROStreamRead(&s, &associationCount, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001628 for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) {
Joe Drago3320e5f2020-04-21 17:36:27 -07001629 avifBool essential = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08001630 uint16_t propertyIndex = 0;
1631 if (propertyIndexIsU16) {
Joe Drago345aaa12019-09-25 13:42:12 -07001632 CHECK(avifROStreamReadU16(&s, &propertyIndex));
Joe Drago3320e5f2020-04-21 17:36:27 -07001633 essential = ((propertyIndex & 0x8000) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001634 propertyIndex &= 0x7fff;
1635 } else {
1636 uint8_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001637 CHECK(avifROStreamRead(&s, &tmp, 1));
Joe Drago3320e5f2020-04-21 17:36:27 -07001638 essential = ((tmp & 0x80) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001639 propertyIndex = tmp & 0x7f;
1640 }
1641
1642 if (propertyIndex == 0) {
1643 // Not associated with any item
1644 continue;
1645 }
1646 --propertyIndex; // 1-indexed
1647
Joe Drago9f2b87b2020-06-03 19:36:38 -07001648 if (propertyIndex >= meta->properties.count) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001649 avifDiagnosticsPrintf(diag,
Joe Drago618ed5f2021-05-04 12:13:24 -07001650 "Box[ipma] for item ID [%u] contains an illegal property index [%u] (out of [%u] properties)",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001651 itemID,
1652 propertyIndex,
1653 meta->properties.count);
Joe Drago8f7a3002019-02-07 19:35:37 -08001654 return AVIF_FALSE;
1655 }
1656
Joe Dragoa72da5b2020-06-15 19:40:17 -07001657 // Copy property to item
1658 avifProperty * srcProp = &meta->properties.prop[propertyIndex];
1659
1660 static const char * supportedTypes[] = { "ispe", "auxC", "colr", "av1C", "pasp", "clap", "irot", "imir", "pixi" };
1661 size_t supportedTypesCount = sizeof(supportedTypes) / sizeof(supportedTypes[0]);
1662 avifBool supportedType = AVIF_FALSE;
1663 for (size_t i = 0; i < supportedTypesCount; ++i) {
1664 if (!memcmp(srcProp->type, supportedTypes[i], 4)) {
1665 supportedType = AVIF_TRUE;
1666 break;
1667 }
1668 }
1669 if (supportedType) {
1670 avifProperty * dstProp = (avifProperty *)avifArrayPushPtr(&item->properties);
1671 memcpy(dstProp, srcProp, sizeof(avifProperty));
Joe Drago3320e5f2020-04-21 17:36:27 -07001672 } else {
1673 if (essential) {
1674 // Discovered an essential item property that libavif doesn't support!
1675 // Make a note to ignore this item later.
1676 item->hasUnsupportedEssentialProperty = AVIF_TRUE;
1677 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001678 }
1679 }
1680 }
1681
1682 return AVIF_TRUE;
1683}
1684
Joe Drago4d5f4a42021-04-28 13:11:45 -07001685static avifBool avifParsePrimaryItemBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08001686{
Joe Drago9f2b87b2020-06-03 19:36:38 -07001687 if (meta->primaryItemID > 0) {
Joe Dragof6a42272019-11-21 15:21:41 -08001688 // Illegal to have multiple pitm boxes, bail out
Joe Drago618ed5f2021-05-04 12:13:24 -07001689 avifDiagnosticsPrintf(diag, "Multiple boxes of unique Box[pitm] found");
Joe Dragof6a42272019-11-21 15:21:41 -08001690 return AVIF_FALSE;
1691 }
1692
Joe Drago618ed5f2021-05-04 12:13:24 -07001693 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pitm]");
Joe Dragof6a42272019-11-21 15:21:41 -08001694
1695 uint8_t version;
1696 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
1697
1698 if (version == 0) {
1699 uint16_t tmp16;
1700 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
Joe Drago9f2b87b2020-06-03 19:36:38 -07001701 meta->primaryItemID = tmp16;
Joe Dragof6a42272019-11-21 15:21:41 -08001702 } else {
Joe Drago9f2b87b2020-06-03 19:36:38 -07001703 CHECK(avifROStreamReadU32(&s, &meta->primaryItemID)); // unsigned int(32) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -08001704 }
1705 return AVIF_TRUE;
1706}
1707
Joe Drago4d5f4a42021-04-28 13:11:45 -07001708static avifBool avifParseItemDataBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08001709{
Joe Dragof6a42272019-11-21 15:21:41 -08001710 // Check to see if we've already seen an idat box for this meta box. If so, bail out
Joe Drago9f2b87b2020-06-03 19:36:38 -07001711 for (uint32_t i = 0; i < meta->idats.count; ++i) {
Joe Dragoba1eb492020-06-22 17:05:04 -07001712 if (meta->idats.idat[i].id == meta->idatID) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001713 avifDiagnosticsPrintf(diag, "Meta box contains multiple idat boxes");
Joe Dragof6a42272019-11-21 15:21:41 -08001714 return AVIF_FALSE;
1715 }
1716 }
1717
Joe Drago9f2b87b2020-06-03 19:36:38 -07001718 int index = avifArrayPushIndex(&meta->idats);
1719 avifDecoderItemData * idat = &meta->idats.idat[index];
Joe Dragoba1eb492020-06-22 17:05:04 -07001720 idat->id = meta->idatID;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001721 avifRWDataSet(&idat->data, raw, rawLen);
Joe Dragof6a42272019-11-21 15:21:41 -08001722 return AVIF_TRUE;
1723}
1724
Joe Drago4d5f4a42021-04-28 13:11:45 -07001725static avifBool avifParseItemPropertiesBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001726{
Joe Drago618ed5f2021-05-04 12:13:24 -07001727 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001728
1729 avifBoxHeader ipcoHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001730 CHECK(avifROStreamReadBoxHeader(&s, &ipcoHeader));
wantehchangbc35a5f2020-08-12 15:27:39 -07001731 if (memcmp(ipcoHeader.type, "ipco", 4)) {
Joe Dragoba4d67d2021-05-04 12:34:37 -07001732 avifDiagnosticsPrintf(diag, "Failed to find Box[ipco] as the first box in Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001733 return AVIF_FALSE;
1734 }
1735
1736 // Read all item properties inside of ItemPropertyContainerBox
Joe Drago4d5f4a42021-04-28 13:11:45 -07001737 CHECK(avifParseItemPropertyContainerBox(&meta->properties, avifROStreamCurrent(&s), ipcoHeader.size, diag));
Joe Drago345aaa12019-09-25 13:42:12 -07001738 CHECK(avifROStreamSkip(&s, ipcoHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001739
Joe Dragoc60ccae2021-03-31 10:30:48 -07001740 uint32_t versionAndFlagsSeen[MAX_IPMA_VERSION_AND_FLAGS_SEEN];
1741 uint32_t versionAndFlagsSeenCount = 0;
1742
Joe Drago8f7a3002019-02-07 19:35:37 -08001743 // Now read all ItemPropertyAssociation until the end of the box, and make associations
Joe Drago345aaa12019-09-25 13:42:12 -07001744 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001745 avifBoxHeader ipmaHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001746 CHECK(avifROStreamReadBoxHeader(&s, &ipmaHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001747
1748 if (!memcmp(ipmaHeader.type, "ipma", 4)) {
Joe Dragoc60ccae2021-03-31 10:30:48 -07001749 uint32_t versionAndFlags;
Joe Drago4d5f4a42021-04-28 13:11:45 -07001750 CHECK(avifParseItemPropertyAssociation(meta, avifROStreamCurrent(&s), ipmaHeader.size, diag, &versionAndFlags));
Joe Dragoc60ccae2021-03-31 10:30:48 -07001751 for (uint32_t i = 0; i < versionAndFlagsSeenCount; ++i) {
1752 if (versionAndFlagsSeen[i] == versionAndFlags) {
1753 // HEIF (ISO 23008-12:2017) 9.3.1 - There shall be at most one
1754 // ItemPropertyAssociation box with a given pair of values of version and
1755 // flags.
Joe Drago618ed5f2021-05-04 12:13:24 -07001756 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 -07001757 return AVIF_FALSE;
1758 }
1759 }
1760 if (versionAndFlagsSeenCount == MAX_IPMA_VERSION_AND_FLAGS_SEEN) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001761 avifDiagnosticsPrintf(diag, "Exceeded possible count of unique ipma version and flags tuples");
Joe Dragoc60ccae2021-03-31 10:30:48 -07001762 return AVIF_FALSE;
1763 }
1764 versionAndFlagsSeen[versionAndFlagsSeenCount] = versionAndFlags;
1765 ++versionAndFlagsSeenCount;
Joe Drago8f7a3002019-02-07 19:35:37 -08001766 } else {
1767 // These must all be type ipma
Joe Drago618ed5f2021-05-04 12:13:24 -07001768 avifDiagnosticsPrintf(diag, "Box[iprp] contains a box that isn't type 'ipma'");
Joe Drago8f7a3002019-02-07 19:35:37 -08001769 return AVIF_FALSE;
1770 }
1771
Joe Drago345aaa12019-09-25 13:42:12 -07001772 CHECK(avifROStreamSkip(&s, ipmaHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001773 }
1774 return AVIF_TRUE;
1775}
1776
Joe Drago4d5f4a42021-04-28 13:11:45 -07001777static avifBool avifParseItemInfoEntry(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001778{
Joe Drago618ed5f2021-05-04 12:13:24 -07001779 BEGIN_STREAM(s, raw, rawLen, diag, "Box[infe]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001780
Joe Drago345aaa12019-09-25 13:42:12 -07001781 CHECK(avifROStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
Joe Drago8f7a3002019-02-07 19:35:37 -08001782
Joe Drago345aaa12019-09-25 13:42:12 -07001783 uint16_t itemID; // unsigned int(16) item_ID;
1784 CHECK(avifROStreamReadU16(&s, &itemID)); //
1785 uint16_t itemProtectionIndex; // unsigned int(16) item_protection_index;
1786 CHECK(avifROStreamReadU16(&s, &itemProtectionIndex)); //
1787 uint8_t itemType[4]; // unsigned int(32) item_type;
1788 CHECK(avifROStreamRead(&s, itemType, 4)); //
Joe Drago8f7a3002019-02-07 19:35:37 -08001789
Joe Dragof6a42272019-11-21 15:21:41 -08001790 avifContentType contentType;
1791 if (!memcmp(itemType, "mime", 4)) {
1792 CHECK(avifROStreamReadString(&s, NULL, 0)); // string item_name; (skipped)
1793 CHECK(avifROStreamReadString(&s, contentType.contentType, CONTENTTYPE_SIZE)); // string content_type;
1794 } else {
1795 memset(&contentType, 0, sizeof(contentType));
1796 }
1797
Joe Drago9f2b87b2020-06-03 19:36:38 -07001798 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07001799 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001800 avifDiagnosticsPrintf(diag, "Box[infe] has an invalid item ID [%u]", itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07001801 return AVIF_FALSE;
1802 }
1803
Joe Drago05559c92019-07-17 16:33:38 -07001804 memcpy(item->type, itemType, sizeof(itemType));
Joe Dragof6a42272019-11-21 15:21:41 -08001805 memcpy(&item->contentType, &contentType, sizeof(contentType));
Joe Drago8f7a3002019-02-07 19:35:37 -08001806 return AVIF_TRUE;
1807}
1808
Joe Drago4d5f4a42021-04-28 13:11:45 -07001809static avifBool avifParseItemInfoBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001810{
Joe Drago618ed5f2021-05-04 12:13:24 -07001811 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iinf]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001812
1813 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07001814 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08001815 uint32_t entryCount;
1816 if (version == 0) {
1817 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001818 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08001819 entryCount = tmp;
1820 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001821 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08001822 } else {
Joe Dragoba4d67d2021-05-04 12:34:37 -07001823 avifDiagnosticsPrintf(diag, "Box[iinf] has an unsupported version %u", version);
Joe Drago8f7a3002019-02-07 19:35:37 -08001824 return AVIF_FALSE;
1825 }
1826
Joe Drago678b9382019-02-09 03:17:47 -08001827 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001828 avifBoxHeader infeHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001829 CHECK(avifROStreamReadBoxHeader(&s, &infeHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001830
1831 if (!memcmp(infeHeader.type, "infe", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001832 CHECK(avifParseItemInfoEntry(meta, avifROStreamCurrent(&s), infeHeader.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08001833 } else {
Joe Drago618ed5f2021-05-04 12:13:24 -07001834 // These must all be type infe
1835 avifDiagnosticsPrintf(diag, "Box[iinf] contains a box that isn't type 'infe'");
Joe Drago8f7a3002019-02-07 19:35:37 -08001836 return AVIF_FALSE;
1837 }
1838
Joe Drago345aaa12019-09-25 13:42:12 -07001839 CHECK(avifROStreamSkip(&s, infeHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001840 }
1841
1842 return AVIF_TRUE;
1843}
1844
Joe Drago4d5f4a42021-04-28 13:11:45 -07001845static avifBool avifParseItemReferenceBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001846{
Joe Drago618ed5f2021-05-04 12:13:24 -07001847 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iref]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001848
1849 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07001850 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08001851
Joe Drago345aaa12019-09-25 13:42:12 -07001852 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001853 avifBoxHeader irefHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001854 CHECK(avifROStreamReadBoxHeader(&s, &irefHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001855
1856 uint32_t fromID = 0;
1857 if (version == 0) {
1858 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001859 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001860 fromID = tmp;
1861 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001862 CHECK(avifROStreamReadU32(&s, &fromID)); // unsigned int(32) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001863 } else {
1864 // unsupported iref version, skip it
1865 break;
1866 }
1867
1868 uint16_t referenceCount = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07001869 CHECK(avifROStreamReadU16(&s, &referenceCount)); // unsigned int(16) reference_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08001870
1871 for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) {
1872 uint32_t toID = 0;
1873 if (version == 0) {
1874 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001875 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001876 toID = tmp;
1877 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001878 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001879 } else {
1880 // unsupported iref version, skip it
1881 break;
1882 }
1883
1884 // Read this reference as "{fromID} is a {irefType} for {toID}"
1885 if (fromID && toID) {
Joe Drago9f2b87b2020-06-03 19:36:38 -07001886 avifDecoderItem * item = avifMetaFindItem(meta, fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07001887 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001888 avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID [%u]", fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07001889 return AVIF_FALSE;
1890 }
1891
Joe Drago8f7a3002019-02-07 19:35:37 -08001892 if (!memcmp(irefHeader.type, "thmb", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001893 item->thumbnailForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08001894 } else if (!memcmp(irefHeader.type, "auxl", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001895 item->auxForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08001896 } else if (!memcmp(irefHeader.type, "cdsc", 4)) {
Joe Dragof6a42272019-11-21 15:21:41 -08001897 item->descForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08001898 } else if (!memcmp(irefHeader.type, "dimg", 4)) {
Joe Drago060d5342020-03-03 10:53:49 -08001899 // derived images refer in the opposite direction
Joe Drago9f2b87b2020-06-03 19:36:38 -07001900 avifDecoderItem * dimg = avifMetaFindItem(meta, toID);
Joe Drago060d5342020-03-03 10:53:49 -08001901 if (!dimg) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001902 avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID dimg ref [%u]", toID);
Joe Drago060d5342020-03-03 10:53:49 -08001903 return AVIF_FALSE;
1904 }
1905
1906 dimg->dimgForID = fromID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08001907 } else if (!memcmp(irefHeader.type, "prem", 4)) {
Yuan Tonge4850be2021-01-22 14:21:25 +08001908 item->premByID = toID;
1909 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001910 }
1911 }
1912 }
1913
1914 return AVIF_TRUE;
1915}
1916
Joe Drago4d5f4a42021-04-28 13:11:45 -07001917static avifBool avifParseMetaBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001918{
Joe Drago618ed5f2021-05-04 12:13:24 -07001919 BEGIN_STREAM(s, raw, rawLen, diag, "Box[meta]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001920
Joe Drago345aaa12019-09-25 13:42:12 -07001921 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08001922
Joe Dragoba1eb492020-06-22 17:05:04 -07001923 ++meta->idatID; // for tracking idat
Joe Dragof6a42272019-11-21 15:21:41 -08001924
Joe Dragoe0185182021-03-31 08:14:51 -07001925 avifBool firstBox = AVIF_TRUE;
Joe Drago8f439092020-08-28 15:05:17 -07001926 uint32_t uniqueBoxFlags = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07001927 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001928 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001929 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08001930
Joe Dragoe0185182021-03-31 08:14:51 -07001931 if (firstBox) {
1932 if (!memcmp(header.type, "hdlr", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001933 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 0, "meta", "hdlr", diag));
1934 CHECK(avifParseHandlerBox(avifROStreamCurrent(&s), header.size, diag));
Joe Dragoe0185182021-03-31 08:14:51 -07001935 firstBox = AVIF_FALSE;
1936 } else {
1937 // hdlr must be the first box!
Joe Dragoba4d67d2021-05-04 12:34:37 -07001938 avifDiagnosticsPrintf(diag, "Box[meta] does not have a Box[hdlr] as its first child box");
Joe Dragoe0185182021-03-31 08:14:51 -07001939 return AVIF_FALSE;
1940 }
1941 } else if (!memcmp(header.type, "iloc", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001942 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 1, "meta", "iloc", diag));
1943 CHECK(avifParseItemLocationBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Dragof6a42272019-11-21 15:21:41 -08001944 } else if (!memcmp(header.type, "pitm", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001945 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 2, "meta", "pitm", diag));
1946 CHECK(avifParsePrimaryItemBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Dragof6a42272019-11-21 15:21:41 -08001947 } else if (!memcmp(header.type, "idat", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001948 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 3, "meta", "idat", diag));
1949 CHECK(avifParseItemDataBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08001950 } else if (!memcmp(header.type, "iprp", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001951 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 4, "meta", "iprp", diag));
1952 CHECK(avifParseItemPropertiesBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08001953 } else if (!memcmp(header.type, "iinf", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001954 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 5, "meta", "iinf", diag));
1955 CHECK(avifParseItemInfoBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08001956 } else if (!memcmp(header.type, "iref", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001957 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 6, "meta", "iref", diag));
1958 CHECK(avifParseItemReferenceBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08001959 }
1960
Joe Drago345aaa12019-09-25 13:42:12 -07001961 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001962 }
Joe Dragoe0185182021-03-31 08:14:51 -07001963 if (firstBox) {
1964 // The meta box must not be empty (it must contain at least a hdlr box)
Joe Dragoba4d67d2021-05-04 12:34:37 -07001965 avifDiagnosticsPrintf(diag, "Box[meta] has no child boxes");
Joe Dragoe0185182021-03-31 08:14:51 -07001966 return AVIF_FALSE;
1967 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001968 return AVIF_TRUE;
1969}
1970
Joe Drago4d5f4a42021-04-28 13:11:45 -07001971static avifBool avifParseTrackHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001972{
Joe Drago618ed5f2021-05-04 12:13:24 -07001973 BEGIN_STREAM(s, raw, rawLen, diag, "Box[tkhd]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07001974
1975 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001976 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001977
1978 uint32_t ignored32, trackID;
1979 uint64_t ignored64;
1980 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001981 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
1982 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
1983 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07001984 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
1985 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001986 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07001987 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
1988 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
1989 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07001990 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
1991 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001992 } else {
1993 // Unsupported version
Joe Drago618ed5f2021-05-04 12:13:24 -07001994 avifDiagnosticsPrintf(diag, "Box[tkhd] has an unsupported version [%u]", version);
Joe Dragoae7e2c32019-07-18 15:22:25 -07001995 return AVIF_FALSE;
1996 }
1997
Joe Dragofc4144e2019-09-27 20:35:06 -07001998 // Skipping the following 52 bytes here:
1999 // ------------------------------------
2000 // const unsigned int(32)[2] reserved = 0;
2001 // template int(16) layer = 0;
2002 // template int(16) alternate_group = 0;
2003 // template int(16) volume = {if track_is_audio 0x0100 else 0};
2004 // const unsigned int(16) reserved = 0;
2005 // template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix
2006 CHECK(avifROStreamSkip(&s, 52));
2007
2008 uint32_t width, height;
2009 CHECK(avifROStreamReadU32(&s, &width)); // unsigned int(32) width;
2010 CHECK(avifROStreamReadU32(&s, &height)); // unsigned int(32) height;
2011 track->width = width >> 16;
2012 track->height = height >> 16;
2013
Joe Dragoae7e2c32019-07-18 15:22:25 -07002014 // TODO: support scaling based on width/height track header info?
2015
2016 track->id = trackID;
2017 return AVIF_TRUE;
2018}
2019
Joe Drago4d5f4a42021-04-28 13:11:45 -07002020static avifBool avifParseMediaHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002021{
Joe Drago618ed5f2021-05-04 12:13:24 -07002022 BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdhd]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002023
2024 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07002025 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002026
2027 uint32_t ignored32, mediaTimescale, mediaDuration32;
2028 uint64_t ignored64, mediaDuration64;
2029 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002030 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
2031 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
2032 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
2033 CHECK(avifROStreamReadU64(&s, &mediaDuration64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002034 track->mediaDuration = mediaDuration64;
2035 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07002036 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
2037 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
2038 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
2039 CHECK(avifROStreamReadU32(&s, &mediaDuration32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002040 track->mediaDuration = (uint64_t)mediaDuration32;
2041 } else {
2042 // Unsupported version
Joe Drago618ed5f2021-05-04 12:13:24 -07002043 avifDiagnosticsPrintf(diag, "Box[mdhd] has an unsupported version [%u]", version);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002044 return AVIF_FALSE;
2045 }
2046
2047 track->mediaTimescale = mediaTimescale;
2048 return AVIF_TRUE;
2049}
2050
Joe Drago618ed5f2021-05-04 12:13:24 -07002051static avifBool avifParseChunkOffsetBox(avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002052{
Joe Drago618ed5f2021-05-04 12:13:24 -07002053 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stco]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002054
Joe Drago345aaa12019-09-25 13:42:12 -07002055 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002056
2057 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002058 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002059 for (uint32_t i = 0; i < entryCount; ++i) {
2060 uint64_t offset;
2061 if (largeOffsets) {
Joe Drago345aaa12019-09-25 13:42:12 -07002062 CHECK(avifROStreamReadU64(&s, &offset)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002063 } else {
2064 uint32_t offset32;
Joe Drago345aaa12019-09-25 13:42:12 -07002065 CHECK(avifROStreamReadU32(&s, &offset32)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002066 offset = (uint64_t)offset32;
2067 }
2068
2069 avifSampleTableChunk * chunk = (avifSampleTableChunk *)avifArrayPushPtr(&sampleTable->chunks);
2070 chunk->offset = offset;
2071 }
2072 return AVIF_TRUE;
2073}
2074
Joe Drago4d5f4a42021-04-28 13:11:45 -07002075static avifBool avifParseSampleToChunkBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002076{
Joe Drago618ed5f2021-05-04 12:13:24 -07002077 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsc]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002078
Joe Drago345aaa12019-09-25 13:42:12 -07002079 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002080
2081 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002082 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago859c9102021-03-11 18:06:40 -08002083 uint32_t prevFirstChunk = 0;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002084 for (uint32_t i = 0; i < entryCount; ++i) {
2085 avifSampleTableSampleToChunk * sampleToChunk = (avifSampleTableSampleToChunk *)avifArrayPushPtr(&sampleTable->sampleToChunks);
Joe Drago345aaa12019-09-25 13:42:12 -07002086 CHECK(avifROStreamReadU32(&s, &sampleToChunk->firstChunk)); // unsigned int(32) first_chunk;
2087 CHECK(avifROStreamReadU32(&s, &sampleToChunk->samplesPerChunk)); // unsigned int(32) samples_per_chunk;
2088 CHECK(avifROStreamReadU32(&s, &sampleToChunk->sampleDescriptionIndex)); // unsigned int(32) sample_description_index;
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002089 // The first_chunk fields should start with 1 and be strictly increasing.
2090 if (i == 0) {
2091 if (sampleToChunk->firstChunk != 1) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002092 avifDiagnosticsPrintf(diag, "Box[stsc] does not begin with chunk 1 [%u]", sampleToChunk->firstChunk);
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002093 return AVIF_FALSE;
2094 }
2095 } else {
2096 if (sampleToChunk->firstChunk <= prevFirstChunk) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002097 avifDiagnosticsPrintf(diag, "Box[stsc] chunks are not strictly increasing");
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002098 return AVIF_FALSE;
2099 }
2100 }
2101 prevFirstChunk = sampleToChunk->firstChunk;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002102 }
2103 return AVIF_TRUE;
2104}
2105
Joe Drago618ed5f2021-05-04 12:13:24 -07002106static avifBool avifParseSampleSizeBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002107{
Joe Drago618ed5f2021-05-04 12:13:24 -07002108 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsz]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002109
Joe Drago345aaa12019-09-25 13:42:12 -07002110 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002111
Joe Drago370be3f2020-02-07 15:59:42 -08002112 uint32_t allSamplesSize, sampleCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002113 CHECK(avifROStreamReadU32(&s, &allSamplesSize)); // unsigned int(32) sample_size;
Joe Drago370be3f2020-02-07 15:59:42 -08002114 CHECK(avifROStreamReadU32(&s, &sampleCount)); // unsigned int(32) sample_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002115
Joe Drago370be3f2020-02-07 15:59:42 -08002116 if (allSamplesSize > 0) {
2117 sampleTable->allSamplesSize = allSamplesSize;
2118 } else {
2119 for (uint32_t i = 0; i < sampleCount; ++i) {
2120 avifSampleTableSampleSize * sampleSize = (avifSampleTableSampleSize *)avifArrayPushPtr(&sampleTable->sampleSizes);
Joe Drago345aaa12019-09-25 13:42:12 -07002121 CHECK(avifROStreamReadU32(&s, &sampleSize->size)); // unsigned int(32) entry_size;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002122 }
2123 }
2124 return AVIF_TRUE;
2125}
2126
Joe Drago618ed5f2021-05-04 12:13:24 -07002127static avifBool avifParseSyncSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago22c1ad92019-09-26 12:46:50 -07002128{
Joe Drago618ed5f2021-05-04 12:13:24 -07002129 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stss]");
Joe Drago22c1ad92019-09-26 12:46:50 -07002130
2131 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
2132
2133 uint32_t entryCount;
2134 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
2135
2136 for (uint32_t i = 0; i < entryCount; ++i) {
2137 uint32_t sampleNumber = 0;
2138 CHECK(avifROStreamReadU32(&s, &sampleNumber)); // unsigned int(32) sample_number;
2139 avifSyncSample * syncSample = (avifSyncSample *)avifArrayPushPtr(&sampleTable->syncSamples);
2140 syncSample->sampleNumber = sampleNumber;
2141 }
2142 return AVIF_TRUE;
2143}
2144
Joe Drago618ed5f2021-05-04 12:13:24 -07002145static avifBool avifParseTimeToSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002146{
Joe Drago618ed5f2021-05-04 12:13:24 -07002147 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stts]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002148
Joe Drago345aaa12019-09-25 13:42:12 -07002149 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002150
2151 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002152 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002153
2154 for (uint32_t i = 0; i < entryCount; ++i) {
2155 avifSampleTableTimeToSample * timeToSample = (avifSampleTableTimeToSample *)avifArrayPushPtr(&sampleTable->timeToSamples);
Joe Drago345aaa12019-09-25 13:42:12 -07002156 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleCount)); // unsigned int(32) sample_count;
2157 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleDelta)); // unsigned int(32) sample_delta;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002158 }
2159 return AVIF_TRUE;
2160}
2161
Joe Drago4d5f4a42021-04-28 13:11:45 -07002162static avifBool avifParseSampleDescriptionBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago2c0924c2019-09-26 17:41:01 -07002163{
Joe Drago618ed5f2021-05-04 12:13:24 -07002164 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsd]");
Joe Drago2c0924c2019-09-26 17:41:01 -07002165
2166 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
2167
2168 uint32_t entryCount;
2169 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
2170
2171 for (uint32_t i = 0; i < entryCount; ++i) {
2172 avifBoxHeader sampleEntryHeader;
2173 CHECK(avifROStreamReadBoxHeader(&s, &sampleEntryHeader));
2174
2175 avifSampleDescription * description = (avifSampleDescription *)avifArrayPushPtr(&sampleTable->sampleDescriptions);
Joe Dragoa72da5b2020-06-15 19:40:17 -07002176 avifArrayCreate(&description->properties, sizeof(avifProperty), 16);
Joe Drago2c0924c2019-09-26 17:41:01 -07002177 memcpy(description->format, sampleEntryHeader.type, sizeof(description->format));
Joe Drago6500fd62019-10-08 17:17:34 -07002178 size_t remainingBytes = avifROStreamRemainingBytes(&s);
2179 if (!memcmp(description->format, "av01", 4) && (remainingBytes > VISUALSAMPLEENTRY_SIZE)) {
Joe Drago11d23592021-01-05 14:18:57 -08002180 CHECK(avifParseItemPropertyContainerBox(&description->properties,
2181 avifROStreamCurrent(&s) + VISUALSAMPLEENTRY_SIZE,
Joe Drago4d5f4a42021-04-28 13:11:45 -07002182 remainingBytes - VISUALSAMPLEENTRY_SIZE,
2183 diag));
Joe Drago6500fd62019-10-08 17:17:34 -07002184 }
Joe Drago2c0924c2019-09-26 17:41:01 -07002185
2186 CHECK(avifROStreamSkip(&s, sampleEntryHeader.size));
2187 }
2188 return AVIF_TRUE;
2189}
2190
Joe Drago4d5f4a42021-04-28 13:11:45 -07002191static avifBool avifParseSampleTableBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002192{
2193 if (track->sampleTable) {
2194 // A TrackBox may only have one SampleTable
Joe Dragoba4d67d2021-05-04 12:34:37 -07002195 avifDiagnosticsPrintf(diag, "Duplicate Box[stbl] for a single track detected");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002196 return AVIF_FALSE;
2197 }
2198 track->sampleTable = avifSampleTableCreate();
2199
Joe Drago618ed5f2021-05-04 12:13:24 -07002200 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stbl]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002201
Joe Drago345aaa12019-09-25 13:42:12 -07002202 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002203 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002204 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002205
2206 if (!memcmp(header.type, "stco", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002207 CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002208 } else if (!memcmp(header.type, "co64", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002209 CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002210 } else if (!memcmp(header.type, "stsc", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002211 CHECK(avifParseSampleToChunkBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002212 } else if (!memcmp(header.type, "stsz", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002213 CHECK(avifParseSampleSizeBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Drago22c1ad92019-09-26 12:46:50 -07002214 } else if (!memcmp(header.type, "stss", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002215 CHECK(avifParseSyncSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002216 } else if (!memcmp(header.type, "stts", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002217 CHECK(avifParseTimeToSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Drago2c0924c2019-09-26 17:41:01 -07002218 } else if (!memcmp(header.type, "stsd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002219 CHECK(avifParseSampleDescriptionBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002220 }
2221
Joe Drago345aaa12019-09-25 13:42:12 -07002222 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002223 }
Joe Dragoae7e2c32019-07-18 15:22:25 -07002224 return AVIF_TRUE;
2225}
2226
Joe Drago4d5f4a42021-04-28 13:11:45 -07002227static avifBool avifParseMediaInformationBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002228{
Joe Drago618ed5f2021-05-04 12:13:24 -07002229 BEGIN_STREAM(s, raw, rawLen, diag, "Box[minf]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002230
Joe Drago345aaa12019-09-25 13:42:12 -07002231 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002232 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002233 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002234
2235 if (!memcmp(header.type, "stbl", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002236 CHECK(avifParseSampleTableBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002237 }
2238
Joe Drago345aaa12019-09-25 13:42:12 -07002239 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002240 }
2241 return AVIF_TRUE;
2242}
2243
Joe Drago4d5f4a42021-04-28 13:11:45 -07002244static avifBool avifParseMediaBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002245{
Joe Drago618ed5f2021-05-04 12:13:24 -07002246 BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdia]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002247
Joe Drago345aaa12019-09-25 13:42:12 -07002248 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002249 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002250 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002251
2252 if (!memcmp(header.type, "mdhd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002253 CHECK(avifParseMediaHeaderBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002254 } else if (!memcmp(header.type, "minf", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002255 CHECK(avifParseMediaInformationBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002256 }
2257
Joe Drago345aaa12019-09-25 13:42:12 -07002258 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002259 }
2260 return AVIF_TRUE;
2261}
2262
Joe Drago618ed5f2021-05-04 12:13:24 -07002263static avifBool avifTrackReferenceBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago46ea0582019-07-22 15:55:47 -07002264{
Joe Drago618ed5f2021-05-04 12:13:24 -07002265 BEGIN_STREAM(s, raw, rawLen, diag, "Box[tref]");
Joe Drago46ea0582019-07-22 15:55:47 -07002266
Joe Drago345aaa12019-09-25 13:42:12 -07002267 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002268 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002269 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago46ea0582019-07-22 15:55:47 -07002270
2271 if (!memcmp(header.type, "auxl", 4)) {
2272 uint32_t toID;
Joe Dragoceb2fa02021-01-29 18:14:55 -08002273 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) track_IDs[];
Joe Drago345aaa12019-09-25 13:42:12 -07002274 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Joe Drago46ea0582019-07-22 15:55:47 -07002275 track->auxForID = toID;
Yuan Tonge4850be2021-01-22 14:21:25 +08002276 } else if (!memcmp(header.type, "prem", 4)) {
2277 uint32_t byID;
Joe Dragoceb2fa02021-01-29 18:14:55 -08002278 CHECK(avifROStreamReadU32(&s, &byID)); // unsigned int(32) track_IDs[];
2279 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Yuan Tonge4850be2021-01-22 14:21:25 +08002280 track->premByID = byID;
Joe Drago46ea0582019-07-22 15:55:47 -07002281 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07002282 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago46ea0582019-07-22 15:55:47 -07002283 }
2284 }
2285 return AVIF_TRUE;
2286}
2287
Joe Drago618ed5f2021-05-04 12:13:24 -07002288static avifBool avifParseTrackBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002289{
Joe Drago618ed5f2021-05-04 12:13:24 -07002290 BEGIN_STREAM(s, raw, rawLen, diag, "Box[trak]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002291
Joe Dragoa72da5b2020-06-15 19:40:17 -07002292 avifTrack * track = avifDecoderDataCreateTrack(data);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002293
Joe Drago345aaa12019-09-25 13:42:12 -07002294 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002295 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002296 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002297
2298 if (!memcmp(header.type, "tkhd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002299 CHECK(avifParseTrackHeaderBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07002300 } else if (!memcmp(header.type, "meta", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002301 CHECK(avifParseMetaBox(track->meta, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002302 } else if (!memcmp(header.type, "mdia", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002303 CHECK(avifParseMediaBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Drago46ea0582019-07-22 15:55:47 -07002304 } else if (!memcmp(header.type, "tref", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002305 CHECK(avifTrackReferenceBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002306 }
2307
Joe Drago345aaa12019-09-25 13:42:12 -07002308 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002309 }
2310 return AVIF_TRUE;
2311}
2312
Joe Drago618ed5f2021-05-04 12:13:24 -07002313static avifBool avifParseMoovBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002314{
Joe Drago618ed5f2021-05-04 12:13:24 -07002315 BEGIN_STREAM(s, raw, rawLen, diag, "Box[moov]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002316
Joe Drago345aaa12019-09-25 13:42:12 -07002317 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002318 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002319 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002320
2321 if (!memcmp(header.type, "trak", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002322 CHECK(avifParseTrackBox(data, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002323 }
2324
Joe Drago345aaa12019-09-25 13:42:12 -07002325 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002326 }
2327 return AVIF_TRUE;
2328}
2329
Joe Drago4d5f4a42021-04-28 13:11:45 -07002330static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002331{
Joe Drago618ed5f2021-05-04 12:13:24 -07002332 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002333
Joe Drago345aaa12019-09-25 13:42:12 -07002334 CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
2335 CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
Joe Drago8f7a3002019-02-07 19:35:37 -08002336
Joe Drago345aaa12019-09-25 13:42:12 -07002337 size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
Joe Drago8f7a3002019-02-07 19:35:37 -08002338 if ((compatibleBrandsBytes % 4) != 0) {
Joe Drago48867292021-05-04 13:23:08 -07002339 avifDiagnosticsPrintf(diag, "Box[ftyp] contains a compatible brands section that isn't divisible by 4 [%zu]", compatibleBrandsBytes);
Joe Drago8f7a3002019-02-07 19:35:37 -08002340 return AVIF_FALSE;
2341 }
Wan-Teh Chang6da0a882020-07-01 12:19:31 -07002342 ftyp->compatibleBrands = avifROStreamCurrent(&s);
2343 CHECK(avifROStreamSkip(&s, compatibleBrandsBytes));
Joe Drago7e37b972019-07-24 12:44:47 -07002344 ftyp->compatibleBrandsCount = (int)compatibleBrandsBytes / 4;
Joe Drago8f7a3002019-02-07 19:35:37 -08002345
2346 return AVIF_TRUE;
2347}
2348
Joe Dragobb39aab2020-11-03 19:23:40 -08002349static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand);
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002350static avifBool avifFileTypeIsCompatible(avifFileType * ftyp);
2351
Joe Dragobe4cbb92020-09-21 12:14:05 -07002352static avifResult avifParse(avifDecoder * decoder)
Joe Drago8f7a3002019-02-07 19:35:37 -08002353{
Joe Drago9aa931f2020-09-24 13:10:11 -07002354 // Note: this top-level function is the only avifParse*() function that returns avifResult instead of avifBool.
2355 // Be sure to use CHECKERR() in this function with an explicit error result instead of simply using CHECK().
2356
Joe Dragobe4cbb92020-09-21 12:14:05 -07002357 avifResult readResult;
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002358 uint64_t parseOffset = 0;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002359 avifDecoderData * data = decoder->data;
Joe Dragobb39aab2020-11-03 19:23:40 -08002360 avifBool ftypSeen = AVIF_FALSE;
2361 avifBool metaSeen = AVIF_FALSE;
2362 avifBool moovSeen = AVIF_FALSE;
2363 avifBool needsMeta = AVIF_FALSE;
2364 avifBool needsMoov = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08002365
Joe Dragobe4cbb92020-09-21 12:14:05 -07002366 for (;;) {
2367 // Read just enough to get the next box header (a max of 32 bytes)
2368 avifROData headerContents;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002369 if ((decoder->io->sizeHint > 0) && (parseOffset > decoder->io->sizeHint)) {
2370 return AVIF_RESULT_BMFF_PARSE_FAILED;
2371 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002372 readResult = decoder->io->read(decoder->io, 0, parseOffset, 32, &headerContents);
2373 if (readResult != AVIF_RESULT_OK) {
2374 return readResult;
2375 }
2376 if (!headerContents.size) {
2377 // If we got AVIF_RESULT_OK from the reader but received 0 bytes,
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07002378 // we've reached the end of the file with no errors. Hooray!
Joe Dragobe4cbb92020-09-21 12:14:05 -07002379 break;
Joe Drago8f7a3002019-02-07 19:35:37 -08002380 }
2381
Joe Dragobe4cbb92020-09-21 12:14:05 -07002382 // Parse the header, and find out how many bytes it actually was
Joe Drago618ed5f2021-05-04 12:13:24 -07002383 BEGIN_STREAM(headerStream, headerContents.data, headerContents.size, &decoder->diag, "File-level box header");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002384 avifBoxHeader header;
Joe Drago468ded82020-09-24 12:52:51 -07002385 CHECKERR(avifROStreamReadBoxHeaderPartial(&headerStream, &header), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002386 parseOffset += headerStream.offset;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002387 assert((decoder->io->sizeHint == 0) || (parseOffset <= decoder->io->sizeHint));
Joe Dragobe4cbb92020-09-21 12:14:05 -07002388
2389 // Try to get the remainder of the box, if necessary
2390 avifROData boxContents = AVIF_DATA_EMPTY;
2391
2392 // TODO: reorg this code to only do these memcmps once each
Wan-Teh Changb309e982021-02-20 16:59:36 -08002393 if (!memcmp(header.type, "ftyp", 4) || !memcmp(header.type, "meta", 4) || !memcmp(header.type, "moov", 4)) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002394 readResult = decoder->io->read(decoder->io, 0, parseOffset, header.size, &boxContents);
2395 if (readResult != AVIF_RESULT_OK) {
2396 return readResult;
2397 }
2398 if (boxContents.size != header.size) {
2399 // A truncated box, bail out
Joe Dragofe5d5e42020-09-24 13:07:58 -07002400 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002401 }
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002402 } else if (header.size > (UINT64_MAX - parseOffset)) {
2403 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002404 }
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002405 parseOffset += header.size;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002406
2407 if (!memcmp(header.type, "ftyp", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002408 CHECKERR(!ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002409 avifFileType ftyp;
Joe Drago4d5f4a42021-04-28 13:11:45 -07002410 CHECKERR(avifParseFileTypeBox(&ftyp, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002411 if (!avifFileTypeIsCompatible(&ftyp)) {
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002412 return AVIF_RESULT_INVALID_FTYP;
2413 }
Joe Dragobb39aab2020-11-03 19:23:40 -08002414 ftypSeen = AVIF_TRUE;
2415 needsMeta = avifFileTypeHasBrand(&ftyp, "avif");
2416 needsMoov = avifFileTypeHasBrand(&ftyp, "avis");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002417 } else if (!memcmp(header.type, "meta", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002418 CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Drago4d5f4a42021-04-28 13:11:45 -07002419 CHECKERR(avifParseMetaBox(data->meta, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002420 metaSeen = AVIF_TRUE;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002421 } else if (!memcmp(header.type, "moov", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002422 CHECKERR(!moovSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Drago618ed5f2021-05-04 12:13:24 -07002423 CHECKERR(avifParseMoovBox(data, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002424 moovSeen = AVIF_TRUE;
2425 }
2426
2427 // See if there is enough information to consider Parse() a success and early-out:
2428 // * If the brand 'avif' is present, require a meta box
2429 // * If the brand 'avis' is present, require a moov box
2430 if (ftypSeen && (!needsMeta || metaSeen) && (!needsMoov || moovSeen)) {
2431 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002432 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002433 }
Joe Drago029375a2021-04-28 11:57:56 -07002434 if (!ftypSeen) {
2435 return AVIF_RESULT_INVALID_FTYP;
2436 }
2437 if ((needsMeta && !metaSeen) || (needsMoov && !moovSeen)) {
2438 return AVIF_RESULT_TRUNCATED_DATA;
2439 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002440 return AVIF_RESULT_OK;
Joe Drago8f7a3002019-02-07 19:35:37 -08002441}
2442
2443// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08002444
Joe Dragobb39aab2020-11-03 19:23:40 -08002445static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand)
Joe Drago7e37b972019-07-24 12:44:47 -07002446{
Joe Dragobb39aab2020-11-03 19:23:40 -08002447 if (!memcmp(ftyp->majorBrand, brand, 4)) {
2448 return AVIF_TRUE;
2449 }
2450
2451 for (int compatibleBrandIndex = 0; compatibleBrandIndex < ftyp->compatibleBrandsCount; ++compatibleBrandIndex) {
2452 const uint8_t * compatibleBrand = &ftyp->compatibleBrands[4 * compatibleBrandIndex];
2453 if (!memcmp(compatibleBrand, brand, 4)) {
2454 return AVIF_TRUE;
Joe Drago7e37b972019-07-24 12:44:47 -07002455 }
2456 }
Joe Dragobb39aab2020-11-03 19:23:40 -08002457 return AVIF_FALSE;
2458}
2459
2460static avifBool avifFileTypeIsCompatible(avifFileType * ftyp)
2461{
2462 return avifFileTypeHasBrand(ftyp, "avif") || avifFileTypeHasBrand(ftyp, "avis");
Joe Drago7e37b972019-07-24 12:44:47 -07002463}
2464
Wan-Teh Change184dc12020-05-11 12:47:21 -07002465avifBool avifPeekCompatibleFileType(const avifROData * input)
Joe Drago7e37b972019-07-24 12:44:47 -07002466{
Joe Drago618ed5f2021-05-04 12:13:24 -07002467 BEGIN_STREAM(s, input->data, input->size, NULL, NULL);
Joe Drago7e37b972019-07-24 12:44:47 -07002468
2469 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002470 CHECK(avifROStreamReadBoxHeader(&s, &header));
wantehchangbc35a5f2020-08-12 15:27:39 -07002471 if (memcmp(header.type, "ftyp", 4)) {
Joe Drago7e37b972019-07-24 12:44:47 -07002472 return AVIF_FALSE;
2473 }
2474
2475 avifFileType ftyp;
2476 memset(&ftyp, 0, sizeof(avifFileType));
Joe Drago4d5f4a42021-04-28 13:11:45 -07002477 avifBool parsed = avifParseFileTypeBox(&ftyp, avifROStreamCurrent(&s), header.size, NULL);
Joe Drago7e37b972019-07-24 12:44:47 -07002478 if (!parsed) {
2479 return AVIF_FALSE;
2480 }
2481 return avifFileTypeIsCompatible(&ftyp);
2482}
2483
2484// ---------------------------------------------------------------------------
2485
Joe Drago0b05eee2019-06-12 13:24:39 -07002486avifDecoder * avifDecoderCreate(void)
2487{
2488 avifDecoder * decoder = (avifDecoder *)avifAlloc(sizeof(avifDecoder));
2489 memset(decoder, 0, sizeof(avifDecoder));
Joe Dragoede5c202020-11-11 09:42:57 -08002490 decoder->maxThreads = 1;
Joe Dragod2340b42021-03-14 13:20:02 -07002491 decoder->imageCountLimit = AVIF_DEFAULT_IMAGE_COUNT_LIMIT;
Joe Drago4d5f4a42021-04-28 13:11:45 -07002492 avifDiagnosticsClearError(&decoder->diag);
Joe Drago0b05eee2019-06-12 13:24:39 -07002493 return decoder;
2494}
2495
Joe Drago46ea0582019-07-22 15:55:47 -07002496static void avifDecoderCleanup(avifDecoder * decoder)
2497{
2498 if (decoder->data) {
Joe Drago800b47f2020-03-18 16:22:37 -07002499 avifDecoderDataDestroy(decoder->data);
Joe Drago46ea0582019-07-22 15:55:47 -07002500 decoder->data = NULL;
2501 }
2502
2503 if (decoder->image) {
2504 avifImageDestroy(decoder->image);
2505 decoder->image = NULL;
2506 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07002507 avifDiagnosticsClearError(&decoder->diag);
Joe Drago46ea0582019-07-22 15:55:47 -07002508}
2509
Joe Drago0b05eee2019-06-12 13:24:39 -07002510void avifDecoderDestroy(avifDecoder * decoder)
2511{
Joe Drago46ea0582019-07-22 15:55:47 -07002512 avifDecoderCleanup(decoder);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002513 avifIODestroy(decoder->io);
Joe Drago0b05eee2019-06-12 13:24:39 -07002514 avifFree(decoder);
2515}
2516
Joe Drago46ea0582019-07-22 15:55:47 -07002517avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source)
Joe Drago444f0512019-01-23 17:03:24 -08002518{
Joe Drago46ea0582019-07-22 15:55:47 -07002519 decoder->requestedSource = source;
2520 return avifDecoderReset(decoder);
2521}
Joe Drago33f1d362019-02-13 16:46:22 -08002522
Wan-Teh Change67f9362020-10-12 16:07:57 -07002523void avifDecoderSetIO(avifDecoder * decoder, avifIO * io)
Joe Drago46ea0582019-07-22 15:55:47 -07002524{
Joe Dragobe4cbb92020-09-21 12:14:05 -07002525 avifIODestroy(decoder->io);
2526 decoder->io = io;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002527}
2528
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002529avifResult avifDecoderSetIOMemory(avifDecoder * decoder, const uint8_t * data, size_t size)
Joe Dragobe4cbb92020-09-21 12:14:05 -07002530{
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002531 avifIO * io = avifIOCreateMemoryReader(data, size);
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002532 assert(io);
Wan-Teh Change67f9362020-10-12 16:07:57 -07002533 avifDecoderSetIO(decoder, io);
2534 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002535}
2536
Joe Dragobe4cbb92020-09-21 12:14:05 -07002537avifResult avifDecoderSetIOFile(avifDecoder * decoder, const char * filename)
2538{
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002539 avifIO * io = avifIOCreateFileReader(filename);
2540 if (!io) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002541 return AVIF_RESULT_IO_ERROR;
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002542 }
Wan-Teh Change67f9362020-10-12 16:07:57 -07002543 avifDecoderSetIO(decoder, io);
2544 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002545}
2546
Joe Drago4bcdfde2020-11-13 17:50:55 -08002547// 0-byte extents are ignored/overwritten during the merge, as they are the signal from helper
2548// functions that no extent was necessary for this given sample. If both provided extents are
2549// >0 bytes, this will set dst to be an extent that bounds both supplied extents.
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002550static avifResult avifExtentMerge(avifExtent * dst, const avifExtent * src)
Joe Drago4bcdfde2020-11-13 17:50:55 -08002551{
2552 if (!dst->size) {
2553 memcpy(dst, src, sizeof(avifExtent));
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002554 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08002555 }
2556 if (!src->size) {
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002557 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08002558 }
2559
2560 const uint64_t minExtent1 = dst->offset;
2561 const uint64_t maxExtent1 = dst->offset + dst->size;
2562 const uint64_t minExtent2 = src->offset;
2563 const uint64_t maxExtent2 = src->offset + src->size;
2564 dst->offset = AVIF_MIN(minExtent1, minExtent2);
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002565 const uint64_t extentLength = AVIF_MAX(maxExtent1, maxExtent2) - dst->offset;
2566 if (extentLength > SIZE_MAX) {
2567 return AVIF_RESULT_BMFF_PARSE_FAILED;
2568 }
2569 dst->size = (size_t)extentLength;
2570 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08002571}
2572
Joe Drago93d5bf92020-11-17 14:31:57 -08002573avifResult avifDecoderNthImageMaxExtent(const avifDecoder * decoder, uint32_t frameIndex, avifExtent * outExtent)
Joe Drago4bcdfde2020-11-13 17:50:55 -08002574{
2575 if (!decoder->data) {
2576 // Nothing has been parsed yet
2577 return AVIF_RESULT_NO_CONTENT;
2578 }
2579
2580 memset(outExtent, 0, sizeof(avifExtent));
2581
Joe Drago93d5bf92020-11-17 14:31:57 -08002582 uint32_t startFrameIndex = avifDecoderNearestKeyframe(decoder, frameIndex);
Joe Drago4bcdfde2020-11-13 17:50:55 -08002583 uint32_t endFrameIndex = frameIndex;
2584 for (uint32_t currentFrameIndex = startFrameIndex; currentFrameIndex <= endFrameIndex; ++currentFrameIndex) {
2585 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
2586 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
2587 if (currentFrameIndex >= tile->input->samples.count) {
2588 return AVIF_RESULT_NO_IMAGES_REMAINING;
2589 }
2590
2591 avifDecodeSample * sample = &tile->input->samples.sample[currentFrameIndex];
2592 avifExtent sampleExtent;
2593 if (sample->itemID) {
2594 // The data comes from an item. Let avifDecoderItemMaxExtent() do the heavy lifting.
2595
2596 avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
2597 avifResult maxExtentResult = avifDecoderItemMaxExtent(item, &sampleExtent);
2598 if (maxExtentResult != AVIF_RESULT_OK) {
2599 return maxExtentResult;
2600 }
2601 } else {
2602 // The data likely comes from a sample table. Use the sample position directly.
2603
2604 sampleExtent.offset = sample->offset;
2605 sampleExtent.size = sample->size;
2606 }
2607
2608 if (sampleExtent.size > UINT64_MAX - sampleExtent.offset) {
2609 return AVIF_RESULT_BMFF_PARSE_FAILED;
2610 }
2611
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002612 avifResult extentMergeResult = avifExtentMerge(outExtent, &sampleExtent);
2613 if (extentMergeResult != AVIF_RESULT_OK) {
2614 return extentMergeResult;
2615 }
Joe Drago4bcdfde2020-11-13 17:50:55 -08002616 }
2617 }
2618 return AVIF_RESULT_OK;
2619}
2620
Joe Dragobe4cbb92020-09-21 12:14:05 -07002621static avifResult avifDecoderPrepareSample(avifDecoder * decoder, avifDecodeSample * sample, size_t partialByteCount)
2622{
2623 if (!sample->data.size || sample->partialData) {
2624 // This sample hasn't been read from IO or had its extents fully merged yet.
2625
2626 if (sample->itemID) {
Wan-Teh Chang4548b162020-11-06 11:48:25 -08002627 // The data comes from an item. Let avifDecoderItemRead() do the heavy lifting.
Joe Dragobe4cbb92020-09-21 12:14:05 -07002628
2629 avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
2630 avifROData itemContents;
Joe Drago618ed5f2021-05-04 12:13:24 -07002631 avifResult readResult = avifDecoderItemRead(item, decoder->io, &itemContents, partialByteCount, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002632 if (readResult != AVIF_RESULT_OK) {
2633 return readResult;
2634 }
2635
Wan-Teh Chang4548b162020-11-06 11:48:25 -08002636 // avifDecoderItemRead is guaranteed to already be persisted by either the underlying IO
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07002637 // or by mergedExtents; just reuse the buffer here.
Joe Dragobe4cbb92020-09-21 12:14:05 -07002638 memcpy(&sample->data, &itemContents, sizeof(avifROData));
2639 sample->ownsData = AVIF_FALSE;
2640 sample->partialData = item->partialMergedExtents;
2641 } else {
2642 // The data likely comes from a sample table. Pull the sample and make a copy if necessary.
2643
2644 size_t bytesToRead = sample->size;
2645 if (partialByteCount && (bytesToRead > partialByteCount)) {
2646 bytesToRead = partialByteCount;
2647 }
2648
2649 avifROData sampleContents;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002650 if ((decoder->io->sizeHint > 0) && (sample->offset > decoder->io->sizeHint)) {
2651 return AVIF_RESULT_BMFF_PARSE_FAILED;
2652 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002653 avifResult readResult = decoder->io->read(decoder->io, 0, sample->offset, bytesToRead, &sampleContents);
2654 if (readResult != AVIF_RESULT_OK) {
2655 return readResult;
2656 }
2657 if (sampleContents.size != bytesToRead) {
2658 return AVIF_RESULT_TRUNCATED_DATA;
2659 }
2660
2661 sample->ownsData = !decoder->io->persistent;
2662 sample->partialData = (bytesToRead != sample->size);
2663 if (decoder->io->persistent) {
2664 memcpy(&sample->data, &sampleContents, sizeof(avifROData));
2665 } else {
2666 avifRWDataSet((avifRWData *)&sample->data, sampleContents.data, sampleContents.size);
2667 }
2668 }
2669 }
2670 return AVIF_RESULT_OK;
2671}
2672
2673avifResult avifDecoderParse(avifDecoder * decoder)
2674{
Joe Drago4d5f4a42021-04-28 13:11:45 -07002675 avifDiagnosticsClearError(&decoder->diag);
2676
Joe Dragobe4cbb92020-09-21 12:14:05 -07002677 if (!decoder->io || !decoder->io->read) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002678 return AVIF_RESULT_IO_NOT_SET;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002679 }
2680
Joe Drago46ea0582019-07-22 15:55:47 -07002681 // Cleanup anything lingering in the decoder
2682 avifDecoderCleanup(decoder);
2683
Joe Drago444f0512019-01-23 17:03:24 -08002684 // -----------------------------------------------------------------------
2685 // Parse BMFF boxes
2686
Joe Drago800b47f2020-03-18 16:22:37 -07002687 decoder->data = avifDecoderDataCreate();
Joe Drago4d5f4a42021-04-28 13:11:45 -07002688 decoder->data->diag = &decoder->diag;
Joe Drago46ea0582019-07-22 15:55:47 -07002689
Joe Dragobe4cbb92020-09-21 12:14:05 -07002690 avifResult parseResult = avifParse(decoder);
2691 if (parseResult != AVIF_RESULT_OK) {
2692 return parseResult;
Joe Drago444f0512019-01-23 17:03:24 -08002693 }
2694
Joe Drago46ea0582019-07-22 15:55:47 -07002695 return avifDecoderReset(decoder);
2696}
2697
Joe Dragobe4cbb92020-09-21 12:14:05 -07002698static avifCodec * avifCodecCreateInternal(avifCodecChoice choice)
Joe Drago46ea0582019-07-22 15:55:47 -07002699{
Joe Dragobe4cbb92020-09-21 12:14:05 -07002700 return avifCodecCreate(choice, AVIF_CODEC_FLAG_CAN_DECODE);
Joe Drago46ea0582019-07-22 15:55:47 -07002701}
2702
Joe Drago22c1ad92019-09-26 12:46:50 -07002703static avifResult avifDecoderFlush(avifDecoder * decoder)
2704{
Joe Drago800b47f2020-03-18 16:22:37 -07002705 avifDecoderDataResetCodec(decoder->data);
Joe Drago22c1ad92019-09-26 12:46:50 -07002706
Joe Drago060d5342020-03-03 10:53:49 -08002707 for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) {
2708 avifTile * tile = &decoder->data->tiles.tile[i];
Joe Dragobe4cbb92020-09-21 12:14:05 -07002709 tile->codec = avifCodecCreateInternal(decoder->codecChoice);
Joe Drago060d5342020-03-03 10:53:49 -08002710 if (!tile->codec) {
Joe Drago53355352019-10-28 19:04:51 -07002711 return AVIF_RESULT_NO_CODEC_AVAILABLE;
2712 }
Joe Dragoede5c202020-11-11 09:42:57 -08002713 if (!tile->codec->open(tile->codec, decoder)) {
Joe Drago060d5342020-03-03 10:53:49 -08002714 return AVIF_RESULT_DECODE_COLOR_FAILED;
Joe Drago22c1ad92019-09-26 12:46:50 -07002715 }
2716 }
2717 return AVIF_RESULT_OK;
2718}
2719
Joe Drago46ea0582019-07-22 15:55:47 -07002720avifResult avifDecoderReset(avifDecoder * decoder)
2721{
Joe Drago4d5f4a42021-04-28 13:11:45 -07002722 avifDiagnosticsClearError(&decoder->diag);
2723
Joe Drago800b47f2020-03-18 16:22:37 -07002724 avifDecoderData * data = decoder->data;
Joe Drago46ea0582019-07-22 15:55:47 -07002725 if (!data) {
2726 // Nothing to reset.
2727 return AVIF_RESULT_OK;
2728 }
2729
Joe Drago060d5342020-03-03 10:53:49 -08002730 memset(&data->colorGrid, 0, sizeof(data->colorGrid));
2731 memset(&data->alphaGrid, 0, sizeof(data->alphaGrid));
Joe Drago800b47f2020-03-18 16:22:37 -07002732 avifDecoderDataClearTiles(data);
Joe Drago89f0cc82020-03-09 16:13:27 -07002733
2734 // Prepare / cleanup decoded image state
Joe Dragoa0da4a42020-05-08 14:27:40 -07002735 if (decoder->image) {
2736 avifImageDestroy(decoder->image);
Joe Drago8f7a3002019-02-07 19:35:37 -08002737 }
Joe Dragoa0da4a42020-05-08 14:27:40 -07002738 decoder->image = avifImageCreateEmpty();
2739 data->cicpSet = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08002740
Joe Drago70cbf602019-07-24 15:30:55 -07002741 memset(&decoder->ioStats, 0, sizeof(decoder->ioStats));
2742
Joe Drago444f0512019-01-23 17:03:24 -08002743 // -----------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -07002744 // Build decode input
Joe Drago444f0512019-01-23 17:03:24 -08002745
Joe Drago46ea0582019-07-22 15:55:47 -07002746 data->sourceSampleTable = NULL; // Reset
2747 if (decoder->requestedSource == AVIF_DECODER_SOURCE_AUTO) {
2748 if (data->tracks.count > 0) {
2749 data->source = AVIF_DECODER_SOURCE_TRACKS;
2750 } else {
2751 data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;
Joe Drago76370232019-07-16 11:00:52 -07002752 }
Joe Drago46ea0582019-07-22 15:55:47 -07002753 } else {
2754 data->source = decoder->requestedSource;
Joe Drago76370232019-07-16 11:00:52 -07002755 }
2756
Joe Dragoa72da5b2020-06-15 19:40:17 -07002757 const avifPropertyArray * colorProperties = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07002758 if (data->source == AVIF_DECODER_SOURCE_TRACKS) {
2759 avifTrack * colorTrack = NULL;
2760 avifTrack * alphaTrack = NULL;
2761
2762 // Find primary track - this probably needs some better detection
2763 uint32_t colorTrackIndex = 0;
wantehchangb207b4d2020-08-11 17:50:22 -07002764 for (; colorTrackIndex < data->tracks.count; ++colorTrackIndex) {
2765 avifTrack * track = &data->tracks.track[colorTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07002766 if (!track->sampleTable) {
2767 continue;
2768 }
Joe Dragoba1eb492020-06-22 17:05:04 -07002769 if (!track->id) { // trak box might be missing a tkhd box inside, skip it
Joe Drago4a25c192020-06-03 16:29:58 -07002770 continue;
2771 }
Joe Drago46ea0582019-07-22 15:55:47 -07002772 if (!track->sampleTable->chunks.count) {
2773 continue;
2774 }
Joe Drago2c0924c2019-09-26 17:41:01 -07002775 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
2776 continue;
2777 }
Joe Drago46ea0582019-07-22 15:55:47 -07002778 if (track->auxForID != 0) {
2779 continue;
2780 }
2781
2782 // Found one!
Joe Drago444f0512019-01-23 17:03:24 -08002783 break;
2784 }
wantehchangb207b4d2020-08-11 17:50:22 -07002785 if (colorTrackIndex == data->tracks.count) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002786 avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track");
Joe Drago46ea0582019-07-22 15:55:47 -07002787 return AVIF_RESULT_NO_CONTENT;
Joe Drago444f0512019-01-23 17:03:24 -08002788 }
wantehchangb207b4d2020-08-11 17:50:22 -07002789 colorTrack = &data->tracks.track[colorTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07002790
Joe Dragoa72da5b2020-06-15 19:40:17 -07002791 colorProperties = avifSampleTableGetProperties(colorTrack->sampleTable);
2792 if (!colorProperties) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002793 avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track's color properties");
Joe Dragoa72da5b2020-06-15 19:40:17 -07002794 return AVIF_RESULT_BMFF_PARSE_FAILED;
2795 }
2796
2797 // Find Exif and/or XMP metadata, if any
2798 if (colorTrack->meta) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002799 // See the comment above avifDecoderFindMetadata() for the explanation of using 0 here
2800 avifResult findResult = avifDecoderFindMetadata(decoder, colorTrack->meta, decoder->image, 0);
2801 if (findResult != AVIF_RESULT_OK) {
2802 return findResult;
Joe Dragoa72da5b2020-06-15 19:40:17 -07002803 }
2804 }
2805
Joe Drago46ea0582019-07-22 15:55:47 -07002806 uint32_t alphaTrackIndex = 0;
wantehchangb207b4d2020-08-11 17:50:22 -07002807 for (; alphaTrackIndex < data->tracks.count; ++alphaTrackIndex) {
2808 avifTrack * track = &data->tracks.track[alphaTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07002809 if (!track->sampleTable) {
2810 continue;
2811 }
Joe Drago4a25c192020-06-03 16:29:58 -07002812 if (!track->id) {
2813 continue;
2814 }
Joe Drago46ea0582019-07-22 15:55:47 -07002815 if (!track->sampleTable->chunks.count) {
2816 continue;
2817 }
Joe Drago2c0924c2019-09-26 17:41:01 -07002818 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
2819 continue;
2820 }
Joe Drago46ea0582019-07-22 15:55:47 -07002821 if (track->auxForID == colorTrack->id) {
2822 // Found it!
2823 break;
2824 }
2825 }
wantehchangb207b4d2020-08-11 17:50:22 -07002826 if (alphaTrackIndex != data->tracks.count) {
2827 alphaTrack = &data->tracks.track[alphaTrackIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -08002828 }
Joe Drago444f0512019-01-23 17:03:24 -08002829
wantehchangb207b4d2020-08-11 17:50:22 -07002830 avifTile * colorTile = avifDecoderDataCreateTile(data);
Joe Drago4d5f4a42021-04-28 13:11:45 -07002831 if (!avifCodecDecodeInputGetSamples(colorTile->input, colorTrack->sampleTable, decoder->imageCountLimit, decoder->io->sizeHint, data->diag)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002832 return AVIF_RESULT_BMFF_PARSE_FAILED;
2833 }
wantehchangb207b4d2020-08-11 17:50:22 -07002834 data->colorTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07002835
2836 if (alphaTrack) {
wantehchang76e16bf2020-08-12 13:01:31 -07002837 avifTile * alphaTile = avifDecoderDataCreateTile(data);
Joe Drago4d5f4a42021-04-28 13:11:45 -07002838 if (!avifCodecDecodeInputGetSamples(alphaTile->input,
2839 alphaTrack->sampleTable,
2840 decoder->imageCountLimit,
2841 decoder->io->sizeHint,
2842 data->diag)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002843 return AVIF_RESULT_BMFF_PARSE_FAILED;
2844 }
Joe Drago060d5342020-03-03 10:53:49 -08002845 alphaTile->input->alpha = AVIF_TRUE;
wantehchangb207b4d2020-08-11 17:50:22 -07002846 data->alphaTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07002847 }
2848
2849 // Stash off sample table for future timing information
2850 data->sourceSampleTable = colorTrack->sampleTable;
2851
2852 // Image sequence timing
2853 decoder->imageIndex = -1;
Joe Drago060d5342020-03-03 10:53:49 -08002854 decoder->imageCount = colorTile->input->samples.count;
Joe Drago46ea0582019-07-22 15:55:47 -07002855 decoder->timescale = colorTrack->mediaTimescale;
2856 decoder->durationInTimescales = colorTrack->mediaDuration;
2857 if (colorTrack->mediaTimescale) {
2858 decoder->duration = (double)decoder->durationInTimescales / (double)colorTrack->mediaTimescale;
2859 } else {
2860 decoder->duration = 0;
2861 }
2862 memset(&decoder->imageTiming, 0, sizeof(decoder->imageTiming)); // to be set in avifDecoderNextImage()
Joe Drago41700852019-09-26 17:01:43 -07002863
Joe Dragoc554f5f2020-06-09 18:59:51 -07002864 decoder->image->width = colorTrack->width;
2865 decoder->image->height = colorTrack->height;
Joe Dragoc554f5f2020-06-09 18:59:51 -07002866 decoder->alphaPresent = (alphaTrack != NULL);
Joe Dragoceb2fa02021-01-29 18:14:55 -08002867 decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorTrack->premByID == alphaTrack->id);
Joe Drago46ea0582019-07-22 15:55:47 -07002868 } else {
2869 // Create from items
2870
Joe Dragobe4cbb92020-09-21 12:14:05 -07002871 avifDecoderItem * colorItem = NULL;
2872 avifDecoderItem * alphaItem = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07002873
Joe Drago51d11142021-03-31 07:46:30 -07002874 if (data->meta->primaryItemID == 0) {
2875 // A primary item is required
Joe Drago618ed5f2021-05-04 12:13:24 -07002876 avifDiagnosticsPrintf(&decoder->diag, "Primary item not specified");
Joe Drago51d11142021-03-31 07:46:30 -07002877 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
2878 }
2879
Joe Dragof6a42272019-11-21 15:21:41 -08002880 // Find the colorOBU (primary) item
Joe Drago9f2b87b2020-06-03 19:36:38 -07002881 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
2882 avifDecoderItem * item = &data->meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07002883 if (!item->size) {
2884 continue;
Joe Drago8f7a3002019-02-07 19:35:37 -08002885 }
Joe Drago3320e5f2020-04-21 17:36:27 -07002886 if (item->hasUnsupportedEssentialProperty) {
2887 // An essential property isn't supported by libavif; ignore the item.
2888 continue;
2889 }
Joe Drago951a0022020-03-09 16:19:44 -07002890 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08002891 if (memcmp(item->type, "av01", 4) && !isGrid) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002892 // probably exif or some other data
2893 continue;
2894 }
2895 if (item->thumbnailForID != 0) {
2896 // It's a thumbnail, skip it
2897 continue;
2898 }
Joe Drago618ed5f2021-05-04 12:13:24 -07002899 if (item->id != data->meta->primaryItemID) {
2900 // This is not the primary item, skip it
Joe Dragof6a42272019-11-21 15:21:41 -08002901 continue;
2902 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002903
Joe Drago060d5342020-03-03 10:53:49 -08002904 if (isGrid) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002905 avifROData readData;
Joe Drago618ed5f2021-05-04 12:13:24 -07002906 avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, data->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002907 if (readResult != AVIF_RESULT_OK) {
2908 return readResult;
2909 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07002910 if (!avifParseImageGridBox(&data->colorGrid, readData.data, readData.size, data->diag)) {
Joe Drago060d5342020-03-03 10:53:49 -08002911 return AVIF_RESULT_INVALID_IMAGE_GRID;
2912 }
Joe Drago060d5342020-03-03 10:53:49 -08002913 }
2914
Joe Dragobe4cbb92020-09-21 12:14:05 -07002915 colorItem = item;
Joe Drago46ea0582019-07-22 15:55:47 -07002916 break;
2917 }
2918
Joe Dragobe4cbb92020-09-21 12:14:05 -07002919 if (!colorItem) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002920 avifDiagnosticsPrintf(&decoder->diag, "Primary item not found");
Joe Drago060d5342020-03-03 10:53:49 -08002921 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
2922 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002923 colorProperties = &colorItem->properties;
Joe Drago46ea0582019-07-22 15:55:47 -07002924
Joe Drago060d5342020-03-03 10:53:49 -08002925 // Find the alphaOBU item, if any
Joe Drago9f2b87b2020-06-03 19:36:38 -07002926 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
2927 avifDecoderItem * item = &data->meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07002928 if (!item->size) {
2929 continue;
Joe Drago060d5342020-03-03 10:53:49 -08002930 }
Joe Drago3320e5f2020-04-21 17:36:27 -07002931 if (item->hasUnsupportedEssentialProperty) {
2932 // An essential property isn't supported by libavif; ignore the item.
2933 continue;
2934 }
Joe Drago951a0022020-03-09 16:19:44 -07002935 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08002936 if (memcmp(item->type, "av01", 4) && !isGrid) {
2937 // probably exif or some other data
2938 continue;
2939 }
Joe Dragof6a42272019-11-21 15:21:41 -08002940
Joe Dragobe4cbb92020-09-21 12:14:05 -07002941 // Is this an alpha auxiliary item of whatever we chose for colorItem?
Joe Dragoa72da5b2020-06-15 19:40:17 -07002942 const avifProperty * auxCProp = avifPropertyArrayFind(&item->properties, "auxC");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002943 if (auxCProp && isAlphaURN(auxCProp->u.auxC.auxType) && (item->auxForID == colorItem->id)) {
Joe Drago060d5342020-03-03 10:53:49 -08002944 if (isGrid) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002945 avifROData readData;
Joe Drago618ed5f2021-05-04 12:13:24 -07002946 avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, data->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002947 if (readResult != AVIF_RESULT_OK) {
2948 return readResult;
2949 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07002950 if (!avifParseImageGridBox(&data->alphaGrid, readData.data, readData.size, data->diag)) {
Joe Drago060d5342020-03-03 10:53:49 -08002951 return AVIF_RESULT_INVALID_IMAGE_GRID;
2952 }
Joe Dragof6a42272019-11-21 15:21:41 -08002953 }
2954
Joe Dragobe4cbb92020-09-21 12:14:05 -07002955 alphaItem = item;
Joe Drago060d5342020-03-03 10:53:49 -08002956 break;
Joe Dragof6a42272019-11-21 15:21:41 -08002957 }
Joe Drago444f0512019-01-23 17:03:24 -08002958 }
Joe Drago444f0512019-01-23 17:03:24 -08002959
Joe Drago060d5342020-03-03 10:53:49 -08002960 // Find Exif and/or XMP metadata, if any
Joe Dragobe4cbb92020-09-21 12:14:05 -07002961 avifResult findResult = avifDecoderFindMetadata(decoder, data->meta, decoder->image, colorItem->id);
2962 if (findResult != AVIF_RESULT_OK) {
2963 return findResult;
Joe Drago060d5342020-03-03 10:53:49 -08002964 }
2965
Wan-Teh Chang4295bcb2020-04-05 15:41:05 -07002966 if ((data->colorGrid.rows > 0) && (data->colorGrid.columns > 0)) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002967 if (!avifDecoderDataGenerateImageGridTiles(data, &data->colorGrid, colorItem, AVIF_FALSE)) {
Joe Drago060d5342020-03-03 10:53:49 -08002968 return AVIF_RESULT_INVALID_IMAGE_GRID;
2969 }
2970 data->colorTileCount = data->tiles.count;
2971 } else {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002972 if (colorItem->size == 0) {
Joe Drago060d5342020-03-03 10:53:49 -08002973 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
2974 }
2975
wantehchangb207b4d2020-08-11 17:50:22 -07002976 avifTile * colorTile = avifDecoderDataCreateTile(data);
Joe Dragoe3e3bfa2020-06-02 16:33:53 -07002977 avifDecodeSample * colorSample = (avifDecodeSample *)avifArrayPushPtr(&colorTile->input->samples);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002978 colorSample->itemID = colorItem->id;
2979 colorSample->offset = 0;
2980 colorSample->size = colorItem->size;
Joe Drago060d5342020-03-03 10:53:49 -08002981 colorSample->sync = AVIF_TRUE;
wantehchangb207b4d2020-08-11 17:50:22 -07002982 data->colorTileCount = 1;
Joe Drago060d5342020-03-03 10:53:49 -08002983 }
2984
Joe Dragobe4cbb92020-09-21 12:14:05 -07002985 if (alphaItem) {
Wan-Teh Chang272aafe2020-08-12 17:31:38 -07002986 if ((data->alphaGrid.rows > 0) && (data->alphaGrid.columns > 0)) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002987 if (!avifDecoderDataGenerateImageGridTiles(data, &data->alphaGrid, alphaItem, AVIF_TRUE)) {
Wan-Teh Chang272aafe2020-08-12 17:31:38 -07002988 return AVIF_RESULT_INVALID_IMAGE_GRID;
2989 }
2990 data->alphaTileCount = data->tiles.count - data->colorTileCount;
2991 } else {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002992 if (alphaItem->size == 0) {
Wan-Teh Chang272aafe2020-08-12 17:31:38 -07002993 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
2994 }
2995
wantehchang76e16bf2020-08-12 13:01:31 -07002996 avifTile * alphaTile = avifDecoderDataCreateTile(data);
Joe Dragoe3e3bfa2020-06-02 16:33:53 -07002997 avifDecodeSample * alphaSample = (avifDecodeSample *)avifArrayPushPtr(&alphaTile->input->samples);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002998 alphaSample->itemID = alphaItem->id;
2999 alphaSample->offset = 0;
3000 alphaSample->size = alphaItem->size;
Joe Drago060d5342020-03-03 10:53:49 -08003001 alphaSample->sync = AVIF_TRUE;
3002 alphaTile->input->alpha = AVIF_TRUE;
wantehchangb207b4d2020-08-11 17:50:22 -07003003 data->alphaTileCount = 1;
Joe Drago060d5342020-03-03 10:53:49 -08003004 }
Joe Drago444f0512019-01-23 17:03:24 -08003005 }
Joe Drago33f1d362019-02-13 16:46:22 -08003006
Joe Drago46ea0582019-07-22 15:55:47 -07003007 // Set all counts and timing to safe-but-uninteresting values
3008 decoder->imageIndex = -1;
3009 decoder->imageCount = 1;
3010 decoder->imageTiming.timescale = 1;
3011 decoder->imageTiming.pts = 0;
3012 decoder->imageTiming.ptsInTimescales = 0;
3013 decoder->imageTiming.duration = 1;
3014 decoder->imageTiming.durationInTimescales = 1;
3015 decoder->timescale = 1;
3016 decoder->duration = 1;
3017 decoder->durationInTimescales = 1;
Joe Drago70cbf602019-07-24 15:30:55 -07003018
Joe Dragobe4cbb92020-09-21 12:14:05 -07003019 decoder->ioStats.colorOBUSize = colorItem->size;
3020 decoder->ioStats.alphaOBUSize = alphaItem ? alphaItem->size : 0;
Joe Drago41700852019-09-26 17:01:43 -07003021
Joe Dragoa72da5b2020-06-15 19:40:17 -07003022 const avifProperty * ispeProp = avifPropertyArrayFind(colorProperties, "ispe");
3023 if (ispeProp) {
3024 decoder->image->width = ispeProp->u.ispe.width;
3025 decoder->image->height = ispeProp->u.ispe.height;
Joe Drago41700852019-09-26 17:01:43 -07003026 } else {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003027 decoder->image->width = 0;
3028 decoder->image->height = 0;
Joe Drago41700852019-09-26 17:01:43 -07003029 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07003030 decoder->alphaPresent = (alphaItem != NULL);
Joe Dragoceb2fa02021-01-29 18:14:55 -08003031 decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorItem->premByID == alphaItem->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003032
Joe Drago9b942c22021-05-06 12:33:12 -07003033 avifResult colorItemValidationResult = avifDecoderItemValidateAV1(colorItem, &decoder->diag, decoder->strictFlags);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003034 if (colorItemValidationResult != AVIF_RESULT_OK) {
3035 return colorItemValidationResult;
3036 }
3037 if (alphaItem) {
Joe Drago9b942c22021-05-06 12:33:12 -07003038 avifResult alphaItemValidationResult = avifDecoderItemValidateAV1(alphaItem, &decoder->diag, decoder->strictFlags);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003039 if (alphaItemValidationResult != AVIF_RESULT_OK) {
3040 return alphaItemValidationResult;
3041 }
3042 }
Joe Drago00bcaaf2020-06-05 15:29:38 -07003043 }
3044
Joe Drago11f2a5e2020-07-06 10:49:00 -07003045 // Sanity check tiles
3046 for (uint32_t tileIndex = 0; tileIndex < data->tiles.count; ++tileIndex) {
3047 avifTile * tile = &data->tiles.tile[tileIndex];
3048 for (uint32_t sampleIndex = 0; sampleIndex < tile->input->samples.count; ++sampleIndex) {
Joe Drago043311b2020-07-06 16:48:41 -07003049 avifDecodeSample * sample = &tile->input->samples.sample[sampleIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003050 if (!sample->size) {
Joe Drago11f2a5e2020-07-06 10:49:00 -07003051 // Every sample must have some data
3052 return AVIF_RESULT_BMFF_PARSE_FAILED;
3053 }
3054 }
3055 }
3056
Joe Dragobf58fe72020-11-05 13:25:14 -08003057 // Find and adopt all colr boxes "at most one for a given value of colour type" (HEIF 6.5.5.1, from Amendment 3)
3058 // Accept one of each type, and bail out if more than one of a given type is provided.
3059 avifBool colrICCSeen = AVIF_FALSE;
3060 avifBool colrNCLXSeen = AVIF_FALSE;
3061 for (uint32_t propertyIndex = 0; propertyIndex < colorProperties->count; ++propertyIndex) {
3062 avifProperty * prop = &colorProperties->prop[propertyIndex];
3063
3064 if (!memcmp(prop->type, "colr", 4)) {
3065 if (prop->u.colr.hasICC) {
3066 if (colrICCSeen) {
3067 return AVIF_RESULT_BMFF_PARSE_FAILED;
3068 }
3069 colrICCSeen = AVIF_TRUE;
3070 avifImageSetProfileICC(decoder->image, prop->u.colr.icc, prop->u.colr.iccSize);
3071 }
3072 if (prop->u.colr.hasNCLX) {
3073 if (colrNCLXSeen) {
3074 return AVIF_RESULT_BMFF_PARSE_FAILED;
3075 }
3076 colrNCLXSeen = AVIF_TRUE;
3077 data->cicpSet = AVIF_TRUE;
3078 decoder->image->colorPrimaries = prop->u.colr.colorPrimaries;
3079 decoder->image->transferCharacteristics = prop->u.colr.transferCharacteristics;
3080 decoder->image->matrixCoefficients = prop->u.colr.matrixCoefficients;
3081 decoder->image->yuvRange = prop->u.colr.range;
3082 }
Joe Dragoa72da5b2020-06-15 19:40:17 -07003083 }
3084 }
3085
3086 // Transformations
3087 const avifProperty * paspProp = avifPropertyArrayFind(colorProperties, "pasp");
3088 if (paspProp) {
3089 decoder->image->transformFlags |= AVIF_TRANSFORM_PASP;
3090 memcpy(&decoder->image->pasp, &paspProp->u.pasp, sizeof(avifPixelAspectRatioBox));
3091 }
3092 const avifProperty * clapProp = avifPropertyArrayFind(colorProperties, "clap");
3093 if (clapProp) {
3094 decoder->image->transformFlags |= AVIF_TRANSFORM_CLAP;
3095 memcpy(&decoder->image->clap, &clapProp->u.clap, sizeof(avifCleanApertureBox));
3096 }
3097 const avifProperty * irotProp = avifPropertyArrayFind(colorProperties, "irot");
3098 if (irotProp) {
3099 decoder->image->transformFlags |= AVIF_TRANSFORM_IROT;
3100 memcpy(&decoder->image->irot, &irotProp->u.irot, sizeof(avifImageRotation));
3101 }
3102 const avifProperty * imirProp = avifPropertyArrayFind(colorProperties, "imir");
3103 if (imirProp) {
3104 decoder->image->transformFlags |= AVIF_TRANSFORM_IMIR;
3105 memcpy(&decoder->image->imir, &imirProp->u.imir, sizeof(avifImageMirror));
3106 }
3107
wantehchangb207b4d2020-08-11 17:50:22 -07003108 if (!data->cicpSet && (data->tiles.count > 0)) {
Joe Dragoda92a382020-06-09 17:08:45 -07003109 avifTile * firstTile = &data->tiles.tile[0];
3110 if (firstTile->input->samples.count > 0) {
3111 avifDecodeSample * sample = &firstTile->input->samples.sample[0];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003112
3113 // Harvest CICP from the AV1's sequence header, which should be very close to the front
3114 // of the first sample. Read in successively larger chunks until we successfully parse the sequence.
3115 static const size_t searchSampleChunkIncrement = 64;
Wan-Teh Changac145712021-02-08 15:53:44 -08003116 static const size_t searchSampleSizeMax = 4096;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003117 size_t searchSampleSize = 0;
Wan-Teh Changc6c2fe82020-10-08 10:44:04 -07003118 do {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003119 searchSampleSize += searchSampleChunkIncrement;
3120 if (searchSampleSize > sample->size) {
3121 searchSampleSize = sample->size;
3122 }
3123
3124 avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, searchSampleSize);
3125 if (prepareResult != AVIF_RESULT_OK) {
3126 return prepareResult;
3127 }
3128
3129 avifSequenceHeader sequenceHeader;
3130 if (avifSequenceHeaderParse(&sequenceHeader, &sample->data)) {
3131 data->cicpSet = AVIF_TRUE;
3132 decoder->image->colorPrimaries = sequenceHeader.colorPrimaries;
3133 decoder->image->transferCharacteristics = sequenceHeader.transferCharacteristics;
3134 decoder->image->matrixCoefficients = sequenceHeader.matrixCoefficients;
3135 decoder->image->yuvRange = sequenceHeader.range;
3136 break;
3137 }
Wan-Teh Changac145712021-02-08 15:53:44 -08003138 } while (searchSampleSize != sample->size && searchSampleSize < searchSampleSizeMax);
Joe Dragoda92a382020-06-09 17:08:45 -07003139 }
3140 }
3141
Joe Dragoa72da5b2020-06-15 19:40:17 -07003142 const avifProperty * av1CProp = avifPropertyArrayFind(colorProperties, "av1C");
3143 if (av1CProp) {
Joe Dragob8401122020-06-19 11:45:49 -07003144 decoder->image->depth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
3145 if (av1CProp->u.av1C.monochrome) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003146 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
Joe Drago7b2cf802020-06-09 17:57:23 -07003147 } else {
Joe Dragob8401122020-06-19 11:45:49 -07003148 if (av1CProp->u.av1C.chromaSubsamplingX && av1CProp->u.av1C.chromaSubsamplingY) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003149 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV420;
Joe Dragob8401122020-06-19 11:45:49 -07003150 } else if (av1CProp->u.av1C.chromaSubsamplingX) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003151 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV422;
Joe Drago7b2cf802020-06-09 17:57:23 -07003152
3153 } else {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003154 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
Joe Drago7b2cf802020-06-09 17:57:23 -07003155 }
3156 }
Joe Dragob8401122020-06-19 11:45:49 -07003157 decoder->image->yuvChromaSamplePosition = (avifChromaSamplePosition)av1CProp->u.av1C.chromaSamplePosition;
Joe Drago00bcaaf2020-06-05 15:29:38 -07003158 } else {
Joe Dragof48a3382020-06-19 14:13:44 -07003159 // An av1C box is mandatory in all valid AVIF configurations. Bail out.
3160 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Drago46ea0582019-07-22 15:55:47 -07003161 }
3162
Joe Drago22c1ad92019-09-26 12:46:50 -07003163 return avifDecoderFlush(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07003164}
Joe Drago444f0512019-01-23 17:03:24 -08003165
Joe Drago5998f592020-11-13 15:38:20 -08003166avifResult avifDecoderNextImage(avifDecoder * decoder)
Joe Drago46ea0582019-07-22 15:55:47 -07003167{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003168 avifDiagnosticsClearError(&decoder->diag);
3169
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003170 if (!decoder->data) {
3171 // Nothing has been parsed yet
3172 return AVIF_RESULT_NO_CONTENT;
3173 }
3174
Joe Dragobe4cbb92020-09-21 12:14:05 -07003175 if (!decoder->io || !decoder->io->read) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07003176 return AVIF_RESULT_IO_NOT_SET;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003177 }
3178
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003179 const uint32_t nextImageIndex = (uint32_t)(decoder->imageIndex + 1);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003180
3181 // Acquire all sample data for the current image first, allowing for any read call to bail out
3182 // with AVIF_RESULT_WAITING_ON_IO harmlessly / idempotently.
3183 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
3184 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
Joe Drago5998f592020-11-13 15:38:20 -08003185 if (nextImageIndex >= tile->input->samples.count) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003186 return AVIF_RESULT_NO_IMAGES_REMAINING;
3187 }
3188
Joe Drago5998f592020-11-13 15:38:20 -08003189 avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003190 avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, 0);
3191 if (prepareResult != AVIF_RESULT_OK) {
3192 return prepareResult;
3193 }
3194 }
3195
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003196 // Decode all tiles now that the sample data is ready.
Joe Drago060d5342020-03-03 10:53:49 -08003197 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
3198 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
Joe Drago41eb62b2019-02-08 15:38:18 -08003199
Wan-Teh Changb4977b32020-10-05 18:04:54 -07003200 const avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003201
3202 if (!tile->codec->getNextImage(tile->codec, sample, tile->input->alpha, tile->image)) {
Wan-Teh Chang365bd5e2021-03-23 18:28:40 -07003203 return tile->input->alpha ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED;
Joe Drago46ea0582019-07-22 15:55:47 -07003204 }
Joe Drago060d5342020-03-03 10:53:49 -08003205 }
3206
3207 if (decoder->data->tiles.count != (decoder->data->colorTileCount + decoder->data->alphaTileCount)) {
3208 // TODO: assert here? This should be impossible.
3209 return AVIF_RESULT_UNKNOWN_ERROR;
3210 }
3211
Wan-Teh Changab8d9a52020-08-12 17:17:37 -07003212 if ((decoder->data->colorGrid.rows > 0) && (decoder->data->colorGrid.columns > 0)) {
Joe Drago11d23592021-01-05 14:18:57 -08003213 if (!avifDecoderDataFillImageGrid(decoder->data, &decoder->data->colorGrid, decoder->image, 0, decoder->data->colorTileCount, AVIF_FALSE)) {
Joe Drago060d5342020-03-03 10:53:49 -08003214 return AVIF_RESULT_INVALID_IMAGE_GRID;
3215 }
3216 } else {
3217 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
3218
3219 if (decoder->data->colorTileCount != 1) {
3220 return AVIF_RESULT_DECODE_COLOR_FAILED;
3221 }
3222
3223 avifImage * srcColor = decoder->data->tiles.tile[0].image;
3224
3225 if ((decoder->image->width != srcColor->width) || (decoder->image->height != srcColor->height) ||
3226 (decoder->image->depth != srcColor->depth)) {
3227 avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL);
3228
3229 decoder->image->width = srcColor->width;
3230 decoder->image->height = srcColor->height;
3231 decoder->image->depth = srcColor->depth;
Joe Dragoc554f5f2020-06-09 18:59:51 -07003232 }
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09003233
Joe Dragoc00d5832020-08-13 16:03:28 -07003234#if 0
3235 // This code is currently unnecessary as the CICP is always set by the end of avifDecoderParse().
3236 if (!decoder->data->cicpSet) {
3237 decoder->data->cicpSet = AVIF_TRUE;
3238 decoder->image->colorPrimaries = srcColor->colorPrimaries;
3239 decoder->image->transferCharacteristics = srcColor->transferCharacteristics;
3240 decoder->image->matrixCoefficients = srcColor->matrixCoefficients;
3241 }
3242#endif
3243
Joe Drago060d5342020-03-03 10:53:49 -08003244 avifImageStealPlanes(decoder->image, srcColor, AVIF_PLANES_YUV);
3245 }
3246
Wan-Teh Changab8d9a52020-08-12 17:17:37 -07003247 if ((decoder->data->alphaGrid.rows > 0) && (decoder->data->alphaGrid.columns > 0)) {
Joe Drago11d23592021-01-05 14:18:57 -08003248 if (!avifDecoderDataFillImageGrid(decoder->data,
3249 &decoder->data->alphaGrid,
3250 decoder->image,
3251 decoder->data->colorTileCount,
3252 decoder->data->alphaTileCount,
3253 AVIF_TRUE)) {
Joe Drago060d5342020-03-03 10:53:49 -08003254 return AVIF_RESULT_INVALID_IMAGE_GRID;
3255 }
3256 } else {
3257 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
3258
3259 if (decoder->data->alphaTileCount == 0) {
3260 avifImageFreePlanes(decoder->image, AVIF_PLANES_A); // no alpha
3261 } else {
3262 if (decoder->data->alphaTileCount != 1) {
3263 return AVIF_RESULT_DECODE_ALPHA_FAILED;
3264 }
3265
3266 avifImage * srcAlpha = decoder->data->tiles.tile[decoder->data->colorTileCount].image;
3267 if ((decoder->image->width != srcAlpha->width) || (decoder->image->height != srcAlpha->height) ||
3268 (decoder->image->depth != srcAlpha->depth)) {
3269 return AVIF_RESULT_DECODE_ALPHA_FAILED;
3270 }
3271
3272 avifImageStealPlanes(decoder->image, srcAlpha, AVIF_PLANES_A);
Joe Drago3fd2db22020-08-13 16:07:24 -07003273 decoder->image->alphaRange = srcAlpha->alphaRange;
Joe Drago060d5342020-03-03 10:53:49 -08003274 }
3275 }
Joe Drago7ad3ad62019-02-07 11:17:34 -08003276
Joe Dragobe4cbb92020-09-21 12:14:05 -07003277 decoder->imageIndex = nextImageIndex;
Joe Drago46ea0582019-07-22 15:55:47 -07003278 if (decoder->data->sourceSampleTable) {
3279 // Decoding from a track! Provide timing information.
Joe Drago05559c92019-07-17 16:33:38 -07003280
Joe Dragoe9c58602020-04-13 17:23:13 -07003281 avifResult timingResult = avifDecoderNthImageTiming(decoder, decoder->imageIndex, &decoder->imageTiming);
3282 if (timingResult != AVIF_RESULT_OK) {
3283 return timingResult;
Joe Drago22c1ad92019-09-26 12:46:50 -07003284 }
Joe Dragoe9c58602020-04-13 17:23:13 -07003285 }
3286 return AVIF_RESULT_OK;
3287}
Joe Drago46ea0582019-07-22 15:55:47 -07003288
Wan-Teh Change184dc12020-05-11 12:47:21 -07003289avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming)
Joe Dragoe9c58602020-04-13 17:23:13 -07003290{
3291 if (!decoder->data) {
3292 // Nothing has been parsed yet
3293 return AVIF_RESULT_NO_CONTENT;
3294 }
3295
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07003296 if ((frameIndex > INT_MAX) || ((int)frameIndex >= decoder->imageCount)) {
Joe Dragoe9c58602020-04-13 17:23:13 -07003297 // Impossible index
3298 return AVIF_RESULT_NO_IMAGES_REMAINING;
3299 }
3300
3301 if (!decoder->data->sourceSampleTable) {
3302 // There isn't any real timing associated with this decode, so
3303 // just hand back the defaults chosen in avifDecoderReset().
3304 memcpy(outTiming, &decoder->imageTiming, sizeof(avifImageTiming));
3305 return AVIF_RESULT_OK;
3306 }
3307
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003308 outTiming->timescale = decoder->timescale;
3309 outTiming->ptsInTimescales = 0;
Joe Dragoe9c58602020-04-13 17:23:13 -07003310 for (int imageIndex = 0; imageIndex < (int)frameIndex; ++imageIndex) {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003311 outTiming->ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07003312 }
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003313 outTiming->durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, frameIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07003314
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003315 if (outTiming->timescale > 0) {
3316 outTiming->pts = (double)outTiming->ptsInTimescales / (double)outTiming->timescale;
3317 outTiming->duration = (double)outTiming->durationInTimescales / (double)outTiming->timescale;
Joe Dragoe9c58602020-04-13 17:23:13 -07003318 } else {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003319 outTiming->pts = 0.0;
3320 outTiming->duration = 0.0;
Joe Drago444f0512019-01-23 17:03:24 -08003321 }
Joe Drago46ea0582019-07-22 15:55:47 -07003322 return AVIF_RESULT_OK;
3323}
3324
Joe Drago22c1ad92019-09-26 12:46:50 -07003325avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex)
3326{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003327 avifDiagnosticsClearError(&decoder->diag);
3328
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07003329 if (frameIndex > INT_MAX) {
3330 // Impossible index
3331 return AVIF_RESULT_NO_IMAGES_REMAINING;
3332 }
3333
Joe Drago22c1ad92019-09-26 12:46:50 -07003334 int requestedIndex = (int)frameIndex;
3335 if (requestedIndex == decoder->imageIndex) {
3336 // We're here already, nothing to do
3337 return AVIF_RESULT_OK;
3338 }
3339
3340 if (requestedIndex == (decoder->imageIndex + 1)) {
3341 // it's just the next image, nothing special here
3342 return avifDecoderNextImage(decoder);
3343 }
3344
3345 if (requestedIndex >= decoder->imageCount) {
3346 // Impossible index
3347 return AVIF_RESULT_NO_IMAGES_REMAINING;
3348 }
3349
Wan-Teh Chang722fb7e2020-11-06 12:39:58 -08003350 int nearestKeyFrame = (int)avifDecoderNearestKeyframe(decoder, frameIndex);
3351 if ((nearestKeyFrame > (decoder->imageIndex + 1)) || (requestedIndex < decoder->imageIndex)) {
3352 // If we get here, a decoder flush is necessary
3353 decoder->imageIndex = nearestKeyFrame - 1; // prepare to read nearest keyframe
3354 avifDecoderFlush(decoder);
3355 }
Joe Drago22c1ad92019-09-26 12:46:50 -07003356 for (;;) {
3357 avifResult result = avifDecoderNextImage(decoder);
3358 if (result != AVIF_RESULT_OK) {
3359 return result;
3360 }
3361
3362 if (requestedIndex == decoder->imageIndex) {
3363 break;
3364 }
Joe Dragofc4144e2019-09-27 20:35:06 -07003365 }
Joe Drago22c1ad92019-09-26 12:46:50 -07003366 return AVIF_RESULT_OK;
3367}
3368
Wan-Teh Change184dc12020-05-11 12:47:21 -07003369avifBool avifDecoderIsKeyframe(const avifDecoder * decoder, uint32_t frameIndex)
Joe Drago22c1ad92019-09-26 12:46:50 -07003370{
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003371 if (!decoder->data) {
3372 // Nothing has been parsed yet
3373 return AVIF_FALSE;
3374 }
3375
Joe Drago060d5342020-03-03 10:53:49 -08003376 if ((decoder->data->tiles.count > 0) && decoder->data->tiles.tile[0].input) {
3377 if (frameIndex < decoder->data->tiles.tile[0].input->samples.count) {
3378 return decoder->data->tiles.tile[0].input->samples.sample[frameIndex].sync;
Joe Drago22c1ad92019-09-26 12:46:50 -07003379 }
3380 }
3381 return AVIF_FALSE;
3382}
3383
Wan-Teh Change184dc12020-05-11 12:47:21 -07003384uint32_t avifDecoderNearestKeyframe(const avifDecoder * decoder, uint32_t frameIndex)
Joe Drago22c1ad92019-09-26 12:46:50 -07003385{
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003386 if (!decoder->data) {
3387 // Nothing has been parsed yet
3388 return 0;
3389 }
3390
Joe Drago22c1ad92019-09-26 12:46:50 -07003391 for (; frameIndex != 0; --frameIndex) {
3392 if (avifDecoderIsKeyframe(decoder, frameIndex)) {
3393 break;
3394 }
3395 }
3396 return frameIndex;
3397}
3398
Joe Dragobe4cbb92020-09-21 12:14:05 -07003399avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image)
Joe Drago46ea0582019-07-22 15:55:47 -07003400{
Joe Dragobe4cbb92020-09-21 12:14:05 -07003401 avifResult result = avifDecoderParse(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07003402 if (result != AVIF_RESULT_OK) {
3403 return result;
Joe Drago05559c92019-07-17 16:33:38 -07003404 }
Joe Drago46ea0582019-07-22 15:55:47 -07003405 result = avifDecoderNextImage(decoder);
3406 if (result != AVIF_RESULT_OK) {
3407 return result;
3408 }
Joe Drago250221a2020-06-01 11:11:06 -07003409 avifImageCopy(image, decoder->image, AVIF_PLANES_ALL);
Joe Drago46ea0582019-07-22 15:55:47 -07003410 return AVIF_RESULT_OK;
Joe Drago444f0512019-01-23 17:03:24 -08003411}
Joe Dragobe4cbb92020-09-21 12:14:05 -07003412
Wan-Teh Chang7de44452020-10-08 11:43:18 -07003413avifResult avifDecoderReadMemory(avifDecoder * decoder, avifImage * image, const uint8_t * data, size_t size)
Joe Dragobe4cbb92020-09-21 12:14:05 -07003414{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003415 avifDiagnosticsClearError(&decoder->diag);
Wan-Teh Chang7de44452020-10-08 11:43:18 -07003416 avifResult result = avifDecoderSetIOMemory(decoder, data, size);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003417 if (result != AVIF_RESULT_OK) {
3418 return result;
3419 }
3420 return avifDecoderRead(decoder, image);
3421}
3422
3423avifResult avifDecoderReadFile(avifDecoder * decoder, avifImage * image, const char * filename)
3424{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003425 avifDiagnosticsClearError(&decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003426 avifResult result = avifDecoderSetIOFile(decoder, filename);
3427 if (result != AVIF_RESULT_OK) {
3428 return result;
3429 }
3430 return avifDecoderRead(decoder, image);
3431}