blob: 7be9f1f0ca425615f2d47c8ade82491221ca8b87 [file] [log] [blame]
Joe Drago444f0512019-01-23 17:03:24 -08001// Copyright 2019 Joe Drago. All rights reserved.
2// SPDX-License-Identifier: BSD-2-Clause
3
4#include "avif/internal.h"
5
Wan-Teh Changb856dc22020-09-28 13:00:56 -07006#include <assert.h>
Joe Drago4d5f4a42021-04-28 13:11:45 -07007#include <inttypes.h>
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07008#include <limits.h>
Joe Drago444f0512019-01-23 17:03:24 -08009#include <string.h>
10
Joe Dragocd1e4c32019-02-08 11:26:31 -080011#define AUXTYPE_SIZE 64
Joe Dragof6a42272019-11-21 15:21:41 -080012#define CONTENTTYPE_SIZE 64
Joe Drago8f7a3002019-02-07 19:35:37 -080013
Joe Drago6500fd62019-10-08 17:17:34 -070014// class VisualSampleEntry(codingname) extends SampleEntry(codingname) {
15// unsigned int(16) pre_defined = 0;
16// const unsigned int(16) reserved = 0;
17// unsigned int(32)[3] pre_defined = 0;
18// unsigned int(16) width;
19// unsigned int(16) height;
20// template unsigned int(32) horizresolution = 0x00480000; // 72 dpi
21// template unsigned int(32) vertresolution = 0x00480000; // 72 dpi
22// const unsigned int(32) reserved = 0;
23// template unsigned int(16) frame_count = 1;
24// string[32] compressorname;
25// template unsigned int(16) depth = 0x0018;
26// int(16) pre_defined = -1;
27// // other boxes from derived specifications
28// CleanApertureBox clap; // optional
29// PixelAspectRatioBox pasp; // optional
30// }
31static const size_t VISUALSAMPLEENTRY_SIZE = 78;
32
Joe Dragof6a42272019-11-21 15:21:41 -080033static const char xmpContentType[] = CONTENT_TYPE_XMP;
34static const size_t xmpContentTypeSize = sizeof(xmpContentType);
35
Joe Dragoc60ccae2021-03-31 10:30:48 -070036// The only supported ipma box values for both version and flags are [0,1], so there technically
37// can't be more than 4 unique tuples right now.
38#define MAX_IPMA_VERSION_AND_FLAGS_SEEN 4
39
Joe Dragobffba3b2021-05-26 15:46:10 -070040#define MAX_AV1_LAYER_COUNT 4
41
Joe Dragob13e5722019-02-08 19:07:25 -080042// ---------------------------------------------------------------------------
43// Box data structures
44
45// ftyp
46typedef struct avifFileType
47{
48 uint8_t majorBrand[4];
49 uint32_t minorVersion;
Wan-Teh Chang6da0a882020-07-01 12:19:31 -070050 // If not null, points to a memory block of 4 * compatibleBrandsCount bytes.
51 const uint8_t * compatibleBrands;
Joe Dragob13e5722019-02-08 19:07:25 -080052 int compatibleBrandsCount;
53} avifFileType;
54
55// ispe
Joe Drago8f7a3002019-02-07 19:35:37 -080056typedef struct avifImageSpatialExtents
57{
58 uint32_t width;
59 uint32_t height;
60} avifImageSpatialExtents;
Joe Drago444f0512019-01-23 17:03:24 -080061
Joe Dragob13e5722019-02-08 19:07:25 -080062// auxC
Joe Dragocd1e4c32019-02-08 11:26:31 -080063typedef struct avifAuxiliaryType
64{
65 char auxType[AUXTYPE_SIZE];
66} avifAuxiliaryType;
67
Joe Dragof6a42272019-11-21 15:21:41 -080068// infe mime content_type
69typedef struct avifContentType
70{
71 char contentType[CONTENTTYPE_SIZE];
72} avifContentType;
73
Joe Dragob13e5722019-02-08 19:07:25 -080074// colr
Joe Drago41eb62b2019-02-08 15:38:18 -080075typedef struct avifColourInformationBox
76{
Joe Dragoa0da4a42020-05-08 14:27:40 -070077 avifBool hasICC;
Joe Drago345aaa12019-09-25 13:42:12 -070078 const uint8_t * icc;
Joe Drago41eb62b2019-02-08 15:38:18 -080079 size_t iccSize;
Joe Dragoa0da4a42020-05-08 14:27:40 -070080
81 avifBool hasNCLX;
Wan-Teh Chang559def52021-02-01 14:25:31 -080082 avifColorPrimaries colorPrimaries;
83 avifTransferCharacteristics transferCharacteristics;
84 avifMatrixCoefficients matrixCoefficients;
Joe Dragoa0da4a42020-05-08 14:27:40 -070085 avifRange range;
Joe Drago41eb62b2019-02-08 15:38:18 -080086} avifColourInformationBox;
87
Joe Drago60421562020-04-23 11:32:26 -070088#define MAX_PIXI_PLANE_DEPTHS 4
89typedef struct avifPixelInformationProperty
90{
91 uint8_t planeDepths[MAX_PIXI_PLANE_DEPTHS];
92 uint8_t planeCount;
93} avifPixelInformationProperty;
94
Joe Dragobffba3b2021-05-26 15:46:10 -070095typedef struct avifOperatingPointSelectorProperty
96{
97 uint8_t opIndex;
98} avifOperatingPointSelectorProperty;
99
100typedef struct avifLayerSelectorProperty
101{
102 uint16_t layerID;
103} avifLayerSelectorProperty;
104
105typedef struct avifAV1LayeredImageIndexingProperty
106{
107 uint32_t layerSize[3];
108} avifAV1LayeredImageIndexingProperty;
109
Joe Dragob13e5722019-02-08 19:07:25 -0800110// ---------------------------------------------------------------------------
111// Top-level structures
112
Joe Drago9f2b87b2020-06-03 19:36:38 -0700113struct avifMeta;
114
Joe Dragoa72da5b2020-06-15 19:40:17 -0700115// Temporary storage for ipco/stsd contents until they can be associated and memcpy'd to an avifDecoderItem
Joe Drago8f7a3002019-02-07 19:35:37 -0800116typedef struct avifProperty
117{
118 uint8_t type[4];
Wan-Teh Chang2031dc12020-05-22 09:24:46 -0700119 union
120 {
121 avifImageSpatialExtents ispe;
122 avifAuxiliaryType auxC;
123 avifColourInformationBox colr;
124 avifCodecConfigurationBox av1C;
125 avifPixelAspectRatioBox pasp;
126 avifCleanApertureBox clap;
127 avifImageRotation irot;
128 avifImageMirror imir;
129 avifPixelInformationProperty pixi;
Joe Dragobffba3b2021-05-26 15:46:10 -0700130 avifOperatingPointSelectorProperty a1op;
131 avifLayerSelectorProperty lsel;
132 avifAV1LayeredImageIndexingProperty a1lx;
Wan-Teh Chang2031dc12020-05-22 09:24:46 -0700133 } u;
Joe Drago8f7a3002019-02-07 19:35:37 -0800134} avifProperty;
Joe Drago05559c92019-07-17 16:33:38 -0700135AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop);
Joe Drago8f7a3002019-02-07 19:35:37 -0800136
Joe Dragoa72da5b2020-06-15 19:40:17 -0700137static const avifProperty * avifPropertyArrayFind(const avifPropertyArray * properties, const char * type)
138{
139 for (uint32_t propertyIndex = 0; propertyIndex < properties->count; ++propertyIndex) {
140 avifProperty * prop = &properties->prop[propertyIndex];
141 if (!memcmp(prop->type, type, 4)) {
142 return prop;
143 }
144 }
145 return NULL;
146}
147
Joe Drago4bcdfde2020-11-13 17:50:55 -0800148AVIF_ARRAY_DECLARE(avifExtentArray, avifExtent, extent);
Joe Dragoa4956902020-08-28 01:24:35 -0700149
Joe Dragoa72da5b2020-06-15 19:40:17 -0700150// one "item" worth for decoding (all iref, iloc, iprp, etc refer to one of these)
151typedef struct avifDecoderItem
152{
153 uint32_t id;
Joe Dragoba1eb492020-06-22 17:05:04 -0700154 struct avifMeta * meta; // Unowned; A back-pointer for convenience
Joe Dragoa72da5b2020-06-15 19:40:17 -0700155 uint8_t type[4];
Joe Drago217056b2020-11-13 16:19:35 -0800156 size_t size;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700157 uint32_t idatID; // If non-zero, offset is relative to this idat box (iloc construction_method==1)
Joe Drago46104d62021-05-27 18:17:29 -0700158 uint32_t width; // Set from this item's ispe property, if present
159 uint32_t height; // Set from this item's ispe property, if present
Joe Dragoa72da5b2020-06-15 19:40:17 -0700160 avifContentType contentType;
161 avifPropertyArray properties;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800162 avifExtentArray extents; // All extent offsets/sizes
Joe Dragobe4cbb92020-09-21 12:14:05 -0700163 avifRWData mergedExtents; // if set, is a single contiguous block of this item's extents (unused when extents.count == 1)
164 avifBool ownsMergedExtents; // if true, mergedExtents must be freed when this item is destroyed
165 avifBool partialMergedExtents; // If true, mergedExtents doesn't have all of the item data yet
166 uint32_t thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
167 uint32_t auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID}
168 uint32_t descForID; // if non-zero, this item is a content description for Item #{descForID}
169 uint32_t dimgForID; // if non-zero, this item is a derived image for Item #{dimgForID}
Yuan Tonge4850be2021-01-22 14:21:25 +0800170 uint32_t premByID; // if non-zero, this item is premultiplied by Item #{premByID}
Joe Dragoa72da5b2020-06-15 19:40:17 -0700171 avifBool hasUnsupportedEssentialProperty; // If true, this item cites a property flagged as 'essential' that libavif doesn't support (yet). Ignore the item, if so.
Joe Dragobffba3b2021-05-26 15:46:10 -0700172 avifBool ipmaSeen; // if true, this item already received a property association
173 avifBool progressive; // if true, this item has progressive layers (a1lx), but does not select a specific layer (lsel)
Joe Dragoa72da5b2020-06-15 19:40:17 -0700174} avifDecoderItem;
175AVIF_ARRAY_DECLARE(avifDecoderItemArray, avifDecoderItem, item);
176
Joe Dragof6a42272019-11-21 15:21:41 -0800177// idat storage
Joe Drago800b47f2020-03-18 16:22:37 -0700178typedef struct avifDecoderItemData
Joe Dragof6a42272019-11-21 15:21:41 -0800179{
180 uint32_t id;
Joe Dragobe4cbb92020-09-21 12:14:05 -0700181 avifRWData data;
Joe Drago800b47f2020-03-18 16:22:37 -0700182} avifDecoderItemData;
183AVIF_ARRAY_DECLARE(avifDecoderItemDataArray, avifDecoderItemData, idat);
Joe Dragof6a42272019-11-21 15:21:41 -0800184
Joe Drago060d5342020-03-03 10:53:49 -0800185// grid storage
186typedef struct avifImageGrid
187{
Joe Drago79ebcf32020-11-18 22:37:10 -0800188 uint32_t rows; // Legal range: [1-256]
189 uint32_t columns; // Legal range: [1-256]
Joe Drago060d5342020-03-03 10:53:49 -0800190 uint32_t outputWidth;
191 uint32_t outputHeight;
192} avifImageGrid;
193
Joe Dragoae7e2c32019-07-18 15:22:25 -0700194// ---------------------------------------------------------------------------
195// avifTrack
196
197typedef struct avifSampleTableChunk
198{
199 uint64_t offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700200} avifSampleTableChunk;
201AVIF_ARRAY_DECLARE(avifSampleTableChunkArray, avifSampleTableChunk, chunk);
202
203typedef struct avifSampleTableSampleToChunk
204{
205 uint32_t firstChunk;
206 uint32_t samplesPerChunk;
207 uint32_t sampleDescriptionIndex;
208} avifSampleTableSampleToChunk;
209AVIF_ARRAY_DECLARE(avifSampleTableSampleToChunkArray, avifSampleTableSampleToChunk, sampleToChunk);
210
211typedef struct avifSampleTableSampleSize
212{
213 uint32_t size;
214} avifSampleTableSampleSize;
215AVIF_ARRAY_DECLARE(avifSampleTableSampleSizeArray, avifSampleTableSampleSize, sampleSize);
216
217typedef struct avifSampleTableTimeToSample
218{
219 uint32_t sampleCount;
220 uint32_t sampleDelta;
221} avifSampleTableTimeToSample;
222AVIF_ARRAY_DECLARE(avifSampleTableTimeToSampleArray, avifSampleTableTimeToSample, timeToSample);
223
Joe Drago22c1ad92019-09-26 12:46:50 -0700224typedef struct avifSyncSample
225{
226 uint32_t sampleNumber;
227} avifSyncSample;
228AVIF_ARRAY_DECLARE(avifSyncSampleArray, avifSyncSample, syncSample);
229
Joe Drago2c0924c2019-09-26 17:41:01 -0700230typedef struct avifSampleDescription
231{
232 uint8_t format[4];
Joe Dragoa72da5b2020-06-15 19:40:17 -0700233 avifPropertyArray properties;
Joe Drago2c0924c2019-09-26 17:41:01 -0700234} avifSampleDescription;
235AVIF_ARRAY_DECLARE(avifSampleDescriptionArray, avifSampleDescription, description);
236
Joe Dragoae7e2c32019-07-18 15:22:25 -0700237typedef struct avifSampleTable
238{
239 avifSampleTableChunkArray chunks;
Joe Drago2c0924c2019-09-26 17:41:01 -0700240 avifSampleDescriptionArray sampleDescriptions;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700241 avifSampleTableSampleToChunkArray sampleToChunks;
242 avifSampleTableSampleSizeArray sampleSizes;
243 avifSampleTableTimeToSampleArray timeToSamples;
Joe Drago22c1ad92019-09-26 12:46:50 -0700244 avifSyncSampleArray syncSamples;
Joe Drago370be3f2020-02-07 15:59:42 -0800245 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 -0700246} avifSampleTable;
247
Joe Drago46ea0582019-07-22 15:55:47 -0700248static avifSampleTable * avifSampleTableCreate()
Joe Dragoae7e2c32019-07-18 15:22:25 -0700249{
250 avifSampleTable * sampleTable = (avifSampleTable *)avifAlloc(sizeof(avifSampleTable));
251 memset(sampleTable, 0, sizeof(avifSampleTable));
252 avifArrayCreate(&sampleTable->chunks, sizeof(avifSampleTableChunk), 16);
Joe Drago2c0924c2019-09-26 17:41:01 -0700253 avifArrayCreate(&sampleTable->sampleDescriptions, sizeof(avifSampleDescription), 2);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700254 avifArrayCreate(&sampleTable->sampleToChunks, sizeof(avifSampleTableSampleToChunk), 16);
255 avifArrayCreate(&sampleTable->sampleSizes, sizeof(avifSampleTableSampleSize), 16);
256 avifArrayCreate(&sampleTable->timeToSamples, sizeof(avifSampleTableTimeToSample), 16);
Joe Drago759e6742019-09-26 18:07:21 -0700257 avifArrayCreate(&sampleTable->syncSamples, sizeof(avifSyncSample), 16);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700258 return sampleTable;
259}
260
Joe Drago46ea0582019-07-22 15:55:47 -0700261static void avifSampleTableDestroy(avifSampleTable * sampleTable)
Joe Dragoae7e2c32019-07-18 15:22:25 -0700262{
263 avifArrayDestroy(&sampleTable->chunks);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700264 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
265 avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
266 avifArrayDestroy(&description->properties);
267 }
Joe Drago2c0924c2019-09-26 17:41:01 -0700268 avifArrayDestroy(&sampleTable->sampleDescriptions);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700269 avifArrayDestroy(&sampleTable->sampleToChunks);
270 avifArrayDestroy(&sampleTable->sampleSizes);
271 avifArrayDestroy(&sampleTable->timeToSamples);
Joe Drago22c1ad92019-09-26 12:46:50 -0700272 avifArrayDestroy(&sampleTable->syncSamples);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700273 avifFree(sampleTable);
274}
275
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700276static uint32_t avifSampleTableGetImageDelta(const avifSampleTable * sampleTable, int imageIndex)
Joe Drago46ea0582019-07-22 15:55:47 -0700277{
278 int maxSampleIndex = 0;
279 for (uint32_t i = 0; i < sampleTable->timeToSamples.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700280 const avifSampleTableTimeToSample * timeToSample = &sampleTable->timeToSamples.timeToSample[i];
Joe Drago46ea0582019-07-22 15:55:47 -0700281 maxSampleIndex += timeToSample->sampleCount;
282 if ((imageIndex < maxSampleIndex) || (i == (sampleTable->timeToSamples.count - 1))) {
283 return timeToSample->sampleDelta;
284 }
285 }
286
287 // TODO: fail here?
288 return 1;
289}
290
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700291static avifBool avifSampleTableHasFormat(const avifSampleTable * sampleTable, const char * format)
Joe Drago2c0924c2019-09-26 17:41:01 -0700292{
293 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
294 if (!memcmp(sampleTable->sampleDescriptions.description[i].format, format, 4)) {
295 return AVIF_TRUE;
296 }
297 }
298 return AVIF_FALSE;
299}
300
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700301static uint32_t avifCodecConfigurationBoxGetDepth(const avifCodecConfigurationBox * av1C)
Joe Drago6500fd62019-10-08 17:17:34 -0700302{
303 if (av1C->twelveBit) {
304 return 12;
305 } else if (av1C->highBitdepth) {
306 return 10;
307 }
308 return 8;
309}
310
Joe Drago4a70b472021-05-06 17:21:33 -0700311// This is used as a hint to validating the clap box in avifDecoderItemValidateAV1.
312static avifPixelFormat avifCodecConfigurationBoxGetFormat(const avifCodecConfigurationBox * av1C)
313{
314 if (av1C->monochrome) {
315 return AVIF_PIXEL_FORMAT_YUV400;
Wan-Teh Changa1aa19c2021-05-28 14:29:25 -0700316 } else if (av1C->chromaSubsamplingY == 1) {
Joe Drago4a70b472021-05-06 17:21:33 -0700317 return AVIF_PIXEL_FORMAT_YUV420;
Wan-Teh Changa1aa19c2021-05-28 14:29:25 -0700318 } else if (av1C->chromaSubsamplingX == 1) {
Joe Drago4a70b472021-05-06 17:21:33 -0700319 return AVIF_PIXEL_FORMAT_YUV422;
320 }
321 return AVIF_PIXEL_FORMAT_YUV444;
322}
323
Joe Dragoa72da5b2020-06-15 19:40:17 -0700324static const avifPropertyArray * avifSampleTableGetProperties(const avifSampleTable * sampleTable)
Joe Drago6500fd62019-10-08 17:17:34 -0700325{
326 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700327 const avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
Joe Dragoa72da5b2020-06-15 19:40:17 -0700328 if (!memcmp(description->format, "av01", 4)) {
329 return &description->properties;
Joe Drago6500fd62019-10-08 17:17:34 -0700330 }
331 }
Joe Drago00bcaaf2020-06-05 15:29:38 -0700332 return NULL;
Joe Drago6500fd62019-10-08 17:17:34 -0700333}
334
Joe Dragoae7e2c32019-07-18 15:22:25 -0700335// one video track ("trak" contents)
336typedef struct avifTrack
337{
338 uint32_t id;
Wan-Teh Chang53adb6a2021-02-20 16:58:43 -0800339 uint32_t auxForID; // if non-zero, this track is an auxC plane for Track #{auxForID}
340 uint32_t premByID; // if non-zero, this track is premultiplied by Track #{premByID}
Joe Dragoae7e2c32019-07-18 15:22:25 -0700341 uint32_t mediaTimescale;
342 uint64_t mediaDuration;
Joe Dragofc4144e2019-09-27 20:35:06 -0700343 uint32_t width;
344 uint32_t height;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700345 avifSampleTable * sampleTable;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700346 struct avifMeta * meta;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700347} avifTrack;
348AVIF_ARRAY_DECLARE(avifTrackArray, avifTrack, track);
349
350// ---------------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -0700351// avifCodecDecodeInput
352
Joe Drago399df4f2019-07-23 16:45:14 -0700353avifCodecDecodeInput * avifCodecDecodeInputCreate(void)
Joe Drago46ea0582019-07-22 15:55:47 -0700354{
355 avifCodecDecodeInput * decodeInput = (avifCodecDecodeInput *)avifAlloc(sizeof(avifCodecDecodeInput));
356 memset(decodeInput, 0, sizeof(avifCodecDecodeInput));
Joe Dragoe3e3bfa2020-06-02 16:33:53 -0700357 avifArrayCreate(&decodeInput->samples, sizeof(avifDecodeSample), 1);
Joe Drago46ea0582019-07-22 15:55:47 -0700358 return decodeInput;
359}
360
Joe Drago8b34ad72019-07-22 16:56:32 -0700361void avifCodecDecodeInputDestroy(avifCodecDecodeInput * decodeInput)
Joe Drago46ea0582019-07-22 15:55:47 -0700362{
Joe Dragobe4cbb92020-09-21 12:14:05 -0700363 for (uint32_t sampleIndex = 0; sampleIndex < decodeInput->samples.count; ++sampleIndex) {
364 avifDecodeSample * sample = &decodeInput->samples.sample[sampleIndex];
365 if (sample->ownsData) {
366 avifRWDataFree((avifRWData *)&sample->data);
367 }
368 }
Joe Drago46ea0582019-07-22 15:55:47 -0700369 avifArrayDestroy(&decodeInput->samples);
370 avifFree(decodeInput);
371}
372
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700373// Returns how many samples are in the chunk.
374static uint32_t avifGetSampleCountOfChunk(const avifSampleTableSampleToChunkArray * sampleToChunks, uint32_t chunkIndex)
375{
376 uint32_t sampleCount = 0;
377 for (int sampleToChunkIndex = sampleToChunks->count - 1; sampleToChunkIndex >= 0; --sampleToChunkIndex) {
378 const avifSampleTableSampleToChunk * sampleToChunk = &sampleToChunks->sampleToChunk[sampleToChunkIndex];
379 if (sampleToChunk->firstChunk <= (chunkIndex + 1)) {
380 sampleCount = sampleToChunk->samplesPerChunk;
381 break;
382 }
383 }
384 return sampleCount;
385}
386
Joe Dragobffba3b2021-05-26 15:46:10 -0700387static avifBool avifCodecDecodeInputFillFromSampleTable(avifCodecDecodeInput * decodeInput,
388 avifSampleTable * sampleTable,
389 const uint32_t imageCountLimit,
390 const uint64_t sizeHint,
391 avifDiagnostics * diag)
Joe Drago46ea0582019-07-22 15:55:47 -0700392{
Joe Dragod2340b42021-03-14 13:20:02 -0700393 if (imageCountLimit) {
394 // Verify that the we're not about to exceed the frame count limit.
395
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700396 uint32_t imageCountLeft = imageCountLimit;
Joe Dragod2340b42021-03-14 13:20:02 -0700397 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
398 // First, figure out how many samples are in this chunk
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700399 uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex);
Joe Dragod2340b42021-03-14 13:20:02 -0700400 if (sampleCount == 0) {
401 // chunks with 0 samples are invalid
Joe Drago4d5f4a42021-04-28 13:11:45 -0700402 avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples");
Joe Dragod2340b42021-03-14 13:20:02 -0700403 return AVIF_FALSE;
404 }
405
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700406 if (sampleCount > imageCountLeft) {
Joe Dragod2340b42021-03-14 13:20:02 -0700407 // This file exceeds the imageCountLimit, bail out
Joe Drago4d5f4a42021-04-28 13:11:45 -0700408 avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit");
Joe Dragod2340b42021-03-14 13:20:02 -0700409 return AVIF_FALSE;
410 }
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700411 imageCountLeft -= sampleCount;
Joe Dragod2340b42021-03-14 13:20:02 -0700412 }
413 }
414
Joe Drago46ea0582019-07-22 15:55:47 -0700415 uint32_t sampleSizeIndex = 0;
416 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
417 avifSampleTableChunk * chunk = &sampleTable->chunks.chunk[chunkIndex];
418
419 // First, figure out how many samples are in this chunk
Wan-Teh Changb8c89442021-03-19 14:17:57 -0700420 uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex);
Joe Drago46ea0582019-07-22 15:55:47 -0700421 if (sampleCount == 0) {
422 // chunks with 0 samples are invalid
Joe Drago4d5f4a42021-04-28 13:11:45 -0700423 avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples");
Joe Drago46ea0582019-07-22 15:55:47 -0700424 return AVIF_FALSE;
425 }
426
427 uint64_t sampleOffset = chunk->offset;
428 for (uint32_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
Joe Drago370be3f2020-02-07 15:59:42 -0800429 uint32_t sampleSize = sampleTable->allSamplesSize;
430 if (sampleSize == 0) {
431 if (sampleSizeIndex >= sampleTable->sampleSizes.count) {
432 // We've run out of samples to sum
Joe Drago4d5f4a42021-04-28 13:11:45 -0700433 avifDiagnosticsPrintf(diag, "Truncated sample table");
Joe Drago370be3f2020-02-07 15:59:42 -0800434 return AVIF_FALSE;
435 }
436 avifSampleTableSampleSize * sampleSizePtr = &sampleTable->sampleSizes.sampleSize[sampleSizeIndex];
437 sampleSize = sampleSizePtr->size;
Joe Drago46ea0582019-07-22 15:55:47 -0700438 }
439
Joe Dragoe3e3bfa2020-06-02 16:33:53 -0700440 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700441 sample->offset = sampleOffset;
442 sample->size = sampleSize;
Joe Drago9d195462021-06-14 14:14:35 -0700443 sample->spatialID = AVIF_SPATIAL_ID_UNSET; // Not filtering by spatial_id
444 sample->sync = AVIF_FALSE; // to potentially be set to true following the outer loop
Joe Drago46ea0582019-07-22 15:55:47 -0700445
Wan-Teh Chang3ca14242020-09-30 16:39:38 -0700446 if (sampleSize > UINT64_MAX - sampleOffset) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700447 avifDiagnosticsPrintf(diag,
Joe Drago48867292021-05-04 13:23:08 -0700448 "Sample table contains an offset/size pair which overflows: [%" PRIu64 " / %u]",
Joe Drago4d5f4a42021-04-28 13:11:45 -0700449 sampleOffset,
450 sampleSize);
Wan-Teh Chang3ca14242020-09-30 16:39:38 -0700451 return AVIF_FALSE;
452 }
Wan-Teh Chang136d7572020-10-08 15:13:42 -0700453 if (sizeHint && ((sampleOffset + sampleSize) > sizeHint)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -0700454 avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data");
Joe Drago34c0d312020-04-30 15:23:03 -0700455 return AVIF_FALSE;
456 }
Joe Drago46ea0582019-07-22 15:55:47 -0700457
Joe Drago370be3f2020-02-07 15:59:42 -0800458 sampleOffset += sampleSize;
Joe Drago46ea0582019-07-22 15:55:47 -0700459 ++sampleSizeIndex;
460 }
461 }
Joe Drago22c1ad92019-09-26 12:46:50 -0700462
463 // Mark appropriate samples as sync
464 for (uint32_t syncSampleIndex = 0; syncSampleIndex < sampleTable->syncSamples.count; ++syncSampleIndex) {
465 uint32_t frameIndex = sampleTable->syncSamples.syncSample[syncSampleIndex].sampleNumber - 1; // sampleNumber is 1-based
466 if (frameIndex < decodeInput->samples.count) {
467 decodeInput->samples.sample[frameIndex].sync = AVIF_TRUE;
468 }
469 }
470
471 // Assume frame 0 is sync, just in case the stss box is absent in the BMFF. (Unnecessary?)
472 if (decodeInput->samples.count > 0) {
473 decodeInput->samples.sample[0].sync = AVIF_TRUE;
474 }
Joe Drago46ea0582019-07-22 15:55:47 -0700475 return AVIF_TRUE;
476}
477
Joe Dragobffba3b2021-05-26 15:46:10 -0700478static avifBool avifCodecDecodeInputFillFromDecoderItem(avifCodecDecodeInput * decodeInput,
479 avifDecoderItem * item,
480 avifBool allowProgressive,
481 const uint32_t imageCountLimit,
482 const uint64_t sizeHint,
483 avifDiagnostics * diag)
484{
485 if (sizeHint && (item->size > sizeHint)) {
486 avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data");
487 return AVIF_FALSE;
488 }
489
490 uint8_t layerCount = 0;
491 size_t layerSizes[4] = { 0 };
492 const avifProperty * a1lxProp = avifPropertyArrayFind(&item->properties, "a1lx");
493 if (a1lxProp) {
494 // Calculate layer count and all layer sizes from the a1lx box, and then validate
495
496 size_t remainingSize = item->size;
497 for (int i = 0; i < 3; ++i) {
498 ++layerCount;
499
500 const size_t layerSize = (size_t)a1lxProp->u.a1lx.layerSize[i];
501 if (layerSize) {
502 if (layerSize >= remainingSize) { // >= instead of > because there must be room for the last layer
503 avifDiagnosticsPrintf(diag, "a1lx layer index [%d] does not fit in item size", i);
504 return AVIF_FALSE;
505 }
506 layerSizes[i] = layerSize;
507 remainingSize -= layerSize;
508 } else {
509 layerSizes[i] = remainingSize;
510 remainingSize = 0;
511 break;
512 }
513 }
514 if (remainingSize > 0) {
515 assert(layerCount == 3);
516 ++layerCount;
517 layerSizes[3] = remainingSize;
518 }
519 }
520
521 const avifProperty * lselProp = avifPropertyArrayFind(&item->properties, "lsel");
522 item->progressive = (a1lxProp && !lselProp); // Progressive images offer layers via the a1lxProp, but don't specify a layer selection with lsel.
523 if (lselProp) {
524 // Layer selection. This requires that the underlying AV1 codec decodes all layers,
525 // and then only returns the requested layer as a single frame. To the user of libavif,
526 // this appears to be a single frame.
527
528 decodeInput->allLayers = AVIF_TRUE;
529
530 size_t sampleSize = 0;
531 if (layerCount > 0) {
532 // Optimization: If we're selecting a layer that doesn't require the entire image's payload (hinted via the a1lx box)
533
534 if (lselProp->u.lsel.layerID >= layerCount) {
535 avifDiagnosticsPrintf(diag,
536 "lsel property requests layer index [%u] which isn't present in a1lx property ([%u] layers)",
537 lselProp->u.lsel.layerID,
538 layerCount);
539 return AVIF_FALSE;
540 }
541
542 for (uint8_t i = 0; i <= lselProp->u.lsel.layerID; ++i) {
543 sampleSize += layerSizes[i];
544 }
545 } else {
546 // This layer's payload subsection is unknown, just use the whole payload
547 sampleSize = item->size;
548 }
549
550 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
551 sample->itemID = item->id;
552 assert(lselProp->u.lsel.layerID < MAX_AV1_LAYER_COUNT);
553 sample->offset = 0;
554 sample->size = sampleSize;
Joe Drago9d195462021-06-14 14:14:35 -0700555 sample->spatialID = (uint8_t)lselProp->u.lsel.layerID;
Joe Dragobffba3b2021-05-26 15:46:10 -0700556 sample->sync = AVIF_TRUE;
557 } else if (allowProgressive && a1lxProp) {
558 // Progressive image. Decode all layers and expose them all to the user.
559
560 if (imageCountLimit && (layerCount > imageCountLimit)) {
561 avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit (progressive)");
562 return AVIF_FALSE;
563 }
564
565 decodeInput->allLayers = AVIF_TRUE;
566
567 size_t offset = 0;
568 for (int i = 0; i < layerCount; ++i) {
569 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
570 sample->itemID = item->id;
571 sample->offset = offset;
572 sample->size = layerSizes[i];
Joe Drago9d195462021-06-14 14:14:35 -0700573 sample->spatialID = AVIF_SPATIAL_ID_UNSET;
Joe Dragobffba3b2021-05-26 15:46:10 -0700574 sample->sync = (i == 0); // Assume all layers depend on the first layer
575
576 offset += layerSizes[i];
577 }
578 } else {
579 // Typical case: Use the entire item's payload for a single frame output
580
581 avifDecodeSample * sample = (avifDecodeSample *)avifArrayPushPtr(&decodeInput->samples);
582 sample->itemID = item->id;
583 sample->offset = 0;
584 sample->size = item->size;
Joe Drago9d195462021-06-14 14:14:35 -0700585 sample->spatialID = AVIF_SPATIAL_ID_UNSET;
Joe Dragobffba3b2021-05-26 15:46:10 -0700586 sample->sync = AVIF_TRUE;
587 }
588 return AVIF_TRUE;
589}
590
Joe Drago46ea0582019-07-22 15:55:47 -0700591// ---------------------------------------------------------------------------
Joe Drago8f439092020-08-28 15:05:17 -0700592// Helper macros / functions
Joe Dragoa72da5b2020-06-15 19:40:17 -0700593
Joe Drago618ed5f2021-05-04 12:13:24 -0700594#define BEGIN_STREAM(VARNAME, PTR, SIZE, DIAG, CONTEXT) \
595 avifROStream VARNAME; \
596 avifROData VARNAME##_roData; \
597 VARNAME##_roData.data = PTR; \
598 VARNAME##_roData.size = SIZE; \
599 avifROStreamStart(&VARNAME, &VARNAME##_roData, DIAG, CONTEXT)
Joe Dragoa72da5b2020-06-15 19:40:17 -0700600
Joe Drago8f439092020-08-28 15:05:17 -0700601// Use this to keep track of whether or not a child box that must be unique (0 or 1 present) has
602// been seen yet, when parsing a parent box. If the "seen" bit is already set for a given box when
603// it is encountered during parse, an error is thrown. Which bit corresponds to which box is
604// dictated entirely by the calling function.
Joe Drago4d5f4a42021-04-28 13:11:45 -0700605static avifBool uniqueBoxSeen(uint32_t * uniqueBoxFlags, uint32_t whichFlag, const char * parentBoxType, const char * boxType, avifDiagnostics * diagnostics)
Joe Drago8f439092020-08-28 15:05:17 -0700606{
607 const uint32_t flag = 1 << whichFlag;
608 if (*uniqueBoxFlags & flag) {
609 // This box has already been seen. Error!
Joe Drago618ed5f2021-05-04 12:13:24 -0700610 avifDiagnosticsPrintf(diagnostics, "Box[%s] contains a duplicate unique box of type '%s'", parentBoxType, boxType);
Joe Drago8f439092020-08-28 15:05:17 -0700611 return AVIF_FALSE;
612 }
613
614 // Mark this box as seen.
615 *uniqueBoxFlags |= flag;
616 return AVIF_TRUE;
617}
618
Joe Dragoa72da5b2020-06-15 19:40:17 -0700619// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700620// avifDecoderData
Joe Dragoae7e2c32019-07-18 15:22:25 -0700621
Joe Drago060d5342020-03-03 10:53:49 -0800622typedef struct avifTile
623{
624 avifCodecDecodeInput * input;
625 struct avifCodec * codec;
626 avifImage * image;
Joe Drago46104d62021-05-27 18:17:29 -0700627 uint32_t width; // Either avifTrack.width or avifDecoderItem.width
628 uint32_t height; // Either avifTrack.height or avifDecoderItem.height
Joe Dragoe79bc372021-06-09 17:54:08 -0700629 uint8_t operatingPoint;
Joe Drago060d5342020-03-03 10:53:49 -0800630} avifTile;
631AVIF_ARRAY_DECLARE(avifTileArray, avifTile, tile);
632
Joe Dragoba1eb492020-06-22 17:05:04 -0700633// This holds one "meta" box (from the BMFF and HEIF standards) worth of relevant-to-AVIF information.
634// * If a meta box is parsed from the root level of the BMFF, it can contain the information about
635// "items" which might be color planes, alpha planes, or EXIF or XMP metadata.
636// * If a meta box is parsed from inside of a track ("trak") box, any metadata (EXIF/XMP) items inside
637// of that box are implicitly associated with that track.
Joe Drago9f2b87b2020-06-03 19:36:38 -0700638typedef struct avifMeta
Joe Drago444f0512019-01-23 17:03:24 -0800639{
Joe Dragoba1eb492020-06-22 17:05:04 -0700640 // Items (from HEIF) are the generic storage for any data that does not require timed processing
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700641 // (single image color planes, alpha planes, EXIF, XMP, etc). Each item has a unique integer ID >1,
642 // and is defined by a series of child boxes in a meta box:
Joe Dragoba1eb492020-06-22 17:05:04 -0700643 // * iloc - location: byte offset to item data, item size in bytes
644 // * iinf - information: type of item (color planes, alpha plane, EXIF, XMP)
645 // * ipco - properties: dimensions, aspect ratio, image transformations, references to other items
646 // * ipma - associations: Attaches an item in the properties list to a given item
647 //
648 // Items are lazily created in this array when any of the above boxes refer to one by a new (unseen) ID,
649 // and are then further modified/updated as new information for an item's ID is parsed.
Joe Drago800b47f2020-03-18 16:22:37 -0700650 avifDecoderItemArray items;
Joe Dragoba1eb492020-06-22 17:05:04 -0700651
652 // Any ipco boxes explained above are populated into this array as a staging area, which are
653 // then duplicated into the appropriate items upon encountering an item property association
654 // (ipma) box.
Joe Drago05559c92019-07-17 16:33:38 -0700655 avifPropertyArray properties;
Joe Dragoba1eb492020-06-22 17:05:04 -0700656
657 // Filled with the contents of "idat" boxes, which are raw data that an item can directly refer to in its
658 // item location box (iloc) instead of just giving an offset into the overall file. If all items' iloc boxes
659 // simply point at an offset/length in the file itself, this array will likely be empty.
Joe Drago800b47f2020-03-18 16:22:37 -0700660 avifDecoderItemDataArray idats;
Joe Dragoba1eb492020-06-22 17:05:04 -0700661
662 // Ever-incrementing ID for uniquely identifying which 'meta' box contains an idat (when
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700663 // multiple meta boxes exist as BMFF siblings). Each time avifParseMetaBox() is called on an
Joe Dragoba1eb492020-06-22 17:05:04 -0700664 // avifMeta struct, this value is incremented. Any time an additional meta box is detected at
665 // the same "level" (root level, trak level, etc), this ID helps distinguish which meta box's
Wan-Teh Chang4b331fb2020-07-07 14:10:02 -0700666 // "idat" is which, as items implicitly reference idat boxes that exist in the same meta
Joe Dragoba1eb492020-06-22 17:05:04 -0700667 // box.
668 uint32_t idatID;
669
670 // Contents of a pitm box, which signal which of the items in this file is the main image. For
671 // AVIF, this should point at an av01 type item containing color planes, and all other items
672 // are ignored unless they refer to this item in some way (alpha plane, EXIF/XMP metadata).
Joe Drago9f2b87b2020-06-03 19:36:38 -0700673 uint32_t primaryItemID;
674} avifMeta;
675
676static avifMeta * avifMetaCreate()
677{
678 avifMeta * meta = (avifMeta *)avifAlloc(sizeof(avifMeta));
679 memset(meta, 0, sizeof(avifMeta));
680 avifArrayCreate(&meta->items, sizeof(avifDecoderItem), 8);
681 avifArrayCreate(&meta->properties, sizeof(avifProperty), 16);
682 avifArrayCreate(&meta->idats, sizeof(avifDecoderItemData), 1);
683 return meta;
684}
685
686static void avifMetaDestroy(avifMeta * meta)
687{
Joe Dragoa72da5b2020-06-15 19:40:17 -0700688 for (uint32_t i = 0; i < meta->items.count; ++i) {
689 avifDecoderItem * item = &meta->items.item[i];
690 avifArrayDestroy(&item->properties);
Joe Dragoa4956902020-08-28 01:24:35 -0700691 avifArrayDestroy(&item->extents);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700692 if (item->ownsMergedExtents) {
693 avifRWDataFree(&item->mergedExtents);
694 }
Joe Dragoa72da5b2020-06-15 19:40:17 -0700695 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700696 avifArrayDestroy(&meta->items);
697 avifArrayDestroy(&meta->properties);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700698 for (uint32_t i = 0; i < meta->idats.count; ++i) {
699 avifDecoderItemData * idat = &meta->idats.idat[i];
700 avifRWDataFree(&idat->data);
701 }
Joe Drago9f2b87b2020-06-03 19:36:38 -0700702 avifArrayDestroy(&meta->idats);
703 avifFree(meta);
704}
705
706static avifDecoderItem * avifMetaFindItem(avifMeta * meta, uint32_t itemID)
707{
708 if (itemID == 0) {
709 return NULL;
710 }
711
712 for (uint32_t i = 0; i < meta->items.count; ++i) {
713 if (meta->items.item[i].id == itemID) {
714 return &meta->items.item[i];
715 }
716 }
717
718 avifDecoderItem * item = (avifDecoderItem *)avifArrayPushPtr(&meta->items);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700719 avifArrayCreate(&item->properties, sizeof(avifProperty), 16);
Joe Drago4bcdfde2020-11-13 17:50:55 -0800720 avifArrayCreate(&item->extents, sizeof(avifExtent), 1);
Joe Drago9f2b87b2020-06-03 19:36:38 -0700721 item->id = itemID;
722 item->meta = meta;
723 return item;
724}
725
726typedef struct avifDecoderData
727{
Wan-Teh Chang6fc17582020-09-24 15:16:37 -0700728 avifMeta * meta; // The root-level meta box
Joe Dragoae7e2c32019-07-18 15:22:25 -0700729 avifTrackArray tracks;
Joe Drago060d5342020-03-03 10:53:49 -0800730 avifTileArray tiles;
731 unsigned int colorTileCount;
732 unsigned int alphaTileCount;
733 avifImageGrid colorGrid;
734 avifImageGrid alphaGrid;
Joe Drago46ea0582019-07-22 15:55:47 -0700735 avifDecoderSource source;
Joe Drago4d5f4a42021-04-28 13:11:45 -0700736 avifDiagnostics * diag; // Shallow copy; owned by avifDecoder
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700737 const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
Joe Dragoc00d5832020-08-13 16:03:28 -0700738 avifBool cicpSet; // True if avifDecoder's image has had its CICP set correctly yet.
739 // This allows nclx colr boxes to override AV1 CICP, as specified in the MIAF
740 // standard (ISO/IEC 23000-22:2019), section 7.3.6.4:
741 //
742 // "The colour information property takes precedence over any colour information in the image
743 // bitstream, i.e. if the property is present, colour information in the bitstream shall be ignored."
Joe Drago800b47f2020-03-18 16:22:37 -0700744} avifDecoderData;
Joe Drago444f0512019-01-23 17:03:24 -0800745
Joe Drago800b47f2020-03-18 16:22:37 -0700746static avifDecoderData * avifDecoderDataCreate()
Joe Drago05559c92019-07-17 16:33:38 -0700747{
Joe Drago800b47f2020-03-18 16:22:37 -0700748 avifDecoderData * data = (avifDecoderData *)avifAlloc(sizeof(avifDecoderData));
749 memset(data, 0, sizeof(avifDecoderData));
Joe Drago9f2b87b2020-06-03 19:36:38 -0700750 data->meta = avifMetaCreate();
Joe Dragoae7e2c32019-07-18 15:22:25 -0700751 avifArrayCreate(&data->tracks, sizeof(avifTrack), 2);
Joe Drago060d5342020-03-03 10:53:49 -0800752 avifArrayCreate(&data->tiles, sizeof(avifTile), 8);
Joe Drago05559c92019-07-17 16:33:38 -0700753 return data;
754}
755
Joe Drago800b47f2020-03-18 16:22:37 -0700756static void avifDecoderDataResetCodec(avifDecoderData * data)
Joe Drago05559c92019-07-17 16:33:38 -0700757{
Joe Drago060d5342020-03-03 10:53:49 -0800758 for (unsigned int i = 0; i < data->tiles.count; ++i) {
759 avifTile * tile = &data->tiles.tile[i];
Joe Drago9d368782020-03-04 17:53:17 -0800760 if (tile->image) {
761 avifImageFreePlanes(tile->image, AVIF_PLANES_ALL); // forget any pointers into codec image buffers
762 }
Joe Drago060d5342020-03-03 10:53:49 -0800763 if (tile->codec) {
764 avifCodecDestroy(tile->codec);
765 tile->codec = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -0700766 }
767 }
768}
769
Joe Dragoe79bc372021-06-09 17:54:08 -0700770static avifTile * avifDecoderDataCreateTile(avifDecoderData * data, uint32_t width, uint32_t height, uint8_t operatingPoint)
Joe Drago060d5342020-03-03 10:53:49 -0800771{
772 avifTile * tile = (avifTile *)avifArrayPushPtr(&data->tiles);
773 tile->image = avifImageCreateEmpty();
774 tile->input = avifCodecDecodeInputCreate();
Joe Drago46104d62021-05-27 18:17:29 -0700775 tile->width = width;
776 tile->height = height;
Joe Dragoe79bc372021-06-09 17:54:08 -0700777 tile->operatingPoint = operatingPoint;
Joe Drago060d5342020-03-03 10:53:49 -0800778 return tile;
779}
780
Joe Dragoa72da5b2020-06-15 19:40:17 -0700781static avifTrack * avifDecoderDataCreateTrack(avifDecoderData * data)
782{
783 avifTrack * track = (avifTrack *)avifArrayPushPtr(&data->tracks);
784 track->meta = avifMetaCreate();
785 return track;
786}
787
Joe Drago800b47f2020-03-18 16:22:37 -0700788static void avifDecoderDataClearTiles(avifDecoderData * data)
Joe Drago060d5342020-03-03 10:53:49 -0800789{
790 for (unsigned int i = 0; i < data->tiles.count; ++i) {
791 avifTile * tile = &data->tiles.tile[i];
792 if (tile->input) {
793 avifCodecDecodeInputDestroy(tile->input);
794 tile->input = NULL;
795 }
796 if (tile->codec) {
797 avifCodecDestroy(tile->codec);
798 tile->codec = NULL;
799 }
800 if (tile->image) {
801 avifImageDestroy(tile->image);
802 tile->image = NULL;
803 }
804 }
805 data->tiles.count = 0;
806 data->colorTileCount = 0;
807 data->alphaTileCount = 0;
808}
809
Joe Drago800b47f2020-03-18 16:22:37 -0700810static void avifDecoderDataDestroy(avifDecoderData * data)
Joe Drago46ea0582019-07-22 15:55:47 -0700811{
Joe Drago9f2b87b2020-06-03 19:36:38 -0700812 avifMetaDestroy(data->meta);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700813 for (uint32_t i = 0; i < data->tracks.count; ++i) {
Joe Dragoa72da5b2020-06-15 19:40:17 -0700814 avifTrack * track = &data->tracks.track[i];
815 if (track->sampleTable) {
816 avifSampleTableDestroy(track->sampleTable);
817 }
818 if (track->meta) {
819 avifMetaDestroy(track->meta);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700820 }
821 }
822 avifArrayDestroy(&data->tracks);
Joe Drago800b47f2020-03-18 16:22:37 -0700823 avifDecoderDataClearTiles(data);
Joe Drago060d5342020-03-03 10:53:49 -0800824 avifArrayDestroy(&data->tiles);
Joe Drago05559c92019-07-17 16:33:38 -0700825 avifFree(data);
826}
827
Joe Drago4bcdfde2020-11-13 17:50:55 -0800828// This returns the max extent that has to be read in order to decode this item. If
829// the item is stored in an idat, the data has already been read during Parse() and
830// this function will return AVIF_RESULT_OK with a 0-byte extent.
831static avifResult avifDecoderItemMaxExtent(const avifDecoderItem * item, avifExtent * outExtent)
832{
833 if (item->extents.count == 0) {
834 return AVIF_RESULT_TRUNCATED_DATA;
835 }
836
837 if (item->idatID != 0) {
838 // construction_method: idat(1)
839
Wan-Teh Chang80275392020-11-17 12:35:11 -0800840 // Find associated idat box
Joe Drago4bcdfde2020-11-13 17:50:55 -0800841 for (uint32_t i = 0; i < item->meta->idats.count; ++i) {
842 if (item->meta->idats.idat[i].id == item->idatID) {
843 // Already read from a meta box during Parse()
844 memset(outExtent, 0, sizeof(avifExtent));
845 return AVIF_RESULT_OK;
846 }
847 }
848
Wan-Teh Chang80275392020-11-17 12:35:11 -0800849 // no associated idat box was found in the meta box, bail out
Joe Drago4bcdfde2020-11-13 17:50:55 -0800850 return AVIF_RESULT_NO_CONTENT;
851 }
852
853 // construction_method: file(0)
854
Wan-Teh Chang15584522020-11-17 14:11:12 -0800855 // Assert that the for loop below will execute at least one iteration.
856 assert(item->extents.count != 0);
857 uint64_t minOffset = UINT64_MAX;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800858 uint64_t maxOffset = 0;
859 for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) {
860 avifExtent * extent = &item->extents.extent[extentIter];
861
862 if (extent->size > UINT64_MAX - extent->offset) {
863 return AVIF_RESULT_BMFF_PARSE_FAILED;
864 }
865 const uint64_t endOffset = extent->offset + extent->size;
866
Wan-Teh Chang15584522020-11-17 14:11:12 -0800867 if (minOffset > extent->offset) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800868 minOffset = extent->offset;
Wan-Teh Chang15584522020-11-17 14:11:12 -0800869 }
870 if (maxOffset < endOffset) {
Joe Drago4bcdfde2020-11-13 17:50:55 -0800871 maxOffset = endOffset;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800872 }
873 }
874
875 outExtent->offset = minOffset;
Wan-Teh Changd69958e2020-11-17 12:14:27 -0800876 const uint64_t extentLength = maxOffset - minOffset;
877 if (extentLength > SIZE_MAX) {
878 return AVIF_RESULT_BMFF_PARSE_FAILED;
879 }
880 outExtent->size = (size_t)extentLength;
Joe Drago4bcdfde2020-11-13 17:50:55 -0800881 return AVIF_RESULT_OK;
882}
883
Joe Dragobffba3b2021-05-26 15:46:10 -0700884static uint8_t avifDecoderItemOperatingPoint(const avifDecoderItem * item)
885{
886 const avifProperty * a1opProp = avifPropertyArrayFind(&item->properties, "a1op");
887 if (a1opProp) {
888 return a1opProp->u.a1op.opIndex;
889 }
890 return 0; // default
891}
892
Joe Drago9b942c22021-05-06 12:33:12 -0700893static avifResult avifDecoderItemValidateAV1(const avifDecoderItem * item, avifDiagnostics * diag, const avifStrictFlags strictFlags)
Joe Dragoc10c6be2021-03-31 08:55:19 -0700894{
895 const avifProperty * av1CProp = avifPropertyArrayFind(&item->properties, "av1C");
896 if (!av1CProp) {
897 // An av1C box is mandatory in all valid AVIF configurations. Bail out.
Joe Drago618ed5f2021-05-04 12:13:24 -0700898 avifDiagnosticsPrintf(diag, "Item ID %u is missing mandatory av1C property", item->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -0700899 return AVIF_RESULT_BMFF_PARSE_FAILED;
900 }
Joe Dragoc10c6be2021-03-31 08:55:19 -0700901
902 const avifProperty * pixiProp = avifPropertyArrayFind(&item->properties, "pixi");
Joe Drago9b942c22021-05-06 12:33:12 -0700903 if (!pixiProp && (strictFlags & AVIF_STRICT_PIXI_REQUIRED)) {
Joe Dragoc10c6be2021-03-31 08:55:19 -0700904 // A pixi box is mandatory in all valid AVIF configurations. Bail out.
Joe Dragof5b98a62021-05-10 12:25:29 -0700905 avifDiagnosticsPrintf(diag, "[Strict] Item ID %u is missing mandatory pixi property", item->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -0700906 return AVIF_RESULT_BMFF_PARSE_FAILED;
907 }
908
Joe Drago9b942c22021-05-06 12:33:12 -0700909 if (pixiProp) {
Joe Drago4a70b472021-05-06 17:21:33 -0700910 const uint32_t av1CDepth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
Joe Drago9b942c22021-05-06 12:33:12 -0700911 for (uint8_t i = 0; i < pixiProp->u.pixi.planeCount; ++i) {
912 if (pixiProp->u.pixi.planeDepths[i] != av1CDepth) {
913 // pixi depth must match av1C depth
914 avifDiagnosticsPrintf(diag,
915 "Item ID %u depth specified by pixi property [%u] does not match av1C property depth [%u]",
916 item->id,
917 pixiProp->u.pixi.planeDepths[i],
918 av1CDepth);
919 return AVIF_RESULT_BMFF_PARSE_FAILED;
920 }
Joe Dragoc10c6be2021-03-31 08:55:19 -0700921 }
922 }
Joe Drago4a70b472021-05-06 17:21:33 -0700923
924 if (strictFlags & AVIF_STRICT_CLAP_VALID) {
925 const avifProperty * clapProp = avifPropertyArrayFind(&item->properties, "clap");
926 if (clapProp) {
927 const avifProperty * ispeProp = avifPropertyArrayFind(&item->properties, "ispe");
928 if (!ispeProp) {
Joe Dragof5b98a62021-05-10 12:25:29 -0700929 avifDiagnosticsPrintf(diag,
930 "[Strict] Item ID %u is missing an ispe property, so its clap property cannot be validated",
931 item->id);
Joe Drago4a70b472021-05-06 17:21:33 -0700932 return AVIF_RESULT_BMFF_PARSE_FAILED;
933 }
934
935 avifCropRect cropRect;
936 const uint32_t imageW = ispeProp->u.ispe.width;
937 const uint32_t imageH = ispeProp->u.ispe.height;
938 const avifPixelFormat av1CFormat = avifCodecConfigurationBoxGetFormat(&av1CProp->u.av1C);
939 avifBool validClap = avifCropRectConvertCleanApertureBox(&cropRect, &clapProp->u.clap, imageW, imageH, av1CFormat, diag);
940 if (!validClap) {
941 return AVIF_RESULT_BMFF_PARSE_FAILED;
942 }
943 }
944 }
Joe Dragoc10c6be2021-03-31 08:55:19 -0700945 return AVIF_RESULT_OK;
946}
947
Joe Dragobffba3b2021-05-26 15:46:10 -0700948static avifResult avifDecoderItemRead(avifDecoderItem * item,
949 avifIO * io,
950 avifROData * outData,
951 size_t offset,
952 size_t partialByteCount,
953 avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -0800954{
Joe Dragobe4cbb92020-09-21 12:14:05 -0700955 if (item->mergedExtents.data && !item->partialMergedExtents) {
Joe Dragoa4956902020-08-28 01:24:35 -0700956 // Multiple extents have already been concatenated for this item, just return it
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700957 memcpy(outData, &item->mergedExtents, sizeof(avifROData));
Joe Dragobe4cbb92020-09-21 12:14:05 -0700958 return AVIF_RESULT_OK;
959 }
960
961 if (item->extents.count == 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700962 avifDiagnosticsPrintf(diag, "Item ID %u has zero extents", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700963 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragoa4956902020-08-28 01:24:35 -0700964 }
965
966 // Find this item's source of all extents' data, based on the construction method
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -0700967 const avifRWData * idatBuffer = NULL;
Wan-Teh Chang610b0a12020-10-06 16:47:21 -0700968 if (item->idatID != 0) {
Joe Dragof6a42272019-11-21 15:21:41 -0800969 // construction_method: idat(1)
970
Wan-Teh Chang80275392020-11-17 12:35:11 -0800971 // Find associated idat box
Joe Drago9f2b87b2020-06-03 19:36:38 -0700972 for (uint32_t i = 0; i < item->meta->idats.count; ++i) {
973 if (item->meta->idats.idat[i].id == item->idatID) {
Joe Dragobe4cbb92020-09-21 12:14:05 -0700974 idatBuffer = &item->meta->idats.idat[i].data;
Joe Dragof6a42272019-11-21 15:21:41 -0800975 break;
976 }
977 }
978
Joe Dragobe4cbb92020-09-21 12:14:05 -0700979 if (idatBuffer == NULL) {
Wan-Teh Chang80275392020-11-17 12:35:11 -0800980 // no associated idat box was found in the meta box, bail out
Joe Drago618ed5f2021-05-04 12:13:24 -0700981 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 -0700982 return AVIF_RESULT_NO_CONTENT;
Joe Dragof6a42272019-11-21 15:21:41 -0800983 }
984 }
985
Joe Dragobe4cbb92020-09-21 12:14:05 -0700986 // Merge extents into a single contiguous buffer
Wan-Teh Chang4548b162020-11-06 11:48:25 -0800987 if ((io->sizeHint > 0) && (item->size > io->sizeHint)) {
Joe Drago618ed5f2021-05-04 12:13:24 -0700988 // Sanity check: somehow the sum of extents exceeds the entire file or idat size!
989 avifDiagnosticsPrintf(diag, "Item ID %u reported size failed size hint sanity check. Truncated data?", item->id);
Joe Dragobe4cbb92020-09-21 12:14:05 -0700990 return AVIF_RESULT_TRUNCATED_DATA;
Joe Drago8f439092020-08-28 15:05:17 -0700991 }
Joe Dragobe4cbb92020-09-21 12:14:05 -0700992
Joe Dragobffba3b2021-05-26 15:46:10 -0700993 const size_t readOutputSize = (partialByteCount && (partialByteCount < item->size)) ? partialByteCount : item->size;
994
995 // Read in everything from the item's offset 0 all the way to the end of the read
996 if (offset > SIZE_MAX - readOutputSize) {
997 avifDiagnosticsPrintf(diag, "Item ID %u read has overflowing offset", item->id);
998 return AVIF_RESULT_TRUNCATED_DATA;
999 }
1000 const size_t totalBytesToRead = offset + readOutputSize;
1001 if (totalBytesToRead > item->size) {
1002 avifDiagnosticsPrintf(diag, "Item ID %u read has overflowing offset+size", item->id);
1003 return AVIF_RESULT_TRUNCATED_DATA;
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001004 }
1005
Joe Dragobe4cbb92020-09-21 12:14:05 -07001006 // If there is a single extent for this item and the source of the read buffer is going to be
1007 // persistent for the lifetime of the avifDecoder (whether it comes from its own internal
1008 // idatBuffer or from a known-persistent IO), we can avoid buffer duplication and just use the
1009 // preexisting buffer.
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001010 avifBool singlePersistentBuffer = ((item->extents.count == 1) && (idatBuffer || io->persistent));
Joe Dragobe4cbb92020-09-21 12:14:05 -07001011 if (!singlePersistentBuffer) {
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001012 avifRWDataRealloc(&item->mergedExtents, totalBytesToRead);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001013 item->ownsMergedExtents = AVIF_TRUE;
1014 }
1015
1016 // Set this until we manage to fill the entire mergedExtents buffer
1017 item->partialMergedExtents = AVIF_TRUE;
Joe Drago8f439092020-08-28 15:05:17 -07001018
Joe Dragoa4956902020-08-28 01:24:35 -07001019 uint8_t * front = item->mergedExtents.data;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001020 size_t remainingBytes = totalBytesToRead;
Joe Dragoa4956902020-08-28 01:24:35 -07001021 for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) {
Joe Drago4bcdfde2020-11-13 17:50:55 -08001022 avifExtent * extent = &item->extents.extent[extentIter];
Joe Dragobe4cbb92020-09-21 12:14:05 -07001023
1024 size_t bytesToRead = extent->size;
1025 if (bytesToRead > remainingBytes) {
1026 bytesToRead = remainingBytes;
Joe Dragoa4956902020-08-28 01:24:35 -07001027 }
1028
Joe Dragobe4cbb92020-09-21 12:14:05 -07001029 avifROData offsetBuffer;
1030 if (idatBuffer) {
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001031 if (extent->offset > idatBuffer->size) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001032 avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent offset in idat buffer", item->id);
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001033 return AVIF_RESULT_BMFF_PARSE_FAILED;
1034 }
Wan-Teh Changa17693d2021-06-02 14:57:18 -07001035 // Since extent->offset (a uint64_t) is not bigger than idatBuffer->size (a size_t),
1036 // it is safe to cast extent->offset to size_t.
1037 const size_t extentOffset = (size_t)extent->offset;
1038 if (extent->size > idatBuffer->size - extentOffset) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001039 avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent size in idat buffer", item->id);
Wan-Teh Chang610b0a12020-10-06 16:47:21 -07001040 return AVIF_RESULT_BMFF_PARSE_FAILED;
1041 }
Wan-Teh Changa17693d2021-06-02 14:57:18 -07001042 offsetBuffer.data = idatBuffer->data + extentOffset;
1043 offsetBuffer.size = idatBuffer->size - extentOffset;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001044 } else {
1045 // construction_method: file(0)
1046
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001047 if ((io->sizeHint > 0) && (extent->offset > io->sizeHint)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001048 avifDiagnosticsPrintf(diag, "Item ID %u extent offset failed size hint sanity check. Truncated data?", item->id);
Wan-Teh Changb856dc22020-09-28 13:00:56 -07001049 return AVIF_RESULT_BMFF_PARSE_FAILED;
1050 }
Wan-Teh Chang4548b162020-11-06 11:48:25 -08001051 avifResult readResult = io->read(io, 0, extent->offset, bytesToRead, &offsetBuffer);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001052 if (readResult != AVIF_RESULT_OK) {
1053 return readResult;
1054 }
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -07001055 if (bytesToRead != offsetBuffer.size) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001056 avifDiagnosticsPrintf(diag,
1057 "Item ID %u tried to read %zu bytes, but only received %zu bytes",
Joe Drago48867292021-05-04 13:23:08 -07001058 item->id,
Joe Drago618ed5f2021-05-04 12:13:24 -07001059 bytesToRead,
1060 offsetBuffer.size);
Wan-Teh Chang3c304dc2020-10-08 12:07:45 -07001061 return AVIF_RESULT_TRUNCATED_DATA;
1062 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001063 }
1064
1065 if (singlePersistentBuffer) {
1066 memcpy(&item->mergedExtents, &offsetBuffer, sizeof(avifRWData));
1067 item->mergedExtents.size = bytesToRead;
1068 } else {
Joe Dragodb1009a2021-03-16 15:26:14 -07001069 assert(item->ownsMergedExtents);
1070 assert(front);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001071 memcpy(front, offsetBuffer.data, bytesToRead);
1072 front += bytesToRead;
1073 }
1074
1075 remainingBytes -= bytesToRead;
1076 if (remainingBytes == 0) {
1077 // This happens when partialByteCount is set
1078 break;
1079 }
Joe Dragoa4956902020-08-28 01:24:35 -07001080 }
1081 if (remainingBytes != 0) {
1082 // This should be impossible?
Joe Drago618ed5f2021-05-04 12:13:24 -07001083 avifDiagnosticsPrintf(diag, "Item ID %u has %zu unexpected trailing bytes", item->id, remainingBytes);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001084 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragof6a42272019-11-21 15:21:41 -08001085 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001086
Joe Dragobffba3b2021-05-26 15:46:10 -07001087 outData->data = item->mergedExtents.data + offset;
1088 outData->size = readOutputSize;
Joe Dragobe4cbb92020-09-21 12:14:05 -07001089 item->partialMergedExtents = (item->size != totalBytesToRead);
1090 return AVIF_RESULT_OK;
Joe Dragof6a42272019-11-21 15:21:41 -08001091}
1092
Joe Dragobffba3b2021-05-26 15:46:10 -07001093static avifBool avifDecoderGenerateImageGridTiles(avifDecoder * decoder, avifImageGrid * grid, avifDecoderItem * gridItem, avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -08001094{
Wan-Teh Chang7ca3dd92020-11-20 12:50:44 -08001095 unsigned int tilesRequested = grid->rows * grid->columns;
Joe Drago060d5342020-03-03 10:53:49 -08001096
1097 // Count number of dimg for this item, bail out if it doesn't match perfectly
1098 unsigned int tilesAvailable = 0;
Joe Drago9f2b87b2020-06-03 19:36:38 -07001099 for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) {
1100 avifDecoderItem * item = &gridItem->meta->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -08001101 if (item->dimgForID == gridItem->id) {
1102 if (memcmp(item->type, "av01", 4)) {
1103 continue;
1104 }
Joe Drago3320e5f2020-04-21 17:36:27 -07001105 if (item->hasUnsupportedEssentialProperty) {
Wan-Teh Chang29aaade2020-08-10 16:14:16 -07001106 // An essential property isn't supported by libavif; can't
1107 // decode a grid image if any tile in the grid isn't supported.
Joe Dragobffba3b2021-05-26 15:46:10 -07001108 avifDiagnosticsPrintf(&decoder->diag, "Grid image contains tile with an unsupported property marked as essential");
Wan-Teh Chang29aaade2020-08-10 16:14:16 -07001109 return AVIF_FALSE;
Joe Drago3320e5f2020-04-21 17:36:27 -07001110 }
Joe Drago060d5342020-03-03 10:53:49 -08001111
1112 ++tilesAvailable;
1113 }
1114 }
1115
1116 if (tilesRequested != tilesAvailable) {
Joe Dragobffba3b2021-05-26 15:46:10 -07001117 avifDiagnosticsPrintf(&decoder->diag,
Wan-Teh Changf01e2252021-06-10 17:40:21 -07001118 "Grid image of dimensions %ux%u requires %u tiles, and only %u were found",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001119 grid->columns,
1120 grid->rows,
1121 tilesRequested,
1122 tilesAvailable);
Joe Drago060d5342020-03-03 10:53:49 -08001123 return AVIF_FALSE;
1124 }
1125
Joe Drago9c5f5652020-06-29 15:13:44 -07001126 avifBool firstTile = AVIF_TRUE;
Joe Drago9f2b87b2020-06-03 19:36:38 -07001127 for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) {
1128 avifDecoderItem * item = &gridItem->meta->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -08001129 if (item->dimgForID == gridItem->id) {
1130 if (memcmp(item->type, "av01", 4)) {
1131 continue;
1132 }
1133
Joe Drago46104d62021-05-27 18:17:29 -07001134 avifTile * tile = avifDecoderDataCreateTile(decoder->data, item->width, item->height, avifDecoderItemOperatingPoint(item));
Joe Dragobffba3b2021-05-26 15:46:10 -07001135 if (!avifCodecDecodeInputFillFromDecoderItem(tile->input,
1136 item,
1137 decoder->allowProgressive,
1138 decoder->imageCountLimit,
1139 decoder->io->sizeHint,
1140 &decoder->diag)) {
1141 return AVIF_FALSE;
1142 }
Joe Drago060d5342020-03-03 10:53:49 -08001143 tile->input->alpha = alpha;
Joe Drago9c5f5652020-06-29 15:13:44 -07001144
1145 if (firstTile) {
1146 firstTile = AVIF_FALSE;
1147
1148 // Adopt the av1C property of the first av01 tile, so that it can be queried from
1149 // the top-level color/alpha item during avifDecoderReset().
1150 const avifProperty * srcProp = avifPropertyArrayFind(&item->properties, "av1C");
1151 if (!srcProp) {
Joe Dragobffba3b2021-05-26 15:46:10 -07001152 avifDiagnosticsPrintf(&decoder->diag, "Grid image's first tile is missing an av1C property");
Joe Drago9c5f5652020-06-29 15:13:44 -07001153 return AVIF_FALSE;
1154 }
1155 avifProperty * dstProp = (avifProperty *)avifArrayPushPtr(&gridItem->properties);
1156 memcpy(dstProp, srcProp, sizeof(avifProperty));
Joe Dragobffba3b2021-05-26 15:46:10 -07001157
1158 if (!alpha) {
1159 if (item->progressive) {
1160 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_AVAILABLE;
1161 if (tile->input->samples.count > 1) {
1162 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_ACTIVE;
1163 decoder->imageCount = tile->input->samples.count;
1164 }
1165 }
1166 }
Joe Drago9c5f5652020-06-29 15:13:44 -07001167 }
Joe Drago060d5342020-03-03 10:53:49 -08001168 }
1169 }
1170 return AVIF_TRUE;
1171}
1172
Joe Drago800b47f2020-03-18 16:22:37 -07001173static avifBool avifDecoderDataFillImageGrid(avifDecoderData * data,
1174 avifImageGrid * grid,
1175 avifImage * dstImage,
1176 unsigned int firstTileIndex,
1177 unsigned int tileCount,
1178 avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -08001179{
1180 if (tileCount == 0) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001181 avifDiagnosticsPrintf(data->diag, "Cannot fill grid image, no tiles");
Joe Drago060d5342020-03-03 10:53:49 -08001182 return AVIF_FALSE;
1183 }
1184
1185 avifTile * firstTile = &data->tiles.tile[firstTileIndex];
Joe Dragoa0da4a42020-05-08 14:27:40 -07001186 avifBool firstTileUVPresent = (firstTile->image->yuvPlanes[AVIF_CHAN_U] && firstTile->image->yuvPlanes[AVIF_CHAN_V]);
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09001187
Joe Dragoa0da4a42020-05-08 14:27:40 -07001188 // Check for tile consistency: All tiles in a grid image should match in the properties checked below.
Joe Drago060d5342020-03-03 10:53:49 -08001189 for (unsigned int i = 1; i < tileCount; ++i) {
1190 avifTile * tile = &data->tiles.tile[firstTileIndex + i];
Joe Drago951a0022020-03-09 16:19:44 -07001191 avifBool uvPresent = (tile->image->yuvPlanes[AVIF_CHAN_U] && tile->image->yuvPlanes[AVIF_CHAN_V]);
Joe Dragoa0da4a42020-05-08 14:27:40 -07001192 if ((tile->image->width != firstTile->image->width) || (tile->image->height != firstTile->image->height) ||
1193 (tile->image->depth != firstTile->image->depth) || (tile->image->yuvFormat != firstTile->image->yuvFormat) ||
1194 (tile->image->yuvRange != firstTile->image->yuvRange) || (uvPresent != firstTileUVPresent) ||
wantehchangbc35a5f2020-08-12 15:27:39 -07001195 (tile->image->colorPrimaries != firstTile->image->colorPrimaries) ||
1196 (tile->image->transferCharacteristics != firstTile->image->transferCharacteristics) ||
Wan-Teh Chang5bdead52020-08-12 17:02:22 -07001197 (tile->image->matrixCoefficients != firstTile->image->matrixCoefficients) ||
1198 (tile->image->alphaRange != firstTile->image->alphaRange)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001199 avifDiagnosticsPrintf(data->diag, "Grid image contains mismatched tiles");
Joe Drago060d5342020-03-03 10:53:49 -08001200 return AVIF_FALSE;
1201 }
1202 }
1203
wantehchangdf586a82020-08-12 13:06:08 -07001204 // Validate grid image size and tile size.
1205 //
1206 // HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1:
TYTYb587c592020-12-08 17:42:18 +08001207 // The tiled input images shall completely "cover" the reconstructed image grid canvas, ...
wantehchangbc35a5f2020-08-12 15:27:39 -07001208 if (((firstTile->image->width * grid->columns) < grid->outputWidth) ||
1209 ((firstTile->image->height * grid->rows) < grid->outputHeight)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001210 avifDiagnosticsPrintf(data->diag,
1211 "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 -07001212 return AVIF_FALSE;
1213 }
1214 // 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 -07001215 if (((firstTile->image->width * (grid->columns - 1)) >= grid->outputWidth) ||
1216 ((firstTile->image->height * (grid->rows - 1)) >= grid->outputHeight)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001217 avifDiagnosticsPrintf(data->diag,
1218 "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 -07001219 return AVIF_FALSE;
1220 }
1221 // Check the restrictions in MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2.
1222 //
1223 // 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 -07001224 if ((firstTile->image->width < 64) || (firstTile->image->height < 64)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001225 avifDiagnosticsPrintf(data->diag,
Wan-Teh Changf01e2252021-06-10 17:40:21 -07001226 "Grid image tiles are smaller than 64x64 (%ux%u). See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001227 firstTile->image->width,
1228 firstTile->image->height);
wantehchangdf586a82020-08-12 13:06:08 -07001229 return AVIF_FALSE;
1230 }
1231 if (!alpha) {
wantehchangbc35a5f2020-08-12 15:27:39 -07001232 if ((firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) || (firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420)) {
wantehchangdf586a82020-08-12 13:06:08 -07001233 // The horizontal tile offsets and widths, and the output width, shall be even numbers.
wantehchangbc35a5f2020-08-12 15:27:39 -07001234 if (((firstTile->image->width & 1) != 0) || ((grid->outputWidth & 1) != 0)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001235 avifDiagnosticsPrintf(data->diag,
1236 "Grid image horizontal tile offsets and widths [%u], and the output width [%u], shall be even numbers.",
1237 firstTile->image->width,
1238 grid->outputWidth);
wantehchangdf586a82020-08-12 13:06:08 -07001239 return AVIF_FALSE;
1240 }
1241 }
1242 if (firstTile->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
1243 // The vertical tile offsets and heights, and the output height, shall be even numbers.
wantehchangbc35a5f2020-08-12 15:27:39 -07001244 if (((firstTile->image->height & 1) != 0) || ((grid->outputHeight & 1) != 0)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001245 avifDiagnosticsPrintf(data->diag,
1246 "Grid image vertical tile offsets and heights [%u], and the output height [%u], shall be even numbers.",
1247 firstTile->image->height,
1248 grid->outputHeight);
wantehchangdf586a82020-08-12 13:06:08 -07001249 return AVIF_FALSE;
1250 }
1251 }
1252 }
1253
Joe Dragoa0da4a42020-05-08 14:27:40 -07001254 // Lazily populate dstImage with the new frame's properties. If we're decoding alpha,
1255 // these values must already match.
1256 if ((dstImage->width != grid->outputWidth) || (dstImage->height != grid->outputHeight) ||
Wan-Teh Chang5bdead52020-08-12 17:02:22 -07001257 (dstImage->depth != firstTile->image->depth) || (!alpha && (dstImage->yuvFormat != firstTile->image->yuvFormat))) {
Joe Drago060d5342020-03-03 10:53:49 -08001258 if (alpha) {
1259 // Alpha doesn't match size, just bail out
Joe Drago4d5f4a42021-04-28 13:11:45 -07001260 avifDiagnosticsPrintf(data->diag, "Alpha plane dimensions do not match color plane dimensions");
Joe Drago060d5342020-03-03 10:53:49 -08001261 return AVIF_FALSE;
1262 }
1263
1264 avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
1265 dstImage->width = grid->outputWidth;
1266 dstImage->height = grid->outputHeight;
Joe Dragoa0da4a42020-05-08 14:27:40 -07001267 dstImage->depth = firstTile->image->depth;
1268 dstImage->yuvFormat = firstTile->image->yuvFormat;
Joe Dragoc00d5832020-08-13 16:03:28 -07001269 dstImage->yuvRange = firstTile->image->yuvRange;
Joe Dragoa0da4a42020-05-08 14:27:40 -07001270 if (!data->cicpSet) {
1271 data->cicpSet = AVIF_TRUE;
Joe Dragoc00d5832020-08-13 16:03:28 -07001272 dstImage->colorPrimaries = firstTile->image->colorPrimaries;
1273 dstImage->transferCharacteristics = firstTile->image->transferCharacteristics;
1274 dstImage->matrixCoefficients = firstTile->image->matrixCoefficients;
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09001275 }
Joe Drago060d5342020-03-03 10:53:49 -08001276 }
Joe Dragod0eeb182020-05-18 17:23:48 -07001277 if (alpha) {
1278 dstImage->alphaRange = firstTile->image->alphaRange;
1279 }
Joe Drago060d5342020-03-03 10:53:49 -08001280
1281 avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV);
1282
1283 avifPixelFormatInfo formatInfo;
Joe Drago7a249f52020-08-13 12:58:03 -07001284 avifGetPixelFormatInfo(firstTile->image->yuvFormat, &formatInfo);
Joe Drago060d5342020-03-03 10:53:49 -08001285
1286 unsigned int tileIndex = firstTileIndex;
1287 size_t pixelBytes = avifImageUsesU16(dstImage) ? 2 : 1;
1288 for (unsigned int rowIndex = 0; rowIndex < grid->rows; ++rowIndex) {
1289 for (unsigned int colIndex = 0; colIndex < grid->columns; ++colIndex, ++tileIndex) {
1290 avifTile * tile = &data->tiles.tile[tileIndex];
1291
Joe Dragoa0da4a42020-05-08 14:27:40 -07001292 unsigned int widthToCopy = firstTile->image->width;
1293 unsigned int maxX = firstTile->image->width * (colIndex + 1);
Joe Drago060d5342020-03-03 10:53:49 -08001294 if (maxX > grid->outputWidth) {
1295 widthToCopy -= maxX - grid->outputWidth;
1296 }
1297
Joe Dragoa0da4a42020-05-08 14:27:40 -07001298 unsigned int heightToCopy = firstTile->image->height;
1299 unsigned int maxY = firstTile->image->height * (rowIndex + 1);
Joe Drago060d5342020-03-03 10:53:49 -08001300 if (maxY > grid->outputHeight) {
1301 heightToCopy -= maxY - grid->outputHeight;
1302 }
1303
1304 // Y and A channels
Joe Dragodb1009a2021-03-16 15:26:14 -07001305 size_t yaColOffset = (size_t)colIndex * firstTile->image->width;
1306 size_t yaRowOffset = (size_t)rowIndex * firstTile->image->height;
Joe Drago060d5342020-03-03 10:53:49 -08001307 size_t yaRowBytes = widthToCopy * pixelBytes;
1308
1309 if (alpha) {
1310 // A
1311 for (unsigned int j = 0; j < heightToCopy; ++j) {
1312 uint8_t * src = &tile->image->alphaPlane[j * tile->image->alphaRowBytes];
1313 uint8_t * dst = &dstImage->alphaPlane[(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->alphaRowBytes)];
1314 memcpy(dst, src, yaRowBytes);
1315 }
1316 } else {
1317 // Y
1318 for (unsigned int j = 0; j < heightToCopy; ++j) {
1319 uint8_t * src = &tile->image->yuvPlanes[AVIF_CHAN_Y][j * tile->image->yuvRowBytes[AVIF_CHAN_Y]];
1320 uint8_t * dst =
1321 &dstImage->yuvPlanes[AVIF_CHAN_Y][(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_Y])];
1322 memcpy(dst, src, yaRowBytes);
1323 }
1324
Joe Dragoa0da4a42020-05-08 14:27:40 -07001325 if (!firstTileUVPresent) {
Joe Drago060d5342020-03-03 10:53:49 -08001326 continue;
1327 }
1328
1329 // UV
Joe Drago060d5342020-03-03 10:53:49 -08001330 heightToCopy >>= formatInfo.chromaShiftY;
1331 size_t uvColOffset = yaColOffset >> formatInfo.chromaShiftX;
1332 size_t uvRowOffset = yaRowOffset >> formatInfo.chromaShiftY;
1333 size_t uvRowBytes = yaRowBytes >> formatInfo.chromaShiftX;
1334 for (unsigned int j = 0; j < heightToCopy; ++j) {
1335 uint8_t * srcU = &tile->image->yuvPlanes[AVIF_CHAN_U][j * tile->image->yuvRowBytes[AVIF_CHAN_U]];
1336 uint8_t * dstU =
1337 &dstImage->yuvPlanes[AVIF_CHAN_U][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_U])];
1338 memcpy(dstU, srcU, uvRowBytes);
1339
1340 uint8_t * srcV = &tile->image->yuvPlanes[AVIF_CHAN_V][j * tile->image->yuvRowBytes[AVIF_CHAN_V]];
1341 uint8_t * dstV =
1342 &dstImage->yuvPlanes[AVIF_CHAN_V][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_V])];
1343 memcpy(dstV, srcV, uvRowBytes);
1344 }
1345 }
1346 }
1347 }
1348
1349 return AVIF_TRUE;
1350}
1351
Joe Drago39267fd2020-06-19 15:21:14 -07001352// If colorId == 0 (a sentinel value as item IDs must be nonzero), accept any found EXIF/XMP metadata. Passing in 0
1353// 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 -07001354// inside of a trak box are implicitly associated to the track.
Joe Dragobe4cbb92020-09-21 12:14:05 -07001355static avifResult avifDecoderFindMetadata(avifDecoder * decoder, avifMeta * meta, avifImage * image, uint32_t colorId)
Joe Dragoa72da5b2020-06-15 19:40:17 -07001356{
Joe Dragobe4cbb92020-09-21 12:14:05 -07001357 if (decoder->ignoreExif && decoder->ignoreXMP) {
1358 // Nothing to do!
1359 return AVIF_RESULT_OK;
1360 }
Joe Dragoa72da5b2020-06-15 19:40:17 -07001361
1362 for (uint32_t itemIndex = 0; itemIndex < meta->items.count; ++itemIndex) {
1363 avifDecoderItem * item = &meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07001364 if (!item->size) {
1365 continue;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001366 }
1367 if (item->hasUnsupportedEssentialProperty) {
1368 // An essential property isn't supported by libavif; ignore the item.
1369 continue;
1370 }
1371
1372 if ((colorId > 0) && (item->descForID != colorId)) {
1373 // Not a content description (metadata) for the colorOBU, skip it
1374 continue;
1375 }
1376
Joe Dragobe4cbb92020-09-21 12:14:05 -07001377 if (!decoder->ignoreExif && !memcmp(item->type, "Exif", 4)) {
1378 avifROData exifContents;
Joe Dragobffba3b2021-05-26 15:46:10 -07001379 avifResult readResult = avifDecoderItemRead(item, decoder->io, &exifContents, 0, 0, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001380 if (readResult != AVIF_RESULT_OK) {
1381 return readResult;
1382 }
1383
Joe Dragoa72da5b2020-06-15 19:40:17 -07001384 // Advance past Annex A.2.1's header
Joe Drago618ed5f2021-05-04 12:13:24 -07001385 BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size, &decoder->diag, "Exif header");
Joe Dragoa72da5b2020-06-15 19:40:17 -07001386 uint32_t exifTiffHeaderOffset;
Wan-Teh Changf1d60192020-10-07 16:23:08 -07001387 CHECKERR(avifROStreamReadU32(&exifBoxStream, &exifTiffHeaderOffset), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) exif_tiff_header_offset;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001388
Joe Dragobe4cbb92020-09-21 12:14:05 -07001389 avifImageSetMetadataExif(image, avifROStreamCurrent(&exifBoxStream), avifROStreamRemainingBytes(&exifBoxStream));
1390 } else if (!decoder->ignoreXMP && !memcmp(item->type, "mime", 4) &&
1391 !memcmp(item->contentType.contentType, xmpContentType, xmpContentTypeSize)) {
1392 avifROData xmpContents;
Joe Dragobffba3b2021-05-26 15:46:10 -07001393 avifResult readResult = avifDecoderItemRead(item, decoder->io, &xmpContents, 0, 0, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07001394 if (readResult != AVIF_RESULT_OK) {
1395 return readResult;
1396 }
1397
1398 avifImageSetMetadataXMP(image, xmpContents.data, xmpContents.size);
Joe Dragoa72da5b2020-06-15 19:40:17 -07001399 }
1400 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07001401 return AVIF_RESULT_OK;
Joe Dragoa72da5b2020-06-15 19:40:17 -07001402}
1403
Joe Drago8f7a3002019-02-07 19:35:37 -08001404// ---------------------------------------------------------------------------
Joe Dragocd1e4c32019-02-08 11:26:31 -08001405// URN
1406
Joe Dragoa72da5b2020-06-15 19:40:17 -07001407static avifBool isAlphaURN(const char * urn)
Joe Dragocd1e4c32019-02-08 11:26:31 -08001408{
wantehchang3fde0d02020-03-10 23:58:32 -07001409 return !strcmp(urn, URN_ALPHA0) || !strcmp(urn, URN_ALPHA1);
Joe Dragocd1e4c32019-02-08 11:26:31 -08001410}
1411
1412// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08001413// BMFF Parsing
1414
Joe Drago4d5f4a42021-04-28 13:11:45 -07001415static avifBool avifParseHandlerBox(const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoe0185182021-03-31 08:14:51 -07001416{
Joe Drago618ed5f2021-05-04 12:13:24 -07001417 BEGIN_STREAM(s, raw, rawLen, diag, "Box[hdlr]");
Joe Dragoe0185182021-03-31 08:14:51 -07001418
1419 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1420
1421 uint32_t predefined;
1422 CHECK(avifROStreamReadU32(&s, &predefined)); // unsigned int(32) pre_defined = 0;
1423 if (predefined != 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001424 avifDiagnosticsPrintf(diag, "Box[hdlr] contains a pre_defined value that is nonzero");
Joe Dragoe0185182021-03-31 08:14:51 -07001425 return AVIF_FALSE;
1426 }
1427
1428 uint8_t handlerType[4];
1429 CHECK(avifROStreamRead(&s, handlerType, 4)); // unsigned int(32) handler_type;
1430 if (memcmp(handlerType, "pict", 4) != 0) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001431 avifDiagnosticsPrintf(diag, "Box[hdlr] handler_type is not 'pict'");
Joe Dragoe0185182021-03-31 08:14:51 -07001432 return AVIF_FALSE;
1433 }
1434
1435 for (int i = 0; i < 3; ++i) {
1436 uint32_t reserved;
1437 CHECK(avifROStreamReadU32(&s, &reserved)); // const unsigned int(32)[3] reserved = 0;
1438 }
1439
1440 // Verify that a valid string is here, but don't bother to store it
1441 CHECK(avifROStreamReadString(&s, NULL, 0)); // string name;
1442 return AVIF_TRUE;
1443}
1444
Joe Drago4d5f4a42021-04-28 13:11:45 -07001445static avifBool avifParseItemLocationBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago444f0512019-01-23 17:03:24 -08001446{
Joe Drago618ed5f2021-05-04 12:13:24 -07001447 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iloc]");
Joe Drago444f0512019-01-23 17:03:24 -08001448
Joe Dragof6a42272019-11-21 15:21:41 -08001449 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001450 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragof6a42272019-11-21 15:21:41 -08001451 if (version > 2) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001452 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported version [%u]", version);
Joe Dragof6a42272019-11-21 15:21:41 -08001453 return AVIF_FALSE;
1454 }
Joe Drago444f0512019-01-23 17:03:24 -08001455
Joe Drago8f7a3002019-02-07 19:35:37 -08001456 uint8_t offsetSizeAndLengthSize;
Joe Drago345aaa12019-09-25 13:42:12 -07001457 CHECK(avifROStreamRead(&s, &offsetSizeAndLengthSize, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001458 uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
1459 uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
Joe Drago444f0512019-01-23 17:03:24 -08001460
Joe Dragof6a42272019-11-21 15:21:41 -08001461 uint8_t baseOffsetSizeAndIndexSize;
1462 CHECK(avifROStreamRead(&s, &baseOffsetSizeAndIndexSize, 1));
1463 uint8_t baseOffsetSize = (baseOffsetSizeAndIndexSize >> 4) & 0xf; // unsigned int(4) base_offset_size;
1464 uint8_t indexSize = 0;
1465 if ((version == 1) || (version == 2)) {
1466 indexSize = baseOffsetSizeAndIndexSize & 0xf; // unsigned int(4) index_size;
1467 if (indexSize != 0) {
1468 // extent_index unsupported
Joe Drago618ed5f2021-05-04 12:13:24 -07001469 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported extent_index");
Joe Dragof6a42272019-11-21 15:21:41 -08001470 return AVIF_FALSE;
1471 }
1472 }
Joe Drago444f0512019-01-23 17:03:24 -08001473
Joe Dragof6a42272019-11-21 15:21:41 -08001474 uint16_t tmp16;
1475 uint32_t itemCount;
1476 if (version < 2) {
1477 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_count;
1478 itemCount = tmp16;
1479 } else {
1480 CHECK(avifROStreamReadU32(&s, &itemCount)); // unsigned int(32) item_count;
1481 }
1482 for (uint32_t i = 0; i < itemCount; ++i) {
1483 uint32_t itemID;
1484 uint32_t idatID = 0;
1485 if (version < 2) {
1486 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
1487 itemID = tmp16;
1488 } else {
1489 CHECK(avifROStreamReadU32(&s, &itemID)); // unsigned int(32) item_ID;
1490 }
1491
1492 if ((version == 1) || (version == 2)) {
1493 uint8_t ignored;
1494 uint8_t constructionMethod;
1495 CHECK(avifROStreamRead(&s, &ignored, 1)); // unsigned int(12) reserved = 0;
1496 CHECK(avifROStreamRead(&s, &constructionMethod, 1)); // unsigned int(4) construction_method;
1497 constructionMethod = constructionMethod & 0xf;
1498 if ((constructionMethod != 0 /* file */) && (constructionMethod != 1 /* idat */)) {
1499 // construction method item(2) unsupported
Joe Drago618ed5f2021-05-04 12:13:24 -07001500 avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported construction method [%u]", constructionMethod);
Joe Dragof6a42272019-11-21 15:21:41 -08001501 return AVIF_FALSE;
1502 }
1503 if (constructionMethod == 1) {
Joe Dragoba1eb492020-06-22 17:05:04 -07001504 idatID = meta->idatID;
Joe Dragof6a42272019-11-21 15:21:41 -08001505 }
1506 }
1507
Joe Dragoa4956902020-08-28 01:24:35 -07001508 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
1509 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001510 avifDiagnosticsPrintf(diag, "Box[iloc] has an invalid item ID [%u]", itemID);
Joe Dragoa4956902020-08-28 01:24:35 -07001511 return AVIF_FALSE;
1512 }
Joe Drago8f439092020-08-28 15:05:17 -07001513 if (item->extents.count > 0) {
1514 // This item has already been given extents via this iloc box. This is invalid.
Joe Drago4d5f4a42021-04-28 13:11:45 -07001515 avifDiagnosticsPrintf(diag, "Item ID [%u] contains duplicate sets of extents", itemID);
Joe Drago8f439092020-08-28 15:05:17 -07001516 return AVIF_FALSE;
1517 }
Joe Dragoa4956902020-08-28 01:24:35 -07001518 item->idatID = idatID;
1519
Joe Drago345aaa12019-09-25 13:42:12 -07001520 uint16_t dataReferenceIndex; // unsigned int(16) data_ref rence_index;
1521 CHECK(avifROStreamReadU16(&s, &dataReferenceIndex)); //
1522 uint64_t baseOffset; // unsigned int(base_offset_size*8) base_offset;
1523 CHECK(avifROStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
1524 uint16_t extentCount; // unsigned int(16) extent_count;
1525 CHECK(avifROStreamReadU16(&s, &extentCount)); //
Joe Dragoa4956902020-08-28 01:24:35 -07001526 for (int extentIter = 0; extentIter < extentCount; ++extentIter) {
Joe Dragof6a42272019-11-21 15:21:41 -08001527 // If extent_index is ever supported, this spec must be implemented here:
1528 // :: if (((version == 1) || (version == 2)) && (index_size > 0)) {
1529 // :: unsigned int(index_size*8) extent_index;
1530 // :: }
1531
Joe Drago8f7a3002019-02-07 19:35:37 -08001532 uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
Joe Drago345aaa12019-09-25 13:42:12 -07001533 CHECK(avifROStreamReadUX8(&s, &extentOffset, offsetSize));
Joe Drago8f7a3002019-02-07 19:35:37 -08001534 uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
Joe Drago345aaa12019-09-25 13:42:12 -07001535 CHECK(avifROStreamReadUX8(&s, &extentLength, lengthSize));
Joe Drago444f0512019-01-23 17:03:24 -08001536
Joe Drago4bcdfde2020-11-13 17:50:55 -08001537 avifExtent * extent = (avifExtent *)avifArrayPushPtr(&item->extents);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001538 if (extentOffset > UINT64_MAX - baseOffset) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001539 avifDiagnosticsPrintf(diag,
1540 "Item ID [%u] contains an extent offset which overflows: [base: %" PRIu64 " offset:%" PRIu64 "]",
1541 itemID,
1542 baseOffset,
1543 extentOffset);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001544 return AVIF_FALSE;
1545 }
1546 uint64_t offset = baseOffset + extentOffset;
Joe Drago217056b2020-11-13 16:19:35 -08001547 extent->offset = offset;
1548 if (extentLength > SIZE_MAX) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001549 avifDiagnosticsPrintf(diag, "Item ID [%u] contains an extent length which overflows: [%" PRIu64 "]", itemID, extentLength);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001550 return AVIF_FALSE;
1551 }
Joe Drago217056b2020-11-13 16:19:35 -08001552 extent->size = (size_t)extentLength;
1553 if (extent->size > SIZE_MAX - item->size) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001554 avifDiagnosticsPrintf(diag,
Joe Drago48867292021-05-04 13:23:08 -07001555 "Item ID [%u] contains an extent length which overflows the item size: [%zu, %zu]",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001556 itemID,
1557 extent->size,
1558 item->size);
Wan-Teh Changb9853692020-08-28 15:32:36 -07001559 return AVIF_FALSE;
1560 }
Joe Dragoa4956902020-08-28 01:24:35 -07001561 item->size += extent->size;
Joe Drago444f0512019-01-23 17:03:24 -08001562 }
1563 }
1564 return AVIF_TRUE;
1565}
1566
Joe Drago4d5f4a42021-04-28 13:11:45 -07001567static avifBool avifParseImageGridBox(avifImageGrid * grid, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago060d5342020-03-03 10:53:49 -08001568{
Joe Drago618ed5f2021-05-04 12:13:24 -07001569 BEGIN_STREAM(s, raw, rawLen, diag, "Box[grid]");
Joe Drago060d5342020-03-03 10:53:49 -08001570
1571 uint8_t version, flags;
1572 CHECK(avifROStreamRead(&s, &version, 1)); // unsigned int(8) version = 0;
1573 if (version != 0) {
Joe Dragoba4d67d2021-05-04 12:34:37 -07001574 avifDiagnosticsPrintf(diag, "Box[grid] has unsupported version [%u]", version);
Joe Drago060d5342020-03-03 10:53:49 -08001575 return AVIF_FALSE;
1576 }
Joe Drago79ebcf32020-11-18 22:37:10 -08001577 uint8_t rowsMinusOne, columnsMinusOne;
1578 CHECK(avifROStreamRead(&s, &flags, 1)); // unsigned int(8) flags;
1579 CHECK(avifROStreamRead(&s, &rowsMinusOne, 1)); // unsigned int(8) rows_minus_one;
1580 CHECK(avifROStreamRead(&s, &columnsMinusOne, 1)); // unsigned int(8) columns_minus_one;
1581 grid->rows = (uint32_t)rowsMinusOne + 1;
1582 grid->columns = (uint32_t)columnsMinusOne + 1;
Joe Drago060d5342020-03-03 10:53:49 -08001583
1584 uint32_t fieldLength = ((flags & 1) + 1) * 16;
1585 if (fieldLength == 16) {
1586 uint16_t outputWidth16, outputHeight16;
1587 CHECK(avifROStreamReadU16(&s, &outputWidth16)); // unsigned int(FieldLength) output_width;
1588 CHECK(avifROStreamReadU16(&s, &outputHeight16)); // unsigned int(FieldLength) output_height;
1589 grid->outputWidth = outputWidth16;
1590 grid->outputHeight = outputHeight16;
1591 } else {
1592 if (fieldLength != 32) {
1593 // This should be impossible
Joe Drago4d5f4a42021-04-28 13:11:45 -07001594 avifDiagnosticsPrintf(diag, "Grid box contains illegal field length: [%u]", fieldLength);
Joe Drago060d5342020-03-03 10:53:49 -08001595 return AVIF_FALSE;
1596 }
1597 CHECK(avifROStreamReadU32(&s, &grid->outputWidth)); // unsigned int(FieldLength) output_width;
1598 CHECK(avifROStreamReadU32(&s, &grid->outputHeight)); // unsigned int(FieldLength) output_height;
1599 }
wantehchangbc35a5f2020-08-12 15:27:39 -07001600 if ((grid->outputWidth == 0) || (grid->outputHeight == 0) || (grid->outputWidth > (AVIF_MAX_IMAGE_SIZE / grid->outputHeight))) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001601 avifDiagnosticsPrintf(diag, "Grid box contains illegal dimensions: [%u x %u]", grid->outputWidth, grid->outputHeight);
Wan-Teh Chang0a8e7242020-08-10 13:24:59 -07001602 return AVIF_FALSE;
1603 }
wantehchangae2074b2020-08-12 13:02:27 -07001604 return avifROStreamRemainingBytes(&s) == 0;
Joe Drago060d5342020-03-03 10:53:49 -08001605}
1606
Joe Drago618ed5f2021-05-04 12:13:24 -07001607static avifBool avifParseImageSpatialExtentsProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001608{
Joe Drago618ed5f2021-05-04 12:13:24 -07001609 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ispe]");
Joe Drago345aaa12019-09-25 13:42:12 -07001610 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08001611
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001612 avifImageSpatialExtents * ispe = &prop->u.ispe;
1613 CHECK(avifROStreamReadU32(&s, &ispe->width));
1614 CHECK(avifROStreamReadU32(&s, &ispe->height));
Joe Drago8f7a3002019-02-07 19:35:37 -08001615 return AVIF_TRUE;
1616}
1617
Joe Drago618ed5f2021-05-04 12:13:24 -07001618static avifBool avifParseAuxiliaryTypeProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragocd1e4c32019-02-08 11:26:31 -08001619{
Joe Drago618ed5f2021-05-04 12:13:24 -07001620 BEGIN_STREAM(s, raw, rawLen, diag, "Box[auxC]");
Joe Drago345aaa12019-09-25 13:42:12 -07001621 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001622
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001623 CHECK(avifROStreamReadString(&s, prop->u.auxC.auxType, AUXTYPE_SIZE));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001624 return AVIF_TRUE;
1625}
1626
Joe Drago618ed5f2021-05-04 12:13:24 -07001627static avifBool avifParseColourInformationBox(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago41eb62b2019-02-08 15:38:18 -08001628{
Joe Drago618ed5f2021-05-04 12:13:24 -07001629 BEGIN_STREAM(s, raw, rawLen, diag, "Box[colr]");
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001630
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001631 avifColourInformationBox * colr = &prop->u.colr;
1632 colr->hasICC = AVIF_FALSE;
1633 colr->hasNCLX = AVIF_FALSE;
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001634
Joe Dragoa0da4a42020-05-08 14:27:40 -07001635 uint8_t colorType[4]; // unsigned int(32) colour_type;
1636 CHECK(avifROStreamRead(&s, colorType, 4));
1637 if (!memcmp(colorType, "rICC", 4) || !memcmp(colorType, "prof", 4)) {
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001638 colr->hasICC = AVIF_TRUE;
1639 colr->icc = avifROStreamCurrent(&s);
1640 colr->iccSize = avifROStreamRemainingBytes(&s);
Joe Dragoa0da4a42020-05-08 14:27:40 -07001641 } else if (!memcmp(colorType, "nclx", 4)) {
Joe Dragofb5a5f02021-01-31 20:43:57 -08001642 CHECK(avifROStreamReadU16(&s, &colr->colorPrimaries)); // unsigned int(16) colour_primaries;
1643 CHECK(avifROStreamReadU16(&s, &colr->transferCharacteristics)); // unsigned int(16) transfer_characteristics;
1644 CHECK(avifROStreamReadU16(&s, &colr->matrixCoefficients)); // unsigned int(16) matrix_coefficients;
Joe Drago97b071c2019-07-17 14:24:56 -07001645 // unsigned int(1) full_range_flag;
1646 // unsigned int(7) reserved = 0;
Joe Drago74cd1c92020-04-16 12:17:11 -07001647 uint8_t tmp8;
1648 CHECK(avifROStreamRead(&s, &tmp8, 1));
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001649 colr->range = (tmp8 & 0x80) ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED;
1650 colr->hasNCLX = AVIF_TRUE;
Joe Drago41eb62b2019-02-08 15:38:18 -08001651 }
1652 return AVIF_TRUE;
1653}
1654
Joe Drago4d5f4a42021-04-28 13:11:45 -07001655static avifBool avifParseAV1CodecConfigurationBox(const uint8_t * raw, size_t rawLen, avifCodecConfigurationBox * av1C, avifDiagnostics * diag)
Joe Drago6500fd62019-10-08 17:17:34 -07001656{
Joe Drago618ed5f2021-05-04 12:13:24 -07001657 BEGIN_STREAM(s, raw, rawLen, diag, "Box[av1C]");
Joe Drago6500fd62019-10-08 17:17:34 -07001658
1659 uint8_t markerAndVersion = 0;
1660 CHECK(avifROStreamRead(&s, &markerAndVersion, 1));
1661 uint8_t seqProfileAndIndex = 0;
1662 CHECK(avifROStreamRead(&s, &seqProfileAndIndex, 1));
1663 uint8_t rawFlags = 0;
1664 CHECK(avifROStreamRead(&s, &rawFlags, 1));
1665
1666 if (markerAndVersion != 0x81) {
1667 // Marker and version must both == 1
Joe Drago4d5f4a42021-04-28 13:11:45 -07001668 avifDiagnosticsPrintf(diag, "av1C contains illegal marker and version pair: [%u]", markerAndVersion);
Joe Drago6500fd62019-10-08 17:17:34 -07001669 return AVIF_FALSE;
1670 }
1671
1672 av1C->seqProfile = (seqProfileAndIndex >> 5) & 0x7; // unsigned int (3) seq_profile;
1673 av1C->seqLevelIdx0 = (seqProfileAndIndex >> 0) & 0x1f; // unsigned int (5) seq_level_idx_0;
1674 av1C->seqTier0 = (rawFlags >> 7) & 0x1; // unsigned int (1) seq_tier_0;
1675 av1C->highBitdepth = (rawFlags >> 6) & 0x1; // unsigned int (1) high_bitdepth;
1676 av1C->twelveBit = (rawFlags >> 5) & 0x1; // unsigned int (1) twelve_bit;
1677 av1C->monochrome = (rawFlags >> 4) & 0x1; // unsigned int (1) monochrome;
1678 av1C->chromaSubsamplingX = (rawFlags >> 3) & 0x1; // unsigned int (1) chroma_subsampling_x;
1679 av1C->chromaSubsamplingY = (rawFlags >> 2) & 0x1; // unsigned int (1) chroma_subsampling_y;
1680 av1C->chromaSamplePosition = (rawFlags >> 0) & 0x3; // unsigned int (2) chroma_sample_position;
1681 return AVIF_TRUE;
1682}
1683
Joe Drago4d5f4a42021-04-28 13:11:45 -07001684static avifBool avifParseAV1CodecConfigurationBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago6500fd62019-10-08 17:17:34 -07001685{
Joe Drago4d5f4a42021-04-28 13:11:45 -07001686 return avifParseAV1CodecConfigurationBox(raw, rawLen, &prop->u.av1C, diag);
Joe Drago6500fd62019-10-08 17:17:34 -07001687}
1688
Joe Drago618ed5f2021-05-04 12:13:24 -07001689static avifBool avifParsePixelAspectRatioBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001690{
Joe Drago618ed5f2021-05-04 12:13:24 -07001691 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pasp]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001692
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001693 avifPixelAspectRatioBox * pasp = &prop->u.pasp;
Joe Drago89f0cc82020-03-09 16:13:27 -07001694 CHECK(avifROStreamReadU32(&s, &pasp->hSpacing)); // unsigned int(32) hSpacing;
1695 CHECK(avifROStreamReadU32(&s, &pasp->vSpacing)); // unsigned int(32) vSpacing;
1696 return AVIF_TRUE;
1697}
1698
Joe Drago618ed5f2021-05-04 12:13:24 -07001699static avifBool avifParseCleanApertureBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001700{
Joe Drago618ed5f2021-05-04 12:13:24 -07001701 BEGIN_STREAM(s, raw, rawLen, diag, "Box[clap]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001702
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001703 avifCleanApertureBox * clap = &prop->u.clap;
Joe Drago89f0cc82020-03-09 16:13:27 -07001704 CHECK(avifROStreamReadU32(&s, &clap->widthN)); // unsigned int(32) cleanApertureWidthN;
1705 CHECK(avifROStreamReadU32(&s, &clap->widthD)); // unsigned int(32) cleanApertureWidthD;
1706 CHECK(avifROStreamReadU32(&s, &clap->heightN)); // unsigned int(32) cleanApertureHeightN;
1707 CHECK(avifROStreamReadU32(&s, &clap->heightD)); // unsigned int(32) cleanApertureHeightD;
1708 CHECK(avifROStreamReadU32(&s, &clap->horizOffN)); // unsigned int(32) horizOffN;
1709 CHECK(avifROStreamReadU32(&s, &clap->horizOffD)); // unsigned int(32) horizOffD;
1710 CHECK(avifROStreamReadU32(&s, &clap->vertOffN)); // unsigned int(32) vertOffN;
1711 CHECK(avifROStreamReadU32(&s, &clap->vertOffD)); // unsigned int(32) vertOffD;
1712 return AVIF_TRUE;
1713}
1714
Joe Drago4d5f4a42021-04-28 13:11:45 -07001715static avifBool avifParseImageRotationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001716{
Joe Drago618ed5f2021-05-04 12:13:24 -07001717 BEGIN_STREAM(s, raw, rawLen, diag, "Box[irot]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001718
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001719 avifImageRotation * irot = &prop->u.irot;
Joe Drago89f0cc82020-03-09 16:13:27 -07001720 CHECK(avifROStreamRead(&s, &irot->angle, 1)); // unsigned int (6) reserved = 0; unsigned int (2) angle;
1721 if ((irot->angle & 0xfc) != 0) {
1722 // reserved bits must be 0
Joe Drago618ed5f2021-05-04 12:13:24 -07001723 avifDiagnosticsPrintf(diag, "Box[irot] contains nonzero reserved bits [%u]", irot->angle);
Joe Drago89f0cc82020-03-09 16:13:27 -07001724 return AVIF_FALSE;
1725 }
1726 return AVIF_TRUE;
1727}
1728
Joe Drago4d5f4a42021-04-28 13:11:45 -07001729static avifBool avifParseImageMirrorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago89f0cc82020-03-09 16:13:27 -07001730{
Joe Drago618ed5f2021-05-04 12:13:24 -07001731 BEGIN_STREAM(s, raw, rawLen, diag, "Box[imir]");
Joe Drago89f0cc82020-03-09 16:13:27 -07001732
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001733 avifImageMirror * imir = &prop->u.imir;
Joe Dragob551bb32021-06-03 15:22:05 -07001734 CHECK(avifROStreamRead(&s, &imir->mode, 1)); // unsigned int (7) reserved = 0; unsigned int (1) mode;
1735 if ((imir->mode & 0xfe) != 0) {
Joe Drago89f0cc82020-03-09 16:13:27 -07001736 // reserved bits must be 0
Joe Dragob551bb32021-06-03 15:22:05 -07001737 avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", imir->mode);
Joe Drago89f0cc82020-03-09 16:13:27 -07001738 return AVIF_FALSE;
1739 }
1740 return AVIF_TRUE;
1741}
1742
Joe Drago4d5f4a42021-04-28 13:11:45 -07001743static avifBool avifParsePixelInformationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago60421562020-04-23 11:32:26 -07001744{
Joe Drago618ed5f2021-05-04 12:13:24 -07001745 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pixi]");
Joe Drago60421562020-04-23 11:32:26 -07001746 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1747
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001748 avifPixelInformationProperty * pixi = &prop->u.pixi;
Joe Drago60421562020-04-23 11:32:26 -07001749 CHECK(avifROStreamRead(&s, &pixi->planeCount, 1)); // unsigned int (8) num_channels;
1750 if (pixi->planeCount > MAX_PIXI_PLANE_DEPTHS) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001751 avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported plane count [%u]", pixi->planeCount);
Joe Drago60421562020-04-23 11:32:26 -07001752 return AVIF_FALSE;
1753 }
1754 for (uint8_t i = 0; i < pixi->planeCount; ++i) {
1755 CHECK(avifROStreamRead(&s, &pixi->planeDepths[i], 1)); // unsigned int (8) bits_per_channel;
1756 }
1757 return AVIF_TRUE;
1758}
1759
Joe Dragobffba3b2021-05-26 15:46:10 -07001760static avifBool avifParseOperatingPointSelectorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
1761{
1762 BEGIN_STREAM(s, raw, rawLen, diag, "Box[a1op]");
1763
1764 avifOperatingPointSelectorProperty * a1op = &prop->u.a1op;
1765 CHECK(avifROStreamRead(&s, &a1op->opIndex, 1));
1766 if (a1op->opIndex > 31) { // 31 is AV1's max operating point value
1767 avifDiagnosticsPrintf(diag, "Box[a1op] contains an unsupported operating point [%u]", a1op->opIndex);
1768 return AVIF_FALSE;
1769 }
1770 return AVIF_TRUE;
1771}
1772
1773static avifBool avifParseLayerSelectorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
1774{
1775 BEGIN_STREAM(s, raw, rawLen, diag, "Box[lsel]");
1776
1777 avifLayerSelectorProperty * lsel = &prop->u.lsel;
1778 CHECK(avifROStreamReadU16(&s, &lsel->layerID));
1779 if (lsel->layerID >= MAX_AV1_LAYER_COUNT) {
1780 avifDiagnosticsPrintf(diag, "Box[lsel] contains an unsupported layer [%u]", lsel->layerID);
1781 return AVIF_FALSE;
1782 }
1783 return AVIF_TRUE;
1784}
1785
1786static avifBool avifParseAV1LayeredImageIndexingProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
1787{
1788 BEGIN_STREAM(s, raw, rawLen, diag, "Box[a1lx]");
1789
1790 avifAV1LayeredImageIndexingProperty * a1lx = &prop->u.a1lx;
1791
1792 uint8_t largeSize = 0;
1793 CHECK(avifROStreamRead(&s, &largeSize, 1));
1794 if (largeSize & 0xFE) {
1795 avifDiagnosticsPrintf(diag, "Box[a1lx] has bits set in the reserved section [%u]", largeSize);
1796 return AVIF_FALSE;
1797 }
1798
1799 for (int i = 0; i < 3; ++i) {
1800 if (largeSize) {
1801 CHECK(avifROStreamReadU32(&s, &a1lx->layerSize[i]));
1802 } else {
1803 uint16_t layerSize16;
1804 CHECK(avifROStreamReadU16(&s, &layerSize16));
1805 a1lx->layerSize[i] = (uint32_t)layerSize16;
1806 }
1807 }
1808
1809 // Layer sizes will be validated layer (when the item's size is known)
1810 return AVIF_TRUE;
1811}
1812
Joe Drago4d5f4a42021-04-28 13:11:45 -07001813static avifBool avifParseItemPropertyContainerBox(avifPropertyArray * properties, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08001814{
Wan-Teh Changd8867d12021-05-27 16:08:55 -07001815 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipco]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001816
Joe Drago345aaa12019-09-25 13:42:12 -07001817 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001818 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001819 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08001820
Joe Dragoa72da5b2020-06-15 19:40:17 -07001821 int propertyIndex = avifArrayPushIndex(properties);
1822 avifProperty * prop = &properties->prop[propertyIndex];
Wan-Teh Chang2031dc12020-05-22 09:24:46 -07001823 memcpy(prop->type, header.type, 4);
Joe Drago8f7a3002019-02-07 19:35:37 -08001824 if (!memcmp(header.type, "ispe", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001825 CHECK(avifParseImageSpatialExtentsProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001826 } else if (!memcmp(header.type, "auxC", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001827 CHECK(avifParseAuxiliaryTypeProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001828 } else if (!memcmp(header.type, "colr", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001829 CHECK(avifParseColourInformationBox(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001830 } else if (!memcmp(header.type, "av1C", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001831 CHECK(avifParseAV1CodecConfigurationBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001832 } else if (!memcmp(header.type, "pasp", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001833 CHECK(avifParsePixelAspectRatioBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001834 } else if (!memcmp(header.type, "clap", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001835 CHECK(avifParseCleanApertureBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001836 } else if (!memcmp(header.type, "irot", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001837 CHECK(avifParseImageRotationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001838 } else if (!memcmp(header.type, "imir", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001839 CHECK(avifParseImageMirrorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07001840 } else if (!memcmp(header.type, "pixi", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001841 CHECK(avifParsePixelInformationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Dragobffba3b2021-05-26 15:46:10 -07001842 } else if (!memcmp(header.type, "a1op", 4)) {
1843 CHECK(avifParseOperatingPointSelectorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
1844 } else if (!memcmp(header.type, "lsel", 4)) {
1845 CHECK(avifParseLayerSelectorProperty(prop, avifROStreamCurrent(&s), header.size, diag));
1846 } else if (!memcmp(header.type, "a1lx", 4)) {
1847 CHECK(avifParseAV1LayeredImageIndexingProperty(prop, avifROStreamCurrent(&s), header.size, diag));
Joe Drago60421562020-04-23 11:32:26 -07001848 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001849
Joe Drago345aaa12019-09-25 13:42:12 -07001850 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001851 }
1852 return AVIF_TRUE;
1853}
1854
Joe Drago4d5f4a42021-04-28 13:11:45 -07001855static avifBool avifParseItemPropertyAssociation(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag, uint32_t * outVersionAndFlags)
Joe Drago8f7a3002019-02-07 19:35:37 -08001856{
Joe Dragoc60ccae2021-03-31 10:30:48 -07001857 // NOTE: If this function ever adds support for versions other than [0,1] or flags other than
1858 // [0,1], please increase the value of MAX_IPMA_VERSION_AND_FLAGS_SEEN accordingly.
1859
Joe Drago618ed5f2021-05-04 12:13:24 -07001860 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipma]");
Joe Drago8f7a3002019-02-07 19:35:37 -08001861
1862 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07001863 uint32_t flags;
1864 CHECK(avifROStreamReadVersionAndFlags(&s, &version, &flags));
1865 avifBool propertyIndexIsU16 = ((flags & 0x1) != 0);
Wan-Teh Chang33d64622021-04-08 08:49:22 -07001866 *outVersionAndFlags = ((uint32_t)version << 24) | flags;
Joe Drago8f7a3002019-02-07 19:35:37 -08001867
1868 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001869 CHECK(avifROStreamReadU32(&s, &entryCount));
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001870 unsigned int prevItemID = 0;
Joe Drago8f7a3002019-02-07 19:35:37 -08001871 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001872 // ISO/IEC 23008-12, First edition, 2017-12, Section 9.3.1:
1873 // Each ItemPropertyAssociation box shall be ordered by increasing item_ID, and there shall
1874 // be at most one association box for each item_ID, in any ItemPropertyAssociation box.
Joe Drago8f7a3002019-02-07 19:35:37 -08001875 unsigned int itemID;
1876 if (version < 1) {
1877 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001878 CHECK(avifROStreamReadU16(&s, &tmp));
Joe Drago8f7a3002019-02-07 19:35:37 -08001879 itemID = tmp;
1880 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07001881 CHECK(avifROStreamReadU32(&s, &itemID));
Joe Drago8f7a3002019-02-07 19:35:37 -08001882 }
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001883 if (itemID <= prevItemID) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001884 avifDiagnosticsPrintf(diag, "Box[ipma] item IDs are not ordered by increasing ID");
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001885 return AVIF_FALSE;
1886 }
1887 prevItemID = itemID;
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001888
1889 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
1890 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001891 avifDiagnosticsPrintf(diag, "Box[ipma] has an invalid item ID [%u]", itemID);
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001892 return AVIF_FALSE;
1893 }
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001894 if (item->ipmaSeen) {
Joe Drago618ed5f2021-05-04 12:13:24 -07001895 avifDiagnosticsPrintf(diag, "Duplicate Box[ipma] for item ID [%u]", itemID);
Wan-Teh Changf4c65382020-11-03 14:27:45 -08001896 return AVIF_FALSE;
1897 }
1898 item->ipmaSeen = AVIF_TRUE;
Wan-Teh Chang750a1922020-10-28 17:54:35 -07001899
Joe Drago8f7a3002019-02-07 19:35:37 -08001900 uint8_t associationCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001901 CHECK(avifROStreamRead(&s, &associationCount, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001902 for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) {
Joe Drago3320e5f2020-04-21 17:36:27 -07001903 avifBool essential = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08001904 uint16_t propertyIndex = 0;
1905 if (propertyIndexIsU16) {
Joe Drago345aaa12019-09-25 13:42:12 -07001906 CHECK(avifROStreamReadU16(&s, &propertyIndex));
Joe Drago3320e5f2020-04-21 17:36:27 -07001907 essential = ((propertyIndex & 0x8000) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001908 propertyIndex &= 0x7fff;
1909 } else {
1910 uint8_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001911 CHECK(avifROStreamRead(&s, &tmp, 1));
Joe Drago3320e5f2020-04-21 17:36:27 -07001912 essential = ((tmp & 0x80) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001913 propertyIndex = tmp & 0x7f;
1914 }
1915
1916 if (propertyIndex == 0) {
1917 // Not associated with any item
1918 continue;
1919 }
1920 --propertyIndex; // 1-indexed
1921
Joe Drago9f2b87b2020-06-03 19:36:38 -07001922 if (propertyIndex >= meta->properties.count) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07001923 avifDiagnosticsPrintf(diag,
Joe Drago618ed5f2021-05-04 12:13:24 -07001924 "Box[ipma] for item ID [%u] contains an illegal property index [%u] (out of [%u] properties)",
Joe Drago4d5f4a42021-04-28 13:11:45 -07001925 itemID,
1926 propertyIndex,
1927 meta->properties.count);
Joe Drago8f7a3002019-02-07 19:35:37 -08001928 return AVIF_FALSE;
1929 }
1930
Joe Dragoa72da5b2020-06-15 19:40:17 -07001931 // Copy property to item
1932 avifProperty * srcProp = &meta->properties.prop[propertyIndex];
1933
Joe Dragobffba3b2021-05-26 15:46:10 -07001934 static const char * supportedTypes[] = { "ispe", "auxC", "colr", "av1C", "pasp", "clap",
1935 "irot", "imir", "pixi", "a1op", "lsel", "a1lx" };
Joe Dragoa72da5b2020-06-15 19:40:17 -07001936 size_t supportedTypesCount = sizeof(supportedTypes) / sizeof(supportedTypes[0]);
1937 avifBool supportedType = AVIF_FALSE;
1938 for (size_t i = 0; i < supportedTypesCount; ++i) {
1939 if (!memcmp(srcProp->type, supportedTypes[i], 4)) {
1940 supportedType = AVIF_TRUE;
1941 break;
1942 }
1943 }
1944 if (supportedType) {
Joe Dragobffba3b2021-05-26 15:46:10 -07001945 if (!essential) {
1946 // Verify that it is legal for this property to not be flagged as essential. Any
1947 // types in this list are *required* in the spec to be flagged as essential when
1948 // associated with an item.
1949 static const char * const essentialTypes[] = {
1950
1951 // AVIF: Section 2.3.2.1.1: "If associated, it shall be marked as essential."
1952 "a1op",
1953
1954 // HEIF: Section 6.5.11.1: "essential shall be equal to 1 for an 'lsel' item property."
1955 "lsel"
1956
1957 };
1958 size_t essentialTypesCount = sizeof(essentialTypes) / sizeof(essentialTypes[0]);
1959 for (size_t i = 0; i < essentialTypesCount; ++i) {
1960 if (!memcmp(srcProp->type, essentialTypes[i], 4)) {
1961 avifDiagnosticsPrintf(diag,
1962 "Item ID [%u] has a %s property association which must be marked essential, but is not",
1963 itemID,
1964 essentialTypes[i]);
1965 return AVIF_FALSE;
1966 }
1967 }
1968 }
1969
1970 // Supported and valid; associate it with this item.
Joe Dragoa72da5b2020-06-15 19:40:17 -07001971 avifProperty * dstProp = (avifProperty *)avifArrayPushPtr(&item->properties);
1972 memcpy(dstProp, srcProp, sizeof(avifProperty));
Joe Drago3320e5f2020-04-21 17:36:27 -07001973 } else {
1974 if (essential) {
1975 // Discovered an essential item property that libavif doesn't support!
1976 // Make a note to ignore this item later.
1977 item->hasUnsupportedEssentialProperty = AVIF_TRUE;
1978 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001979 }
1980 }
1981 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001982 return AVIF_TRUE;
1983}
1984
Joe Drago4d5f4a42021-04-28 13:11:45 -07001985static avifBool avifParsePrimaryItemBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08001986{
Joe Drago9f2b87b2020-06-03 19:36:38 -07001987 if (meta->primaryItemID > 0) {
Joe Dragof6a42272019-11-21 15:21:41 -08001988 // Illegal to have multiple pitm boxes, bail out
Joe Drago618ed5f2021-05-04 12:13:24 -07001989 avifDiagnosticsPrintf(diag, "Multiple boxes of unique Box[pitm] found");
Joe Dragof6a42272019-11-21 15:21:41 -08001990 return AVIF_FALSE;
1991 }
1992
Joe Drago618ed5f2021-05-04 12:13:24 -07001993 BEGIN_STREAM(s, raw, rawLen, diag, "Box[pitm]");
Joe Dragof6a42272019-11-21 15:21:41 -08001994
1995 uint8_t version;
1996 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
1997
1998 if (version == 0) {
1999 uint16_t tmp16;
2000 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
Joe Drago9f2b87b2020-06-03 19:36:38 -07002001 meta->primaryItemID = tmp16;
Joe Dragof6a42272019-11-21 15:21:41 -08002002 } else {
Joe Drago9f2b87b2020-06-03 19:36:38 -07002003 CHECK(avifROStreamReadU32(&s, &meta->primaryItemID)); // unsigned int(32) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -08002004 }
2005 return AVIF_TRUE;
2006}
2007
Joe Drago4d5f4a42021-04-28 13:11:45 -07002008static avifBool avifParseItemDataBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragof6a42272019-11-21 15:21:41 -08002009{
Joe Dragof6a42272019-11-21 15:21:41 -08002010 // 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 -07002011 for (uint32_t i = 0; i < meta->idats.count; ++i) {
Joe Dragoba1eb492020-06-22 17:05:04 -07002012 if (meta->idats.idat[i].id == meta->idatID) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002013 avifDiagnosticsPrintf(diag, "Meta box contains multiple idat boxes");
Joe Dragof6a42272019-11-21 15:21:41 -08002014 return AVIF_FALSE;
2015 }
2016 }
2017
Joe Drago9f2b87b2020-06-03 19:36:38 -07002018 int index = avifArrayPushIndex(&meta->idats);
2019 avifDecoderItemData * idat = &meta->idats.idat[index];
Joe Dragoba1eb492020-06-22 17:05:04 -07002020 idat->id = meta->idatID;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002021 avifRWDataSet(&idat->data, raw, rawLen);
Joe Dragof6a42272019-11-21 15:21:41 -08002022 return AVIF_TRUE;
2023}
2024
Joe Drago4d5f4a42021-04-28 13:11:45 -07002025static avifBool avifParseItemPropertiesBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002026{
Joe Drago618ed5f2021-05-04 12:13:24 -07002027 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002028
2029 avifBoxHeader ipcoHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002030 CHECK(avifROStreamReadBoxHeader(&s, &ipcoHeader));
wantehchangbc35a5f2020-08-12 15:27:39 -07002031 if (memcmp(ipcoHeader.type, "ipco", 4)) {
Joe Dragoba4d67d2021-05-04 12:34:37 -07002032 avifDiagnosticsPrintf(diag, "Failed to find Box[ipco] as the first box in Box[iprp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002033 return AVIF_FALSE;
2034 }
2035
2036 // Read all item properties inside of ItemPropertyContainerBox
Joe Drago4d5f4a42021-04-28 13:11:45 -07002037 CHECK(avifParseItemPropertyContainerBox(&meta->properties, avifROStreamCurrent(&s), ipcoHeader.size, diag));
Joe Drago345aaa12019-09-25 13:42:12 -07002038 CHECK(avifROStreamSkip(&s, ipcoHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002039
Joe Dragoc60ccae2021-03-31 10:30:48 -07002040 uint32_t versionAndFlagsSeen[MAX_IPMA_VERSION_AND_FLAGS_SEEN];
2041 uint32_t versionAndFlagsSeenCount = 0;
2042
Joe Drago8f7a3002019-02-07 19:35:37 -08002043 // Now read all ItemPropertyAssociation until the end of the box, and make associations
Joe Drago345aaa12019-09-25 13:42:12 -07002044 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002045 avifBoxHeader ipmaHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002046 CHECK(avifROStreamReadBoxHeader(&s, &ipmaHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08002047
2048 if (!memcmp(ipmaHeader.type, "ipma", 4)) {
Joe Dragoc60ccae2021-03-31 10:30:48 -07002049 uint32_t versionAndFlags;
Joe Drago4d5f4a42021-04-28 13:11:45 -07002050 CHECK(avifParseItemPropertyAssociation(meta, avifROStreamCurrent(&s), ipmaHeader.size, diag, &versionAndFlags));
Joe Dragoc60ccae2021-03-31 10:30:48 -07002051 for (uint32_t i = 0; i < versionAndFlagsSeenCount; ++i) {
2052 if (versionAndFlagsSeen[i] == versionAndFlags) {
2053 // HEIF (ISO 23008-12:2017) 9.3.1 - There shall be at most one
2054 // ItemPropertyAssociation box with a given pair of values of version and
2055 // flags.
Joe Drago618ed5f2021-05-04 12:13:24 -07002056 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 -07002057 return AVIF_FALSE;
2058 }
2059 }
2060 if (versionAndFlagsSeenCount == MAX_IPMA_VERSION_AND_FLAGS_SEEN) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002061 avifDiagnosticsPrintf(diag, "Exceeded possible count of unique ipma version and flags tuples");
Joe Dragoc60ccae2021-03-31 10:30:48 -07002062 return AVIF_FALSE;
2063 }
2064 versionAndFlagsSeen[versionAndFlagsSeenCount] = versionAndFlags;
2065 ++versionAndFlagsSeenCount;
Joe Drago8f7a3002019-02-07 19:35:37 -08002066 } else {
2067 // These must all be type ipma
Joe Drago618ed5f2021-05-04 12:13:24 -07002068 avifDiagnosticsPrintf(diag, "Box[iprp] contains a box that isn't type 'ipma'");
Joe Drago8f7a3002019-02-07 19:35:37 -08002069 return AVIF_FALSE;
2070 }
2071
Joe Drago345aaa12019-09-25 13:42:12 -07002072 CHECK(avifROStreamSkip(&s, ipmaHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002073 }
2074 return AVIF_TRUE;
2075}
2076
Joe Drago4d5f4a42021-04-28 13:11:45 -07002077static avifBool avifParseItemInfoEntry(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002078{
Joe Drago618ed5f2021-05-04 12:13:24 -07002079 BEGIN_STREAM(s, raw, rawLen, diag, "Box[infe]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002080
Joe Drago345aaa12019-09-25 13:42:12 -07002081 CHECK(avifROStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
Joe Drago8f7a3002019-02-07 19:35:37 -08002082
Joe Drago345aaa12019-09-25 13:42:12 -07002083 uint16_t itemID; // unsigned int(16) item_ID;
2084 CHECK(avifROStreamReadU16(&s, &itemID)); //
2085 uint16_t itemProtectionIndex; // unsigned int(16) item_protection_index;
2086 CHECK(avifROStreamReadU16(&s, &itemProtectionIndex)); //
2087 uint8_t itemType[4]; // unsigned int(32) item_type;
2088 CHECK(avifROStreamRead(&s, itemType, 4)); //
Joe Drago8f7a3002019-02-07 19:35:37 -08002089
Joe Dragof6a42272019-11-21 15:21:41 -08002090 avifContentType contentType;
2091 if (!memcmp(itemType, "mime", 4)) {
2092 CHECK(avifROStreamReadString(&s, NULL, 0)); // string item_name; (skipped)
2093 CHECK(avifROStreamReadString(&s, contentType.contentType, CONTENTTYPE_SIZE)); // string content_type;
2094 } else {
2095 memset(&contentType, 0, sizeof(contentType));
2096 }
2097
Joe Drago9f2b87b2020-06-03 19:36:38 -07002098 avifDecoderItem * item = avifMetaFindItem(meta, itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07002099 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002100 avifDiagnosticsPrintf(diag, "Box[infe] has an invalid item ID [%u]", itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07002101 return AVIF_FALSE;
2102 }
2103
Joe Drago05559c92019-07-17 16:33:38 -07002104 memcpy(item->type, itemType, sizeof(itemType));
Joe Dragof6a42272019-11-21 15:21:41 -08002105 memcpy(&item->contentType, &contentType, sizeof(contentType));
Joe Drago8f7a3002019-02-07 19:35:37 -08002106 return AVIF_TRUE;
2107}
2108
Joe Drago4d5f4a42021-04-28 13:11:45 -07002109static avifBool avifParseItemInfoBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002110{
Joe Drago618ed5f2021-05-04 12:13:24 -07002111 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iinf]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002112
2113 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07002114 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08002115 uint32_t entryCount;
2116 if (version == 0) {
2117 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002118 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08002119 entryCount = tmp;
2120 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002121 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08002122 } else {
Joe Dragoba4d67d2021-05-04 12:34:37 -07002123 avifDiagnosticsPrintf(diag, "Box[iinf] has an unsupported version %u", version);
Joe Drago8f7a3002019-02-07 19:35:37 -08002124 return AVIF_FALSE;
2125 }
2126
Joe Drago678b9382019-02-09 03:17:47 -08002127 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002128 avifBoxHeader infeHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002129 CHECK(avifROStreamReadBoxHeader(&s, &infeHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08002130
2131 if (!memcmp(infeHeader.type, "infe", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002132 CHECK(avifParseItemInfoEntry(meta, avifROStreamCurrent(&s), infeHeader.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002133 } else {
Joe Drago618ed5f2021-05-04 12:13:24 -07002134 // These must all be type infe
2135 avifDiagnosticsPrintf(diag, "Box[iinf] contains a box that isn't type 'infe'");
Joe Drago8f7a3002019-02-07 19:35:37 -08002136 return AVIF_FALSE;
2137 }
2138
Joe Drago345aaa12019-09-25 13:42:12 -07002139 CHECK(avifROStreamSkip(&s, infeHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002140 }
2141
2142 return AVIF_TRUE;
2143}
2144
Joe Drago4d5f4a42021-04-28 13:11:45 -07002145static avifBool avifParseItemReferenceBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002146{
Joe Drago618ed5f2021-05-04 12:13:24 -07002147 BEGIN_STREAM(s, raw, rawLen, diag, "Box[iref]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002148
2149 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07002150 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08002151
Joe Drago345aaa12019-09-25 13:42:12 -07002152 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002153 avifBoxHeader irefHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07002154 CHECK(avifROStreamReadBoxHeader(&s, &irefHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08002155
2156 uint32_t fromID = 0;
2157 if (version == 0) {
2158 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002159 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002160 fromID = tmp;
2161 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002162 CHECK(avifROStreamReadU32(&s, &fromID)); // unsigned int(32) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002163 } else {
2164 // unsupported iref version, skip it
2165 break;
2166 }
2167
2168 uint16_t referenceCount = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07002169 CHECK(avifROStreamReadU16(&s, &referenceCount)); // unsigned int(16) reference_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08002170
2171 for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) {
2172 uint32_t toID = 0;
2173 if (version == 0) {
2174 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07002175 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002176 toID = tmp;
2177 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002178 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08002179 } else {
2180 // unsupported iref version, skip it
2181 break;
2182 }
2183
2184 // Read this reference as "{fromID} is a {irefType} for {toID}"
2185 if (fromID && toID) {
Joe Drago9f2b87b2020-06-03 19:36:38 -07002186 avifDecoderItem * item = avifMetaFindItem(meta, fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07002187 if (!item) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002188 avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID [%u]", fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07002189 return AVIF_FALSE;
2190 }
2191
Joe Drago8f7a3002019-02-07 19:35:37 -08002192 if (!memcmp(irefHeader.type, "thmb", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07002193 item->thumbnailForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002194 } else if (!memcmp(irefHeader.type, "auxl", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07002195 item->auxForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002196 } else if (!memcmp(irefHeader.type, "cdsc", 4)) {
Joe Dragof6a42272019-11-21 15:21:41 -08002197 item->descForID = toID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002198 } else if (!memcmp(irefHeader.type, "dimg", 4)) {
Joe Drago060d5342020-03-03 10:53:49 -08002199 // derived images refer in the opposite direction
Joe Drago9f2b87b2020-06-03 19:36:38 -07002200 avifDecoderItem * dimg = avifMetaFindItem(meta, toID);
Joe Drago060d5342020-03-03 10:53:49 -08002201 if (!dimg) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002202 avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID dimg ref [%u]", toID);
Joe Drago060d5342020-03-03 10:53:49 -08002203 return AVIF_FALSE;
2204 }
2205
2206 dimg->dimgForID = fromID;
Wan-Teh Changb309e982021-02-20 16:59:36 -08002207 } else if (!memcmp(irefHeader.type, "prem", 4)) {
Yuan Tonge4850be2021-01-22 14:21:25 +08002208 item->premByID = toID;
2209 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002210 }
2211 }
2212 }
2213
2214 return AVIF_TRUE;
2215}
2216
Joe Drago4d5f4a42021-04-28 13:11:45 -07002217static avifBool avifParseMetaBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002218{
Joe Drago618ed5f2021-05-04 12:13:24 -07002219 BEGIN_STREAM(s, raw, rawLen, diag, "Box[meta]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002220
Joe Drago345aaa12019-09-25 13:42:12 -07002221 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08002222
Joe Dragoba1eb492020-06-22 17:05:04 -07002223 ++meta->idatID; // for tracking idat
Joe Dragof6a42272019-11-21 15:21:41 -08002224
Joe Dragoe0185182021-03-31 08:14:51 -07002225 avifBool firstBox = AVIF_TRUE;
Joe Drago8f439092020-08-28 15:05:17 -07002226 uint32_t uniqueBoxFlags = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07002227 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002228 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002229 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08002230
Joe Dragoe0185182021-03-31 08:14:51 -07002231 if (firstBox) {
2232 if (!memcmp(header.type, "hdlr", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002233 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 0, "meta", "hdlr", diag));
2234 CHECK(avifParseHandlerBox(avifROStreamCurrent(&s), header.size, diag));
Joe Dragoe0185182021-03-31 08:14:51 -07002235 firstBox = AVIF_FALSE;
2236 } else {
2237 // hdlr must be the first box!
Joe Dragoba4d67d2021-05-04 12:34:37 -07002238 avifDiagnosticsPrintf(diag, "Box[meta] does not have a Box[hdlr] as its first child box");
Joe Dragoe0185182021-03-31 08:14:51 -07002239 return AVIF_FALSE;
2240 }
2241 } else if (!memcmp(header.type, "iloc", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002242 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 1, "meta", "iloc", diag));
2243 CHECK(avifParseItemLocationBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Dragof6a42272019-11-21 15:21:41 -08002244 } else if (!memcmp(header.type, "pitm", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002245 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 2, "meta", "pitm", diag));
2246 CHECK(avifParsePrimaryItemBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Dragof6a42272019-11-21 15:21:41 -08002247 } else if (!memcmp(header.type, "idat", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002248 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 3, "meta", "idat", diag));
2249 CHECK(avifParseItemDataBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002250 } else if (!memcmp(header.type, "iprp", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002251 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 4, "meta", "iprp", diag));
2252 CHECK(avifParseItemPropertiesBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002253 } else if (!memcmp(header.type, "iinf", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002254 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 5, "meta", "iinf", diag));
2255 CHECK(avifParseItemInfoBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002256 } else if (!memcmp(header.type, "iref", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002257 CHECK(uniqueBoxSeen(&uniqueBoxFlags, 6, "meta", "iref", diag));
2258 CHECK(avifParseItemReferenceBox(meta, avifROStreamCurrent(&s), header.size, diag));
Joe Drago8f7a3002019-02-07 19:35:37 -08002259 }
2260
Joe Drago345aaa12019-09-25 13:42:12 -07002261 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08002262 }
Joe Dragoe0185182021-03-31 08:14:51 -07002263 if (firstBox) {
2264 // The meta box must not be empty (it must contain at least a hdlr box)
Joe Dragoba4d67d2021-05-04 12:34:37 -07002265 avifDiagnosticsPrintf(diag, "Box[meta] has no child boxes");
Joe Dragoe0185182021-03-31 08:14:51 -07002266 return AVIF_FALSE;
2267 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002268 return AVIF_TRUE;
2269}
2270
Joe Drago4d5f4a42021-04-28 13:11:45 -07002271static avifBool avifParseTrackHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002272{
Joe Drago618ed5f2021-05-04 12:13:24 -07002273 BEGIN_STREAM(s, raw, rawLen, diag, "Box[tkhd]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002274
2275 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07002276 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002277
2278 uint32_t ignored32, trackID;
2279 uint64_t ignored64;
2280 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002281 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
2282 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
2283 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07002284 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
2285 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002286 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07002287 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
2288 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
2289 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07002290 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
2291 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002292 } else {
2293 // Unsupported version
Joe Drago618ed5f2021-05-04 12:13:24 -07002294 avifDiagnosticsPrintf(diag, "Box[tkhd] has an unsupported version [%u]", version);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002295 return AVIF_FALSE;
2296 }
2297
Joe Dragofc4144e2019-09-27 20:35:06 -07002298 // Skipping the following 52 bytes here:
2299 // ------------------------------------
2300 // const unsigned int(32)[2] reserved = 0;
2301 // template int(16) layer = 0;
2302 // template int(16) alternate_group = 0;
2303 // template int(16) volume = {if track_is_audio 0x0100 else 0};
2304 // const unsigned int(16) reserved = 0;
2305 // template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix
2306 CHECK(avifROStreamSkip(&s, 52));
2307
2308 uint32_t width, height;
2309 CHECK(avifROStreamReadU32(&s, &width)); // unsigned int(32) width;
2310 CHECK(avifROStreamReadU32(&s, &height)); // unsigned int(32) height;
2311 track->width = width >> 16;
2312 track->height = height >> 16;
2313
Joe Drago46104d62021-05-27 18:17:29 -07002314 if ((track->width == 0) || (track->height == 0) || (track->width > (AVIF_MAX_IMAGE_SIZE / track->height))) {
2315 avifDiagnosticsPrintf(diag, "Track ID [%u] has an invalid size [%ux%u]", track->id, track->width, track->height);
2316 return AVIF_FALSE;
2317 }
2318
Joe Dragoae7e2c32019-07-18 15:22:25 -07002319 // TODO: support scaling based on width/height track header info?
2320
2321 track->id = trackID;
2322 return AVIF_TRUE;
2323}
2324
Joe Drago4d5f4a42021-04-28 13:11:45 -07002325static avifBool avifParseMediaHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002326{
Joe Drago618ed5f2021-05-04 12:13:24 -07002327 BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdhd]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002328
2329 uint8_t version;
Joe Drago4a25c192020-06-03 16:29:58 -07002330 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002331
2332 uint32_t ignored32, mediaTimescale, mediaDuration32;
2333 uint64_t ignored64, mediaDuration64;
2334 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07002335 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
2336 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
2337 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
2338 CHECK(avifROStreamReadU64(&s, &mediaDuration64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002339 track->mediaDuration = mediaDuration64;
2340 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07002341 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
2342 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
2343 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
2344 CHECK(avifROStreamReadU32(&s, &mediaDuration32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002345 track->mediaDuration = (uint64_t)mediaDuration32;
2346 } else {
2347 // Unsupported version
Joe Drago618ed5f2021-05-04 12:13:24 -07002348 avifDiagnosticsPrintf(diag, "Box[mdhd] has an unsupported version [%u]", version);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002349 return AVIF_FALSE;
2350 }
2351
2352 track->mediaTimescale = mediaTimescale;
2353 return AVIF_TRUE;
2354}
2355
Joe Drago618ed5f2021-05-04 12:13:24 -07002356static avifBool avifParseChunkOffsetBox(avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002357{
Wan-Teh Chang72de1662021-05-28 18:11:11 -07002358 BEGIN_STREAM(s, raw, rawLen, diag, largeOffsets ? "Box[co64]" : "Box[stco]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002359
Joe Drago345aaa12019-09-25 13:42:12 -07002360 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002361
2362 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002363 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002364 for (uint32_t i = 0; i < entryCount; ++i) {
2365 uint64_t offset;
2366 if (largeOffsets) {
Joe Drago345aaa12019-09-25 13:42:12 -07002367 CHECK(avifROStreamReadU64(&s, &offset)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002368 } else {
2369 uint32_t offset32;
Joe Drago345aaa12019-09-25 13:42:12 -07002370 CHECK(avifROStreamReadU32(&s, &offset32)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002371 offset = (uint64_t)offset32;
2372 }
2373
2374 avifSampleTableChunk * chunk = (avifSampleTableChunk *)avifArrayPushPtr(&sampleTable->chunks);
2375 chunk->offset = offset;
2376 }
2377 return AVIF_TRUE;
2378}
2379
Joe Drago4d5f4a42021-04-28 13:11:45 -07002380static avifBool avifParseSampleToChunkBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002381{
Joe Drago618ed5f2021-05-04 12:13:24 -07002382 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsc]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002383
Joe Drago345aaa12019-09-25 13:42:12 -07002384 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002385
2386 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002387 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago859c9102021-03-11 18:06:40 -08002388 uint32_t prevFirstChunk = 0;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002389 for (uint32_t i = 0; i < entryCount; ++i) {
2390 avifSampleTableSampleToChunk * sampleToChunk = (avifSampleTableSampleToChunk *)avifArrayPushPtr(&sampleTable->sampleToChunks);
Joe Drago345aaa12019-09-25 13:42:12 -07002391 CHECK(avifROStreamReadU32(&s, &sampleToChunk->firstChunk)); // unsigned int(32) first_chunk;
2392 CHECK(avifROStreamReadU32(&s, &sampleToChunk->samplesPerChunk)); // unsigned int(32) samples_per_chunk;
2393 CHECK(avifROStreamReadU32(&s, &sampleToChunk->sampleDescriptionIndex)); // unsigned int(32) sample_description_index;
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002394 // The first_chunk fields should start with 1 and be strictly increasing.
2395 if (i == 0) {
2396 if (sampleToChunk->firstChunk != 1) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002397 avifDiagnosticsPrintf(diag, "Box[stsc] does not begin with chunk 1 [%u]", sampleToChunk->firstChunk);
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002398 return AVIF_FALSE;
2399 }
2400 } else {
2401 if (sampleToChunk->firstChunk <= prevFirstChunk) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002402 avifDiagnosticsPrintf(diag, "Box[stsc] chunks are not strictly increasing");
Wan-Teh Chang2456d2f2021-03-11 17:31:03 -08002403 return AVIF_FALSE;
2404 }
2405 }
2406 prevFirstChunk = sampleToChunk->firstChunk;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002407 }
2408 return AVIF_TRUE;
2409}
2410
Joe Drago618ed5f2021-05-04 12:13:24 -07002411static avifBool avifParseSampleSizeBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002412{
Joe Drago618ed5f2021-05-04 12:13:24 -07002413 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsz]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002414
Joe Drago345aaa12019-09-25 13:42:12 -07002415 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002416
Joe Drago370be3f2020-02-07 15:59:42 -08002417 uint32_t allSamplesSize, sampleCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002418 CHECK(avifROStreamReadU32(&s, &allSamplesSize)); // unsigned int(32) sample_size;
Joe Drago370be3f2020-02-07 15:59:42 -08002419 CHECK(avifROStreamReadU32(&s, &sampleCount)); // unsigned int(32) sample_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002420
Joe Drago370be3f2020-02-07 15:59:42 -08002421 if (allSamplesSize > 0) {
2422 sampleTable->allSamplesSize = allSamplesSize;
2423 } else {
2424 for (uint32_t i = 0; i < sampleCount; ++i) {
2425 avifSampleTableSampleSize * sampleSize = (avifSampleTableSampleSize *)avifArrayPushPtr(&sampleTable->sampleSizes);
Joe Drago345aaa12019-09-25 13:42:12 -07002426 CHECK(avifROStreamReadU32(&s, &sampleSize->size)); // unsigned int(32) entry_size;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002427 }
2428 }
2429 return AVIF_TRUE;
2430}
2431
Joe Drago618ed5f2021-05-04 12:13:24 -07002432static avifBool avifParseSyncSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago22c1ad92019-09-26 12:46:50 -07002433{
Joe Drago618ed5f2021-05-04 12:13:24 -07002434 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stss]");
Joe Drago22c1ad92019-09-26 12:46:50 -07002435
2436 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
2437
2438 uint32_t entryCount;
2439 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
2440
2441 for (uint32_t i = 0; i < entryCount; ++i) {
2442 uint32_t sampleNumber = 0;
2443 CHECK(avifROStreamReadU32(&s, &sampleNumber)); // unsigned int(32) sample_number;
2444 avifSyncSample * syncSample = (avifSyncSample *)avifArrayPushPtr(&sampleTable->syncSamples);
2445 syncSample->sampleNumber = sampleNumber;
2446 }
2447 return AVIF_TRUE;
2448}
2449
Joe Drago618ed5f2021-05-04 12:13:24 -07002450static avifBool avifParseTimeToSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002451{
Joe Drago618ed5f2021-05-04 12:13:24 -07002452 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stts]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002453
Joe Drago345aaa12019-09-25 13:42:12 -07002454 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002455
2456 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07002457 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002458
2459 for (uint32_t i = 0; i < entryCount; ++i) {
2460 avifSampleTableTimeToSample * timeToSample = (avifSampleTableTimeToSample *)avifArrayPushPtr(&sampleTable->timeToSamples);
Joe Drago345aaa12019-09-25 13:42:12 -07002461 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleCount)); // unsigned int(32) sample_count;
2462 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleDelta)); // unsigned int(32) sample_delta;
Joe Dragoae7e2c32019-07-18 15:22:25 -07002463 }
2464 return AVIF_TRUE;
2465}
2466
Joe Drago4d5f4a42021-04-28 13:11:45 -07002467static avifBool avifParseSampleDescriptionBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago2c0924c2019-09-26 17:41:01 -07002468{
Joe Drago618ed5f2021-05-04 12:13:24 -07002469 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsd]");
Joe Drago2c0924c2019-09-26 17:41:01 -07002470
2471 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
2472
2473 uint32_t entryCount;
2474 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
2475
2476 for (uint32_t i = 0; i < entryCount; ++i) {
2477 avifBoxHeader sampleEntryHeader;
2478 CHECK(avifROStreamReadBoxHeader(&s, &sampleEntryHeader));
2479
2480 avifSampleDescription * description = (avifSampleDescription *)avifArrayPushPtr(&sampleTable->sampleDescriptions);
Joe Dragoa72da5b2020-06-15 19:40:17 -07002481 avifArrayCreate(&description->properties, sizeof(avifProperty), 16);
Joe Drago2c0924c2019-09-26 17:41:01 -07002482 memcpy(description->format, sampleEntryHeader.type, sizeof(description->format));
Joe Drago6500fd62019-10-08 17:17:34 -07002483 size_t remainingBytes = avifROStreamRemainingBytes(&s);
2484 if (!memcmp(description->format, "av01", 4) && (remainingBytes > VISUALSAMPLEENTRY_SIZE)) {
Joe Drago11d23592021-01-05 14:18:57 -08002485 CHECK(avifParseItemPropertyContainerBox(&description->properties,
2486 avifROStreamCurrent(&s) + VISUALSAMPLEENTRY_SIZE,
Joe Drago4d5f4a42021-04-28 13:11:45 -07002487 remainingBytes - VISUALSAMPLEENTRY_SIZE,
2488 diag));
Joe Drago6500fd62019-10-08 17:17:34 -07002489 }
Joe Drago2c0924c2019-09-26 17:41:01 -07002490
2491 CHECK(avifROStreamSkip(&s, sampleEntryHeader.size));
2492 }
2493 return AVIF_TRUE;
2494}
2495
Joe Drago4d5f4a42021-04-28 13:11:45 -07002496static avifBool avifParseSampleTableBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002497{
2498 if (track->sampleTable) {
2499 // A TrackBox may only have one SampleTable
Joe Dragoba4d67d2021-05-04 12:34:37 -07002500 avifDiagnosticsPrintf(diag, "Duplicate Box[stbl] for a single track detected");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002501 return AVIF_FALSE;
2502 }
2503 track->sampleTable = avifSampleTableCreate();
2504
Joe Drago618ed5f2021-05-04 12:13:24 -07002505 BEGIN_STREAM(s, raw, rawLen, diag, "Box[stbl]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002506
Joe Drago345aaa12019-09-25 13:42:12 -07002507 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002508 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002509 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002510
2511 if (!memcmp(header.type, "stco", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002512 CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002513 } else if (!memcmp(header.type, "co64", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002514 CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002515 } else if (!memcmp(header.type, "stsc", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002516 CHECK(avifParseSampleToChunkBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002517 } else if (!memcmp(header.type, "stsz", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002518 CHECK(avifParseSampleSizeBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Drago22c1ad92019-09-26 12:46:50 -07002519 } else if (!memcmp(header.type, "stss", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002520 CHECK(avifParseSyncSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002521 } else if (!memcmp(header.type, "stts", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002522 CHECK(avifParseTimeToSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Drago2c0924c2019-09-26 17:41:01 -07002523 } else if (!memcmp(header.type, "stsd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002524 CHECK(avifParseSampleDescriptionBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002525 }
2526
Joe Drago345aaa12019-09-25 13:42:12 -07002527 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002528 }
Joe Dragoae7e2c32019-07-18 15:22:25 -07002529 return AVIF_TRUE;
2530}
2531
Joe Drago4d5f4a42021-04-28 13:11:45 -07002532static avifBool avifParseMediaInformationBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002533{
Joe Drago618ed5f2021-05-04 12:13:24 -07002534 BEGIN_STREAM(s, raw, rawLen, diag, "Box[minf]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002535
Joe Drago345aaa12019-09-25 13:42:12 -07002536 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002537 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002538 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002539
2540 if (!memcmp(header.type, "stbl", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002541 CHECK(avifParseSampleTableBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002542 }
2543
Joe Drago345aaa12019-09-25 13:42:12 -07002544 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002545 }
2546 return AVIF_TRUE;
2547}
2548
Joe Drago4d5f4a42021-04-28 13:11:45 -07002549static avifBool avifParseMediaBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002550{
Joe Drago618ed5f2021-05-04 12:13:24 -07002551 BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdia]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002552
Joe Drago345aaa12019-09-25 13:42:12 -07002553 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002554 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002555 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002556
2557 if (!memcmp(header.type, "mdhd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002558 CHECK(avifParseMediaHeaderBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002559 } else if (!memcmp(header.type, "minf", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002560 CHECK(avifParseMediaInformationBox(track, avifROStreamCurrent(&s), header.size, diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002561 }
2562
Joe Drago345aaa12019-09-25 13:42:12 -07002563 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002564 }
2565 return AVIF_TRUE;
2566}
2567
Joe Drago618ed5f2021-05-04 12:13:24 -07002568static avifBool avifTrackReferenceBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago46ea0582019-07-22 15:55:47 -07002569{
Joe Drago618ed5f2021-05-04 12:13:24 -07002570 BEGIN_STREAM(s, raw, rawLen, diag, "Box[tref]");
Joe Drago46ea0582019-07-22 15:55:47 -07002571
Joe Drago345aaa12019-09-25 13:42:12 -07002572 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002573 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002574 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago46ea0582019-07-22 15:55:47 -07002575
2576 if (!memcmp(header.type, "auxl", 4)) {
2577 uint32_t toID;
Joe Dragoceb2fa02021-01-29 18:14:55 -08002578 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) track_IDs[];
Joe Drago345aaa12019-09-25 13:42:12 -07002579 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Joe Drago46ea0582019-07-22 15:55:47 -07002580 track->auxForID = toID;
Yuan Tonge4850be2021-01-22 14:21:25 +08002581 } else if (!memcmp(header.type, "prem", 4)) {
2582 uint32_t byID;
Joe Dragoceb2fa02021-01-29 18:14:55 -08002583 CHECK(avifROStreamReadU32(&s, &byID)); // unsigned int(32) track_IDs[];
2584 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Yuan Tonge4850be2021-01-22 14:21:25 +08002585 track->premByID = byID;
Joe Drago46ea0582019-07-22 15:55:47 -07002586 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07002587 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago46ea0582019-07-22 15:55:47 -07002588 }
2589 }
2590 return AVIF_TRUE;
2591}
2592
Wan-Teh Chang53121812021-05-28 18:02:59 -07002593static avifBool avifParseTrackBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002594{
Wan-Teh Chang53121812021-05-28 18:02:59 -07002595 BEGIN_STREAM(s, raw, rawLen, data->diag, "Box[trak]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002596
Joe Dragoa72da5b2020-06-15 19:40:17 -07002597 avifTrack * track = avifDecoderDataCreateTrack(data);
Joe Dragoae7e2c32019-07-18 15:22:25 -07002598
Joe Drago345aaa12019-09-25 13:42:12 -07002599 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002600 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002601 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002602
2603 if (!memcmp(header.type, "tkhd", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002604 CHECK(avifParseTrackHeaderBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoa72da5b2020-06-15 19:40:17 -07002605 } else if (!memcmp(header.type, "meta", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002606 CHECK(avifParseMetaBox(track->meta, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002607 } else if (!memcmp(header.type, "mdia", 4)) {
Joe Drago4d5f4a42021-04-28 13:11:45 -07002608 CHECK(avifParseMediaBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Drago46ea0582019-07-22 15:55:47 -07002609 } else if (!memcmp(header.type, "tref", 4)) {
Joe Drago618ed5f2021-05-04 12:13:24 -07002610 CHECK(avifTrackReferenceBox(track, avifROStreamCurrent(&s), header.size, data->diag));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002611 }
2612
Joe Drago345aaa12019-09-25 13:42:12 -07002613 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002614 }
2615 return AVIF_TRUE;
2616}
2617
Wan-Teh Chang53121812021-05-28 18:02:59 -07002618static avifBool avifParseMoovBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07002619{
Wan-Teh Chang53121812021-05-28 18:02:59 -07002620 BEGIN_STREAM(s, raw, rawLen, data->diag, "Box[moov]");
Joe Dragoae7e2c32019-07-18 15:22:25 -07002621
Joe Drago345aaa12019-09-25 13:42:12 -07002622 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07002623 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002624 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002625
2626 if (!memcmp(header.type, "trak", 4)) {
Wan-Teh Chang53121812021-05-28 18:02:59 -07002627 CHECK(avifParseTrackBox(data, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002628 }
2629
Joe Drago345aaa12019-09-25 13:42:12 -07002630 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07002631 }
2632 return AVIF_TRUE;
2633}
2634
Joe Drago4d5f4a42021-04-28 13:11:45 -07002635static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
Joe Drago8f7a3002019-02-07 19:35:37 -08002636{
Joe Drago618ed5f2021-05-04 12:13:24 -07002637 BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]");
Joe Drago8f7a3002019-02-07 19:35:37 -08002638
Joe Drago345aaa12019-09-25 13:42:12 -07002639 CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
2640 CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
Joe Drago8f7a3002019-02-07 19:35:37 -08002641
Joe Drago345aaa12019-09-25 13:42:12 -07002642 size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
Joe Drago8f7a3002019-02-07 19:35:37 -08002643 if ((compatibleBrandsBytes % 4) != 0) {
Joe Drago48867292021-05-04 13:23:08 -07002644 avifDiagnosticsPrintf(diag, "Box[ftyp] contains a compatible brands section that isn't divisible by 4 [%zu]", compatibleBrandsBytes);
Joe Drago8f7a3002019-02-07 19:35:37 -08002645 return AVIF_FALSE;
2646 }
Wan-Teh Chang6da0a882020-07-01 12:19:31 -07002647 ftyp->compatibleBrands = avifROStreamCurrent(&s);
2648 CHECK(avifROStreamSkip(&s, compatibleBrandsBytes));
Joe Drago7e37b972019-07-24 12:44:47 -07002649 ftyp->compatibleBrandsCount = (int)compatibleBrandsBytes / 4;
Joe Drago8f7a3002019-02-07 19:35:37 -08002650
2651 return AVIF_TRUE;
2652}
2653
Joe Dragobb39aab2020-11-03 19:23:40 -08002654static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand);
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002655static avifBool avifFileTypeIsCompatible(avifFileType * ftyp);
2656
Joe Dragobe4cbb92020-09-21 12:14:05 -07002657static avifResult avifParse(avifDecoder * decoder)
Joe Drago8f7a3002019-02-07 19:35:37 -08002658{
Joe Drago9aa931f2020-09-24 13:10:11 -07002659 // Note: this top-level function is the only avifParse*() function that returns avifResult instead of avifBool.
2660 // Be sure to use CHECKERR() in this function with an explicit error result instead of simply using CHECK().
2661
Joe Dragobe4cbb92020-09-21 12:14:05 -07002662 avifResult readResult;
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002663 uint64_t parseOffset = 0;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002664 avifDecoderData * data = decoder->data;
Joe Dragobb39aab2020-11-03 19:23:40 -08002665 avifBool ftypSeen = AVIF_FALSE;
2666 avifBool metaSeen = AVIF_FALSE;
2667 avifBool moovSeen = AVIF_FALSE;
2668 avifBool needsMeta = AVIF_FALSE;
2669 avifBool needsMoov = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08002670
Joe Dragobe4cbb92020-09-21 12:14:05 -07002671 for (;;) {
2672 // Read just enough to get the next box header (a max of 32 bytes)
2673 avifROData headerContents;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002674 if ((decoder->io->sizeHint > 0) && (parseOffset > decoder->io->sizeHint)) {
2675 return AVIF_RESULT_BMFF_PARSE_FAILED;
2676 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002677 readResult = decoder->io->read(decoder->io, 0, parseOffset, 32, &headerContents);
2678 if (readResult != AVIF_RESULT_OK) {
2679 return readResult;
2680 }
2681 if (!headerContents.size) {
2682 // If we got AVIF_RESULT_OK from the reader but received 0 bytes,
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07002683 // we've reached the end of the file with no errors. Hooray!
Joe Dragobe4cbb92020-09-21 12:14:05 -07002684 break;
Joe Drago8f7a3002019-02-07 19:35:37 -08002685 }
2686
Joe Dragobe4cbb92020-09-21 12:14:05 -07002687 // Parse the header, and find out how many bytes it actually was
Joe Drago618ed5f2021-05-04 12:13:24 -07002688 BEGIN_STREAM(headerStream, headerContents.data, headerContents.size, &decoder->diag, "File-level box header");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002689 avifBoxHeader header;
Joe Drago468ded82020-09-24 12:52:51 -07002690 CHECKERR(avifROStreamReadBoxHeaderPartial(&headerStream, &header), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002691 parseOffset += headerStream.offset;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002692 assert((decoder->io->sizeHint == 0) || (parseOffset <= decoder->io->sizeHint));
Joe Dragobe4cbb92020-09-21 12:14:05 -07002693
2694 // Try to get the remainder of the box, if necessary
2695 avifROData boxContents = AVIF_DATA_EMPTY;
2696
2697 // TODO: reorg this code to only do these memcmps once each
Wan-Teh Changb309e982021-02-20 16:59:36 -08002698 if (!memcmp(header.type, "ftyp", 4) || !memcmp(header.type, "meta", 4) || !memcmp(header.type, "moov", 4)) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07002699 readResult = decoder->io->read(decoder->io, 0, parseOffset, header.size, &boxContents);
2700 if (readResult != AVIF_RESULT_OK) {
2701 return readResult;
2702 }
2703 if (boxContents.size != header.size) {
2704 // A truncated box, bail out
Joe Dragofe5d5e42020-09-24 13:07:58 -07002705 return AVIF_RESULT_TRUNCATED_DATA;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002706 }
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002707 } else if (header.size > (UINT64_MAX - parseOffset)) {
2708 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002709 }
Wan-Teh Chang3b8b81c2020-09-28 10:35:08 -07002710 parseOffset += header.size;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002711
2712 if (!memcmp(header.type, "ftyp", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002713 CHECKERR(!ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002714 avifFileType ftyp;
Joe Drago4d5f4a42021-04-28 13:11:45 -07002715 CHECKERR(avifParseFileTypeBox(&ftyp, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002716 if (!avifFileTypeIsCompatible(&ftyp)) {
Wan-Teh Chang6fc17582020-09-24 15:16:37 -07002717 return AVIF_RESULT_INVALID_FTYP;
2718 }
Joe Dragobb39aab2020-11-03 19:23:40 -08002719 ftypSeen = AVIF_TRUE;
2720 needsMeta = avifFileTypeHasBrand(&ftyp, "avif");
2721 needsMoov = avifFileTypeHasBrand(&ftyp, "avis");
Joe Dragobe4cbb92020-09-21 12:14:05 -07002722 } else if (!memcmp(header.type, "meta", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002723 CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Drago4d5f4a42021-04-28 13:11:45 -07002724 CHECKERR(avifParseMetaBox(data->meta, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002725 metaSeen = AVIF_TRUE;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002726 } else if (!memcmp(header.type, "moov", 4)) {
Joe Dragobb39aab2020-11-03 19:23:40 -08002727 CHECKERR(!moovSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
Wan-Teh Chang53121812021-05-28 18:02:59 -07002728 CHECKERR(avifParseMoovBox(data, boxContents.data, boxContents.size), AVIF_RESULT_BMFF_PARSE_FAILED);
Joe Dragobb39aab2020-11-03 19:23:40 -08002729 moovSeen = AVIF_TRUE;
2730 }
2731
2732 // See if there is enough information to consider Parse() a success and early-out:
2733 // * If the brand 'avif' is present, require a meta box
2734 // * If the brand 'avis' is present, require a moov box
2735 if (ftypSeen && (!needsMeta || metaSeen) && (!needsMoov || moovSeen)) {
2736 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002737 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002738 }
Joe Drago029375a2021-04-28 11:57:56 -07002739 if (!ftypSeen) {
2740 return AVIF_RESULT_INVALID_FTYP;
2741 }
2742 if ((needsMeta && !metaSeen) || (needsMoov && !moovSeen)) {
2743 return AVIF_RESULT_TRUNCATED_DATA;
2744 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002745 return AVIF_RESULT_OK;
Joe Drago8f7a3002019-02-07 19:35:37 -08002746}
2747
2748// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08002749
Joe Dragobb39aab2020-11-03 19:23:40 -08002750static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand)
Joe Drago7e37b972019-07-24 12:44:47 -07002751{
Joe Dragobb39aab2020-11-03 19:23:40 -08002752 if (!memcmp(ftyp->majorBrand, brand, 4)) {
2753 return AVIF_TRUE;
2754 }
2755
2756 for (int compatibleBrandIndex = 0; compatibleBrandIndex < ftyp->compatibleBrandsCount; ++compatibleBrandIndex) {
2757 const uint8_t * compatibleBrand = &ftyp->compatibleBrands[4 * compatibleBrandIndex];
2758 if (!memcmp(compatibleBrand, brand, 4)) {
2759 return AVIF_TRUE;
Joe Drago7e37b972019-07-24 12:44:47 -07002760 }
2761 }
Joe Dragobb39aab2020-11-03 19:23:40 -08002762 return AVIF_FALSE;
2763}
2764
2765static avifBool avifFileTypeIsCompatible(avifFileType * ftyp)
2766{
2767 return avifFileTypeHasBrand(ftyp, "avif") || avifFileTypeHasBrand(ftyp, "avis");
Joe Drago7e37b972019-07-24 12:44:47 -07002768}
2769
Wan-Teh Change184dc12020-05-11 12:47:21 -07002770avifBool avifPeekCompatibleFileType(const avifROData * input)
Joe Drago7e37b972019-07-24 12:44:47 -07002771{
Joe Drago618ed5f2021-05-04 12:13:24 -07002772 BEGIN_STREAM(s, input->data, input->size, NULL, NULL);
Joe Drago7e37b972019-07-24 12:44:47 -07002773
2774 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07002775 CHECK(avifROStreamReadBoxHeader(&s, &header));
wantehchangbc35a5f2020-08-12 15:27:39 -07002776 if (memcmp(header.type, "ftyp", 4)) {
Joe Drago7e37b972019-07-24 12:44:47 -07002777 return AVIF_FALSE;
2778 }
2779
2780 avifFileType ftyp;
2781 memset(&ftyp, 0, sizeof(avifFileType));
Joe Drago4d5f4a42021-04-28 13:11:45 -07002782 avifBool parsed = avifParseFileTypeBox(&ftyp, avifROStreamCurrent(&s), header.size, NULL);
Joe Drago7e37b972019-07-24 12:44:47 -07002783 if (!parsed) {
2784 return AVIF_FALSE;
2785 }
2786 return avifFileTypeIsCompatible(&ftyp);
2787}
2788
2789// ---------------------------------------------------------------------------
2790
Joe Drago0b05eee2019-06-12 13:24:39 -07002791avifDecoder * avifDecoderCreate(void)
2792{
2793 avifDecoder * decoder = (avifDecoder *)avifAlloc(sizeof(avifDecoder));
2794 memset(decoder, 0, sizeof(avifDecoder));
Joe Dragoede5c202020-11-11 09:42:57 -08002795 decoder->maxThreads = 1;
Joe Dragod2340b42021-03-14 13:20:02 -07002796 decoder->imageCountLimit = AVIF_DEFAULT_IMAGE_COUNT_LIMIT;
Joe Drago4d102ed2021-05-07 14:27:19 -07002797 decoder->strictFlags = AVIF_STRICT_ENABLED;
Joe Drago0b05eee2019-06-12 13:24:39 -07002798 return decoder;
2799}
2800
Joe Drago46ea0582019-07-22 15:55:47 -07002801static void avifDecoderCleanup(avifDecoder * decoder)
2802{
2803 if (decoder->data) {
Joe Drago800b47f2020-03-18 16:22:37 -07002804 avifDecoderDataDestroy(decoder->data);
Joe Drago46ea0582019-07-22 15:55:47 -07002805 decoder->data = NULL;
2806 }
2807
2808 if (decoder->image) {
2809 avifImageDestroy(decoder->image);
2810 decoder->image = NULL;
2811 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07002812 avifDiagnosticsClearError(&decoder->diag);
Joe Drago46ea0582019-07-22 15:55:47 -07002813}
2814
Joe Drago0b05eee2019-06-12 13:24:39 -07002815void avifDecoderDestroy(avifDecoder * decoder)
2816{
Joe Drago46ea0582019-07-22 15:55:47 -07002817 avifDecoderCleanup(decoder);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002818 avifIODestroy(decoder->io);
Joe Drago0b05eee2019-06-12 13:24:39 -07002819 avifFree(decoder);
2820}
2821
Joe Drago46ea0582019-07-22 15:55:47 -07002822avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source)
Joe Drago444f0512019-01-23 17:03:24 -08002823{
Joe Drago46ea0582019-07-22 15:55:47 -07002824 decoder->requestedSource = source;
2825 return avifDecoderReset(decoder);
2826}
Joe Drago33f1d362019-02-13 16:46:22 -08002827
Wan-Teh Change67f9362020-10-12 16:07:57 -07002828void avifDecoderSetIO(avifDecoder * decoder, avifIO * io)
Joe Drago46ea0582019-07-22 15:55:47 -07002829{
Joe Dragobe4cbb92020-09-21 12:14:05 -07002830 avifIODestroy(decoder->io);
2831 decoder->io = io;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002832}
2833
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002834avifResult avifDecoderSetIOMemory(avifDecoder * decoder, const uint8_t * data, size_t size)
Joe Dragobe4cbb92020-09-21 12:14:05 -07002835{
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002836 avifIO * io = avifIOCreateMemoryReader(data, size);
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002837 assert(io);
Wan-Teh Change67f9362020-10-12 16:07:57 -07002838 avifDecoderSetIO(decoder, io);
2839 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002840}
2841
Joe Dragobe4cbb92020-09-21 12:14:05 -07002842avifResult avifDecoderSetIOFile(avifDecoder * decoder, const char * filename)
2843{
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002844 avifIO * io = avifIOCreateFileReader(filename);
2845 if (!io) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002846 return AVIF_RESULT_IO_ERROR;
Wan-Teh Chang7de44452020-10-08 11:43:18 -07002847 }
Wan-Teh Change67f9362020-10-12 16:07:57 -07002848 avifDecoderSetIO(decoder, io);
2849 return AVIF_RESULT_OK;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002850}
2851
Joe Drago4bcdfde2020-11-13 17:50:55 -08002852// 0-byte extents are ignored/overwritten during the merge, as they are the signal from helper
2853// functions that no extent was necessary for this given sample. If both provided extents are
2854// >0 bytes, this will set dst to be an extent that bounds both supplied extents.
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002855static avifResult avifExtentMerge(avifExtent * dst, const avifExtent * src)
Joe Drago4bcdfde2020-11-13 17:50:55 -08002856{
2857 if (!dst->size) {
2858 memcpy(dst, src, sizeof(avifExtent));
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002859 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08002860 }
2861 if (!src->size) {
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002862 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08002863 }
2864
2865 const uint64_t minExtent1 = dst->offset;
2866 const uint64_t maxExtent1 = dst->offset + dst->size;
2867 const uint64_t minExtent2 = src->offset;
2868 const uint64_t maxExtent2 = src->offset + src->size;
2869 dst->offset = AVIF_MIN(minExtent1, minExtent2);
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002870 const uint64_t extentLength = AVIF_MAX(maxExtent1, maxExtent2) - dst->offset;
2871 if (extentLength > SIZE_MAX) {
2872 return AVIF_RESULT_BMFF_PARSE_FAILED;
2873 }
2874 dst->size = (size_t)extentLength;
2875 return AVIF_RESULT_OK;
Joe Drago4bcdfde2020-11-13 17:50:55 -08002876}
2877
Joe Drago93d5bf92020-11-17 14:31:57 -08002878avifResult avifDecoderNthImageMaxExtent(const avifDecoder * decoder, uint32_t frameIndex, avifExtent * outExtent)
Joe Drago4bcdfde2020-11-13 17:50:55 -08002879{
2880 if (!decoder->data) {
2881 // Nothing has been parsed yet
2882 return AVIF_RESULT_NO_CONTENT;
2883 }
2884
2885 memset(outExtent, 0, sizeof(avifExtent));
2886
Joe Drago93d5bf92020-11-17 14:31:57 -08002887 uint32_t startFrameIndex = avifDecoderNearestKeyframe(decoder, frameIndex);
Joe Drago4bcdfde2020-11-13 17:50:55 -08002888 uint32_t endFrameIndex = frameIndex;
2889 for (uint32_t currentFrameIndex = startFrameIndex; currentFrameIndex <= endFrameIndex; ++currentFrameIndex) {
2890 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
2891 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
2892 if (currentFrameIndex >= tile->input->samples.count) {
2893 return AVIF_RESULT_NO_IMAGES_REMAINING;
2894 }
2895
2896 avifDecodeSample * sample = &tile->input->samples.sample[currentFrameIndex];
2897 avifExtent sampleExtent;
2898 if (sample->itemID) {
2899 // The data comes from an item. Let avifDecoderItemMaxExtent() do the heavy lifting.
2900
2901 avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
2902 avifResult maxExtentResult = avifDecoderItemMaxExtent(item, &sampleExtent);
2903 if (maxExtentResult != AVIF_RESULT_OK) {
2904 return maxExtentResult;
2905 }
2906 } else {
2907 // The data likely comes from a sample table. Use the sample position directly.
2908
2909 sampleExtent.offset = sample->offset;
2910 sampleExtent.size = sample->size;
2911 }
2912
2913 if (sampleExtent.size > UINT64_MAX - sampleExtent.offset) {
2914 return AVIF_RESULT_BMFF_PARSE_FAILED;
2915 }
2916
Wan-Teh Changd69958e2020-11-17 12:14:27 -08002917 avifResult extentMergeResult = avifExtentMerge(outExtent, &sampleExtent);
2918 if (extentMergeResult != AVIF_RESULT_OK) {
2919 return extentMergeResult;
2920 }
Joe Drago4bcdfde2020-11-13 17:50:55 -08002921 }
2922 }
2923 return AVIF_RESULT_OK;
2924}
2925
Joe Dragobe4cbb92020-09-21 12:14:05 -07002926static avifResult avifDecoderPrepareSample(avifDecoder * decoder, avifDecodeSample * sample, size_t partialByteCount)
2927{
2928 if (!sample->data.size || sample->partialData) {
2929 // This sample hasn't been read from IO or had its extents fully merged yet.
2930
Joe Dragobffba3b2021-05-26 15:46:10 -07002931 size_t bytesToRead = sample->size;
2932 if (partialByteCount && (bytesToRead > partialByteCount)) {
2933 bytesToRead = partialByteCount;
2934 }
2935
Joe Dragobe4cbb92020-09-21 12:14:05 -07002936 if (sample->itemID) {
Wan-Teh Chang4548b162020-11-06 11:48:25 -08002937 // The data comes from an item. Let avifDecoderItemRead() do the heavy lifting.
Joe Dragobe4cbb92020-09-21 12:14:05 -07002938
2939 avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
2940 avifROData itemContents;
Joe Dragobffba3b2021-05-26 15:46:10 -07002941 avifResult readResult = avifDecoderItemRead(item, decoder->io, &itemContents, sample->offset, bytesToRead, &decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07002942 if (readResult != AVIF_RESULT_OK) {
2943 return readResult;
2944 }
2945
Wan-Teh Chang4548b162020-11-06 11:48:25 -08002946 // avifDecoderItemRead is guaranteed to already be persisted by either the underlying IO
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07002947 // or by mergedExtents; just reuse the buffer here.
Joe Dragobe4cbb92020-09-21 12:14:05 -07002948 memcpy(&sample->data, &itemContents, sizeof(avifROData));
2949 sample->ownsData = AVIF_FALSE;
2950 sample->partialData = item->partialMergedExtents;
2951 } else {
2952 // The data likely comes from a sample table. Pull the sample and make a copy if necessary.
2953
Joe Dragobe4cbb92020-09-21 12:14:05 -07002954 avifROData sampleContents;
Wan-Teh Changb856dc22020-09-28 13:00:56 -07002955 if ((decoder->io->sizeHint > 0) && (sample->offset > decoder->io->sizeHint)) {
2956 return AVIF_RESULT_BMFF_PARSE_FAILED;
2957 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07002958 avifResult readResult = decoder->io->read(decoder->io, 0, sample->offset, bytesToRead, &sampleContents);
2959 if (readResult != AVIF_RESULT_OK) {
2960 return readResult;
2961 }
2962 if (sampleContents.size != bytesToRead) {
2963 return AVIF_RESULT_TRUNCATED_DATA;
2964 }
2965
2966 sample->ownsData = !decoder->io->persistent;
2967 sample->partialData = (bytesToRead != sample->size);
2968 if (decoder->io->persistent) {
2969 memcpy(&sample->data, &sampleContents, sizeof(avifROData));
2970 } else {
2971 avifRWDataSet((avifRWData *)&sample->data, sampleContents.data, sampleContents.size);
2972 }
2973 }
2974 }
2975 return AVIF_RESULT_OK;
2976}
2977
2978avifResult avifDecoderParse(avifDecoder * decoder)
2979{
Joe Drago4d5f4a42021-04-28 13:11:45 -07002980 avifDiagnosticsClearError(&decoder->diag);
2981
Joe Dragobe4cbb92020-09-21 12:14:05 -07002982 if (!decoder->io || !decoder->io->read) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07002983 return AVIF_RESULT_IO_NOT_SET;
Joe Dragobe4cbb92020-09-21 12:14:05 -07002984 }
2985
Joe Drago46ea0582019-07-22 15:55:47 -07002986 // Cleanup anything lingering in the decoder
2987 avifDecoderCleanup(decoder);
2988
Joe Drago444f0512019-01-23 17:03:24 -08002989 // -----------------------------------------------------------------------
2990 // Parse BMFF boxes
2991
Joe Drago800b47f2020-03-18 16:22:37 -07002992 decoder->data = avifDecoderDataCreate();
Joe Drago4d5f4a42021-04-28 13:11:45 -07002993 decoder->data->diag = &decoder->diag;
Joe Drago46ea0582019-07-22 15:55:47 -07002994
Joe Dragobe4cbb92020-09-21 12:14:05 -07002995 avifResult parseResult = avifParse(decoder);
2996 if (parseResult != AVIF_RESULT_OK) {
2997 return parseResult;
Joe Drago444f0512019-01-23 17:03:24 -08002998 }
2999
Joe Drago46104d62021-05-27 18:17:29 -07003000 // Walk the decoded items (if any) and harvest ispe
3001 avifDecoderData * data = decoder->data;
3002 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
3003 avifDecoderItem * item = &data->meta->items.item[itemIndex];
3004 if (!item->size) {
3005 continue;
3006 }
3007 if (item->hasUnsupportedEssentialProperty) {
3008 // An essential property isn't supported by libavif; ignore the item.
3009 continue;
3010 }
3011 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
3012 if (memcmp(item->type, "av01", 4) && !isGrid) {
3013 // probably exif or some other data
3014 continue;
3015 }
3016
3017 const avifProperty * ispeProp = avifPropertyArrayFind(&item->properties, "ispe");
3018 if (ispeProp) {
3019 item->width = ispeProp->u.ispe.width;
3020 item->height = ispeProp->u.ispe.height;
3021
3022 if ((item->width == 0) || (item->height == 0) || (item->width > (AVIF_MAX_IMAGE_SIZE / item->height))) {
3023 avifDiagnosticsPrintf(data->diag, "Item ID [%u] has an invalid size [%ux%u]", item->id, item->width, item->height);
3024 return AVIF_RESULT_BMFF_PARSE_FAILED;
3025 }
3026 } else {
3027 avifDiagnosticsPrintf(data->diag, "Item ID [%u] is missing a mandatory ispe property", item->id);
3028 return AVIF_RESULT_BMFF_PARSE_FAILED;
3029 }
3030 }
Joe Drago46ea0582019-07-22 15:55:47 -07003031 return avifDecoderReset(decoder);
3032}
3033
Joe Dragobe4cbb92020-09-21 12:14:05 -07003034static avifCodec * avifCodecCreateInternal(avifCodecChoice choice)
Joe Drago46ea0582019-07-22 15:55:47 -07003035{
Joe Dragobe4cbb92020-09-21 12:14:05 -07003036 return avifCodecCreate(choice, AVIF_CODEC_FLAG_CAN_DECODE);
Joe Drago46ea0582019-07-22 15:55:47 -07003037}
3038
Joe Drago22c1ad92019-09-26 12:46:50 -07003039static avifResult avifDecoderFlush(avifDecoder * decoder)
3040{
Joe Drago800b47f2020-03-18 16:22:37 -07003041 avifDecoderDataResetCodec(decoder->data);
Joe Drago22c1ad92019-09-26 12:46:50 -07003042
Joe Drago060d5342020-03-03 10:53:49 -08003043 for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) {
3044 avifTile * tile = &decoder->data->tiles.tile[i];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003045 tile->codec = avifCodecCreateInternal(decoder->codecChoice);
Joe Drago060d5342020-03-03 10:53:49 -08003046 if (!tile->codec) {
Joe Drago53355352019-10-28 19:04:51 -07003047 return AVIF_RESULT_NO_CODEC_AVAILABLE;
3048 }
Joe Dragobfc5aca2021-05-17 12:00:39 -07003049 tile->codec->diag = &decoder->diag;
Joe Dragoe79bc372021-06-09 17:54:08 -07003050 tile->codec->operatingPoint = tile->operatingPoint;
Joe Dragobffba3b2021-05-26 15:46:10 -07003051 tile->codec->allLayers = tile->input->allLayers;
Joe Drago22c1ad92019-09-26 12:46:50 -07003052 }
3053 return AVIF_RESULT_OK;
3054}
3055
Joe Drago46ea0582019-07-22 15:55:47 -07003056avifResult avifDecoderReset(avifDecoder * decoder)
3057{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003058 avifDiagnosticsClearError(&decoder->diag);
3059
Joe Drago800b47f2020-03-18 16:22:37 -07003060 avifDecoderData * data = decoder->data;
Joe Drago46ea0582019-07-22 15:55:47 -07003061 if (!data) {
3062 // Nothing to reset.
3063 return AVIF_RESULT_OK;
3064 }
3065
Joe Drago060d5342020-03-03 10:53:49 -08003066 memset(&data->colorGrid, 0, sizeof(data->colorGrid));
3067 memset(&data->alphaGrid, 0, sizeof(data->alphaGrid));
Joe Drago800b47f2020-03-18 16:22:37 -07003068 avifDecoderDataClearTiles(data);
Joe Drago89f0cc82020-03-09 16:13:27 -07003069
3070 // Prepare / cleanup decoded image state
Joe Dragoa0da4a42020-05-08 14:27:40 -07003071 if (decoder->image) {
3072 avifImageDestroy(decoder->image);
Joe Drago8f7a3002019-02-07 19:35:37 -08003073 }
Joe Dragoa0da4a42020-05-08 14:27:40 -07003074 decoder->image = avifImageCreateEmpty();
Joe Dragobffba3b2021-05-26 15:46:10 -07003075 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_UNAVAILABLE;
Joe Dragoa0da4a42020-05-08 14:27:40 -07003076 data->cicpSet = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08003077
Joe Drago70cbf602019-07-24 15:30:55 -07003078 memset(&decoder->ioStats, 0, sizeof(decoder->ioStats));
3079
Joe Drago444f0512019-01-23 17:03:24 -08003080 // -----------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -07003081 // Build decode input
Joe Drago444f0512019-01-23 17:03:24 -08003082
Joe Drago46ea0582019-07-22 15:55:47 -07003083 data->sourceSampleTable = NULL; // Reset
3084 if (decoder->requestedSource == AVIF_DECODER_SOURCE_AUTO) {
3085 if (data->tracks.count > 0) {
3086 data->source = AVIF_DECODER_SOURCE_TRACKS;
3087 } else {
3088 data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;
Joe Drago76370232019-07-16 11:00:52 -07003089 }
Joe Drago46ea0582019-07-22 15:55:47 -07003090 } else {
3091 data->source = decoder->requestedSource;
Joe Drago76370232019-07-16 11:00:52 -07003092 }
3093
Joe Dragoa72da5b2020-06-15 19:40:17 -07003094 const avifPropertyArray * colorProperties = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07003095 if (data->source == AVIF_DECODER_SOURCE_TRACKS) {
3096 avifTrack * colorTrack = NULL;
3097 avifTrack * alphaTrack = NULL;
3098
3099 // Find primary track - this probably needs some better detection
3100 uint32_t colorTrackIndex = 0;
wantehchangb207b4d2020-08-11 17:50:22 -07003101 for (; colorTrackIndex < data->tracks.count; ++colorTrackIndex) {
3102 avifTrack * track = &data->tracks.track[colorTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07003103 if (!track->sampleTable) {
3104 continue;
3105 }
Joe Dragoba1eb492020-06-22 17:05:04 -07003106 if (!track->id) { // trak box might be missing a tkhd box inside, skip it
Joe Drago4a25c192020-06-03 16:29:58 -07003107 continue;
3108 }
Joe Drago46ea0582019-07-22 15:55:47 -07003109 if (!track->sampleTable->chunks.count) {
3110 continue;
3111 }
Joe Drago2c0924c2019-09-26 17:41:01 -07003112 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
3113 continue;
3114 }
Joe Drago46ea0582019-07-22 15:55:47 -07003115 if (track->auxForID != 0) {
3116 continue;
3117 }
3118
3119 // Found one!
Joe Drago444f0512019-01-23 17:03:24 -08003120 break;
3121 }
wantehchangb207b4d2020-08-11 17:50:22 -07003122 if (colorTrackIndex == data->tracks.count) {
Joe Drago618ed5f2021-05-04 12:13:24 -07003123 avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track");
Joe Drago46ea0582019-07-22 15:55:47 -07003124 return AVIF_RESULT_NO_CONTENT;
Joe Drago444f0512019-01-23 17:03:24 -08003125 }
wantehchangb207b4d2020-08-11 17:50:22 -07003126 colorTrack = &data->tracks.track[colorTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07003127
Joe Dragoa72da5b2020-06-15 19:40:17 -07003128 colorProperties = avifSampleTableGetProperties(colorTrack->sampleTable);
3129 if (!colorProperties) {
Joe Drago618ed5f2021-05-04 12:13:24 -07003130 avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track's color properties");
Joe Dragoa72da5b2020-06-15 19:40:17 -07003131 return AVIF_RESULT_BMFF_PARSE_FAILED;
3132 }
3133
3134 // Find Exif and/or XMP metadata, if any
3135 if (colorTrack->meta) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003136 // See the comment above avifDecoderFindMetadata() for the explanation of using 0 here
3137 avifResult findResult = avifDecoderFindMetadata(decoder, colorTrack->meta, decoder->image, 0);
3138 if (findResult != AVIF_RESULT_OK) {
3139 return findResult;
Joe Dragoa72da5b2020-06-15 19:40:17 -07003140 }
3141 }
3142
Joe Drago46ea0582019-07-22 15:55:47 -07003143 uint32_t alphaTrackIndex = 0;
wantehchangb207b4d2020-08-11 17:50:22 -07003144 for (; alphaTrackIndex < data->tracks.count; ++alphaTrackIndex) {
3145 avifTrack * track = &data->tracks.track[alphaTrackIndex];
Joe Drago46ea0582019-07-22 15:55:47 -07003146 if (!track->sampleTable) {
3147 continue;
3148 }
Joe Drago4a25c192020-06-03 16:29:58 -07003149 if (!track->id) {
3150 continue;
3151 }
Joe Drago46ea0582019-07-22 15:55:47 -07003152 if (!track->sampleTable->chunks.count) {
3153 continue;
3154 }
Joe Drago2c0924c2019-09-26 17:41:01 -07003155 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
3156 continue;
3157 }
Joe Drago46ea0582019-07-22 15:55:47 -07003158 if (track->auxForID == colorTrack->id) {
3159 // Found it!
3160 break;
3161 }
3162 }
wantehchangb207b4d2020-08-11 17:50:22 -07003163 if (alphaTrackIndex != data->tracks.count) {
3164 alphaTrack = &data->tracks.track[alphaTrackIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -08003165 }
Joe Drago444f0512019-01-23 17:03:24 -08003166
Joe Drago46104d62021-05-27 18:17:29 -07003167 avifTile * colorTile = avifDecoderDataCreateTile(data, colorTrack->width, colorTrack->height, 0); // No way to set operating point via tracks
Joe Dragobffba3b2021-05-26 15:46:10 -07003168 if (!avifCodecDecodeInputFillFromSampleTable(colorTile->input,
3169 colorTrack->sampleTable,
3170 decoder->imageCountLimit,
3171 decoder->io->sizeHint,
3172 data->diag)) {
Joe Drago46ea0582019-07-22 15:55:47 -07003173 return AVIF_RESULT_BMFF_PARSE_FAILED;
3174 }
wantehchangb207b4d2020-08-11 17:50:22 -07003175 data->colorTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07003176
3177 if (alphaTrack) {
Joe Drago46104d62021-05-27 18:17:29 -07003178 avifTile * alphaTile = avifDecoderDataCreateTile(data, alphaTrack->width, alphaTrack->height, 0); // No way to set operating point via tracks
Joe Dragobffba3b2021-05-26 15:46:10 -07003179 if (!avifCodecDecodeInputFillFromSampleTable(alphaTile->input,
3180 alphaTrack->sampleTable,
3181 decoder->imageCountLimit,
3182 decoder->io->sizeHint,
3183 data->diag)) {
Joe Drago46ea0582019-07-22 15:55:47 -07003184 return AVIF_RESULT_BMFF_PARSE_FAILED;
3185 }
Joe Drago060d5342020-03-03 10:53:49 -08003186 alphaTile->input->alpha = AVIF_TRUE;
wantehchangb207b4d2020-08-11 17:50:22 -07003187 data->alphaTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07003188 }
3189
3190 // Stash off sample table for future timing information
3191 data->sourceSampleTable = colorTrack->sampleTable;
3192
3193 // Image sequence timing
3194 decoder->imageIndex = -1;
Joe Drago060d5342020-03-03 10:53:49 -08003195 decoder->imageCount = colorTile->input->samples.count;
Joe Drago46ea0582019-07-22 15:55:47 -07003196 decoder->timescale = colorTrack->mediaTimescale;
3197 decoder->durationInTimescales = colorTrack->mediaDuration;
3198 if (colorTrack->mediaTimescale) {
3199 decoder->duration = (double)decoder->durationInTimescales / (double)colorTrack->mediaTimescale;
3200 } else {
3201 decoder->duration = 0;
3202 }
3203 memset(&decoder->imageTiming, 0, sizeof(decoder->imageTiming)); // to be set in avifDecoderNextImage()
Joe Drago41700852019-09-26 17:01:43 -07003204
Joe Dragoc554f5f2020-06-09 18:59:51 -07003205 decoder->image->width = colorTrack->width;
3206 decoder->image->height = colorTrack->height;
Joe Dragoc554f5f2020-06-09 18:59:51 -07003207 decoder->alphaPresent = (alphaTrack != NULL);
Joe Dragoceb2fa02021-01-29 18:14:55 -08003208 decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorTrack->premByID == alphaTrack->id);
Joe Drago46ea0582019-07-22 15:55:47 -07003209 } else {
3210 // Create from items
3211
Joe Dragobe4cbb92020-09-21 12:14:05 -07003212 avifDecoderItem * colorItem = NULL;
3213 avifDecoderItem * alphaItem = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07003214
Joe Drago51d11142021-03-31 07:46:30 -07003215 if (data->meta->primaryItemID == 0) {
3216 // A primary item is required
Joe Drago618ed5f2021-05-04 12:13:24 -07003217 avifDiagnosticsPrintf(&decoder->diag, "Primary item not specified");
Joe Drago51d11142021-03-31 07:46:30 -07003218 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3219 }
3220
Joe Dragof6a42272019-11-21 15:21:41 -08003221 // Find the colorOBU (primary) item
Joe Drago9f2b87b2020-06-03 19:36:38 -07003222 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
3223 avifDecoderItem * item = &data->meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07003224 if (!item->size) {
3225 continue;
Joe Drago8f7a3002019-02-07 19:35:37 -08003226 }
Joe Drago3320e5f2020-04-21 17:36:27 -07003227 if (item->hasUnsupportedEssentialProperty) {
3228 // An essential property isn't supported by libavif; ignore the item.
3229 continue;
3230 }
Joe Drago951a0022020-03-09 16:19:44 -07003231 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08003232 if (memcmp(item->type, "av01", 4) && !isGrid) {
Joe Drago8f7a3002019-02-07 19:35:37 -08003233 // probably exif or some other data
3234 continue;
3235 }
3236 if (item->thumbnailForID != 0) {
3237 // It's a thumbnail, skip it
3238 continue;
3239 }
Joe Drago618ed5f2021-05-04 12:13:24 -07003240 if (item->id != data->meta->primaryItemID) {
3241 // This is not the primary item, skip it
Joe Dragof6a42272019-11-21 15:21:41 -08003242 continue;
3243 }
Joe Drago8f7a3002019-02-07 19:35:37 -08003244
Joe Drago060d5342020-03-03 10:53:49 -08003245 if (isGrid) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003246 avifROData readData;
Joe Dragobffba3b2021-05-26 15:46:10 -07003247 avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, 0, data->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003248 if (readResult != AVIF_RESULT_OK) {
3249 return readResult;
3250 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07003251 if (!avifParseImageGridBox(&data->colorGrid, readData.data, readData.size, data->diag)) {
Joe Drago060d5342020-03-03 10:53:49 -08003252 return AVIF_RESULT_INVALID_IMAGE_GRID;
3253 }
Joe Drago060d5342020-03-03 10:53:49 -08003254 }
3255
Joe Dragobe4cbb92020-09-21 12:14:05 -07003256 colorItem = item;
Joe Drago46ea0582019-07-22 15:55:47 -07003257 break;
3258 }
3259
Joe Dragobe4cbb92020-09-21 12:14:05 -07003260 if (!colorItem) {
Joe Drago618ed5f2021-05-04 12:13:24 -07003261 avifDiagnosticsPrintf(&decoder->diag, "Primary item not found");
Joe Drago060d5342020-03-03 10:53:49 -08003262 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3263 }
Joe Dragobe4cbb92020-09-21 12:14:05 -07003264 colorProperties = &colorItem->properties;
Joe Drago46ea0582019-07-22 15:55:47 -07003265
Joe Drago060d5342020-03-03 10:53:49 -08003266 // Find the alphaOBU item, if any
Joe Drago9f2b87b2020-06-03 19:36:38 -07003267 for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) {
3268 avifDecoderItem * item = &data->meta->items.item[itemIndex];
Joe Dragoba1eb492020-06-22 17:05:04 -07003269 if (!item->size) {
3270 continue;
Joe Drago060d5342020-03-03 10:53:49 -08003271 }
Joe Drago3320e5f2020-04-21 17:36:27 -07003272 if (item->hasUnsupportedEssentialProperty) {
3273 // An essential property isn't supported by libavif; ignore the item.
3274 continue;
3275 }
Joe Drago951a0022020-03-09 16:19:44 -07003276 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08003277 if (memcmp(item->type, "av01", 4) && !isGrid) {
3278 // probably exif or some other data
3279 continue;
3280 }
Joe Dragof6a42272019-11-21 15:21:41 -08003281
Joe Dragobe4cbb92020-09-21 12:14:05 -07003282 // Is this an alpha auxiliary item of whatever we chose for colorItem?
Joe Dragoa72da5b2020-06-15 19:40:17 -07003283 const avifProperty * auxCProp = avifPropertyArrayFind(&item->properties, "auxC");
Joe Dragobe4cbb92020-09-21 12:14:05 -07003284 if (auxCProp && isAlphaURN(auxCProp->u.auxC.auxType) && (item->auxForID == colorItem->id)) {
Joe Drago060d5342020-03-03 10:53:49 -08003285 if (isGrid) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003286 avifROData readData;
Joe Dragobffba3b2021-05-26 15:46:10 -07003287 avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, 0, data->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003288 if (readResult != AVIF_RESULT_OK) {
3289 return readResult;
3290 }
Joe Drago4d5f4a42021-04-28 13:11:45 -07003291 if (!avifParseImageGridBox(&data->alphaGrid, readData.data, readData.size, data->diag)) {
Joe Drago060d5342020-03-03 10:53:49 -08003292 return AVIF_RESULT_INVALID_IMAGE_GRID;
3293 }
Joe Dragof6a42272019-11-21 15:21:41 -08003294 }
3295
Joe Dragobe4cbb92020-09-21 12:14:05 -07003296 alphaItem = item;
Joe Drago060d5342020-03-03 10:53:49 -08003297 break;
Joe Dragof6a42272019-11-21 15:21:41 -08003298 }
Joe Drago444f0512019-01-23 17:03:24 -08003299 }
Joe Drago444f0512019-01-23 17:03:24 -08003300
Joe Drago060d5342020-03-03 10:53:49 -08003301 // Find Exif and/or XMP metadata, if any
Joe Dragobe4cbb92020-09-21 12:14:05 -07003302 avifResult findResult = avifDecoderFindMetadata(decoder, data->meta, decoder->image, colorItem->id);
3303 if (findResult != AVIF_RESULT_OK) {
3304 return findResult;
Joe Drago060d5342020-03-03 10:53:49 -08003305 }
3306
Joe Drago46ea0582019-07-22 15:55:47 -07003307 // Set all counts and timing to safe-but-uninteresting values
3308 decoder->imageIndex = -1;
3309 decoder->imageCount = 1;
3310 decoder->imageTiming.timescale = 1;
3311 decoder->imageTiming.pts = 0;
3312 decoder->imageTiming.ptsInTimescales = 0;
3313 decoder->imageTiming.duration = 1;
3314 decoder->imageTiming.durationInTimescales = 1;
3315 decoder->timescale = 1;
3316 decoder->duration = 1;
3317 decoder->durationInTimescales = 1;
Joe Drago70cbf602019-07-24 15:30:55 -07003318
Joe Dragobffba3b2021-05-26 15:46:10 -07003319 if ((data->colorGrid.rows > 0) && (data->colorGrid.columns > 0)) {
3320 if (!avifDecoderGenerateImageGridTiles(decoder, &data->colorGrid, colorItem, AVIF_FALSE)) {
3321 return AVIF_RESULT_INVALID_IMAGE_GRID;
3322 }
3323 data->colorTileCount = data->tiles.count;
3324 } else {
3325 if (colorItem->size == 0) {
3326 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3327 }
3328
Joe Drago46104d62021-05-27 18:17:29 -07003329 avifTile * colorTile =
3330 avifDecoderDataCreateTile(data, colorItem->width, colorItem->height, avifDecoderItemOperatingPoint(colorItem));
Joe Dragobffba3b2021-05-26 15:46:10 -07003331 if (!avifCodecDecodeInputFillFromDecoderItem(colorTile->input,
3332 colorItem,
3333 decoder->allowProgressive,
3334 decoder->imageCountLimit,
3335 decoder->io->sizeHint,
3336 &decoder->diag)) {
3337 return AVIF_FALSE;
3338 }
3339 data->colorTileCount = 1;
3340
3341 if (colorItem->progressive) {
3342 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_AVAILABLE;
3343 if (colorTile->input->samples.count > 1) {
3344 decoder->progressiveState = AVIF_PROGRESSIVE_STATE_ACTIVE;
3345 decoder->imageCount = colorTile->input->samples.count;
3346 }
3347 }
3348 }
3349
3350 if (alphaItem) {
3351 if ((data->alphaGrid.rows > 0) && (data->alphaGrid.columns > 0)) {
3352 if (!avifDecoderGenerateImageGridTiles(decoder, &data->alphaGrid, alphaItem, AVIF_TRUE)) {
3353 return AVIF_RESULT_INVALID_IMAGE_GRID;
3354 }
3355 data->alphaTileCount = data->tiles.count - data->colorTileCount;
3356 } else {
3357 if (alphaItem->size == 0) {
3358 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
3359 }
3360
Joe Drago46104d62021-05-27 18:17:29 -07003361 avifTile * alphaTile =
3362 avifDecoderDataCreateTile(data, alphaItem->width, alphaItem->height, avifDecoderItemOperatingPoint(alphaItem));
Joe Dragobffba3b2021-05-26 15:46:10 -07003363 if (!avifCodecDecodeInputFillFromDecoderItem(alphaTile->input,
3364 alphaItem,
3365 decoder->allowProgressive,
3366 decoder->imageCountLimit,
3367 decoder->io->sizeHint,
3368 &decoder->diag)) {
3369 return AVIF_FALSE;
3370 }
3371 alphaTile->input->alpha = AVIF_TRUE;
3372 data->alphaTileCount = 1;
3373 }
3374 }
3375
Joe Dragobe4cbb92020-09-21 12:14:05 -07003376 decoder->ioStats.colorOBUSize = colorItem->size;
3377 decoder->ioStats.alphaOBUSize = alphaItem ? alphaItem->size : 0;
Joe Drago41700852019-09-26 17:01:43 -07003378
Joe Drago46104d62021-05-27 18:17:29 -07003379 decoder->image->width = colorItem->width;
3380 decoder->image->height = colorItem->height;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003381 decoder->alphaPresent = (alphaItem != NULL);
Joe Dragoceb2fa02021-01-29 18:14:55 -08003382 decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorItem->premByID == alphaItem->id);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003383
Joe Drago9b942c22021-05-06 12:33:12 -07003384 avifResult colorItemValidationResult = avifDecoderItemValidateAV1(colorItem, &decoder->diag, decoder->strictFlags);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003385 if (colorItemValidationResult != AVIF_RESULT_OK) {
3386 return colorItemValidationResult;
3387 }
3388 if (alphaItem) {
Joe Drago9b942c22021-05-06 12:33:12 -07003389 avifResult alphaItemValidationResult = avifDecoderItemValidateAV1(alphaItem, &decoder->diag, decoder->strictFlags);
Joe Dragoc10c6be2021-03-31 08:55:19 -07003390 if (alphaItemValidationResult != AVIF_RESULT_OK) {
3391 return alphaItemValidationResult;
3392 }
3393 }
Joe Drago00bcaaf2020-06-05 15:29:38 -07003394 }
3395
Joe Drago11f2a5e2020-07-06 10:49:00 -07003396 // Sanity check tiles
3397 for (uint32_t tileIndex = 0; tileIndex < data->tiles.count; ++tileIndex) {
3398 avifTile * tile = &data->tiles.tile[tileIndex];
3399 for (uint32_t sampleIndex = 0; sampleIndex < tile->input->samples.count; ++sampleIndex) {
Joe Drago043311b2020-07-06 16:48:41 -07003400 avifDecodeSample * sample = &tile->input->samples.sample[sampleIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003401 if (!sample->size) {
Joe Drago11f2a5e2020-07-06 10:49:00 -07003402 // Every sample must have some data
3403 return AVIF_RESULT_BMFF_PARSE_FAILED;
3404 }
3405 }
3406 }
3407
Joe Dragobf58fe72020-11-05 13:25:14 -08003408 // Find and adopt all colr boxes "at most one for a given value of colour type" (HEIF 6.5.5.1, from Amendment 3)
3409 // Accept one of each type, and bail out if more than one of a given type is provided.
3410 avifBool colrICCSeen = AVIF_FALSE;
3411 avifBool colrNCLXSeen = AVIF_FALSE;
3412 for (uint32_t propertyIndex = 0; propertyIndex < colorProperties->count; ++propertyIndex) {
3413 avifProperty * prop = &colorProperties->prop[propertyIndex];
3414
3415 if (!memcmp(prop->type, "colr", 4)) {
3416 if (prop->u.colr.hasICC) {
3417 if (colrICCSeen) {
3418 return AVIF_RESULT_BMFF_PARSE_FAILED;
3419 }
3420 colrICCSeen = AVIF_TRUE;
3421 avifImageSetProfileICC(decoder->image, prop->u.colr.icc, prop->u.colr.iccSize);
3422 }
3423 if (prop->u.colr.hasNCLX) {
3424 if (colrNCLXSeen) {
3425 return AVIF_RESULT_BMFF_PARSE_FAILED;
3426 }
3427 colrNCLXSeen = AVIF_TRUE;
3428 data->cicpSet = AVIF_TRUE;
3429 decoder->image->colorPrimaries = prop->u.colr.colorPrimaries;
3430 decoder->image->transferCharacteristics = prop->u.colr.transferCharacteristics;
3431 decoder->image->matrixCoefficients = prop->u.colr.matrixCoefficients;
3432 decoder->image->yuvRange = prop->u.colr.range;
3433 }
Joe Dragoa72da5b2020-06-15 19:40:17 -07003434 }
3435 }
3436
3437 // Transformations
3438 const avifProperty * paspProp = avifPropertyArrayFind(colorProperties, "pasp");
3439 if (paspProp) {
3440 decoder->image->transformFlags |= AVIF_TRANSFORM_PASP;
3441 memcpy(&decoder->image->pasp, &paspProp->u.pasp, sizeof(avifPixelAspectRatioBox));
3442 }
3443 const avifProperty * clapProp = avifPropertyArrayFind(colorProperties, "clap");
3444 if (clapProp) {
3445 decoder->image->transformFlags |= AVIF_TRANSFORM_CLAP;
3446 memcpy(&decoder->image->clap, &clapProp->u.clap, sizeof(avifCleanApertureBox));
3447 }
3448 const avifProperty * irotProp = avifPropertyArrayFind(colorProperties, "irot");
3449 if (irotProp) {
3450 decoder->image->transformFlags |= AVIF_TRANSFORM_IROT;
3451 memcpy(&decoder->image->irot, &irotProp->u.irot, sizeof(avifImageRotation));
3452 }
3453 const avifProperty * imirProp = avifPropertyArrayFind(colorProperties, "imir");
3454 if (imirProp) {
3455 decoder->image->transformFlags |= AVIF_TRANSFORM_IMIR;
3456 memcpy(&decoder->image->imir, &imirProp->u.imir, sizeof(avifImageMirror));
3457 }
3458
wantehchangb207b4d2020-08-11 17:50:22 -07003459 if (!data->cicpSet && (data->tiles.count > 0)) {
Joe Dragoda92a382020-06-09 17:08:45 -07003460 avifTile * firstTile = &data->tiles.tile[0];
3461 if (firstTile->input->samples.count > 0) {
3462 avifDecodeSample * sample = &firstTile->input->samples.sample[0];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003463
3464 // Harvest CICP from the AV1's sequence header, which should be very close to the front
3465 // of the first sample. Read in successively larger chunks until we successfully parse the sequence.
3466 static const size_t searchSampleChunkIncrement = 64;
Wan-Teh Changac145712021-02-08 15:53:44 -08003467 static const size_t searchSampleSizeMax = 4096;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003468 size_t searchSampleSize = 0;
Wan-Teh Changc6c2fe82020-10-08 10:44:04 -07003469 do {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003470 searchSampleSize += searchSampleChunkIncrement;
3471 if (searchSampleSize > sample->size) {
3472 searchSampleSize = sample->size;
3473 }
3474
3475 avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, searchSampleSize);
3476 if (prepareResult != AVIF_RESULT_OK) {
3477 return prepareResult;
3478 }
3479
3480 avifSequenceHeader sequenceHeader;
3481 if (avifSequenceHeaderParse(&sequenceHeader, &sample->data)) {
3482 data->cicpSet = AVIF_TRUE;
3483 decoder->image->colorPrimaries = sequenceHeader.colorPrimaries;
3484 decoder->image->transferCharacteristics = sequenceHeader.transferCharacteristics;
3485 decoder->image->matrixCoefficients = sequenceHeader.matrixCoefficients;
3486 decoder->image->yuvRange = sequenceHeader.range;
3487 break;
3488 }
Wan-Teh Changac145712021-02-08 15:53:44 -08003489 } while (searchSampleSize != sample->size && searchSampleSize < searchSampleSizeMax);
Joe Dragoda92a382020-06-09 17:08:45 -07003490 }
3491 }
3492
Joe Dragoa72da5b2020-06-15 19:40:17 -07003493 const avifProperty * av1CProp = avifPropertyArrayFind(colorProperties, "av1C");
3494 if (av1CProp) {
Joe Dragob8401122020-06-19 11:45:49 -07003495 decoder->image->depth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
3496 if (av1CProp->u.av1C.monochrome) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003497 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
Joe Drago7b2cf802020-06-09 17:57:23 -07003498 } else {
Joe Dragob8401122020-06-19 11:45:49 -07003499 if (av1CProp->u.av1C.chromaSubsamplingX && av1CProp->u.av1C.chromaSubsamplingY) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003500 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV420;
Joe Dragob8401122020-06-19 11:45:49 -07003501 } else if (av1CProp->u.av1C.chromaSubsamplingX) {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003502 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV422;
Joe Drago7b2cf802020-06-09 17:57:23 -07003503
3504 } else {
Joe Dragoc554f5f2020-06-09 18:59:51 -07003505 decoder->image->yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
Joe Drago7b2cf802020-06-09 17:57:23 -07003506 }
3507 }
Joe Dragob8401122020-06-19 11:45:49 -07003508 decoder->image->yuvChromaSamplePosition = (avifChromaSamplePosition)av1CProp->u.av1C.chromaSamplePosition;
Joe Drago00bcaaf2020-06-05 15:29:38 -07003509 } else {
Joe Dragof48a3382020-06-19 14:13:44 -07003510 // An av1C box is mandatory in all valid AVIF configurations. Bail out.
3511 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Drago46ea0582019-07-22 15:55:47 -07003512 }
3513
Joe Drago22c1ad92019-09-26 12:46:50 -07003514 return avifDecoderFlush(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07003515}
Joe Drago444f0512019-01-23 17:03:24 -08003516
Joe Drago5998f592020-11-13 15:38:20 -08003517avifResult avifDecoderNextImage(avifDecoder * decoder)
Joe Drago46ea0582019-07-22 15:55:47 -07003518{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003519 avifDiagnosticsClearError(&decoder->diag);
3520
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003521 if (!decoder->data) {
3522 // Nothing has been parsed yet
3523 return AVIF_RESULT_NO_CONTENT;
3524 }
3525
Joe Dragobe4cbb92020-09-21 12:14:05 -07003526 if (!decoder->io || !decoder->io->read) {
Wan-Teh Chang31c7c1a2020-10-13 16:45:41 -07003527 return AVIF_RESULT_IO_NOT_SET;
Joe Dragobe4cbb92020-09-21 12:14:05 -07003528 }
3529
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003530 const uint32_t nextImageIndex = (uint32_t)(decoder->imageIndex + 1);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003531
3532 // Acquire all sample data for the current image first, allowing for any read call to bail out
3533 // with AVIF_RESULT_WAITING_ON_IO harmlessly / idempotently.
3534 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
3535 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
Joe Drago5998f592020-11-13 15:38:20 -08003536 if (nextImageIndex >= tile->input->samples.count) {
Joe Dragobe4cbb92020-09-21 12:14:05 -07003537 return AVIF_RESULT_NO_IMAGES_REMAINING;
3538 }
3539
Joe Drago5998f592020-11-13 15:38:20 -08003540 avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003541 avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, 0);
3542 if (prepareResult != AVIF_RESULT_OK) {
3543 return prepareResult;
3544 }
3545 }
3546
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003547 // Decode all tiles now that the sample data is ready.
Joe Drago060d5342020-03-03 10:53:49 -08003548 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
3549 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
Joe Drago41eb62b2019-02-08 15:38:18 -08003550
Wan-Teh Changb4977b32020-10-05 18:04:54 -07003551 const avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex];
Joe Dragobe4cbb92020-09-21 12:14:05 -07003552
Joe Drago405e8722021-05-25 15:08:00 -07003553 if (!tile->codec->getNextImage(tile->codec, decoder, sample, tile->input->alpha, tile->image)) {
Wan-Teh Chang365bd5e2021-03-23 18:28:40 -07003554 return tile->input->alpha ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED;
Joe Drago46ea0582019-07-22 15:55:47 -07003555 }
Joe Drago46104d62021-05-27 18:17:29 -07003556
3557 // Scale the decoded image so that it corresponds to this tile's output dimensions
3558 if ((tile->width != tile->image->width) || (tile->height != tile->image->height)) {
3559 if (!avifImageScale(tile->image, tile->width, tile->height, &decoder->diag)) {
3560 return tile->input->alpha ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED;
3561 }
3562 }
Joe Drago060d5342020-03-03 10:53:49 -08003563 }
3564
3565 if (decoder->data->tiles.count != (decoder->data->colorTileCount + decoder->data->alphaTileCount)) {
3566 // TODO: assert here? This should be impossible.
3567 return AVIF_RESULT_UNKNOWN_ERROR;
3568 }
3569
Wan-Teh Changab8d9a52020-08-12 17:17:37 -07003570 if ((decoder->data->colorGrid.rows > 0) && (decoder->data->colorGrid.columns > 0)) {
Joe Drago11d23592021-01-05 14:18:57 -08003571 if (!avifDecoderDataFillImageGrid(decoder->data, &decoder->data->colorGrid, decoder->image, 0, decoder->data->colorTileCount, AVIF_FALSE)) {
Joe Drago060d5342020-03-03 10:53:49 -08003572 return AVIF_RESULT_INVALID_IMAGE_GRID;
3573 }
3574 } else {
3575 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
3576
3577 if (decoder->data->colorTileCount != 1) {
3578 return AVIF_RESULT_DECODE_COLOR_FAILED;
3579 }
3580
3581 avifImage * srcColor = decoder->data->tiles.tile[0].image;
3582
3583 if ((decoder->image->width != srcColor->width) || (decoder->image->height != srcColor->height) ||
3584 (decoder->image->depth != srcColor->depth)) {
3585 avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL);
3586
3587 decoder->image->width = srcColor->width;
3588 decoder->image->height = srcColor->height;
3589 decoder->image->depth = srcColor->depth;
Joe Dragoc554f5f2020-06-09 18:59:51 -07003590 }
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09003591
Joe Dragoc00d5832020-08-13 16:03:28 -07003592#if 0
3593 // This code is currently unnecessary as the CICP is always set by the end of avifDecoderParse().
3594 if (!decoder->data->cicpSet) {
3595 decoder->data->cicpSet = AVIF_TRUE;
3596 decoder->image->colorPrimaries = srcColor->colorPrimaries;
3597 decoder->image->transferCharacteristics = srcColor->transferCharacteristics;
3598 decoder->image->matrixCoefficients = srcColor->matrixCoefficients;
3599 }
3600#endif
3601
Joe Drago060d5342020-03-03 10:53:49 -08003602 avifImageStealPlanes(decoder->image, srcColor, AVIF_PLANES_YUV);
3603 }
3604
Wan-Teh Changab8d9a52020-08-12 17:17:37 -07003605 if ((decoder->data->alphaGrid.rows > 0) && (decoder->data->alphaGrid.columns > 0)) {
Joe Drago11d23592021-01-05 14:18:57 -08003606 if (!avifDecoderDataFillImageGrid(decoder->data,
3607 &decoder->data->alphaGrid,
3608 decoder->image,
3609 decoder->data->colorTileCount,
3610 decoder->data->alphaTileCount,
3611 AVIF_TRUE)) {
Joe Drago060d5342020-03-03 10:53:49 -08003612 return AVIF_RESULT_INVALID_IMAGE_GRID;
3613 }
3614 } else {
3615 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
3616
3617 if (decoder->data->alphaTileCount == 0) {
3618 avifImageFreePlanes(decoder->image, AVIF_PLANES_A); // no alpha
3619 } else {
3620 if (decoder->data->alphaTileCount != 1) {
3621 return AVIF_RESULT_DECODE_ALPHA_FAILED;
3622 }
3623
3624 avifImage * srcAlpha = decoder->data->tiles.tile[decoder->data->colorTileCount].image;
3625 if ((decoder->image->width != srcAlpha->width) || (decoder->image->height != srcAlpha->height) ||
3626 (decoder->image->depth != srcAlpha->depth)) {
3627 return AVIF_RESULT_DECODE_ALPHA_FAILED;
3628 }
3629
3630 avifImageStealPlanes(decoder->image, srcAlpha, AVIF_PLANES_A);
Joe Drago3fd2db22020-08-13 16:07:24 -07003631 decoder->image->alphaRange = srcAlpha->alphaRange;
Joe Drago060d5342020-03-03 10:53:49 -08003632 }
3633 }
Joe Drago7ad3ad62019-02-07 11:17:34 -08003634
Joe Dragobe4cbb92020-09-21 12:14:05 -07003635 decoder->imageIndex = nextImageIndex;
Joe Drago46ea0582019-07-22 15:55:47 -07003636 if (decoder->data->sourceSampleTable) {
3637 // Decoding from a track! Provide timing information.
Joe Drago05559c92019-07-17 16:33:38 -07003638
Joe Dragoe9c58602020-04-13 17:23:13 -07003639 avifResult timingResult = avifDecoderNthImageTiming(decoder, decoder->imageIndex, &decoder->imageTiming);
3640 if (timingResult != AVIF_RESULT_OK) {
3641 return timingResult;
Joe Drago22c1ad92019-09-26 12:46:50 -07003642 }
Joe Dragoe9c58602020-04-13 17:23:13 -07003643 }
3644 return AVIF_RESULT_OK;
3645}
Joe Drago46ea0582019-07-22 15:55:47 -07003646
Wan-Teh Change184dc12020-05-11 12:47:21 -07003647avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming)
Joe Dragoe9c58602020-04-13 17:23:13 -07003648{
3649 if (!decoder->data) {
3650 // Nothing has been parsed yet
3651 return AVIF_RESULT_NO_CONTENT;
3652 }
3653
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07003654 if ((frameIndex > INT_MAX) || ((int)frameIndex >= decoder->imageCount)) {
Joe Dragoe9c58602020-04-13 17:23:13 -07003655 // Impossible index
3656 return AVIF_RESULT_NO_IMAGES_REMAINING;
3657 }
3658
3659 if (!decoder->data->sourceSampleTable) {
3660 // There isn't any real timing associated with this decode, so
3661 // just hand back the defaults chosen in avifDecoderReset().
3662 memcpy(outTiming, &decoder->imageTiming, sizeof(avifImageTiming));
3663 return AVIF_RESULT_OK;
3664 }
3665
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003666 outTiming->timescale = decoder->timescale;
3667 outTiming->ptsInTimescales = 0;
Joe Dragoe9c58602020-04-13 17:23:13 -07003668 for (int imageIndex = 0; imageIndex < (int)frameIndex; ++imageIndex) {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003669 outTiming->ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07003670 }
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003671 outTiming->durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, frameIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07003672
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003673 if (outTiming->timescale > 0) {
3674 outTiming->pts = (double)outTiming->ptsInTimescales / (double)outTiming->timescale;
3675 outTiming->duration = (double)outTiming->durationInTimescales / (double)outTiming->timescale;
Joe Dragoe9c58602020-04-13 17:23:13 -07003676 } else {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07003677 outTiming->pts = 0.0;
3678 outTiming->duration = 0.0;
Joe Drago444f0512019-01-23 17:03:24 -08003679 }
Joe Drago46ea0582019-07-22 15:55:47 -07003680 return AVIF_RESULT_OK;
3681}
3682
Joe Drago22c1ad92019-09-26 12:46:50 -07003683avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex)
3684{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003685 avifDiagnosticsClearError(&decoder->diag);
3686
Wan-Teh Chang25b9c702020-10-08 12:17:17 -07003687 if (frameIndex > INT_MAX) {
3688 // Impossible index
3689 return AVIF_RESULT_NO_IMAGES_REMAINING;
3690 }
3691
Joe Drago22c1ad92019-09-26 12:46:50 -07003692 int requestedIndex = (int)frameIndex;
3693 if (requestedIndex == decoder->imageIndex) {
3694 // We're here already, nothing to do
3695 return AVIF_RESULT_OK;
3696 }
3697
3698 if (requestedIndex == (decoder->imageIndex + 1)) {
3699 // it's just the next image, nothing special here
3700 return avifDecoderNextImage(decoder);
3701 }
3702
3703 if (requestedIndex >= decoder->imageCount) {
3704 // Impossible index
3705 return AVIF_RESULT_NO_IMAGES_REMAINING;
3706 }
3707
Wan-Teh Chang722fb7e2020-11-06 12:39:58 -08003708 int nearestKeyFrame = (int)avifDecoderNearestKeyframe(decoder, frameIndex);
3709 if ((nearestKeyFrame > (decoder->imageIndex + 1)) || (requestedIndex < decoder->imageIndex)) {
3710 // If we get here, a decoder flush is necessary
3711 decoder->imageIndex = nearestKeyFrame - 1; // prepare to read nearest keyframe
3712 avifDecoderFlush(decoder);
3713 }
Joe Drago22c1ad92019-09-26 12:46:50 -07003714 for (;;) {
3715 avifResult result = avifDecoderNextImage(decoder);
3716 if (result != AVIF_RESULT_OK) {
3717 return result;
3718 }
3719
3720 if (requestedIndex == decoder->imageIndex) {
3721 break;
3722 }
Joe Dragofc4144e2019-09-27 20:35:06 -07003723 }
Joe Drago22c1ad92019-09-26 12:46:50 -07003724 return AVIF_RESULT_OK;
3725}
3726
Wan-Teh Change184dc12020-05-11 12:47:21 -07003727avifBool avifDecoderIsKeyframe(const avifDecoder * decoder, uint32_t frameIndex)
Joe Drago22c1ad92019-09-26 12:46:50 -07003728{
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003729 if (!decoder->data) {
3730 // Nothing has been parsed yet
3731 return AVIF_FALSE;
3732 }
3733
Joe Drago060d5342020-03-03 10:53:49 -08003734 if ((decoder->data->tiles.count > 0) && decoder->data->tiles.tile[0].input) {
3735 if (frameIndex < decoder->data->tiles.tile[0].input->samples.count) {
3736 return decoder->data->tiles.tile[0].input->samples.sample[frameIndex].sync;
Joe Drago22c1ad92019-09-26 12:46:50 -07003737 }
3738 }
3739 return AVIF_FALSE;
3740}
3741
Wan-Teh Change184dc12020-05-11 12:47:21 -07003742uint32_t avifDecoderNearestKeyframe(const avifDecoder * decoder, uint32_t frameIndex)
Joe Drago22c1ad92019-09-26 12:46:50 -07003743{
Wan-Teh Changaf0d4842020-11-17 12:27:34 -08003744 if (!decoder->data) {
3745 // Nothing has been parsed yet
3746 return 0;
3747 }
3748
Joe Drago22c1ad92019-09-26 12:46:50 -07003749 for (; frameIndex != 0; --frameIndex) {
3750 if (avifDecoderIsKeyframe(decoder, frameIndex)) {
3751 break;
3752 }
3753 }
3754 return frameIndex;
3755}
3756
Joe Dragobe4cbb92020-09-21 12:14:05 -07003757avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image)
Joe Drago46ea0582019-07-22 15:55:47 -07003758{
Joe Dragobe4cbb92020-09-21 12:14:05 -07003759 avifResult result = avifDecoderParse(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07003760 if (result != AVIF_RESULT_OK) {
3761 return result;
Joe Drago05559c92019-07-17 16:33:38 -07003762 }
Joe Drago46ea0582019-07-22 15:55:47 -07003763 result = avifDecoderNextImage(decoder);
3764 if (result != AVIF_RESULT_OK) {
3765 return result;
3766 }
Joe Drago250221a2020-06-01 11:11:06 -07003767 avifImageCopy(image, decoder->image, AVIF_PLANES_ALL);
Joe Drago46ea0582019-07-22 15:55:47 -07003768 return AVIF_RESULT_OK;
Joe Drago444f0512019-01-23 17:03:24 -08003769}
Joe Dragobe4cbb92020-09-21 12:14:05 -07003770
Wan-Teh Chang7de44452020-10-08 11:43:18 -07003771avifResult avifDecoderReadMemory(avifDecoder * decoder, avifImage * image, const uint8_t * data, size_t size)
Joe Dragobe4cbb92020-09-21 12:14:05 -07003772{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003773 avifDiagnosticsClearError(&decoder->diag);
Wan-Teh Chang7de44452020-10-08 11:43:18 -07003774 avifResult result = avifDecoderSetIOMemory(decoder, data, size);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003775 if (result != AVIF_RESULT_OK) {
3776 return result;
3777 }
3778 return avifDecoderRead(decoder, image);
3779}
3780
3781avifResult avifDecoderReadFile(avifDecoder * decoder, avifImage * image, const char * filename)
3782{
Joe Drago4d5f4a42021-04-28 13:11:45 -07003783 avifDiagnosticsClearError(&decoder->diag);
Joe Dragobe4cbb92020-09-21 12:14:05 -07003784 avifResult result = avifDecoderSetIOFile(decoder, filename);
3785 if (result != AVIF_RESULT_OK) {
3786 return result;
3787 }
3788 return avifDecoderRead(decoder, image);
3789}