blob: 735d8417567eb08ac48adebc2c2737b9b9f2322c [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
Joe Drago444f0512019-01-23 17:03:24 -08006#include <string.h>
7
Joe Dragocd1e4c32019-02-08 11:26:31 -08008#define AUXTYPE_SIZE 64
Joe Dragof6a42272019-11-21 15:21:41 -08009#define CONTENTTYPE_SIZE 64
Joe Drago8f7a3002019-02-07 19:35:37 -080010#define MAX_COMPATIBLE_BRANDS 32
Joe Drago8f7a3002019-02-07 19:35:37 -080011
Joe Drago6500fd62019-10-08 17:17:34 -070012// class VisualSampleEntry(codingname) extends SampleEntry(codingname) {
13// unsigned int(16) pre_defined = 0;
14// const unsigned int(16) reserved = 0;
15// unsigned int(32)[3] pre_defined = 0;
16// unsigned int(16) width;
17// unsigned int(16) height;
18// template unsigned int(32) horizresolution = 0x00480000; // 72 dpi
19// template unsigned int(32) vertresolution = 0x00480000; // 72 dpi
20// const unsigned int(32) reserved = 0;
21// template unsigned int(16) frame_count = 1;
22// string[32] compressorname;
23// template unsigned int(16) depth = 0x0018;
24// int(16) pre_defined = -1;
25// // other boxes from derived specifications
26// CleanApertureBox clap; // optional
27// PixelAspectRatioBox pasp; // optional
28// }
29static const size_t VISUALSAMPLEENTRY_SIZE = 78;
30
Joe Dragof6a42272019-11-21 15:21:41 -080031static const char xmpContentType[] = CONTENT_TYPE_XMP;
32static const size_t xmpContentTypeSize = sizeof(xmpContentType);
33
Joe Dragob13e5722019-02-08 19:07:25 -080034// ---------------------------------------------------------------------------
35// Box data structures
36
37// ftyp
38typedef struct avifFileType
39{
40 uint8_t majorBrand[4];
41 uint32_t minorVersion;
42 uint8_t compatibleBrands[4 * MAX_COMPATIBLE_BRANDS];
43 int compatibleBrandsCount;
44} avifFileType;
45
46// ispe
Joe Drago8f7a3002019-02-07 19:35:37 -080047typedef struct avifImageSpatialExtents
48{
49 uint32_t width;
50 uint32_t height;
51} avifImageSpatialExtents;
Joe Drago444f0512019-01-23 17:03:24 -080052
Joe Dragob13e5722019-02-08 19:07:25 -080053// auxC
Joe Dragocd1e4c32019-02-08 11:26:31 -080054typedef struct avifAuxiliaryType
55{
56 char auxType[AUXTYPE_SIZE];
57} avifAuxiliaryType;
58
Joe Dragof6a42272019-11-21 15:21:41 -080059// infe mime content_type
60typedef struct avifContentType
61{
62 char contentType[CONTENTTYPE_SIZE];
63} avifContentType;
64
Joe Dragob13e5722019-02-08 19:07:25 -080065// colr
Joe Drago41eb62b2019-02-08 15:38:18 -080066typedef struct avifColourInformationBox
67{
68 avifProfileFormat format;
Joe Drago345aaa12019-09-25 13:42:12 -070069 const uint8_t * icc;
Joe Drago41eb62b2019-02-08 15:38:18 -080070 size_t iccSize;
Joe Dragoe7ce20d2019-02-11 16:37:38 -080071 avifNclxColorProfile nclx;
Joe Drago41eb62b2019-02-08 15:38:18 -080072} avifColourInformationBox;
73
Joe Drago60421562020-04-23 11:32:26 -070074#define MAX_PIXI_PLANE_DEPTHS 4
75typedef struct avifPixelInformationProperty
76{
77 uint8_t planeDepths[MAX_PIXI_PLANE_DEPTHS];
78 uint8_t planeCount;
79} avifPixelInformationProperty;
80
Joe Dragob13e5722019-02-08 19:07:25 -080081// ---------------------------------------------------------------------------
82// Top-level structures
83
Joe Drago800b47f2020-03-18 16:22:37 -070084// one "item" worth for decoding (all iref, iloc, iprp, etc refer to one of these)
85typedef struct avifDecoderItem
Joe Drago444f0512019-01-23 17:03:24 -080086{
Joe Dragoae7e2c32019-07-18 15:22:25 -070087 uint32_t id;
Joe Drago444f0512019-01-23 17:03:24 -080088 uint8_t type[4];
89 uint32_t offset;
90 uint32_t size;
Joe Dragof6a42272019-11-21 15:21:41 -080091 uint32_t idatID; // If non-zero, offset is relative to this idat box (iloc construction_method==1)
Joe Drago41eb62b2019-02-08 15:38:18 -080092 avifBool ispePresent;
Joe Drago8f7a3002019-02-07 19:35:37 -080093 avifImageSpatialExtents ispe;
Joe Drago41eb62b2019-02-08 15:38:18 -080094 avifBool auxCPresent;
Joe Dragocd1e4c32019-02-08 11:26:31 -080095 avifAuxiliaryType auxC;
Joe Dragof6a42272019-11-21 15:21:41 -080096 avifContentType contentType;
Joe Drago41eb62b2019-02-08 15:38:18 -080097 avifBool colrPresent;
98 avifColourInformationBox colr;
Joe Drago6500fd62019-10-08 17:17:34 -070099 avifBool av1CPresent;
100 avifCodecConfigurationBox av1C;
Joe Drago89f0cc82020-03-09 16:13:27 -0700101 avifBool paspPresent;
102 avifPixelAspectRatioBox pasp;
103 avifBool clapPresent;
104 avifCleanApertureBox clap;
105 avifBool irotPresent;
106 avifImageRotation irot;
107 avifBool imirPresent;
108 avifImageMirror imir;
Joe Drago60421562020-04-23 11:32:26 -0700109 avifBool pixiPresent;
110 avifPixelInformationProperty pixi;
Joe Drago79d6f362019-07-22 22:36:30 -0700111 uint32_t thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
112 uint32_t auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID}
Joe Dragof6a42272019-11-21 15:21:41 -0800113 uint32_t descForID; // if non-zero, this item is a content description for Item #{descForID}
Joe Drago060d5342020-03-03 10:53:49 -0800114 uint32_t dimgForID; // if non-zero, this item is a derived image for Item #{dimgForID}
Joe Drago3320e5f2020-04-21 17:36:27 -0700115 avifBool hasUnsupportedEssentialProperty; // If true, this file cites a property flagged as 'essential' that libavif doesn't support (yet). Ignore the item, if so.
Joe Drago800b47f2020-03-18 16:22:37 -0700116} avifDecoderItem;
117AVIF_ARRAY_DECLARE(avifDecoderItemArray, avifDecoderItem, item);
Joe Drago444f0512019-01-23 17:03:24 -0800118
Joe Drago800b47f2020-03-18 16:22:37 -0700119// Temporary storage for ipco contents until they can be associated and memcpy'd to an avifDecoderItem
Joe Drago8f7a3002019-02-07 19:35:37 -0800120typedef struct avifProperty
121{
122 uint8_t type[4];
123 avifImageSpatialExtents ispe;
Joe Dragocd1e4c32019-02-08 11:26:31 -0800124 avifAuxiliaryType auxC;
Joe Drago41eb62b2019-02-08 15:38:18 -0800125 avifColourInformationBox colr;
Joe Drago6500fd62019-10-08 17:17:34 -0700126 avifCodecConfigurationBox av1C;
Joe Drago89f0cc82020-03-09 16:13:27 -0700127 avifPixelAspectRatioBox pasp;
128 avifCleanApertureBox clap;
129 avifImageRotation irot;
130 avifImageMirror imir;
Joe Drago60421562020-04-23 11:32:26 -0700131 avifPixelInformationProperty pixi;
Joe Drago8f7a3002019-02-07 19:35:37 -0800132} avifProperty;
Joe Drago05559c92019-07-17 16:33:38 -0700133AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop);
Joe Drago8f7a3002019-02-07 19:35:37 -0800134
Joe Dragof6a42272019-11-21 15:21:41 -0800135// idat storage
Joe Drago800b47f2020-03-18 16:22:37 -0700136typedef struct avifDecoderItemData
Joe Dragof6a42272019-11-21 15:21:41 -0800137{
138 uint32_t id;
139 avifROData data;
Joe Drago800b47f2020-03-18 16:22:37 -0700140} avifDecoderItemData;
141AVIF_ARRAY_DECLARE(avifDecoderItemDataArray, avifDecoderItemData, idat);
Joe Dragof6a42272019-11-21 15:21:41 -0800142
Joe Drago060d5342020-03-03 10:53:49 -0800143// grid storage
144typedef struct avifImageGrid
145{
146 uint8_t rows;
147 uint8_t columns;
148 uint32_t outputWidth;
149 uint32_t outputHeight;
150} avifImageGrid;
151
Joe Dragoae7e2c32019-07-18 15:22:25 -0700152// ---------------------------------------------------------------------------
153// avifTrack
154
155typedef struct avifSampleTableChunk
156{
157 uint64_t offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700158} avifSampleTableChunk;
159AVIF_ARRAY_DECLARE(avifSampleTableChunkArray, avifSampleTableChunk, chunk);
160
161typedef struct avifSampleTableSampleToChunk
162{
163 uint32_t firstChunk;
164 uint32_t samplesPerChunk;
165 uint32_t sampleDescriptionIndex;
166} avifSampleTableSampleToChunk;
167AVIF_ARRAY_DECLARE(avifSampleTableSampleToChunkArray, avifSampleTableSampleToChunk, sampleToChunk);
168
169typedef struct avifSampleTableSampleSize
170{
171 uint32_t size;
172} avifSampleTableSampleSize;
173AVIF_ARRAY_DECLARE(avifSampleTableSampleSizeArray, avifSampleTableSampleSize, sampleSize);
174
175typedef struct avifSampleTableTimeToSample
176{
177 uint32_t sampleCount;
178 uint32_t sampleDelta;
179} avifSampleTableTimeToSample;
180AVIF_ARRAY_DECLARE(avifSampleTableTimeToSampleArray, avifSampleTableTimeToSample, timeToSample);
181
Joe Drago22c1ad92019-09-26 12:46:50 -0700182typedef struct avifSyncSample
183{
184 uint32_t sampleNumber;
185} avifSyncSample;
186AVIF_ARRAY_DECLARE(avifSyncSampleArray, avifSyncSample, syncSample);
187
Joe Drago2c0924c2019-09-26 17:41:01 -0700188typedef struct avifSampleDescription
189{
190 uint8_t format[4];
Joe Drago6500fd62019-10-08 17:17:34 -0700191 avifBool av1CPresent;
192 avifCodecConfigurationBox av1C;
Joe Drago2c0924c2019-09-26 17:41:01 -0700193} avifSampleDescription;
194AVIF_ARRAY_DECLARE(avifSampleDescriptionArray, avifSampleDescription, description);
195
Joe Dragoae7e2c32019-07-18 15:22:25 -0700196typedef struct avifSampleTable
197{
198 avifSampleTableChunkArray chunks;
Joe Drago2c0924c2019-09-26 17:41:01 -0700199 avifSampleDescriptionArray sampleDescriptions;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700200 avifSampleTableSampleToChunkArray sampleToChunks;
201 avifSampleTableSampleSizeArray sampleSizes;
202 avifSampleTableTimeToSampleArray timeToSamples;
Joe Drago22c1ad92019-09-26 12:46:50 -0700203 avifSyncSampleArray syncSamples;
Joe Drago370be3f2020-02-07 15:59:42 -0800204 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 -0700205} avifSampleTable;
206
Joe Drago46ea0582019-07-22 15:55:47 -0700207static avifSampleTable * avifSampleTableCreate()
Joe Dragoae7e2c32019-07-18 15:22:25 -0700208{
209 avifSampleTable * sampleTable = (avifSampleTable *)avifAlloc(sizeof(avifSampleTable));
210 memset(sampleTable, 0, sizeof(avifSampleTable));
211 avifArrayCreate(&sampleTable->chunks, sizeof(avifSampleTableChunk), 16);
Joe Drago2c0924c2019-09-26 17:41:01 -0700212 avifArrayCreate(&sampleTable->sampleDescriptions, sizeof(avifSampleDescription), 2);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700213 avifArrayCreate(&sampleTable->sampleToChunks, sizeof(avifSampleTableSampleToChunk), 16);
214 avifArrayCreate(&sampleTable->sampleSizes, sizeof(avifSampleTableSampleSize), 16);
215 avifArrayCreate(&sampleTable->timeToSamples, sizeof(avifSampleTableTimeToSample), 16);
Joe Drago759e6742019-09-26 18:07:21 -0700216 avifArrayCreate(&sampleTable->syncSamples, sizeof(avifSyncSample), 16);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700217 return sampleTable;
218}
219
Joe Drago46ea0582019-07-22 15:55:47 -0700220static void avifSampleTableDestroy(avifSampleTable * sampleTable)
Joe Dragoae7e2c32019-07-18 15:22:25 -0700221{
222 avifArrayDestroy(&sampleTable->chunks);
Joe Drago2c0924c2019-09-26 17:41:01 -0700223 avifArrayDestroy(&sampleTable->sampleDescriptions);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700224 avifArrayDestroy(&sampleTable->sampleToChunks);
225 avifArrayDestroy(&sampleTable->sampleSizes);
226 avifArrayDestroy(&sampleTable->timeToSamples);
Joe Drago22c1ad92019-09-26 12:46:50 -0700227 avifArrayDestroy(&sampleTable->syncSamples);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700228 avifFree(sampleTable);
229}
230
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700231static uint32_t avifSampleTableGetImageDelta(const avifSampleTable * sampleTable, int imageIndex)
Joe Drago46ea0582019-07-22 15:55:47 -0700232{
233 int maxSampleIndex = 0;
234 for (uint32_t i = 0; i < sampleTable->timeToSamples.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700235 const avifSampleTableTimeToSample * timeToSample = &sampleTable->timeToSamples.timeToSample[i];
Joe Drago46ea0582019-07-22 15:55:47 -0700236 maxSampleIndex += timeToSample->sampleCount;
237 if ((imageIndex < maxSampleIndex) || (i == (sampleTable->timeToSamples.count - 1))) {
238 return timeToSample->sampleDelta;
239 }
240 }
241
242 // TODO: fail here?
243 return 1;
244}
245
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700246static avifBool avifSampleTableHasFormat(const avifSampleTable * sampleTable, const char * format)
Joe Drago2c0924c2019-09-26 17:41:01 -0700247{
248 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
249 if (!memcmp(sampleTable->sampleDescriptions.description[i].format, format, 4)) {
250 return AVIF_TRUE;
251 }
252 }
253 return AVIF_FALSE;
254}
255
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700256static uint32_t avifCodecConfigurationBoxGetDepth(const avifCodecConfigurationBox * av1C)
Joe Drago6500fd62019-10-08 17:17:34 -0700257{
258 if (av1C->twelveBit) {
259 return 12;
260 } else if (av1C->highBitdepth) {
261 return 10;
262 }
263 return 8;
264}
265
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700266static uint32_t avifSampleTableGetDepth(const avifSampleTable * sampleTable)
Joe Drago6500fd62019-10-08 17:17:34 -0700267{
268 for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) {
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700269 const avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i];
Joe Drago6500fd62019-10-08 17:17:34 -0700270 if (!memcmp(description->format, "av01", 4) && description->av1CPresent) {
271 return avifCodecConfigurationBoxGetDepth(&description->av1C);
272 }
273 }
274 return 0;
275}
276
Joe Dragoae7e2c32019-07-18 15:22:25 -0700277// one video track ("trak" contents)
278typedef struct avifTrack
279{
280 uint32_t id;
281 uint32_t auxForID; // if non-zero, this item is an auxC plane for Track #{auxForID}
282 uint32_t mediaTimescale;
283 uint64_t mediaDuration;
Joe Dragofc4144e2019-09-27 20:35:06 -0700284 uint32_t width;
285 uint32_t height;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700286 avifSampleTable * sampleTable;
287} avifTrack;
288AVIF_ARRAY_DECLARE(avifTrackArray, avifTrack, track);
289
290// ---------------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -0700291// avifCodecDecodeInput
292
Joe Drago399df4f2019-07-23 16:45:14 -0700293avifCodecDecodeInput * avifCodecDecodeInputCreate(void)
Joe Drago46ea0582019-07-22 15:55:47 -0700294{
295 avifCodecDecodeInput * decodeInput = (avifCodecDecodeInput *)avifAlloc(sizeof(avifCodecDecodeInput));
296 memset(decodeInput, 0, sizeof(avifCodecDecodeInput));
Joe Drago22c1ad92019-09-26 12:46:50 -0700297 avifArrayCreate(&decodeInput->samples, sizeof(avifSample), 1);
Joe Drago46ea0582019-07-22 15:55:47 -0700298 return decodeInput;
299}
300
Joe Drago8b34ad72019-07-22 16:56:32 -0700301void avifCodecDecodeInputDestroy(avifCodecDecodeInput * decodeInput)
Joe Drago46ea0582019-07-22 15:55:47 -0700302{
303 avifArrayDestroy(&decodeInput->samples);
304 avifFree(decodeInput);
305}
306
Joe Drago345aaa12019-09-25 13:42:12 -0700307static avifBool avifCodecDecodeInputGetSamples(avifCodecDecodeInput * decodeInput, avifSampleTable * sampleTable, avifROData * rawInput)
Joe Drago46ea0582019-07-22 15:55:47 -0700308{
309 uint32_t sampleSizeIndex = 0;
310 for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) {
311 avifSampleTableChunk * chunk = &sampleTable->chunks.chunk[chunkIndex];
312
313 // First, figure out how many samples are in this chunk
314 uint32_t sampleCount = 0;
315 for (int sampleToChunkIndex = sampleTable->sampleToChunks.count - 1; sampleToChunkIndex >= 0; --sampleToChunkIndex) {
316 avifSampleTableSampleToChunk * sampleToChunk = &sampleTable->sampleToChunks.sampleToChunk[sampleToChunkIndex];
317 if (sampleToChunk->firstChunk <= (chunkIndex + 1)) {
318 sampleCount = sampleToChunk->samplesPerChunk;
319 break;
320 }
321 }
322 if (sampleCount == 0) {
323 // chunks with 0 samples are invalid
324 return AVIF_FALSE;
325 }
326
327 uint64_t sampleOffset = chunk->offset;
328 for (uint32_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
Joe Drago370be3f2020-02-07 15:59:42 -0800329 uint32_t sampleSize = sampleTable->allSamplesSize;
330 if (sampleSize == 0) {
331 if (sampleSizeIndex >= sampleTable->sampleSizes.count) {
332 // We've run out of samples to sum
333 return AVIF_FALSE;
334 }
335 avifSampleTableSampleSize * sampleSizePtr = &sampleTable->sampleSizes.sampleSize[sampleSizeIndex];
336 sampleSize = sampleSizePtr->size;
Joe Drago46ea0582019-07-22 15:55:47 -0700337 }
338
Joe Drago22c1ad92019-09-26 12:46:50 -0700339 avifSample * sample = (avifSample *)avifArrayPushPtr(&decodeInput->samples);
340 sample->data.data = rawInput->data + sampleOffset;
Joe Drago370be3f2020-02-07 15:59:42 -0800341 sample->data.size = sampleSize;
Joe Drago22c1ad92019-09-26 12:46:50 -0700342 sample->sync = AVIF_FALSE; // to potentially be set to true following the outer loop
Joe Drago46ea0582019-07-22 15:55:47 -0700343
344 if (sampleOffset > (uint64_t)rawInput->size) {
345 return AVIF_FALSE;
346 }
347
Joe Drago370be3f2020-02-07 15:59:42 -0800348 sampleOffset += sampleSize;
Joe Drago46ea0582019-07-22 15:55:47 -0700349 ++sampleSizeIndex;
350 }
351 }
Joe Drago22c1ad92019-09-26 12:46:50 -0700352
353 // Mark appropriate samples as sync
354 for (uint32_t syncSampleIndex = 0; syncSampleIndex < sampleTable->syncSamples.count; ++syncSampleIndex) {
355 uint32_t frameIndex = sampleTable->syncSamples.syncSample[syncSampleIndex].sampleNumber - 1; // sampleNumber is 1-based
356 if (frameIndex < decodeInput->samples.count) {
357 decodeInput->samples.sample[frameIndex].sync = AVIF_TRUE;
358 }
359 }
360
361 // Assume frame 0 is sync, just in case the stss box is absent in the BMFF. (Unnecessary?)
362 if (decodeInput->samples.count > 0) {
363 decodeInput->samples.sample[0].sync = AVIF_TRUE;
364 }
Joe Drago46ea0582019-07-22 15:55:47 -0700365 return AVIF_TRUE;
366}
367
368// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700369// avifDecoderData
Joe Dragoae7e2c32019-07-18 15:22:25 -0700370
Joe Drago060d5342020-03-03 10:53:49 -0800371typedef struct avifTile
372{
373 avifCodecDecodeInput * input;
374 struct avifCodec * codec;
375 avifImage * image;
376} avifTile;
377AVIF_ARRAY_DECLARE(avifTileArray, avifTile, tile);
378
Joe Drago800b47f2020-03-18 16:22:37 -0700379typedef struct avifDecoderData
Joe Drago444f0512019-01-23 17:03:24 -0800380{
Joe Drago8f7a3002019-02-07 19:35:37 -0800381 avifFileType ftyp;
Joe Drago800b47f2020-03-18 16:22:37 -0700382 avifDecoderItemArray items;
Joe Drago05559c92019-07-17 16:33:38 -0700383 avifPropertyArray properties;
Joe Drago800b47f2020-03-18 16:22:37 -0700384 avifDecoderItemDataArray idats;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700385 avifTrackArray tracks;
Joe Drago345aaa12019-09-25 13:42:12 -0700386 avifROData rawInput;
Joe Drago060d5342020-03-03 10:53:49 -0800387 avifTileArray tiles;
388 unsigned int colorTileCount;
389 unsigned int alphaTileCount;
390 avifImageGrid colorGrid;
391 avifImageGrid alphaGrid;
Joe Drago46ea0582019-07-22 15:55:47 -0700392 avifDecoderSource source;
Wan-Teh Chang306c3062020-04-05 12:17:33 -0700393 const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack
Joe Dragof6a42272019-11-21 15:21:41 -0800394 uint32_t primaryItemID;
395 uint32_t metaBoxID; // Ever-incrementing ID for tracking which 'meta' box contains an idat, and which idat an iloc might refer to
Joe Drago800b47f2020-03-18 16:22:37 -0700396} avifDecoderData;
Joe Drago444f0512019-01-23 17:03:24 -0800397
Joe Drago800b47f2020-03-18 16:22:37 -0700398static avifDecoderData * avifDecoderDataCreate()
Joe Drago05559c92019-07-17 16:33:38 -0700399{
Joe Drago800b47f2020-03-18 16:22:37 -0700400 avifDecoderData * data = (avifDecoderData *)avifAlloc(sizeof(avifDecoderData));
401 memset(data, 0, sizeof(avifDecoderData));
402 avifArrayCreate(&data->items, sizeof(avifDecoderItem), 8);
Joe Drago05559c92019-07-17 16:33:38 -0700403 avifArrayCreate(&data->properties, sizeof(avifProperty), 16);
Joe Drago800b47f2020-03-18 16:22:37 -0700404 avifArrayCreate(&data->idats, sizeof(avifDecoderItemData), 1);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700405 avifArrayCreate(&data->tracks, sizeof(avifTrack), 2);
Joe Drago060d5342020-03-03 10:53:49 -0800406 avifArrayCreate(&data->tiles, sizeof(avifTile), 8);
Joe Drago05559c92019-07-17 16:33:38 -0700407 return data;
408}
409
Joe Drago800b47f2020-03-18 16:22:37 -0700410static void avifDecoderDataResetCodec(avifDecoderData * data)
Joe Drago05559c92019-07-17 16:33:38 -0700411{
Joe Drago060d5342020-03-03 10:53:49 -0800412 for (unsigned int i = 0; i < data->tiles.count; ++i) {
413 avifTile * tile = &data->tiles.tile[i];
Joe Drago9d368782020-03-04 17:53:17 -0800414 if (tile->image) {
415 avifImageFreePlanes(tile->image, AVIF_PLANES_ALL); // forget any pointers into codec image buffers
416 }
Joe Drago060d5342020-03-03 10:53:49 -0800417 if (tile->codec) {
418 avifCodecDestroy(tile->codec);
419 tile->codec = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -0700420 }
421 }
422}
423
Joe Drago800b47f2020-03-18 16:22:37 -0700424static avifTile * avifDecoderDataCreateTile(avifDecoderData * data)
Joe Drago060d5342020-03-03 10:53:49 -0800425{
426 avifTile * tile = (avifTile *)avifArrayPushPtr(&data->tiles);
427 tile->image = avifImageCreateEmpty();
428 tile->input = avifCodecDecodeInputCreate();
429 return tile;
430}
431
Joe Drago800b47f2020-03-18 16:22:37 -0700432static void avifDecoderDataClearTiles(avifDecoderData * data)
Joe Drago060d5342020-03-03 10:53:49 -0800433{
434 for (unsigned int i = 0; i < data->tiles.count; ++i) {
435 avifTile * tile = &data->tiles.tile[i];
436 if (tile->input) {
437 avifCodecDecodeInputDestroy(tile->input);
438 tile->input = NULL;
439 }
440 if (tile->codec) {
441 avifCodecDestroy(tile->codec);
442 tile->codec = NULL;
443 }
444 if (tile->image) {
445 avifImageDestroy(tile->image);
446 tile->image = NULL;
447 }
448 }
449 data->tiles.count = 0;
450 data->colorTileCount = 0;
451 data->alphaTileCount = 0;
452}
453
Joe Drago800b47f2020-03-18 16:22:37 -0700454static void avifDecoderDataDestroy(avifDecoderData * data)
Joe Drago46ea0582019-07-22 15:55:47 -0700455{
Joe Drago05559c92019-07-17 16:33:38 -0700456 avifArrayDestroy(&data->items);
457 avifArrayDestroy(&data->properties);
Joe Dragof6a42272019-11-21 15:21:41 -0800458 avifArrayDestroy(&data->idats);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700459 for (uint32_t i = 0; i < data->tracks.count; ++i) {
460 if (data->tracks.track[i].sampleTable) {
461 avifSampleTableDestroy(data->tracks.track[i].sampleTable);
462 }
463 }
464 avifArrayDestroy(&data->tracks);
Joe Drago800b47f2020-03-18 16:22:37 -0700465 avifDecoderDataClearTiles(data);
Joe Drago060d5342020-03-03 10:53:49 -0800466 avifArrayDestroy(&data->tiles);
Joe Drago05559c92019-07-17 16:33:38 -0700467 avifFree(data);
468}
469
Joe Drago800b47f2020-03-18 16:22:37 -0700470static avifDecoderItem * avifDecoderDataFindItem(avifDecoderData * data, uint32_t itemID)
Joe Drago444f0512019-01-23 17:03:24 -0800471{
472 if (itemID == 0) {
Joe Drago05559c92019-07-17 16:33:38 -0700473 return NULL;
Joe Drago444f0512019-01-23 17:03:24 -0800474 }
475
Joe Drago05559c92019-07-17 16:33:38 -0700476 for (uint32_t i = 0; i < data->items.count; ++i) {
477 if (data->items.item[i].id == itemID) {
478 return &data->items.item[i];
Joe Drago444f0512019-01-23 17:03:24 -0800479 }
480 }
481
Joe Drago800b47f2020-03-18 16:22:37 -0700482 avifDecoderItem * item = (avifDecoderItem *)avifArrayPushPtr(&data->items);
Joe Drago05559c92019-07-17 16:33:38 -0700483 item->id = itemID;
484 return item;
Joe Drago444f0512019-01-23 17:03:24 -0800485}
486
Joe Drago800b47f2020-03-18 16:22:37 -0700487static const uint8_t * avifDecoderDataCalcItemPtr(avifDecoderData * data, avifDecoderItem * item)
Joe Dragof6a42272019-11-21 15:21:41 -0800488{
489 avifROData * offsetBuffer = NULL;
490 if (item->idatID == 0) {
491 // construction_method: file(0)
492
493 offsetBuffer = &data->rawInput;
494 } else {
495 // construction_method: idat(1)
496
497 // Find associated idat block
498 for (uint32_t i = 0; i < data->idats.count; ++i) {
499 if (data->idats.idat[i].id == item->idatID) {
500 offsetBuffer = &data->idats.idat[i].data;
501 break;
502 }
503 }
504
505 if (offsetBuffer == NULL) {
506 // no idat box was found in this meta box, bail out
507 return NULL;
508 }
509 }
510
511 if (item->offset > offsetBuffer->size) {
512 return NULL;
513 }
514 uint64_t offsetSize = (uint64_t)item->offset + (uint64_t)item->size;
515 if (offsetSize > (uint64_t)offsetBuffer->size) {
516 return NULL;
517 }
518 return offsetBuffer->data + item->offset;
519}
520
Joe Drago800b47f2020-03-18 16:22:37 -0700521static avifBool avifDecoderDataGenerateImageGridTiles(avifDecoderData * data, avifImageGrid * grid, avifDecoderItem * gridItem, avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -0800522{
523 unsigned int tilesRequested = (unsigned int)grid->rows * (unsigned int)grid->columns;
524
525 // Count number of dimg for this item, bail out if it doesn't match perfectly
526 unsigned int tilesAvailable = 0;
527 for (uint32_t i = 0; i < data->items.count; ++i) {
Joe Drago800b47f2020-03-18 16:22:37 -0700528 avifDecoderItem * item = &data->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -0800529 if (item->dimgForID == gridItem->id) {
530 if (memcmp(item->type, "av01", 4)) {
531 continue;
532 }
Joe Drago3320e5f2020-04-21 17:36:27 -0700533 if (item->hasUnsupportedEssentialProperty) {
534 // An essential property isn't supported by libavif; ignore the item.
535 continue;
536 }
Joe Drago060d5342020-03-03 10:53:49 -0800537
538 ++tilesAvailable;
539 }
540 }
541
542 if (tilesRequested != tilesAvailable) {
543 return AVIF_FALSE;
544 }
545
546 for (uint32_t i = 0; i < data->items.count; ++i) {
Joe Drago800b47f2020-03-18 16:22:37 -0700547 avifDecoderItem * item = &data->items.item[i];
Joe Drago060d5342020-03-03 10:53:49 -0800548 if (item->dimgForID == gridItem->id) {
549 if (memcmp(item->type, "av01", 4)) {
550 continue;
551 }
Joe Drago3320e5f2020-04-21 17:36:27 -0700552 if (item->hasUnsupportedEssentialProperty) {
553 // An essential property isn't supported by libavif; ignore the item.
554 continue;
555 }
Joe Drago060d5342020-03-03 10:53:49 -0800556
Joe Drago800b47f2020-03-18 16:22:37 -0700557 avifTile * tile = avifDecoderDataCreateTile(data);
Joe Drago060d5342020-03-03 10:53:49 -0800558 avifSample * sample = (avifSample *)avifArrayPushPtr(&tile->input->samples);
Joe Drago800b47f2020-03-18 16:22:37 -0700559 sample->data.data = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -0800560 sample->data.size = item->size;
561 sample->sync = AVIF_TRUE;
562 tile->input->alpha = alpha;
563 }
564 }
565 return AVIF_TRUE;
566}
567
Joe Drago800b47f2020-03-18 16:22:37 -0700568static avifBool avifDecoderDataFillImageGrid(avifDecoderData * data,
569 avifImageGrid * grid,
570 avifImage * dstImage,
571 unsigned int firstTileIndex,
572 unsigned int tileCount,
573 avifBool alpha)
Joe Drago060d5342020-03-03 10:53:49 -0800574{
575 if (tileCount == 0) {
576 return AVIF_FALSE;
577 }
578
579 avifTile * firstTile = &data->tiles.tile[firstTileIndex];
580 unsigned int tileWidth = firstTile->image->width;
581 unsigned int tileHeight = firstTile->image->height;
582 unsigned int tileDepth = firstTile->image->depth;
583 avifPixelFormat tileFormat = firstTile->image->yuvFormat;
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +0900584
585 avifProfileFormat tileProfile = firstTile->image->profileFormat;
586 avifNclxColorProfile * tileNCLX = &firstTile->image->nclx;
Joe Drago060d5342020-03-03 10:53:49 -0800587 avifRange tileRange = firstTile->image->yuvRange;
wantehchang3fde0d02020-03-10 23:58:32 -0700588 avifBool tileUVPresent = (firstTile->image->yuvPlanes[AVIF_CHAN_U] && firstTile->image->yuvPlanes[AVIF_CHAN_V]);
Joe Drago060d5342020-03-03 10:53:49 -0800589
590 for (unsigned int i = 1; i < tileCount; ++i) {
591 avifTile * tile = &data->tiles.tile[firstTileIndex + i];
Joe Drago951a0022020-03-09 16:19:44 -0700592 avifBool uvPresent = (tile->image->yuvPlanes[AVIF_CHAN_U] && tile->image->yuvPlanes[AVIF_CHAN_V]);
Joe Drago060d5342020-03-03 10:53:49 -0800593 if ((tile->image->width != tileWidth) || (tile->image->height != tileHeight) || (tile->image->depth != tileDepth) ||
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +0900594 (tile->image->yuvFormat != tileFormat) || (tile->image->yuvRange != tileRange) || (uvPresent != tileUVPresent) ||
595 ((tileProfile == AVIF_PROFILE_FORMAT_NCLX) &&
Joe Dragof16cd2d2020-03-09 11:25:37 -0700596 ((tile->image->profileFormat != tileProfile) || (tile->image->nclx.colourPrimaries != tileNCLX->colourPrimaries) ||
597 (tile->image->nclx.transferCharacteristics != tileNCLX->transferCharacteristics) ||
Joe Drago74cd1c92020-04-16 12:17:11 -0700598 (tile->image->nclx.matrixCoefficients != tileNCLX->matrixCoefficients) || (tile->image->nclx.range != tileNCLX->range)))) {
Joe Drago060d5342020-03-03 10:53:49 -0800599 return AVIF_FALSE;
600 }
601 }
602
603 if ((dstImage->width != grid->outputWidth) || (dstImage->height != grid->outputHeight) || (dstImage->depth != tileDepth) ||
604 (dstImage->yuvFormat != tileFormat)) {
605 if (alpha) {
606 // Alpha doesn't match size, just bail out
607 return AVIF_FALSE;
608 }
609
610 avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
611 dstImage->width = grid->outputWidth;
612 dstImage->height = grid->outputHeight;
613 dstImage->depth = tileDepth;
614 dstImage->yuvFormat = tileFormat;
615 dstImage->yuvRange = tileRange;
Joe Dragof16cd2d2020-03-09 11:25:37 -0700616 if ((dstImage->profileFormat == AVIF_PROFILE_FORMAT_NONE) && (tileProfile == AVIF_PROFILE_FORMAT_NCLX)) {
617 avifImageSetProfileNCLX(dstImage, tileNCLX);
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +0900618 }
Joe Drago060d5342020-03-03 10:53:49 -0800619 }
620
621 avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV);
622
623 avifPixelFormatInfo formatInfo;
624 avifGetPixelFormatInfo(tileFormat, &formatInfo);
625
626 unsigned int tileIndex = firstTileIndex;
627 size_t pixelBytes = avifImageUsesU16(dstImage) ? 2 : 1;
628 for (unsigned int rowIndex = 0; rowIndex < grid->rows; ++rowIndex) {
629 for (unsigned int colIndex = 0; colIndex < grid->columns; ++colIndex, ++tileIndex) {
630 avifTile * tile = &data->tiles.tile[tileIndex];
631
632 unsigned int widthToCopy = tileWidth;
633 unsigned int maxX = tileWidth * (colIndex + 1);
634 if (maxX > grid->outputWidth) {
635 widthToCopy -= maxX - grid->outputWidth;
636 }
637
638 unsigned int heightToCopy = tileHeight;
639 unsigned int maxY = tileHeight * (rowIndex + 1);
640 if (maxY > grid->outputHeight) {
641 heightToCopy -= maxY - grid->outputHeight;
642 }
643
644 // Y and A channels
645 size_t yaColOffset = colIndex * tileWidth;
646 size_t yaRowOffset = rowIndex * tileHeight;
647 size_t yaRowBytes = widthToCopy * pixelBytes;
648
649 if (alpha) {
650 // A
651 for (unsigned int j = 0; j < heightToCopy; ++j) {
652 uint8_t * src = &tile->image->alphaPlane[j * tile->image->alphaRowBytes];
653 uint8_t * dst = &dstImage->alphaPlane[(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->alphaRowBytes)];
654 memcpy(dst, src, yaRowBytes);
655 }
656 } else {
657 // Y
658 for (unsigned int j = 0; j < heightToCopy; ++j) {
659 uint8_t * src = &tile->image->yuvPlanes[AVIF_CHAN_Y][j * tile->image->yuvRowBytes[AVIF_CHAN_Y]];
660 uint8_t * dst =
661 &dstImage->yuvPlanes[AVIF_CHAN_Y][(yaColOffset * pixelBytes) + ((yaRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_Y])];
662 memcpy(dst, src, yaRowBytes);
663 }
664
665 if (!tileUVPresent) {
666 continue;
667 }
668
669 // UV
Joe Drago060d5342020-03-03 10:53:49 -0800670 heightToCopy >>= formatInfo.chromaShiftY;
671 size_t uvColOffset = yaColOffset >> formatInfo.chromaShiftX;
672 size_t uvRowOffset = yaRowOffset >> formatInfo.chromaShiftY;
673 size_t uvRowBytes = yaRowBytes >> formatInfo.chromaShiftX;
674 for (unsigned int j = 0; j < heightToCopy; ++j) {
675 uint8_t * srcU = &tile->image->yuvPlanes[AVIF_CHAN_U][j * tile->image->yuvRowBytes[AVIF_CHAN_U]];
676 uint8_t * dstU =
677 &dstImage->yuvPlanes[AVIF_CHAN_U][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_U])];
678 memcpy(dstU, srcU, uvRowBytes);
679
680 uint8_t * srcV = &tile->image->yuvPlanes[AVIF_CHAN_V][j * tile->image->yuvRowBytes[AVIF_CHAN_V]];
681 uint8_t * dstV =
682 &dstImage->yuvPlanes[AVIF_CHAN_V][(uvColOffset * pixelBytes) + ((uvRowOffset + j) * dstImage->yuvRowBytes[AVIF_CHAN_V])];
683 memcpy(dstV, srcV, uvRowBytes);
684 }
685 }
686 }
687 }
688
689 return AVIF_TRUE;
690}
691
Joe Drago8f7a3002019-02-07 19:35:37 -0800692// ---------------------------------------------------------------------------
Joe Dragocd1e4c32019-02-08 11:26:31 -0800693// URN
694
695static avifBool isAlphaURN(char * urn)
696{
wantehchang3fde0d02020-03-10 23:58:32 -0700697 return !strcmp(urn, URN_ALPHA0) || !strcmp(urn, URN_ALPHA1);
Joe Dragocd1e4c32019-02-08 11:26:31 -0800698}
699
700// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -0800701// BMFF Parsing
702
Joe Drago341a6a62019-07-23 16:12:59 -0700703#define BEGIN_STREAM(VARNAME, PTR, SIZE) \
Joe Drago345aaa12019-09-25 13:42:12 -0700704 avifROStream VARNAME; \
705 avifROData VARNAME##_roData; \
706 VARNAME##_roData.data = PTR; \
707 VARNAME##_roData.size = SIZE; \
708 avifROStreamStart(&VARNAME, &VARNAME##_roData)
Joe Drago8f7a3002019-02-07 19:35:37 -0800709
Joe Drago800b47f2020-03-18 16:22:37 -0700710static avifBool avifParseItemLocationBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago444f0512019-01-23 17:03:24 -0800711{
Joe Drago8f7a3002019-02-07 19:35:37 -0800712 BEGIN_STREAM(s, raw, rawLen);
Joe Drago444f0512019-01-23 17:03:24 -0800713
Joe Dragof6a42272019-11-21 15:21:41 -0800714 uint8_t version;
715 uint8_t flags[3];
716 CHECK(avifROStreamReadVersionAndFlags(&s, &version, flags));
717 if (version > 2) {
718 return AVIF_FALSE;
719 }
Joe Drago444f0512019-01-23 17:03:24 -0800720
Joe Drago8f7a3002019-02-07 19:35:37 -0800721 uint8_t offsetSizeAndLengthSize;
Joe Drago345aaa12019-09-25 13:42:12 -0700722 CHECK(avifROStreamRead(&s, &offsetSizeAndLengthSize, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -0800723 uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
724 uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
Joe Drago444f0512019-01-23 17:03:24 -0800725
Joe Dragof6a42272019-11-21 15:21:41 -0800726 uint8_t baseOffsetSizeAndIndexSize;
727 CHECK(avifROStreamRead(&s, &baseOffsetSizeAndIndexSize, 1));
728 uint8_t baseOffsetSize = (baseOffsetSizeAndIndexSize >> 4) & 0xf; // unsigned int(4) base_offset_size;
729 uint8_t indexSize = 0;
730 if ((version == 1) || (version == 2)) {
731 indexSize = baseOffsetSizeAndIndexSize & 0xf; // unsigned int(4) index_size;
732 if (indexSize != 0) {
733 // extent_index unsupported
734 return AVIF_FALSE;
735 }
736 }
Joe Drago444f0512019-01-23 17:03:24 -0800737
Joe Dragof6a42272019-11-21 15:21:41 -0800738 uint16_t tmp16;
739 uint32_t itemCount;
740 if (version < 2) {
741 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_count;
742 itemCount = tmp16;
743 } else {
744 CHECK(avifROStreamReadU32(&s, &itemCount)); // unsigned int(32) item_count;
745 }
746 for (uint32_t i = 0; i < itemCount; ++i) {
747 uint32_t itemID;
748 uint32_t idatID = 0;
749 if (version < 2) {
750 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
751 itemID = tmp16;
752 } else {
753 CHECK(avifROStreamReadU32(&s, &itemID)); // unsigned int(32) item_ID;
754 }
755
756 if ((version == 1) || (version == 2)) {
757 uint8_t ignored;
758 uint8_t constructionMethod;
759 CHECK(avifROStreamRead(&s, &ignored, 1)); // unsigned int(12) reserved = 0;
760 CHECK(avifROStreamRead(&s, &constructionMethod, 1)); // unsigned int(4) construction_method;
761 constructionMethod = constructionMethod & 0xf;
762 if ((constructionMethod != 0 /* file */) && (constructionMethod != 1 /* idat */)) {
763 // construction method item(2) unsupported
764 return AVIF_FALSE;
765 }
766 if (constructionMethod == 1) {
767 idatID = data->metaBoxID;
768 }
769 }
770
Joe Drago345aaa12019-09-25 13:42:12 -0700771 uint16_t dataReferenceIndex; // unsigned int(16) data_ref rence_index;
772 CHECK(avifROStreamReadU16(&s, &dataReferenceIndex)); //
773 uint64_t baseOffset; // unsigned int(base_offset_size*8) base_offset;
774 CHECK(avifROStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
775 uint16_t extentCount; // unsigned int(16) extent_count;
776 CHECK(avifROStreamReadU16(&s, &extentCount)); //
Joe Drago8f7a3002019-02-07 19:35:37 -0800777 if (extentCount == 1) {
Joe Dragof6a42272019-11-21 15:21:41 -0800778 // If extent_index is ever supported, this spec must be implemented here:
779 // :: if (((version == 1) || (version == 2)) && (index_size > 0)) {
780 // :: unsigned int(index_size*8) extent_index;
781 // :: }
782
Joe Drago8f7a3002019-02-07 19:35:37 -0800783 uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
Joe Drago345aaa12019-09-25 13:42:12 -0700784 CHECK(avifROStreamReadUX8(&s, &extentOffset, offsetSize));
Joe Drago8f7a3002019-02-07 19:35:37 -0800785 uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
Joe Drago345aaa12019-09-25 13:42:12 -0700786 CHECK(avifROStreamReadUX8(&s, &extentLength, lengthSize));
Joe Drago444f0512019-01-23 17:03:24 -0800787
Joe Drago800b47f2020-03-18 16:22:37 -0700788 avifDecoderItem * item = avifDecoderDataFindItem(data, itemID);
Joe Drago46ea0582019-07-22 15:55:47 -0700789 if (!item) {
790 return AVIF_FALSE;
791 }
Joe Drago05559c92019-07-17 16:33:38 -0700792 item->id = itemID;
793 item->offset = (uint32_t)(baseOffset + extentOffset);
794 item->size = (uint32_t)extentLength;
Joe Dragof6a42272019-11-21 15:21:41 -0800795 item->idatID = idatID;
Joe Drago444f0512019-01-23 17:03:24 -0800796 } else {
Joe Drago8f7a3002019-02-07 19:35:37 -0800797 // TODO: support more than one extent
798 return AVIF_FALSE;
Joe Drago444f0512019-01-23 17:03:24 -0800799 }
800 }
801 return AVIF_TRUE;
802}
803
Joe Drago060d5342020-03-03 10:53:49 -0800804static avifBool avifParseImageGridBox(avifImageGrid * grid, const uint8_t * raw, size_t rawLen)
805{
806 BEGIN_STREAM(s, raw, rawLen);
807
808 uint8_t version, flags;
809 CHECK(avifROStreamRead(&s, &version, 1)); // unsigned int(8) version = 0;
810 if (version != 0) {
811 return AVIF_FALSE;
812 }
813 CHECK(avifROStreamRead(&s, &flags, 1)); // unsigned int(8) flags;
814 CHECK(avifROStreamRead(&s, &grid->rows, 1)); // unsigned int(8) rows_minus_one;
815 CHECK(avifROStreamRead(&s, &grid->columns, 1)); // unsigned int(8) columns_minus_one;
816 ++grid->rows;
817 ++grid->columns;
818
819 uint32_t fieldLength = ((flags & 1) + 1) * 16;
820 if (fieldLength == 16) {
821 uint16_t outputWidth16, outputHeight16;
822 CHECK(avifROStreamReadU16(&s, &outputWidth16)); // unsigned int(FieldLength) output_width;
823 CHECK(avifROStreamReadU16(&s, &outputHeight16)); // unsigned int(FieldLength) output_height;
824 grid->outputWidth = outputWidth16;
825 grid->outputHeight = outputHeight16;
826 } else {
827 if (fieldLength != 32) {
828 // This should be impossible
829 return AVIF_FALSE;
830 }
831 CHECK(avifROStreamReadU32(&s, &grid->outputWidth)); // unsigned int(FieldLength) output_width;
832 CHECK(avifROStreamReadU32(&s, &grid->outputHeight)); // unsigned int(FieldLength) output_height;
833 }
834 return AVIF_TRUE;
835}
836
Joe Drago800b47f2020-03-18 16:22:37 -0700837static avifBool avifParseImageSpatialExtentsProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago8f7a3002019-02-07 19:35:37 -0800838{
839 BEGIN_STREAM(s, raw, rawLen);
Joe Drago345aaa12019-09-25 13:42:12 -0700840 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -0800841
Joe Drago345aaa12019-09-25 13:42:12 -0700842 CHECK(avifROStreamReadU32(&s, &data->properties.prop[propertyIndex].ispe.width));
843 CHECK(avifROStreamReadU32(&s, &data->properties.prop[propertyIndex].ispe.height));
Joe Drago8f7a3002019-02-07 19:35:37 -0800844 return AVIF_TRUE;
845}
846
Joe Drago800b47f2020-03-18 16:22:37 -0700847static avifBool avifParseAuxiliaryTypeProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Dragocd1e4c32019-02-08 11:26:31 -0800848{
849 BEGIN_STREAM(s, raw, rawLen);
Joe Drago345aaa12019-09-25 13:42:12 -0700850 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragocd1e4c32019-02-08 11:26:31 -0800851
Joe Drago345aaa12019-09-25 13:42:12 -0700852 CHECK(avifROStreamReadString(&s, data->properties.prop[propertyIndex].auxC.auxType, AUXTYPE_SIZE));
Joe Dragocd1e4c32019-02-08 11:26:31 -0800853 return AVIF_TRUE;
854}
855
Joe Drago800b47f2020-03-18 16:22:37 -0700856static avifBool avifParseColourInformationBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago41eb62b2019-02-08 15:38:18 -0800857{
858 BEGIN_STREAM(s, raw, rawLen);
Joe Dragoe7ce20d2019-02-11 16:37:38 -0800859
Joe Drago05559c92019-07-17 16:33:38 -0700860 data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NONE;
Joe Dragoe7ce20d2019-02-11 16:37:38 -0800861
Joe Drago41eb62b2019-02-08 15:38:18 -0800862 uint8_t colourType[4]; // unsigned int(32) colour_type;
Joe Drago345aaa12019-09-25 13:42:12 -0700863 CHECK(avifROStreamRead(&s, colourType, 4));
Joe Drago41eb62b2019-02-08 15:38:18 -0800864 if (!memcmp(colourType, "rICC", 4) || !memcmp(colourType, "prof", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700865 data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_ICC;
Joe Drago345aaa12019-09-25 13:42:12 -0700866 data->properties.prop[propertyIndex].colr.icc = avifROStreamCurrent(&s);
867 data->properties.prop[propertyIndex].colr.iccSize = avifROStreamRemainingBytes(&s);
Joe Dragoe7ce20d2019-02-11 16:37:38 -0800868 } else if (!memcmp(colourType, "nclx", 4)) {
Joe Drago74cd1c92020-04-16 12:17:11 -0700869 uint16_t tmp16;
Joe Drago97b071c2019-07-17 14:24:56 -0700870 // unsigned int(16) colour_primaries;
Joe Drago74cd1c92020-04-16 12:17:11 -0700871 CHECK(avifROStreamReadU16(&s, &tmp16));
872 data->properties.prop[propertyIndex].colr.nclx.colourPrimaries = (avifNclxColourPrimaries)tmp16;
Joe Drago97b071c2019-07-17 14:24:56 -0700873 // unsigned int(16) transfer_characteristics;
Joe Drago74cd1c92020-04-16 12:17:11 -0700874 CHECK(avifROStreamReadU16(&s, &tmp16));
875 data->properties.prop[propertyIndex].colr.nclx.transferCharacteristics = (avifNclxTransferCharacteristics)tmp16;
Joe Drago97b071c2019-07-17 14:24:56 -0700876 // unsigned int(16) matrix_coefficients;
Joe Drago74cd1c92020-04-16 12:17:11 -0700877 CHECK(avifROStreamReadU16(&s, &tmp16));
878 data->properties.prop[propertyIndex].colr.nclx.matrixCoefficients = (avifNclxMatrixCoefficients)tmp16;
Joe Drago97b071c2019-07-17 14:24:56 -0700879 // unsigned int(1) full_range_flag;
880 // unsigned int(7) reserved = 0;
Joe Drago74cd1c92020-04-16 12:17:11 -0700881 uint8_t tmp8;
882 CHECK(avifROStreamRead(&s, &tmp8, 1));
Wan-Teh Changd7a48ff2020-04-23 10:44:44 -0700883 data->properties.prop[propertyIndex].colr.nclx.range = (avifRange)(tmp8 & 0x80);
Joe Drago05559c92019-07-17 16:33:38 -0700884 data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NCLX;
Joe Drago41eb62b2019-02-08 15:38:18 -0800885 }
886 return AVIF_TRUE;
887}
888
Joe Drago6500fd62019-10-08 17:17:34 -0700889static avifBool avifParseAV1CodecConfigurationBox(const uint8_t * raw, size_t rawLen, avifCodecConfigurationBox * av1C)
890{
891 BEGIN_STREAM(s, raw, rawLen);
892
893 uint8_t markerAndVersion = 0;
894 CHECK(avifROStreamRead(&s, &markerAndVersion, 1));
895 uint8_t seqProfileAndIndex = 0;
896 CHECK(avifROStreamRead(&s, &seqProfileAndIndex, 1));
897 uint8_t rawFlags = 0;
898 CHECK(avifROStreamRead(&s, &rawFlags, 1));
899
900 if (markerAndVersion != 0x81) {
901 // Marker and version must both == 1
902 return AVIF_FALSE;
903 }
904
905 av1C->seqProfile = (seqProfileAndIndex >> 5) & 0x7; // unsigned int (3) seq_profile;
906 av1C->seqLevelIdx0 = (seqProfileAndIndex >> 0) & 0x1f; // unsigned int (5) seq_level_idx_0;
907 av1C->seqTier0 = (rawFlags >> 7) & 0x1; // unsigned int (1) seq_tier_0;
908 av1C->highBitdepth = (rawFlags >> 6) & 0x1; // unsigned int (1) high_bitdepth;
909 av1C->twelveBit = (rawFlags >> 5) & 0x1; // unsigned int (1) twelve_bit;
910 av1C->monochrome = (rawFlags >> 4) & 0x1; // unsigned int (1) monochrome;
911 av1C->chromaSubsamplingX = (rawFlags >> 3) & 0x1; // unsigned int (1) chroma_subsampling_x;
912 av1C->chromaSubsamplingY = (rawFlags >> 2) & 0x1; // unsigned int (1) chroma_subsampling_y;
913 av1C->chromaSamplePosition = (rawFlags >> 0) & 0x3; // unsigned int (2) chroma_sample_position;
914 return AVIF_TRUE;
915}
916
Joe Drago800b47f2020-03-18 16:22:37 -0700917static avifBool avifParseAV1CodecConfigurationBoxProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago6500fd62019-10-08 17:17:34 -0700918{
919 return avifParseAV1CodecConfigurationBox(raw, rawLen, &data->properties.prop[propertyIndex].av1C);
920}
921
Joe Drago800b47f2020-03-18 16:22:37 -0700922static avifBool avifParsePixelAspectRatioBoxProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago89f0cc82020-03-09 16:13:27 -0700923{
924 BEGIN_STREAM(s, raw, rawLen);
925
926 avifPixelAspectRatioBox * pasp = &data->properties.prop[propertyIndex].pasp;
927 CHECK(avifROStreamReadU32(&s, &pasp->hSpacing)); // unsigned int(32) hSpacing;
928 CHECK(avifROStreamReadU32(&s, &pasp->vSpacing)); // unsigned int(32) vSpacing;
929 return AVIF_TRUE;
930}
931
Joe Drago800b47f2020-03-18 16:22:37 -0700932static avifBool avifParseCleanApertureBoxProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago89f0cc82020-03-09 16:13:27 -0700933{
934 BEGIN_STREAM(s, raw, rawLen);
935
936 avifCleanApertureBox * clap = &data->properties.prop[propertyIndex].clap;
937 CHECK(avifROStreamReadU32(&s, &clap->widthN)); // unsigned int(32) cleanApertureWidthN;
938 CHECK(avifROStreamReadU32(&s, &clap->widthD)); // unsigned int(32) cleanApertureWidthD;
939 CHECK(avifROStreamReadU32(&s, &clap->heightN)); // unsigned int(32) cleanApertureHeightN;
940 CHECK(avifROStreamReadU32(&s, &clap->heightD)); // unsigned int(32) cleanApertureHeightD;
941 CHECK(avifROStreamReadU32(&s, &clap->horizOffN)); // unsigned int(32) horizOffN;
942 CHECK(avifROStreamReadU32(&s, &clap->horizOffD)); // unsigned int(32) horizOffD;
943 CHECK(avifROStreamReadU32(&s, &clap->vertOffN)); // unsigned int(32) vertOffN;
944 CHECK(avifROStreamReadU32(&s, &clap->vertOffD)); // unsigned int(32) vertOffD;
945 return AVIF_TRUE;
946}
947
Joe Drago800b47f2020-03-18 16:22:37 -0700948static avifBool avifParseImageRotationProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago89f0cc82020-03-09 16:13:27 -0700949{
950 BEGIN_STREAM(s, raw, rawLen);
951
952 avifImageRotation * irot = &data->properties.prop[propertyIndex].irot;
953 CHECK(avifROStreamRead(&s, &irot->angle, 1)); // unsigned int (6) reserved = 0; unsigned int (2) angle;
954 if ((irot->angle & 0xfc) != 0) {
955 // reserved bits must be 0
956 return AVIF_FALSE;
957 }
958 return AVIF_TRUE;
959}
960
Joe Drago800b47f2020-03-18 16:22:37 -0700961static avifBool avifParseImageMirrorProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
Joe Drago89f0cc82020-03-09 16:13:27 -0700962{
963 BEGIN_STREAM(s, raw, rawLen);
964
965 avifImageMirror * imir = &data->properties.prop[propertyIndex].imir;
966 CHECK(avifROStreamRead(&s, &imir->axis, 1)); // unsigned int (7) reserved = 0; unsigned int (1) axis;
967 if ((imir->axis & 0xfe) != 0) {
968 // reserved bits must be 0
969 return AVIF_FALSE;
970 }
971 return AVIF_TRUE;
972}
973
Joe Drago60421562020-04-23 11:32:26 -0700974static avifBool avifParsePixelInformationProperty(avifDecoderData * data, const uint8_t * raw, size_t rawLen, int propertyIndex)
975{
976 BEGIN_STREAM(s, raw, rawLen);
977 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
978
979 avifPixelInformationProperty * pixi = &data->properties.prop[propertyIndex].pixi;
980 CHECK(avifROStreamRead(&s, &pixi->planeCount, 1)); // unsigned int (8) num_channels;
981 if (pixi->planeCount > MAX_PIXI_PLANE_DEPTHS) {
982 return AVIF_FALSE;
983 }
984 for (uint8_t i = 0; i < pixi->planeCount; ++i) {
985 CHECK(avifROStreamRead(&s, &pixi->planeDepths[i], 1)); // unsigned int (8) bits_per_channel;
986 }
987 return AVIF_TRUE;
988}
989
Joe Drago800b47f2020-03-18 16:22:37 -0700990static avifBool avifParseItemPropertyContainerBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -0800991{
992 BEGIN_STREAM(s, raw, rawLen);
993
Joe Drago345aaa12019-09-25 13:42:12 -0700994 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -0800995 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -0700996 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -0800997
Joe Drago05559c92019-07-17 16:33:38 -0700998 int propertyIndex = avifArrayPushIndex(&data->properties);
999 memcpy(data->properties.prop[propertyIndex].type, header.type, 4);
Joe Drago8f7a3002019-02-07 19:35:37 -08001000 if (!memcmp(header.type, "ispe", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001001 CHECK(avifParseImageSpatialExtentsProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
Joe Drago8f7a3002019-02-07 19:35:37 -08001002 }
Joe Dragocd1e4c32019-02-08 11:26:31 -08001003 if (!memcmp(header.type, "auxC", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001004 CHECK(avifParseAuxiliaryTypeProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001005 }
Joe Drago41eb62b2019-02-08 15:38:18 -08001006 if (!memcmp(header.type, "colr", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001007 CHECK(avifParseColourInformationBox(data, avifROStreamCurrent(&s), header.size, propertyIndex));
Joe Drago41eb62b2019-02-08 15:38:18 -08001008 }
Joe Drago6500fd62019-10-08 17:17:34 -07001009 if (!memcmp(header.type, "av1C", 4)) {
1010 CHECK(avifParseAV1CodecConfigurationBoxProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
1011 }
Joe Drago89f0cc82020-03-09 16:13:27 -07001012 if (!memcmp(header.type, "pasp", 4)) {
1013 CHECK(avifParsePixelAspectRatioBoxProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
1014 }
1015 if (!memcmp(header.type, "clap", 4)) {
1016 CHECK(avifParseCleanApertureBoxProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
1017 }
1018 if (!memcmp(header.type, "irot", 4)) {
1019 CHECK(avifParseImageRotationProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
1020 }
1021 if (!memcmp(header.type, "imir", 4)) {
1022 CHECK(avifParseImageMirrorProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
1023 }
Joe Drago60421562020-04-23 11:32:26 -07001024 if (!memcmp(header.type, "pixi", 4)) {
1025 CHECK(avifParsePixelInformationProperty(data, avifROStreamCurrent(&s), header.size, propertyIndex));
1026 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001027
Joe Drago345aaa12019-09-25 13:42:12 -07001028 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001029 }
1030 return AVIF_TRUE;
1031}
1032
Joe Drago800b47f2020-03-18 16:22:37 -07001033static avifBool avifParseItemPropertyAssociation(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001034{
1035 BEGIN_STREAM(s, raw, rawLen);
1036
1037 uint8_t version;
1038 uint8_t flags[3];
Joe Drago345aaa12019-09-25 13:42:12 -07001039 CHECK(avifROStreamReadVersionAndFlags(&s, &version, flags));
Joe Drago951a0022020-03-09 16:19:44 -07001040 avifBool propertyIndexIsU16 = ((flags[2] & 0x1) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001041
1042 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001043 CHECK(avifROStreamReadU32(&s, &entryCount));
Joe Drago8f7a3002019-02-07 19:35:37 -08001044 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
1045 unsigned int itemID;
1046 if (version < 1) {
1047 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001048 CHECK(avifROStreamReadU16(&s, &tmp));
Joe Drago8f7a3002019-02-07 19:35:37 -08001049 itemID = tmp;
1050 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07001051 CHECK(avifROStreamReadU32(&s, &itemID));
Joe Drago8f7a3002019-02-07 19:35:37 -08001052 }
1053 uint8_t associationCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001054 CHECK(avifROStreamRead(&s, &associationCount, 1));
Joe Drago8f7a3002019-02-07 19:35:37 -08001055 for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) {
Joe Drago3320e5f2020-04-21 17:36:27 -07001056 avifBool essential = AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -08001057 uint16_t propertyIndex = 0;
1058 if (propertyIndexIsU16) {
Joe Drago345aaa12019-09-25 13:42:12 -07001059 CHECK(avifROStreamReadU16(&s, &propertyIndex));
Joe Drago3320e5f2020-04-21 17:36:27 -07001060 essential = ((propertyIndex & 0x8000) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001061 propertyIndex &= 0x7fff;
1062 } else {
1063 uint8_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001064 CHECK(avifROStreamRead(&s, &tmp, 1));
Joe Drago3320e5f2020-04-21 17:36:27 -07001065 essential = ((tmp & 0x80) != 0);
Joe Drago8f7a3002019-02-07 19:35:37 -08001066 propertyIndex = tmp & 0x7f;
1067 }
1068
1069 if (propertyIndex == 0) {
1070 // Not associated with any item
1071 continue;
1072 }
1073 --propertyIndex; // 1-indexed
1074
Joe Drago05559c92019-07-17 16:33:38 -07001075 if (propertyIndex >= data->properties.count) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001076 return AVIF_FALSE;
1077 }
1078
Joe Drago800b47f2020-03-18 16:22:37 -07001079 avifDecoderItem * item = avifDecoderDataFindItem(data, itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07001080 if (!item) {
1081 return AVIF_FALSE;
1082 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001083
1084 // Associate property with item
Joe Drago05559c92019-07-17 16:33:38 -07001085 avifProperty * prop = &data->properties.prop[propertyIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -08001086 if (!memcmp(prop->type, "ispe", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001087 item->ispePresent = AVIF_TRUE;
1088 memcpy(&item->ispe, &prop->ispe, sizeof(avifImageSpatialExtents));
Joe Dragocd1e4c32019-02-08 11:26:31 -08001089 } else if (!memcmp(prop->type, "auxC", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001090 item->auxCPresent = AVIF_TRUE;
1091 memcpy(&item->auxC, &prop->auxC, sizeof(avifAuxiliaryType));
Joe Drago41eb62b2019-02-08 15:38:18 -08001092 } else if (!memcmp(prop->type, "colr", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001093 item->colrPresent = AVIF_TRUE;
1094 memcpy(&item->colr, &prop->colr, sizeof(avifColourInformationBox));
Joe Drago6500fd62019-10-08 17:17:34 -07001095 } else if (!memcmp(prop->type, "av1C", 4)) {
1096 item->av1CPresent = AVIF_TRUE;
1097 memcpy(&item->av1C, &prop->av1C, sizeof(avifCodecConfigurationBox));
Joe Drago89f0cc82020-03-09 16:13:27 -07001098 } else if (!memcmp(prop->type, "pasp", 4)) {
1099 item->paspPresent = AVIF_TRUE;
1100 memcpy(&item->pasp, &prop->pasp, sizeof(avifPixelAspectRatioBox));
1101 } else if (!memcmp(prop->type, "clap", 4)) {
1102 item->clapPresent = AVIF_TRUE;
1103 memcpy(&item->clap, &prop->clap, sizeof(avifCleanApertureBox));
1104 } else if (!memcmp(prop->type, "irot", 4)) {
1105 item->irotPresent = AVIF_TRUE;
1106 memcpy(&item->irot, &prop->irot, sizeof(avifImageRotation));
1107 } else if (!memcmp(prop->type, "imir", 4)) {
1108 item->imirPresent = AVIF_TRUE;
1109 memcpy(&item->imir, &prop->imir, sizeof(avifImageMirror));
Joe Drago60421562020-04-23 11:32:26 -07001110 } else if (!memcmp(prop->type, "pixi", 4)) {
1111 item->pixiPresent = AVIF_TRUE;
1112 memcpy(&item->pixi, &prop->pixi, sizeof(avifPixelInformationProperty));
Joe Drago3320e5f2020-04-21 17:36:27 -07001113 } else {
1114 if (essential) {
1115 // Discovered an essential item property that libavif doesn't support!
1116 // Make a note to ignore this item later.
1117 item->hasUnsupportedEssentialProperty = AVIF_TRUE;
1118 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001119 }
1120 }
1121 }
1122
1123 return AVIF_TRUE;
1124}
1125
Joe Drago800b47f2020-03-18 16:22:37 -07001126static avifBool avifParsePrimaryItemBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Dragof6a42272019-11-21 15:21:41 -08001127{
1128 if (data->primaryItemID > 0) {
1129 // Illegal to have multiple pitm boxes, bail out
1130 return AVIF_FALSE;
1131 }
1132
1133 BEGIN_STREAM(s, raw, rawLen);
1134
1135 uint8_t version;
1136 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
1137
1138 if (version == 0) {
1139 uint16_t tmp16;
1140 CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID;
1141 data->primaryItemID = tmp16;
1142 } else {
1143 CHECK(avifROStreamReadU32(&s, &data->primaryItemID)); // unsigned int(32) item_ID;
1144 }
1145 return AVIF_TRUE;
1146}
1147
Joe Drago800b47f2020-03-18 16:22:37 -07001148static avifBool avifParseItemDataBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Dragof6a42272019-11-21 15:21:41 -08001149{
1150 uint32_t idatID = data->metaBoxID;
1151
1152 // Check to see if we've already seen an idat box for this meta box. If so, bail out
1153 for (uint32_t i = 0; i < data->idats.count; ++i) {
1154 if (data->idats.idat[i].id == idatID) {
1155 return AVIF_FALSE;
1156 }
1157 }
1158
1159 int index = avifArrayPushIndex(&data->idats);
Joe Drago800b47f2020-03-18 16:22:37 -07001160 avifDecoderItemData * idat = &data->idats.idat[index];
Joe Dragof6a42272019-11-21 15:21:41 -08001161 idat->id = idatID;
1162 idat->data.data = raw;
1163 idat->data.size = rawLen;
1164 return AVIF_TRUE;
1165}
1166
Joe Drago800b47f2020-03-18 16:22:37 -07001167static avifBool avifParseItemPropertiesBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001168{
1169 BEGIN_STREAM(s, raw, rawLen);
1170
1171 avifBoxHeader ipcoHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001172 CHECK(avifROStreamReadBoxHeader(&s, &ipcoHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001173 if (memcmp(ipcoHeader.type, "ipco", 4) != 0) {
1174 return AVIF_FALSE;
1175 }
1176
1177 // Read all item properties inside of ItemPropertyContainerBox
Joe Drago345aaa12019-09-25 13:42:12 -07001178 CHECK(avifParseItemPropertyContainerBox(data, avifROStreamCurrent(&s), ipcoHeader.size));
1179 CHECK(avifROStreamSkip(&s, ipcoHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001180
1181 // Now read all ItemPropertyAssociation until the end of the box, and make associations
Joe Drago345aaa12019-09-25 13:42:12 -07001182 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001183 avifBoxHeader ipmaHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001184 CHECK(avifROStreamReadBoxHeader(&s, &ipmaHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001185
1186 if (!memcmp(ipmaHeader.type, "ipma", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001187 CHECK(avifParseItemPropertyAssociation(data, avifROStreamCurrent(&s), ipmaHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001188 } else {
1189 // These must all be type ipma
1190 return AVIF_FALSE;
1191 }
1192
Joe Drago345aaa12019-09-25 13:42:12 -07001193 CHECK(avifROStreamSkip(&s, ipmaHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001194 }
1195 return AVIF_TRUE;
1196}
1197
Joe Drago800b47f2020-03-18 16:22:37 -07001198static avifBool avifParseItemInfoEntry(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001199{
1200 BEGIN_STREAM(s, raw, rawLen);
1201
Joe Drago345aaa12019-09-25 13:42:12 -07001202 CHECK(avifROStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
Joe Drago8f7a3002019-02-07 19:35:37 -08001203
Joe Drago345aaa12019-09-25 13:42:12 -07001204 uint16_t itemID; // unsigned int(16) item_ID;
1205 CHECK(avifROStreamReadU16(&s, &itemID)); //
1206 uint16_t itemProtectionIndex; // unsigned int(16) item_protection_index;
1207 CHECK(avifROStreamReadU16(&s, &itemProtectionIndex)); //
1208 uint8_t itemType[4]; // unsigned int(32) item_type;
1209 CHECK(avifROStreamRead(&s, itemType, 4)); //
Joe Drago8f7a3002019-02-07 19:35:37 -08001210
Joe Dragof6a42272019-11-21 15:21:41 -08001211 avifContentType contentType;
1212 if (!memcmp(itemType, "mime", 4)) {
1213 CHECK(avifROStreamReadString(&s, NULL, 0)); // string item_name; (skipped)
1214 CHECK(avifROStreamReadString(&s, contentType.contentType, CONTENTTYPE_SIZE)); // string content_type;
1215 } else {
1216 memset(&contentType, 0, sizeof(contentType));
1217 }
1218
Joe Drago800b47f2020-03-18 16:22:37 -07001219 avifDecoderItem * item = avifDecoderDataFindItem(data, itemID);
Joe Drago46ea0582019-07-22 15:55:47 -07001220 if (!item) {
1221 return AVIF_FALSE;
1222 }
1223
Joe Drago05559c92019-07-17 16:33:38 -07001224 memcpy(item->type, itemType, sizeof(itemType));
Joe Dragof6a42272019-11-21 15:21:41 -08001225 memcpy(&item->contentType, &contentType, sizeof(contentType));
Joe Drago8f7a3002019-02-07 19:35:37 -08001226 return AVIF_TRUE;
1227}
1228
Joe Drago800b47f2020-03-18 16:22:37 -07001229static avifBool avifParseItemInfoBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001230{
1231 BEGIN_STREAM(s, raw, rawLen);
1232
1233 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07001234 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08001235 uint32_t entryCount;
1236 if (version == 0) {
1237 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001238 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08001239 entryCount = tmp;
1240 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001241 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08001242 } else {
1243 return AVIF_FALSE;
1244 }
1245
Joe Drago678b9382019-02-09 03:17:47 -08001246 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001247 avifBoxHeader infeHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001248 CHECK(avifROStreamReadBoxHeader(&s, &infeHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001249
1250 if (!memcmp(infeHeader.type, "infe", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001251 CHECK(avifParseItemInfoEntry(data, avifROStreamCurrent(&s), infeHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001252 } else {
1253 // These must all be type ipma
1254 return AVIF_FALSE;
1255 }
1256
Joe Drago345aaa12019-09-25 13:42:12 -07001257 CHECK(avifROStreamSkip(&s, infeHeader.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001258 }
1259
1260 return AVIF_TRUE;
1261}
1262
Joe Drago800b47f2020-03-18 16:22:37 -07001263static avifBool avifParseItemReferenceBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001264{
1265 BEGIN_STREAM(s, raw, rawLen);
1266
1267 uint8_t version;
Joe Drago345aaa12019-09-25 13:42:12 -07001268 CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
Joe Drago8f7a3002019-02-07 19:35:37 -08001269
Joe Drago345aaa12019-09-25 13:42:12 -07001270 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001271 avifBoxHeader irefHeader;
Joe Drago345aaa12019-09-25 13:42:12 -07001272 CHECK(avifROStreamReadBoxHeader(&s, &irefHeader));
Joe Drago8f7a3002019-02-07 19:35:37 -08001273
1274 uint32_t fromID = 0;
1275 if (version == 0) {
1276 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001277 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001278 fromID = tmp;
1279 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001280 CHECK(avifROStreamReadU32(&s, &fromID)); // unsigned int(32) from_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001281 } else {
1282 // unsupported iref version, skip it
1283 break;
1284 }
1285
1286 uint16_t referenceCount = 0;
Joe Drago345aaa12019-09-25 13:42:12 -07001287 CHECK(avifROStreamReadU16(&s, &referenceCount)); // unsigned int(16) reference_count;
Joe Drago8f7a3002019-02-07 19:35:37 -08001288
1289 for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) {
1290 uint32_t toID = 0;
1291 if (version == 0) {
1292 uint16_t tmp;
Joe Drago345aaa12019-09-25 13:42:12 -07001293 CHECK(avifROStreamReadU16(&s, &tmp)); // unsigned int(16) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001294 toID = tmp;
1295 } else if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001296 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) to_item_ID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001297 } else {
1298 // unsupported iref version, skip it
1299 break;
1300 }
1301
1302 // Read this reference as "{fromID} is a {irefType} for {toID}"
1303 if (fromID && toID) {
Joe Drago800b47f2020-03-18 16:22:37 -07001304 avifDecoderItem * item = avifDecoderDataFindItem(data, fromID);
Joe Drago46ea0582019-07-22 15:55:47 -07001305 if (!item) {
1306 return AVIF_FALSE;
1307 }
1308
Joe Drago8f7a3002019-02-07 19:35:37 -08001309 if (!memcmp(irefHeader.type, "thmb", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001310 item->thumbnailForID = toID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001311 }
1312 if (!memcmp(irefHeader.type, "auxl", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -07001313 item->auxForID = toID;
Joe Drago8f7a3002019-02-07 19:35:37 -08001314 }
Joe Dragof6a42272019-11-21 15:21:41 -08001315 if (!memcmp(irefHeader.type, "cdsc", 4)) {
1316 item->descForID = toID;
1317 }
Joe Drago060d5342020-03-03 10:53:49 -08001318 if (!memcmp(irefHeader.type, "dimg", 4)) {
1319 // derived images refer in the opposite direction
Joe Drago800b47f2020-03-18 16:22:37 -07001320 avifDecoderItem * dimg = avifDecoderDataFindItem(data, toID);
Joe Drago060d5342020-03-03 10:53:49 -08001321 if (!dimg) {
1322 return AVIF_FALSE;
1323 }
1324
1325 dimg->dimgForID = fromID;
1326 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001327 }
1328 }
1329 }
1330
1331 return AVIF_TRUE;
1332}
1333
Joe Drago800b47f2020-03-18 16:22:37 -07001334static avifBool avifParseMetaBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001335{
1336 BEGIN_STREAM(s, raw, rawLen);
1337
Joe Drago345aaa12019-09-25 13:42:12 -07001338 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -08001339
Joe Dragof6a42272019-11-21 15:21:41 -08001340 ++data->metaBoxID; // for tracking idat
1341
Joe Drago345aaa12019-09-25 13:42:12 -07001342 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001343 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001344 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08001345
1346 if (!memcmp(header.type, "iloc", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001347 CHECK(avifParseItemLocationBox(data, avifROStreamCurrent(&s), header.size));
Joe Dragof6a42272019-11-21 15:21:41 -08001348 } else if (!memcmp(header.type, "pitm", 4)) {
1349 CHECK(avifParsePrimaryItemBox(data, avifROStreamCurrent(&s), header.size));
1350 } else if (!memcmp(header.type, "idat", 4)) {
1351 CHECK(avifParseItemDataBox(data, avifROStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001352 } else if (!memcmp(header.type, "iprp", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001353 CHECK(avifParseItemPropertiesBox(data, avifROStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001354 } else if (!memcmp(header.type, "iinf", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001355 CHECK(avifParseItemInfoBox(data, avifROStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001356 } else if (!memcmp(header.type, "iref", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001357 CHECK(avifParseItemReferenceBox(data, avifROStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001358 }
1359
Joe Drago345aaa12019-09-25 13:42:12 -07001360 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001361 }
1362 return AVIF_TRUE;
1363}
1364
Joe Drago800b47f2020-03-18 16:22:37 -07001365static avifBool avifParseTrackHeaderBox(avifDecoderData * data, avifTrack * track, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001366{
1367 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001368 (void)data;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001369
1370 uint8_t version;
1371 uint8_t flags[3];
Joe Drago345aaa12019-09-25 13:42:12 -07001372 CHECK(avifROStreamReadVersionAndFlags(&s, &version, flags));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001373
1374 uint32_t ignored32, trackID;
1375 uint64_t ignored64;
1376 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001377 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
1378 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
1379 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07001380 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
1381 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001382 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07001383 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
1384 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
1385 CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
Joe Dragofc4144e2019-09-27 20:35:06 -07001386 CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0;
1387 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001388 } else {
1389 // Unsupported version
1390 return AVIF_FALSE;
1391 }
1392
Joe Dragofc4144e2019-09-27 20:35:06 -07001393 // Skipping the following 52 bytes here:
1394 // ------------------------------------
1395 // const unsigned int(32)[2] reserved = 0;
1396 // template int(16) layer = 0;
1397 // template int(16) alternate_group = 0;
1398 // template int(16) volume = {if track_is_audio 0x0100 else 0};
1399 // const unsigned int(16) reserved = 0;
1400 // template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix
1401 CHECK(avifROStreamSkip(&s, 52));
1402
1403 uint32_t width, height;
1404 CHECK(avifROStreamReadU32(&s, &width)); // unsigned int(32) width;
1405 CHECK(avifROStreamReadU32(&s, &height)); // unsigned int(32) height;
1406 track->width = width >> 16;
1407 track->height = height >> 16;
1408
Joe Dragoae7e2c32019-07-18 15:22:25 -07001409 // TODO: support scaling based on width/height track header info?
1410
1411 track->id = trackID;
1412 return AVIF_TRUE;
1413}
1414
Joe Drago800b47f2020-03-18 16:22:37 -07001415static avifBool avifParseMediaHeaderBox(avifDecoderData * data, avifTrack * track, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001416{
1417 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001418 (void)data;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001419
1420 uint8_t version;
1421 uint8_t flags[3];
Joe Drago345aaa12019-09-25 13:42:12 -07001422 CHECK(avifROStreamReadVersionAndFlags(&s, &version, flags));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001423
1424 uint32_t ignored32, mediaTimescale, mediaDuration32;
1425 uint64_t ignored64, mediaDuration64;
1426 if (version == 1) {
Joe Drago345aaa12019-09-25 13:42:12 -07001427 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
1428 CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
1429 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
1430 CHECK(avifROStreamReadU64(&s, &mediaDuration64)); // unsigned int(64) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001431 track->mediaDuration = mediaDuration64;
1432 } else if (version == 0) {
Joe Drago345aaa12019-09-25 13:42:12 -07001433 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
1434 CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
1435 CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
1436 CHECK(avifROStreamReadU32(&s, &mediaDuration32)); // unsigned int(32) duration;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001437 track->mediaDuration = (uint64_t)mediaDuration32;
1438 } else {
1439 // Unsupported version
1440 return AVIF_FALSE;
1441 }
1442
1443 track->mediaTimescale = mediaTimescale;
1444 return AVIF_TRUE;
1445}
1446
Joe Drago800b47f2020-03-18 16:22:37 -07001447static avifBool avifParseChunkOffsetBox(avifDecoderData * data, avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001448{
1449 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001450 (void)data;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001451
Joe Drago345aaa12019-09-25 13:42:12 -07001452 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001453
1454 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001455 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001456 for (uint32_t i = 0; i < entryCount; ++i) {
1457 uint64_t offset;
1458 if (largeOffsets) {
Joe Drago345aaa12019-09-25 13:42:12 -07001459 CHECK(avifROStreamReadU64(&s, &offset)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001460 } else {
1461 uint32_t offset32;
Joe Drago345aaa12019-09-25 13:42:12 -07001462 CHECK(avifROStreamReadU32(&s, &offset32)); // unsigned int(32) chunk_offset;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001463 offset = (uint64_t)offset32;
1464 }
1465
1466 avifSampleTableChunk * chunk = (avifSampleTableChunk *)avifArrayPushPtr(&sampleTable->chunks);
1467 chunk->offset = offset;
1468 }
1469 return AVIF_TRUE;
1470}
1471
Joe Drago800b47f2020-03-18 16:22:37 -07001472static avifBool avifParseSampleToChunkBox(avifDecoderData * data, avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001473{
1474 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001475 (void)data;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001476
Joe Drago345aaa12019-09-25 13:42:12 -07001477 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001478
1479 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001480 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001481 for (uint32_t i = 0; i < entryCount; ++i) {
1482 avifSampleTableSampleToChunk * sampleToChunk = (avifSampleTableSampleToChunk *)avifArrayPushPtr(&sampleTable->sampleToChunks);
Joe Drago345aaa12019-09-25 13:42:12 -07001483 CHECK(avifROStreamReadU32(&s, &sampleToChunk->firstChunk)); // unsigned int(32) first_chunk;
1484 CHECK(avifROStreamReadU32(&s, &sampleToChunk->samplesPerChunk)); // unsigned int(32) samples_per_chunk;
1485 CHECK(avifROStreamReadU32(&s, &sampleToChunk->sampleDescriptionIndex)); // unsigned int(32) sample_description_index;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001486 }
1487 return AVIF_TRUE;
1488}
1489
Joe Drago800b47f2020-03-18 16:22:37 -07001490static avifBool avifParseSampleSizeBox(avifDecoderData * data, avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001491{
1492 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001493 (void)data;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001494
Joe Drago345aaa12019-09-25 13:42:12 -07001495 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001496
Joe Drago370be3f2020-02-07 15:59:42 -08001497 uint32_t allSamplesSize, sampleCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001498 CHECK(avifROStreamReadU32(&s, &allSamplesSize)); // unsigned int(32) sample_size;
Joe Drago370be3f2020-02-07 15:59:42 -08001499 CHECK(avifROStreamReadU32(&s, &sampleCount)); // unsigned int(32) sample_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001500
Joe Drago370be3f2020-02-07 15:59:42 -08001501 if (allSamplesSize > 0) {
1502 sampleTable->allSamplesSize = allSamplesSize;
1503 } else {
1504 for (uint32_t i = 0; i < sampleCount; ++i) {
1505 avifSampleTableSampleSize * sampleSize = (avifSampleTableSampleSize *)avifArrayPushPtr(&sampleTable->sampleSizes);
Joe Drago345aaa12019-09-25 13:42:12 -07001506 CHECK(avifROStreamReadU32(&s, &sampleSize->size)); // unsigned int(32) entry_size;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001507 }
1508 }
1509 return AVIF_TRUE;
1510}
1511
Joe Drago800b47f2020-03-18 16:22:37 -07001512static avifBool avifParseSyncSampleBox(avifDecoderData * data, avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
Joe Drago22c1ad92019-09-26 12:46:50 -07001513{
1514 BEGIN_STREAM(s, raw, rawLen);
1515 (void)data;
1516
1517 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1518
1519 uint32_t entryCount;
1520 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
1521
1522 for (uint32_t i = 0; i < entryCount; ++i) {
1523 uint32_t sampleNumber = 0;
1524 CHECK(avifROStreamReadU32(&s, &sampleNumber)); // unsigned int(32) sample_number;
1525 avifSyncSample * syncSample = (avifSyncSample *)avifArrayPushPtr(&sampleTable->syncSamples);
1526 syncSample->sampleNumber = sampleNumber;
1527 }
1528 return AVIF_TRUE;
1529}
1530
Joe Drago800b47f2020-03-18 16:22:37 -07001531static avifBool avifParseTimeToSampleBox(avifDecoderData * data, avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001532{
1533 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001534 (void)data;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001535
Joe Drago345aaa12019-09-25 13:42:12 -07001536 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001537
1538 uint32_t entryCount;
Joe Drago345aaa12019-09-25 13:42:12 -07001539 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001540
1541 for (uint32_t i = 0; i < entryCount; ++i) {
1542 avifSampleTableTimeToSample * timeToSample = (avifSampleTableTimeToSample *)avifArrayPushPtr(&sampleTable->timeToSamples);
Joe Drago345aaa12019-09-25 13:42:12 -07001543 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleCount)); // unsigned int(32) sample_count;
1544 CHECK(avifROStreamReadU32(&s, &timeToSample->sampleDelta)); // unsigned int(32) sample_delta;
Joe Dragoae7e2c32019-07-18 15:22:25 -07001545 }
1546 return AVIF_TRUE;
1547}
1548
Joe Drago800b47f2020-03-18 16:22:37 -07001549static avifBool avifParseSampleDescriptionBox(avifDecoderData * data, avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
Joe Drago2c0924c2019-09-26 17:41:01 -07001550{
1551 BEGIN_STREAM(s, raw, rawLen);
1552 (void)data;
1553
1554 CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
1555
1556 uint32_t entryCount;
1557 CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
1558
1559 for (uint32_t i = 0; i < entryCount; ++i) {
1560 avifBoxHeader sampleEntryHeader;
1561 CHECK(avifROStreamReadBoxHeader(&s, &sampleEntryHeader));
1562
1563 avifSampleDescription * description = (avifSampleDescription *)avifArrayPushPtr(&sampleTable->sampleDescriptions);
1564 memcpy(description->format, sampleEntryHeader.type, sizeof(description->format));
Joe Drago6500fd62019-10-08 17:17:34 -07001565 size_t remainingBytes = avifROStreamRemainingBytes(&s);
1566 if (!memcmp(description->format, "av01", 4) && (remainingBytes > VISUALSAMPLEENTRY_SIZE)) {
1567 BEGIN_STREAM(av01Stream, avifROStreamCurrent(&s) + VISUALSAMPLEENTRY_SIZE, remainingBytes - VISUALSAMPLEENTRY_SIZE);
1568 while (avifROStreamHasBytesLeft(&av01Stream, 1)) {
1569 avifBoxHeader av01ChildHeader;
1570 CHECK(avifROStreamReadBoxHeader(&av01Stream, &av01ChildHeader));
1571
1572 if (!memcmp(av01ChildHeader.type, "av1C", 4)) {
1573 CHECK(avifParseAV1CodecConfigurationBox(avifROStreamCurrent(&av01Stream), av01ChildHeader.size, &description->av1C));
1574 description->av1CPresent = AVIF_TRUE;
1575 }
1576
1577 CHECK(avifROStreamSkip(&av01Stream, av01ChildHeader.size));
1578 }
1579 }
Joe Drago2c0924c2019-09-26 17:41:01 -07001580
1581 CHECK(avifROStreamSkip(&s, sampleEntryHeader.size));
1582 }
1583 return AVIF_TRUE;
1584}
1585
Joe Drago800b47f2020-03-18 16:22:37 -07001586static avifBool avifParseSampleTableBox(avifDecoderData * data, avifTrack * track, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001587{
1588 if (track->sampleTable) {
1589 // A TrackBox may only have one SampleTable
1590 return AVIF_FALSE;
1591 }
1592 track->sampleTable = avifSampleTableCreate();
1593
1594 BEGIN_STREAM(s, raw, rawLen);
1595
Joe Drago345aaa12019-09-25 13:42:12 -07001596 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07001597 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001598 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001599
1600 if (!memcmp(header.type, "stco", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001601 CHECK(avifParseChunkOffsetBox(data, track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001602 } else if (!memcmp(header.type, "co64", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001603 CHECK(avifParseChunkOffsetBox(data, track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001604 } else if (!memcmp(header.type, "stsc", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001605 CHECK(avifParseSampleToChunkBox(data, track->sampleTable, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001606 } else if (!memcmp(header.type, "stsz", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001607 CHECK(avifParseSampleSizeBox(data, track->sampleTable, avifROStreamCurrent(&s), header.size));
Joe Drago22c1ad92019-09-26 12:46:50 -07001608 } else if (!memcmp(header.type, "stss", 4)) {
1609 CHECK(avifParseSyncSampleBox(data, track->sampleTable, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001610 } else if (!memcmp(header.type, "stts", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001611 CHECK(avifParseTimeToSampleBox(data, track->sampleTable, avifROStreamCurrent(&s), header.size));
Joe Drago2c0924c2019-09-26 17:41:01 -07001612 } else if (!memcmp(header.type, "stsd", 4)) {
1613 CHECK(avifParseSampleDescriptionBox(data, track->sampleTable, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001614 }
1615
Joe Drago345aaa12019-09-25 13:42:12 -07001616 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001617 }
Joe Dragoae7e2c32019-07-18 15:22:25 -07001618 return AVIF_TRUE;
1619}
1620
Joe Drago800b47f2020-03-18 16:22:37 -07001621static avifBool avifParseMediaInformationBox(avifDecoderData * data, avifTrack * track, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001622{
1623 BEGIN_STREAM(s, raw, rawLen);
1624
Joe Drago345aaa12019-09-25 13:42:12 -07001625 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07001626 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001627 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001628
1629 if (!memcmp(header.type, "stbl", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001630 CHECK(avifParseSampleTableBox(data, track, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001631 }
1632
Joe Drago345aaa12019-09-25 13:42:12 -07001633 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001634 }
1635 return AVIF_TRUE;
1636}
1637
Joe Drago800b47f2020-03-18 16:22:37 -07001638static avifBool avifParseMediaBox(avifDecoderData * data, avifTrack * track, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001639{
1640 BEGIN_STREAM(s, raw, rawLen);
1641
Joe Drago345aaa12019-09-25 13:42:12 -07001642 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07001643 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001644 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001645
1646 if (!memcmp(header.type, "mdhd", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001647 CHECK(avifParseMediaHeaderBox(data, track, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001648 } else if (!memcmp(header.type, "minf", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001649 CHECK(avifParseMediaInformationBox(data, track, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001650 }
1651
Joe Drago345aaa12019-09-25 13:42:12 -07001652 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001653 }
1654 return AVIF_TRUE;
1655}
1656
Joe Drago800b47f2020-03-18 16:22:37 -07001657static avifBool avifTrackReferenceBox(avifDecoderData * data, avifTrack * track, const uint8_t * raw, size_t rawLen)
Joe Drago46ea0582019-07-22 15:55:47 -07001658{
1659 BEGIN_STREAM(s, raw, rawLen);
Joe Drago79d6f362019-07-22 22:36:30 -07001660 (void)data;
Joe Drago46ea0582019-07-22 15:55:47 -07001661
Joe Drago345aaa12019-09-25 13:42:12 -07001662 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago46ea0582019-07-22 15:55:47 -07001663 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001664 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago46ea0582019-07-22 15:55:47 -07001665
1666 if (!memcmp(header.type, "auxl", 4)) {
1667 uint32_t toID;
Joe Drago345aaa12019-09-25 13:42:12 -07001668 CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) track_IDs[]
1669 CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one
Joe Drago46ea0582019-07-22 15:55:47 -07001670 track->auxForID = toID;
1671 } else {
Joe Drago345aaa12019-09-25 13:42:12 -07001672 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago46ea0582019-07-22 15:55:47 -07001673 }
1674 }
1675 return AVIF_TRUE;
1676}
1677
Joe Drago800b47f2020-03-18 16:22:37 -07001678static avifBool avifParseTrackBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001679{
1680 BEGIN_STREAM(s, raw, rawLen);
1681
1682 avifTrack * track = (avifTrack *)avifArrayPushPtr(&data->tracks);
1683
Joe Drago345aaa12019-09-25 13:42:12 -07001684 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07001685 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001686 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001687
1688 if (!memcmp(header.type, "tkhd", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001689 CHECK(avifParseTrackHeaderBox(data, track, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001690 } else if (!memcmp(header.type, "mdia", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001691 CHECK(avifParseMediaBox(data, track, avifROStreamCurrent(&s), header.size));
Joe Drago46ea0582019-07-22 15:55:47 -07001692 } else if (!memcmp(header.type, "tref", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001693 CHECK(avifTrackReferenceBox(data, track, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001694 }
1695
Joe Drago345aaa12019-09-25 13:42:12 -07001696 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001697 }
1698 return AVIF_TRUE;
1699}
1700
Joe Drago800b47f2020-03-18 16:22:37 -07001701static avifBool avifParseMoovBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Dragoae7e2c32019-07-18 15:22:25 -07001702{
1703 BEGIN_STREAM(s, raw, rawLen);
1704
Joe Drago345aaa12019-09-25 13:42:12 -07001705 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Dragoae7e2c32019-07-18 15:22:25 -07001706 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001707 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001708
1709 if (!memcmp(header.type, "trak", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001710 CHECK(avifParseTrackBox(data, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001711 }
1712
Joe Drago345aaa12019-09-25 13:42:12 -07001713 CHECK(avifROStreamSkip(&s, header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001714 }
1715 return AVIF_TRUE;
1716}
1717
Joe Drago345aaa12019-09-25 13:42:12 -07001718static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001719{
1720 BEGIN_STREAM(s, raw, rawLen);
1721
Joe Drago345aaa12019-09-25 13:42:12 -07001722 CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
1723 CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
Joe Drago8f7a3002019-02-07 19:35:37 -08001724
Joe Drago345aaa12019-09-25 13:42:12 -07001725 size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
Joe Drago8f7a3002019-02-07 19:35:37 -08001726 if ((compatibleBrandsBytes % 4) != 0) {
1727 return AVIF_FALSE;
1728 }
1729 if (compatibleBrandsBytes > (4 * MAX_COMPATIBLE_BRANDS)) {
1730 // TODO: stop clamping and resize this
1731 compatibleBrandsBytes = (4 * MAX_COMPATIBLE_BRANDS);
1732 }
Joe Drago345aaa12019-09-25 13:42:12 -07001733 CHECK(avifROStreamRead(&s, ftyp->compatibleBrands, compatibleBrandsBytes));
Joe Drago7e37b972019-07-24 12:44:47 -07001734 ftyp->compatibleBrandsCount = (int)compatibleBrandsBytes / 4;
Joe Drago8f7a3002019-02-07 19:35:37 -08001735
1736 return AVIF_TRUE;
1737}
1738
Joe Drago800b47f2020-03-18 16:22:37 -07001739static avifBool avifParse(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
Joe Drago8f7a3002019-02-07 19:35:37 -08001740{
1741 BEGIN_STREAM(s, raw, rawLen);
1742
Joe Drago345aaa12019-09-25 13:42:12 -07001743 while (avifROStreamHasBytesLeft(&s, 1)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001744 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001745 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago8f7a3002019-02-07 19:35:37 -08001746
1747 if (!memcmp(header.type, "ftyp", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001748 CHECK(avifParseFileTypeBox(&data->ftyp, avifROStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001749 } else if (!memcmp(header.type, "meta", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001750 CHECK(avifParseMetaBox(data, avifROStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -07001751 } else if (!memcmp(header.type, "moov", 4)) {
Joe Drago345aaa12019-09-25 13:42:12 -07001752 CHECK(avifParseMoovBox(data, avifROStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001753 }
1754
Joe Drago345aaa12019-09-25 13:42:12 -07001755 CHECK(avifROStreamSkip(&s, header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -08001756 }
1757 return AVIF_TRUE;
1758}
1759
1760// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -08001761
Joe Drago7e37b972019-07-24 12:44:47 -07001762static avifBool avifFileTypeIsCompatible(avifFileType * ftyp)
1763{
Joe Drago951a0022020-03-09 16:19:44 -07001764 avifBool avifCompatible = (memcmp(ftyp->majorBrand, "avif", 4) == 0);
Joe Drago7e37b972019-07-24 12:44:47 -07001765 if (!avifCompatible) {
Joe Drago951a0022020-03-09 16:19:44 -07001766 avifCompatible = (memcmp(ftyp->majorBrand, "avis", 4) == 0);
Joe Drago2c0924c2019-09-26 17:41:01 -07001767 }
1768 if (!avifCompatible) {
Joe Drago951a0022020-03-09 16:19:44 -07001769 avifCompatible = (memcmp(ftyp->majorBrand, "av01", 4) == 0);
Joe Drago2c0924c2019-09-26 17:41:01 -07001770 }
1771 if (!avifCompatible) {
1772 for (int compatibleBrandIndex = 0; compatibleBrandIndex < ftyp->compatibleBrandsCount; ++compatibleBrandIndex) {
1773 uint8_t * compatibleBrand = &ftyp->compatibleBrands[4 * compatibleBrandIndex];
1774 if (!memcmp(compatibleBrand, "avif", 4)) {
1775 avifCompatible = AVIF_TRUE;
1776 break;
1777 }
1778 if (!memcmp(compatibleBrand, "avis", 4)) {
1779 avifCompatible = AVIF_TRUE;
1780 break;
1781 }
1782 if (!memcmp(compatibleBrand, "av01", 4)) {
1783 avifCompatible = AVIF_TRUE;
1784 break;
Joe Drago7e37b972019-07-24 12:44:47 -07001785 }
1786 }
1787 }
1788 return avifCompatible;
1789}
1790
Joe Drago345aaa12019-09-25 13:42:12 -07001791avifBool avifPeekCompatibleFileType(avifROData * input)
Joe Drago7e37b972019-07-24 12:44:47 -07001792{
1793 BEGIN_STREAM(s, input->data, input->size);
1794
1795 avifBoxHeader header;
Joe Drago345aaa12019-09-25 13:42:12 -07001796 CHECK(avifROStreamReadBoxHeader(&s, &header));
Joe Drago7e37b972019-07-24 12:44:47 -07001797 if (memcmp(header.type, "ftyp", 4) != 0) {
1798 return AVIF_FALSE;
1799 }
1800
1801 avifFileType ftyp;
1802 memset(&ftyp, 0, sizeof(avifFileType));
Joe Drago345aaa12019-09-25 13:42:12 -07001803 avifBool parsed = avifParseFileTypeBox(&ftyp, avifROStreamCurrent(&s), header.size);
Joe Drago7e37b972019-07-24 12:44:47 -07001804 if (!parsed) {
1805 return AVIF_FALSE;
1806 }
1807 return avifFileTypeIsCompatible(&ftyp);
1808}
1809
1810// ---------------------------------------------------------------------------
1811
Joe Drago0b05eee2019-06-12 13:24:39 -07001812avifDecoder * avifDecoderCreate(void)
1813{
1814 avifDecoder * decoder = (avifDecoder *)avifAlloc(sizeof(avifDecoder));
1815 memset(decoder, 0, sizeof(avifDecoder));
1816 return decoder;
1817}
1818
Joe Drago46ea0582019-07-22 15:55:47 -07001819static void avifDecoderCleanup(avifDecoder * decoder)
1820{
1821 if (decoder->data) {
Joe Drago800b47f2020-03-18 16:22:37 -07001822 avifDecoderDataDestroy(decoder->data);
Joe Drago46ea0582019-07-22 15:55:47 -07001823 decoder->data = NULL;
1824 }
1825
1826 if (decoder->image) {
1827 avifImageDestroy(decoder->image);
1828 decoder->image = NULL;
1829 }
1830}
1831
Joe Drago0b05eee2019-06-12 13:24:39 -07001832void avifDecoderDestroy(avifDecoder * decoder)
1833{
Joe Drago46ea0582019-07-22 15:55:47 -07001834 avifDecoderCleanup(decoder);
Joe Drago0b05eee2019-06-12 13:24:39 -07001835 avifFree(decoder);
1836}
1837
Joe Drago46ea0582019-07-22 15:55:47 -07001838avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source)
Joe Drago444f0512019-01-23 17:03:24 -08001839{
Joe Drago46ea0582019-07-22 15:55:47 -07001840 decoder->requestedSource = source;
1841 return avifDecoderReset(decoder);
1842}
Joe Drago33f1d362019-02-13 16:46:22 -08001843
Joe Drago345aaa12019-09-25 13:42:12 -07001844avifResult avifDecoderParse(avifDecoder * decoder, avifROData * rawInput)
Joe Drago46ea0582019-07-22 15:55:47 -07001845{
Joe Drago46ea0582019-07-22 15:55:47 -07001846 // Cleanup anything lingering in the decoder
1847 avifDecoderCleanup(decoder);
1848
Joe Drago444f0512019-01-23 17:03:24 -08001849 // -----------------------------------------------------------------------
1850 // Parse BMFF boxes
1851
Joe Drago800b47f2020-03-18 16:22:37 -07001852 decoder->data = avifDecoderDataCreate();
Joe Drago46ea0582019-07-22 15:55:47 -07001853
1854 // Shallow copy, on purpose
Joe Drago345aaa12019-09-25 13:42:12 -07001855 memcpy(&decoder->data->rawInput, rawInput, sizeof(avifROData));
Joe Drago46ea0582019-07-22 15:55:47 -07001856
1857 if (!avifParse(decoder->data, decoder->data->rawInput.data, decoder->data->rawInput.size)) {
1858 return AVIF_RESULT_BMFF_PARSE_FAILED;
Joe Drago444f0512019-01-23 17:03:24 -08001859 }
1860
Joe Drago7e37b972019-07-24 12:44:47 -07001861 avifBool avifCompatible = avifFileTypeIsCompatible(&decoder->data->ftyp);
Joe Drago8f7a3002019-02-07 19:35:37 -08001862 if (!avifCompatible) {
Joe Drago46ea0582019-07-22 15:55:47 -07001863 return AVIF_RESULT_INVALID_FTYP;
1864 }
1865
1866 // Sanity check items
1867 for (uint32_t itemIndex = 0; itemIndex < decoder->data->items.count; ++itemIndex) {
Joe Drago800b47f2020-03-18 16:22:37 -07001868 avifDecoderItem * item = &decoder->data->items.item[itemIndex];
Joe Drago3320e5f2020-04-21 17:36:27 -07001869 if (item->hasUnsupportedEssentialProperty) {
1870 // An essential property isn't supported by libavif; ignore the item.
1871 continue;
1872 }
Joe Drago800b47f2020-03-18 16:22:37 -07001873 const uint8_t * p = avifDecoderDataCalcItemPtr(decoder->data, item);
Joe Dragof6a42272019-11-21 15:21:41 -08001874 if (p == NULL) {
Joe Drago46ea0582019-07-22 15:55:47 -07001875 return AVIF_RESULT_BMFF_PARSE_FAILED;
1876 }
1877 }
1878
1879 // Sanity check tracks
1880 for (uint32_t trackIndex = 0; trackIndex < decoder->data->tracks.count; ++trackIndex) {
1881 avifTrack * track = &decoder->data->tracks.track[trackIndex];
1882 if (!track->sampleTable) {
1883 continue;
1884 }
1885
1886 for (uint32_t chunkIndex = 0; chunkIndex < track->sampleTable->chunks.count; ++chunkIndex) {
1887 avifSampleTableChunk * chunk = &track->sampleTable->chunks.chunk[chunkIndex];
1888 if (chunk->offset > decoder->data->rawInput.size) {
1889 return AVIF_RESULT_BMFF_PARSE_FAILED;
1890 }
1891 }
1892 }
1893 return avifDecoderReset(decoder);
1894}
1895
Joe Drago53355352019-10-28 19:04:51 -07001896static avifCodec * avifCodecCreateInternal(avifCodecChoice choice, avifCodecDecodeInput * decodeInput)
Joe Drago46ea0582019-07-22 15:55:47 -07001897{
Joe Drago53355352019-10-28 19:04:51 -07001898 avifCodec * codec = avifCodecCreate(choice, AVIF_CODEC_FLAG_CAN_DECODE);
Joe Drago46ea0582019-07-22 15:55:47 -07001899 if (codec) {
1900 codec->decodeInput = decodeInput;
1901 }
1902 return codec;
1903}
1904
Joe Drago22c1ad92019-09-26 12:46:50 -07001905static avifResult avifDecoderFlush(avifDecoder * decoder)
1906{
Joe Drago800b47f2020-03-18 16:22:37 -07001907 avifDecoderDataResetCodec(decoder->data);
Joe Drago22c1ad92019-09-26 12:46:50 -07001908
Joe Drago060d5342020-03-03 10:53:49 -08001909 for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) {
1910 avifTile * tile = &decoder->data->tiles.tile[i];
1911 tile->codec = avifCodecCreateInternal(decoder->codecChoice, tile->input);
1912 if (!tile->codec) {
Joe Drago53355352019-10-28 19:04:51 -07001913 return AVIF_RESULT_NO_CODEC_AVAILABLE;
1914 }
Joe Drago060d5342020-03-03 10:53:49 -08001915 if (!tile->codec->open(tile->codec, decoder->imageIndex + 1)) {
1916 return AVIF_RESULT_DECODE_COLOR_FAILED;
Joe Drago22c1ad92019-09-26 12:46:50 -07001917 }
1918 }
1919 return AVIF_RESULT_OK;
1920}
1921
Joe Drago46ea0582019-07-22 15:55:47 -07001922avifResult avifDecoderReset(avifDecoder * decoder)
1923{
Joe Drago800b47f2020-03-18 16:22:37 -07001924 avifDecoderData * data = decoder->data;
Joe Drago46ea0582019-07-22 15:55:47 -07001925 if (!data) {
1926 // Nothing to reset.
1927 return AVIF_RESULT_OK;
1928 }
1929
Joe Drago060d5342020-03-03 10:53:49 -08001930 memset(&data->colorGrid, 0, sizeof(data->colorGrid));
1931 memset(&data->alphaGrid, 0, sizeof(data->alphaGrid));
Joe Drago800b47f2020-03-18 16:22:37 -07001932 avifDecoderDataClearTiles(data);
Joe Drago89f0cc82020-03-09 16:13:27 -07001933
1934 // Prepare / cleanup decoded image state
Joe Drago46ea0582019-07-22 15:55:47 -07001935 if (!decoder->image) {
1936 decoder->image = avifImageCreateEmpty();
Joe Drago8f7a3002019-02-07 19:35:37 -08001937 }
Joe Drago89f0cc82020-03-09 16:13:27 -07001938 decoder->image->transformFlags = AVIF_TRANSFORM_NONE;
Joe Drago8f7a3002019-02-07 19:35:37 -08001939
Joe Drago70cbf602019-07-24 15:30:55 -07001940 memset(&decoder->ioStats, 0, sizeof(decoder->ioStats));
1941
Joe Drago444f0512019-01-23 17:03:24 -08001942 // -----------------------------------------------------------------------
Joe Drago46ea0582019-07-22 15:55:47 -07001943 // Build decode input
Joe Drago444f0512019-01-23 17:03:24 -08001944
Joe Drago46ea0582019-07-22 15:55:47 -07001945 data->sourceSampleTable = NULL; // Reset
1946 if (decoder->requestedSource == AVIF_DECODER_SOURCE_AUTO) {
1947 if (data->tracks.count > 0) {
1948 data->source = AVIF_DECODER_SOURCE_TRACKS;
1949 } else {
1950 data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM;
Joe Drago76370232019-07-16 11:00:52 -07001951 }
Joe Drago46ea0582019-07-22 15:55:47 -07001952 } else {
1953 data->source = decoder->requestedSource;
Joe Drago76370232019-07-16 11:00:52 -07001954 }
1955
Joe Drago46ea0582019-07-22 15:55:47 -07001956 if (data->source == AVIF_DECODER_SOURCE_TRACKS) {
1957 avifTrack * colorTrack = NULL;
1958 avifTrack * alphaTrack = NULL;
1959
1960 // Find primary track - this probably needs some better detection
1961 uint32_t colorTrackIndex = 0;
1962 for (; colorTrackIndex < decoder->data->tracks.count; ++colorTrackIndex) {
1963 avifTrack * track = &decoder->data->tracks.track[colorTrackIndex];
1964 if (!track->sampleTable) {
1965 continue;
1966 }
1967 if (!track->sampleTable->chunks.count) {
1968 continue;
1969 }
Joe Drago2c0924c2019-09-26 17:41:01 -07001970 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
1971 continue;
1972 }
Joe Drago46ea0582019-07-22 15:55:47 -07001973 if (track->auxForID != 0) {
1974 continue;
1975 }
1976
1977 // Found one!
Joe Drago444f0512019-01-23 17:03:24 -08001978 break;
1979 }
Joe Drago46ea0582019-07-22 15:55:47 -07001980 if (colorTrackIndex == decoder->data->tracks.count) {
1981 return AVIF_RESULT_NO_CONTENT;
Joe Drago444f0512019-01-23 17:03:24 -08001982 }
Joe Drago46ea0582019-07-22 15:55:47 -07001983 colorTrack = &decoder->data->tracks.track[colorTrackIndex];
1984
1985 uint32_t alphaTrackIndex = 0;
1986 for (; alphaTrackIndex < decoder->data->tracks.count; ++alphaTrackIndex) {
1987 avifTrack * track = &decoder->data->tracks.track[alphaTrackIndex];
1988 if (!track->sampleTable) {
1989 continue;
1990 }
1991 if (!track->sampleTable->chunks.count) {
1992 continue;
1993 }
Joe Drago2c0924c2019-09-26 17:41:01 -07001994 if (!avifSampleTableHasFormat(track->sampleTable, "av01")) {
1995 continue;
1996 }
Joe Drago46ea0582019-07-22 15:55:47 -07001997 if (track->auxForID == colorTrack->id) {
1998 // Found it!
1999 break;
2000 }
2001 }
2002 if (alphaTrackIndex != decoder->data->tracks.count) {
2003 alphaTrack = &decoder->data->tracks.track[alphaTrackIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -08002004 }
Joe Drago444f0512019-01-23 17:03:24 -08002005
Joe Drago800b47f2020-03-18 16:22:37 -07002006 avifTile * colorTile = avifDecoderDataCreateTile(decoder->data);
Joe Drago060d5342020-03-03 10:53:49 -08002007 if (!avifCodecDecodeInputGetSamples(colorTile->input, colorTrack->sampleTable, &decoder->data->rawInput)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002008 return AVIF_RESULT_BMFF_PARSE_FAILED;
2009 }
Joe Drago060d5342020-03-03 10:53:49 -08002010 decoder->data->colorTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07002011
Joe Drago060d5342020-03-03 10:53:49 -08002012 avifTile * alphaTile = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07002013 if (alphaTrack) {
Joe Drago800b47f2020-03-18 16:22:37 -07002014 alphaTile = avifDecoderDataCreateTile(decoder->data);
Joe Drago060d5342020-03-03 10:53:49 -08002015 if (!avifCodecDecodeInputGetSamples(alphaTile->input, alphaTrack->sampleTable, &decoder->data->rawInput)) {
Joe Drago46ea0582019-07-22 15:55:47 -07002016 return AVIF_RESULT_BMFF_PARSE_FAILED;
2017 }
Joe Drago060d5342020-03-03 10:53:49 -08002018 alphaTile->input->alpha = AVIF_TRUE;
2019 decoder->data->alphaTileCount = 1;
Joe Drago46ea0582019-07-22 15:55:47 -07002020 }
2021
2022 // Stash off sample table for future timing information
2023 data->sourceSampleTable = colorTrack->sampleTable;
2024
2025 // Image sequence timing
2026 decoder->imageIndex = -1;
Joe Drago060d5342020-03-03 10:53:49 -08002027 decoder->imageCount = colorTile->input->samples.count;
Joe Drago46ea0582019-07-22 15:55:47 -07002028 decoder->timescale = colorTrack->mediaTimescale;
2029 decoder->durationInTimescales = colorTrack->mediaDuration;
2030 if (colorTrack->mediaTimescale) {
2031 decoder->duration = (double)decoder->durationInTimescales / (double)colorTrack->mediaTimescale;
2032 } else {
2033 decoder->duration = 0;
2034 }
2035 memset(&decoder->imageTiming, 0, sizeof(decoder->imageTiming)); // to be set in avifDecoderNextImage()
Joe Drago41700852019-09-26 17:01:43 -07002036
Joe Dragofc4144e2019-09-27 20:35:06 -07002037 decoder->containerWidth = colorTrack->width;
2038 decoder->containerHeight = colorTrack->height;
Joe Drago6500fd62019-10-08 17:17:34 -07002039 decoder->containerDepth = avifSampleTableGetDepth(colorTrack->sampleTable);
Joe Drago46ea0582019-07-22 15:55:47 -07002040 } else {
2041 // Create from items
2042
Joe Drago345aaa12019-09-25 13:42:12 -07002043 avifROData colorOBU = AVIF_DATA_EMPTY;
2044 avifROData alphaOBU = AVIF_DATA_EMPTY;
Joe Dragof6a42272019-11-21 15:21:41 -08002045 avifROData exifData = AVIF_DATA_EMPTY;
2046 avifROData xmpData = AVIF_DATA_EMPTY;
Joe Drago800b47f2020-03-18 16:22:37 -07002047 avifDecoderItem * colorOBUItem = NULL;
2048 avifDecoderItem * alphaOBUItem = NULL;
Joe Drago46ea0582019-07-22 15:55:47 -07002049
Joe Dragof6a42272019-11-21 15:21:41 -08002050 // Find the colorOBU (primary) item
Joe Drago05559c92019-07-17 16:33:38 -07002051 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
Joe Drago800b47f2020-03-18 16:22:37 -07002052 avifDecoderItem * item = &data->items.item[itemIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -08002053 if (!item->id || !item->size) {
2054 break;
2055 }
Joe Drago3320e5f2020-04-21 17:36:27 -07002056 if (item->hasUnsupportedEssentialProperty) {
2057 // An essential property isn't supported by libavif; ignore the item.
2058 continue;
2059 }
Joe Drago951a0022020-03-09 16:19:44 -07002060 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08002061 if (memcmp(item->type, "av01", 4) && !isGrid) {
Joe Drago8f7a3002019-02-07 19:35:37 -08002062 // probably exif or some other data
2063 continue;
2064 }
2065 if (item->thumbnailForID != 0) {
2066 // It's a thumbnail, skip it
2067 continue;
2068 }
Joe Dragof6a42272019-11-21 15:21:41 -08002069 if ((data->primaryItemID > 0) && (item->id != data->primaryItemID)) {
2070 // a primary item ID was specified, require it
2071 continue;
2072 }
Joe Drago8f7a3002019-02-07 19:35:37 -08002073
Joe Drago060d5342020-03-03 10:53:49 -08002074 if (isGrid) {
Joe Drago800b47f2020-03-18 16:22:37 -07002075 const uint8_t * itemPtr = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -08002076 if (itemPtr == NULL) {
2077 return AVIF_RESULT_BMFF_PARSE_FAILED;
2078 }
2079 if (!avifParseImageGridBox(&data->colorGrid, itemPtr, item->size)) {
2080 return AVIF_RESULT_INVALID_IMAGE_GRID;
2081 }
2082 } else {
Joe Drago800b47f2020-03-18 16:22:37 -07002083 colorOBU.data = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -08002084 colorOBU.size = item->size;
2085 }
2086
Joe Drago46ea0582019-07-22 15:55:47 -07002087 colorOBUItem = item;
Joe Drago46ea0582019-07-22 15:55:47 -07002088 break;
2089 }
2090
Joe Drago060d5342020-03-03 10:53:49 -08002091 if (!colorOBUItem) {
2092 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
2093 }
Joe Drago46ea0582019-07-22 15:55:47 -07002094
Joe Drago060d5342020-03-03 10:53:49 -08002095 // Find the alphaOBU item, if any
2096 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
Joe Drago800b47f2020-03-18 16:22:37 -07002097 avifDecoderItem * item = &data->items.item[itemIndex];
Joe Drago060d5342020-03-03 10:53:49 -08002098 if (!item->id || !item->size) {
2099 break;
2100 }
Joe Drago3320e5f2020-04-21 17:36:27 -07002101 if (item->hasUnsupportedEssentialProperty) {
2102 // An essential property isn't supported by libavif; ignore the item.
2103 continue;
2104 }
Joe Drago951a0022020-03-09 16:19:44 -07002105 avifBool isGrid = (memcmp(item->type, "grid", 4) == 0);
Joe Drago060d5342020-03-03 10:53:49 -08002106 if (memcmp(item->type, "av01", 4) && !isGrid) {
2107 // probably exif or some other data
2108 continue;
2109 }
2110 if (item->thumbnailForID != 0) {
2111 // It's a thumbnail, skip it
2112 continue;
Joe Drago8f7a3002019-02-07 19:35:37 -08002113 }
Joe Dragof6a42272019-11-21 15:21:41 -08002114
Joe Drago060d5342020-03-03 10:53:49 -08002115 if (isAlphaURN(item->auxC.auxType) && (item->auxForID == colorOBUItem->id)) {
2116 if (isGrid) {
Joe Drago800b47f2020-03-18 16:22:37 -07002117 const uint8_t * itemPtr = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -08002118 if (itemPtr == NULL) {
2119 return AVIF_RESULT_BMFF_PARSE_FAILED;
2120 }
2121 if (!avifParseImageGridBox(&data->alphaGrid, itemPtr, item->size)) {
2122 return AVIF_RESULT_INVALID_IMAGE_GRID;
2123 }
2124 } else {
Joe Drago800b47f2020-03-18 16:22:37 -07002125 alphaOBU.data = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -08002126 alphaOBU.size = item->size;
Joe Dragof6a42272019-11-21 15:21:41 -08002127 }
2128
Joe Drago060d5342020-03-03 10:53:49 -08002129 alphaOBUItem = item;
2130 break;
Joe Dragof6a42272019-11-21 15:21:41 -08002131 }
Joe Drago444f0512019-01-23 17:03:24 -08002132 }
Joe Drago444f0512019-01-23 17:03:24 -08002133
Joe Drago060d5342020-03-03 10:53:49 -08002134 // Find Exif and/or XMP metadata, if any
2135 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
Joe Drago800b47f2020-03-18 16:22:37 -07002136 avifDecoderItem * item = &data->items.item[itemIndex];
Joe Drago060d5342020-03-03 10:53:49 -08002137 if (!item->id || !item->size) {
2138 break;
2139 }
Joe Drago3320e5f2020-04-21 17:36:27 -07002140 if (item->hasUnsupportedEssentialProperty) {
2141 // An essential property isn't supported by libavif; ignore the item.
2142 continue;
2143 }
Joe Drago060d5342020-03-03 10:53:49 -08002144
2145 if (item->descForID != colorOBUItem->id) {
2146 // Not a content description (metadata) for the colorOBU, skip it
2147 continue;
2148 }
2149
2150 if (!memcmp(item->type, "Exif", 4)) {
2151 // Advance past Annex A.2.1's header
Joe Drago800b47f2020-03-18 16:22:37 -07002152 const uint8_t * boxPtr = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -08002153 BEGIN_STREAM(exifBoxStream, boxPtr, item->size);
2154 uint32_t exifTiffHeaderOffset;
2155 CHECK(avifROStreamReadU32(&exifBoxStream, &exifTiffHeaderOffset)); // unsigned int(32) exif_tiff_header_offset;
2156
2157 exifData.data = avifROStreamCurrent(&exifBoxStream);
2158 exifData.size = avifROStreamRemainingBytes(&exifBoxStream);
2159 }
2160
2161 if (!memcmp(item->type, "mime", 4) && !memcmp(item->contentType.contentType, xmpContentType, xmpContentTypeSize)) {
Joe Drago800b47f2020-03-18 16:22:37 -07002162 xmpData.data = avifDecoderDataCalcItemPtr(data, item);
Joe Drago060d5342020-03-03 10:53:49 -08002163 xmpData.size = item->size;
2164 }
2165 }
2166
Wan-Teh Chang4295bcb2020-04-05 15:41:05 -07002167 if ((data->colorGrid.rows > 0) && (data->colorGrid.columns > 0)) {
Joe Drago800b47f2020-03-18 16:22:37 -07002168 if (!avifDecoderDataGenerateImageGridTiles(data, &data->colorGrid, colorOBUItem, AVIF_FALSE)) {
Joe Drago060d5342020-03-03 10:53:49 -08002169 return AVIF_RESULT_INVALID_IMAGE_GRID;
2170 }
2171 data->colorTileCount = data->tiles.count;
2172 } else {
2173 if (colorOBU.size == 0) {
2174 return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
2175 }
2176
Joe Drago800b47f2020-03-18 16:22:37 -07002177 avifTile * colorTile = avifDecoderDataCreateTile(decoder->data);
Joe Drago060d5342020-03-03 10:53:49 -08002178 avifSample * colorSample = (avifSample *)avifArrayPushPtr(&colorTile->input->samples);
2179 memcpy(&colorSample->data, &colorOBU, sizeof(avifROData));
2180 colorSample->sync = AVIF_TRUE;
2181 decoder->data->colorTileCount = 1;
2182 }
2183
Benbuck Nason78e3c9d2020-03-27 14:35:27 -07002184 if ((data->alphaGrid.rows > 0) && (data->alphaGrid.columns > 0) && alphaOBUItem) {
Joe Drago800b47f2020-03-18 16:22:37 -07002185 if (!avifDecoderDataGenerateImageGridTiles(data, &data->alphaGrid, alphaOBUItem, AVIF_FALSE)) {
Joe Drago060d5342020-03-03 10:53:49 -08002186 return AVIF_RESULT_INVALID_IMAGE_GRID;
2187 }
2188 data->alphaTileCount = data->tiles.count - data->colorTileCount;
2189 } else {
2190 avifTile * alphaTile = NULL;
2191 if (alphaOBU.size > 0) {
Joe Drago800b47f2020-03-18 16:22:37 -07002192 alphaTile = avifDecoderDataCreateTile(decoder->data);
Joe Drago060d5342020-03-03 10:53:49 -08002193
2194 avifSample * alphaSample = (avifSample *)avifArrayPushPtr(&alphaTile->input->samples);
2195 memcpy(&alphaSample->data, &alphaOBU, sizeof(avifROData));
2196 alphaSample->sync = AVIF_TRUE;
2197 alphaTile->input->alpha = AVIF_TRUE;
2198 decoder->data->alphaTileCount = 1;
2199 }
Joe Drago444f0512019-01-23 17:03:24 -08002200 }
Joe Drago33f1d362019-02-13 16:46:22 -08002201
Joe Drago46ea0582019-07-22 15:55:47 -07002202 if (colorOBUItem->colrPresent) {
2203 if (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_ICC) {
2204 avifImageSetProfileICC(decoder->image, colorOBUItem->colr.icc, colorOBUItem->colr.iccSize);
2205 } else if (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_NCLX) {
2206 avifImageSetProfileNCLX(decoder->image, &colorOBUItem->colr.nclx);
2207 }
2208 }
2209
Joe Drago89f0cc82020-03-09 16:13:27 -07002210 // Transformations
2211 if (colorOBUItem->paspPresent) {
2212 decoder->image->transformFlags |= AVIF_TRANSFORM_PASP;
2213 memcpy(&decoder->image->pasp, &colorOBUItem->pasp, sizeof(avifPixelAspectRatioBox));
2214 }
2215 if (colorOBUItem->clapPresent) {
2216 decoder->image->transformFlags |= AVIF_TRANSFORM_CLAP;
2217 memcpy(&decoder->image->clap, &colorOBUItem->clap, sizeof(avifCleanApertureBox));
2218 }
2219 if (colorOBUItem->irotPresent) {
2220 decoder->image->transformFlags |= AVIF_TRANSFORM_IROT;
2221 memcpy(&decoder->image->irot, &colorOBUItem->irot, sizeof(avifImageRotation));
2222 }
2223 if (colorOBUItem->imirPresent) {
2224 decoder->image->transformFlags |= AVIF_TRANSFORM_IMIR;
2225 memcpy(&decoder->image->imir, &colorOBUItem->imir, sizeof(avifImageMirror));
2226 }
2227
Joe Dragof6a42272019-11-21 15:21:41 -08002228 if (exifData.data && exifData.size) {
2229 avifImageSetMetadataExif(decoder->image, exifData.data, exifData.size);
2230 }
2231 if (xmpData.data && xmpData.size) {
2232 avifImageSetMetadataXMP(decoder->image, xmpData.data, xmpData.size);
2233 }
2234
Joe Drago46ea0582019-07-22 15:55:47 -07002235 // Set all counts and timing to safe-but-uninteresting values
2236 decoder->imageIndex = -1;
2237 decoder->imageCount = 1;
2238 decoder->imageTiming.timescale = 1;
2239 decoder->imageTiming.pts = 0;
2240 decoder->imageTiming.ptsInTimescales = 0;
2241 decoder->imageTiming.duration = 1;
2242 decoder->imageTiming.durationInTimescales = 1;
2243 decoder->timescale = 1;
2244 decoder->duration = 1;
2245 decoder->durationInTimescales = 1;
Joe Drago70cbf602019-07-24 15:30:55 -07002246
2247 decoder->ioStats.colorOBUSize = colorOBU.size;
2248 decoder->ioStats.alphaOBUSize = alphaOBU.size;
Joe Drago41700852019-09-26 17:01:43 -07002249
2250 if (colorOBUItem->ispePresent) {
Joe Dragofc4144e2019-09-27 20:35:06 -07002251 decoder->containerWidth = colorOBUItem->ispe.width;
2252 decoder->containerHeight = colorOBUItem->ispe.height;
Joe Drago41700852019-09-26 17:01:43 -07002253 } else {
Joe Dragofc4144e2019-09-27 20:35:06 -07002254 decoder->containerWidth = 0;
2255 decoder->containerHeight = 0;
Joe Drago41700852019-09-26 17:01:43 -07002256 }
Joe Drago6500fd62019-10-08 17:17:34 -07002257 if (colorOBUItem->av1CPresent) {
2258 decoder->containerDepth = avifCodecConfigurationBoxGetDepth(&colorOBUItem->av1C);
2259 } else {
2260 decoder->containerDepth = 0;
2261 }
Joe Drago46ea0582019-07-22 15:55:47 -07002262 }
2263
Joe Drago22c1ad92019-09-26 12:46:50 -07002264 return avifDecoderFlush(decoder);
Joe Drago46ea0582019-07-22 15:55:47 -07002265}
Joe Drago444f0512019-01-23 17:03:24 -08002266
Joe Drago46ea0582019-07-22 15:55:47 -07002267avifResult avifDecoderNextImage(avifDecoder * decoder)
2268{
Joe Drago060d5342020-03-03 10:53:49 -08002269 for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) {
2270 avifTile * tile = &decoder->data->tiles.tile[tileIndex];
Joe Drago41eb62b2019-02-08 15:38:18 -08002271
Joe Drago060d5342020-03-03 10:53:49 -08002272 if (!tile->codec->getNextImage(tile->codec, tile->image)) {
2273 if (tile->input->alpha) {
2274 return AVIF_RESULT_DECODE_ALPHA_FAILED;
2275 } else {
2276 if (tile->image->width) {
2277 // We've sent at least one image, but we've run out now.
2278 return AVIF_RESULT_NO_IMAGES_REMAINING;
2279 }
2280 return AVIF_RESULT_DECODE_COLOR_FAILED;
2281 }
Joe Drago46ea0582019-07-22 15:55:47 -07002282 }
Joe Drago060d5342020-03-03 10:53:49 -08002283 }
2284
2285 if (decoder->data->tiles.count != (decoder->data->colorTileCount + decoder->data->alphaTileCount)) {
2286 // TODO: assert here? This should be impossible.
2287 return AVIF_RESULT_UNKNOWN_ERROR;
2288 }
2289
2290 if ((decoder->data->colorGrid.rows > 0) || (decoder->data->colorGrid.columns > 0)) {
Joe Drago800b47f2020-03-18 16:22:37 -07002291 if (!avifDecoderDataFillImageGrid(
2292 decoder->data, &decoder->data->colorGrid, decoder->image, 0, decoder->data->colorTileCount, AVIF_FALSE)) {
Joe Drago060d5342020-03-03 10:53:49 -08002293 return AVIF_RESULT_INVALID_IMAGE_GRID;
2294 }
2295 } else {
2296 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
2297
2298 if (decoder->data->colorTileCount != 1) {
2299 return AVIF_RESULT_DECODE_COLOR_FAILED;
2300 }
2301
2302 avifImage * srcColor = decoder->data->tiles.tile[0].image;
2303
2304 if ((decoder->image->width != srcColor->width) || (decoder->image->height != srcColor->height) ||
2305 (decoder->image->depth != srcColor->depth)) {
2306 avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL);
2307
2308 decoder->image->width = srcColor->width;
2309 decoder->image->height = srcColor->height;
2310 decoder->image->depth = srcColor->depth;
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09002311
Joe Dragof16cd2d2020-03-09 11:25:37 -07002312 if (decoder->image->profileFormat == AVIF_PROFILE_FORMAT_NONE && srcColor->profileFormat == AVIF_PROFILE_FORMAT_NCLX) {
2313 avifImageSetProfileNCLX(decoder->image, &srcColor->nclx);
psi / Ryo Hirafuji922d8a12020-03-10 03:24:57 +09002314 }
Joe Drago060d5342020-03-03 10:53:49 -08002315 }
2316
2317 avifImageStealPlanes(decoder->image, srcColor, AVIF_PLANES_YUV);
2318 }
2319
2320 if ((decoder->data->alphaGrid.rows > 0) || (decoder->data->alphaGrid.columns > 0)) {
Joe Drago800b47f2020-03-18 16:22:37 -07002321 if (!avifDecoderDataFillImageGrid(
Joe Drago060d5342020-03-03 10:53:49 -08002322 decoder->data, &decoder->data->alphaGrid, decoder->image, decoder->data->colorTileCount, decoder->data->alphaTileCount, AVIF_TRUE)) {
2323 return AVIF_RESULT_INVALID_IMAGE_GRID;
2324 }
2325 } else {
2326 // Normal (most common) non-grid path. Just steal the planes from the only "tile".
2327
2328 if (decoder->data->alphaTileCount == 0) {
2329 avifImageFreePlanes(decoder->image, AVIF_PLANES_A); // no alpha
2330 } else {
2331 if (decoder->data->alphaTileCount != 1) {
2332 return AVIF_RESULT_DECODE_ALPHA_FAILED;
2333 }
2334
2335 avifImage * srcAlpha = decoder->data->tiles.tile[decoder->data->colorTileCount].image;
2336 if ((decoder->image->width != srcAlpha->width) || (decoder->image->height != srcAlpha->height) ||
2337 (decoder->image->depth != srcAlpha->depth)) {
2338 return AVIF_RESULT_DECODE_ALPHA_FAILED;
2339 }
2340
2341 avifImageStealPlanes(decoder->image, srcAlpha, AVIF_PLANES_A);
2342 }
2343 }
Joe Drago7ad3ad62019-02-07 11:17:34 -08002344
Joe Drago46ea0582019-07-22 15:55:47 -07002345 ++decoder->imageIndex;
2346 if (decoder->data->sourceSampleTable) {
2347 // Decoding from a track! Provide timing information.
Joe Drago05559c92019-07-17 16:33:38 -07002348
Joe Dragoe9c58602020-04-13 17:23:13 -07002349 avifResult timingResult = avifDecoderNthImageTiming(decoder, decoder->imageIndex, &decoder->imageTiming);
2350 if (timingResult != AVIF_RESULT_OK) {
2351 return timingResult;
Joe Drago22c1ad92019-09-26 12:46:50 -07002352 }
Joe Dragoe9c58602020-04-13 17:23:13 -07002353 }
2354 return AVIF_RESULT_OK;
2355}
Joe Drago46ea0582019-07-22 15:55:47 -07002356
Joe Dragoe9c58602020-04-13 17:23:13 -07002357avifResult avifDecoderNthImageTiming(avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming)
2358{
2359 if (!decoder->data) {
2360 // Nothing has been parsed yet
2361 return AVIF_RESULT_NO_CONTENT;
2362 }
2363
2364 if ((int)frameIndex >= decoder->imageCount) {
2365 // Impossible index
2366 return AVIF_RESULT_NO_IMAGES_REMAINING;
2367 }
2368
2369 if (!decoder->data->sourceSampleTable) {
2370 // There isn't any real timing associated with this decode, so
2371 // just hand back the defaults chosen in avifDecoderReset().
2372 memcpy(outTiming, &decoder->imageTiming, sizeof(avifImageTiming));
2373 return AVIF_RESULT_OK;
2374 }
2375
Wan-Teh Chang167e4062020-04-14 16:06:12 -07002376 outTiming->timescale = decoder->timescale;
2377 outTiming->ptsInTimescales = 0;
Joe Dragoe9c58602020-04-13 17:23:13 -07002378 for (int imageIndex = 0; imageIndex < (int)frameIndex; ++imageIndex) {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07002379 outTiming->ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07002380 }
Wan-Teh Chang167e4062020-04-14 16:06:12 -07002381 outTiming->durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, frameIndex);
Joe Dragoe9c58602020-04-13 17:23:13 -07002382
Wan-Teh Chang167e4062020-04-14 16:06:12 -07002383 if (outTiming->timescale > 0) {
2384 outTiming->pts = (double)outTiming->ptsInTimescales / (double)outTiming->timescale;
2385 outTiming->duration = (double)outTiming->durationInTimescales / (double)outTiming->timescale;
Joe Dragoe9c58602020-04-13 17:23:13 -07002386 } else {
Wan-Teh Chang167e4062020-04-14 16:06:12 -07002387 outTiming->pts = 0.0;
2388 outTiming->duration = 0.0;
Joe Drago444f0512019-01-23 17:03:24 -08002389 }
Joe Drago46ea0582019-07-22 15:55:47 -07002390 return AVIF_RESULT_OK;
2391}
2392
Joe Drago22c1ad92019-09-26 12:46:50 -07002393avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex)
2394{
2395 int requestedIndex = (int)frameIndex;
2396 if (requestedIndex == decoder->imageIndex) {
2397 // We're here already, nothing to do
2398 return AVIF_RESULT_OK;
2399 }
2400
2401 if (requestedIndex == (decoder->imageIndex + 1)) {
2402 // it's just the next image, nothing special here
2403 return avifDecoderNextImage(decoder);
2404 }
2405
2406 if (requestedIndex >= decoder->imageCount) {
2407 // Impossible index
2408 return AVIF_RESULT_NO_IMAGES_REMAINING;
2409 }
2410
2411 // If we get here, a decoder flush is necessary
Joe Drago22c1ad92019-09-26 12:46:50 -07002412 decoder->imageIndex = ((int)avifDecoderNearestKeyframe(decoder, frameIndex)) - 1; // prepare to read nearest keyframe
Joe Drago81978022019-09-26 18:23:57 -07002413 avifDecoderFlush(decoder);
Joe Drago22c1ad92019-09-26 12:46:50 -07002414 for (;;) {
2415 avifResult result = avifDecoderNextImage(decoder);
2416 if (result != AVIF_RESULT_OK) {
2417 return result;
2418 }
2419
2420 if (requestedIndex == decoder->imageIndex) {
2421 break;
2422 }
Joe Dragofc4144e2019-09-27 20:35:06 -07002423 }
Joe Drago22c1ad92019-09-26 12:46:50 -07002424 return AVIF_RESULT_OK;
2425}
2426
2427avifBool avifDecoderIsKeyframe(avifDecoder * decoder, uint32_t frameIndex)
2428{
Joe Drago060d5342020-03-03 10:53:49 -08002429 if ((decoder->data->tiles.count > 0) && decoder->data->tiles.tile[0].input) {
2430 if (frameIndex < decoder->data->tiles.tile[0].input->samples.count) {
2431 return decoder->data->tiles.tile[0].input->samples.sample[frameIndex].sync;
Joe Drago22c1ad92019-09-26 12:46:50 -07002432 }
2433 }
2434 return AVIF_FALSE;
2435}
2436
2437uint32_t avifDecoderNearestKeyframe(avifDecoder * decoder, uint32_t frameIndex)
2438{
2439 for (; frameIndex != 0; --frameIndex) {
2440 if (avifDecoderIsKeyframe(decoder, frameIndex)) {
2441 break;
2442 }
2443 }
2444 return frameIndex;
2445}
2446
Joe Drago345aaa12019-09-25 13:42:12 -07002447avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image, avifROData * input)
Joe Drago46ea0582019-07-22 15:55:47 -07002448{
2449 avifResult result = avifDecoderParse(decoder, input);
2450 if (result != AVIF_RESULT_OK) {
2451 return result;
Joe Drago05559c92019-07-17 16:33:38 -07002452 }
Joe Drago46ea0582019-07-22 15:55:47 -07002453 result = avifDecoderNextImage(decoder);
2454 if (result != AVIF_RESULT_OK) {
2455 return result;
2456 }
Joe Drago46ea0582019-07-22 15:55:47 -07002457 avifImageCopy(image, decoder->image);
2458 return AVIF_RESULT_OK;
Joe Drago444f0512019-01-23 17:03:24 -08002459}