blob: ad059319c97c77b94f36b586ecd7f40de0112e9f [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 Dragoceb2fa02021-01-29 18:14:55 -08006#include <assert.h>
Joe Drago444f0512019-01-23 17:03:24 -08007#include <string.h>
Joe Drago4a25c192020-06-03 16:29:58 -07008#include <time.h>
Joe Drago444f0512019-01-23 17:03:24 -08009
Joe Drago224b0182019-02-08 10:42:29 -080010#define MAX_ASSOCIATIONS 16
11struct ipmaArray
12{
13 uint8_t associations[MAX_ASSOCIATIONS];
Joe Drago0da29972020-04-23 11:55:10 -070014 avifBool essential[MAX_ASSOCIATIONS];
Joe Drago224b0182019-02-08 10:42:29 -080015 uint8_t count;
16};
Joe Drago0da29972020-04-23 11:55:10 -070017static void ipmaPush(struct ipmaArray * ipma, uint8_t assoc, avifBool essential)
Joe Drago224b0182019-02-08 10:42:29 -080018{
19 ipma->associations[ipma->count] = assoc;
Joe Drago0da29972020-04-23 11:55:10 -070020 ipma->essential[ipma->count] = essential;
Joe Drago224b0182019-02-08 10:42:29 -080021 ++ipma->count;
22}
23
Joe Dragoa72da5b2020-06-15 19:40:17 -070024// Used to store offsets in meta boxes which need to point at mdat offsets that
25// aren't known yet. When an item's mdat payload is written, all registered fixups
26// will have this now-known offset "fixed up".
27typedef struct avifOffsetFixup
28{
29 size_t offset;
30} avifOffsetFixup;
31AVIF_ARRAY_DECLARE(avifOffsetFixupArray, avifOffsetFixup, fixup);
32
Joe Dragocd1e4c32019-02-08 11:26:31 -080033static const char alphaURN[] = URN_ALPHA0;
34static const size_t alphaURNSize = sizeof(alphaURN);
35
Joe Dragof6a42272019-11-21 15:21:41 -080036static const char xmpContentType[] = CONTENT_TYPE_XMP;
37static const size_t xmpContentTypeSize = sizeof(xmpContentType);
38
Wan-Teh Change184dc12020-05-11 12:47:21 -070039static avifBool avifImageIsOpaque(const avifImage * image);
Joe Drago345aaa12019-09-25 13:42:12 -070040static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg);
Joe Drago444f0512019-01-23 17:03:24 -080041
Joe Drago800b47f2020-03-18 16:22:37 -070042// ---------------------------------------------------------------------------
Joe Drago5b596c42020-06-02 17:13:38 -070043// avifCodecEncodeOutput
44
45avifCodecEncodeOutput * avifCodecEncodeOutputCreate(void)
46{
47 avifCodecEncodeOutput * encodeOutput = (avifCodecEncodeOutput *)avifAlloc(sizeof(avifCodecEncodeOutput));
48 memset(encodeOutput, 0, sizeof(avifCodecEncodeOutput));
Wan-Teh Changf732a4d2022-01-21 15:56:35 -080049 if (!avifArrayCreate(&encodeOutput->samples, sizeof(avifEncodeSample), 1)) {
50 goto error;
51 }
Joe Drago5b596c42020-06-02 17:13:38 -070052 return encodeOutput;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -080053
54error:
55 avifCodecEncodeOutputDestroy(encodeOutput);
56 return NULL;
Joe Drago5b596c42020-06-02 17:13:38 -070057}
58
59void avifCodecEncodeOutputAddSample(avifCodecEncodeOutput * encodeOutput, const uint8_t * data, size_t len, avifBool sync)
60{
61 avifEncodeSample * sample = (avifEncodeSample *)avifArrayPushPtr(&encodeOutput->samples);
62 avifRWDataSet(&sample->data, data, len);
63 sample->sync = sync;
64}
65
66void avifCodecEncodeOutputDestroy(avifCodecEncodeOutput * encodeOutput)
67{
68 for (uint32_t sampleIndex = 0; sampleIndex < encodeOutput->samples.count; ++sampleIndex) {
69 avifRWDataFree(&encodeOutput->samples.sample[sampleIndex].data);
70 }
71 avifArrayDestroy(&encodeOutput->samples);
72 avifFree(encodeOutput);
73}
74
75// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -070076// avifEncoderItem
77
78// one "item" worth for encoder
79typedef struct avifEncoderItem
80{
81 uint16_t id;
82 uint8_t type[4];
Joe Drago5b596c42020-06-02 17:13:38 -070083 avifCodec * codec; // only present on type==av01
Joe Drago25fe1272020-07-09 14:20:39 -070084 avifCodecEncodeOutput * encodeOutput; // AV1 sample data
85 avifRWData metadataPayload; // Exif/XMP data
Joe Drago2172ed02020-11-04 18:04:44 -080086 avifCodecConfigurationBox av1C; // Harvested in avifEncoderFinish(), if encodeOutput has samples
Joe Dragoaec9cff2020-12-11 14:23:37 -080087 uint32_t cellIndex; // Which row-major cell index corresponds to this item. ignored on non-av01 types
Joe Drago800b47f2020-03-18 16:22:37 -070088 avifBool alpha;
Wan-Teh Chang0f418912022-02-17 09:16:05 -080089 avifBool hiddenImage; // A hidden image item has (flags & 1) equal to 1 in its ItemInfoEntry.
Joe Drago800b47f2020-03-18 16:22:37 -070090
91 const char * infeName;
92 size_t infeNameSize;
93 const char * infeContentType;
94 size_t infeContentTypeSize;
Joe Dragoa72da5b2020-06-15 19:40:17 -070095 avifOffsetFixupArray mdatFixups;
Joe Drago800b47f2020-03-18 16:22:37 -070096
97 uint16_t irefToID; // if non-zero, make an iref from this id -> irefToID
98 const char * irefType;
99
Wan-Teh Changf9fca862020-12-22 15:08:12 -0800100 uint32_t gridCols; // if non-zero (legal range [1-256]), this is a grid item
101 uint32_t gridRows; // if non-zero (legal range [1-256]), this is a grid item
Joe Dragoaec9cff2020-12-11 14:23:37 -0800102
103 uint16_t dimgFromID; // if non-zero, make an iref from dimgFromID -> this id
104
Joe Drago800b47f2020-03-18 16:22:37 -0700105 struct ipmaArray ipma;
106} avifEncoderItem;
107AVIF_ARRAY_DECLARE(avifEncoderItemArray, avifEncoderItem, item);
108
109// ---------------------------------------------------------------------------
Joe Drago8210c1b2020-06-02 17:40:28 -0700110// avifEncoderFrame
111
112typedef struct avifEncoderFrame
113{
Joe Drago4a25c192020-06-03 16:29:58 -0700114 uint64_t durationInTimescales;
Joe Drago8210c1b2020-06-02 17:40:28 -0700115} avifEncoderFrame;
116AVIF_ARRAY_DECLARE(avifEncoderFrameArray, avifEncoderFrame, frame);
117
118// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700119// avifEncoderData
120
121typedef struct avifEncoderData
122{
123 avifEncoderItemArray items;
Joe Drago8210c1b2020-06-02 17:40:28 -0700124 avifEncoderFrameArray frames;
Joe Drago250221a2020-06-01 11:11:06 -0700125 avifImage * imageMetadata;
Joe Drago800b47f2020-03-18 16:22:37 -0700126 uint16_t lastItemID;
127 uint16_t primaryItemID;
Joe Dragoaba51d82020-11-19 13:12:29 -0800128 avifBool singleImage; // if true, the AVIF_ADD_IMAGE_FLAG_SINGLE flag was set on the first call to avifEncoderAddImage()
Joe Dragoaec9cff2020-12-11 14:23:37 -0800129 avifBool alphaPresent;
Joe Drago800b47f2020-03-18 16:22:37 -0700130} avifEncoderData;
131
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800132static void avifEncoderDataDestroy(avifEncoderData * data);
133
Joe Drago800b47f2020-03-18 16:22:37 -0700134static avifEncoderData * avifEncoderDataCreate()
135{
136 avifEncoderData * data = (avifEncoderData *)avifAlloc(sizeof(avifEncoderData));
137 memset(data, 0, sizeof(avifEncoderData));
Joe Drago250221a2020-06-01 11:11:06 -0700138 data->imageMetadata = avifImageCreateEmpty();
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800139 if (!avifArrayCreate(&data->items, sizeof(avifEncoderItem), 8)) {
140 goto error;
141 }
142 if (!avifArrayCreate(&data->frames, sizeof(avifEncoderFrame), 1)) {
143 goto error;
144 }
Joe Drago800b47f2020-03-18 16:22:37 -0700145 return data;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800146
147error:
148 avifEncoderDataDestroy(data);
149 return NULL;
Joe Drago800b47f2020-03-18 16:22:37 -0700150}
151
Wan-Teh Chang8adbb972020-12-18 14:12:51 -0800152static avifEncoderItem * avifEncoderDataCreateItem(avifEncoderData * data, const char * type, const char * infeName, size_t infeNameSize, uint32_t cellIndex)
Joe Drago800b47f2020-03-18 16:22:37 -0700153{
154 avifEncoderItem * item = (avifEncoderItem *)avifArrayPushPtr(&data->items);
Wan-Teh Changd9cffc52022-01-29 22:31:01 -0800155 ++data->lastItemID;
Joe Drago800b47f2020-03-18 16:22:37 -0700156 item->id = data->lastItemID;
157 memcpy(item->type, type, sizeof(item->type));
158 item->infeName = infeName;
159 item->infeNameSize = infeNameSize;
Joe Drago5b596c42020-06-02 17:13:38 -0700160 item->encodeOutput = avifCodecEncodeOutputCreate();
Joe Dragoaec9cff2020-12-11 14:23:37 -0800161 item->cellIndex = cellIndex;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800162 if (!avifArrayCreate(&item->mdatFixups, sizeof(avifOffsetFixup), 4)) {
163 goto error;
164 }
Joe Drago800b47f2020-03-18 16:22:37 -0700165 return item;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800166
167error:
168 avifCodecEncodeOutputDestroy(item->encodeOutput);
Wan-Teh Changd9cffc52022-01-29 22:31:01 -0800169 --data->lastItemID;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800170 avifArrayPop(&data->items);
171 return NULL;
Joe Drago800b47f2020-03-18 16:22:37 -0700172}
173
Joe Dragoceb2fa02021-01-29 18:14:55 -0800174static avifEncoderItem * avifEncoderDataFindItemByID(avifEncoderData * data, uint16_t id)
175{
176 for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
177 avifEncoderItem * item = &data->items.item[itemIndex];
178 if (item->id == id) {
179 return item;
180 }
181 }
182 return NULL;
183}
184
Joe Drago800b47f2020-03-18 16:22:37 -0700185static void avifEncoderDataDestroy(avifEncoderData * data)
186{
187 for (uint32_t i = 0; i < data->items.count; ++i) {
188 avifEncoderItem * item = &data->items.item[i];
189 if (item->codec) {
190 avifCodecDestroy(item->codec);
191 }
Joe Drago5b596c42020-06-02 17:13:38 -0700192 avifCodecEncodeOutputDestroy(item->encodeOutput);
Joe Drago25fe1272020-07-09 14:20:39 -0700193 avifRWDataFree(&item->metadataPayload);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700194 avifArrayDestroy(&item->mdatFixups);
Joe Drago800b47f2020-03-18 16:22:37 -0700195 }
Joe Drago250221a2020-06-01 11:11:06 -0700196 avifImageDestroy(data->imageMetadata);
Joe Drago800b47f2020-03-18 16:22:37 -0700197 avifArrayDestroy(&data->items);
Joe Drago8210c1b2020-06-02 17:40:28 -0700198 avifArrayDestroy(&data->frames);
Joe Drago800b47f2020-03-18 16:22:37 -0700199 avifFree(data);
200}
201
Wan-Teh Chang9f89df92020-07-08 18:54:26 -0700202static void avifEncoderItemAddMdatFixup(avifEncoderItem * item, const avifRWStream * s)
Joe Dragoa72da5b2020-06-15 19:40:17 -0700203{
204 avifOffsetFixup * fixup = (avifOffsetFixup *)avifArrayPushPtr(&item->mdatFixups);
205 fixup->offset = avifRWStreamOffset(s);
206}
207
Joe Drago800b47f2020-03-18 16:22:37 -0700208// ---------------------------------------------------------------------------
Joe Drago5779fc42021-09-08 11:15:26 -0700209// avifItemPropertyDedup - Provides ipco deduplication
210
211typedef struct avifItemProperty
212{
213 uint8_t index;
214 size_t offset;
215 size_t size;
216} avifItemProperty;
217AVIF_ARRAY_DECLARE(avifItemPropertyArray, avifItemProperty, property);
218
219typedef struct avifItemPropertyDedup
220{
221 avifItemPropertyArray properties;
222 avifRWStream s; // Temporary stream for each new property, checked against already-written boxes for deduplications
223 avifRWData buffer; // Temporary storage for 's'
224 uint8_t nextIndex; // 1-indexed, incremented every time another unique property is finished
225} avifItemPropertyDedup;
226
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800227static void avifItemPropertyDedupDestroy(avifItemPropertyDedup * dedup);
228
Joe Drago5779fc42021-09-08 11:15:26 -0700229static avifItemPropertyDedup * avifItemPropertyDedupCreate(void)
230{
231 avifItemPropertyDedup * dedup = (avifItemPropertyDedup *)avifAlloc(sizeof(avifItemPropertyDedup));
232 memset(dedup, 0, sizeof(avifItemPropertyDedup));
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800233 if (!avifArrayCreate(&dedup->properties, sizeof(avifItemProperty), 8)) {
234 goto error;
235 }
Joe Drago5779fc42021-09-08 11:15:26 -0700236 avifRWDataRealloc(&dedup->buffer, 2048); // This will resize automatically (if necessary)
237 return dedup;
Wan-Teh Changf732a4d2022-01-21 15:56:35 -0800238
239error:
240 avifItemPropertyDedupDestroy(dedup);
241 return NULL;
Joe Drago5779fc42021-09-08 11:15:26 -0700242}
243
244static void avifItemPropertyDedupDestroy(avifItemPropertyDedup * dedup)
245{
246 avifArrayDestroy(&dedup->properties);
247 avifRWDataFree(&dedup->buffer);
248 avifFree(dedup);
249}
250
251// Resets the dedup's temporary write stream in preparation for a single item property's worth of writing
252static void avifItemPropertyDedupStart(avifItemPropertyDedup * dedup)
253{
254 avifRWStreamStart(&dedup->s, &dedup->buffer);
255}
256
257// This compares the newly written item property (in the dedup's temporary storage buffer) to
258// already-written properties (whose offsets/sizes in outputStream are recorded in the dedup). If a
259// match is found, the previous item's index is used. If this new property is unique, it is
260// assigned the next available property index, written to the output stream, and its offset/size in
261// the output stream is recorded in the dedup for future comparisons.
262//
263// This function always returns a valid 1-indexed property index for usage in a property association
264// (ipma) box later. If the most recent property was a duplicate of a previous property, the return
265// value will be the index of the original property, otherwise it will be the index of the newly
266// created property.
267static uint8_t avifItemPropertyDedupFinish(avifItemPropertyDedup * dedup, avifRWStream * outputStream)
268{
269 const size_t newPropertySize = avifRWStreamOffset(&dedup->s);
270
271 for (size_t i = 0; i < dedup->properties.count; ++i) {
272 avifItemProperty * property = &dedup->properties.property[i];
273 if ((property->size == newPropertySize) &&
274 !memcmp(&outputStream->raw->data[property->offset], dedup->buffer.data, newPropertySize)) {
275 // We've already written this exact property, reuse it
276 return property->index;
277 }
278 }
279
280 // Write a new property, and remember its location in the output stream for future deduplication
281 avifItemProperty * property = (avifItemProperty *)avifArrayPushPtr(&dedup->properties);
282 property->index = ++dedup->nextIndex; // preincrement so the first new index is 1 (as ipma is 1-indexed)
283 property->size = newPropertySize;
284 property->offset = avifRWStreamOffset(outputStream);
285 avifRWStreamWrite(outputStream, dedup->buffer.data, newPropertySize);
286 return property->index;
287}
288
289// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700290
Joe Drago0b05eee2019-06-12 13:24:39 -0700291avifEncoder * avifEncoderCreate(void)
292{
293 avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder));
294 memset(encoder, 0, sizeof(avifEncoder));
295 encoder->maxThreads = 1;
Joe Drago46ea0582019-07-22 15:55:47 -0700296 encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
297 encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
Joe Drago56fa2bc2020-03-03 13:29:48 -0800298 encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
299 encoder->maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
Joe Drago4bf01872019-11-15 18:09:19 -0800300 encoder->tileRowsLog2 = 0;
301 encoder->tileColsLog2 = 0;
302 encoder->speed = AVIF_SPEED_DEFAULT;
Joe Dragod6850242020-06-16 16:05:37 -0700303 encoder->keyframeInterval = 0;
Wan-Teh Chang2792c0a2020-08-28 16:10:31 -0700304 encoder->timescale = 1;
305 encoder->data = avifEncoderDataCreate();
Joe Dragoe1097bd2020-08-27 18:55:03 -0700306 encoder->csOptions = avifCodecSpecificOptionsCreate();
Joe Drago0b05eee2019-06-12 13:24:39 -0700307 return encoder;
308}
309
310void avifEncoderDestroy(avifEncoder * encoder)
311{
Joe Dragoe1097bd2020-08-27 18:55:03 -0700312 avifCodecSpecificOptionsDestroy(encoder->csOptions);
Joe Drago800b47f2020-03-18 16:22:37 -0700313 avifEncoderDataDestroy(encoder->data);
Joe Drago0b05eee2019-06-12 13:24:39 -0700314 avifFree(encoder);
315}
316
Joe Dragoe1097bd2020-08-27 18:55:03 -0700317void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value)
318{
319 avifCodecSpecificOptionsSet(encoder->csOptions, key, value);
320}
321
Joe Drago5779fc42021-09-08 11:15:26 -0700322// This function is used in two codepaths:
323// * writing color *item* properties
324// * writing color *track* properties
325//
326// Item properties must have property associations with them and can be deduplicated (by reusing
327// these associations), so this function leverages the ipma and dedup arguments to do this.
328//
329// Track properties, however, are implicitly associated by the track in which they are contained, so
330// there is no need to build a property association box (ipma), and no way to deduplicate/reuse a
331// property. In this case, the ipma and dedup properties should/will be set to NULL, and this
332// function will avoid using them.
333static void avifEncoderWriteColorProperties(avifRWStream * outputStream,
334 const avifImage * imageMetadata,
335 struct ipmaArray * ipma,
336 avifItemPropertyDedup * dedup)
Joe Drago332180d2020-06-15 16:55:16 -0700337{
Joe Drago5779fc42021-09-08 11:15:26 -0700338 avifRWStream * s = outputStream;
339 if (dedup) {
340 assert(ipma);
341
342 // Use the dedup's temporary stream for box writes
343 s = &dedup->s;
344 }
345
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700346 if (imageMetadata->icc.size > 0) {
Joe Drago5779fc42021-09-08 11:15:26 -0700347 if (dedup) {
348 avifItemPropertyDedupStart(dedup);
349 }
Joe Drago332180d2020-06-15 16:55:16 -0700350 avifBoxMarker colr = avifRWStreamWriteBox(s, "colr", AVIF_BOX_SIZE_TBD);
351 avifRWStreamWriteChars(s, "prof", 4); // unsigned int(32) colour_type;
352 avifRWStreamWrite(s, imageMetadata->icc.data, imageMetadata->icc.size);
353 avifRWStreamFinishBox(s, colr);
Joe Drago5779fc42021-09-08 11:15:26 -0700354 if (dedup) {
355 ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE);
Joe Drago332180d2020-06-15 16:55:16 -0700356 }
Joe Dragobf58fe72020-11-05 13:25:14 -0800357 }
358
359 // HEIF 6.5.5.1, from Amendment 3 allows multiple colr boxes: "at most one for a given value of colour type"
360 // Therefore, *always* writing an nclx box, even if an a prof box was already written above.
Joe Drago5779fc42021-09-08 11:15:26 -0700361 if (dedup) {
362 avifItemPropertyDedupStart(dedup);
363 }
Joe Dragobf58fe72020-11-05 13:25:14 -0800364 avifBoxMarker colr = avifRWStreamWriteBox(s, "colr", AVIF_BOX_SIZE_TBD);
365 avifRWStreamWriteChars(s, "nclx", 4); // unsigned int(32) colour_type;
Joe Dragofb5a5f02021-01-31 20:43:57 -0800366 avifRWStreamWriteU16(s, imageMetadata->colorPrimaries); // unsigned int(16) colour_primaries;
367 avifRWStreamWriteU16(s, imageMetadata->transferCharacteristics); // unsigned int(16) transfer_characteristics;
368 avifRWStreamWriteU16(s, imageMetadata->matrixCoefficients); // unsigned int(16) matrix_coefficients;
Joe Dragobf58fe72020-11-05 13:25:14 -0800369 avifRWStreamWriteU8(s, (imageMetadata->yuvRange == AVIF_RANGE_FULL) ? 0x80 : 0); // unsigned int(1) full_range_flag;
370 // unsigned int(7) reserved = 0;
371 avifRWStreamFinishBox(s, colr);
Joe Drago5779fc42021-09-08 11:15:26 -0700372 if (dedup) {
373 ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE);
Joe Drago332180d2020-06-15 16:55:16 -0700374 }
375
376 // Write (Optional) Transformations
377 if (imageMetadata->transformFlags & AVIF_TRANSFORM_PASP) {
Joe Drago5779fc42021-09-08 11:15:26 -0700378 if (dedup) {
379 avifItemPropertyDedupStart(dedup);
380 }
Joe Drago332180d2020-06-15 16:55:16 -0700381 avifBoxMarker pasp = avifRWStreamWriteBox(s, "pasp", AVIF_BOX_SIZE_TBD);
382 avifRWStreamWriteU32(s, imageMetadata->pasp.hSpacing); // unsigned int(32) hSpacing;
383 avifRWStreamWriteU32(s, imageMetadata->pasp.vSpacing); // unsigned int(32) vSpacing;
384 avifRWStreamFinishBox(s, pasp);
Joe Drago5779fc42021-09-08 11:15:26 -0700385 if (dedup) {
386 ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE);
Joe Drago332180d2020-06-15 16:55:16 -0700387 }
388 }
389 if (imageMetadata->transformFlags & AVIF_TRANSFORM_CLAP) {
Joe Drago5779fc42021-09-08 11:15:26 -0700390 if (dedup) {
391 avifItemPropertyDedupStart(dedup);
392 }
Joe Drago332180d2020-06-15 16:55:16 -0700393 avifBoxMarker clap = avifRWStreamWriteBox(s, "clap", AVIF_BOX_SIZE_TBD);
394 avifRWStreamWriteU32(s, imageMetadata->clap.widthN); // unsigned int(32) cleanApertureWidthN;
395 avifRWStreamWriteU32(s, imageMetadata->clap.widthD); // unsigned int(32) cleanApertureWidthD;
396 avifRWStreamWriteU32(s, imageMetadata->clap.heightN); // unsigned int(32) cleanApertureHeightN;
397 avifRWStreamWriteU32(s, imageMetadata->clap.heightD); // unsigned int(32) cleanApertureHeightD;
398 avifRWStreamWriteU32(s, imageMetadata->clap.horizOffN); // unsigned int(32) horizOffN;
399 avifRWStreamWriteU32(s, imageMetadata->clap.horizOffD); // unsigned int(32) horizOffD;
400 avifRWStreamWriteU32(s, imageMetadata->clap.vertOffN); // unsigned int(32) vertOffN;
401 avifRWStreamWriteU32(s, imageMetadata->clap.vertOffD); // unsigned int(32) vertOffD;
402 avifRWStreamFinishBox(s, clap);
Joe Drago5779fc42021-09-08 11:15:26 -0700403 if (dedup) {
404 ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE);
Joe Drago332180d2020-06-15 16:55:16 -0700405 }
406 }
407 if (imageMetadata->transformFlags & AVIF_TRANSFORM_IROT) {
Joe Drago5779fc42021-09-08 11:15:26 -0700408 if (dedup) {
409 avifItemPropertyDedupStart(dedup);
410 }
Joe Drago332180d2020-06-15 16:55:16 -0700411 avifBoxMarker irot = avifRWStreamWriteBox(s, "irot", AVIF_BOX_SIZE_TBD);
412 uint8_t angle = imageMetadata->irot.angle & 0x3;
413 avifRWStreamWrite(s, &angle, 1); // unsigned int (6) reserved = 0; unsigned int (2) angle;
414 avifRWStreamFinishBox(s, irot);
Joe Drago5779fc42021-09-08 11:15:26 -0700415 if (dedup) {
416 ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE);
Joe Drago332180d2020-06-15 16:55:16 -0700417 }
418 }
419 if (imageMetadata->transformFlags & AVIF_TRANSFORM_IMIR) {
Joe Drago5779fc42021-09-08 11:15:26 -0700420 if (dedup) {
421 avifItemPropertyDedupStart(dedup);
422 }
Joe Drago332180d2020-06-15 16:55:16 -0700423 avifBoxMarker imir = avifRWStreamWriteBox(s, "imir", AVIF_BOX_SIZE_TBD);
Joe Dragob551bb32021-06-03 15:22:05 -0700424 uint8_t mode = imageMetadata->imir.mode & 0x1;
425 avifRWStreamWrite(s, &mode, 1); // unsigned int (7) reserved = 0; unsigned int (1) mode;
Joe Drago332180d2020-06-15 16:55:16 -0700426 avifRWStreamFinishBox(s, imir);
Joe Drago5779fc42021-09-08 11:15:26 -0700427 if (dedup) {
428 ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE);
Joe Drago332180d2020-06-15 16:55:16 -0700429 }
430 }
431}
432
Joe Dragoa72da5b2020-06-15 19:40:17 -0700433// Write unassociated metadata items (EXIF, XMP) to a small meta box inside of a trak box.
434// These items are implicitly associated with the track they are contained within.
435static void avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s)
436{
437 // Count how many non-av01 items (such as EXIF/XMP) are being written
438 uint32_t metadataItemCount = 0;
439 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
440 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
441 if (memcmp(item->type, "av01", 4) != 0) {
442 ++metadataItemCount;
443 }
444 }
445 if (metadataItemCount == 0) {
446 // Don't even bother writing the trak meta box
447 return;
448 }
449
450 avifBoxMarker meta = avifRWStreamWriteFullBox(s, "meta", AVIF_BOX_SIZE_TBD, 0, 0);
451
452 avifBoxMarker hdlr = avifRWStreamWriteFullBox(s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0);
453 avifRWStreamWriteU32(s, 0); // unsigned int(32) pre_defined = 0;
454 avifRWStreamWriteChars(s, "pict", 4); // unsigned int(32) handler_type;
455 avifRWStreamWriteZeros(s, 12); // const unsigned int(32)[3] reserved = 0;
456 avifRWStreamWriteChars(s, "libavif", 8); // string name; (writing null terminator)
457 avifRWStreamFinishBox(s, hdlr);
458
459 avifBoxMarker iloc = avifRWStreamWriteFullBox(s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0);
460 uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size;
461 // unsigned int(4) length_size;
462 avifRWStreamWrite(s, &offsetSizeAndLengthSize, 1); //
463 avifRWStreamWriteZeros(s, 1); // unsigned int(4) base_offset_size;
464 // unsigned int(4) reserved;
465 avifRWStreamWriteU16(s, (uint16_t)metadataItemCount); // unsigned int(16) item_count;
466 for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) {
467 avifEncoderItem * item = &encoder->data->items.item[trakItemIndex];
468 if (memcmp(item->type, "av01", 4) == 0) {
Joe Drago25fe1272020-07-09 14:20:39 -0700469 // Skip over all non-metadata items
Joe Dragoa72da5b2020-06-15 19:40:17 -0700470 continue;
471 }
472
Joe Drago25fe1272020-07-09 14:20:39 -0700473 avifRWStreamWriteU16(s, item->id); // unsigned int(16) item_ID;
474 avifRWStreamWriteU16(s, 0); // unsigned int(16) data_reference_index;
475 avifRWStreamWriteU16(s, 1); // unsigned int(16) extent_count;
476 avifEncoderItemAddMdatFixup(item, s); //
477 avifRWStreamWriteU32(s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
478 avifRWStreamWriteU32(s, (uint32_t)item->metadataPayload.size); // unsigned int(length_size*8) extent_length;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700479 }
480 avifRWStreamFinishBox(s, iloc);
481
482 avifBoxMarker iinf = avifRWStreamWriteFullBox(s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0);
483 avifRWStreamWriteU16(s, (uint16_t)metadataItemCount); // unsigned int(16) entry_count;
484 for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) {
485 avifEncoderItem * item = &encoder->data->items.item[trakItemIndex];
486 if (memcmp(item->type, "av01", 4) == 0) {
487 continue;
488 }
489
Wan-Teh Chang0f418912022-02-17 09:16:05 -0800490 assert(!item->hiddenImage);
Joe Dragoa72da5b2020-06-15 19:40:17 -0700491 avifBoxMarker infe = avifRWStreamWriteFullBox(s, "infe", AVIF_BOX_SIZE_TBD, 2, 0);
492 avifRWStreamWriteU16(s, item->id); // unsigned int(16) item_ID;
493 avifRWStreamWriteU16(s, 0); // unsigned int(16) item_protection_index;
494 avifRWStreamWrite(s, item->type, 4); // unsigned int(32) item_type;
495 avifRWStreamWriteChars(s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator)
496 if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator)
497 avifRWStreamWriteChars(s, item->infeContentType, item->infeContentTypeSize);
498 }
499 avifRWStreamFinishBox(s, infe);
500 }
501 avifRWStreamFinishBox(s, iinf);
502
503 avifRWStreamFinishBox(s, meta);
504}
505
Wan-Teh Changf9fca862020-12-22 15:08:12 -0800506static void avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t gridRows, const avifImage * firstCell)
Joe Dragoaec9cff2020-12-11 14:23:37 -0800507{
508 // ISO/IEC 23008-12 6.6.2.3.2
509 // aligned(8) class ImageGrid {
510 // unsigned int(8) version = 0;
511 // unsigned int(8) flags;
512 // FieldLength = ((flags & 1) + 1) * 16;
513 // unsigned int(8) rows_minus_one;
514 // unsigned int(8) columns_minus_one;
515 // unsigned int(FieldLength) output_width;
516 // unsigned int(FieldLength) output_height;
517 // }
518
519 uint32_t gridWidth = firstCell->width * gridCols;
520 uint32_t gridHeight = firstCell->height * gridRows;
521 uint8_t gridFlags = ((gridWidth > 65535) || (gridHeight > 65535)) ? 1 : 0;
522
523 avifRWStream s;
524 avifRWStreamStart(&s, data);
Wan-Teh Chang8adbb972020-12-18 14:12:51 -0800525 avifRWStreamWriteU8(&s, 0); // unsigned int(8) version = 0;
526 avifRWStreamWriteU8(&s, gridFlags); // unsigned int(8) flags;
527 avifRWStreamWriteU8(&s, (uint8_t)(gridRows - 1)); // unsigned int(8) rows_minus_one;
528 avifRWStreamWriteU8(&s, (uint8_t)(gridCols - 1)); // unsigned int(8) columns_minus_one;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800529 if (gridFlags & 1) {
530 avifRWStreamWriteU32(&s, gridWidth); // unsigned int(FieldLength) output_width;
531 avifRWStreamWriteU32(&s, gridHeight); // unsigned int(FieldLength) output_height;
532 } else {
533 uint16_t tmpWidth = (uint16_t)gridWidth;
534 uint16_t tmpHeight = (uint16_t)gridHeight;
535 avifRWStreamWriteU16(&s, tmpWidth); // unsigned int(FieldLength) output_width;
536 avifRWStreamWriteU16(&s, tmpHeight); // unsigned int(FieldLength) output_height;
537 }
538 avifRWStreamFinishWrite(&s);
539}
540
541static avifResult avifEncoderAddImageInternal(avifEncoder * encoder,
Wan-Teh Changf9fca862020-12-22 15:08:12 -0800542 uint32_t gridCols,
543 uint32_t gridRows,
Wan-Teh Chang2b7f04e2020-12-14 09:51:01 -0800544 const avifImage * const * cellImages,
Joe Dragoaec9cff2020-12-11 14:23:37 -0800545 uint64_t durationInTimescales,
Joe Dragoe7c95e02021-05-06 12:48:55 -0700546 avifAddImageFlags addImageFlags)
Joe Drago444f0512019-01-23 17:03:24 -0800547{
Joe Drago250221a2020-06-01 11:11:06 -0700548 // -----------------------------------------------------------------------
Joe Dragoc534e702020-10-30 17:56:42 -0700549 // Verify encoding is possible
550
551 if (!avifCodecName(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE)) {
552 return AVIF_RESULT_NO_CODEC_AVAILABLE;
553 }
554
555 // -----------------------------------------------------------------------
Joe Dragoaec9cff2020-12-11 14:23:37 -0800556 // Validate images
Joe Drago250221a2020-06-01 11:11:06 -0700557
Joe Dragoaec9cff2020-12-11 14:23:37 -0800558 const uint32_t cellCount = gridCols * gridRows;
559 if (cellCount == 0) {
560 return AVIF_RESULT_INVALID_ARGUMENT;
Joe Drago7ad3ad62019-02-07 11:17:34 -0800561 }
562
Joe Dragoaec9cff2020-12-11 14:23:37 -0800563 const avifImage * firstCell = cellImages[0];
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800564 if ((firstCell->depth != 8) && (firstCell->depth != 10) && (firstCell->depth != 12)) {
565 return AVIF_RESULT_UNSUPPORTED_DEPTH;
566 }
567
568 if (!firstCell->width || !firstCell->height) {
569 return AVIF_RESULT_NO_CONTENT;
570 }
571
Yannis Guyonbf28a922022-02-09 23:19:32 +0100572 if ((cellCount > 1) && !avifAreGridDimensionsValid(firstCell->yuvFormat,
573 gridCols * firstCell->width,
574 gridRows * firstCell->height,
575 firstCell->width,
576 firstCell->height,
577 &encoder->diag)) {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800578 return AVIF_RESULT_INVALID_IMAGE_GRID;
Joe Drago444f0512019-01-23 17:03:24 -0800579 }
580
Joe Dragoaec9cff2020-12-11 14:23:37 -0800581 for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) {
582 const avifImage * cellImage = cellImages[cellIndex];
Yannis Guyonbf28a922022-02-09 23:19:32 +0100583 // HEIF (ISO 23008-12:2017), Section 6.6.2.3.1:
584 // All input images shall have exactly the same width and height; call those tile_width and tile_height.
585 // MIAF (ISO 23000-22:2019), Section 7.3.11.4.1:
586 // All input images of a grid image item shall use the same coding format, chroma sampling format, and the
587 // same decoder configuration (see 7.3.6.2).
588 if ((cellImage->width != firstCell->width) || (cellImage->height != firstCell->height) ||
589 (cellImage->depth != firstCell->depth) || (cellImage->yuvFormat != firstCell->yuvFormat) ||
590 (cellImage->yuvRange != firstCell->yuvRange) || (cellImage->colorPrimaries != firstCell->colorPrimaries) ||
591 (cellImage->transferCharacteristics != firstCell->transferCharacteristics) ||
592 (cellImage->matrixCoefficients != firstCell->matrixCoefficients) || (cellImage->alphaRange != firstCell->alphaRange) ||
593 (!!cellImage->alphaPlane != !!firstCell->alphaPlane) || (cellImage->alphaPremultiplied != firstCell->alphaPremultiplied)) {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800594 return AVIF_RESULT_INVALID_IMAGE_GRID;
595 }
596
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800597 if (!cellImage->yuvPlanes[AVIF_CHAN_Y]) {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800598 return AVIF_RESULT_NO_CONTENT;
599 }
600
601 if (cellImage->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
602 return AVIF_RESULT_NO_YUV_FORMAT_SELECTED;
603 }
Joe Drago250221a2020-06-01 11:11:06 -0700604 }
605
606 // -----------------------------------------------------------------------
Joe Dragoaba51d82020-11-19 13:12:29 -0800607 // Validate flags
608
609 if (encoder->data->singleImage) {
610 // The previous call to avifEncoderAddImage() set AVIF_ADD_IMAGE_FLAG_SINGLE.
611 // avifEncoderAddImage() cannot be called again for this encode.
612 return AVIF_RESULT_ENCODE_COLOR_FAILED;
613 }
614
615 if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) {
616 encoder->data->singleImage = AVIF_TRUE;
617
618 if (encoder->data->items.count > 0) {
619 // AVIF_ADD_IMAGE_FLAG_SINGLE may only be set on the first and only image.
620 return AVIF_RESULT_INVALID_ARGUMENT;
621 }
622 }
623
624 // -----------------------------------------------------------------------
Joe Drago250221a2020-06-01 11:11:06 -0700625
Joe Drago4a25c192020-06-03 16:29:58 -0700626 if (durationInTimescales == 0) {
627 durationInTimescales = 1;
Joe Drago8210c1b2020-06-02 17:40:28 -0700628 }
629
Joe Drago250221a2020-06-01 11:11:06 -0700630 if (encoder->data->items.count == 0) {
631 // Make a copy of the first image's metadata (sans pixels) for future writing/validation
Joe Dragoaec9cff2020-12-11 14:23:37 -0800632 avifImageCopy(encoder->data->imageMetadata, firstCell, 0);
Joe Drago250221a2020-06-01 11:11:06 -0700633
634 // Prepare all AV1 items
635
Joe Dragoaec9cff2020-12-11 14:23:37 -0800636 uint16_t gridColorID = 0;
637 if (cellCount > 1) {
638 avifEncoderItem * gridColorItem = avifEncoderDataCreateItem(encoder->data, "grid", "Color", 6, 0);
639 avifWriteGridPayload(&gridColorItem->metadataPayload, gridCols, gridRows, firstCell);
640 gridColorItem->gridCols = gridCols;
641 gridColorItem->gridRows = gridRows;
Joe Drago250221a2020-06-01 11:11:06 -0700642
Joe Dragoaec9cff2020-12-11 14:23:37 -0800643 gridColorID = gridColorItem->id;
644 encoder->data->primaryItemID = gridColorID;
645 }
646
647 for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) {
648 avifEncoderItem * item = avifEncoderDataCreateItem(encoder->data, "av01", "Color", 6, cellIndex);
649 item->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
650 if (!item->codec) {
651 // Just bail out early, we're not surviving this function without an encoder compiled in
652 return AVIF_RESULT_NO_CODEC_AVAILABLE;
653 }
654 item->codec->csOptions = encoder->csOptions;
Joe Dragobfc5aca2021-05-17 12:00:39 -0700655 item->codec->diag = &encoder->diag;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800656
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800657 if (cellCount > 1) {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800658 item->dimgFromID = gridColorID;
Wan-Teh Chang0f418912022-02-17 09:16:05 -0800659 item->hiddenImage = AVIF_TRUE;
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800660 } else {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800661 encoder->data->primaryItemID = item->id;
662 }
663 }
664
665 encoder->data->alphaPresent = (firstCell->alphaPlane != NULL);
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800666 if (encoder->data->alphaPresent && (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE)) {
Joe Drago96e09182020-07-09 15:32:47 -0700667 // If encoding a single image in which the alpha plane exists but is entirely opaque,
668 // simply skip writing an alpha AV1 payload entirely, as it'll be interpreted as opaque
669 // and is less bytes.
670 //
671 // However, if encoding an image sequence, the first frame's alpha plane being entirely
672 // opaque could be a false positive for removing the alpha AV1 payload, as it might simply
673 // be a fade out later in the sequence. This is why avifImageIsOpaque() is only called
674 // when encoding a single image.
675
Joe Dragoaec9cff2020-12-11 14:23:37 -0800676 encoder->data->alphaPresent = AVIF_FALSE;
677 for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) {
678 const avifImage * cellImage = cellImages[cellIndex];
679 if (!avifImageIsOpaque(cellImage)) {
680 encoder->data->alphaPresent = AVIF_TRUE;
681 break;
682 }
Joe Drago250221a2020-06-01 11:11:06 -0700683 }
Joe Dragoaec9cff2020-12-11 14:23:37 -0800684 }
685
686 if (encoder->data->alphaPresent) {
687 uint16_t gridAlphaID = 0;
688 if (cellCount > 1) {
689 avifEncoderItem * gridAlphaItem = avifEncoderDataCreateItem(encoder->data, "grid", "Alpha", 6, 0);
690 avifWriteGridPayload(&gridAlphaItem->metadataPayload, gridCols, gridRows, firstCell);
691 gridAlphaItem->alpha = AVIF_TRUE;
692 gridAlphaItem->irefToID = encoder->data->primaryItemID;
693 gridAlphaItem->irefType = "auxl";
Wan-Teh Chang69c32ef2020-12-18 12:16:42 -0800694 gridAlphaItem->gridCols = gridCols;
695 gridAlphaItem->gridRows = gridRows;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800696 gridAlphaID = gridAlphaItem->id;
Yuan Tonge4850be2021-01-22 14:21:25 +0800697
698 if (encoder->data->imageMetadata->alphaPremultiplied) {
Joe Dragoceb2fa02021-01-29 18:14:55 -0800699 avifEncoderItem * primaryItem = avifEncoderDataFindItemByID(encoder->data, encoder->data->primaryItemID);
700 assert(primaryItem);
701 primaryItem->irefType = "prem";
702 primaryItem->irefToID = gridAlphaID;
Yuan Tonge4850be2021-01-22 14:21:25 +0800703 }
Joe Dragoaec9cff2020-12-11 14:23:37 -0800704 }
705
706 for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) {
Wan-Teh Chang69c32ef2020-12-18 12:16:42 -0800707 avifEncoderItem * item = avifEncoderDataCreateItem(encoder->data, "av01", "Alpha", 6, cellIndex);
Joe Dragoaec9cff2020-12-11 14:23:37 -0800708 item->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
709 if (!item->codec) {
710 return AVIF_RESULT_NO_CODEC_AVAILABLE;
711 }
712 item->codec->csOptions = encoder->csOptions;
Joe Dragobfc5aca2021-05-17 12:00:39 -0700713 item->codec->diag = &encoder->diag;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800714 item->alpha = AVIF_TRUE;
715
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800716 if (cellCount > 1) {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800717 item->dimgFromID = gridAlphaID;
Wan-Teh Chang0f418912022-02-17 09:16:05 -0800718 item->hiddenImage = AVIF_TRUE;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800719 } else {
720 item->irefToID = encoder->data->primaryItemID;
721 item->irefType = "auxl";
Yuan Tonge4850be2021-01-22 14:21:25 +0800722
723 if (encoder->data->imageMetadata->alphaPremultiplied) {
Joe Dragoceb2fa02021-01-29 18:14:55 -0800724 avifEncoderItem * primaryItem = avifEncoderDataFindItemByID(encoder->data, encoder->data->primaryItemID);
725 assert(primaryItem);
726 primaryItem->irefType = "prem";
727 primaryItem->irefToID = item->id;
Yuan Tonge4850be2021-01-22 14:21:25 +0800728 }
Joe Dragoaec9cff2020-12-11 14:23:37 -0800729 }
730 }
Joe Drago250221a2020-06-01 11:11:06 -0700731 }
732
733 // -----------------------------------------------------------------------
734 // Create metadata items (Exif, XMP)
735
Joe Dragoaec9cff2020-12-11 14:23:37 -0800736 if (firstCell->exif.size > 0) {
Joe Drago250221a2020-06-01 11:11:06 -0700737 // Validate Exif payload (if any) and find TIFF header offset
738 uint32_t exifTiffHeaderOffset = 0;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800739 if (firstCell->exif.size < 4) {
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700740 // Can't even fit the TIFF header, something is wrong
741 return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
742 }
Joe Drago250221a2020-06-01 11:11:06 -0700743
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700744 const uint8_t tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
745 const uint8_t tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
Joe Dragoaec9cff2020-12-11 14:23:37 -0800746 for (; exifTiffHeaderOffset < (firstCell->exif.size - 4); ++exifTiffHeaderOffset) {
747 if (!memcmp(&firstCell->exif.data[exifTiffHeaderOffset], tiffHeaderBE, sizeof(tiffHeaderBE))) {
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700748 break;
Joe Drago250221a2020-06-01 11:11:06 -0700749 }
Joe Dragoaec9cff2020-12-11 14:23:37 -0800750 if (!memcmp(&firstCell->exif.data[exifTiffHeaderOffset], tiffHeaderLE, sizeof(tiffHeaderLE))) {
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700751 break;
752 }
753 }
Joe Drago250221a2020-06-01 11:11:06 -0700754
Joe Dragoaec9cff2020-12-11 14:23:37 -0800755 if (exifTiffHeaderOffset >= firstCell->exif.size - 4) {
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700756 // Couldn't find the TIFF header
757 return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
Joe Drago250221a2020-06-01 11:11:06 -0700758 }
759
Joe Dragoaec9cff2020-12-11 14:23:37 -0800760 avifEncoderItem * exifItem = avifEncoderDataCreateItem(encoder->data, "Exif", "Exif", 5, 0);
Joe Drago250221a2020-06-01 11:11:06 -0700761 exifItem->irefToID = encoder->data->primaryItemID;
762 exifItem->irefType = "cdsc";
763
Joe Dragoaec9cff2020-12-11 14:23:37 -0800764 avifRWDataRealloc(&exifItem->metadataPayload, sizeof(uint32_t) + firstCell->exif.size);
Joe Drago250221a2020-06-01 11:11:06 -0700765 exifTiffHeaderOffset = avifHTONL(exifTiffHeaderOffset);
Joe Drago25fe1272020-07-09 14:20:39 -0700766 memcpy(exifItem->metadataPayload.data, &exifTiffHeaderOffset, sizeof(uint32_t));
Joe Dragoaec9cff2020-12-11 14:23:37 -0800767 memcpy(exifItem->metadataPayload.data + sizeof(uint32_t), firstCell->exif.data, firstCell->exif.size);
Joe Drago250221a2020-06-01 11:11:06 -0700768 }
769
Joe Dragoaec9cff2020-12-11 14:23:37 -0800770 if (firstCell->xmp.size > 0) {
771 avifEncoderItem * xmpItem = avifEncoderDataCreateItem(encoder->data, "mime", "XMP", 4, 0);
Joe Drago250221a2020-06-01 11:11:06 -0700772 xmpItem->irefToID = encoder->data->primaryItemID;
773 xmpItem->irefType = "cdsc";
774
775 xmpItem->infeContentType = xmpContentType;
776 xmpItem->infeContentTypeSize = xmpContentTypeSize;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800777 avifRWDataSet(&xmpItem->metadataPayload, firstCell->xmp.data, firstCell->xmp.size);
Joe Drago250221a2020-06-01 11:11:06 -0700778 }
Joe Drago250221a2020-06-01 11:11:06 -0700779 } else {
780 // Another frame in an image sequence
Joe Drago96e09182020-07-09 15:32:47 -0700781
Wan-Teh Chang0b7c20e2020-12-18 16:21:57 -0800782 if (encoder->data->alphaPresent && !firstCell->alphaPlane) {
783 // If the first image in the sequence had an alpha plane (even if fully opaque), all
784 // subsequence images must have alpha as well.
785 return AVIF_RESULT_ENCODE_ALPHA_FAILED;
Joe Drago96e09182020-07-09 15:32:47 -0700786 }
wantehchangcbfab6b2020-03-11 11:53:53 -0700787 }
788
Joe Drago444f0512019-01-23 17:03:24 -0800789 // -----------------------------------------------------------------------
790 // Encode AV1 OBUs
791
Joe Dragod6850242020-06-16 16:05:37 -0700792 if (encoder->keyframeInterval && ((encoder->data->frames.count % encoder->keyframeInterval) == 0)) {
793 addImageFlags |= AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME;
Joe Drago3736df92020-06-16 15:39:55 -0700794 }
795
Joe Drago800b47f2020-03-18 16:22:37 -0700796 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
797 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Drago250221a2020-06-01 11:11:06 -0700798 if (item->codec) {
Joe Dragoaec9cff2020-12-11 14:23:37 -0800799 const avifImage * cellImage = cellImages[item->cellIndex];
Joe Dragoe1097bd2020-08-27 18:55:03 -0700800 avifResult encodeResult =
Joe Dragoaec9cff2020-12-11 14:23:37 -0800801 item->codec->encodeImage(item->codec, encoder, cellImage, item->alpha, addImageFlags, item->encodeOutput);
Joe Dragoe1097bd2020-08-27 18:55:03 -0700802 if (encodeResult == AVIF_RESULT_UNKNOWN_ERROR) {
803 encodeResult = item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
804 }
805 if (encodeResult != AVIF_RESULT_OK) {
806 return encodeResult;
Joe Drago250221a2020-06-01 11:11:06 -0700807 }
808 }
809 }
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700810
Joe Drago8210c1b2020-06-02 17:40:28 -0700811 avifEncoderFrame * frame = (avifEncoderFrame *)avifArrayPushPtr(&encoder->data->frames);
Joe Drago4a25c192020-06-03 16:29:58 -0700812 frame->durationInTimescales = durationInTimescales;
Joe Drago250221a2020-06-01 11:11:06 -0700813 return AVIF_RESULT_OK;
814}
815
Joe Dragoe7c95e02021-05-06 12:48:55 -0700816avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags)
Joe Dragoaec9cff2020-12-11 14:23:37 -0800817{
Joe Dragobfc5aca2021-05-17 12:00:39 -0700818 avifDiagnosticsClearError(&encoder->diag);
Joe Dragoaec9cff2020-12-11 14:23:37 -0800819 return avifEncoderAddImageInternal(encoder, 1, 1, &image, durationInTimescales, addImageFlags);
820}
821
Joe Dragoe7c95e02021-05-06 12:48:55 -0700822avifResult avifEncoderAddImageGrid(avifEncoder * encoder,
823 uint32_t gridCols,
824 uint32_t gridRows,
825 const avifImage * const * cellImages,
826 avifAddImageFlags addImageFlags)
Joe Dragoaec9cff2020-12-11 14:23:37 -0800827{
Joe Dragobfc5aca2021-05-17 12:00:39 -0700828 avifDiagnosticsClearError(&encoder->diag);
Wan-Teh Changf9fca862020-12-22 15:08:12 -0800829 if ((gridCols == 0) || (gridCols > 256) || (gridRows == 0) || (gridRows > 256)) {
830 return AVIF_RESULT_INVALID_IMAGE_GRID;
831 }
Joe Drago11d23592021-01-05 14:18:57 -0800832 return avifEncoderAddImageInternal(encoder, gridCols, gridRows, cellImages, 1, addImageFlags | AVIF_ADD_IMAGE_FLAG_SINGLE); // only single image grids are supported
Joe Dragoaec9cff2020-12-11 14:23:37 -0800833}
834
Wan-Teh Chang2b7f04e2020-12-14 09:51:01 -0800835static size_t avifEncoderFindExistingChunk(avifRWStream * s, size_t mdatStartOffset, const uint8_t * data, size_t size)
Joe Dragoaec9cff2020-12-11 14:23:37 -0800836{
837 const size_t mdatCurrentOffset = avifRWStreamOffset(s);
838 const size_t mdatSearchSize = mdatCurrentOffset - mdatStartOffset;
839 if (mdatSearchSize < size) {
840 return 0;
841 }
Wan-Teh Chang8adbb972020-12-18 14:12:51 -0800842 const size_t mdatEndSearchOffset = mdatCurrentOffset - size;
Joe Dragoaec9cff2020-12-11 14:23:37 -0800843 for (size_t searchOffset = mdatStartOffset; searchOffset <= mdatEndSearchOffset; ++searchOffset) {
844 if (!memcmp(data, &s->raw->data[searchOffset], size)) {
845 return searchOffset;
846 }
847 }
848 return 0;
849}
850
Joe Dragoc7e1d752020-06-15 19:46:50 -0700851avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output)
Joe Drago250221a2020-06-01 11:11:06 -0700852{
Joe Dragobfc5aca2021-05-17 12:00:39 -0700853 avifDiagnosticsClearError(&encoder->diag);
Wan-Teh Changb139eed2020-07-08 18:04:14 -0700854 if (encoder->data->items.count == 0) {
Joe Drago250221a2020-06-01 11:11:06 -0700855 return AVIF_RESULT_NO_CONTENT;
856 }
857
858 // -----------------------------------------------------------------------
859 // Finish up AV1 encoding
860
861 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
862 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
863 if (item->codec) {
Joe Drago5b596c42020-06-02 17:13:38 -0700864 if (!item->codec->encodeFinish(item->codec, item->encodeOutput)) {
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700865 return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
866 }
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700867
Joe Drago8210c1b2020-06-02 17:40:28 -0700868 if (item->encodeOutput->samples.count != encoder->data->frames.count) {
Joe Drago250221a2020-06-01 11:11:06 -0700869 return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
Joe Drago800b47f2020-03-18 16:22:37 -0700870 }
Joe Drago46ea0582019-07-22 15:55:47 -0700871 }
872 }
873
Joe Drago444f0512019-01-23 17:03:24 -0800874 // -----------------------------------------------------------------------
Joe Drago2172ed02020-11-04 18:04:44 -0800875 // Harvest av1C properties from AV1 sequence headers
876
877 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
878 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
879 if (item->encodeOutput->samples.count > 0) {
Wan-Teh Chang9fbcb9b2020-11-06 18:48:58 -0800880 const avifEncodeSample * firstSample = &item->encodeOutput->samples.sample[0];
Joe Drago2172ed02020-11-04 18:04:44 -0800881 avifSequenceHeader sequenceHeader;
882 if (avifSequenceHeaderParse(&sequenceHeader, (const avifROData *)&firstSample->data)) {
Wan-Teh Chang21961f42022-03-14 16:25:44 -0700883 item->av1C = sequenceHeader.av1C;
Joe Drago2172ed02020-11-04 18:04:44 -0800884 } else {
885 // This must be an invalid AV1 payload
886 return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
887 }
888 }
889 }
890
891 // -----------------------------------------------------------------------
Joe Drago250221a2020-06-01 11:11:06 -0700892 // Begin write stream
893
Wan-Teh Changb11ae432020-07-08 18:25:24 -0700894 const avifImage * imageMetadata = encoder->data->imageMetadata;
895 // The epoch for creation_time and modification_time is midnight, Jan. 1,
896 // 1904, in UTC time. Add the number of seconds between that epoch and the
897 // Unix epoch.
898 uint64_t now = (uint64_t)time(NULL) + 2082844800;
Joe Drago250221a2020-06-01 11:11:06 -0700899
900 avifRWStream s;
901 avifRWStreamStart(&s, output);
902
903 // -----------------------------------------------------------------------
Joe Drago444f0512019-01-23 17:03:24 -0800904 // Write ftyp
905
Joe Drago4a25c192020-06-03 16:29:58 -0700906 const char * majorBrand = "avif";
907 if (encoder->data->frames.count > 1) {
908 majorBrand = "avis";
909 }
910
911 avifBoxMarker ftyp = avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD);
912 avifRWStreamWriteChars(&s, majorBrand, 4); // unsigned int(32) major_brand;
Joe Drago250221a2020-06-01 11:11:06 -0700913 avifRWStreamWriteU32(&s, 0); // unsigned int(32) minor_version;
914 avifRWStreamWriteChars(&s, "avif", 4); // unsigned int(32) compatible_brands[];
Joe Drago4a25c192020-06-03 16:29:58 -0700915 if (encoder->data->frames.count > 1) { //
916 avifRWStreamWriteChars(&s, "avis", 4); // ... compatible_brands[]
Joe Drago70f5a2e2020-06-03 17:54:19 -0700917 avifRWStreamWriteChars(&s, "msf1", 4); // ... compatible_brands[]
Vignesh Venkatasubramanian0cf13332022-02-17 14:06:50 -0800918 avifRWStreamWriteChars(&s, "iso8", 4); // ... compatible_brands[]
Joe Drago4a25c192020-06-03 16:29:58 -0700919 } //
Joe Drago250221a2020-06-01 11:11:06 -0700920 avifRWStreamWriteChars(&s, "mif1", 4); // ... compatible_brands[]
921 avifRWStreamWriteChars(&s, "miaf", 4); // ... compatible_brands[]
922 if ((imageMetadata->depth == 8) || (imageMetadata->depth == 10)) { //
923 if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { //
924 avifRWStreamWriteChars(&s, "MA1B", 4); // ... compatible_brands[]
925 } else if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { //
926 avifRWStreamWriteChars(&s, "MA1A", 4); // ... compatible_brands[]
Joe Dragoeb652d82019-04-23 16:29:07 -0700927 }
928 }
Joe Drago345aaa12019-09-25 13:42:12 -0700929 avifRWStreamFinishBox(&s, ftyp);
Joe Drago444f0512019-01-23 17:03:24 -0800930
931 // -----------------------------------------------------------------------
Joe Drago444f0512019-01-23 17:03:24 -0800932 // Start meta
933
Joe Drago4a25c192020-06-03 16:29:58 -0700934 avifBoxMarker meta = avifRWStreamWriteFullBox(&s, "meta", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago444f0512019-01-23 17:03:24 -0800935
936 // -----------------------------------------------------------------------
937 // Write hdlr
938
Joe Drago4a25c192020-06-03 16:29:58 -0700939 avifBoxMarker hdlr = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago345aaa12019-09-25 13:42:12 -0700940 avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0;
941 avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type;
942 avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0;
943 avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator)
944 avifRWStreamFinishBox(&s, hdlr);
Joe Drago444f0512019-01-23 17:03:24 -0800945
946 // -----------------------------------------------------------------------
947 // Write pitm
948
Joe Drago800b47f2020-03-18 16:22:37 -0700949 if (encoder->data->primaryItemID != 0) {
Joe Drago4a25c192020-06-03 16:29:58 -0700950 avifRWStreamWriteFullBox(&s, "pitm", sizeof(uint16_t), 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700951 avifRWStreamWriteU16(&s, encoder->data->primaryItemID); // unsigned int(16) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -0800952 }
953
954 // -----------------------------------------------------------------------
Joe Drago444f0512019-01-23 17:03:24 -0800955 // Write iloc
956
Joe Drago4a25c192020-06-03 16:29:58 -0700957 avifBoxMarker iloc = avifRWStreamWriteFullBox(&s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago444f0512019-01-23 17:03:24 -0800958
Joe Drago800b47f2020-03-18 16:22:37 -0700959 uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size;
960 // unsigned int(4) length_size;
961 avifRWStreamWrite(&s, &offsetSizeAndLengthSize, 1); //
962 avifRWStreamWriteZeros(&s, 1); // unsigned int(4) base_offset_size;
963 // unsigned int(4) reserved;
964 avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) item_count;
Joe Drago444f0512019-01-23 17:03:24 -0800965
Joe Drago800b47f2020-03-18 16:22:37 -0700966 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
967 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Drago2c4e7142020-06-03 10:31:46 -0700968
Joe Drago25fe1272020-07-09 14:20:39 -0700969 uint32_t contentSize = (uint32_t)item->metadataPayload.size;
Joe Drago2c4e7142020-06-03 10:31:46 -0700970 if (item->encodeOutput->samples.count > 0) {
Joe Drago25fe1272020-07-09 14:20:39 -0700971 // This is choosing sample 0's size as there are two cases here:
Joe Dragod6aa99c2020-07-09 14:27:46 -0700972 // * This is a single image, in which case this is correct
Joe Drago25fe1272020-07-09 14:20:39 -0700973 // * This is an image sequence, but this file should still be a valid single-image avif,
974 // so there must still be a primary item pointing at a sync sample. Since the first
975 // frame of the image sequence is guaranteed to be a sync sample, it is chosen here.
976 //
Joe Drago746325a2020-07-09 14:22:55 -0700977 // TODO: Offer the ability for a user to specify which frame in the sequence should
Joe Drago25fe1272020-07-09 14:20:39 -0700978 // become the primary item's image, and force that frame to be a keyframe.
Joe Drago2c4e7142020-06-03 10:31:46 -0700979 contentSize = (uint32_t)item->encodeOutput->samples.sample[0].data.size;
980 }
981
982 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
983 avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
984 avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
Joe Dragoa72da5b2020-06-15 19:40:17 -0700985 avifEncoderItemAddMdatFixup(item, &s); //
Joe Drago2c4e7142020-06-03 10:31:46 -0700986 avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
987 avifRWStreamWriteU32(&s, (uint32_t)contentSize); // unsigned int(length_size*8) extent_length;
Joe Dragof6a42272019-11-21 15:21:41 -0800988 }
989
Joe Drago345aaa12019-09-25 13:42:12 -0700990 avifRWStreamFinishBox(&s, iloc);
Joe Drago444f0512019-01-23 17:03:24 -0800991
992 // -----------------------------------------------------------------------
993 // Write iinf
994
Joe Drago4a25c192020-06-03 16:29:58 -0700995 avifBoxMarker iinf = avifRWStreamWriteFullBox(&s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700996 avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) entry_count;
Joe Drago444f0512019-01-23 17:03:24 -0800997
Joe Drago800b47f2020-03-18 16:22:37 -0700998 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
999 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
1000
Wan-Teh Chang0f418912022-02-17 09:16:05 -08001001 uint32_t flags = item->hiddenImage ? 1 : 0;
1002 avifBoxMarker infe = avifRWStreamWriteFullBox(&s, "infe", AVIF_BOX_SIZE_TBD, 2, flags);
Joe Drago800b47f2020-03-18 16:22:37 -07001003 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -08001004 avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index;
Joe Drago800b47f2020-03-18 16:22:37 -07001005 avifRWStreamWrite(&s, item->type, 4); // unsigned int(32) item_type;
1006 avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator)
1007 if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator)
1008 avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize);
1009 }
1010 avifRWStreamFinishBox(&s, infe);
Joe Drago444f0512019-01-23 17:03:24 -08001011 }
Joe Drago800b47f2020-03-18 16:22:37 -07001012
Joe Drago345aaa12019-09-25 13:42:12 -07001013 avifRWStreamFinishBox(&s, iinf);
Joe Drago444f0512019-01-23 17:03:24 -08001014
1015 // -----------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -07001016 // Write iref boxes
Joe Drago8f7a3002019-02-07 19:35:37 -08001017
Joe Drago3818cf42020-07-31 13:08:37 -07001018 avifBoxMarker iref = 0;
Joe Drago800b47f2020-03-18 16:22:37 -07001019 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
1020 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Dragoaec9cff2020-12-11 14:23:37 -08001021
1022 // Count how many other items refer to this item with dimgFromID
1023 uint16_t dimgCount = 0;
1024 for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) {
1025 avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex];
1026 if (dimgItem->dimgFromID == item->id) {
1027 ++dimgCount;
1028 }
1029 }
1030
1031 if (dimgCount > 0) {
1032 if (!iref) {
1033 iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0);
1034 }
1035 avifBoxMarker refType = avifRWStreamWriteBox(&s, "dimg", AVIF_BOX_SIZE_TBD);
1036 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID;
1037 avifRWStreamWriteU16(&s, dimgCount); // unsigned int(16) reference_count;
1038 for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) {
1039 avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex];
1040 if (dimgItem->dimgFromID == item->id) {
1041 avifRWStreamWriteU16(&s, dimgItem->id); // unsigned int(16) to_item_ID;
1042 }
1043 }
1044 avifRWStreamFinishBox(&s, refType);
1045 }
1046
Joe Drago800b47f2020-03-18 16:22:37 -07001047 if (item->irefToID != 0) {
Joe Drago3818cf42020-07-31 13:08:37 -07001048 if (!iref) {
1049 iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0);
1050 }
Joe Drago4a25c192020-06-03 16:29:58 -07001051 avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD);
Joe Drago800b47f2020-03-18 16:22:37 -07001052 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID;
1053 avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
1054 avifRWStreamWriteU16(&s, item->irefToID); // unsigned int(16) to_item_ID;
1055 avifRWStreamFinishBox(&s, refType);
Joe Drago800b47f2020-03-18 16:22:37 -07001056 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001057 }
Joe Drago3818cf42020-07-31 13:08:37 -07001058 if (iref) {
1059 avifRWStreamFinishBox(&s, iref);
1060 }
Joe Drago8f7a3002019-02-07 19:35:37 -08001061
1062 // -----------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -07001063 // Write iprp -> ipco/ipma
Joe Drago444f0512019-01-23 17:03:24 -08001064
Joe Drago4a25c192020-06-03 16:29:58 -07001065 avifBoxMarker iprp = avifRWStreamWriteBox(&s, "iprp", AVIF_BOX_SIZE_TBD);
Joe Drago224b0182019-02-08 10:42:29 -08001066
Joe Drago5779fc42021-09-08 11:15:26 -07001067 avifItemPropertyDedup * dedup = avifItemPropertyDedupCreate();
Joe Drago4a25c192020-06-03 16:29:58 -07001068 avifBoxMarker ipco = avifRWStreamWriteBox(&s, "ipco", AVIF_BOX_SIZE_TBD);
Joe Drago800b47f2020-03-18 16:22:37 -07001069 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
1070 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Dragoaec9cff2020-12-11 14:23:37 -08001071 const avifBool isGrid = (item->gridCols > 0);
Joe Drago800b47f2020-03-18 16:22:37 -07001072 memset(&item->ipma, 0, sizeof(item->ipma));
Joe Dragoaec9cff2020-12-11 14:23:37 -08001073 if (!item->codec && !isGrid) {
Joe Drago800b47f2020-03-18 16:22:37 -07001074 // No ipma to write for this item
1075 continue;
1076 }
Joe Dragoe4dba562019-02-07 21:52:32 -08001077
Joe Dragoaec9cff2020-12-11 14:23:37 -08001078 if (item->dimgFromID) {
1079 // All image cells from a grid should share the exact same properties, so see if we've
1080 // already written properties out for another cell in this grid, and if so, just steal
1081 // their ipma and move on. This is a sneaky way to provide iprp deduplication.
1082
1083 avifBool foundPreviousCell = AVIF_FALSE;
1084 for (uint32_t dedupIndex = 0; dedupIndex < itemIndex; ++dedupIndex) {
1085 avifEncoderItem * dedupItem = &encoder->data->items.item[dedupIndex];
1086 if (item->dimgFromID == dedupItem->dimgFromID) {
1087 // We've already written dedup's items out. Steal their ipma indices and move on!
Wan-Teh Chang21961f42022-03-14 16:25:44 -07001088 item->ipma = dedupItem->ipma;
Joe Dragoaec9cff2020-12-11 14:23:37 -08001089 foundPreviousCell = AVIF_TRUE;
1090 break;
1091 }
1092 }
1093 if (foundPreviousCell) {
1094 continue;
1095 }
1096 }
1097
Wan-Teh Chang8adbb972020-12-18 14:12:51 -08001098 uint32_t imageWidth = imageMetadata->width;
1099 uint32_t imageHeight = imageMetadata->height;
Joe Dragoaec9cff2020-12-11 14:23:37 -08001100 if (isGrid) {
1101 imageWidth = imageMetadata->width * item->gridCols;
1102 imageHeight = imageMetadata->height * item->gridRows;
1103 }
1104
Joe Drago800b47f2020-03-18 16:22:37 -07001105 // Properties all av01 items need
1106
Joe Drago5779fc42021-09-08 11:15:26 -07001107 avifItemPropertyDedupStart(dedup);
1108 avifBoxMarker ispe = avifRWStreamWriteFullBox(&dedup->s, "ispe", AVIF_BOX_SIZE_TBD, 0, 0);
1109 avifRWStreamWriteU32(&dedup->s, imageWidth); // unsigned int(32) image_width;
1110 avifRWStreamWriteU32(&dedup->s, imageHeight); // unsigned int(32) image_height;
1111 avifRWStreamFinishBox(&dedup->s, ispe);
1112 ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE);
Joe Drago800b47f2020-03-18 16:22:37 -07001113
Joe Drago5779fc42021-09-08 11:15:26 -07001114 avifItemPropertyDedupStart(dedup);
Joe Drago332180d2020-06-15 16:55:16 -07001115 uint8_t channelCount = (item->alpha || (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) ? 1 : 3;
Joe Drago5779fc42021-09-08 11:15:26 -07001116 avifBoxMarker pixi = avifRWStreamWriteFullBox(&dedup->s, "pixi", AVIF_BOX_SIZE_TBD, 0, 0);
1117 avifRWStreamWriteU8(&dedup->s, channelCount); // unsigned int (8) num_channels;
Joe Drago800b47f2020-03-18 16:22:37 -07001118 for (uint8_t chan = 0; chan < channelCount; ++chan) {
Joe Drago5779fc42021-09-08 11:15:26 -07001119 avifRWStreamWriteU8(&dedup->s, (uint8_t)imageMetadata->depth); // unsigned int (8) bits_per_channel;
Joe Drago800b47f2020-03-18 16:22:37 -07001120 }
Joe Drago5779fc42021-09-08 11:15:26 -07001121 avifRWStreamFinishBox(&dedup->s, pixi);
1122 ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE);
Joe Drago800b47f2020-03-18 16:22:37 -07001123
Joe Dragoaec9cff2020-12-11 14:23:37 -08001124 if (item->codec) {
Joe Drago5779fc42021-09-08 11:15:26 -07001125 avifItemPropertyDedupStart(dedup);
1126 writeConfigBox(&dedup->s, &item->av1C);
1127 ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_TRUE);
Joe Dragoaec9cff2020-12-11 14:23:37 -08001128 }
Joe Drago800b47f2020-03-18 16:22:37 -07001129
1130 if (item->alpha) {
1131 // Alpha specific properties
1132
Joe Drago5779fc42021-09-08 11:15:26 -07001133 avifItemPropertyDedupStart(dedup);
1134 avifBoxMarker auxC = avifRWStreamWriteFullBox(&dedup->s, "auxC", AVIF_BOX_SIZE_TBD, 0, 0);
1135 avifRWStreamWriteChars(&dedup->s, alphaURN, alphaURNSize); // string aux_type;
1136 avifRWStreamFinishBox(&dedup->s, auxC);
1137 ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE);
Joe Drago800b47f2020-03-18 16:22:37 -07001138 } else {
1139 // Color specific properties
1140
Joe Drago5779fc42021-09-08 11:15:26 -07001141 avifEncoderWriteColorProperties(&s, imageMetadata, &item->ipma, dedup);
Joe Dragoe4dba562019-02-07 21:52:32 -08001142 }
Joe Dragoe4dba562019-02-07 21:52:32 -08001143 }
Joe Drago800b47f2020-03-18 16:22:37 -07001144 avifRWStreamFinishBox(&s, ipco);
Joe Drago5779fc42021-09-08 11:15:26 -07001145 avifItemPropertyDedupDestroy(dedup);
1146 dedup = NULL;
Joe Drago800b47f2020-03-18 16:22:37 -07001147
Joe Drago4a25c192020-06-03 16:29:58 -07001148 avifBoxMarker ipma = avifRWStreamWriteFullBox(&s, "ipma", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -07001149 {
1150 int ipmaCount = 0;
1151 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
1152 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
1153 if (item->ipma.count > 0) {
1154 ++ipmaCount;
1155 }
1156 }
1157 avifRWStreamWriteU32(&s, ipmaCount); // unsigned int(32) entry_count;
1158
1159 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
1160 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
1161 if (item->ipma.count == 0) {
1162 continue;
1163 }
1164
Joe Drago0da29972020-04-23 11:55:10 -07001165 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
1166 avifRWStreamWriteU8(&s, item->ipma.count); // unsigned int(8) association_count;
1167 for (int i = 0; i < item->ipma.count; ++i) { //
1168 uint8_t essentialAndIndex = item->ipma.associations[i];
1169 if (item->ipma.essential[i]) {
1170 essentialAndIndex |= 0x80;
1171 }
1172 avifRWStreamWriteU8(&s, essentialAndIndex); // bit(1) essential; unsigned int(7) property_index;
Joe Drago800b47f2020-03-18 16:22:37 -07001173 }
1174 }
1175 }
1176 avifRWStreamFinishBox(&s, ipma);
1177
Joe Drago345aaa12019-09-25 13:42:12 -07001178 avifRWStreamFinishBox(&s, iprp);
Joe Drago444f0512019-01-23 17:03:24 -08001179
1180 // -----------------------------------------------------------------------
Joe Dragoeb652d82019-04-23 16:29:07 -07001181 // Finish meta box
Joe Drago444f0512019-01-23 17:03:24 -08001182
Joe Drago345aaa12019-09-25 13:42:12 -07001183 avifRWStreamFinishBox(&s, meta);
Joe Dragoeb652d82019-04-23 16:29:07 -07001184
1185 // -----------------------------------------------------------------------
Joe Drago4a25c192020-06-03 16:29:58 -07001186 // Write tracks (if an image sequence)
Joe Drago2c4e7142020-06-03 10:31:46 -07001187
1188 if (encoder->data->frames.count > 1) {
Vignesh Venkatasubramaniane234f672022-02-16 11:04:52 -08001189 static const uint8_t unityMatrix[9][4] = {
1190 /* clang-format off */
1191 { 0x00, 0x01, 0x00, 0x00 },
1192 { 0 },
1193 { 0 },
1194 { 0 },
1195 { 0x00, 0x01, 0x00, 0x00 },
1196 { 0 },
1197 { 0 },
1198 { 0 },
1199 { 0x40, 0x00, 0x00, 0x00 }
1200 /* clang-format on */
1201 };
Joe Drago4a25c192020-06-03 16:29:58 -07001202
1203 uint64_t durationInTimescales = 0;
1204 for (uint32_t frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) {
Wan-Teh Changb139eed2020-07-08 18:04:14 -07001205 const avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex];
Joe Drago4a25c192020-06-03 16:29:58 -07001206 durationInTimescales += frame->durationInTimescales;
1207 }
1208
1209 // -------------------------------------------------------------------
1210 // Start moov
1211
1212 avifBoxMarker moov = avifRWStreamWriteBox(&s, "moov", AVIF_BOX_SIZE_TBD);
1213
1214 avifBoxMarker mvhd = avifRWStreamWriteFullBox(&s, "mvhd", AVIF_BOX_SIZE_TBD, 1, 0);
1215 avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time;
1216 avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time;
1217 avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale;
1218 avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration;
1219 avifRWStreamWriteU32(&s, 0x00010000); // template int(32) rate = 0x00010000; // typically 1.0
1220 avifRWStreamWriteU16(&s, 0x0100); // template int(16) volume = 0x0100; // typically, full volume
1221 avifRWStreamWriteU16(&s, 0); // const bit(16) reserved = 0;
1222 avifRWStreamWriteZeros(&s, 8); // const unsigned int(32)[2] reserved = 0;
Wan-Teh Chang8557cd62020-07-08 18:42:10 -07001223 avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix));
Joe Drago4a25c192020-06-03 16:29:58 -07001224 avifRWStreamWriteZeros(&s, 24); // bit(32)[6] pre_defined = 0;
1225 avifRWStreamWriteU32(&s, encoder->data->items.count); // unsigned int(32) next_track_ID;
1226 avifRWStreamFinishBox(&s, mvhd);
1227
1228 // -------------------------------------------------------------------
1229 // Write tracks
1230
1231 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
1232 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
1233 if (item->encodeOutput->samples.count == 0) {
1234 continue;
1235 }
1236
1237 uint32_t syncSamplesCount = 0;
1238 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
1239 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
1240 if (sample->sync) {
1241 ++syncSamplesCount;
1242 }
1243 }
1244
1245 avifBoxMarker trak = avifRWStreamWriteBox(&s, "trak", AVIF_BOX_SIZE_TBD);
1246
Joe Drago85e387a2020-06-03 17:12:53 -07001247 avifBoxMarker tkhd = avifRWStreamWriteFullBox(&s, "tkhd", AVIF_BOX_SIZE_TBD, 1, 1);
Joe Drago4a25c192020-06-03 16:29:58 -07001248 avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time;
1249 avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time;
1250 avifRWStreamWriteU32(&s, itemIndex + 1); // unsigned int(32) track_ID;
1251 avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0;
1252 avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration;
1253 avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 2); // const unsigned int(32)[2] reserved = 0;
1254 avifRWStreamWriteU16(&s, 0); // template int(16) layer = 0;
1255 avifRWStreamWriteU16(&s, 0); // template int(16) alternate_group = 0;
1256 avifRWStreamWriteU16(&s, 0); // template int(16) volume = {if track_is_audio 0x0100 else 0};
1257 avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0;
Wan-Teh Chang8557cd62020-07-08 18:42:10 -07001258 avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix)); // template int(32)[9] matrix= // { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };
Joe Drago4a25c192020-06-03 16:29:58 -07001259 avifRWStreamWriteU32(&s, imageMetadata->width << 16); // unsigned int(32) width;
1260 avifRWStreamWriteU32(&s, imageMetadata->height << 16); // unsigned int(32) height;
1261 avifRWStreamFinishBox(&s, tkhd);
1262
1263 if (item->irefToID != 0) {
1264 avifBoxMarker tref = avifRWStreamWriteBox(&s, "tref", AVIF_BOX_SIZE_TBD);
1265 avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD);
1266 avifRWStreamWriteU32(&s, (uint32_t)item->irefToID);
1267 avifRWStreamFinishBox(&s, refType);
1268 avifRWStreamFinishBox(&s, tref);
1269 }
1270
Joe Dragoa72da5b2020-06-15 19:40:17 -07001271 if (!item->alpha) {
1272 avifEncoderWriteTrackMetaBox(encoder, &s);
1273 }
1274
Joe Drago4a25c192020-06-03 16:29:58 -07001275 avifBoxMarker mdia = avifRWStreamWriteBox(&s, "mdia", AVIF_BOX_SIZE_TBD);
1276
1277 avifBoxMarker mdhd = avifRWStreamWriteFullBox(&s, "mdhd", AVIF_BOX_SIZE_TBD, 1, 0);
1278 avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time;
1279 avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time;
1280 avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale;
1281 avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration;
1282 avifRWStreamWriteU16(&s, 21956); // bit(1) pad = 0; unsigned int(5)[3] language; ("und")
1283 avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0;
1284 avifRWStreamFinishBox(&s, mdhd);
1285
1286 avifBoxMarker hdlrTrak = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0);
1287 avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0;
1288 avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type;
1289 avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0;
1290 avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator)
1291 avifRWStreamFinishBox(&s, hdlrTrak);
1292
1293 avifBoxMarker minf = avifRWStreamWriteBox(&s, "minf", AVIF_BOX_SIZE_TBD);
1294
1295 avifBoxMarker vmhd = avifRWStreamWriteFullBox(&s, "vmhd", AVIF_BOX_SIZE_TBD, 0, 1);
1296 avifRWStreamWriteU16(&s, 0); // template unsigned int(16) graphicsmode = 0; (copy over the existing image)
1297 avifRWStreamWriteZeros(&s, 6); // template unsigned int(16)[3] opcolor = {0, 0, 0};
1298 avifRWStreamFinishBox(&s, vmhd);
1299
Joe Drago20ec1762020-06-03 17:34:43 -07001300 avifBoxMarker dinf = avifRWStreamWriteBox(&s, "dinf", AVIF_BOX_SIZE_TBD);
1301 avifBoxMarker dref = avifRWStreamWriteFullBox(&s, "dref", AVIF_BOX_SIZE_TBD, 0, 0);
1302 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
1303 avifRWStreamWriteFullBox(&s, "url ", 0, 0, 1); // flags:1 means data is in this file
1304 avifRWStreamFinishBox(&s, dref);
1305 avifRWStreamFinishBox(&s, dinf);
1306
Joe Drago4a25c192020-06-03 16:29:58 -07001307 avifBoxMarker stbl = avifRWStreamWriteBox(&s, "stbl", AVIF_BOX_SIZE_TBD);
1308
1309 avifBoxMarker stco = avifRWStreamWriteFullBox(&s, "stco", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Dragoa72da5b2020-06-15 19:40:17 -07001310 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
1311 avifEncoderItemAddMdatFixup(item, &s); //
1312 avifRWStreamWriteU32(&s, 1); // unsigned int(32) chunk_offset; (set later)
Joe Drago4a25c192020-06-03 16:29:58 -07001313 avifRWStreamFinishBox(&s, stco);
1314
1315 avifBoxMarker stsc = avifRWStreamWriteFullBox(&s, "stsc", AVIF_BOX_SIZE_TBD, 0, 0);
1316 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
1317 avifRWStreamWriteU32(&s, 1); // unsigned int(32) first_chunk;
1318 avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) samples_per_chunk;
1319 avifRWStreamWriteU32(&s, 1); // unsigned int(32) sample_description_index;
1320 avifRWStreamFinishBox(&s, stsc);
1321
1322 avifBoxMarker stsz = avifRWStreamWriteFullBox(&s, "stsz", AVIF_BOX_SIZE_TBD, 0, 0);
1323 avifRWStreamWriteU32(&s, 0); // unsigned int(32) sample_size;
1324 avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) sample_count;
1325 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
1326 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
1327 avifRWStreamWriteU32(&s, (uint32_t)sample->data.size); // unsigned int(32) entry_size;
1328 }
1329 avifRWStreamFinishBox(&s, stsz);
1330
1331 avifBoxMarker stss = avifRWStreamWriteFullBox(&s, "stss", AVIF_BOX_SIZE_TBD, 0, 0);
1332 avifRWStreamWriteU32(&s, syncSamplesCount); // unsigned int(32) entry_count;
1333 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
1334 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
1335 if (sample->sync) {
1336 avifRWStreamWriteU32(&s, sampleIndex + 1); // unsigned int(32) sample_number;
1337 }
1338 }
1339 avifRWStreamFinishBox(&s, stss);
1340
Joe Drago4a25c192020-06-03 16:29:58 -07001341 avifBoxMarker stts = avifRWStreamWriteFullBox(&s, "stts", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago85e387a2020-06-03 17:12:53 -07001342 size_t sttsEntryCountOffset = avifRWStreamOffset(&s);
1343 uint32_t sttsEntryCount = 0;
1344 avifRWStreamWriteU32(&s, 0); // unsigned int(32) entry_count;
1345 for (uint32_t sampleCount = 0, frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) {
Joe Drago4a25c192020-06-03 16:29:58 -07001346 avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex];
Joe Drago85e387a2020-06-03 17:12:53 -07001347 ++sampleCount;
1348 if (frameIndex < (encoder->data->frames.count - 1)) {
1349 avifEncoderFrame * nextFrame = &encoder->data->frames.frame[frameIndex + 1];
1350 if (frame->durationInTimescales == nextFrame->durationInTimescales) {
1351 continue;
1352 }
1353 }
1354 avifRWStreamWriteU32(&s, sampleCount); // unsigned int(32) sample_count;
Joe Drago4a25c192020-06-03 16:29:58 -07001355 avifRWStreamWriteU32(&s, (uint32_t)frame->durationInTimescales); // unsigned int(32) sample_delta;
Joe Drago85e387a2020-06-03 17:12:53 -07001356 sampleCount = 0;
1357 ++sttsEntryCount;
Joe Drago4a25c192020-06-03 16:29:58 -07001358 }
Joe Drago85e387a2020-06-03 17:12:53 -07001359 size_t prevOffset = avifRWStreamOffset(&s);
1360 avifRWStreamSetOffset(&s, sttsEntryCountOffset);
1361 avifRWStreamWriteU32(&s, sttsEntryCount);
1362 avifRWStreamSetOffset(&s, prevOffset);
Joe Drago4a25c192020-06-03 16:29:58 -07001363 avifRWStreamFinishBox(&s, stts);
1364
1365 avifBoxMarker stsd = avifRWStreamWriteFullBox(&s, "stsd", AVIF_BOX_SIZE_TBD, 0, 0);
1366 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
1367 avifBoxMarker av01 = avifRWStreamWriteBox(&s, "av01", AVIF_BOX_SIZE_TBD);
1368 avifRWStreamWriteZeros(&s, 6); // const unsigned int(8)[6] reserved = 0;
1369 avifRWStreamWriteU16(&s, 1); // unsigned int(16) data_reference_index;
1370 avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0;
1371 avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0;
1372 avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 3); // unsigned int(32)[3] pre_defined = 0;
1373 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->width); // unsigned int(16) width;
1374 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->height); // unsigned int(16) height;
1375 avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) horizresolution
1376 avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) vertresolution
1377 avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0;
1378 avifRWStreamWriteU16(&s, 1); // template unsigned int(16) frame_count = 1;
Wan-Teh Chang1a32f222020-07-09 15:44:25 -07001379 avifRWStreamWriteChars(&s, "\012AOM Coding", 11); // string[32] compressorname;
1380 avifRWStreamWriteZeros(&s, 32 - 11); //
Joe Drago4a25c192020-06-03 16:29:58 -07001381 avifRWStreamWriteU16(&s, 0x0018); // template unsigned int(16) depth = 0x0018;
1382 avifRWStreamWriteU16(&s, (uint16_t)0xffff); // int(16) pre_defined = -1;
Joe Drago2172ed02020-11-04 18:04:44 -08001383 writeConfigBox(&s, &item->av1C);
Joe Drago332180d2020-06-15 16:55:16 -07001384 if (!item->alpha) {
1385 avifEncoderWriteColorProperties(&s, imageMetadata, NULL, NULL);
1386 }
Vignesh Venkatasubramanianf18f54e2022-02-17 14:10:17 -08001387
1388 avifBoxMarker ccst = avifRWStreamWriteFullBox(&s, "ccst", AVIF_BOX_SIZE_TBD, 0, 0);
1389 const uint8_t ccstValue = (0 << 7) | // unsigned int(1) all_ref_pics_intra;
1390 (1 << 6) | // unsigned int(1) intra_pred_used;
1391 (15 << 2); // unsigned int(4) max_ref_per_pic;
1392 avifRWStreamWriteU8(&s, ccstValue);
1393 avifRWStreamWriteZeros(&s, 3); // unsigned int(26) reserved; (two zero bits are written along with ccstValue).
1394 avifRWStreamFinishBox(&s, ccst);
1395
Joe Drago4a25c192020-06-03 16:29:58 -07001396 avifRWStreamFinishBox(&s, av01);
1397 avifRWStreamFinishBox(&s, stsd);
1398
1399 avifRWStreamFinishBox(&s, stbl);
1400
1401 avifRWStreamFinishBox(&s, minf);
1402 avifRWStreamFinishBox(&s, mdia);
1403 avifRWStreamFinishBox(&s, trak);
1404 }
1405
1406 // -------------------------------------------------------------------
1407 // Finish moov box
1408
1409 avifRWStreamFinishBox(&s, moov);
Joe Drago2c4e7142020-06-03 10:31:46 -07001410 }
1411
1412 // -----------------------------------------------------------------------
Joe Dragoeb652d82019-04-23 16:29:07 -07001413 // Write mdat
1414
Joe Dragoaec9cff2020-12-11 14:23:37 -08001415 encoder->ioStats.colorOBUSize = 0;
1416 encoder->ioStats.alphaOBUSize = 0;
1417
Joe Drago4a25c192020-06-03 16:29:58 -07001418 avifBoxMarker mdat = avifRWStreamWriteBox(&s, "mdat", AVIF_BOX_SIZE_TBD);
Joe Dragoaec9cff2020-12-11 14:23:37 -08001419 const size_t mdatStartOffset = avifRWStreamOffset(&s);
Joe Drago76e421e2020-09-21 13:20:24 -07001420 for (uint32_t itemPasses = 0; itemPasses < 3; ++itemPasses) {
1421 // Use multiple passes to pack in the following order:
1422 // * Pass 0: metadata (Exif/XMP)
1423 // * Pass 1: alpha (AV1)
1424 // * Pass 2: all other item data (AV1 color)
Joe Dragoe888d542020-09-08 20:42:09 -07001425 //
Wan-Teh Chang277d0d72020-10-08 10:24:41 -07001426 // See here for the discussion on alpha coming before color:
Joe Dragoe888d542020-09-08 20:42:09 -07001427 // https://github.com/AOMediaCodec/libavif/issues/287
1428 //
Joe Drago76e421e2020-09-21 13:20:24 -07001429 // Exif and XMP are packed first as they're required to be fully available
1430 // by avifDecoderParse() before it returns AVIF_RESULT_OK, unless ignoreXMP
1431 // and ignoreExif are enabled.
1432 //
1433 const avifBool metadataPass = (itemPasses == 0);
1434 const avifBool alphaPass = (itemPasses == 1);
Joe Drago800b47f2020-03-18 16:22:37 -07001435
Joe Dragoe888d542020-09-08 20:42:09 -07001436 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
1437 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Drago0e13ce12021-04-01 17:18:53 -07001438 const avifBool isGrid = (item->gridCols > 0); // Grids store their payload in metadataPayload, so use this to distinguish grid payloads from XMP/Exif
Joe Dragoe888d542020-09-08 20:42:09 -07001439 if ((item->metadataPayload.size == 0) && (item->encodeOutput->samples.count == 0)) {
Joe Drago76e421e2020-09-21 13:20:24 -07001440 // this item has nothing for the mdat box
Joe Dragoe888d542020-09-08 20:42:09 -07001441 continue;
Joe Drago2c4e7142020-06-03 10:31:46 -07001442 }
Joe Dragoe568f732021-03-31 00:13:37 -07001443 if (!isGrid && (metadataPass != (item->metadataPayload.size > 0))) {
Joe Drago0e13ce12021-04-01 17:18:53 -07001444 // only process metadata (XMP/Exif) payloads when metadataPass is true
Joe Drago76e421e2020-09-21 13:20:24 -07001445 continue;
1446 }
1447 if (alphaPass != item->alpha) {
1448 // only process alpha payloads when alphaPass is true
Joe Dragoe888d542020-09-08 20:42:09 -07001449 continue;
1450 }
Joe Drago800b47f2020-03-18 16:22:37 -07001451
Joe Dragoaec9cff2020-12-11 14:23:37 -08001452 size_t chunkOffset = 0;
1453
1454 // Deduplication - See if an identical chunk to this has already been written
Qiang Zhou295cab32020-12-31 23:20:37 +08001455 if (item->encodeOutput->samples.count > 0) {
Joe Dragoaec9cff2020-12-11 14:23:37 -08001456 avifEncodeSample * sample = &item->encodeOutput->samples.sample[0];
1457 chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, sample->data.data, sample->data.size);
Joe Dragoe888d542020-09-08 20:42:09 -07001458 } else {
Joe Dragoaec9cff2020-12-11 14:23:37 -08001459 chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, item->metadataPayload.data, item->metadataPayload.size);
1460 }
1461
1462 if (!chunkOffset) {
1463 // We've never seen this chunk before; write it out
Wan-Teh Chang8adbb972020-12-18 14:12:51 -08001464 chunkOffset = avifRWStreamOffset(&s);
Joe Dragoaec9cff2020-12-11 14:23:37 -08001465 if (item->encodeOutput->samples.count > 0) {
1466 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
1467 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
1468 avifRWStreamWrite(&s, sample->data.data, sample->data.size);
1469
1470 if (item->alpha) {
1471 encoder->ioStats.alphaOBUSize += sample->data.size;
1472 } else {
1473 encoder->ioStats.colorOBUSize += sample->data.size;
1474 }
1475 }
1476 } else {
1477 avifRWStreamWrite(&s, item->metadataPayload.data, item->metadataPayload.size);
1478 }
Joe Dragoe888d542020-09-08 20:42:09 -07001479 }
1480
1481 for (uint32_t fixupIndex = 0; fixupIndex < item->mdatFixups.count; ++fixupIndex) {
1482 avifOffsetFixup * fixup = &item->mdatFixups.fixup[fixupIndex];
1483 size_t prevOffset = avifRWStreamOffset(&s);
1484 avifRWStreamSetOffset(&s, fixup->offset);
Joe Dragoaec9cff2020-12-11 14:23:37 -08001485 avifRWStreamWriteU32(&s, (uint32_t)chunkOffset);
Joe Dragoe888d542020-09-08 20:42:09 -07001486 avifRWStreamSetOffset(&s, prevOffset);
1487 }
Joe Drago800b47f2020-03-18 16:22:37 -07001488 }
Joe Dragof6a42272019-11-21 15:21:41 -08001489 }
Joe Drago345aaa12019-09-25 13:42:12 -07001490 avifRWStreamFinishBox(&s, mdat);
Joe Dragoeb652d82019-04-23 16:29:07 -07001491
1492 // -----------------------------------------------------------------------
1493 // Finish up stream
1494
Joe Drago345aaa12019-09-25 13:42:12 -07001495 avifRWStreamFinishWrite(&s);
Joe Drago0a5a0e42019-04-11 11:32:56 -07001496
Joe Drago250221a2020-06-01 11:11:06 -07001497 return AVIF_RESULT_OK;
1498}
Joe Drago0b05eee2019-06-12 13:24:39 -07001499
Joe Drago250221a2020-06-01 11:11:06 -07001500avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avifRWData * output)
1501{
Joe Dragod6850242020-06-16 16:05:37 -07001502 avifResult addImageResult = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
Joe Drago250221a2020-06-01 11:11:06 -07001503 if (addImageResult != AVIF_RESULT_OK) {
1504 return addImageResult;
1505 }
1506 return avifEncoderFinish(encoder, output);
Joe Drago444f0512019-01-23 17:03:24 -08001507}
1508
Wan-Teh Change184dc12020-05-11 12:47:21 -07001509static avifBool avifImageIsOpaque(const avifImage * image)
Joe Drago444f0512019-01-23 17:03:24 -08001510{
Joe Drago7ad3ad62019-02-07 11:17:34 -08001511 if (!image->alphaPlane) {
1512 return AVIF_TRUE;
1513 }
1514
Joe Drago444f0512019-01-23 17:03:24 -08001515 int maxChannel = (1 << image->depth) - 1;
Joe Drago7ad3ad62019-02-07 11:17:34 -08001516 if (avifImageUsesU16(image)) {
Joe Drago341a6a62019-07-23 16:12:59 -07001517 for (uint32_t j = 0; j < image->height; ++j) {
1518 for (uint32_t i = 0; i < image->width; ++i) {
Joe Drago7ad3ad62019-02-07 11:17:34 -08001519 uint16_t * p = (uint16_t *)&image->alphaPlane[(i * 2) + (j * image->alphaRowBytes)];
1520 if (*p != maxChannel) {
1521 return AVIF_FALSE;
1522 }
1523 }
1524 }
1525 } else {
Joe Drago341a6a62019-07-23 16:12:59 -07001526 for (uint32_t j = 0; j < image->height; ++j) {
1527 for (uint32_t i = 0; i < image->width; ++i) {
Joe Drago7ad3ad62019-02-07 11:17:34 -08001528 if (image->alphaPlane[i + (j * image->alphaRowBytes)] != maxChannel) {
1529 return AVIF_FALSE;
1530 }
Joe Drago444f0512019-01-23 17:03:24 -08001531 }
1532 }
1533 }
1534 return AVIF_TRUE;
1535}
Joe Drago15857972019-02-13 17:48:28 -08001536
Joe Drago345aaa12019-09-25 13:42:12 -07001537static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg)
Joe Drago15857972019-02-13 17:48:28 -08001538{
Joe Drago4a25c192020-06-03 16:29:58 -07001539 avifBoxMarker av1C = avifRWStreamWriteBox(s, "av1C", AVIF_BOX_SIZE_TBD);
Joe Drago15857972019-02-13 17:48:28 -08001540
1541 // unsigned int (1) marker = 1;
1542 // unsigned int (7) version = 1;
Joe Drago345aaa12019-09-25 13:42:12 -07001543 avifRWStreamWriteU8(s, 0x80 | 0x1);
Joe Drago15857972019-02-13 17:48:28 -08001544
1545 // unsigned int (3) seq_profile;
1546 // unsigned int (5) seq_level_idx_0;
Joe Drago345aaa12019-09-25 13:42:12 -07001547 avifRWStreamWriteU8(s, (uint8_t)((cfg->seqProfile & 0x7) << 5) | (uint8_t)(cfg->seqLevelIdx0 & 0x1f));
Joe Drago15857972019-02-13 17:48:28 -08001548
1549 uint8_t bits = 0;
1550 bits |= (cfg->seqTier0 & 0x1) << 7; // unsigned int (1) seq_tier_0;
1551 bits |= (cfg->highBitdepth & 0x1) << 6; // unsigned int (1) high_bitdepth;
1552 bits |= (cfg->twelveBit & 0x1) << 5; // unsigned int (1) twelve_bit;
1553 bits |= (cfg->monochrome & 0x1) << 4; // unsigned int (1) monochrome;
1554 bits |= (cfg->chromaSubsamplingX & 0x1) << 3; // unsigned int (1) chroma_subsampling_x;
1555 bits |= (cfg->chromaSubsamplingY & 0x1) << 2; // unsigned int (1) chroma_subsampling_y;
1556 bits |= (cfg->chromaSamplePosition & 0x3); // unsigned int (2) chroma_sample_position;
Joe Drago345aaa12019-09-25 13:42:12 -07001557 avifRWStreamWriteU8(s, bits);
Joe Drago15857972019-02-13 17:48:28 -08001558
1559 // unsigned int (3) reserved = 0;
1560 // unsigned int (1) initial_presentation_delay_present;
1561 // if (initial_presentation_delay_present) {
1562 // unsigned int (4) initial_presentation_delay_minus_one;
1563 // } else {
1564 // unsigned int (4) reserved = 0;
1565 // }
Joe Drago345aaa12019-09-25 13:42:12 -07001566 avifRWStreamWriteU8(s, 0);
Joe Drago15857972019-02-13 17:48:28 -08001567
Joe Drago345aaa12019-09-25 13:42:12 -07001568 avifRWStreamFinishBox(s, av1C);
Joe Drago15857972019-02-13 17:48:28 -08001569}