blob: 022eeb540ccbf3e746450502916bdb586d4c1be9 [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 Drago7efd8032019-02-08 14:49:15 -08008// from the MIAF spec:
9// ---
10// Section 6.7
11// "α is an alpha plane value, scaled into the range of 0 (fully transparent) to 1 (fully opaque), inclusive"
12// ---
13// Section 7.3.5.2
14// "the sample values of the alpha plane divided by the maximum value (e.g. by 255 for 8-bit sample
15// values) provides the multiplier to be used to obtain the intensity for the associated master image"
16// ---
17// The define AVIF_FIX_STUDIO_ALPHA detects when the alpha OBU is incorrectly using studio range
18// and corrects it before returning the alpha pixels to the caller.
19#define AVIF_FIX_STUDIO_ALPHA
20
Joe Dragocd1e4c32019-02-08 11:26:31 -080021#define AUXTYPE_SIZE 64
Joe Drago8f7a3002019-02-07 19:35:37 -080022#define MAX_COMPATIBLE_BRANDS 32
Joe Drago8f7a3002019-02-07 19:35:37 -080023
Joe Dragob13e5722019-02-08 19:07:25 -080024// ---------------------------------------------------------------------------
25// Box data structures
26
27// ftyp
28typedef struct avifFileType
29{
30 uint8_t majorBrand[4];
31 uint32_t minorVersion;
32 uint8_t compatibleBrands[4 * MAX_COMPATIBLE_BRANDS];
33 int compatibleBrandsCount;
34} avifFileType;
35
36// ispe
Joe Drago8f7a3002019-02-07 19:35:37 -080037typedef struct avifImageSpatialExtents
38{
39 uint32_t width;
40 uint32_t height;
41} avifImageSpatialExtents;
Joe Drago444f0512019-01-23 17:03:24 -080042
Joe Dragob13e5722019-02-08 19:07:25 -080043// auxC
Joe Dragocd1e4c32019-02-08 11:26:31 -080044typedef struct avifAuxiliaryType
45{
46 char auxType[AUXTYPE_SIZE];
47} avifAuxiliaryType;
48
Joe Dragob13e5722019-02-08 19:07:25 -080049// colr
Joe Drago41eb62b2019-02-08 15:38:18 -080050typedef struct avifColourInformationBox
51{
52 avifProfileFormat format;
53 uint8_t * icc;
54 size_t iccSize;
Joe Dragoe7ce20d2019-02-11 16:37:38 -080055 avifNclxColorProfile nclx;
Joe Drago41eb62b2019-02-08 15:38:18 -080056} avifColourInformationBox;
57
Joe Dragob13e5722019-02-08 19:07:25 -080058// ---------------------------------------------------------------------------
59// Top-level structures
60
61// one "item" worth (all iref, iloc, iprp, etc refer to one of these)
Joe Drago444f0512019-01-23 17:03:24 -080062typedef struct avifItem
63{
Joe Dragoae7e2c32019-07-18 15:22:25 -070064 uint32_t id;
Joe Drago444f0512019-01-23 17:03:24 -080065 uint8_t type[4];
66 uint32_t offset;
67 uint32_t size;
Joe Drago41eb62b2019-02-08 15:38:18 -080068 avifBool ispePresent;
Joe Drago8f7a3002019-02-07 19:35:37 -080069 avifImageSpatialExtents ispe;
Joe Drago41eb62b2019-02-08 15:38:18 -080070 avifBool auxCPresent;
Joe Dragocd1e4c32019-02-08 11:26:31 -080071 avifAuxiliaryType auxC;
Joe Drago41eb62b2019-02-08 15:38:18 -080072 avifBool colrPresent;
73 avifColourInformationBox colr;
Joe Drago8f7a3002019-02-07 19:35:37 -080074 int thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
Joe Dragocd1e4c32019-02-08 11:26:31 -080075 int auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID}
Joe Drago444f0512019-01-23 17:03:24 -080076} avifItem;
Joe Drago05559c92019-07-17 16:33:38 -070077AVIF_ARRAY_DECLARE(avifItemArray, avifItem, item);
Joe Drago444f0512019-01-23 17:03:24 -080078
Joe Dragob13e5722019-02-08 19:07:25 -080079// Temporary storage for ipco contents until they can be associated and memcpy'd to an avifItem
Joe Drago8f7a3002019-02-07 19:35:37 -080080typedef struct avifProperty
81{
82 uint8_t type[4];
83 avifImageSpatialExtents ispe;
Joe Dragocd1e4c32019-02-08 11:26:31 -080084 avifAuxiliaryType auxC;
Joe Drago41eb62b2019-02-08 15:38:18 -080085 avifColourInformationBox colr;
Joe Drago8f7a3002019-02-07 19:35:37 -080086} avifProperty;
Joe Drago05559c92019-07-17 16:33:38 -070087AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop);
Joe Drago8f7a3002019-02-07 19:35:37 -080088
Joe Dragoae7e2c32019-07-18 15:22:25 -070089// ---------------------------------------------------------------------------
90// avifTrack
91
92typedef struct avifSampleTableChunk
93{
94 uint64_t offset;
95 uint64_t size;
96} avifSampleTableChunk;
97AVIF_ARRAY_DECLARE(avifSampleTableChunkArray, avifSampleTableChunk, chunk);
98
99typedef struct avifSampleTableSampleToChunk
100{
101 uint32_t firstChunk;
102 uint32_t samplesPerChunk;
103 uint32_t sampleDescriptionIndex;
104} avifSampleTableSampleToChunk;
105AVIF_ARRAY_DECLARE(avifSampleTableSampleToChunkArray, avifSampleTableSampleToChunk, sampleToChunk);
106
107typedef struct avifSampleTableSampleSize
108{
109 uint32_t size;
110} avifSampleTableSampleSize;
111AVIF_ARRAY_DECLARE(avifSampleTableSampleSizeArray, avifSampleTableSampleSize, sampleSize);
112
113typedef struct avifSampleTableTimeToSample
114{
115 uint32_t sampleCount;
116 uint32_t sampleDelta;
117} avifSampleTableTimeToSample;
118AVIF_ARRAY_DECLARE(avifSampleTableTimeToSampleArray, avifSampleTableTimeToSample, timeToSample);
119
120typedef struct avifSampleTable
121{
122 avifSampleTableChunkArray chunks;
123 avifSampleTableSampleToChunkArray sampleToChunks;
124 avifSampleTableSampleSizeArray sampleSizes;
125 avifSampleTableTimeToSampleArray timeToSamples;
126} avifSampleTable;
127
128avifSampleTable * avifSampleTableCreate()
129{
130 avifSampleTable * sampleTable = (avifSampleTable *)avifAlloc(sizeof(avifSampleTable));
131 memset(sampleTable, 0, sizeof(avifSampleTable));
132 avifArrayCreate(&sampleTable->chunks, sizeof(avifSampleTableChunk), 16);
133 avifArrayCreate(&sampleTable->sampleToChunks, sizeof(avifSampleTableSampleToChunk), 16);
134 avifArrayCreate(&sampleTable->sampleSizes, sizeof(avifSampleTableSampleSize), 16);
135 avifArrayCreate(&sampleTable->timeToSamples, sizeof(avifSampleTableTimeToSample), 16);
136 return sampleTable;
137}
138
139void avifSampleTableDestroy(avifSampleTable * sampleTable)
140{
141 avifArrayDestroy(&sampleTable->chunks);
142 avifArrayDestroy(&sampleTable->sampleToChunks);
143 avifArrayDestroy(&sampleTable->sampleSizes);
144 avifArrayDestroy(&sampleTable->timeToSamples);
145 avifFree(sampleTable);
146}
147
148// one video track ("trak" contents)
149typedef struct avifTrack
150{
151 uint32_t id;
152 uint32_t auxForID; // if non-zero, this item is an auxC plane for Track #{auxForID}
153 uint32_t mediaTimescale;
154 uint64_t mediaDuration;
155 avifSampleTable * sampleTable;
156} avifTrack;
157AVIF_ARRAY_DECLARE(avifTrackArray, avifTrack, track);
158
159// ---------------------------------------------------------------------------
160// avifData
161
Joe Drago444f0512019-01-23 17:03:24 -0800162typedef struct avifData
163{
Joe Drago8f7a3002019-02-07 19:35:37 -0800164 avifFileType ftyp;
Joe Drago05559c92019-07-17 16:33:38 -0700165 avifItemArray items;
166 avifPropertyArray properties;
Joe Dragoae7e2c32019-07-18 15:22:25 -0700167 avifTrackArray tracks;
Joe Drago8f7a3002019-02-07 19:35:37 -0800168 int propertyCount;
Joe Drago444f0512019-01-23 17:03:24 -0800169} avifData;
170
Joe Drago05559c92019-07-17 16:33:38 -0700171avifData * avifDataCreate()
172{
173 avifData * data = (avifData *)avifAlloc(sizeof(avifData));
174 memset(data, 0, sizeof(avifData));
175 avifArrayCreate(&data->items, sizeof(avifItem), 8);
176 avifArrayCreate(&data->properties, sizeof(avifProperty), 16);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700177 avifArrayCreate(&data->tracks, sizeof(avifTrack), 2);
Joe Drago05559c92019-07-17 16:33:38 -0700178 return data;
179}
180
181void avifDataDestroy(avifData * data)
182{
183 avifArrayDestroy(&data->items);
184 avifArrayDestroy(&data->properties);
Joe Dragoae7e2c32019-07-18 15:22:25 -0700185 for (uint32_t i = 0; i < data->tracks.count; ++i) {
186 if (data->tracks.track[i].sampleTable) {
187 avifSampleTableDestroy(data->tracks.track[i].sampleTable);
188 }
189 }
190 avifArrayDestroy(&data->tracks);
Joe Drago05559c92019-07-17 16:33:38 -0700191 avifFree(data);
192}
193
Joe Dragoae7e2c32019-07-18 15:22:25 -0700194avifItem * avifDataFindItem(avifData * data, uint32_t itemID)
Joe Drago444f0512019-01-23 17:03:24 -0800195{
196 if (itemID == 0) {
Joe Drago05559c92019-07-17 16:33:38 -0700197 return NULL;
Joe Drago444f0512019-01-23 17:03:24 -0800198 }
199
Joe Drago05559c92019-07-17 16:33:38 -0700200 for (uint32_t i = 0; i < data->items.count; ++i) {
201 if (data->items.item[i].id == itemID) {
202 return &data->items.item[i];
Joe Drago444f0512019-01-23 17:03:24 -0800203 }
204 }
205
Joe Drago05559c92019-07-17 16:33:38 -0700206 avifItem * item = (avifItem *)avifArrayPushPtr(&data->items);
207 item->id = itemID;
208 return item;
Joe Drago444f0512019-01-23 17:03:24 -0800209}
210
Joe Drago8f7a3002019-02-07 19:35:37 -0800211// ---------------------------------------------------------------------------
Joe Dragocd1e4c32019-02-08 11:26:31 -0800212// URN
213
214static avifBool isAlphaURN(char * urn)
215{
Joe Drago97b071c2019-07-17 14:24:56 -0700216 if (!strcmp(urn, URN_ALPHA0))
217 return AVIF_TRUE;
218 if (!strcmp(urn, URN_ALPHA1))
219 return AVIF_TRUE;
Joe Dragocd1e4c32019-02-08 11:26:31 -0800220 return AVIF_FALSE;
221}
222
223// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -0800224// BMFF Parsing
225
Joe Drago7a9a6612019-07-17 11:21:24 -0700226#define BEGIN_STREAM(VARNAME, PTR, SIZE) \
227 avifStream VARNAME; \
228 avifRawData VARNAME##_rawData = { PTR, SIZE }; \
229 avifStreamStart(&VARNAME, &VARNAME##_rawData)
Joe Drago8f7a3002019-02-07 19:35:37 -0800230
231static avifBool avifParseItemLocationBox(avifData * data, uint8_t * raw, size_t rawLen)
Joe Drago444f0512019-01-23 17:03:24 -0800232{
Joe Drago8f7a3002019-02-07 19:35:37 -0800233 BEGIN_STREAM(s, raw, rawLen);
Joe Drago444f0512019-01-23 17:03:24 -0800234
Joe Drago8f7a3002019-02-07 19:35:37 -0800235 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
Joe Drago444f0512019-01-23 17:03:24 -0800236
Joe Drago8f7a3002019-02-07 19:35:37 -0800237 uint8_t offsetSizeAndLengthSize;
238 CHECK(avifStreamRead(&s, &offsetSizeAndLengthSize, 1));
239 uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
240 uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
Joe Drago444f0512019-01-23 17:03:24 -0800241
Joe Drago8f7a3002019-02-07 19:35:37 -0800242 uint8_t baseOffsetSizeAndReserved;
243 CHECK(avifStreamRead(&s, &baseOffsetSizeAndReserved, 1));
244 uint8_t baseOffsetSize = (baseOffsetSizeAndReserved >> 4) & 0xf; // unsigned int(4) base_offset_size;
Joe Drago444f0512019-01-23 17:03:24 -0800245
Joe Drago8f7a3002019-02-07 19:35:37 -0800246 uint16_t itemCount;
247 CHECK(avifStreamReadU16(&s, &itemCount)); // unsigned int(16) item_count;
Joe Dragoea252af2019-02-12 12:08:38 -0800248 for (int i = 0; i < itemCount; ++i) {
Joe Drago8f7a3002019-02-07 19:35:37 -0800249 uint16_t itemID; // unsigned int(16) item_ID;
250 CHECK(avifStreamReadU16(&s, &itemID)); //
251 uint16_t dataReferenceIndex; // unsigned int(16) data_reference_index;
252 CHECK(avifStreamReadU16(&s, &dataReferenceIndex)); //
253 uint64_t baseOffset; // unsigned int(base_offset_size*8) base_offset;
254 CHECK(avifStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
255 uint16_t extentCount; // unsigned int(16) extent_count;
256 CHECK(avifStreamReadU16(&s, &extentCount)); //
257 if (extentCount == 1) {
258 uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
259 CHECK(avifStreamReadUX8(&s, &extentOffset, offsetSize));
260 uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
261 CHECK(avifStreamReadUX8(&s, &extentLength, lengthSize));
Joe Drago444f0512019-01-23 17:03:24 -0800262
Joe Drago05559c92019-07-17 16:33:38 -0700263 avifItem * item = avifDataFindItem(data, itemID);
264 item->id = itemID;
265 item->offset = (uint32_t)(baseOffset + extentOffset);
266 item->size = (uint32_t)extentLength;
Joe Drago444f0512019-01-23 17:03:24 -0800267 } else {
Joe Drago8f7a3002019-02-07 19:35:37 -0800268 // TODO: support more than one extent
269 return AVIF_FALSE;
Joe Drago444f0512019-01-23 17:03:24 -0800270 }
271 }
272 return AVIF_TRUE;
273}
274
Joe Drago8f7a3002019-02-07 19:35:37 -0800275static avifBool avifParseImageSpatialExtentsProperty(avifData * data, uint8_t * raw, size_t rawLen, int propertyIndex)
276{
277 BEGIN_STREAM(s, raw, rawLen);
Joe Dragocd1e4c32019-02-08 11:26:31 -0800278 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -0800279
Joe Drago05559c92019-07-17 16:33:38 -0700280 CHECK(avifStreamReadU32(&s, &data->properties.prop[propertyIndex].ispe.width));
281 CHECK(avifStreamReadU32(&s, &data->properties.prop[propertyIndex].ispe.height));
Joe Drago8f7a3002019-02-07 19:35:37 -0800282 return AVIF_TRUE;
283}
284
Joe Dragocd1e4c32019-02-08 11:26:31 -0800285static avifBool avifParseAuxiliaryTypeProperty(avifData * data, uint8_t * raw, size_t rawLen, int propertyIndex)
286{
287 BEGIN_STREAM(s, raw, rawLen);
288 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
289
Joe Drago05559c92019-07-17 16:33:38 -0700290 CHECK(avifStreamReadString(&s, data->properties.prop[propertyIndex].auxC.auxType, AUXTYPE_SIZE));
Joe Dragocd1e4c32019-02-08 11:26:31 -0800291 return AVIF_TRUE;
292}
293
Joe Drago41eb62b2019-02-08 15:38:18 -0800294static avifBool avifParseColourInformationBox(avifData * data, uint8_t * raw, size_t rawLen, int propertyIndex)
295{
296 BEGIN_STREAM(s, raw, rawLen);
Joe Dragoe7ce20d2019-02-11 16:37:38 -0800297
Joe Drago05559c92019-07-17 16:33:38 -0700298 data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NONE;
Joe Dragoe7ce20d2019-02-11 16:37:38 -0800299
Joe Drago41eb62b2019-02-08 15:38:18 -0800300 uint8_t colourType[4]; // unsigned int(32) colour_type;
301 CHECK(avifStreamRead(&s, colourType, 4));
302 if (!memcmp(colourType, "rICC", 4) || !memcmp(colourType, "prof", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700303 data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_ICC;
304 data->properties.prop[propertyIndex].colr.icc = avifStreamCurrent(&s);
305 data->properties.prop[propertyIndex].colr.iccSize = avifStreamRemainingBytes(&s);
Joe Dragoe7ce20d2019-02-11 16:37:38 -0800306 } else if (!memcmp(colourType, "nclx", 4)) {
Joe Drago97b071c2019-07-17 14:24:56 -0700307 // unsigned int(16) colour_primaries;
Joe Drago05559c92019-07-17 16:33:38 -0700308 CHECK(avifStreamReadU16(&s, &data->properties.prop[propertyIndex].colr.nclx.colourPrimaries));
Joe Drago97b071c2019-07-17 14:24:56 -0700309 // unsigned int(16) transfer_characteristics;
Joe Drago05559c92019-07-17 16:33:38 -0700310 CHECK(avifStreamReadU16(&s, &data->properties.prop[propertyIndex].colr.nclx.transferCharacteristics));
Joe Drago97b071c2019-07-17 14:24:56 -0700311 // unsigned int(16) matrix_coefficients;
Joe Drago05559c92019-07-17 16:33:38 -0700312 CHECK(avifStreamReadU16(&s, &data->properties.prop[propertyIndex].colr.nclx.matrixCoefficients));
Joe Drago97b071c2019-07-17 14:24:56 -0700313 // unsigned int(1) full_range_flag;
314 // unsigned int(7) reserved = 0;
Joe Drago05559c92019-07-17 16:33:38 -0700315 CHECK(avifStreamRead(&s, &data->properties.prop[propertyIndex].colr.nclx.fullRangeFlag, 1));
316 data->properties.prop[propertyIndex].colr.nclx.fullRangeFlag |= 0x80;
317 data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NCLX;
Joe Drago41eb62b2019-02-08 15:38:18 -0800318 }
319 return AVIF_TRUE;
320}
321
Joe Drago8f7a3002019-02-07 19:35:37 -0800322static avifBool avifParseItemPropertyContainerBox(avifData * data, uint8_t * raw, size_t rawLen)
323{
324 BEGIN_STREAM(s, raw, rawLen);
325
326 data->propertyCount = 0;
327
328 while (avifStreamHasBytesLeft(&s, 1)) {
329 avifBoxHeader header;
330 CHECK(avifStreamReadBoxHeader(&s, &header));
331
Joe Drago05559c92019-07-17 16:33:38 -0700332 int propertyIndex = avifArrayPushIndex(&data->properties);
333 memcpy(data->properties.prop[propertyIndex].type, header.type, 4);
Joe Drago8f7a3002019-02-07 19:35:37 -0800334 if (!memcmp(header.type, "ispe", 4)) {
335 CHECK(avifParseImageSpatialExtentsProperty(data, avifStreamCurrent(&s), header.size, propertyIndex));
336 }
Joe Dragocd1e4c32019-02-08 11:26:31 -0800337 if (!memcmp(header.type, "auxC", 4)) {
338 CHECK(avifParseAuxiliaryTypeProperty(data, avifStreamCurrent(&s), header.size, propertyIndex));
339 }
Joe Drago41eb62b2019-02-08 15:38:18 -0800340 if (!memcmp(header.type, "colr", 4)) {
341 CHECK(avifParseColourInformationBox(data, avifStreamCurrent(&s), header.size, propertyIndex));
342 }
Joe Drago8f7a3002019-02-07 19:35:37 -0800343
344 CHECK(avifStreamSkip(&s, header.size));
345 }
346 return AVIF_TRUE;
347}
348
349static avifBool avifParseItemPropertyAssociation(avifData * data, uint8_t * raw, size_t rawLen)
350{
351 BEGIN_STREAM(s, raw, rawLen);
352
353 uint8_t version;
354 uint8_t flags[3];
355 CHECK(avifStreamReadVersionAndFlags(&s, &version, flags));
356 avifBool propertyIndexIsU16 = (flags[2] & 0x1) ? AVIF_TRUE : AVIF_FALSE; // is flags[2] correct?
357
358 uint32_t entryCount;
359 CHECK(avifStreamReadU32(&s, &entryCount));
360 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
361 unsigned int itemID;
362 if (version < 1) {
363 uint16_t tmp;
364 CHECK(avifStreamReadU16(&s, &tmp));
365 itemID = tmp;
366 } else {
367 CHECK(avifStreamReadU32(&s, &itemID));
368 }
369 uint8_t associationCount;
370 CHECK(avifStreamRead(&s, &associationCount, 1));
371 for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) {
Joe Drago64d26e82019-07-12 15:50:27 -0700372 // avifBool essential = AVIF_FALSE; // currently unused
Joe Drago8f7a3002019-02-07 19:35:37 -0800373 uint16_t propertyIndex = 0;
374 if (propertyIndexIsU16) {
375 CHECK(avifStreamReadU16(&s, &propertyIndex));
Joe Drago64d26e82019-07-12 15:50:27 -0700376 // essential = (propertyIndex & 0x8000) ? AVIF_TRUE : AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -0800377 propertyIndex &= 0x7fff;
378 } else {
379 uint8_t tmp;
380 CHECK(avifStreamRead(&s, &tmp, 1));
Joe Drago64d26e82019-07-12 15:50:27 -0700381 // essential = (tmp & 0x80) ? AVIF_TRUE : AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -0800382 propertyIndex = tmp & 0x7f;
383 }
384
385 if (propertyIndex == 0) {
386 // Not associated with any item
387 continue;
388 }
389 --propertyIndex; // 1-indexed
390
Joe Drago05559c92019-07-17 16:33:38 -0700391 if (propertyIndex >= data->properties.count) {
Joe Drago8f7a3002019-02-07 19:35:37 -0800392 return AVIF_FALSE;
393 }
394
Joe Drago05559c92019-07-17 16:33:38 -0700395 avifItem * item = avifDataFindItem(data, itemID);
Joe Drago8f7a3002019-02-07 19:35:37 -0800396
397 // Associate property with item
Joe Drago05559c92019-07-17 16:33:38 -0700398 avifProperty * prop = &data->properties.prop[propertyIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -0800399 if (!memcmp(prop->type, "ispe", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700400 item->ispePresent = AVIF_TRUE;
401 memcpy(&item->ispe, &prop->ispe, sizeof(avifImageSpatialExtents));
Joe Dragocd1e4c32019-02-08 11:26:31 -0800402 } else if (!memcmp(prop->type, "auxC", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700403 item->auxCPresent = AVIF_TRUE;
404 memcpy(&item->auxC, &prop->auxC, sizeof(avifAuxiliaryType));
Joe Drago41eb62b2019-02-08 15:38:18 -0800405 } else if (!memcmp(prop->type, "colr", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700406 item->colrPresent = AVIF_TRUE;
407 memcpy(&item->colr, &prop->colr, sizeof(avifColourInformationBox));
Joe Drago8f7a3002019-02-07 19:35:37 -0800408 }
409 }
410 }
411
412 return AVIF_TRUE;
413}
414
415static avifBool avifParseItemPropertiesBox(avifData * data, uint8_t * raw, size_t rawLen)
416{
417 BEGIN_STREAM(s, raw, rawLen);
418
419 avifBoxHeader ipcoHeader;
420 CHECK(avifStreamReadBoxHeader(&s, &ipcoHeader));
421 if (memcmp(ipcoHeader.type, "ipco", 4) != 0) {
422 return AVIF_FALSE;
423 }
424
425 // Read all item properties inside of ItemPropertyContainerBox
426 CHECK(avifParseItemPropertyContainerBox(data, avifStreamCurrent(&s), ipcoHeader.size));
427 CHECK(avifStreamSkip(&s, ipcoHeader.size));
428
429 // Now read all ItemPropertyAssociation until the end of the box, and make associations
430 while (avifStreamHasBytesLeft(&s, 1)) {
431 avifBoxHeader ipmaHeader;
432 CHECK(avifStreamReadBoxHeader(&s, &ipmaHeader));
433
434 if (!memcmp(ipmaHeader.type, "ipma", 4)) {
435 CHECK(avifParseItemPropertyAssociation(data, avifStreamCurrent(&s), ipmaHeader.size));
436 } else {
437 // These must all be type ipma
438 return AVIF_FALSE;
439 }
440
441 CHECK(avifStreamSkip(&s, ipmaHeader.size));
442 }
443 return AVIF_TRUE;
444}
445
446static avifBool avifParseItemInfoEntry(avifData * data, uint8_t * raw, size_t rawLen)
447{
448 BEGIN_STREAM(s, raw, rawLen);
449
450 CHECK(avifStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
451
452 uint16_t itemID; // unsigned int(16) item_ID;
453 CHECK(avifStreamReadU16(&s, &itemID)); //
454 uint16_t itemProtectionIndex; // unsigned int(16) item_protection_index;
455 CHECK(avifStreamReadU16(&s, &itemProtectionIndex)); //
456 uint8_t itemType[4]; // unsigned int(32) item_type;
457 CHECK(avifStreamRead(&s, itemType, 4)); //
458
Joe Drago05559c92019-07-17 16:33:38 -0700459 avifItem * item = avifDataFindItem(data, itemID);
460 memcpy(item->type, itemType, sizeof(itemType));
Joe Drago8f7a3002019-02-07 19:35:37 -0800461 return AVIF_TRUE;
462}
463
464static avifBool avifParseItemInfoBox(avifData * data, uint8_t * raw, size_t rawLen)
465{
466 BEGIN_STREAM(s, raw, rawLen);
467
468 uint8_t version;
469 CHECK(avifStreamReadVersionAndFlags(&s, &version, NULL));
470 uint32_t entryCount;
471 if (version == 0) {
472 uint16_t tmp;
473 CHECK(avifStreamReadU16(&s, &tmp)); // unsigned int(16) entry_count;
474 entryCount = tmp;
475 } else if (version == 1) {
476 CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(16) entry_count;
477 } else {
478 return AVIF_FALSE;
479 }
480
Joe Drago678b9382019-02-09 03:17:47 -0800481 for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
Joe Drago8f7a3002019-02-07 19:35:37 -0800482 avifBoxHeader infeHeader;
483 CHECK(avifStreamReadBoxHeader(&s, &infeHeader));
484
485 if (!memcmp(infeHeader.type, "infe", 4)) {
486 CHECK(avifParseItemInfoEntry(data, avifStreamCurrent(&s), infeHeader.size));
487 } else {
488 // These must all be type ipma
489 return AVIF_FALSE;
490 }
491
492 CHECK(avifStreamSkip(&s, infeHeader.size));
493 }
494
495 return AVIF_TRUE;
496}
497
498static avifBool avifParseItemReferenceBox(avifData * data, uint8_t * raw, size_t rawLen)
499{
500 BEGIN_STREAM(s, raw, rawLen);
501
502 uint8_t version;
503 CHECK(avifStreamReadVersionAndFlags(&s, &version, NULL));
504
505 while (avifStreamHasBytesLeft(&s, 1)) {
506 avifBoxHeader irefHeader;
507 CHECK(avifStreamReadBoxHeader(&s, &irefHeader));
508
509 uint32_t fromID = 0;
510 if (version == 0) {
511 uint16_t tmp;
512 CHECK(avifStreamReadU16(&s, &tmp)); // unsigned int(16) from_item_ID;
513 fromID = tmp;
514 } else if (version == 1) {
515 CHECK(avifStreamReadU32(&s, &fromID)); // unsigned int(32) from_item_ID;
516 } else {
517 // unsupported iref version, skip it
518 break;
519 }
520
521 uint16_t referenceCount = 0;
522 CHECK(avifStreamReadU16(&s, &referenceCount)); // unsigned int(16) reference_count;
523
524 for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) {
525 uint32_t toID = 0;
526 if (version == 0) {
527 uint16_t tmp;
528 CHECK(avifStreamReadU16(&s, &tmp)); // unsigned int(16) to_item_ID;
529 toID = tmp;
530 } else if (version == 1) {
531 CHECK(avifStreamReadU32(&s, &toID)); // unsigned int(32) to_item_ID;
532 } else {
533 // unsupported iref version, skip it
534 break;
535 }
536
537 // Read this reference as "{fromID} is a {irefType} for {toID}"
538 if (fromID && toID) {
Joe Drago05559c92019-07-17 16:33:38 -0700539 avifItem * item = avifDataFindItem(data, fromID);
Joe Drago8f7a3002019-02-07 19:35:37 -0800540 if (!memcmp(irefHeader.type, "thmb", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700541 item->thumbnailForID = toID;
Joe Drago8f7a3002019-02-07 19:35:37 -0800542 }
543 if (!memcmp(irefHeader.type, "auxl", 4)) {
Joe Drago05559c92019-07-17 16:33:38 -0700544 item->auxForID = toID;
Joe Drago8f7a3002019-02-07 19:35:37 -0800545 }
546 }
547 }
548 }
549
550 return AVIF_TRUE;
551}
552
553static avifBool avifParseMetaBox(avifData * data, uint8_t * raw, size_t rawLen)
554{
555 BEGIN_STREAM(s, raw, rawLen);
556
Joe Dragocd1e4c32019-02-08 11:26:31 -0800557 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
Joe Drago8f7a3002019-02-07 19:35:37 -0800558
559 while (avifStreamHasBytesLeft(&s, 1)) {
560 avifBoxHeader header;
561 CHECK(avifStreamReadBoxHeader(&s, &header));
562
563 if (!memcmp(header.type, "iloc", 4)) {
564 CHECK(avifParseItemLocationBox(data, avifStreamCurrent(&s), header.size));
565 } else if (!memcmp(header.type, "iprp", 4)) {
566 CHECK(avifParseItemPropertiesBox(data, avifStreamCurrent(&s), header.size));
567 } else if (!memcmp(header.type, "iinf", 4)) {
568 CHECK(avifParseItemInfoBox(data, avifStreamCurrent(&s), header.size));
569 } else if (!memcmp(header.type, "iref", 4)) {
570 CHECK(avifParseItemReferenceBox(data, avifStreamCurrent(&s), header.size));
571 }
572
573 CHECK(avifStreamSkip(&s, header.size));
574 }
575 return AVIF_TRUE;
576}
577
Joe Dragoae7e2c32019-07-18 15:22:25 -0700578static avifBool avifParseTrackHeaderBox(avifData * data, avifTrack * track, uint8_t * raw, size_t rawLen)
579{
580 BEGIN_STREAM(s, raw, rawLen);
581
582 uint8_t version;
583 uint8_t flags[3];
584 CHECK(avifStreamReadVersionAndFlags(&s, &version, flags));
585
586 uint32_t ignored32, trackID;
587 uint64_t ignored64;
588 if (version == 1) {
589 CHECK(avifStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
590 CHECK(avifStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
591 CHECK(avifStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
592 } else if (version == 0) {
593 CHECK(avifStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
594 CHECK(avifStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
595 CHECK(avifStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID;
596 } else {
597 // Unsupported version
598 return AVIF_FALSE;
599 }
600
601 // TODO: support scaling based on width/height track header info?
602
603 track->id = trackID;
604 return AVIF_TRUE;
605}
606
607static avifBool avifParseMediaHeaderBox(avifData * data, avifTrack * track, uint8_t * raw, size_t rawLen)
608{
609 BEGIN_STREAM(s, raw, rawLen);
610
611 uint8_t version;
612 uint8_t flags[3];
613 CHECK(avifStreamReadVersionAndFlags(&s, &version, flags));
614
615 uint32_t ignored32, mediaTimescale, mediaDuration32;
616 uint64_t ignored64, mediaDuration64;
617 if (version == 1) {
618 CHECK(avifStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time;
619 CHECK(avifStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time;
620 CHECK(avifStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
621 CHECK(avifStreamReadU64(&s, &mediaDuration64)); // unsigned int(64) duration;
622 track->mediaDuration = mediaDuration64;
623 } else if (version == 0) {
624 CHECK(avifStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time;
625 CHECK(avifStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time;
626 CHECK(avifStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale;
627 CHECK(avifStreamReadU32(&s, &mediaDuration32)); // unsigned int(32) duration;
628 track->mediaDuration = (uint64_t)mediaDuration32;
629 } else {
630 // Unsupported version
631 return AVIF_FALSE;
632 }
633
634 track->mediaTimescale = mediaTimescale;
635 return AVIF_TRUE;
636}
637
638static avifBool avifParseChunkOffsetBox(avifData * data, avifSampleTable * sampleTable, avifBool largeOffsets, uint8_t * raw, size_t rawLen)
639{
640 BEGIN_STREAM(s, raw, rawLen);
641
642 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
643
644 uint32_t entryCount;
645 CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
646 for (uint32_t i = 0; i < entryCount; ++i) {
647 uint64_t offset;
648 if (largeOffsets) {
649 CHECK(avifStreamReadU64(&s, &offset)); // unsigned int(32) chunk_offset;
650 } else {
651 uint32_t offset32;
652 CHECK(avifStreamReadU32(&s, &offset32)); // unsigned int(32) chunk_offset;
653 offset = (uint64_t)offset32;
654 }
655
656 avifSampleTableChunk * chunk = (avifSampleTableChunk *)avifArrayPushPtr(&sampleTable->chunks);
657 chunk->offset = offset;
658 }
659 return AVIF_TRUE;
660}
661
662static avifBool avifParseSampleToChunkBox(avifData * data, avifSampleTable * sampleTable, uint8_t * raw, size_t rawLen)
663{
664 BEGIN_STREAM(s, raw, rawLen);
665
666 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
667
668 uint32_t entryCount;
669 CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
670 for (uint32_t i = 0; i < entryCount; ++i) {
671 avifSampleTableSampleToChunk * sampleToChunk = (avifSampleTableSampleToChunk *)avifArrayPushPtr(&sampleTable->sampleToChunks);
672 CHECK(avifStreamReadU32(&s, &sampleToChunk->firstChunk)); // unsigned int(32) first_chunk;
673 CHECK(avifStreamReadU32(&s, &sampleToChunk->samplesPerChunk)); // unsigned int(32) samples_per_chunk;
674 CHECK(avifStreamReadU32(&s, &sampleToChunk->sampleDescriptionIndex)); // unsigned int(32) sample_description_index;
675 }
676 return AVIF_TRUE;
677}
678
679static avifBool avifParseSampleSizeBox(avifData * data, avifSampleTable * sampleTable, uint8_t * raw, size_t rawLen)
680{
681 BEGIN_STREAM(s, raw, rawLen);
682
683 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
684
685 uint32_t allSamplesSize, entryCount;
686 CHECK(avifStreamReadU32(&s, &allSamplesSize)); // unsigned int(32) sample_size;
687 CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
688
689 for (uint32_t i = 0; i < entryCount; ++i) {
690 avifSampleTableSampleSize * sampleSize = (avifSampleTableSampleSize *)avifArrayPushPtr(&sampleTable->sampleSizes);
691 if (allSamplesSize == 0) {
692 CHECK(avifStreamReadU32(&s, &sampleSize->size)); // unsigned int(32) entry_size;
693 } else {
694 // This could be done more efficiently, memory-wise.
695 sampleSize->size = allSamplesSize;
696 }
697 }
698 return AVIF_TRUE;
699}
700
701static avifBool avifParseTimeToSampleBox(avifData * data, avifSampleTable * sampleTable, uint8_t * raw, size_t rawLen)
702{
703 BEGIN_STREAM(s, raw, rawLen);
704
705 CHECK(avifStreamReadAndEnforceVersion(&s, 0));
706
707 uint32_t entryCount;
708 CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count;
709
710 for (uint32_t i = 0; i < entryCount; ++i) {
711 avifSampleTableTimeToSample * timeToSample = (avifSampleTableTimeToSample *)avifArrayPushPtr(&sampleTable->timeToSamples);
712 CHECK(avifStreamReadU32(&s, &timeToSample->sampleCount)); // unsigned int(32) sample_count;
713 CHECK(avifStreamReadU32(&s, &timeToSample->sampleDelta)); // unsigned int(32) sample_delta;
714 }
715 return AVIF_TRUE;
716}
717
718static avifBool avifParseSampleTableBox(avifData * data, avifTrack * track, uint8_t * raw, size_t rawLen)
719{
720 if (track->sampleTable) {
721 // A TrackBox may only have one SampleTable
722 return AVIF_FALSE;
723 }
724 track->sampleTable = avifSampleTableCreate();
725
726 BEGIN_STREAM(s, raw, rawLen);
727
728 while (avifStreamHasBytesLeft(&s, 1)) {
729 avifBoxHeader header;
730 CHECK(avifStreamReadBoxHeader(&s, &header));
731
732 if (!memcmp(header.type, "stco", 4)) {
733 CHECK(avifParseChunkOffsetBox(data, track->sampleTable, AVIF_FALSE, avifStreamCurrent(&s), header.size));
734 } else if (!memcmp(header.type, "co64", 4)) {
735 CHECK(avifParseChunkOffsetBox(data, track->sampleTable, AVIF_TRUE, avifStreamCurrent(&s), header.size));
736 } else if (!memcmp(header.type, "stsc", 4)) {
737 CHECK(avifParseSampleToChunkBox(data, track->sampleTable, avifStreamCurrent(&s), header.size));
738 } else if (!memcmp(header.type, "stsz", 4)) {
739 CHECK(avifParseSampleSizeBox(data, track->sampleTable, avifStreamCurrent(&s), header.size));
740 } else if (!memcmp(header.type, "stts", 4)) {
741 CHECK(avifParseTimeToSampleBox(data, track->sampleTable, avifStreamCurrent(&s), header.size));
742 }
743
744 CHECK(avifStreamSkip(&s, header.size));
745 }
746
747 // Now calculate chunk sizes from the read-in sample table
748 uint32_t sampleSizeIndex = 0;
749 for (uint32_t chunkIndex = 0; chunkIndex < track->sampleTable->chunks.count; ++chunkIndex) {
750 avifSampleTableChunk * chunk = &track->sampleTable->chunks.chunk[chunkIndex];
751
752 // First, figure out how many samples are in this chunk
753 uint32_t sampleCount = 0;
754 for (int sampleToChunkIndex = track->sampleTable->sampleToChunks.count - 1; sampleToChunkIndex >= 0; --sampleToChunkIndex) {
755 avifSampleTableSampleToChunk * sampleToChunk = &track->sampleTable->sampleToChunks.sampleToChunk[sampleToChunkIndex];
756 if (sampleToChunk->firstChunk <= (chunkIndex + 1)) {
757 sampleCount = sampleToChunk->samplesPerChunk;
758 break;
759 }
760 }
761 if (sampleCount == 0) {
762 // chunks with 0 samples are invalid
763 return AVIF_FALSE;
764 }
765
766 // Then sum up the next sampleCount samples into this chunk
767 for (uint32_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) {
768 if (sampleSizeIndex >= track->sampleTable->sampleSizes.count) {
769 // We've run out of samples to sum
770 return AVIF_FALSE;
771 }
772
773 avifSampleTableSampleSize * sampleSize = &track->sampleTable->sampleSizes.sampleSize[sampleSizeIndex];
774 chunk->size += sampleSize->size;
775 ++sampleSizeIndex;
776 }
777 }
778
779 return AVIF_TRUE;
780}
781
782static avifBool avifParseMediaInformationBox(avifData * data, avifTrack * track, uint8_t * raw, size_t rawLen)
783{
784 BEGIN_STREAM(s, raw, rawLen);
785
786 while (avifStreamHasBytesLeft(&s, 1)) {
787 avifBoxHeader header;
788 CHECK(avifStreamReadBoxHeader(&s, &header));
789
790 if (!memcmp(header.type, "stbl", 4)) {
791 CHECK(avifParseSampleTableBox(data, track, avifStreamCurrent(&s), header.size));
792 }
793
794 CHECK(avifStreamSkip(&s, header.size));
795 }
796 return AVIF_TRUE;
797}
798
799static avifBool avifParseMediaBox(avifData * data, avifTrack * track, uint8_t * raw, size_t rawLen)
800{
801 BEGIN_STREAM(s, raw, rawLen);
802
803 while (avifStreamHasBytesLeft(&s, 1)) {
804 avifBoxHeader header;
805 CHECK(avifStreamReadBoxHeader(&s, &header));
806
807 if (!memcmp(header.type, "mdhd", 4)) {
808 CHECK(avifParseMediaHeaderBox(data, track, avifStreamCurrent(&s), header.size));
809 } else if (!memcmp(header.type, "minf", 4)) {
810 CHECK(avifParseMediaInformationBox(data, track, avifStreamCurrent(&s), header.size));
811 }
812
813 CHECK(avifStreamSkip(&s, header.size));
814 }
815 return AVIF_TRUE;
816}
817
818static avifBool avifParseTrackBox(avifData * data, uint8_t * raw, size_t rawLen)
819{
820 BEGIN_STREAM(s, raw, rawLen);
821
822 avifTrack * track = (avifTrack *)avifArrayPushPtr(&data->tracks);
823
824 while (avifStreamHasBytesLeft(&s, 1)) {
825 avifBoxHeader header;
826 CHECK(avifStreamReadBoxHeader(&s, &header));
827
828 if (!memcmp(header.type, "tkhd", 4)) {
829 CHECK(avifParseTrackHeaderBox(data, track, avifStreamCurrent(&s), header.size));
830 } else if (!memcmp(header.type, "mdia", 4)) {
831 CHECK(avifParseMediaBox(data, track, avifStreamCurrent(&s), header.size));
832 }
833
834 CHECK(avifStreamSkip(&s, header.size));
835 }
836 return AVIF_TRUE;
837}
838
839static avifBool avifParseMoovBox(avifData * data, uint8_t * raw, size_t rawLen)
840{
841 BEGIN_STREAM(s, raw, rawLen);
842
843 while (avifStreamHasBytesLeft(&s, 1)) {
844 avifBoxHeader header;
845 CHECK(avifStreamReadBoxHeader(&s, &header));
846
847 if (!memcmp(header.type, "trak", 4)) {
848 CHECK(avifParseTrackBox(data, avifStreamCurrent(&s), header.size));
849 }
850
851 CHECK(avifStreamSkip(&s, header.size));
852 }
853 return AVIF_TRUE;
854}
855
Joe Drago8f7a3002019-02-07 19:35:37 -0800856static avifBool avifParseFileTypeBox(avifData * data, uint8_t * raw, size_t rawLen)
857{
858 BEGIN_STREAM(s, raw, rawLen);
859
860 CHECK(avifStreamRead(&s, data->ftyp.majorBrand, 4));
861 CHECK(avifStreamReadU32(&s, &data->ftyp.minorVersion));
862
863 size_t compatibleBrandsBytes = avifStreamRemainingBytes(&s);
864 if ((compatibleBrandsBytes % 4) != 0) {
865 return AVIF_FALSE;
866 }
867 if (compatibleBrandsBytes > (4 * MAX_COMPATIBLE_BRANDS)) {
868 // TODO: stop clamping and resize this
869 compatibleBrandsBytes = (4 * MAX_COMPATIBLE_BRANDS);
870 }
871 CHECK(avifStreamRead(&s, data->ftyp.compatibleBrands, compatibleBrandsBytes));
Joe Drago678b9382019-02-09 03:17:47 -0800872 data->ftyp.compatibleBrandsCount = (int)compatibleBrandsBytes / 4;
Joe Drago8f7a3002019-02-07 19:35:37 -0800873
874 return AVIF_TRUE;
875}
876
877static avifBool avifParse(avifData * data, uint8_t * raw, size_t rawLen)
878{
879 BEGIN_STREAM(s, raw, rawLen);
880
881 while (avifStreamHasBytesLeft(&s, 1)) {
882 avifBoxHeader header;
883 CHECK(avifStreamReadBoxHeader(&s, &header));
884
885 if (!memcmp(header.type, "ftyp", 4)) {
886 CHECK(avifParseFileTypeBox(data, avifStreamCurrent(&s), header.size));
887 } else if (!memcmp(header.type, "meta", 4)) {
888 CHECK(avifParseMetaBox(data, avifStreamCurrent(&s), header.size));
Joe Dragoae7e2c32019-07-18 15:22:25 -0700889 } else if (!memcmp(header.type, "moov", 4)) {
890 CHECK(avifParseMoovBox(data, avifStreamCurrent(&s), header.size));
Joe Drago8f7a3002019-02-07 19:35:37 -0800891 }
892
893 CHECK(avifStreamSkip(&s, header.size));
894 }
895 return AVIF_TRUE;
896}
897
898// ---------------------------------------------------------------------------
Joe Drago8f7a3002019-02-07 19:35:37 -0800899
Joe Drago0b05eee2019-06-12 13:24:39 -0700900avifDecoder * avifDecoderCreate(void)
901{
902 avifDecoder * decoder = (avifDecoder *)avifAlloc(sizeof(avifDecoder));
903 memset(decoder, 0, sizeof(avifDecoder));
904 return decoder;
905}
906
907void avifDecoderDestroy(avifDecoder * decoder)
908{
909 avifFree(decoder);
910}
911
912avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image, avifRawData * input)
Joe Drago444f0512019-01-23 17:03:24 -0800913{
Joe Drago33f1d362019-02-13 16:46:22 -0800914 avifCodec * codec = NULL;
Joe Drago05559c92019-07-17 16:33:38 -0700915 avifData * data = NULL;
916 avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
Joe Drago33f1d362019-02-13 16:46:22 -0800917
Joe Drago177914d2019-07-12 17:46:46 -0700918#if !defined(AVIF_CODEC_AOM) && !defined(AVIF_CODEC_DAV1D)
Joe Drago3533b972019-07-12 14:32:20 -0700919 // Just bail out early, we're not surviving this function without a decoder compiled in
920 return AVIF_RESULT_NO_CODEC_AVAILABLE;
921#endif
922
Joe Drago444f0512019-01-23 17:03:24 -0800923 // -----------------------------------------------------------------------
924 // Parse BMFF boxes
925
Joe Drago05559c92019-07-17 16:33:38 -0700926 data = avifDataCreate();
927 if (!avifParse(data, input->data, input->size)) {
928 result = AVIF_RESULT_BMFF_PARSE_FAILED;
929 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -0800930 }
931
Joe Drago05559c92019-07-17 16:33:38 -0700932 avifBool avifCompatible = (memcmp(data->ftyp.majorBrand, "avif", 4) == 0) ? AVIF_TRUE : AVIF_FALSE;
Joe Drago8f7a3002019-02-07 19:35:37 -0800933 if (!avifCompatible) {
Joe Drago05559c92019-07-17 16:33:38 -0700934 for (int compatibleBrandIndex = 0; compatibleBrandIndex < data->ftyp.compatibleBrandsCount; ++compatibleBrandIndex) {
935 uint8_t * compatibleBrand = &data->ftyp.compatibleBrands[4 * compatibleBrandIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -0800936 if (!memcmp(compatibleBrand, "avif", 4)) {
937 avifCompatible = AVIF_TRUE;
938 break;
939 }
940 }
941 }
942 if (!avifCompatible) {
Joe Drago05559c92019-07-17 16:33:38 -0700943 result = AVIF_RESULT_INVALID_FTYP;
944 goto cleanup;
Joe Drago8f7a3002019-02-07 19:35:37 -0800945 }
946
Joe Drago444f0512019-01-23 17:03:24 -0800947 // -----------------------------------------------------------------------
948
949 avifRawData colorOBU = AVIF_RAW_DATA_EMPTY;
950 avifRawData alphaOBU = AVIF_RAW_DATA_EMPTY;
Joe Drago8f7a3002019-02-07 19:35:37 -0800951 avifItem * colorOBUItem = NULL;
952 avifItem * alphaOBUItem = NULL;
Joe Drago444f0512019-01-23 17:03:24 -0800953
Joe Drago76370232019-07-16 11:00:52 -0700954 // Sanity check items
Joe Drago05559c92019-07-17 16:33:38 -0700955 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
956 avifItem * item = &data->items.item[itemIndex];
Joe Drago76370232019-07-16 11:00:52 -0700957 if (item->offset > input->size) {
Joe Drago05559c92019-07-17 16:33:38 -0700958 result = AVIF_RESULT_BMFF_PARSE_FAILED;
959 goto cleanup;
Joe Drago76370232019-07-16 11:00:52 -0700960 }
961 uint64_t offsetSize = (uint64_t)item->offset + (uint64_t)item->size;
962 if (offsetSize > (uint64_t)input->size) {
Joe Drago05559c92019-07-17 16:33:38 -0700963 result = AVIF_RESULT_BMFF_PARSE_FAILED;
964 goto cleanup;
Joe Drago76370232019-07-16 11:00:52 -0700965 }
966 }
967
Joe Drago8f7a3002019-02-07 19:35:37 -0800968 // Find the colorOBU item
Joe Drago05559c92019-07-17 16:33:38 -0700969 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
970 avifItem * item = &data->items.item[itemIndex];
Joe Drago444f0512019-01-23 17:03:24 -0800971 if (!item->id || !item->size) {
972 break;
973 }
Joe Drago444f0512019-01-23 17:03:24 -0800974 if (memcmp(item->type, "av01", 4)) {
975 // probably exif or some other data
976 continue;
977 }
Joe Drago8f7a3002019-02-07 19:35:37 -0800978 if (item->thumbnailForID != 0) {
979 // It's a thumbnail, skip it
980 continue;
981 }
Joe Drago444f0512019-01-23 17:03:24 -0800982
Joe Drago8f7a3002019-02-07 19:35:37 -0800983 colorOBUItem = item;
984 colorOBU.data = input->data + item->offset;
985 colorOBU.size = item->size;
986 break;
987 }
988
989 // Find the alphaOBU item, if any
990 if (colorOBUItem) {
Joe Drago05559c92019-07-17 16:33:38 -0700991 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
992 avifItem * item = &data->items.item[itemIndex];
Joe Drago8f7a3002019-02-07 19:35:37 -0800993 if (!item->id || !item->size) {
994 break;
995 }
Joe Drago8f7a3002019-02-07 19:35:37 -0800996 if (memcmp(item->type, "av01", 4)) {
997 // probably exif or some other data
998 continue;
999 }
1000 if (item->thumbnailForID != 0) {
1001 // It's a thumbnail, skip it
1002 continue;
1003 }
1004
Joe Dragocd1e4c32019-02-08 11:26:31 -08001005 if (isAlphaURN(item->auxC.auxType) && (item->auxForID == colorOBUItem->id)) {
Joe Drago8f7a3002019-02-07 19:35:37 -08001006 alphaOBUItem = item;
1007 alphaOBU.data = input->data + item->offset;
1008 alphaOBU.size = item->size;
1009 break;
1010 }
Joe Drago444f0512019-01-23 17:03:24 -08001011 }
1012 }
1013
1014 if (colorOBU.size == 0) {
Joe Drago05559c92019-07-17 16:33:38 -07001015 result = AVIF_RESULT_NO_AV1_ITEMS_FOUND;
1016 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -08001017 }
1018 avifBool hasAlpha = (alphaOBU.size > 0) ? AVIF_TRUE : AVIF_FALSE;
1019
Joe Drago177914d2019-07-12 17:46:46 -07001020#if defined(AVIF_CODEC_DAV1D)
1021 codec = avifCodecCreateDav1d();
1022#elif defined(AVIF_CODEC_AOM)
Joe Drago3533b972019-07-12 14:32:20 -07001023 codec = avifCodecCreateAOM();
1024#else
Joe Drago7a9a6612019-07-17 11:21:24 -07001025 // #error No decoder available!
Joe Drago3533b972019-07-12 14:32:20 -07001026 return AVIF_RESULT_NO_CODEC_AVAILABLE;
1027#endif
1028 if (!codec->decode(codec, AVIF_CODEC_PLANES_COLOR, &colorOBU)) {
Joe Drago05559c92019-07-17 16:33:38 -07001029 result = AVIF_RESULT_DECODE_COLOR_FAILED;
1030 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -08001031 }
Joe Drago3533b972019-07-12 14:32:20 -07001032 avifCodecImageSize colorPlanesSize = codec->getImageSize(codec, AVIF_CODEC_PLANES_COLOR);
Joe Drago444f0512019-01-23 17:03:24 -08001033
Joe Drago33f1d362019-02-13 16:46:22 -08001034 avifCodecImageSize alphaPlanesSize;
1035 memset(&alphaPlanesSize, 0, sizeof(alphaPlanesSize));
Joe Drago444f0512019-01-23 17:03:24 -08001036 if (hasAlpha) {
Joe Drago3533b972019-07-12 14:32:20 -07001037 if (!codec->decode(codec, AVIF_CODEC_PLANES_ALPHA, &alphaOBU)) {
Joe Drago05559c92019-07-17 16:33:38 -07001038 result = AVIF_RESULT_DECODE_ALPHA_FAILED;
1039 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -08001040 }
Joe Drago3533b972019-07-12 14:32:20 -07001041 alphaPlanesSize = codec->getImageSize(codec, AVIF_CODEC_PLANES_ALPHA);
Joe Drago33f1d362019-02-13 16:46:22 -08001042
1043 if ((colorPlanesSize.width != alphaPlanesSize.width) || (colorPlanesSize.height != alphaPlanesSize.height)) {
Joe Drago05559c92019-07-17 16:33:38 -07001044 result = AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH;
1045 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -08001046 }
1047 }
1048
Joe Drago97b071c2019-07-17 14:24:56 -07001049 if ((colorOBUItem && colorOBUItem->ispePresent &&
1050 ((colorOBUItem->ispe.width != colorPlanesSize.width) || (colorOBUItem->ispe.height != colorPlanesSize.height))) ||
1051 (alphaOBUItem && alphaOBUItem->ispePresent &&
1052 ((alphaOBUItem->ispe.width != alphaPlanesSize.width) || (alphaOBUItem->ispe.height != alphaPlanesSize.height)))) {
Joe Drago05559c92019-07-17 16:33:38 -07001053 result = AVIF_RESULT_ISPE_SIZE_MISMATCH;
1054 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -08001055 }
1056
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001057 if (colorOBUItem->colrPresent) {
1058 if (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_ICC) {
1059 avifImageSetProfileICC(image, colorOBUItem->colr.icc, colorOBUItem->colr.iccSize);
1060 } else if (colorOBUItem->colr.format == AVIF_PROFILE_FORMAT_NCLX) {
Joe Dragof35e41a2019-02-11 17:01:41 -08001061 avifImageSetProfileNCLX(image, &colorOBUItem->colr.nclx);
Joe Dragoe7ce20d2019-02-11 16:37:38 -08001062 }
Joe Drago41eb62b2019-02-08 15:38:18 -08001063 }
1064
Joe Drago7ad3ad62019-02-07 11:17:34 -08001065 avifImageFreePlanes(image, AVIF_PLANES_ALL);
Joe Drago444f0512019-01-23 17:03:24 -08001066
Joe Drago3533b972019-07-12 14:32:20 -07001067 avifResult imageResult = codec->getDecodedImage(codec, image);
Joe Drago33f1d362019-02-13 16:46:22 -08001068 if (imageResult != AVIF_RESULT_OK) {
Joe Drago05559c92019-07-17 16:33:38 -07001069 result = imageResult;
1070 goto cleanup;
Joe Drago444f0512019-01-23 17:03:24 -08001071 }
1072
Joe Drago7efd8032019-02-08 14:49:15 -08001073#if defined(AVIF_FIX_STUDIO_ALPHA)
Joe Drago3533b972019-07-12 14:32:20 -07001074 if (hasAlpha && codec->alphaLimitedRange(codec)) {
Joe Drago33f1d362019-02-13 16:46:22 -08001075 // Naughty! Alpha planes are supposed to be full range. Correct that here.
1076 if (avifImageUsesU16(image)) {
1077 for (int j = 0; j < image->height; ++j) {
1078 for (int i = 0; i < image->height; ++i) {
1079 uint16_t * alpha = (uint16_t *)&image->alphaPlane[(i * 2) + (j * image->alphaRowBytes)];
Joe Dragocd5b93b2019-04-18 11:19:54 -07001080 *alpha = (uint16_t)avifLimitedToFullY(image->depth, *alpha);
Joe Drago7efd8032019-02-08 14:49:15 -08001081 }
Joe Drago33f1d362019-02-13 16:46:22 -08001082 }
1083 } else {
1084 for (int j = 0; j < image->height; ++j) {
1085 for (int i = 0; i < image->height; ++i) {
1086 uint8_t * alpha = &image->alphaPlane[i + (j * image->alphaRowBytes)];
Joe Dragocd5b93b2019-04-18 11:19:54 -07001087 *alpha = (uint8_t)avifLimitedToFullY(image->depth, *alpha);
Joe Drago7efd8032019-02-08 14:49:15 -08001088 }
1089 }
1090 }
Joe Drago7ad3ad62019-02-07 11:17:34 -08001091 }
Joe Drago33f1d362019-02-13 16:46:22 -08001092#endif
Joe Drago7ad3ad62019-02-07 11:17:34 -08001093
Joe Drago05559c92019-07-17 16:33:38 -07001094 decoder->ioStats.colorOBUSize = colorOBU.size;
1095 decoder->ioStats.alphaOBUSize = alphaOBU.size;
1096
1097 result = AVIF_RESULT_OK;
1098cleanup:
Joe Drago33f1d362019-02-13 16:46:22 -08001099 if (codec) {
1100 avifCodecDestroy(codec);
Joe Drago444f0512019-01-23 17:03:24 -08001101 }
Joe Drago05559c92019-07-17 16:33:38 -07001102 if (data) {
1103 avifDataDestroy(data);
1104 }
1105 return result;
Joe Drago444f0512019-01-23 17:03:24 -08001106}