blob: 8b5377747af7ed41e960cee8c8f34ddb71f96fad [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
6#include <string.h>
Joe Drago4a25c192020-06-03 16:29:58 -07007#include <time.h>
Joe Drago444f0512019-01-23 17:03:24 -08008
Joe Drago224b0182019-02-08 10:42:29 -08009#define MAX_ASSOCIATIONS 16
10struct ipmaArray
11{
12 uint8_t associations[MAX_ASSOCIATIONS];
Joe Drago0da29972020-04-23 11:55:10 -070013 avifBool essential[MAX_ASSOCIATIONS];
Joe Drago224b0182019-02-08 10:42:29 -080014 uint8_t count;
15};
Joe Drago0da29972020-04-23 11:55:10 -070016static void ipmaPush(struct ipmaArray * ipma, uint8_t assoc, avifBool essential)
Joe Drago224b0182019-02-08 10:42:29 -080017{
18 ipma->associations[ipma->count] = assoc;
Joe Drago0da29972020-04-23 11:55:10 -070019 ipma->essential[ipma->count] = essential;
Joe Drago224b0182019-02-08 10:42:29 -080020 ++ipma->count;
21}
22
Joe Dragocd1e4c32019-02-08 11:26:31 -080023static const char alphaURN[] = URN_ALPHA0;
24static const size_t alphaURNSize = sizeof(alphaURN);
25
Joe Dragof6a42272019-11-21 15:21:41 -080026static const char xmpContentType[] = CONTENT_TYPE_XMP;
27static const size_t xmpContentTypeSize = sizeof(xmpContentType);
28
Wan-Teh Change184dc12020-05-11 12:47:21 -070029static avifBool avifImageIsOpaque(const avifImage * image);
30static void fillConfigBox(avifCodec * codec, const avifImage * image, avifBool alpha);
Joe Drago345aaa12019-09-25 13:42:12 -070031static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg);
Joe Drago444f0512019-01-23 17:03:24 -080032
Joe Drago800b47f2020-03-18 16:22:37 -070033// ---------------------------------------------------------------------------
Joe Drago5b596c42020-06-02 17:13:38 -070034// avifCodecEncodeOutput
35
36avifCodecEncodeOutput * avifCodecEncodeOutputCreate(void)
37{
38 avifCodecEncodeOutput * encodeOutput = (avifCodecEncodeOutput *)avifAlloc(sizeof(avifCodecEncodeOutput));
39 memset(encodeOutput, 0, sizeof(avifCodecEncodeOutput));
40 avifArrayCreate(&encodeOutput->samples, sizeof(avifEncodeSample), 1);
41 return encodeOutput;
42}
43
44void avifCodecEncodeOutputAddSample(avifCodecEncodeOutput * encodeOutput, const uint8_t * data, size_t len, avifBool sync)
45{
46 avifEncodeSample * sample = (avifEncodeSample *)avifArrayPushPtr(&encodeOutput->samples);
47 avifRWDataSet(&sample->data, data, len);
48 sample->sync = sync;
49}
50
51void avifCodecEncodeOutputDestroy(avifCodecEncodeOutput * encodeOutput)
52{
53 for (uint32_t sampleIndex = 0; sampleIndex < encodeOutput->samples.count; ++sampleIndex) {
54 avifRWDataFree(&encodeOutput->samples.sample[sampleIndex].data);
55 }
56 avifArrayDestroy(&encodeOutput->samples);
57 avifFree(encodeOutput);
58}
59
60// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -070061// avifEncoderItem
62
63// one "item" worth for encoder
64typedef struct avifEncoderItem
65{
66 uint16_t id;
67 uint8_t type[4];
Joe Drago5b596c42020-06-02 17:13:38 -070068 avifCodec * codec; // only present on type==av01
69 avifCodecEncodeOutput * encodeOutput; // AV1 sample data for image sequences
70 avifRWData content; // OBU data on av01 items-based image, metadata payload for Exif/XMP
Joe Drago800b47f2020-03-18 16:22:37 -070071 avifBool alpha;
72
73 const char * infeName;
74 size_t infeNameSize;
75 const char * infeContentType;
76 size_t infeContentTypeSize;
77 size_t infeOffsetOffset; // Stream offset where infe offset was written, so it can be properly set after mdat is written
Joe Drago4a25c192020-06-03 16:29:58 -070078 size_t stcoOffsetOffset; // Stream offset where stream chunk offset was written, so it can be properly set after mdat is written
Joe Drago800b47f2020-03-18 16:22:37 -070079
80 uint16_t irefToID; // if non-zero, make an iref from this id -> irefToID
81 const char * irefType;
82
83 struct ipmaArray ipma;
84} avifEncoderItem;
85AVIF_ARRAY_DECLARE(avifEncoderItemArray, avifEncoderItem, item);
86
87// ---------------------------------------------------------------------------
Joe Drago8210c1b2020-06-02 17:40:28 -070088// avifEncoderFrame
89
90typedef struct avifEncoderFrame
91{
Joe Drago4a25c192020-06-03 16:29:58 -070092 uint64_t durationInTimescales;
Joe Drago8210c1b2020-06-02 17:40:28 -070093} avifEncoderFrame;
94AVIF_ARRAY_DECLARE(avifEncoderFrameArray, avifEncoderFrame, frame);
95
96// ---------------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -070097// avifEncoderData
98
99typedef struct avifEncoderData
100{
101 avifEncoderItemArray items;
Joe Drago8210c1b2020-06-02 17:40:28 -0700102 avifEncoderFrameArray frames;
Joe Drago250221a2020-06-01 11:11:06 -0700103 avifImage * imageMetadata;
104 avifEncoderItem * colorItem;
105 avifEncoderItem * alphaItem;
Joe Drago800b47f2020-03-18 16:22:37 -0700106 uint16_t lastItemID;
107 uint16_t primaryItemID;
108} avifEncoderData;
109
110static avifEncoderData * avifEncoderDataCreate()
111{
112 avifEncoderData * data = (avifEncoderData *)avifAlloc(sizeof(avifEncoderData));
113 memset(data, 0, sizeof(avifEncoderData));
Joe Drago250221a2020-06-01 11:11:06 -0700114 data->imageMetadata = avifImageCreateEmpty();
Joe Drago800b47f2020-03-18 16:22:37 -0700115 avifArrayCreate(&data->items, sizeof(avifEncoderItem), 8);
Joe Drago8210c1b2020-06-02 17:40:28 -0700116 avifArrayCreate(&data->frames, sizeof(avifEncoderFrame), 1);
Joe Drago800b47f2020-03-18 16:22:37 -0700117 return data;
118}
119
120static avifEncoderItem * avifEncoderDataCreateItem(avifEncoderData * data, const char * type, const char * infeName, size_t infeNameSize)
121{
122 avifEncoderItem * item = (avifEncoderItem *)avifArrayPushPtr(&data->items);
123 ++data->lastItemID;
124 item->id = data->lastItemID;
125 memcpy(item->type, type, sizeof(item->type));
126 item->infeName = infeName;
127 item->infeNameSize = infeNameSize;
Joe Drago5b596c42020-06-02 17:13:38 -0700128 item->encodeOutput = avifCodecEncodeOutputCreate();
Joe Drago800b47f2020-03-18 16:22:37 -0700129 return item;
130}
131
132static void avifEncoderDataDestroy(avifEncoderData * data)
133{
134 for (uint32_t i = 0; i < data->items.count; ++i) {
135 avifEncoderItem * item = &data->items.item[i];
136 if (item->codec) {
137 avifCodecDestroy(item->codec);
138 }
Joe Drago5b596c42020-06-02 17:13:38 -0700139 avifCodecEncodeOutputDestroy(item->encodeOutput);
Joe Drago800b47f2020-03-18 16:22:37 -0700140 avifRWDataFree(&item->content);
141 }
Joe Drago250221a2020-06-01 11:11:06 -0700142 avifImageDestroy(data->imageMetadata);
Joe Drago800b47f2020-03-18 16:22:37 -0700143 avifArrayDestroy(&data->items);
Joe Drago8210c1b2020-06-02 17:40:28 -0700144 avifArrayDestroy(&data->frames);
Joe Drago800b47f2020-03-18 16:22:37 -0700145 avifFree(data);
146}
147
148// ---------------------------------------------------------------------------
149
Joe Drago0b05eee2019-06-12 13:24:39 -0700150avifEncoder * avifEncoderCreate(void)
151{
152 avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder));
153 memset(encoder, 0, sizeof(avifEncoder));
154 encoder->maxThreads = 1;
Joe Drago46ea0582019-07-22 15:55:47 -0700155 encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
156 encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
Joe Drago56fa2bc2020-03-03 13:29:48 -0800157 encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
158 encoder->maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
Joe Drago4bf01872019-11-15 18:09:19 -0800159 encoder->tileRowsLog2 = 0;
160 encoder->tileColsLog2 = 0;
161 encoder->speed = AVIF_SPEED_DEFAULT;
Joe Drago800b47f2020-03-18 16:22:37 -0700162 encoder->data = avifEncoderDataCreate();
Joe Drago4a25c192020-06-03 16:29:58 -0700163 encoder->timescale = 1;
Joe Drago0b05eee2019-06-12 13:24:39 -0700164 return encoder;
165}
166
167void avifEncoderDestroy(avifEncoder * encoder)
168{
Joe Drago800b47f2020-03-18 16:22:37 -0700169 avifEncoderDataDestroy(encoder->data);
Joe Drago0b05eee2019-06-12 13:24:39 -0700170 avifFree(encoder);
171}
172
Joe Drago4a25c192020-06-03 16:29:58 -0700173static avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales)
Joe Drago444f0512019-01-23 17:03:24 -0800174{
Joe Drago250221a2020-06-01 11:11:06 -0700175 // -----------------------------------------------------------------------
176 // Validate image
177
Joe Drago7ad3ad62019-02-07 11:17:34 -0800178 if ((image->depth != 8) && (image->depth != 10) && (image->depth != 12)) {
179 return AVIF_RESULT_UNSUPPORTED_DEPTH;
180 }
181
wantehchangcbfab6b2020-03-11 11:53:53 -0700182 if (!image->width || !image->height || !image->yuvPlanes[AVIF_CHAN_Y]) {
Joe Drago250221a2020-06-01 11:11:06 -0700183 return AVIF_RESULT_NO_CONTENT;
Joe Drago444f0512019-01-23 17:03:24 -0800184 }
185
wantehchangcbfab6b2020-03-11 11:53:53 -0700186 if (image->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
Joe Drago250221a2020-06-01 11:11:06 -0700187 return AVIF_RESULT_NO_YUV_FORMAT_SELECTED;
188 }
189
190 // -----------------------------------------------------------------------
191
Joe Drago4a25c192020-06-03 16:29:58 -0700192 if (durationInTimescales == 0) {
193 durationInTimescales = 1;
Joe Drago8210c1b2020-06-02 17:40:28 -0700194 }
195
Joe Drago250221a2020-06-01 11:11:06 -0700196 if (encoder->data->items.count == 0) {
197 // Make a copy of the first image's metadata (sans pixels) for future writing/validation
198 avifImageCopy(encoder->data->imageMetadata, image, 0);
199
200 // Prepare all AV1 items
201
202 encoder->data->colorItem = avifEncoderDataCreateItem(encoder->data, "av01", "Color", 6);
203 encoder->data->colorItem->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
204 if (!encoder->data->colorItem->codec) {
205 // Just bail out early, we're not surviving this function without an encoder compiled in
206 return AVIF_RESULT_NO_CODEC_AVAILABLE;
207 }
208 encoder->data->primaryItemID = encoder->data->colorItem->id;
209
210 avifBool imageIsOpaque = avifImageIsOpaque(image);
211 if (!imageIsOpaque) {
212 encoder->data->alphaItem = avifEncoderDataCreateItem(encoder->data, "av01", "Alpha", 6);
213 encoder->data->alphaItem->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE);
214 if (!encoder->data->alphaItem->codec) {
215 return AVIF_RESULT_NO_CODEC_AVAILABLE;
216 }
217 encoder->data->alphaItem->alpha = AVIF_TRUE;
218 encoder->data->alphaItem->irefToID = encoder->data->primaryItemID;
219 encoder->data->alphaItem->irefType = "auxl";
220 }
221
222 // -----------------------------------------------------------------------
223 // Create metadata items (Exif, XMP)
224
225 if (image->exif.size > 0) {
226 // Validate Exif payload (if any) and find TIFF header offset
227 uint32_t exifTiffHeaderOffset = 0;
228 if (image->exif.size > 0) {
229 if (image->exif.size < 4) {
230 // Can't even fit the TIFF header, something is wrong
231 return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
232 }
233
234 const uint8_t tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
235 const uint8_t tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
236 for (; exifTiffHeaderOffset < (image->exif.size - 4); ++exifTiffHeaderOffset) {
237 if (!memcmp(&image->exif.data[exifTiffHeaderOffset], tiffHeaderBE, sizeof(tiffHeaderBE))) {
238 break;
239 }
240 if (!memcmp(&image->exif.data[exifTiffHeaderOffset], tiffHeaderLE, sizeof(tiffHeaderLE))) {
241 break;
242 }
243 }
244
245 if (exifTiffHeaderOffset >= image->exif.size - 4) {
246 // Couldn't find the TIFF header
247 return AVIF_RESULT_INVALID_EXIF_PAYLOAD;
248 }
249 }
250
251 avifEncoderItem * exifItem = avifEncoderDataCreateItem(encoder->data, "Exif", "Exif", 5);
252 exifItem->irefToID = encoder->data->primaryItemID;
253 exifItem->irefType = "cdsc";
254
255 avifRWDataRealloc(&exifItem->content, sizeof(uint32_t) + image->exif.size);
256 exifTiffHeaderOffset = avifHTONL(exifTiffHeaderOffset);
257 memcpy(exifItem->content.data, &exifTiffHeaderOffset, sizeof(uint32_t));
258 memcpy(exifItem->content.data + sizeof(uint32_t), image->exif.data, image->exif.size);
259 }
260
261 if (image->xmp.size > 0) {
262 avifEncoderItem * xmpItem = avifEncoderDataCreateItem(encoder->data, "mime", "XMP", 4);
263 xmpItem->irefToID = encoder->data->primaryItemID;
264 xmpItem->irefType = "cdsc";
265
266 xmpItem->infeContentType = xmpContentType;
267 xmpItem->infeContentTypeSize = xmpContentTypeSize;
268 avifRWDataSet(&xmpItem->content, image->xmp.data, image->xmp.size);
269 }
270
271 // -----------------------------------------------------------------------
272 // Pre-fill config boxes based on image (codec can query/update later)
273
274 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
275 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
276 if (item->codec) {
277 fillConfigBox(item->codec, image, item->alpha);
278 }
279 }
280 } else {
281 // Another frame in an image sequence
wantehchangcbfab6b2020-03-11 11:53:53 -0700282 }
283
Joe Drago444f0512019-01-23 17:03:24 -0800284 // -----------------------------------------------------------------------
285 // Encode AV1 OBUs
286
Joe Drago800b47f2020-03-18 16:22:37 -0700287 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
288 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Drago250221a2020-06-01 11:11:06 -0700289 if (item->codec) {
Joe Drago5b596c42020-06-02 17:13:38 -0700290 if (!item->codec->encodeImage(item->codec, image, encoder, item->alpha, item->encodeOutput)) {
Joe Drago250221a2020-06-01 11:11:06 -0700291 return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
292 }
293 }
294 }
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700295
Joe Drago8210c1b2020-06-02 17:40:28 -0700296 avifEncoderFrame * frame = (avifEncoderFrame *)avifArrayPushPtr(&encoder->data->frames);
Joe Drago4a25c192020-06-03 16:29:58 -0700297 frame->durationInTimescales = durationInTimescales;
Joe Drago250221a2020-06-01 11:11:06 -0700298 return AVIF_RESULT_OK;
299}
300
301static avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output)
302{
303 if (encoder->data->items.count < 1) {
304 return AVIF_RESULT_NO_CONTENT;
305 }
306
307 // -----------------------------------------------------------------------
308 // Finish up AV1 encoding
309
310 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
311 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
312 if (item->codec) {
Joe Drago5b596c42020-06-02 17:13:38 -0700313 if (!item->codec->encodeFinish(item->codec, item->encodeOutput)) {
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700314 return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
315 }
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700316
Joe Drago8210c1b2020-06-02 17:40:28 -0700317 if (item->encodeOutput->samples.count != encoder->data->frames.count) {
Joe Drago250221a2020-06-01 11:11:06 -0700318 return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED;
Joe Drago800b47f2020-03-18 16:22:37 -0700319 }
Joe Drago46ea0582019-07-22 15:55:47 -0700320
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700321 size_t obuSize = 0;
Joe Drago5b596c42020-06-02 17:13:38 -0700322 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
323 obuSize += item->encodeOutput->samples.sample[sampleIndex].data.size;
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700324 }
Joe Drago800b47f2020-03-18 16:22:37 -0700325 if (item->alpha) {
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700326 encoder->ioStats.alphaOBUSize = obuSize;
Joe Drago800b47f2020-03-18 16:22:37 -0700327 } else {
Joe Dragob3fdc9e2020-06-01 12:33:16 -0700328 encoder->ioStats.colorOBUSize = obuSize;
329 }
Joe Drago46ea0582019-07-22 15:55:47 -0700330 }
331 }
332
Joe Drago444f0512019-01-23 17:03:24 -0800333 // -----------------------------------------------------------------------
Joe Drago250221a2020-06-01 11:11:06 -0700334 // Begin write stream
335
336 avifImage * imageMetadata = encoder->data->imageMetadata;
Joe Drago4a25c192020-06-03 16:29:58 -0700337 uint64_t now = (uint64_t)time(NULL);
Joe Drago250221a2020-06-01 11:11:06 -0700338
339 avifRWStream s;
340 avifRWStreamStart(&s, output);
341
342 // -----------------------------------------------------------------------
Joe Drago444f0512019-01-23 17:03:24 -0800343 // Write ftyp
344
Joe Drago4a25c192020-06-03 16:29:58 -0700345 const char * majorBrand = "avif";
346 if (encoder->data->frames.count > 1) {
347 majorBrand = "avis";
348 }
349
350 avifBoxMarker ftyp = avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD);
351 avifRWStreamWriteChars(&s, majorBrand, 4); // unsigned int(32) major_brand;
Joe Drago250221a2020-06-01 11:11:06 -0700352 avifRWStreamWriteU32(&s, 0); // unsigned int(32) minor_version;
353 avifRWStreamWriteChars(&s, "avif", 4); // unsigned int(32) compatible_brands[];
Joe Drago4a25c192020-06-03 16:29:58 -0700354 if (encoder->data->frames.count > 1) { //
355 avifRWStreamWriteChars(&s, "avis", 4); // ... compatible_brands[]
Joe Drago70f5a2e2020-06-03 17:54:19 -0700356 avifRWStreamWriteChars(&s, "msf1", 4); // ... compatible_brands[]
Joe Drago4a25c192020-06-03 16:29:58 -0700357 } //
Joe Drago250221a2020-06-01 11:11:06 -0700358 avifRWStreamWriteChars(&s, "mif1", 4); // ... compatible_brands[]
359 avifRWStreamWriteChars(&s, "miaf", 4); // ... compatible_brands[]
360 if ((imageMetadata->depth == 8) || (imageMetadata->depth == 10)) { //
361 if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { //
362 avifRWStreamWriteChars(&s, "MA1B", 4); // ... compatible_brands[]
363 } else if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { //
364 avifRWStreamWriteChars(&s, "MA1A", 4); // ... compatible_brands[]
Joe Dragoeb652d82019-04-23 16:29:07 -0700365 }
366 }
Joe Drago345aaa12019-09-25 13:42:12 -0700367 avifRWStreamFinishBox(&s, ftyp);
Joe Drago444f0512019-01-23 17:03:24 -0800368
369 // -----------------------------------------------------------------------
Joe Drago444f0512019-01-23 17:03:24 -0800370 // Start meta
371
Joe Drago4a25c192020-06-03 16:29:58 -0700372 avifBoxMarker meta = avifRWStreamWriteFullBox(&s, "meta", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago444f0512019-01-23 17:03:24 -0800373
374 // -----------------------------------------------------------------------
375 // Write hdlr
376
Joe Drago4a25c192020-06-03 16:29:58 -0700377 avifBoxMarker hdlr = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago345aaa12019-09-25 13:42:12 -0700378 avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0;
379 avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type;
380 avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0;
381 avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator)
382 avifRWStreamFinishBox(&s, hdlr);
Joe Drago444f0512019-01-23 17:03:24 -0800383
384 // -----------------------------------------------------------------------
385 // Write pitm
386
Joe Drago800b47f2020-03-18 16:22:37 -0700387 if (encoder->data->primaryItemID != 0) {
Joe Drago4a25c192020-06-03 16:29:58 -0700388 avifRWStreamWriteFullBox(&s, "pitm", sizeof(uint16_t), 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700389 avifRWStreamWriteU16(&s, encoder->data->primaryItemID); // unsigned int(16) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -0800390 }
391
392 // -----------------------------------------------------------------------
Joe Drago444f0512019-01-23 17:03:24 -0800393 // Write iloc
394
Joe Drago4a25c192020-06-03 16:29:58 -0700395 avifBoxMarker iloc = avifRWStreamWriteFullBox(&s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago444f0512019-01-23 17:03:24 -0800396
Joe Drago800b47f2020-03-18 16:22:37 -0700397 uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size;
398 // unsigned int(4) length_size;
399 avifRWStreamWrite(&s, &offsetSizeAndLengthSize, 1); //
400 avifRWStreamWriteZeros(&s, 1); // unsigned int(4) base_offset_size;
401 // unsigned int(4) reserved;
402 avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) item_count;
Joe Drago444f0512019-01-23 17:03:24 -0800403
Joe Drago800b47f2020-03-18 16:22:37 -0700404 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
405 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Drago2c4e7142020-06-03 10:31:46 -0700406
407 uint32_t contentSize = (uint32_t)item->content.size;
408 if (item->encodeOutput->samples.count > 0) {
409 contentSize = (uint32_t)item->encodeOutput->samples.sample[0].data.size;
410 }
411
412 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
413 avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index;
414 avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count;
415 item->infeOffsetOffset = avifRWStreamOffset(&s); //
416 avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset;
417 avifRWStreamWriteU32(&s, (uint32_t)contentSize); // unsigned int(length_size*8) extent_length;
Joe Dragof6a42272019-11-21 15:21:41 -0800418 }
419
Joe Drago345aaa12019-09-25 13:42:12 -0700420 avifRWStreamFinishBox(&s, iloc);
Joe Drago444f0512019-01-23 17:03:24 -0800421
422 // -----------------------------------------------------------------------
423 // Write iinf
424
Joe Drago4a25c192020-06-03 16:29:58 -0700425 avifBoxMarker iinf = avifRWStreamWriteFullBox(&s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700426 avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) entry_count;
Joe Drago444f0512019-01-23 17:03:24 -0800427
Joe Drago800b47f2020-03-18 16:22:37 -0700428 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
429 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
430
Joe Drago4a25c192020-06-03 16:29:58 -0700431 avifBoxMarker infe = avifRWStreamWriteFullBox(&s, "infe", AVIF_BOX_SIZE_TBD, 2, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700432 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
Joe Dragof6a42272019-11-21 15:21:41 -0800433 avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index;
Joe Drago800b47f2020-03-18 16:22:37 -0700434 avifRWStreamWrite(&s, item->type, 4); // unsigned int(32) item_type;
435 avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator)
436 if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator)
437 avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize);
438 }
439 avifRWStreamFinishBox(&s, infe);
Joe Drago444f0512019-01-23 17:03:24 -0800440 }
Joe Drago800b47f2020-03-18 16:22:37 -0700441
Joe Drago345aaa12019-09-25 13:42:12 -0700442 avifRWStreamFinishBox(&s, iinf);
Joe Drago444f0512019-01-23 17:03:24 -0800443
444 // -----------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700445 // Write iref boxes
Joe Drago8f7a3002019-02-07 19:35:37 -0800446
Joe Drago800b47f2020-03-18 16:22:37 -0700447 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
448 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
449 if (item->irefToID != 0) {
Joe Drago4a25c192020-06-03 16:29:58 -0700450 avifBoxMarker iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0);
451 avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD);
Joe Drago800b47f2020-03-18 16:22:37 -0700452 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID;
453 avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
454 avifRWStreamWriteU16(&s, item->irefToID); // unsigned int(16) to_item_ID;
455 avifRWStreamFinishBox(&s, refType);
456 avifRWStreamFinishBox(&s, iref);
457 }
Joe Drago8f7a3002019-02-07 19:35:37 -0800458 }
459
460 // -----------------------------------------------------------------------
Joe Drago800b47f2020-03-18 16:22:37 -0700461 // Write iprp -> ipco/ipma
Joe Drago444f0512019-01-23 17:03:24 -0800462
Joe Drago4a25c192020-06-03 16:29:58 -0700463 avifBoxMarker iprp = avifRWStreamWriteBox(&s, "iprp", AVIF_BOX_SIZE_TBD);
Joe Drago224b0182019-02-08 10:42:29 -0800464
Joe Drago800b47f2020-03-18 16:22:37 -0700465 uint8_t itemPropertyIndex = 0;
Joe Drago4a25c192020-06-03 16:29:58 -0700466 avifBoxMarker ipco = avifRWStreamWriteBox(&s, "ipco", AVIF_BOX_SIZE_TBD);
Joe Drago800b47f2020-03-18 16:22:37 -0700467 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
468 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
469 memset(&item->ipma, 0, sizeof(item->ipma));
Joe Drago250221a2020-06-01 11:11:06 -0700470 if (!item->codec) {
Joe Drago800b47f2020-03-18 16:22:37 -0700471 // No ipma to write for this item
472 continue;
473 }
Joe Dragoe4dba562019-02-07 21:52:32 -0800474
Joe Drago800b47f2020-03-18 16:22:37 -0700475 // Properties all av01 items need
476
Joe Drago4a25c192020-06-03 16:29:58 -0700477 avifBoxMarker ispe = avifRWStreamWriteFullBox(&s, "ispe", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago250221a2020-06-01 11:11:06 -0700478 avifRWStreamWriteU32(&s, imageMetadata->width); // unsigned int(32) image_width;
479 avifRWStreamWriteU32(&s, imageMetadata->height); // unsigned int(32) image_height;
Joe Drago800b47f2020-03-18 16:22:37 -0700480 avifRWStreamFinishBox(&s, ispe);
Joe Drago0da29972020-04-23 11:55:10 -0700481 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE); // ipma is 1-indexed, doing this afterwards is correct
Joe Drago800b47f2020-03-18 16:22:37 -0700482
483 uint8_t channelCount = item->alpha ? 1 : 3; // TODO: write the correct value here when adding monochrome support
Joe Drago4a25c192020-06-03 16:29:58 -0700484 avifBoxMarker pixi = avifRWStreamWriteFullBox(&s, "pixi", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700485 avifRWStreamWriteU8(&s, channelCount); // unsigned int (8) num_channels;
486 for (uint8_t chan = 0; chan < channelCount; ++chan) {
Joe Drago250221a2020-06-01 11:11:06 -0700487 avifRWStreamWriteU8(&s, (uint8_t)imageMetadata->depth); // unsigned int (8) bits_per_channel;
Joe Drago800b47f2020-03-18 16:22:37 -0700488 }
489 avifRWStreamFinishBox(&s, pixi);
Joe Drago0da29972020-04-23 11:55:10 -0700490 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
Joe Drago800b47f2020-03-18 16:22:37 -0700491
492 writeConfigBox(&s, &item->codec->configBox);
Joe Drago0da29972020-04-23 11:55:10 -0700493 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_TRUE);
Joe Drago800b47f2020-03-18 16:22:37 -0700494
495 if (item->alpha) {
496 // Alpha specific properties
497
Joe Drago4a25c192020-06-03 16:29:58 -0700498 avifBoxMarker auxC = avifRWStreamWriteFullBox(&s, "auxC", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700499 avifRWStreamWriteChars(&s, alphaURN, alphaURNSize); // string aux_type;
500 avifRWStreamFinishBox(&s, auxC);
Joe Drago0da29972020-04-23 11:55:10 -0700501 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
Joe Drago800b47f2020-03-18 16:22:37 -0700502 } else {
503 // Color specific properties
504
Joe Drago250221a2020-06-01 11:11:06 -0700505 if (imageMetadata->icc.data && (imageMetadata->icc.size > 0)) {
Joe Drago4a25c192020-06-03 16:29:58 -0700506 avifBoxMarker colr = avifRWStreamWriteBox(&s, "colr", AVIF_BOX_SIZE_TBD);
Joe Drago345aaa12019-09-25 13:42:12 -0700507 avifRWStreamWriteChars(&s, "prof", 4); // unsigned int(32) colour_type;
Joe Drago250221a2020-06-01 11:11:06 -0700508 avifRWStreamWrite(&s, imageMetadata->icc.data, imageMetadata->icc.size);
Joe Drago345aaa12019-09-25 13:42:12 -0700509 avifRWStreamFinishBox(&s, colr);
Joe Drago0da29972020-04-23 11:55:10 -0700510 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
Joe Dragoa0da4a42020-05-08 14:27:40 -0700511 } else {
Joe Drago4a25c192020-06-03 16:29:58 -0700512 avifBoxMarker colr = avifRWStreamWriteBox(&s, "colr", AVIF_BOX_SIZE_TBD);
Joe Drago250221a2020-06-01 11:11:06 -0700513 avifRWStreamWriteChars(&s, "nclx", 4); // unsigned int(32) colour_type;
514 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->colorPrimaries); // unsigned int(16) colour_primaries;
515 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->transferCharacteristics); // unsigned int(16) transfer_characteristics;
516 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->matrixCoefficients); // unsigned int(16) matrix_coefficients;
517 avifRWStreamWriteU8(&s, (imageMetadata->yuvRange == AVIF_RANGE_FULL) ? 0x80 : 0); // unsigned int(1) full_range_flag;
518 // unsigned int(7) reserved = 0;
Joe Dragoa0da4a42020-05-08 14:27:40 -0700519 avifRWStreamFinishBox(&s, colr);
520 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
Joe Drago41eb62b2019-02-08 15:38:18 -0800521 }
522
Joe Drago89f0cc82020-03-09 16:13:27 -0700523 // Write (Optional) Transformations
Joe Drago250221a2020-06-01 11:11:06 -0700524 if (imageMetadata->transformFlags & AVIF_TRANSFORM_PASP) {
Joe Drago4a25c192020-06-03 16:29:58 -0700525 avifBoxMarker pasp = avifRWStreamWriteBox(&s, "pasp", AVIF_BOX_SIZE_TBD);
Joe Drago250221a2020-06-01 11:11:06 -0700526 avifRWStreamWriteU32(&s, imageMetadata->pasp.hSpacing); // unsigned int(32) hSpacing;
527 avifRWStreamWriteU32(&s, imageMetadata->pasp.vSpacing); // unsigned int(32) vSpacing;
Joe Drago89f0cc82020-03-09 16:13:27 -0700528 avifRWStreamFinishBox(&s, pasp);
Joe Drago0da29972020-04-23 11:55:10 -0700529 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_FALSE);
Joe Drago89f0cc82020-03-09 16:13:27 -0700530 }
Joe Drago250221a2020-06-01 11:11:06 -0700531 if (imageMetadata->transformFlags & AVIF_TRANSFORM_CLAP) {
Joe Drago4a25c192020-06-03 16:29:58 -0700532 avifBoxMarker clap = avifRWStreamWriteBox(&s, "clap", AVIF_BOX_SIZE_TBD);
Joe Drago250221a2020-06-01 11:11:06 -0700533 avifRWStreamWriteU32(&s, imageMetadata->clap.widthN); // unsigned int(32) cleanApertureWidthN;
534 avifRWStreamWriteU32(&s, imageMetadata->clap.widthD); // unsigned int(32) cleanApertureWidthD;
535 avifRWStreamWriteU32(&s, imageMetadata->clap.heightN); // unsigned int(32) cleanApertureHeightN;
536 avifRWStreamWriteU32(&s, imageMetadata->clap.heightD); // unsigned int(32) cleanApertureHeightD;
537 avifRWStreamWriteU32(&s, imageMetadata->clap.horizOffN); // unsigned int(32) horizOffN;
538 avifRWStreamWriteU32(&s, imageMetadata->clap.horizOffD); // unsigned int(32) horizOffD;
539 avifRWStreamWriteU32(&s, imageMetadata->clap.vertOffN); // unsigned int(32) vertOffN;
540 avifRWStreamWriteU32(&s, imageMetadata->clap.vertOffD); // unsigned int(32) vertOffD;
Joe Drago89f0cc82020-03-09 16:13:27 -0700541 avifRWStreamFinishBox(&s, clap);
Joe Drago0c5d6b42020-04-23 11:57:58 -0700542 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_TRUE);
Joe Drago89f0cc82020-03-09 16:13:27 -0700543 }
Joe Drago250221a2020-06-01 11:11:06 -0700544 if (imageMetadata->transformFlags & AVIF_TRANSFORM_IROT) {
Joe Drago4a25c192020-06-03 16:29:58 -0700545 avifBoxMarker irot = avifRWStreamWriteBox(&s, "irot", AVIF_BOX_SIZE_TBD);
Joe Drago250221a2020-06-01 11:11:06 -0700546 uint8_t angle = imageMetadata->irot.angle & 0x3;
Joe Drago89f0cc82020-03-09 16:13:27 -0700547 avifRWStreamWrite(&s, &angle, 1); // unsigned int (6) reserved = 0; unsigned int (2) angle;
548 avifRWStreamFinishBox(&s, irot);
Joe Drago0c5d6b42020-04-23 11:57:58 -0700549 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_TRUE);
Joe Drago89f0cc82020-03-09 16:13:27 -0700550 }
Joe Drago250221a2020-06-01 11:11:06 -0700551 if (imageMetadata->transformFlags & AVIF_TRANSFORM_IMIR) {
Joe Drago4a25c192020-06-03 16:29:58 -0700552 avifBoxMarker imir = avifRWStreamWriteBox(&s, "imir", AVIF_BOX_SIZE_TBD);
Joe Drago250221a2020-06-01 11:11:06 -0700553 uint8_t axis = imageMetadata->imir.axis & 0x1;
Joe Drago89f0cc82020-03-09 16:13:27 -0700554 avifRWStreamWrite(&s, &axis, 1); // unsigned int (7) reserved = 0; unsigned int (1) axis;
555 avifRWStreamFinishBox(&s, imir);
Joe Drago0c5d6b42020-04-23 11:57:58 -0700556 ipmaPush(&item->ipma, ++itemPropertyIndex, AVIF_TRUE);
Joe Dragoe4dba562019-02-07 21:52:32 -0800557 }
558 }
Joe Dragoe4dba562019-02-07 21:52:32 -0800559 }
Joe Drago800b47f2020-03-18 16:22:37 -0700560 avifRWStreamFinishBox(&s, ipco);
561
Joe Drago4a25c192020-06-03 16:29:58 -0700562 avifBoxMarker ipma = avifRWStreamWriteFullBox(&s, "ipma", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago800b47f2020-03-18 16:22:37 -0700563 {
564 int ipmaCount = 0;
565 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
566 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
567 if (item->ipma.count > 0) {
568 ++ipmaCount;
569 }
570 }
571 avifRWStreamWriteU32(&s, ipmaCount); // unsigned int(32) entry_count;
572
573 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
574 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
575 if (item->ipma.count == 0) {
576 continue;
577 }
578
Joe Drago0da29972020-04-23 11:55:10 -0700579 avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID;
580 avifRWStreamWriteU8(&s, item->ipma.count); // unsigned int(8) association_count;
581 for (int i = 0; i < item->ipma.count; ++i) { //
582 uint8_t essentialAndIndex = item->ipma.associations[i];
583 if (item->ipma.essential[i]) {
584 essentialAndIndex |= 0x80;
585 }
586 avifRWStreamWriteU8(&s, essentialAndIndex); // bit(1) essential; unsigned int(7) property_index;
Joe Drago800b47f2020-03-18 16:22:37 -0700587 }
588 }
589 }
590 avifRWStreamFinishBox(&s, ipma);
591
Joe Drago345aaa12019-09-25 13:42:12 -0700592 avifRWStreamFinishBox(&s, iprp);
Joe Drago444f0512019-01-23 17:03:24 -0800593
594 // -----------------------------------------------------------------------
Joe Dragoeb652d82019-04-23 16:29:07 -0700595 // Finish meta box
Joe Drago444f0512019-01-23 17:03:24 -0800596
Joe Drago345aaa12019-09-25 13:42:12 -0700597 avifRWStreamFinishBox(&s, meta);
Joe Dragoeb652d82019-04-23 16:29:07 -0700598
599 // -----------------------------------------------------------------------
Joe Drago4a25c192020-06-03 16:29:58 -0700600 // Write tracks (if an image sequence)
Joe Drago2c4e7142020-06-03 10:31:46 -0700601
602 if (encoder->data->frames.count > 1) {
Joe Drago4a25c192020-06-03 16:29:58 -0700603 static const uint32_t unityMatrix[9] = { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 };
604
605 uint64_t durationInTimescales = 0;
606 for (uint32_t frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) {
607 avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex];
608 durationInTimescales += frame->durationInTimescales;
609 }
610
611 // -------------------------------------------------------------------
612 // Start moov
613
614 avifBoxMarker moov = avifRWStreamWriteBox(&s, "moov", AVIF_BOX_SIZE_TBD);
615
616 avifBoxMarker mvhd = avifRWStreamWriteFullBox(&s, "mvhd", AVIF_BOX_SIZE_TBD, 1, 0);
617 avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time;
618 avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time;
619 avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale;
620 avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration;
621 avifRWStreamWriteU32(&s, 0x00010000); // template int(32) rate = 0x00010000; // typically 1.0
622 avifRWStreamWriteU16(&s, 0x0100); // template int(16) volume = 0x0100; // typically, full volume
623 avifRWStreamWriteU16(&s, 0); // const bit(16) reserved = 0;
624 avifRWStreamWriteZeros(&s, 8); // const unsigned int(32)[2] reserved = 0;
625 avifRWStreamWrite(&s, (const uint8_t *)unityMatrix, sizeof(unityMatrix));
626 avifRWStreamWriteZeros(&s, 24); // bit(32)[6] pre_defined = 0;
627 avifRWStreamWriteU32(&s, encoder->data->items.count); // unsigned int(32) next_track_ID;
628 avifRWStreamFinishBox(&s, mvhd);
629
630 // -------------------------------------------------------------------
631 // Write tracks
632
633 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
634 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
635 if (item->encodeOutput->samples.count == 0) {
636 continue;
637 }
638
639 uint32_t syncSamplesCount = 0;
640 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
641 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
642 if (sample->sync) {
643 ++syncSamplesCount;
644 }
645 }
646
647 avifBoxMarker trak = avifRWStreamWriteBox(&s, "trak", AVIF_BOX_SIZE_TBD);
648
Joe Drago85e387a2020-06-03 17:12:53 -0700649 avifBoxMarker tkhd = avifRWStreamWriteFullBox(&s, "tkhd", AVIF_BOX_SIZE_TBD, 1, 1);
Joe Drago4a25c192020-06-03 16:29:58 -0700650 avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time;
651 avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time;
652 avifRWStreamWriteU32(&s, itemIndex + 1); // unsigned int(32) track_ID;
653 avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0;
654 avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration;
655 avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 2); // const unsigned int(32)[2] reserved = 0;
656 avifRWStreamWriteU16(&s, 0); // template int(16) layer = 0;
657 avifRWStreamWriteU16(&s, 0); // template int(16) alternate_group = 0;
658 avifRWStreamWriteU16(&s, 0); // template int(16) volume = {if track_is_audio 0x0100 else 0};
659 avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0;
660 avifRWStreamWrite(&s, (const uint8_t *)unityMatrix, sizeof(unityMatrix)); // template int(32)[9] matrix= // { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };
661 avifRWStreamWriteU32(&s, imageMetadata->width << 16); // unsigned int(32) width;
662 avifRWStreamWriteU32(&s, imageMetadata->height << 16); // unsigned int(32) height;
663 avifRWStreamFinishBox(&s, tkhd);
664
665 if (item->irefToID != 0) {
666 avifBoxMarker tref = avifRWStreamWriteBox(&s, "tref", AVIF_BOX_SIZE_TBD);
667 avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD);
668 avifRWStreamWriteU32(&s, (uint32_t)item->irefToID);
669 avifRWStreamFinishBox(&s, refType);
670 avifRWStreamFinishBox(&s, tref);
671 }
672
673 avifBoxMarker mdia = avifRWStreamWriteBox(&s, "mdia", AVIF_BOX_SIZE_TBD);
674
675 avifBoxMarker mdhd = avifRWStreamWriteFullBox(&s, "mdhd", AVIF_BOX_SIZE_TBD, 1, 0);
676 avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time;
677 avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time;
678 avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale;
679 avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration;
680 avifRWStreamWriteU16(&s, 21956); // bit(1) pad = 0; unsigned int(5)[3] language; ("und")
681 avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0;
682 avifRWStreamFinishBox(&s, mdhd);
683
684 avifBoxMarker hdlrTrak = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0);
685 avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0;
686 avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type;
687 avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0;
688 avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator)
689 avifRWStreamFinishBox(&s, hdlrTrak);
690
691 avifBoxMarker minf = avifRWStreamWriteBox(&s, "minf", AVIF_BOX_SIZE_TBD);
692
693 avifBoxMarker vmhd = avifRWStreamWriteFullBox(&s, "vmhd", AVIF_BOX_SIZE_TBD, 0, 1);
694 avifRWStreamWriteU16(&s, 0); // template unsigned int(16) graphicsmode = 0; (copy over the existing image)
695 avifRWStreamWriteZeros(&s, 6); // template unsigned int(16)[3] opcolor = {0, 0, 0};
696 avifRWStreamFinishBox(&s, vmhd);
697
Joe Drago20ec1762020-06-03 17:34:43 -0700698 avifBoxMarker dinf = avifRWStreamWriteBox(&s, "dinf", AVIF_BOX_SIZE_TBD);
699 avifBoxMarker dref = avifRWStreamWriteFullBox(&s, "dref", AVIF_BOX_SIZE_TBD, 0, 0);
700 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
701 avifRWStreamWriteFullBox(&s, "url ", 0, 0, 1); // flags:1 means data is in this file
702 avifRWStreamFinishBox(&s, dref);
703 avifRWStreamFinishBox(&s, dinf);
704
Joe Drago4a25c192020-06-03 16:29:58 -0700705 avifBoxMarker stbl = avifRWStreamWriteBox(&s, "stbl", AVIF_BOX_SIZE_TBD);
706
707 avifBoxMarker stco = avifRWStreamWriteFullBox(&s, "stco", AVIF_BOX_SIZE_TBD, 0, 0);
708 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
709 item->stcoOffsetOffset = avifRWStreamOffset(&s); //
710 avifRWStreamWriteU32(&s, 1); // unsigned int(32) chunk_offset; (set later)
711 avifRWStreamFinishBox(&s, stco);
712
713 avifBoxMarker stsc = avifRWStreamWriteFullBox(&s, "stsc", AVIF_BOX_SIZE_TBD, 0, 0);
714 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
715 avifRWStreamWriteU32(&s, 1); // unsigned int(32) first_chunk;
716 avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) samples_per_chunk;
717 avifRWStreamWriteU32(&s, 1); // unsigned int(32) sample_description_index;
718 avifRWStreamFinishBox(&s, stsc);
719
720 avifBoxMarker stsz = avifRWStreamWriteFullBox(&s, "stsz", AVIF_BOX_SIZE_TBD, 0, 0);
721 avifRWStreamWriteU32(&s, 0); // unsigned int(32) sample_size;
722 avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) sample_count;
723 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
724 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
725 avifRWStreamWriteU32(&s, (uint32_t)sample->data.size); // unsigned int(32) entry_size;
726 }
727 avifRWStreamFinishBox(&s, stsz);
728
729 avifBoxMarker stss = avifRWStreamWriteFullBox(&s, "stss", AVIF_BOX_SIZE_TBD, 0, 0);
730 avifRWStreamWriteU32(&s, syncSamplesCount); // unsigned int(32) entry_count;
731 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
732 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
733 if (sample->sync) {
734 avifRWStreamWriteU32(&s, sampleIndex + 1); // unsigned int(32) sample_number;
735 }
736 }
737 avifRWStreamFinishBox(&s, stss);
738
Joe Drago4a25c192020-06-03 16:29:58 -0700739 avifBoxMarker stts = avifRWStreamWriteFullBox(&s, "stts", AVIF_BOX_SIZE_TBD, 0, 0);
Joe Drago85e387a2020-06-03 17:12:53 -0700740 size_t sttsEntryCountOffset = avifRWStreamOffset(&s);
741 uint32_t sttsEntryCount = 0;
742 avifRWStreamWriteU32(&s, 0); // unsigned int(32) entry_count;
743 for (uint32_t sampleCount = 0, frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) {
Joe Drago4a25c192020-06-03 16:29:58 -0700744 avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex];
Joe Drago85e387a2020-06-03 17:12:53 -0700745 ++sampleCount;
746 if (frameIndex < (encoder->data->frames.count - 1)) {
747 avifEncoderFrame * nextFrame = &encoder->data->frames.frame[frameIndex + 1];
748 if (frame->durationInTimescales == nextFrame->durationInTimescales) {
749 continue;
750 }
751 }
752 avifRWStreamWriteU32(&s, sampleCount); // unsigned int(32) sample_count;
Joe Drago4a25c192020-06-03 16:29:58 -0700753 avifRWStreamWriteU32(&s, (uint32_t)frame->durationInTimescales); // unsigned int(32) sample_delta;
Joe Drago85e387a2020-06-03 17:12:53 -0700754 sampleCount = 0;
755 ++sttsEntryCount;
Joe Drago4a25c192020-06-03 16:29:58 -0700756 }
Joe Drago85e387a2020-06-03 17:12:53 -0700757 size_t prevOffset = avifRWStreamOffset(&s);
758 avifRWStreamSetOffset(&s, sttsEntryCountOffset);
759 avifRWStreamWriteU32(&s, sttsEntryCount);
760 avifRWStreamSetOffset(&s, prevOffset);
Joe Drago4a25c192020-06-03 16:29:58 -0700761 avifRWStreamFinishBox(&s, stts);
762
763 avifBoxMarker stsd = avifRWStreamWriteFullBox(&s, "stsd", AVIF_BOX_SIZE_TBD, 0, 0);
764 avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count;
765 avifBoxMarker av01 = avifRWStreamWriteBox(&s, "av01", AVIF_BOX_SIZE_TBD);
766 avifRWStreamWriteZeros(&s, 6); // const unsigned int(8)[6] reserved = 0;
767 avifRWStreamWriteU16(&s, 1); // unsigned int(16) data_reference_index;
768 avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0;
769 avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0;
770 avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 3); // unsigned int(32)[3] pre_defined = 0;
771 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->width); // unsigned int(16) width;
772 avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->height); // unsigned int(16) height;
773 avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) horizresolution
774 avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) vertresolution
775 avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0;
776 avifRWStreamWriteU16(&s, 1); // template unsigned int(16) frame_count = 1;
777 avifRWStreamWriteZeros(&s, 32); // string[32] compressorname;
778 avifRWStreamWriteU16(&s, 0x0018); // template unsigned int(16) depth = 0x0018;
779 avifRWStreamWriteU16(&s, (uint16_t)0xffff); // int(16) pre_defined = -1;
780 writeConfigBox(&s, &item->codec->configBox);
781 avifRWStreamFinishBox(&s, av01);
782 avifRWStreamFinishBox(&s, stsd);
783
784 avifRWStreamFinishBox(&s, stbl);
785
786 avifRWStreamFinishBox(&s, minf);
787 avifRWStreamFinishBox(&s, mdia);
788 avifRWStreamFinishBox(&s, trak);
789 }
790
791 // -------------------------------------------------------------------
792 // Finish moov box
793
794 avifRWStreamFinishBox(&s, moov);
Joe Drago2c4e7142020-06-03 10:31:46 -0700795 }
796
797 // -----------------------------------------------------------------------
Joe Dragoeb652d82019-04-23 16:29:07 -0700798 // Write mdat
799
Joe Drago4a25c192020-06-03 16:29:58 -0700800 avifBoxMarker mdat = avifRWStreamWriteBox(&s, "mdat", AVIF_BOX_SIZE_TBD);
Joe Drago800b47f2020-03-18 16:22:37 -0700801 for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) {
802 avifEncoderItem * item = &encoder->data->items.item[itemIndex];
Joe Drago2c4e7142020-06-03 10:31:46 -0700803 if ((item->content.size == 0) && (item->encodeOutput->samples.count == 0)) {
Joe Drago800b47f2020-03-18 16:22:37 -0700804 continue;
805 }
806
Joe Drago4a25c192020-06-03 16:29:58 -0700807 uint32_t chunkOffset = (uint32_t)s.offset;
Joe Drago2c4e7142020-06-03 10:31:46 -0700808 if (item->encodeOutput->samples.count > 0) {
809 for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) {
810 avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex];
811 avifRWStreamWrite(&s, sample->data.data, sample->data.size);
812 }
813 } else {
814 avifRWStreamWrite(&s, item->content.data, item->content.size);
815 }
Joe Drago800b47f2020-03-18 16:22:37 -0700816
817 if (item->infeOffsetOffset != 0) {
818 size_t prevOffset = avifRWStreamOffset(&s);
819 avifRWStreamSetOffset(&s, item->infeOffsetOffset);
Joe Drago4a25c192020-06-03 16:29:58 -0700820 avifRWStreamWriteU32(&s, chunkOffset);
821 avifRWStreamSetOffset(&s, prevOffset);
822 }
823
824 if (item->stcoOffsetOffset != 0) {
825 size_t prevOffset = avifRWStreamOffset(&s);
826 avifRWStreamSetOffset(&s, item->stcoOffsetOffset);
827 avifRWStreamWriteU32(&s, chunkOffset);
Joe Drago800b47f2020-03-18 16:22:37 -0700828 avifRWStreamSetOffset(&s, prevOffset);
829 }
Joe Dragof6a42272019-11-21 15:21:41 -0800830 }
Joe Drago345aaa12019-09-25 13:42:12 -0700831 avifRWStreamFinishBox(&s, mdat);
Joe Dragoeb652d82019-04-23 16:29:07 -0700832
833 // -----------------------------------------------------------------------
834 // Finish up stream
835
Joe Drago345aaa12019-09-25 13:42:12 -0700836 avifRWStreamFinishWrite(&s);
Joe Drago0a5a0e42019-04-11 11:32:56 -0700837
Joe Drago250221a2020-06-01 11:11:06 -0700838 return AVIF_RESULT_OK;
839}
Joe Drago0b05eee2019-06-12 13:24:39 -0700840
Joe Drago250221a2020-06-01 11:11:06 -0700841avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avifRWData * output)
842{
Joe Drago4a25c192020-06-03 16:29:58 -0700843 avifResult addImageResult = avifEncoderAddImage(encoder, image, 1);
Joe Drago250221a2020-06-01 11:11:06 -0700844 if (addImageResult != AVIF_RESULT_OK) {
845 return addImageResult;
846 }
847 return avifEncoderFinish(encoder, output);
Joe Drago444f0512019-01-23 17:03:24 -0800848}
849
Wan-Teh Change184dc12020-05-11 12:47:21 -0700850static avifBool avifImageIsOpaque(const avifImage * image)
Joe Drago444f0512019-01-23 17:03:24 -0800851{
Joe Drago7ad3ad62019-02-07 11:17:34 -0800852 if (!image->alphaPlane) {
853 return AVIF_TRUE;
854 }
855
Joe Drago444f0512019-01-23 17:03:24 -0800856 int maxChannel = (1 << image->depth) - 1;
Joe Drago7ad3ad62019-02-07 11:17:34 -0800857 if (avifImageUsesU16(image)) {
Joe Drago341a6a62019-07-23 16:12:59 -0700858 for (uint32_t j = 0; j < image->height; ++j) {
859 for (uint32_t i = 0; i < image->width; ++i) {
Joe Drago7ad3ad62019-02-07 11:17:34 -0800860 uint16_t * p = (uint16_t *)&image->alphaPlane[(i * 2) + (j * image->alphaRowBytes)];
861 if (*p != maxChannel) {
862 return AVIF_FALSE;
863 }
864 }
865 }
866 } else {
Joe Drago341a6a62019-07-23 16:12:59 -0700867 for (uint32_t j = 0; j < image->height; ++j) {
868 for (uint32_t i = 0; i < image->width; ++i) {
Joe Drago7ad3ad62019-02-07 11:17:34 -0800869 if (image->alphaPlane[i + (j * image->alphaRowBytes)] != maxChannel) {
870 return AVIF_FALSE;
871 }
Joe Drago444f0512019-01-23 17:03:24 -0800872 }
873 }
874 }
875 return AVIF_TRUE;
876}
Joe Drago15857972019-02-13 17:48:28 -0800877
Wan-Teh Change184dc12020-05-11 12:47:21 -0700878static void fillConfigBox(avifCodec * codec, const avifImage * image, avifBool alpha)
Joe Dragob749e6b2019-10-30 11:22:18 -0700879{
880 avifPixelFormatInfo formatInfo;
881 avifGetPixelFormatInfo(image->yuvFormat, &formatInfo);
882
883 // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only.
884 // Profile 1. 8-bit and 10-bit 4:4:4
885 // Profile 2. 8-bit and 10-bit 4:2:2
886 // 12-bit 4:0:0, 4:2:2 and 4:4:4
887 uint8_t seqProfile = 0;
888 if (image->depth == 12) {
889 // Only seqProfile 2 can handle 12 bit
890 seqProfile = 2;
891 } else {
892 // 8-bit or 10-bit
893
894 if (alpha) {
895 seqProfile = 0;
896 } else {
897 switch (image->yuvFormat) {
898 case AVIF_PIXEL_FORMAT_YUV444:
899 seqProfile = 1;
900 break;
901 case AVIF_PIXEL_FORMAT_YUV422:
902 seqProfile = 2;
903 break;
904 case AVIF_PIXEL_FORMAT_YUV420:
905 seqProfile = 0;
906 break;
Joe Dragoace02e12020-05-19 12:02:04 -0700907 case AVIF_PIXEL_FORMAT_YUV400:
908 seqProfile = 0;
909 break;
Joe Dragob749e6b2019-10-30 11:22:18 -0700910 case AVIF_PIXEL_FORMAT_YV12:
911 seqProfile = 0;
912 break;
913 case AVIF_PIXEL_FORMAT_NONE:
914 default:
915 break;
916 }
917 }
918 }
919
920 // TODO: Choose correct value from Annex A.3 table: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
921 uint8_t seqLevelIdx0 = 31;
922 if ((image->width <= 8192) && (image->height <= 4352) && ((image->width * image->height) <= 8912896)) {
923 // Image is 5.1 compatible
924 seqLevelIdx0 = 13; // 5.1
925 }
926
927 memset(&codec->configBox, 0, sizeof(avifCodecConfigurationBox));
928 codec->configBox.seqProfile = seqProfile;
929 codec->configBox.seqLevelIdx0 = seqLevelIdx0;
930 codec->configBox.seqTier0 = 0;
931 codec->configBox.highBitdepth = (image->depth > 8) ? 1 : 0;
932 codec->configBox.twelveBit = (image->depth == 12) ? 1 : 0;
933 codec->configBox.monochrome = alpha ? 1 : 0;
934 codec->configBox.chromaSubsamplingX = (uint8_t)formatInfo.chromaShiftX;
935 codec->configBox.chromaSubsamplingY = (uint8_t)formatInfo.chromaShiftY;
936
937 // TODO: choose the correct one from below:
938 // * 0 - CSP_UNKNOWN Unknown (in this case the source video transfer function must be signaled outside the AV1 bitstream)
939 // * 1 - CSP_VERTICAL Horizontally co-located with (0, 0) luma sample, vertical position in the middle between two luma samples
940 // * 2 - CSP_COLOCATED co-located with (0, 0) luma sample
941 // * 3 - CSP_RESERVED
942 codec->configBox.chromaSamplePosition = 0;
943}
944
Joe Drago345aaa12019-09-25 13:42:12 -0700945static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg)
Joe Drago15857972019-02-13 17:48:28 -0800946{
Joe Drago4a25c192020-06-03 16:29:58 -0700947 avifBoxMarker av1C = avifRWStreamWriteBox(s, "av1C", AVIF_BOX_SIZE_TBD);
Joe Drago15857972019-02-13 17:48:28 -0800948
949 // unsigned int (1) marker = 1;
950 // unsigned int (7) version = 1;
Joe Drago345aaa12019-09-25 13:42:12 -0700951 avifRWStreamWriteU8(s, 0x80 | 0x1);
Joe Drago15857972019-02-13 17:48:28 -0800952
953 // unsigned int (3) seq_profile;
954 // unsigned int (5) seq_level_idx_0;
Joe Drago345aaa12019-09-25 13:42:12 -0700955 avifRWStreamWriteU8(s, (uint8_t)((cfg->seqProfile & 0x7) << 5) | (uint8_t)(cfg->seqLevelIdx0 & 0x1f));
Joe Drago15857972019-02-13 17:48:28 -0800956
957 uint8_t bits = 0;
958 bits |= (cfg->seqTier0 & 0x1) << 7; // unsigned int (1) seq_tier_0;
959 bits |= (cfg->highBitdepth & 0x1) << 6; // unsigned int (1) high_bitdepth;
960 bits |= (cfg->twelveBit & 0x1) << 5; // unsigned int (1) twelve_bit;
961 bits |= (cfg->monochrome & 0x1) << 4; // unsigned int (1) monochrome;
962 bits |= (cfg->chromaSubsamplingX & 0x1) << 3; // unsigned int (1) chroma_subsampling_x;
963 bits |= (cfg->chromaSubsamplingY & 0x1) << 2; // unsigned int (1) chroma_subsampling_y;
964 bits |= (cfg->chromaSamplePosition & 0x3); // unsigned int (2) chroma_sample_position;
Joe Drago345aaa12019-09-25 13:42:12 -0700965 avifRWStreamWriteU8(s, bits);
Joe Drago15857972019-02-13 17:48:28 -0800966
967 // unsigned int (3) reserved = 0;
968 // unsigned int (1) initial_presentation_delay_present;
969 // if (initial_presentation_delay_present) {
970 // unsigned int (4) initial_presentation_delay_minus_one;
971 // } else {
972 // unsigned int (4) reserved = 0;
973 // }
Joe Drago345aaa12019-09-25 13:42:12 -0700974 avifRWStreamWriteU8(s, 0);
Joe Drago15857972019-02-13 17:48:28 -0800975
Joe Drago345aaa12019-09-25 13:42:12 -0700976 avifRWStreamFinishBox(s, av1C);
Joe Drago15857972019-02-13 17:48:28 -0800977}