Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1 | // Copyright 2019 Joe Drago. All rights reserved. |
| 2 | // SPDX-License-Identifier: BSD-2-Clause |
| 3 | |
| 4 | #include "avif/internal.h" |
| 5 | |
Joe Drago | ceb2fa0 | 2021-01-29 18:14:55 -0800 | [diff] [blame] | 6 | #include <assert.h> |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 7 | #include <string.h> |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 8 | #include <time.h> |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 9 | |
Joe Drago | 224b018 | 2019-02-08 10:42:29 -0800 | [diff] [blame] | 10 | #define MAX_ASSOCIATIONS 16 |
| 11 | struct ipmaArray |
| 12 | { |
| 13 | uint8_t associations[MAX_ASSOCIATIONS]; |
Joe Drago | 0da2997 | 2020-04-23 11:55:10 -0700 | [diff] [blame] | 14 | avifBool essential[MAX_ASSOCIATIONS]; |
Joe Drago | 224b018 | 2019-02-08 10:42:29 -0800 | [diff] [blame] | 15 | uint8_t count; |
| 16 | }; |
Joe Drago | 0da2997 | 2020-04-23 11:55:10 -0700 | [diff] [blame] | 17 | static void ipmaPush(struct ipmaArray * ipma, uint8_t assoc, avifBool essential) |
Joe Drago | 224b018 | 2019-02-08 10:42:29 -0800 | [diff] [blame] | 18 | { |
| 19 | ipma->associations[ipma->count] = assoc; |
Joe Drago | 0da2997 | 2020-04-23 11:55:10 -0700 | [diff] [blame] | 20 | ipma->essential[ipma->count] = essential; |
Joe Drago | 224b018 | 2019-02-08 10:42:29 -0800 | [diff] [blame] | 21 | ++ipma->count; |
| 22 | } |
| 23 | |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 24 | // 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". |
| 27 | typedef struct avifOffsetFixup |
| 28 | { |
| 29 | size_t offset; |
| 30 | } avifOffsetFixup; |
| 31 | AVIF_ARRAY_DECLARE(avifOffsetFixupArray, avifOffsetFixup, fixup); |
| 32 | |
Joe Drago | cd1e4c3 | 2019-02-08 11:26:31 -0800 | [diff] [blame] | 33 | static const char alphaURN[] = URN_ALPHA0; |
| 34 | static const size_t alphaURNSize = sizeof(alphaURN); |
| 35 | |
Joe Drago | f6a4227 | 2019-11-21 15:21:41 -0800 | [diff] [blame] | 36 | static const char xmpContentType[] = CONTENT_TYPE_XMP; |
| 37 | static const size_t xmpContentTypeSize = sizeof(xmpContentType); |
| 38 | |
Wan-Teh Chang | e184dc1 | 2020-05-11 12:47:21 -0700 | [diff] [blame] | 39 | static avifBool avifImageIsOpaque(const avifImage * image); |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 40 | static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 41 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 42 | // --------------------------------------------------------------------------- |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 43 | // avifCodecEncodeOutput |
| 44 | |
| 45 | avifCodecEncodeOutput * avifCodecEncodeOutputCreate(void) |
| 46 | { |
| 47 | avifCodecEncodeOutput * encodeOutput = (avifCodecEncodeOutput *)avifAlloc(sizeof(avifCodecEncodeOutput)); |
| 48 | memset(encodeOutput, 0, sizeof(avifCodecEncodeOutput)); |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 49 | if (!avifArrayCreate(&encodeOutput->samples, sizeof(avifEncodeSample), 1)) { |
| 50 | goto error; |
| 51 | } |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 52 | return encodeOutput; |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 53 | |
| 54 | error: |
| 55 | avifCodecEncodeOutputDestroy(encodeOutput); |
| 56 | return NULL; |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | void 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 | |
| 66 | void 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 Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 76 | // avifEncoderItem |
| 77 | |
| 78 | // one "item" worth for encoder |
| 79 | typedef struct avifEncoderItem |
| 80 | { |
| 81 | uint16_t id; |
| 82 | uint8_t type[4]; |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 83 | avifCodec * codec; // only present on type==av01 |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 84 | avifCodecEncodeOutput * encodeOutput; // AV1 sample data |
| 85 | avifRWData metadataPayload; // Exif/XMP data |
Joe Drago | 2172ed0 | 2020-11-04 18:04:44 -0800 | [diff] [blame] | 86 | avifCodecConfigurationBox av1C; // Harvested in avifEncoderFinish(), if encodeOutput has samples |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 87 | uint32_t cellIndex; // Which row-major cell index corresponds to this item. ignored on non-av01 types |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 88 | avifBool alpha; |
Wan-Teh Chang | 0f41891 | 2022-02-17 09:16:05 -0800 | [diff] [blame] | 89 | avifBool hiddenImage; // A hidden image item has (flags & 1) equal to 1 in its ItemInfoEntry. |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 90 | |
| 91 | const char * infeName; |
| 92 | size_t infeNameSize; |
| 93 | const char * infeContentType; |
| 94 | size_t infeContentTypeSize; |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 95 | avifOffsetFixupArray mdatFixups; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 96 | |
| 97 | uint16_t irefToID; // if non-zero, make an iref from this id -> irefToID |
| 98 | const char * irefType; |
| 99 | |
Wan-Teh Chang | f9fca86 | 2020-12-22 15:08:12 -0800 | [diff] [blame] | 100 | 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 Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 102 | |
| 103 | uint16_t dimgFromID; // if non-zero, make an iref from dimgFromID -> this id |
| 104 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 105 | struct ipmaArray ipma; |
| 106 | } avifEncoderItem; |
| 107 | AVIF_ARRAY_DECLARE(avifEncoderItemArray, avifEncoderItem, item); |
| 108 | |
| 109 | // --------------------------------------------------------------------------- |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 110 | // avifEncoderFrame |
| 111 | |
| 112 | typedef struct avifEncoderFrame |
| 113 | { |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 114 | uint64_t durationInTimescales; |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 115 | } avifEncoderFrame; |
| 116 | AVIF_ARRAY_DECLARE(avifEncoderFrameArray, avifEncoderFrame, frame); |
| 117 | |
| 118 | // --------------------------------------------------------------------------- |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 119 | // avifEncoderData |
| 120 | |
| 121 | typedef struct avifEncoderData |
| 122 | { |
| 123 | avifEncoderItemArray items; |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 124 | avifEncoderFrameArray frames; |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 125 | avifEncoder lastEncoder; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 126 | avifImage * imageMetadata; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 127 | uint16_t lastItemID; |
| 128 | uint16_t primaryItemID; |
Joe Drago | aba51d8 | 2020-11-19 13:12:29 -0800 | [diff] [blame] | 129 | avifBool singleImage; // if true, the AVIF_ADD_IMAGE_FLAG_SINGLE flag was set on the first call to avifEncoderAddImage() |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 130 | avifBool alphaPresent; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 131 | } avifEncoderData; |
| 132 | |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 133 | static void avifEncoderDataDestroy(avifEncoderData * data); |
| 134 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 135 | static avifEncoderData * avifEncoderDataCreate() |
| 136 | { |
| 137 | avifEncoderData * data = (avifEncoderData *)avifAlloc(sizeof(avifEncoderData)); |
| 138 | memset(data, 0, sizeof(avifEncoderData)); |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 139 | data->imageMetadata = avifImageCreateEmpty(); |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 140 | if (!avifArrayCreate(&data->items, sizeof(avifEncoderItem), 8)) { |
| 141 | goto error; |
| 142 | } |
| 143 | if (!avifArrayCreate(&data->frames, sizeof(avifEncoderFrame), 1)) { |
| 144 | goto error; |
| 145 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 146 | return data; |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 147 | |
| 148 | error: |
| 149 | avifEncoderDataDestroy(data); |
| 150 | return NULL; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 151 | } |
| 152 | |
Wan-Teh Chang | 8adbb97 | 2020-12-18 14:12:51 -0800 | [diff] [blame] | 153 | static avifEncoderItem * avifEncoderDataCreateItem(avifEncoderData * data, const char * type, const char * infeName, size_t infeNameSize, uint32_t cellIndex) |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 154 | { |
| 155 | avifEncoderItem * item = (avifEncoderItem *)avifArrayPushPtr(&data->items); |
Wan-Teh Chang | d9cffc5 | 2022-01-29 22:31:01 -0800 | [diff] [blame] | 156 | ++data->lastItemID; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 157 | item->id = data->lastItemID; |
| 158 | memcpy(item->type, type, sizeof(item->type)); |
| 159 | item->infeName = infeName; |
| 160 | item->infeNameSize = infeNameSize; |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 161 | item->encodeOutput = avifCodecEncodeOutputCreate(); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 162 | item->cellIndex = cellIndex; |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 163 | if (!avifArrayCreate(&item->mdatFixups, sizeof(avifOffsetFixup), 4)) { |
| 164 | goto error; |
| 165 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 166 | return item; |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 167 | |
| 168 | error: |
| 169 | avifCodecEncodeOutputDestroy(item->encodeOutput); |
Wan-Teh Chang | d9cffc5 | 2022-01-29 22:31:01 -0800 | [diff] [blame] | 170 | --data->lastItemID; |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 171 | avifArrayPop(&data->items); |
| 172 | return NULL; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 173 | } |
| 174 | |
Joe Drago | ceb2fa0 | 2021-01-29 18:14:55 -0800 | [diff] [blame] | 175 | static avifEncoderItem * avifEncoderDataFindItemByID(avifEncoderData * data, uint16_t id) |
| 176 | { |
| 177 | for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) { |
| 178 | avifEncoderItem * item = &data->items.item[itemIndex]; |
| 179 | if (item->id == id) { |
| 180 | return item; |
| 181 | } |
| 182 | } |
| 183 | return NULL; |
| 184 | } |
| 185 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 186 | static void avifEncoderDataDestroy(avifEncoderData * data) |
| 187 | { |
| 188 | for (uint32_t i = 0; i < data->items.count; ++i) { |
| 189 | avifEncoderItem * item = &data->items.item[i]; |
| 190 | if (item->codec) { |
| 191 | avifCodecDestroy(item->codec); |
| 192 | } |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 193 | avifCodecEncodeOutputDestroy(item->encodeOutput); |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 194 | avifRWDataFree(&item->metadataPayload); |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 195 | avifArrayDestroy(&item->mdatFixups); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 196 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 197 | avifImageDestroy(data->imageMetadata); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 198 | avifArrayDestroy(&data->items); |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 199 | avifArrayDestroy(&data->frames); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 200 | avifFree(data); |
| 201 | } |
| 202 | |
Wan-Teh Chang | 9f89df9 | 2020-07-08 18:54:26 -0700 | [diff] [blame] | 203 | static void avifEncoderItemAddMdatFixup(avifEncoderItem * item, const avifRWStream * s) |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 204 | { |
| 205 | avifOffsetFixup * fixup = (avifOffsetFixup *)avifArrayPushPtr(&item->mdatFixups); |
| 206 | fixup->offset = avifRWStreamOffset(s); |
| 207 | } |
| 208 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 209 | // --------------------------------------------------------------------------- |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 210 | // avifItemPropertyDedup - Provides ipco deduplication |
| 211 | |
| 212 | typedef struct avifItemProperty |
| 213 | { |
| 214 | uint8_t index; |
| 215 | size_t offset; |
| 216 | size_t size; |
| 217 | } avifItemProperty; |
| 218 | AVIF_ARRAY_DECLARE(avifItemPropertyArray, avifItemProperty, property); |
| 219 | |
| 220 | typedef struct avifItemPropertyDedup |
| 221 | { |
| 222 | avifItemPropertyArray properties; |
| 223 | avifRWStream s; // Temporary stream for each new property, checked against already-written boxes for deduplications |
| 224 | avifRWData buffer; // Temporary storage for 's' |
| 225 | uint8_t nextIndex; // 1-indexed, incremented every time another unique property is finished |
| 226 | } avifItemPropertyDedup; |
| 227 | |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 228 | static void avifItemPropertyDedupDestroy(avifItemPropertyDedup * dedup); |
| 229 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 230 | static avifItemPropertyDedup * avifItemPropertyDedupCreate(void) |
| 231 | { |
| 232 | avifItemPropertyDedup * dedup = (avifItemPropertyDedup *)avifAlloc(sizeof(avifItemPropertyDedup)); |
| 233 | memset(dedup, 0, sizeof(avifItemPropertyDedup)); |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 234 | if (!avifArrayCreate(&dedup->properties, sizeof(avifItemProperty), 8)) { |
| 235 | goto error; |
| 236 | } |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 237 | avifRWDataRealloc(&dedup->buffer, 2048); // This will resize automatically (if necessary) |
| 238 | return dedup; |
Wan-Teh Chang | f732a4d | 2022-01-21 15:56:35 -0800 | [diff] [blame] | 239 | |
| 240 | error: |
| 241 | avifItemPropertyDedupDestroy(dedup); |
| 242 | return NULL; |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 243 | } |
| 244 | |
| 245 | static void avifItemPropertyDedupDestroy(avifItemPropertyDedup * dedup) |
| 246 | { |
| 247 | avifArrayDestroy(&dedup->properties); |
| 248 | avifRWDataFree(&dedup->buffer); |
| 249 | avifFree(dedup); |
| 250 | } |
| 251 | |
| 252 | // Resets the dedup's temporary write stream in preparation for a single item property's worth of writing |
| 253 | static void avifItemPropertyDedupStart(avifItemPropertyDedup * dedup) |
| 254 | { |
| 255 | avifRWStreamStart(&dedup->s, &dedup->buffer); |
| 256 | } |
| 257 | |
| 258 | // This compares the newly written item property (in the dedup's temporary storage buffer) to |
| 259 | // already-written properties (whose offsets/sizes in outputStream are recorded in the dedup). If a |
| 260 | // match is found, the previous item's index is used. If this new property is unique, it is |
| 261 | // assigned the next available property index, written to the output stream, and its offset/size in |
| 262 | // the output stream is recorded in the dedup for future comparisons. |
| 263 | // |
| 264 | // This function always returns a valid 1-indexed property index for usage in a property association |
| 265 | // (ipma) box later. If the most recent property was a duplicate of a previous property, the return |
| 266 | // value will be the index of the original property, otherwise it will be the index of the newly |
| 267 | // created property. |
| 268 | static uint8_t avifItemPropertyDedupFinish(avifItemPropertyDedup * dedup, avifRWStream * outputStream) |
| 269 | { |
| 270 | const size_t newPropertySize = avifRWStreamOffset(&dedup->s); |
| 271 | |
| 272 | for (size_t i = 0; i < dedup->properties.count; ++i) { |
| 273 | avifItemProperty * property = &dedup->properties.property[i]; |
| 274 | if ((property->size == newPropertySize) && |
| 275 | !memcmp(&outputStream->raw->data[property->offset], dedup->buffer.data, newPropertySize)) { |
| 276 | // We've already written this exact property, reuse it |
| 277 | return property->index; |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | // Write a new property, and remember its location in the output stream for future deduplication |
| 282 | avifItemProperty * property = (avifItemProperty *)avifArrayPushPtr(&dedup->properties); |
| 283 | property->index = ++dedup->nextIndex; // preincrement so the first new index is 1 (as ipma is 1-indexed) |
| 284 | property->size = newPropertySize; |
| 285 | property->offset = avifRWStreamOffset(outputStream); |
| 286 | avifRWStreamWrite(outputStream, dedup->buffer.data, newPropertySize); |
| 287 | return property->index; |
| 288 | } |
| 289 | |
| 290 | // --------------------------------------------------------------------------- |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 291 | |
Joe Drago | 0b05eee | 2019-06-12 13:24:39 -0700 | [diff] [blame] | 292 | avifEncoder * avifEncoderCreate(void) |
| 293 | { |
| 294 | avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder)); |
| 295 | memset(encoder, 0, sizeof(avifEncoder)); |
| 296 | encoder->maxThreads = 1; |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 297 | encoder->speed = AVIF_SPEED_DEFAULT; |
| 298 | encoder->keyframeInterval = 0; |
| 299 | encoder->timescale = 1; |
Joe Drago | 46ea058 | 2019-07-22 15:55:47 -0700 | [diff] [blame] | 300 | encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS; |
| 301 | encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS; |
Joe Drago | 56fa2bc | 2020-03-03 13:29:48 -0800 | [diff] [blame] | 302 | encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; |
| 303 | encoder->maxQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; |
Joe Drago | 4bf0187 | 2019-11-15 18:09:19 -0800 | [diff] [blame] | 304 | encoder->tileRowsLog2 = 0; |
| 305 | encoder->tileColsLog2 = 0; |
Wan-Teh Chang | 2792c0a | 2020-08-28 16:10:31 -0700 | [diff] [blame] | 306 | encoder->data = avifEncoderDataCreate(); |
Joe Drago | e1097bd | 2020-08-27 18:55:03 -0700 | [diff] [blame] | 307 | encoder->csOptions = avifCodecSpecificOptionsCreate(); |
Joe Drago | 0b05eee | 2019-06-12 13:24:39 -0700 | [diff] [blame] | 308 | return encoder; |
| 309 | } |
| 310 | |
| 311 | void avifEncoderDestroy(avifEncoder * encoder) |
| 312 | { |
Joe Drago | e1097bd | 2020-08-27 18:55:03 -0700 | [diff] [blame] | 313 | avifCodecSpecificOptionsDestroy(encoder->csOptions); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 314 | avifEncoderDataDestroy(encoder->data); |
Joe Drago | 0b05eee | 2019-06-12 13:24:39 -0700 | [diff] [blame] | 315 | avifFree(encoder); |
| 316 | } |
| 317 | |
Joe Drago | e1097bd | 2020-08-27 18:55:03 -0700 | [diff] [blame] | 318 | void avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value) |
| 319 | { |
| 320 | avifCodecSpecificOptionsSet(encoder->csOptions, key, value); |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 321 | } |
| 322 | |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 323 | static void avifEncoderBackupSettings(avifEncoder * encoder) |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 324 | { |
| 325 | avifEncoder * lastEncoder = &encoder->data->lastEncoder; |
| 326 | |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 327 | // lastEncoder->data is only used to mark that lastEncoder is initialized. lastEncoder->data |
| 328 | // must not be dereferenced. |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 329 | lastEncoder->data = encoder->data; |
| 330 | lastEncoder->codecChoice = encoder->codecChoice; |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 331 | lastEncoder->maxThreads = encoder->maxThreads; |
| 332 | lastEncoder->speed = encoder->speed; |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 333 | lastEncoder->keyframeInterval = encoder->keyframeInterval; |
| 334 | lastEncoder->timescale = encoder->timescale; |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 335 | lastEncoder->minQuantizer = encoder->minQuantizer; |
| 336 | lastEncoder->maxQuantizer = encoder->maxQuantizer; |
| 337 | lastEncoder->minQuantizerAlpha = encoder->minQuantizerAlpha; |
| 338 | lastEncoder->maxQuantizerAlpha = encoder->maxQuantizerAlpha; |
| 339 | lastEncoder->tileRowsLog2 = encoder->tileRowsLog2; |
| 340 | lastEncoder->tileColsLog2 = encoder->tileColsLog2; |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 341 | } |
| 342 | |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 343 | // This function detects changes made on avifEncoder. It returns true on success (i.e., if every |
| 344 | // change is valid), or false on failure (i.e., if any setting that can't change was changed). It |
| 345 | // reports detected changes in encoderChanges. |
| 346 | static avifBool avifEncoderDetectChanges(const avifEncoder * encoder, avifEncoderChanges * encoderChanges) |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 347 | { |
| 348 | const avifEncoder * lastEncoder = &encoder->data->lastEncoder; |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 349 | *encoderChanges = 0; |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 350 | |
| 351 | if (!lastEncoder->data) { |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 352 | // lastEncoder is not initialized. |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 353 | return AVIF_TRUE; |
| 354 | } |
| 355 | |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 356 | if ((lastEncoder->codecChoice != encoder->codecChoice) || (lastEncoder->maxThreads != encoder->maxThreads) || |
| 357 | (lastEncoder->speed != encoder->speed) || (lastEncoder->keyframeInterval != encoder->keyframeInterval) || |
| 358 | (lastEncoder->timescale != encoder->timescale)) { |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 359 | return AVIF_FALSE; |
| 360 | } |
| 361 | |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 362 | if (lastEncoder->minQuantizer != encoder->minQuantizer) { |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 363 | *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER; |
| 364 | } |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 365 | if (lastEncoder->maxQuantizer != encoder->maxQuantizer) { |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 366 | *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER; |
| 367 | } |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 368 | if (lastEncoder->minQuantizerAlpha != encoder->minQuantizerAlpha) { |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 369 | *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA; |
| 370 | } |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 371 | if (lastEncoder->maxQuantizerAlpha != encoder->maxQuantizerAlpha) { |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 372 | *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA; |
| 373 | } |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 374 | if (lastEncoder->tileRowsLog2 != encoder->tileRowsLog2) { |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 375 | *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2; |
| 376 | } |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 377 | if (lastEncoder->tileColsLog2 != encoder->tileColsLog2) { |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 378 | *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_COLS_LOG2; |
| 379 | } |
| 380 | if (encoder->csOptions->count > 0) { |
| 381 | *encoderChanges |= AVIF_ENCODER_CHANGE_CODEC_SPECIFIC; |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 382 | } |
| 383 | |
| 384 | return AVIF_TRUE; |
Joe Drago | e1097bd | 2020-08-27 18:55:03 -0700 | [diff] [blame] | 385 | } |
| 386 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 387 | // This function is used in two codepaths: |
| 388 | // * writing color *item* properties |
| 389 | // * writing color *track* properties |
| 390 | // |
| 391 | // Item properties must have property associations with them and can be deduplicated (by reusing |
| 392 | // these associations), so this function leverages the ipma and dedup arguments to do this. |
| 393 | // |
| 394 | // Track properties, however, are implicitly associated by the track in which they are contained, so |
| 395 | // there is no need to build a property association box (ipma), and no way to deduplicate/reuse a |
| 396 | // property. In this case, the ipma and dedup properties should/will be set to NULL, and this |
| 397 | // function will avoid using them. |
| 398 | static void avifEncoderWriteColorProperties(avifRWStream * outputStream, |
| 399 | const avifImage * imageMetadata, |
| 400 | struct ipmaArray * ipma, |
| 401 | avifItemPropertyDedup * dedup) |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 402 | { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 403 | avifRWStream * s = outputStream; |
| 404 | if (dedup) { |
| 405 | assert(ipma); |
| 406 | |
| 407 | // Use the dedup's temporary stream for box writes |
| 408 | s = &dedup->s; |
| 409 | } |
| 410 | |
Wan-Teh Chang | b139eed | 2020-07-08 18:04:14 -0700 | [diff] [blame] | 411 | if (imageMetadata->icc.size > 0) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 412 | if (dedup) { |
| 413 | avifItemPropertyDedupStart(dedup); |
| 414 | } |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 415 | avifBoxMarker colr = avifRWStreamWriteBox(s, "colr", AVIF_BOX_SIZE_TBD); |
| 416 | avifRWStreamWriteChars(s, "prof", 4); // unsigned int(32) colour_type; |
| 417 | avifRWStreamWrite(s, imageMetadata->icc.data, imageMetadata->icc.size); |
| 418 | avifRWStreamFinishBox(s, colr); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 419 | if (dedup) { |
| 420 | ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 421 | } |
Joe Drago | bf58fe7 | 2020-11-05 13:25:14 -0800 | [diff] [blame] | 422 | } |
| 423 | |
| 424 | // HEIF 6.5.5.1, from Amendment 3 allows multiple colr boxes: "at most one for a given value of colour type" |
| 425 | // Therefore, *always* writing an nclx box, even if an a prof box was already written above. |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 426 | if (dedup) { |
| 427 | avifItemPropertyDedupStart(dedup); |
| 428 | } |
Joe Drago | bf58fe7 | 2020-11-05 13:25:14 -0800 | [diff] [blame] | 429 | avifBoxMarker colr = avifRWStreamWriteBox(s, "colr", AVIF_BOX_SIZE_TBD); |
| 430 | avifRWStreamWriteChars(s, "nclx", 4); // unsigned int(32) colour_type; |
Joe Drago | fb5a5f0 | 2021-01-31 20:43:57 -0800 | [diff] [blame] | 431 | avifRWStreamWriteU16(s, imageMetadata->colorPrimaries); // unsigned int(16) colour_primaries; |
| 432 | avifRWStreamWriteU16(s, imageMetadata->transferCharacteristics); // unsigned int(16) transfer_characteristics; |
| 433 | avifRWStreamWriteU16(s, imageMetadata->matrixCoefficients); // unsigned int(16) matrix_coefficients; |
Joe Drago | bf58fe7 | 2020-11-05 13:25:14 -0800 | [diff] [blame] | 434 | avifRWStreamWriteU8(s, (imageMetadata->yuvRange == AVIF_RANGE_FULL) ? 0x80 : 0); // unsigned int(1) full_range_flag; |
| 435 | // unsigned int(7) reserved = 0; |
| 436 | avifRWStreamFinishBox(s, colr); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 437 | if (dedup) { |
| 438 | ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 439 | } |
| 440 | |
| 441 | // Write (Optional) Transformations |
| 442 | if (imageMetadata->transformFlags & AVIF_TRANSFORM_PASP) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 443 | if (dedup) { |
| 444 | avifItemPropertyDedupStart(dedup); |
| 445 | } |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 446 | avifBoxMarker pasp = avifRWStreamWriteBox(s, "pasp", AVIF_BOX_SIZE_TBD); |
| 447 | avifRWStreamWriteU32(s, imageMetadata->pasp.hSpacing); // unsigned int(32) hSpacing; |
| 448 | avifRWStreamWriteU32(s, imageMetadata->pasp.vSpacing); // unsigned int(32) vSpacing; |
| 449 | avifRWStreamFinishBox(s, pasp); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 450 | if (dedup) { |
| 451 | ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 452 | } |
| 453 | } |
| 454 | if (imageMetadata->transformFlags & AVIF_TRANSFORM_CLAP) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 455 | if (dedup) { |
| 456 | avifItemPropertyDedupStart(dedup); |
| 457 | } |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 458 | avifBoxMarker clap = avifRWStreamWriteBox(s, "clap", AVIF_BOX_SIZE_TBD); |
| 459 | avifRWStreamWriteU32(s, imageMetadata->clap.widthN); // unsigned int(32) cleanApertureWidthN; |
| 460 | avifRWStreamWriteU32(s, imageMetadata->clap.widthD); // unsigned int(32) cleanApertureWidthD; |
| 461 | avifRWStreamWriteU32(s, imageMetadata->clap.heightN); // unsigned int(32) cleanApertureHeightN; |
| 462 | avifRWStreamWriteU32(s, imageMetadata->clap.heightD); // unsigned int(32) cleanApertureHeightD; |
| 463 | avifRWStreamWriteU32(s, imageMetadata->clap.horizOffN); // unsigned int(32) horizOffN; |
| 464 | avifRWStreamWriteU32(s, imageMetadata->clap.horizOffD); // unsigned int(32) horizOffD; |
| 465 | avifRWStreamWriteU32(s, imageMetadata->clap.vertOffN); // unsigned int(32) vertOffN; |
| 466 | avifRWStreamWriteU32(s, imageMetadata->clap.vertOffD); // unsigned int(32) vertOffD; |
| 467 | avifRWStreamFinishBox(s, clap); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 468 | if (dedup) { |
| 469 | ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 470 | } |
| 471 | } |
| 472 | if (imageMetadata->transformFlags & AVIF_TRANSFORM_IROT) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 473 | if (dedup) { |
| 474 | avifItemPropertyDedupStart(dedup); |
| 475 | } |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 476 | avifBoxMarker irot = avifRWStreamWriteBox(s, "irot", AVIF_BOX_SIZE_TBD); |
| 477 | uint8_t angle = imageMetadata->irot.angle & 0x3; |
| 478 | avifRWStreamWrite(s, &angle, 1); // unsigned int (6) reserved = 0; unsigned int (2) angle; |
| 479 | avifRWStreamFinishBox(s, irot); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 480 | if (dedup) { |
| 481 | ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 482 | } |
| 483 | } |
| 484 | if (imageMetadata->transformFlags & AVIF_TRANSFORM_IMIR) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 485 | if (dedup) { |
| 486 | avifItemPropertyDedupStart(dedup); |
| 487 | } |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 488 | avifBoxMarker imir = avifRWStreamWriteBox(s, "imir", AVIF_BOX_SIZE_TBD); |
Joe Drago | b551bb3 | 2021-06-03 15:22:05 -0700 | [diff] [blame] | 489 | uint8_t mode = imageMetadata->imir.mode & 0x1; |
| 490 | avifRWStreamWrite(s, &mode, 1); // unsigned int (7) reserved = 0; unsigned int (1) mode; |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 491 | avifRWStreamFinishBox(s, imir); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 492 | if (dedup) { |
| 493 | ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 494 | } |
| 495 | } |
| 496 | } |
| 497 | |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 498 | // Write unassociated metadata items (EXIF, XMP) to a small meta box inside of a trak box. |
| 499 | // These items are implicitly associated with the track they are contained within. |
| 500 | static void avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s) |
| 501 | { |
| 502 | // Count how many non-av01 items (such as EXIF/XMP) are being written |
| 503 | uint32_t metadataItemCount = 0; |
| 504 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 505 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 506 | if (memcmp(item->type, "av01", 4) != 0) { |
| 507 | ++metadataItemCount; |
| 508 | } |
| 509 | } |
| 510 | if (metadataItemCount == 0) { |
| 511 | // Don't even bother writing the trak meta box |
| 512 | return; |
| 513 | } |
| 514 | |
| 515 | avifBoxMarker meta = avifRWStreamWriteFullBox(s, "meta", AVIF_BOX_SIZE_TBD, 0, 0); |
| 516 | |
| 517 | avifBoxMarker hdlr = avifRWStreamWriteFullBox(s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0); |
| 518 | avifRWStreamWriteU32(s, 0); // unsigned int(32) pre_defined = 0; |
| 519 | avifRWStreamWriteChars(s, "pict", 4); // unsigned int(32) handler_type; |
| 520 | avifRWStreamWriteZeros(s, 12); // const unsigned int(32)[3] reserved = 0; |
| 521 | avifRWStreamWriteChars(s, "libavif", 8); // string name; (writing null terminator) |
| 522 | avifRWStreamFinishBox(s, hdlr); |
| 523 | |
| 524 | avifBoxMarker iloc = avifRWStreamWriteFullBox(s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0); |
| 525 | uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size; |
| 526 | // unsigned int(4) length_size; |
| 527 | avifRWStreamWrite(s, &offsetSizeAndLengthSize, 1); // |
| 528 | avifRWStreamWriteZeros(s, 1); // unsigned int(4) base_offset_size; |
| 529 | // unsigned int(4) reserved; |
| 530 | avifRWStreamWriteU16(s, (uint16_t)metadataItemCount); // unsigned int(16) item_count; |
| 531 | for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) { |
| 532 | avifEncoderItem * item = &encoder->data->items.item[trakItemIndex]; |
| 533 | if (memcmp(item->type, "av01", 4) == 0) { |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 534 | // Skip over all non-metadata items |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 535 | continue; |
| 536 | } |
| 537 | |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 538 | avifRWStreamWriteU16(s, item->id); // unsigned int(16) item_ID; |
| 539 | avifRWStreamWriteU16(s, 0); // unsigned int(16) data_reference_index; |
| 540 | avifRWStreamWriteU16(s, 1); // unsigned int(16) extent_count; |
| 541 | avifEncoderItemAddMdatFixup(item, s); // |
| 542 | avifRWStreamWriteU32(s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset; |
| 543 | avifRWStreamWriteU32(s, (uint32_t)item->metadataPayload.size); // unsigned int(length_size*8) extent_length; |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 544 | } |
| 545 | avifRWStreamFinishBox(s, iloc); |
| 546 | |
| 547 | avifBoxMarker iinf = avifRWStreamWriteFullBox(s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0); |
| 548 | avifRWStreamWriteU16(s, (uint16_t)metadataItemCount); // unsigned int(16) entry_count; |
| 549 | for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) { |
| 550 | avifEncoderItem * item = &encoder->data->items.item[trakItemIndex]; |
| 551 | if (memcmp(item->type, "av01", 4) == 0) { |
| 552 | continue; |
| 553 | } |
| 554 | |
Wan-Teh Chang | 0f41891 | 2022-02-17 09:16:05 -0800 | [diff] [blame] | 555 | assert(!item->hiddenImage); |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 556 | avifBoxMarker infe = avifRWStreamWriteFullBox(s, "infe", AVIF_BOX_SIZE_TBD, 2, 0); |
| 557 | avifRWStreamWriteU16(s, item->id); // unsigned int(16) item_ID; |
| 558 | avifRWStreamWriteU16(s, 0); // unsigned int(16) item_protection_index; |
| 559 | avifRWStreamWrite(s, item->type, 4); // unsigned int(32) item_type; |
| 560 | avifRWStreamWriteChars(s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator) |
| 561 | if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) |
| 562 | avifRWStreamWriteChars(s, item->infeContentType, item->infeContentTypeSize); |
| 563 | } |
| 564 | avifRWStreamFinishBox(s, infe); |
| 565 | } |
| 566 | avifRWStreamFinishBox(s, iinf); |
| 567 | |
| 568 | avifRWStreamFinishBox(s, meta); |
| 569 | } |
| 570 | |
Wan-Teh Chang | f9fca86 | 2020-12-22 15:08:12 -0800 | [diff] [blame] | 571 | static void avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t gridRows, const avifImage * firstCell) |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 572 | { |
| 573 | // ISO/IEC 23008-12 6.6.2.3.2 |
| 574 | // aligned(8) class ImageGrid { |
| 575 | // unsigned int(8) version = 0; |
| 576 | // unsigned int(8) flags; |
| 577 | // FieldLength = ((flags & 1) + 1) * 16; |
| 578 | // unsigned int(8) rows_minus_one; |
| 579 | // unsigned int(8) columns_minus_one; |
| 580 | // unsigned int(FieldLength) output_width; |
| 581 | // unsigned int(FieldLength) output_height; |
| 582 | // } |
| 583 | |
| 584 | uint32_t gridWidth = firstCell->width * gridCols; |
| 585 | uint32_t gridHeight = firstCell->height * gridRows; |
| 586 | uint8_t gridFlags = ((gridWidth > 65535) || (gridHeight > 65535)) ? 1 : 0; |
| 587 | |
| 588 | avifRWStream s; |
| 589 | avifRWStreamStart(&s, data); |
Wan-Teh Chang | 8adbb97 | 2020-12-18 14:12:51 -0800 | [diff] [blame] | 590 | avifRWStreamWriteU8(&s, 0); // unsigned int(8) version = 0; |
| 591 | avifRWStreamWriteU8(&s, gridFlags); // unsigned int(8) flags; |
| 592 | avifRWStreamWriteU8(&s, (uint8_t)(gridRows - 1)); // unsigned int(8) rows_minus_one; |
| 593 | avifRWStreamWriteU8(&s, (uint8_t)(gridCols - 1)); // unsigned int(8) columns_minus_one; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 594 | if (gridFlags & 1) { |
| 595 | avifRWStreamWriteU32(&s, gridWidth); // unsigned int(FieldLength) output_width; |
| 596 | avifRWStreamWriteU32(&s, gridHeight); // unsigned int(FieldLength) output_height; |
| 597 | } else { |
| 598 | uint16_t tmpWidth = (uint16_t)gridWidth; |
| 599 | uint16_t tmpHeight = (uint16_t)gridHeight; |
| 600 | avifRWStreamWriteU16(&s, tmpWidth); // unsigned int(FieldLength) output_width; |
| 601 | avifRWStreamWriteU16(&s, tmpHeight); // unsigned int(FieldLength) output_height; |
| 602 | } |
| 603 | avifRWStreamFinishWrite(&s); |
| 604 | } |
| 605 | |
Yannis Guyon | 4dbafcf | 2022-05-13 11:23:07 +0200 | [diff] [blame] | 606 | static avifResult avifEncoderDataCreateExifItem(avifEncoderData * data, const avifRWData * exif) |
| 607 | { |
| 608 | // Validate Exif payload (if any) and find TIFF header offset |
| 609 | uint32_t exifTiffHeaderOffset = 0; |
| 610 | if (exif->size < 4) { |
| 611 | // Can't even fit the TIFF header, something is wrong |
| 612 | return AVIF_RESULT_INVALID_EXIF_PAYLOAD; |
| 613 | } |
| 614 | |
| 615 | const uint8_t tiffHeaderBE[4] = { 'M', 'M', 0, 42 }; |
| 616 | const uint8_t tiffHeaderLE[4] = { 'I', 'I', 42, 0 }; |
| 617 | for (; exifTiffHeaderOffset < (exif->size - 4); ++exifTiffHeaderOffset) { |
| 618 | if (!memcmp(&exif->data[exifTiffHeaderOffset], tiffHeaderBE, sizeof(tiffHeaderBE))) { |
| 619 | break; |
| 620 | } |
| 621 | if (!memcmp(&exif->data[exifTiffHeaderOffset], tiffHeaderLE, sizeof(tiffHeaderLE))) { |
| 622 | break; |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | if (exifTiffHeaderOffset >= exif->size - 4) { |
| 627 | // Couldn't find the TIFF header |
| 628 | return AVIF_RESULT_INVALID_EXIF_PAYLOAD; |
| 629 | } |
| 630 | |
| 631 | avifEncoderItem * exifItem = avifEncoderDataCreateItem(data, "Exif", "Exif", 5, 0); |
| 632 | if (!exifItem) { |
| 633 | return AVIF_RESULT_OUT_OF_MEMORY; |
| 634 | } |
| 635 | exifItem->irefToID = data->primaryItemID; |
| 636 | exifItem->irefType = "cdsc"; |
| 637 | |
| 638 | avifRWDataRealloc(&exifItem->metadataPayload, sizeof(uint32_t) + exif->size); |
| 639 | exifTiffHeaderOffset = avifHTONL(exifTiffHeaderOffset); |
| 640 | memcpy(exifItem->metadataPayload.data, &exifTiffHeaderOffset, sizeof(uint32_t)); |
| 641 | memcpy(exifItem->metadataPayload.data + sizeof(uint32_t), exif->data, exif->size); |
| 642 | return AVIF_RESULT_OK; |
| 643 | } |
| 644 | |
| 645 | static avifResult avifEncoderDataCreateXMPItem(avifEncoderData * data, const avifRWData * xmp) |
| 646 | { |
| 647 | avifEncoderItem * xmpItem = avifEncoderDataCreateItem(data, "mime", "XMP", 4, 0); |
| 648 | if (!xmpItem) { |
| 649 | return AVIF_RESULT_OUT_OF_MEMORY; |
| 650 | } |
| 651 | xmpItem->irefToID = data->primaryItemID; |
| 652 | xmpItem->irefType = "cdsc"; |
| 653 | |
| 654 | xmpItem->infeContentType = xmpContentType; |
| 655 | xmpItem->infeContentTypeSize = xmpContentTypeSize; |
| 656 | avifRWDataSet(&xmpItem->metadataPayload, xmp->data, xmp->size); |
| 657 | return AVIF_RESULT_OK; |
| 658 | } |
| 659 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 660 | static avifResult avifEncoderAddImageInternal(avifEncoder * encoder, |
Wan-Teh Chang | f9fca86 | 2020-12-22 15:08:12 -0800 | [diff] [blame] | 661 | uint32_t gridCols, |
| 662 | uint32_t gridRows, |
Wan-Teh Chang | 2b7f04e | 2020-12-14 09:51:01 -0800 | [diff] [blame] | 663 | const avifImage * const * cellImages, |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 664 | uint64_t durationInTimescales, |
Joe Drago | e7c95e0 | 2021-05-06 12:48:55 -0700 | [diff] [blame] | 665 | avifAddImageFlags addImageFlags) |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 666 | { |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 667 | // ----------------------------------------------------------------------- |
Joe Drago | c534e70 | 2020-10-30 17:56:42 -0700 | [diff] [blame] | 668 | // Verify encoding is possible |
| 669 | |
| 670 | if (!avifCodecName(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE)) { |
| 671 | return AVIF_RESULT_NO_CODEC_AVAILABLE; |
| 672 | } |
| 673 | |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 674 | avifEncoderChanges encoderChanges; |
| 675 | if (!avifEncoderDetectChanges(encoder, &encoderChanges)) { |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 676 | return AVIF_RESULT_CANNOT_CHANGE_SETTING; |
| 677 | } |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 678 | avifEncoderBackupSettings(encoder); |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 679 | |
Joe Drago | c534e70 | 2020-10-30 17:56:42 -0700 | [diff] [blame] | 680 | // ----------------------------------------------------------------------- |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 681 | // Validate images |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 682 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 683 | const uint32_t cellCount = gridCols * gridRows; |
| 684 | if (cellCount == 0) { |
| 685 | return AVIF_RESULT_INVALID_ARGUMENT; |
Joe Drago | 7ad3ad6 | 2019-02-07 11:17:34 -0800 | [diff] [blame] | 686 | } |
| 687 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 688 | const avifImage * firstCell = cellImages[0]; |
Wan-Teh Chang | 0b7c20e | 2020-12-18 16:21:57 -0800 | [diff] [blame] | 689 | if ((firstCell->depth != 8) && (firstCell->depth != 10) && (firstCell->depth != 12)) { |
| 690 | return AVIF_RESULT_UNSUPPORTED_DEPTH; |
| 691 | } |
| 692 | |
| 693 | if (!firstCell->width || !firstCell->height) { |
| 694 | return AVIF_RESULT_NO_CONTENT; |
| 695 | } |
| 696 | |
Yannis Guyon | bf28a92 | 2022-02-09 23:19:32 +0100 | [diff] [blame] | 697 | if ((cellCount > 1) && !avifAreGridDimensionsValid(firstCell->yuvFormat, |
| 698 | gridCols * firstCell->width, |
| 699 | gridRows * firstCell->height, |
| 700 | firstCell->width, |
| 701 | firstCell->height, |
| 702 | &encoder->diag)) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 703 | return AVIF_RESULT_INVALID_IMAGE_GRID; |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 704 | } |
| 705 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 706 | for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { |
| 707 | const avifImage * cellImage = cellImages[cellIndex]; |
Yannis Guyon | bf28a92 | 2022-02-09 23:19:32 +0100 | [diff] [blame] | 708 | // HEIF (ISO 23008-12:2017), Section 6.6.2.3.1: |
| 709 | // All input images shall have exactly the same width and height; call those tile_width and tile_height. |
| 710 | // MIAF (ISO 23000-22:2019), Section 7.3.11.4.1: |
| 711 | // All input images of a grid image item shall use the same coding format, chroma sampling format, and the |
| 712 | // same decoder configuration (see 7.3.6.2). |
| 713 | if ((cellImage->width != firstCell->width) || (cellImage->height != firstCell->height) || |
| 714 | (cellImage->depth != firstCell->depth) || (cellImage->yuvFormat != firstCell->yuvFormat) || |
| 715 | (cellImage->yuvRange != firstCell->yuvRange) || (cellImage->colorPrimaries != firstCell->colorPrimaries) || |
| 716 | (cellImage->transferCharacteristics != firstCell->transferCharacteristics) || |
Wan-Teh Chang | 4e0849d | 2022-04-29 21:12:59 -0700 | [diff] [blame] | 717 | (cellImage->matrixCoefficients != firstCell->matrixCoefficients) || (!!cellImage->alphaPlane != !!firstCell->alphaPlane) || |
| 718 | (cellImage->alphaPremultiplied != firstCell->alphaPremultiplied)) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 719 | return AVIF_RESULT_INVALID_IMAGE_GRID; |
| 720 | } |
| 721 | |
Wan-Teh Chang | 0b7c20e | 2020-12-18 16:21:57 -0800 | [diff] [blame] | 722 | if (!cellImage->yuvPlanes[AVIF_CHAN_Y]) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 723 | return AVIF_RESULT_NO_CONTENT; |
| 724 | } |
| 725 | |
| 726 | if (cellImage->yuvFormat == AVIF_PIXEL_FORMAT_NONE) { |
| 727 | return AVIF_RESULT_NO_YUV_FORMAT_SELECTED; |
| 728 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 729 | } |
| 730 | |
| 731 | // ----------------------------------------------------------------------- |
Joe Drago | aba51d8 | 2020-11-19 13:12:29 -0800 | [diff] [blame] | 732 | // Validate flags |
| 733 | |
| 734 | if (encoder->data->singleImage) { |
| 735 | // The previous call to avifEncoderAddImage() set AVIF_ADD_IMAGE_FLAG_SINGLE. |
| 736 | // avifEncoderAddImage() cannot be called again for this encode. |
| 737 | return AVIF_RESULT_ENCODE_COLOR_FAILED; |
| 738 | } |
| 739 | |
| 740 | if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { |
| 741 | encoder->data->singleImage = AVIF_TRUE; |
| 742 | |
| 743 | if (encoder->data->items.count > 0) { |
| 744 | // AVIF_ADD_IMAGE_FLAG_SINGLE may only be set on the first and only image. |
| 745 | return AVIF_RESULT_INVALID_ARGUMENT; |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | // ----------------------------------------------------------------------- |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 750 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 751 | if (durationInTimescales == 0) { |
| 752 | durationInTimescales = 1; |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 753 | } |
| 754 | |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 755 | if (encoder->data->items.count == 0) { |
| 756 | // Make a copy of the first image's metadata (sans pixels) for future writing/validation |
Yannis Guyon | 1ce971e | 2022-08-01 20:22:04 +0200 | [diff] [blame] | 757 | const avifResult copyResult = avifImageCopy(encoder->data->imageMetadata, firstCell, 0); |
| 758 | if (copyResult != AVIF_RESULT_OK) { |
| 759 | return copyResult; |
| 760 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 761 | |
| 762 | // Prepare all AV1 items |
| 763 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 764 | uint16_t gridColorID = 0; |
| 765 | if (cellCount > 1) { |
| 766 | avifEncoderItem * gridColorItem = avifEncoderDataCreateItem(encoder->data, "grid", "Color", 6, 0); |
| 767 | avifWriteGridPayload(&gridColorItem->metadataPayload, gridCols, gridRows, firstCell); |
| 768 | gridColorItem->gridCols = gridCols; |
| 769 | gridColorItem->gridRows = gridRows; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 770 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 771 | gridColorID = gridColorItem->id; |
| 772 | encoder->data->primaryItemID = gridColorID; |
| 773 | } |
| 774 | |
| 775 | for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { |
| 776 | avifEncoderItem * item = avifEncoderDataCreateItem(encoder->data, "av01", "Color", 6, cellIndex); |
| 777 | item->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE); |
| 778 | if (!item->codec) { |
| 779 | // Just bail out early, we're not surviving this function without an encoder compiled in |
| 780 | return AVIF_RESULT_NO_CODEC_AVAILABLE; |
| 781 | } |
| 782 | item->codec->csOptions = encoder->csOptions; |
Joe Drago | bfc5aca | 2021-05-17 12:00:39 -0700 | [diff] [blame] | 783 | item->codec->diag = &encoder->diag; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 784 | |
Wan-Teh Chang | 0b7c20e | 2020-12-18 16:21:57 -0800 | [diff] [blame] | 785 | if (cellCount > 1) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 786 | item->dimgFromID = gridColorID; |
Wan-Teh Chang | 0f41891 | 2022-02-17 09:16:05 -0800 | [diff] [blame] | 787 | item->hiddenImage = AVIF_TRUE; |
Wan-Teh Chang | 0b7c20e | 2020-12-18 16:21:57 -0800 | [diff] [blame] | 788 | } else { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 789 | encoder->data->primaryItemID = item->id; |
| 790 | } |
| 791 | } |
| 792 | |
| 793 | encoder->data->alphaPresent = (firstCell->alphaPlane != NULL); |
Wan-Teh Chang | 0b7c20e | 2020-12-18 16:21:57 -0800 | [diff] [blame] | 794 | if (encoder->data->alphaPresent && (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE)) { |
Joe Drago | 96e0918 | 2020-07-09 15:32:47 -0700 | [diff] [blame] | 795 | // If encoding a single image in which the alpha plane exists but is entirely opaque, |
| 796 | // simply skip writing an alpha AV1 payload entirely, as it'll be interpreted as opaque |
| 797 | // and is less bytes. |
| 798 | // |
| 799 | // However, if encoding an image sequence, the first frame's alpha plane being entirely |
| 800 | // opaque could be a false positive for removing the alpha AV1 payload, as it might simply |
| 801 | // be a fade out later in the sequence. This is why avifImageIsOpaque() is only called |
| 802 | // when encoding a single image. |
| 803 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 804 | encoder->data->alphaPresent = AVIF_FALSE; |
| 805 | for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { |
| 806 | const avifImage * cellImage = cellImages[cellIndex]; |
| 807 | if (!avifImageIsOpaque(cellImage)) { |
| 808 | encoder->data->alphaPresent = AVIF_TRUE; |
| 809 | break; |
| 810 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 811 | } |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 812 | } |
| 813 | |
| 814 | if (encoder->data->alphaPresent) { |
| 815 | uint16_t gridAlphaID = 0; |
| 816 | if (cellCount > 1) { |
| 817 | avifEncoderItem * gridAlphaItem = avifEncoderDataCreateItem(encoder->data, "grid", "Alpha", 6, 0); |
| 818 | avifWriteGridPayload(&gridAlphaItem->metadataPayload, gridCols, gridRows, firstCell); |
| 819 | gridAlphaItem->alpha = AVIF_TRUE; |
| 820 | gridAlphaItem->irefToID = encoder->data->primaryItemID; |
| 821 | gridAlphaItem->irefType = "auxl"; |
Wan-Teh Chang | 69c32ef | 2020-12-18 12:16:42 -0800 | [diff] [blame] | 822 | gridAlphaItem->gridCols = gridCols; |
| 823 | gridAlphaItem->gridRows = gridRows; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 824 | gridAlphaID = gridAlphaItem->id; |
Yuan Tong | e4850be | 2021-01-22 14:21:25 +0800 | [diff] [blame] | 825 | |
| 826 | if (encoder->data->imageMetadata->alphaPremultiplied) { |
Joe Drago | ceb2fa0 | 2021-01-29 18:14:55 -0800 | [diff] [blame] | 827 | avifEncoderItem * primaryItem = avifEncoderDataFindItemByID(encoder->data, encoder->data->primaryItemID); |
| 828 | assert(primaryItem); |
| 829 | primaryItem->irefType = "prem"; |
| 830 | primaryItem->irefToID = gridAlphaID; |
Yuan Tong | e4850be | 2021-01-22 14:21:25 +0800 | [diff] [blame] | 831 | } |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 832 | } |
| 833 | |
| 834 | for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { |
Wan-Teh Chang | 69c32ef | 2020-12-18 12:16:42 -0800 | [diff] [blame] | 835 | avifEncoderItem * item = avifEncoderDataCreateItem(encoder->data, "av01", "Alpha", 6, cellIndex); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 836 | item->codec = avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE); |
| 837 | if (!item->codec) { |
| 838 | return AVIF_RESULT_NO_CODEC_AVAILABLE; |
| 839 | } |
| 840 | item->codec->csOptions = encoder->csOptions; |
Joe Drago | bfc5aca | 2021-05-17 12:00:39 -0700 | [diff] [blame] | 841 | item->codec->diag = &encoder->diag; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 842 | item->alpha = AVIF_TRUE; |
| 843 | |
Wan-Teh Chang | 0b7c20e | 2020-12-18 16:21:57 -0800 | [diff] [blame] | 844 | if (cellCount > 1) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 845 | item->dimgFromID = gridAlphaID; |
Wan-Teh Chang | 0f41891 | 2022-02-17 09:16:05 -0800 | [diff] [blame] | 846 | item->hiddenImage = AVIF_TRUE; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 847 | } else { |
| 848 | item->irefToID = encoder->data->primaryItemID; |
| 849 | item->irefType = "auxl"; |
Yuan Tong | e4850be | 2021-01-22 14:21:25 +0800 | [diff] [blame] | 850 | |
| 851 | if (encoder->data->imageMetadata->alphaPremultiplied) { |
Joe Drago | ceb2fa0 | 2021-01-29 18:14:55 -0800 | [diff] [blame] | 852 | avifEncoderItem * primaryItem = avifEncoderDataFindItemByID(encoder->data, encoder->data->primaryItemID); |
| 853 | assert(primaryItem); |
| 854 | primaryItem->irefType = "prem"; |
| 855 | primaryItem->irefToID = item->id; |
Yuan Tong | e4850be | 2021-01-22 14:21:25 +0800 | [diff] [blame] | 856 | } |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 857 | } |
| 858 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 859 | } |
| 860 | |
| 861 | // ----------------------------------------------------------------------- |
| 862 | // Create metadata items (Exif, XMP) |
| 863 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 864 | if (firstCell->exif.size > 0) { |
Yannis Guyon | 4dbafcf | 2022-05-13 11:23:07 +0200 | [diff] [blame] | 865 | const avifResult result = avifEncoderDataCreateExifItem(encoder->data, &firstCell->exif); |
| 866 | if (result != AVIF_RESULT_OK) { |
| 867 | return result; |
Wan-Teh Chang | b139eed | 2020-07-08 18:04:14 -0700 | [diff] [blame] | 868 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 869 | } |
| 870 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 871 | if (firstCell->xmp.size > 0) { |
Yannis Guyon | 4dbafcf | 2022-05-13 11:23:07 +0200 | [diff] [blame] | 872 | const avifResult result = avifEncoderDataCreateXMPItem(encoder->data, &firstCell->xmp); |
| 873 | if (result != AVIF_RESULT_OK) { |
| 874 | return result; |
| 875 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 876 | } |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 877 | } else { |
| 878 | // Another frame in an image sequence |
Joe Drago | 96e0918 | 2020-07-09 15:32:47 -0700 | [diff] [blame] | 879 | |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 880 | const avifImage * imageMetadata = encoder->data->imageMetadata; |
Wan-Teh Chang | 1cd9271 | 2022-08-19 18:01:16 -0700 | [diff] [blame] | 881 | // If the first image in the sequence had an alpha plane (even if fully opaque), all |
| 882 | // subsequent images must have alpha as well. |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 883 | if ((imageMetadata->depth != firstCell->depth) || (imageMetadata->yuvFormat != firstCell->yuvFormat) || |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 884 | (imageMetadata->yuvRange != firstCell->yuvRange) || (imageMetadata->colorPrimaries != firstCell->colorPrimaries) || |
| 885 | (imageMetadata->transferCharacteristics != firstCell->transferCharacteristics) || |
| 886 | (imageMetadata->matrixCoefficients != firstCell->matrixCoefficients) || |
Yuan Tong | f38cd85 | 2022-08-19 20:13:09 +0800 | [diff] [blame] | 887 | (imageMetadata->alphaPremultiplied != firstCell->alphaPremultiplied) || |
| 888 | (encoder->data->alphaPresent && !firstCell->alphaPlane)) { |
| 889 | return AVIF_RESULT_INCOMPATIBLE_IMAGE; |
Joe Drago | 96e0918 | 2020-07-09 15:32:47 -0700 | [diff] [blame] | 890 | } |
wantehchang | cbfab6b | 2020-03-11 11:53:53 -0700 | [diff] [blame] | 891 | } |
| 892 | |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 893 | // ----------------------------------------------------------------------- |
| 894 | // Encode AV1 OBUs |
| 895 | |
Joe Drago | d685024 | 2020-06-16 16:05:37 -0700 | [diff] [blame] | 896 | if (encoder->keyframeInterval && ((encoder->data->frames.count % encoder->keyframeInterval) == 0)) { |
| 897 | addImageFlags |= AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME; |
Joe Drago | 3736df9 | 2020-06-16 15:39:55 -0700 | [diff] [blame] | 898 | } |
| 899 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 900 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 901 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 902 | if (item->codec) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 903 | const avifImage * cellImage = cellImages[item->cellIndex]; |
Joe Drago | e1097bd | 2020-08-27 18:55:03 -0700 | [diff] [blame] | 904 | avifResult encodeResult = |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 905 | item->codec->encodeImage(item->codec, encoder, cellImage, item->alpha, encoderChanges, addImageFlags, item->encodeOutput); |
Joe Drago | e1097bd | 2020-08-27 18:55:03 -0700 | [diff] [blame] | 906 | if (encodeResult == AVIF_RESULT_UNKNOWN_ERROR) { |
| 907 | encodeResult = item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; |
| 908 | } |
| 909 | if (encodeResult != AVIF_RESULT_OK) { |
| 910 | return encodeResult; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 911 | } |
| 912 | } |
| 913 | } |
Joe Drago | b3fdc9e | 2020-06-01 12:33:16 -0700 | [diff] [blame] | 914 | |
Yuan Tong | 47df37c | 2022-08-26 12:10:37 +0800 | [diff] [blame] | 915 | avifCodecSpecificOptionsClear(encoder->csOptions); |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 916 | avifEncoderFrame * frame = (avifEncoderFrame *)avifArrayPushPtr(&encoder->data->frames); |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 917 | frame->durationInTimescales = durationInTimescales; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 918 | return AVIF_RESULT_OK; |
| 919 | } |
| 920 | |
Joe Drago | e7c95e0 | 2021-05-06 12:48:55 -0700 | [diff] [blame] | 921 | avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags) |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 922 | { |
Joe Drago | bfc5aca | 2021-05-17 12:00:39 -0700 | [diff] [blame] | 923 | avifDiagnosticsClearError(&encoder->diag); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 924 | return avifEncoderAddImageInternal(encoder, 1, 1, &image, durationInTimescales, addImageFlags); |
| 925 | } |
| 926 | |
Joe Drago | e7c95e0 | 2021-05-06 12:48:55 -0700 | [diff] [blame] | 927 | avifResult avifEncoderAddImageGrid(avifEncoder * encoder, |
| 928 | uint32_t gridCols, |
| 929 | uint32_t gridRows, |
| 930 | const avifImage * const * cellImages, |
| 931 | avifAddImageFlags addImageFlags) |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 932 | { |
Joe Drago | bfc5aca | 2021-05-17 12:00:39 -0700 | [diff] [blame] | 933 | avifDiagnosticsClearError(&encoder->diag); |
Wan-Teh Chang | f9fca86 | 2020-12-22 15:08:12 -0800 | [diff] [blame] | 934 | if ((gridCols == 0) || (gridCols > 256) || (gridRows == 0) || (gridRows > 256)) { |
| 935 | return AVIF_RESULT_INVALID_IMAGE_GRID; |
| 936 | } |
Joe Drago | 11d2359 | 2021-01-05 14:18:57 -0800 | [diff] [blame] | 937 | return avifEncoderAddImageInternal(encoder, gridCols, gridRows, cellImages, 1, addImageFlags | AVIF_ADD_IMAGE_FLAG_SINGLE); // only single image grids are supported |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 938 | } |
| 939 | |
Wan-Teh Chang | 2b7f04e | 2020-12-14 09:51:01 -0800 | [diff] [blame] | 940 | static size_t avifEncoderFindExistingChunk(avifRWStream * s, size_t mdatStartOffset, const uint8_t * data, size_t size) |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 941 | { |
| 942 | const size_t mdatCurrentOffset = avifRWStreamOffset(s); |
| 943 | const size_t mdatSearchSize = mdatCurrentOffset - mdatStartOffset; |
| 944 | if (mdatSearchSize < size) { |
| 945 | return 0; |
| 946 | } |
Wan-Teh Chang | 8adbb97 | 2020-12-18 14:12:51 -0800 | [diff] [blame] | 947 | const size_t mdatEndSearchOffset = mdatCurrentOffset - size; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 948 | for (size_t searchOffset = mdatStartOffset; searchOffset <= mdatEndSearchOffset; ++searchOffset) { |
| 949 | if (!memcmp(data, &s->raw->data[searchOffset], size)) { |
| 950 | return searchOffset; |
| 951 | } |
| 952 | } |
| 953 | return 0; |
| 954 | } |
| 955 | |
Joe Drago | c7e1d75 | 2020-06-15 19:46:50 -0700 | [diff] [blame] | 956 | avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 957 | { |
Joe Drago | bfc5aca | 2021-05-17 12:00:39 -0700 | [diff] [blame] | 958 | avifDiagnosticsClearError(&encoder->diag); |
Wan-Teh Chang | b139eed | 2020-07-08 18:04:14 -0700 | [diff] [blame] | 959 | if (encoder->data->items.count == 0) { |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 960 | return AVIF_RESULT_NO_CONTENT; |
| 961 | } |
| 962 | |
| 963 | // ----------------------------------------------------------------------- |
| 964 | // Finish up AV1 encoding |
| 965 | |
| 966 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 967 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 968 | if (item->codec) { |
Joe Drago | 5b596c4 | 2020-06-02 17:13:38 -0700 | [diff] [blame] | 969 | if (!item->codec->encodeFinish(item->codec, item->encodeOutput)) { |
Joe Drago | b3fdc9e | 2020-06-01 12:33:16 -0700 | [diff] [blame] | 970 | return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; |
| 971 | } |
Joe Drago | b3fdc9e | 2020-06-01 12:33:16 -0700 | [diff] [blame] | 972 | |
Joe Drago | 8210c1b | 2020-06-02 17:40:28 -0700 | [diff] [blame] | 973 | if (item->encodeOutput->samples.count != encoder->data->frames.count) { |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 974 | return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 975 | } |
Joe Drago | 46ea058 | 2019-07-22 15:55:47 -0700 | [diff] [blame] | 976 | } |
| 977 | } |
| 978 | |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 979 | // ----------------------------------------------------------------------- |
Joe Drago | 2172ed0 | 2020-11-04 18:04:44 -0800 | [diff] [blame] | 980 | // Harvest av1C properties from AV1 sequence headers |
| 981 | |
| 982 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 983 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 984 | if (item->encodeOutput->samples.count > 0) { |
Wan-Teh Chang | 9fbcb9b | 2020-11-06 18:48:58 -0800 | [diff] [blame] | 985 | const avifEncodeSample * firstSample = &item->encodeOutput->samples.sample[0]; |
Joe Drago | 2172ed0 | 2020-11-04 18:04:44 -0800 | [diff] [blame] | 986 | avifSequenceHeader sequenceHeader; |
| 987 | if (avifSequenceHeaderParse(&sequenceHeader, (const avifROData *)&firstSample->data)) { |
Wan-Teh Chang | 21961f4 | 2022-03-14 16:25:44 -0700 | [diff] [blame] | 988 | item->av1C = sequenceHeader.av1C; |
Joe Drago | 2172ed0 | 2020-11-04 18:04:44 -0800 | [diff] [blame] | 989 | } else { |
| 990 | // This must be an invalid AV1 payload |
| 991 | return item->alpha ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; |
| 992 | } |
| 993 | } |
| 994 | } |
| 995 | |
| 996 | // ----------------------------------------------------------------------- |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 997 | // Begin write stream |
| 998 | |
Wan-Teh Chang | b11ae43 | 2020-07-08 18:25:24 -0700 | [diff] [blame] | 999 | const avifImage * imageMetadata = encoder->data->imageMetadata; |
| 1000 | // The epoch for creation_time and modification_time is midnight, Jan. 1, |
| 1001 | // 1904, in UTC time. Add the number of seconds between that epoch and the |
| 1002 | // Unix epoch. |
| 1003 | uint64_t now = (uint64_t)time(NULL) + 2082844800; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 1004 | |
| 1005 | avifRWStream s; |
| 1006 | avifRWStreamStart(&s, output); |
| 1007 | |
| 1008 | // ----------------------------------------------------------------------- |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1009 | // Write ftyp |
| 1010 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1011 | const char * majorBrand = "avif"; |
| 1012 | if (encoder->data->frames.count > 1) { |
| 1013 | majorBrand = "avis"; |
| 1014 | } |
| 1015 | |
| 1016 | avifBoxMarker ftyp = avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD); |
| 1017 | avifRWStreamWriteChars(&s, majorBrand, 4); // unsigned int(32) major_brand; |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 1018 | avifRWStreamWriteU32(&s, 0); // unsigned int(32) minor_version; |
| 1019 | avifRWStreamWriteChars(&s, "avif", 4); // unsigned int(32) compatible_brands[]; |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1020 | if (encoder->data->frames.count > 1) { // |
| 1021 | avifRWStreamWriteChars(&s, "avis", 4); // ... compatible_brands[] |
Joe Drago | 70f5a2e | 2020-06-03 17:54:19 -0700 | [diff] [blame] | 1022 | avifRWStreamWriteChars(&s, "msf1", 4); // ... compatible_brands[] |
Vignesh Venkatasubramanian | 0cf1333 | 2022-02-17 14:06:50 -0800 | [diff] [blame] | 1023 | avifRWStreamWriteChars(&s, "iso8", 4); // ... compatible_brands[] |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1024 | } // |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 1025 | avifRWStreamWriteChars(&s, "mif1", 4); // ... compatible_brands[] |
| 1026 | avifRWStreamWriteChars(&s, "miaf", 4); // ... compatible_brands[] |
| 1027 | if ((imageMetadata->depth == 8) || (imageMetadata->depth == 10)) { // |
| 1028 | if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { // |
| 1029 | avifRWStreamWriteChars(&s, "MA1B", 4); // ... compatible_brands[] |
| 1030 | } else if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { // |
| 1031 | avifRWStreamWriteChars(&s, "MA1A", 4); // ... compatible_brands[] |
Joe Drago | eb652d8 | 2019-04-23 16:29:07 -0700 | [diff] [blame] | 1032 | } |
| 1033 | } |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1034 | avifRWStreamFinishBox(&s, ftyp); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1035 | |
| 1036 | // ----------------------------------------------------------------------- |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1037 | // Start meta |
| 1038 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1039 | avifBoxMarker meta = avifRWStreamWriteFullBox(&s, "meta", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1040 | |
| 1041 | // ----------------------------------------------------------------------- |
| 1042 | // Write hdlr |
| 1043 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1044 | avifBoxMarker hdlr = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1045 | avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0; |
| 1046 | avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type; |
| 1047 | avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0; |
| 1048 | avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator) |
| 1049 | avifRWStreamFinishBox(&s, hdlr); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1050 | |
| 1051 | // ----------------------------------------------------------------------- |
| 1052 | // Write pitm |
| 1053 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1054 | if (encoder->data->primaryItemID != 0) { |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1055 | avifRWStreamWriteFullBox(&s, "pitm", sizeof(uint16_t), 0, 0); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1056 | avifRWStreamWriteU16(&s, encoder->data->primaryItemID); // unsigned int(16) item_ID; |
Joe Drago | f6a4227 | 2019-11-21 15:21:41 -0800 | [diff] [blame] | 1057 | } |
| 1058 | |
| 1059 | // ----------------------------------------------------------------------- |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1060 | // Write iloc |
| 1061 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1062 | avifBoxMarker iloc = avifRWStreamWriteFullBox(&s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1063 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1064 | uint8_t offsetSizeAndLengthSize = (4 << 4) + (4 << 0); // unsigned int(4) offset_size; |
| 1065 | // unsigned int(4) length_size; |
| 1066 | avifRWStreamWrite(&s, &offsetSizeAndLengthSize, 1); // |
| 1067 | avifRWStreamWriteZeros(&s, 1); // unsigned int(4) base_offset_size; |
| 1068 | // unsigned int(4) reserved; |
| 1069 | avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) item_count; |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1070 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1071 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1072 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1073 | |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 1074 | uint32_t contentSize = (uint32_t)item->metadataPayload.size; |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1075 | if (item->encodeOutput->samples.count > 0) { |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 1076 | // This is choosing sample 0's size as there are two cases here: |
Joe Drago | d6aa99c | 2020-07-09 14:27:46 -0700 | [diff] [blame] | 1077 | // * This is a single image, in which case this is correct |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 1078 | // * This is an image sequence, but this file should still be a valid single-image avif, |
| 1079 | // so there must still be a primary item pointing at a sync sample. Since the first |
| 1080 | // frame of the image sequence is guaranteed to be a sync sample, it is chosen here. |
| 1081 | // |
Joe Drago | 746325a | 2020-07-09 14:22:55 -0700 | [diff] [blame] | 1082 | // TODO: Offer the ability for a user to specify which frame in the sequence should |
Joe Drago | 25fe127 | 2020-07-09 14:20:39 -0700 | [diff] [blame] | 1083 | // become the primary item's image, and force that frame to be a keyframe. |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1084 | contentSize = (uint32_t)item->encodeOutput->samples.sample[0].data.size; |
| 1085 | } |
| 1086 | |
| 1087 | avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID; |
| 1088 | avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index; |
| 1089 | avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count; |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 1090 | avifEncoderItemAddMdatFixup(item, &s); // |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1091 | avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset; |
| 1092 | avifRWStreamWriteU32(&s, (uint32_t)contentSize); // unsigned int(length_size*8) extent_length; |
Joe Drago | f6a4227 | 2019-11-21 15:21:41 -0800 | [diff] [blame] | 1093 | } |
| 1094 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1095 | avifRWStreamFinishBox(&s, iloc); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1096 | |
| 1097 | // ----------------------------------------------------------------------- |
| 1098 | // Write iinf |
| 1099 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1100 | avifBoxMarker iinf = avifRWStreamWriteFullBox(&s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1101 | avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) entry_count; |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1102 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1103 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1104 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 1105 | |
Wan-Teh Chang | 0f41891 | 2022-02-17 09:16:05 -0800 | [diff] [blame] | 1106 | uint32_t flags = item->hiddenImage ? 1 : 0; |
| 1107 | avifBoxMarker infe = avifRWStreamWriteFullBox(&s, "infe", AVIF_BOX_SIZE_TBD, 2, flags); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1108 | avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID; |
Joe Drago | f6a4227 | 2019-11-21 15:21:41 -0800 | [diff] [blame] | 1109 | avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1110 | avifRWStreamWrite(&s, item->type, 4); // unsigned int(32) item_type; |
| 1111 | avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator) |
| 1112 | if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) |
| 1113 | avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize); |
| 1114 | } |
| 1115 | avifRWStreamFinishBox(&s, infe); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1116 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1117 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1118 | avifRWStreamFinishBox(&s, iinf); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1119 | |
| 1120 | // ----------------------------------------------------------------------- |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1121 | // Write iref boxes |
Joe Drago | 8f7a300 | 2019-02-07 19:35:37 -0800 | [diff] [blame] | 1122 | |
Joe Drago | 3818cf4 | 2020-07-31 13:08:37 -0700 | [diff] [blame] | 1123 | avifBoxMarker iref = 0; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1124 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1125 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1126 | |
| 1127 | // Count how many other items refer to this item with dimgFromID |
| 1128 | uint16_t dimgCount = 0; |
| 1129 | for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) { |
| 1130 | avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex]; |
| 1131 | if (dimgItem->dimgFromID == item->id) { |
| 1132 | ++dimgCount; |
| 1133 | } |
| 1134 | } |
| 1135 | |
| 1136 | if (dimgCount > 0) { |
| 1137 | if (!iref) { |
| 1138 | iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1139 | } |
| 1140 | avifBoxMarker refType = avifRWStreamWriteBox(&s, "dimg", AVIF_BOX_SIZE_TBD); |
| 1141 | avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID; |
| 1142 | avifRWStreamWriteU16(&s, dimgCount); // unsigned int(16) reference_count; |
| 1143 | for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) { |
| 1144 | avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex]; |
| 1145 | if (dimgItem->dimgFromID == item->id) { |
| 1146 | avifRWStreamWriteU16(&s, dimgItem->id); // unsigned int(16) to_item_ID; |
| 1147 | } |
| 1148 | } |
| 1149 | avifRWStreamFinishBox(&s, refType); |
| 1150 | } |
| 1151 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1152 | if (item->irefToID != 0) { |
Joe Drago | 3818cf4 | 2020-07-31 13:08:37 -0700 | [diff] [blame] | 1153 | if (!iref) { |
| 1154 | iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1155 | } |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1156 | avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1157 | avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID; |
| 1158 | avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count; |
| 1159 | avifRWStreamWriteU16(&s, item->irefToID); // unsigned int(16) to_item_ID; |
| 1160 | avifRWStreamFinishBox(&s, refType); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1161 | } |
Joe Drago | 8f7a300 | 2019-02-07 19:35:37 -0800 | [diff] [blame] | 1162 | } |
Joe Drago | 3818cf4 | 2020-07-31 13:08:37 -0700 | [diff] [blame] | 1163 | if (iref) { |
| 1164 | avifRWStreamFinishBox(&s, iref); |
| 1165 | } |
Joe Drago | 8f7a300 | 2019-02-07 19:35:37 -0800 | [diff] [blame] | 1166 | |
| 1167 | // ----------------------------------------------------------------------- |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1168 | // Write iprp -> ipco/ipma |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1169 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1170 | avifBoxMarker iprp = avifRWStreamWriteBox(&s, "iprp", AVIF_BOX_SIZE_TBD); |
Joe Drago | 224b018 | 2019-02-08 10:42:29 -0800 | [diff] [blame] | 1171 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1172 | avifItemPropertyDedup * dedup = avifItemPropertyDedupCreate(); |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1173 | avifBoxMarker ipco = avifRWStreamWriteBox(&s, "ipco", AVIF_BOX_SIZE_TBD); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1174 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1175 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1176 | const avifBool isGrid = (item->gridCols > 0); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1177 | memset(&item->ipma, 0, sizeof(item->ipma)); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1178 | if (!item->codec && !isGrid) { |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1179 | // No ipma to write for this item |
| 1180 | continue; |
| 1181 | } |
Joe Drago | e4dba56 | 2019-02-07 21:52:32 -0800 | [diff] [blame] | 1182 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1183 | if (item->dimgFromID) { |
| 1184 | // All image cells from a grid should share the exact same properties, so see if we've |
| 1185 | // already written properties out for another cell in this grid, and if so, just steal |
| 1186 | // their ipma and move on. This is a sneaky way to provide iprp deduplication. |
| 1187 | |
| 1188 | avifBool foundPreviousCell = AVIF_FALSE; |
| 1189 | for (uint32_t dedupIndex = 0; dedupIndex < itemIndex; ++dedupIndex) { |
| 1190 | avifEncoderItem * dedupItem = &encoder->data->items.item[dedupIndex]; |
| 1191 | if (item->dimgFromID == dedupItem->dimgFromID) { |
| 1192 | // We've already written dedup's items out. Steal their ipma indices and move on! |
Wan-Teh Chang | 21961f4 | 2022-03-14 16:25:44 -0700 | [diff] [blame] | 1193 | item->ipma = dedupItem->ipma; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1194 | foundPreviousCell = AVIF_TRUE; |
| 1195 | break; |
| 1196 | } |
| 1197 | } |
| 1198 | if (foundPreviousCell) { |
| 1199 | continue; |
| 1200 | } |
| 1201 | } |
| 1202 | |
Wan-Teh Chang | 8adbb97 | 2020-12-18 14:12:51 -0800 | [diff] [blame] | 1203 | uint32_t imageWidth = imageMetadata->width; |
| 1204 | uint32_t imageHeight = imageMetadata->height; |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1205 | if (isGrid) { |
| 1206 | imageWidth = imageMetadata->width * item->gridCols; |
| 1207 | imageHeight = imageMetadata->height * item->gridRows; |
| 1208 | } |
| 1209 | |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1210 | // Properties all av01 items need |
| 1211 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1212 | avifItemPropertyDedupStart(dedup); |
| 1213 | avifBoxMarker ispe = avifRWStreamWriteFullBox(&dedup->s, "ispe", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1214 | avifRWStreamWriteU32(&dedup->s, imageWidth); // unsigned int(32) image_width; |
| 1215 | avifRWStreamWriteU32(&dedup->s, imageHeight); // unsigned int(32) image_height; |
| 1216 | avifRWStreamFinishBox(&dedup->s, ispe); |
| 1217 | ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1218 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1219 | avifItemPropertyDedupStart(dedup); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 1220 | uint8_t channelCount = (item->alpha || (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) ? 1 : 3; |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1221 | avifBoxMarker pixi = avifRWStreamWriteFullBox(&dedup->s, "pixi", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1222 | avifRWStreamWriteU8(&dedup->s, channelCount); // unsigned int (8) num_channels; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1223 | for (uint8_t chan = 0; chan < channelCount; ++chan) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1224 | avifRWStreamWriteU8(&dedup->s, (uint8_t)imageMetadata->depth); // unsigned int (8) bits_per_channel; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1225 | } |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1226 | avifRWStreamFinishBox(&dedup->s, pixi); |
| 1227 | ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1228 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1229 | if (item->codec) { |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1230 | avifItemPropertyDedupStart(dedup); |
| 1231 | writeConfigBox(&dedup->s, &item->av1C); |
| 1232 | ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_TRUE); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1233 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1234 | |
| 1235 | if (item->alpha) { |
| 1236 | // Alpha specific properties |
| 1237 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1238 | avifItemPropertyDedupStart(dedup); |
| 1239 | avifBoxMarker auxC = avifRWStreamWriteFullBox(&dedup->s, "auxC", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1240 | avifRWStreamWriteChars(&dedup->s, alphaURN, alphaURNSize); // string aux_type; |
| 1241 | avifRWStreamFinishBox(&dedup->s, auxC); |
| 1242 | ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1243 | } else { |
| 1244 | // Color specific properties |
| 1245 | |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1246 | avifEncoderWriteColorProperties(&s, imageMetadata, &item->ipma, dedup); |
Joe Drago | e4dba56 | 2019-02-07 21:52:32 -0800 | [diff] [blame] | 1247 | } |
Joe Drago | e4dba56 | 2019-02-07 21:52:32 -0800 | [diff] [blame] | 1248 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1249 | avifRWStreamFinishBox(&s, ipco); |
Joe Drago | 5779fc4 | 2021-09-08 11:15:26 -0700 | [diff] [blame] | 1250 | avifItemPropertyDedupDestroy(dedup); |
| 1251 | dedup = NULL; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1252 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1253 | avifBoxMarker ipma = avifRWStreamWriteFullBox(&s, "ipma", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1254 | { |
| 1255 | int ipmaCount = 0; |
| 1256 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1257 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 1258 | if (item->ipma.count > 0) { |
| 1259 | ++ipmaCount; |
| 1260 | } |
| 1261 | } |
| 1262 | avifRWStreamWriteU32(&s, ipmaCount); // unsigned int(32) entry_count; |
| 1263 | |
| 1264 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1265 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 1266 | if (item->ipma.count == 0) { |
| 1267 | continue; |
| 1268 | } |
| 1269 | |
Joe Drago | 0da2997 | 2020-04-23 11:55:10 -0700 | [diff] [blame] | 1270 | avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID; |
| 1271 | avifRWStreamWriteU8(&s, item->ipma.count); // unsigned int(8) association_count; |
| 1272 | for (int i = 0; i < item->ipma.count; ++i) { // |
| 1273 | uint8_t essentialAndIndex = item->ipma.associations[i]; |
| 1274 | if (item->ipma.essential[i]) { |
| 1275 | essentialAndIndex |= 0x80; |
| 1276 | } |
| 1277 | avifRWStreamWriteU8(&s, essentialAndIndex); // bit(1) essential; unsigned int(7) property_index; |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1278 | } |
| 1279 | } |
| 1280 | } |
| 1281 | avifRWStreamFinishBox(&s, ipma); |
| 1282 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1283 | avifRWStreamFinishBox(&s, iprp); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1284 | |
| 1285 | // ----------------------------------------------------------------------- |
Joe Drago | eb652d8 | 2019-04-23 16:29:07 -0700 | [diff] [blame] | 1286 | // Finish meta box |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1287 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1288 | avifRWStreamFinishBox(&s, meta); |
Joe Drago | eb652d8 | 2019-04-23 16:29:07 -0700 | [diff] [blame] | 1289 | |
| 1290 | // ----------------------------------------------------------------------- |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1291 | // Write tracks (if an image sequence) |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1292 | |
| 1293 | if (encoder->data->frames.count > 1) { |
Vignesh Venkatasubramanian | e234f67 | 2022-02-16 11:04:52 -0800 | [diff] [blame] | 1294 | static const uint8_t unityMatrix[9][4] = { |
| 1295 | /* clang-format off */ |
| 1296 | { 0x00, 0x01, 0x00, 0x00 }, |
| 1297 | { 0 }, |
| 1298 | { 0 }, |
| 1299 | { 0 }, |
| 1300 | { 0x00, 0x01, 0x00, 0x00 }, |
| 1301 | { 0 }, |
| 1302 | { 0 }, |
| 1303 | { 0 }, |
| 1304 | { 0x40, 0x00, 0x00, 0x00 } |
| 1305 | /* clang-format on */ |
| 1306 | }; |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1307 | |
| 1308 | uint64_t durationInTimescales = 0; |
| 1309 | for (uint32_t frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) { |
Wan-Teh Chang | b139eed | 2020-07-08 18:04:14 -0700 | [diff] [blame] | 1310 | const avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex]; |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1311 | durationInTimescales += frame->durationInTimescales; |
| 1312 | } |
| 1313 | |
| 1314 | // ------------------------------------------------------------------- |
| 1315 | // Start moov |
| 1316 | |
| 1317 | avifBoxMarker moov = avifRWStreamWriteBox(&s, "moov", AVIF_BOX_SIZE_TBD); |
| 1318 | |
| 1319 | avifBoxMarker mvhd = avifRWStreamWriteFullBox(&s, "mvhd", AVIF_BOX_SIZE_TBD, 1, 0); |
| 1320 | avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time; |
| 1321 | avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time; |
| 1322 | avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale; |
| 1323 | avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration; |
| 1324 | avifRWStreamWriteU32(&s, 0x00010000); // template int(32) rate = 0x00010000; // typically 1.0 |
| 1325 | avifRWStreamWriteU16(&s, 0x0100); // template int(16) volume = 0x0100; // typically, full volume |
| 1326 | avifRWStreamWriteU16(&s, 0); // const bit(16) reserved = 0; |
| 1327 | avifRWStreamWriteZeros(&s, 8); // const unsigned int(32)[2] reserved = 0; |
Wan-Teh Chang | 8557cd6 | 2020-07-08 18:42:10 -0700 | [diff] [blame] | 1328 | avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix)); |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1329 | avifRWStreamWriteZeros(&s, 24); // bit(32)[6] pre_defined = 0; |
| 1330 | avifRWStreamWriteU32(&s, encoder->data->items.count); // unsigned int(32) next_track_ID; |
| 1331 | avifRWStreamFinishBox(&s, mvhd); |
| 1332 | |
| 1333 | // ------------------------------------------------------------------- |
| 1334 | // Write tracks |
| 1335 | |
| 1336 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1337 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
| 1338 | if (item->encodeOutput->samples.count == 0) { |
| 1339 | continue; |
| 1340 | } |
| 1341 | |
| 1342 | uint32_t syncSamplesCount = 0; |
| 1343 | for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { |
| 1344 | avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; |
| 1345 | if (sample->sync) { |
| 1346 | ++syncSamplesCount; |
| 1347 | } |
| 1348 | } |
| 1349 | |
| 1350 | avifBoxMarker trak = avifRWStreamWriteBox(&s, "trak", AVIF_BOX_SIZE_TBD); |
| 1351 | |
Joe Drago | 85e387a | 2020-06-03 17:12:53 -0700 | [diff] [blame] | 1352 | avifBoxMarker tkhd = avifRWStreamWriteFullBox(&s, "tkhd", AVIF_BOX_SIZE_TBD, 1, 1); |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1353 | avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time; |
| 1354 | avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time; |
| 1355 | avifRWStreamWriteU32(&s, itemIndex + 1); // unsigned int(32) track_ID; |
| 1356 | avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0; |
| 1357 | avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration; |
| 1358 | avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 2); // const unsigned int(32)[2] reserved = 0; |
| 1359 | avifRWStreamWriteU16(&s, 0); // template int(16) layer = 0; |
| 1360 | avifRWStreamWriteU16(&s, 0); // template int(16) alternate_group = 0; |
| 1361 | avifRWStreamWriteU16(&s, 0); // template int(16) volume = {if track_is_audio 0x0100 else 0}; |
| 1362 | avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0; |
Wan-Teh Chang | 8557cd6 | 2020-07-08 18:42:10 -0700 | [diff] [blame] | 1363 | avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix)); // template int(32)[9] matrix= // { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1364 | avifRWStreamWriteU32(&s, imageMetadata->width << 16); // unsigned int(32) width; |
| 1365 | avifRWStreamWriteU32(&s, imageMetadata->height << 16); // unsigned int(32) height; |
| 1366 | avifRWStreamFinishBox(&s, tkhd); |
| 1367 | |
| 1368 | if (item->irefToID != 0) { |
| 1369 | avifBoxMarker tref = avifRWStreamWriteBox(&s, "tref", AVIF_BOX_SIZE_TBD); |
| 1370 | avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD); |
| 1371 | avifRWStreamWriteU32(&s, (uint32_t)item->irefToID); |
| 1372 | avifRWStreamFinishBox(&s, refType); |
| 1373 | avifRWStreamFinishBox(&s, tref); |
| 1374 | } |
| 1375 | |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 1376 | if (!item->alpha) { |
| 1377 | avifEncoderWriteTrackMetaBox(encoder, &s); |
| 1378 | } |
| 1379 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1380 | avifBoxMarker mdia = avifRWStreamWriteBox(&s, "mdia", AVIF_BOX_SIZE_TBD); |
| 1381 | |
| 1382 | avifBoxMarker mdhd = avifRWStreamWriteFullBox(&s, "mdhd", AVIF_BOX_SIZE_TBD, 1, 0); |
| 1383 | avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time; |
| 1384 | avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time; |
| 1385 | avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale; |
| 1386 | avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration; |
| 1387 | avifRWStreamWriteU16(&s, 21956); // bit(1) pad = 0; unsigned int(5)[3] language; ("und") |
| 1388 | avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0; |
| 1389 | avifRWStreamFinishBox(&s, mdhd); |
| 1390 | |
| 1391 | avifBoxMarker hdlrTrak = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1392 | avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0; |
| 1393 | avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type; |
| 1394 | avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0; |
| 1395 | avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator) |
| 1396 | avifRWStreamFinishBox(&s, hdlrTrak); |
| 1397 | |
| 1398 | avifBoxMarker minf = avifRWStreamWriteBox(&s, "minf", AVIF_BOX_SIZE_TBD); |
| 1399 | |
| 1400 | avifBoxMarker vmhd = avifRWStreamWriteFullBox(&s, "vmhd", AVIF_BOX_SIZE_TBD, 0, 1); |
| 1401 | avifRWStreamWriteU16(&s, 0); // template unsigned int(16) graphicsmode = 0; (copy over the existing image) |
| 1402 | avifRWStreamWriteZeros(&s, 6); // template unsigned int(16)[3] opcolor = {0, 0, 0}; |
| 1403 | avifRWStreamFinishBox(&s, vmhd); |
| 1404 | |
Joe Drago | 20ec176 | 2020-06-03 17:34:43 -0700 | [diff] [blame] | 1405 | avifBoxMarker dinf = avifRWStreamWriteBox(&s, "dinf", AVIF_BOX_SIZE_TBD); |
| 1406 | avifBoxMarker dref = avifRWStreamWriteFullBox(&s, "dref", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1407 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; |
| 1408 | avifRWStreamWriteFullBox(&s, "url ", 0, 0, 1); // flags:1 means data is in this file |
| 1409 | avifRWStreamFinishBox(&s, dref); |
| 1410 | avifRWStreamFinishBox(&s, dinf); |
| 1411 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1412 | avifBoxMarker stbl = avifRWStreamWriteBox(&s, "stbl", AVIF_BOX_SIZE_TBD); |
| 1413 | |
| 1414 | avifBoxMarker stco = avifRWStreamWriteFullBox(&s, "stco", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | a72da5b | 2020-06-15 19:40:17 -0700 | [diff] [blame] | 1415 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; |
| 1416 | avifEncoderItemAddMdatFixup(item, &s); // |
| 1417 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) chunk_offset; (set later) |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1418 | avifRWStreamFinishBox(&s, stco); |
| 1419 | |
| 1420 | avifBoxMarker stsc = avifRWStreamWriteFullBox(&s, "stsc", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1421 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; |
| 1422 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) first_chunk; |
| 1423 | avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) samples_per_chunk; |
| 1424 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) sample_description_index; |
| 1425 | avifRWStreamFinishBox(&s, stsc); |
| 1426 | |
| 1427 | avifBoxMarker stsz = avifRWStreamWriteFullBox(&s, "stsz", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1428 | avifRWStreamWriteU32(&s, 0); // unsigned int(32) sample_size; |
| 1429 | avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) sample_count; |
| 1430 | for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { |
| 1431 | avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; |
| 1432 | avifRWStreamWriteU32(&s, (uint32_t)sample->data.size); // unsigned int(32) entry_size; |
| 1433 | } |
| 1434 | avifRWStreamFinishBox(&s, stsz); |
| 1435 | |
| 1436 | avifBoxMarker stss = avifRWStreamWriteFullBox(&s, "stss", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1437 | avifRWStreamWriteU32(&s, syncSamplesCount); // unsigned int(32) entry_count; |
| 1438 | for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { |
| 1439 | avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; |
| 1440 | if (sample->sync) { |
| 1441 | avifRWStreamWriteU32(&s, sampleIndex + 1); // unsigned int(32) sample_number; |
| 1442 | } |
| 1443 | } |
| 1444 | avifRWStreamFinishBox(&s, stss); |
| 1445 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1446 | avifBoxMarker stts = avifRWStreamWriteFullBox(&s, "stts", AVIF_BOX_SIZE_TBD, 0, 0); |
Joe Drago | 85e387a | 2020-06-03 17:12:53 -0700 | [diff] [blame] | 1447 | size_t sttsEntryCountOffset = avifRWStreamOffset(&s); |
| 1448 | uint32_t sttsEntryCount = 0; |
| 1449 | avifRWStreamWriteU32(&s, 0); // unsigned int(32) entry_count; |
| 1450 | for (uint32_t sampleCount = 0, frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) { |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1451 | avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex]; |
Joe Drago | 85e387a | 2020-06-03 17:12:53 -0700 | [diff] [blame] | 1452 | ++sampleCount; |
| 1453 | if (frameIndex < (encoder->data->frames.count - 1)) { |
| 1454 | avifEncoderFrame * nextFrame = &encoder->data->frames.frame[frameIndex + 1]; |
| 1455 | if (frame->durationInTimescales == nextFrame->durationInTimescales) { |
| 1456 | continue; |
| 1457 | } |
| 1458 | } |
| 1459 | avifRWStreamWriteU32(&s, sampleCount); // unsigned int(32) sample_count; |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1460 | avifRWStreamWriteU32(&s, (uint32_t)frame->durationInTimescales); // unsigned int(32) sample_delta; |
Joe Drago | 85e387a | 2020-06-03 17:12:53 -0700 | [diff] [blame] | 1461 | sampleCount = 0; |
| 1462 | ++sttsEntryCount; |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1463 | } |
Joe Drago | 85e387a | 2020-06-03 17:12:53 -0700 | [diff] [blame] | 1464 | size_t prevOffset = avifRWStreamOffset(&s); |
| 1465 | avifRWStreamSetOffset(&s, sttsEntryCountOffset); |
| 1466 | avifRWStreamWriteU32(&s, sttsEntryCount); |
| 1467 | avifRWStreamSetOffset(&s, prevOffset); |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1468 | avifRWStreamFinishBox(&s, stts); |
| 1469 | |
| 1470 | avifBoxMarker stsd = avifRWStreamWriteFullBox(&s, "stsd", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1471 | avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; |
| 1472 | avifBoxMarker av01 = avifRWStreamWriteBox(&s, "av01", AVIF_BOX_SIZE_TBD); |
| 1473 | avifRWStreamWriteZeros(&s, 6); // const unsigned int(8)[6] reserved = 0; |
| 1474 | avifRWStreamWriteU16(&s, 1); // unsigned int(16) data_reference_index; |
| 1475 | avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0; |
| 1476 | avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0; |
| 1477 | avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 3); // unsigned int(32)[3] pre_defined = 0; |
| 1478 | avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->width); // unsigned int(16) width; |
| 1479 | avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->height); // unsigned int(16) height; |
| 1480 | avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) horizresolution |
| 1481 | avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) vertresolution |
| 1482 | avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0; |
| 1483 | avifRWStreamWriteU16(&s, 1); // template unsigned int(16) frame_count = 1; |
Wan-Teh Chang | 1a32f22 | 2020-07-09 15:44:25 -0700 | [diff] [blame] | 1484 | avifRWStreamWriteChars(&s, "\012AOM Coding", 11); // string[32] compressorname; |
| 1485 | avifRWStreamWriteZeros(&s, 32 - 11); // |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1486 | avifRWStreamWriteU16(&s, 0x0018); // template unsigned int(16) depth = 0x0018; |
| 1487 | avifRWStreamWriteU16(&s, (uint16_t)0xffff); // int(16) pre_defined = -1; |
Joe Drago | 2172ed0 | 2020-11-04 18:04:44 -0800 | [diff] [blame] | 1488 | writeConfigBox(&s, &item->av1C); |
Joe Drago | 332180d | 2020-06-15 16:55:16 -0700 | [diff] [blame] | 1489 | if (!item->alpha) { |
| 1490 | avifEncoderWriteColorProperties(&s, imageMetadata, NULL, NULL); |
| 1491 | } |
Vignesh Venkatasubramanian | f18f54e | 2022-02-17 14:10:17 -0800 | [diff] [blame] | 1492 | |
| 1493 | avifBoxMarker ccst = avifRWStreamWriteFullBox(&s, "ccst", AVIF_BOX_SIZE_TBD, 0, 0); |
| 1494 | const uint8_t ccstValue = (0 << 7) | // unsigned int(1) all_ref_pics_intra; |
| 1495 | (1 << 6) | // unsigned int(1) intra_pred_used; |
| 1496 | (15 << 2); // unsigned int(4) max_ref_per_pic; |
| 1497 | avifRWStreamWriteU8(&s, ccstValue); |
| 1498 | avifRWStreamWriteZeros(&s, 3); // unsigned int(26) reserved; (two zero bits are written along with ccstValue). |
| 1499 | avifRWStreamFinishBox(&s, ccst); |
| 1500 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1501 | avifRWStreamFinishBox(&s, av01); |
| 1502 | avifRWStreamFinishBox(&s, stsd); |
| 1503 | |
| 1504 | avifRWStreamFinishBox(&s, stbl); |
| 1505 | |
| 1506 | avifRWStreamFinishBox(&s, minf); |
| 1507 | avifRWStreamFinishBox(&s, mdia); |
| 1508 | avifRWStreamFinishBox(&s, trak); |
| 1509 | } |
| 1510 | |
| 1511 | // ------------------------------------------------------------------- |
| 1512 | // Finish moov box |
| 1513 | |
| 1514 | avifRWStreamFinishBox(&s, moov); |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1515 | } |
| 1516 | |
| 1517 | // ----------------------------------------------------------------------- |
Joe Drago | eb652d8 | 2019-04-23 16:29:07 -0700 | [diff] [blame] | 1518 | // Write mdat |
| 1519 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1520 | encoder->ioStats.colorOBUSize = 0; |
| 1521 | encoder->ioStats.alphaOBUSize = 0; |
| 1522 | |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1523 | avifBoxMarker mdat = avifRWStreamWriteBox(&s, "mdat", AVIF_BOX_SIZE_TBD); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1524 | const size_t mdatStartOffset = avifRWStreamOffset(&s); |
Joe Drago | 76e421e | 2020-09-21 13:20:24 -0700 | [diff] [blame] | 1525 | for (uint32_t itemPasses = 0; itemPasses < 3; ++itemPasses) { |
| 1526 | // Use multiple passes to pack in the following order: |
| 1527 | // * Pass 0: metadata (Exif/XMP) |
| 1528 | // * Pass 1: alpha (AV1) |
| 1529 | // * Pass 2: all other item data (AV1 color) |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1530 | // |
Wan-Teh Chang | 277d0d7 | 2020-10-08 10:24:41 -0700 | [diff] [blame] | 1531 | // See here for the discussion on alpha coming before color: |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1532 | // https://github.com/AOMediaCodec/libavif/issues/287 |
| 1533 | // |
Joe Drago | 76e421e | 2020-09-21 13:20:24 -0700 | [diff] [blame] | 1534 | // Exif and XMP are packed first as they're required to be fully available |
| 1535 | // by avifDecoderParse() before it returns AVIF_RESULT_OK, unless ignoreXMP |
| 1536 | // and ignoreExif are enabled. |
| 1537 | // |
| 1538 | const avifBool metadataPass = (itemPasses == 0); |
| 1539 | const avifBool alphaPass = (itemPasses == 1); |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1540 | |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1541 | for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { |
| 1542 | avifEncoderItem * item = &encoder->data->items.item[itemIndex]; |
Joe Drago | 0e13ce1 | 2021-04-01 17:18:53 -0700 | [diff] [blame] | 1543 | const avifBool isGrid = (item->gridCols > 0); // Grids store their payload in metadataPayload, so use this to distinguish grid payloads from XMP/Exif |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1544 | if ((item->metadataPayload.size == 0) && (item->encodeOutput->samples.count == 0)) { |
Joe Drago | 76e421e | 2020-09-21 13:20:24 -0700 | [diff] [blame] | 1545 | // this item has nothing for the mdat box |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1546 | continue; |
Joe Drago | 2c4e714 | 2020-06-03 10:31:46 -0700 | [diff] [blame] | 1547 | } |
Joe Drago | e568f73 | 2021-03-31 00:13:37 -0700 | [diff] [blame] | 1548 | if (!isGrid && (metadataPass != (item->metadataPayload.size > 0))) { |
Joe Drago | 0e13ce1 | 2021-04-01 17:18:53 -0700 | [diff] [blame] | 1549 | // only process metadata (XMP/Exif) payloads when metadataPass is true |
Joe Drago | 76e421e | 2020-09-21 13:20:24 -0700 | [diff] [blame] | 1550 | continue; |
| 1551 | } |
| 1552 | if (alphaPass != item->alpha) { |
| 1553 | // only process alpha payloads when alphaPass is true |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1554 | continue; |
| 1555 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1556 | |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1557 | size_t chunkOffset = 0; |
| 1558 | |
| 1559 | // Deduplication - See if an identical chunk to this has already been written |
Qiang Zhou | 295cab3 | 2020-12-31 23:20:37 +0800 | [diff] [blame] | 1560 | if (item->encodeOutput->samples.count > 0) { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1561 | avifEncodeSample * sample = &item->encodeOutput->samples.sample[0]; |
| 1562 | chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, sample->data.data, sample->data.size); |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1563 | } else { |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1564 | chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, item->metadataPayload.data, item->metadataPayload.size); |
| 1565 | } |
| 1566 | |
| 1567 | if (!chunkOffset) { |
| 1568 | // We've never seen this chunk before; write it out |
Wan-Teh Chang | 8adbb97 | 2020-12-18 14:12:51 -0800 | [diff] [blame] | 1569 | chunkOffset = avifRWStreamOffset(&s); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1570 | if (item->encodeOutput->samples.count > 0) { |
| 1571 | for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { |
| 1572 | avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; |
| 1573 | avifRWStreamWrite(&s, sample->data.data, sample->data.size); |
| 1574 | |
| 1575 | if (item->alpha) { |
| 1576 | encoder->ioStats.alphaOBUSize += sample->data.size; |
| 1577 | } else { |
| 1578 | encoder->ioStats.colorOBUSize += sample->data.size; |
| 1579 | } |
| 1580 | } |
| 1581 | } else { |
| 1582 | avifRWStreamWrite(&s, item->metadataPayload.data, item->metadataPayload.size); |
| 1583 | } |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1584 | } |
| 1585 | |
| 1586 | for (uint32_t fixupIndex = 0; fixupIndex < item->mdatFixups.count; ++fixupIndex) { |
| 1587 | avifOffsetFixup * fixup = &item->mdatFixups.fixup[fixupIndex]; |
| 1588 | size_t prevOffset = avifRWStreamOffset(&s); |
| 1589 | avifRWStreamSetOffset(&s, fixup->offset); |
Joe Drago | aec9cff | 2020-12-11 14:23:37 -0800 | [diff] [blame] | 1590 | avifRWStreamWriteU32(&s, (uint32_t)chunkOffset); |
Joe Drago | e888d54 | 2020-09-08 20:42:09 -0700 | [diff] [blame] | 1591 | avifRWStreamSetOffset(&s, prevOffset); |
| 1592 | } |
Joe Drago | 800b47f | 2020-03-18 16:22:37 -0700 | [diff] [blame] | 1593 | } |
Joe Drago | f6a4227 | 2019-11-21 15:21:41 -0800 | [diff] [blame] | 1594 | } |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1595 | avifRWStreamFinishBox(&s, mdat); |
Joe Drago | eb652d8 | 2019-04-23 16:29:07 -0700 | [diff] [blame] | 1596 | |
| 1597 | // ----------------------------------------------------------------------- |
| 1598 | // Finish up stream |
| 1599 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1600 | avifRWStreamFinishWrite(&s); |
Joe Drago | 0a5a0e4 | 2019-04-11 11:32:56 -0700 | [diff] [blame] | 1601 | |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 1602 | return AVIF_RESULT_OK; |
| 1603 | } |
Joe Drago | 0b05eee | 2019-06-12 13:24:39 -0700 | [diff] [blame] | 1604 | |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 1605 | avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avifRWData * output) |
| 1606 | { |
Joe Drago | d685024 | 2020-06-16 16:05:37 -0700 | [diff] [blame] | 1607 | avifResult addImageResult = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); |
Joe Drago | 250221a | 2020-06-01 11:11:06 -0700 | [diff] [blame] | 1608 | if (addImageResult != AVIF_RESULT_OK) { |
| 1609 | return addImageResult; |
| 1610 | } |
| 1611 | return avifEncoderFinish(encoder, output); |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1612 | } |
| 1613 | |
Wan-Teh Chang | e184dc1 | 2020-05-11 12:47:21 -0700 | [diff] [blame] | 1614 | static avifBool avifImageIsOpaque(const avifImage * image) |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1615 | { |
Joe Drago | 7ad3ad6 | 2019-02-07 11:17:34 -0800 | [diff] [blame] | 1616 | if (!image->alphaPlane) { |
| 1617 | return AVIF_TRUE; |
| 1618 | } |
| 1619 | |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1620 | int maxChannel = (1 << image->depth) - 1; |
Joe Drago | 7ad3ad6 | 2019-02-07 11:17:34 -0800 | [diff] [blame] | 1621 | if (avifImageUsesU16(image)) { |
Joe Drago | 341a6a6 | 2019-07-23 16:12:59 -0700 | [diff] [blame] | 1622 | for (uint32_t j = 0; j < image->height; ++j) { |
| 1623 | for (uint32_t i = 0; i < image->width; ++i) { |
Joe Drago | 7ad3ad6 | 2019-02-07 11:17:34 -0800 | [diff] [blame] | 1624 | uint16_t * p = (uint16_t *)&image->alphaPlane[(i * 2) + (j * image->alphaRowBytes)]; |
| 1625 | if (*p != maxChannel) { |
| 1626 | return AVIF_FALSE; |
| 1627 | } |
| 1628 | } |
| 1629 | } |
| 1630 | } else { |
Joe Drago | 341a6a6 | 2019-07-23 16:12:59 -0700 | [diff] [blame] | 1631 | for (uint32_t j = 0; j < image->height; ++j) { |
| 1632 | for (uint32_t i = 0; i < image->width; ++i) { |
Joe Drago | 7ad3ad6 | 2019-02-07 11:17:34 -0800 | [diff] [blame] | 1633 | if (image->alphaPlane[i + (j * image->alphaRowBytes)] != maxChannel) { |
| 1634 | return AVIF_FALSE; |
| 1635 | } |
Joe Drago | 444f051 | 2019-01-23 17:03:24 -0800 | [diff] [blame] | 1636 | } |
| 1637 | } |
| 1638 | } |
| 1639 | return AVIF_TRUE; |
| 1640 | } |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1641 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1642 | static void writeConfigBox(avifRWStream * s, avifCodecConfigurationBox * cfg) |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1643 | { |
Joe Drago | 4a25c19 | 2020-06-03 16:29:58 -0700 | [diff] [blame] | 1644 | avifBoxMarker av1C = avifRWStreamWriteBox(s, "av1C", AVIF_BOX_SIZE_TBD); |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1645 | |
| 1646 | // unsigned int (1) marker = 1; |
| 1647 | // unsigned int (7) version = 1; |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1648 | avifRWStreamWriteU8(s, 0x80 | 0x1); |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1649 | |
| 1650 | // unsigned int (3) seq_profile; |
| 1651 | // unsigned int (5) seq_level_idx_0; |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1652 | avifRWStreamWriteU8(s, (uint8_t)((cfg->seqProfile & 0x7) << 5) | (uint8_t)(cfg->seqLevelIdx0 & 0x1f)); |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1653 | |
| 1654 | uint8_t bits = 0; |
| 1655 | bits |= (cfg->seqTier0 & 0x1) << 7; // unsigned int (1) seq_tier_0; |
| 1656 | bits |= (cfg->highBitdepth & 0x1) << 6; // unsigned int (1) high_bitdepth; |
| 1657 | bits |= (cfg->twelveBit & 0x1) << 5; // unsigned int (1) twelve_bit; |
| 1658 | bits |= (cfg->monochrome & 0x1) << 4; // unsigned int (1) monochrome; |
| 1659 | bits |= (cfg->chromaSubsamplingX & 0x1) << 3; // unsigned int (1) chroma_subsampling_x; |
| 1660 | bits |= (cfg->chromaSubsamplingY & 0x1) << 2; // unsigned int (1) chroma_subsampling_y; |
| 1661 | bits |= (cfg->chromaSamplePosition & 0x3); // unsigned int (2) chroma_sample_position; |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1662 | avifRWStreamWriteU8(s, bits); |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1663 | |
| 1664 | // unsigned int (3) reserved = 0; |
| 1665 | // unsigned int (1) initial_presentation_delay_present; |
| 1666 | // if (initial_presentation_delay_present) { |
| 1667 | // unsigned int (4) initial_presentation_delay_minus_one; |
| 1668 | // } else { |
| 1669 | // unsigned int (4) reserved = 0; |
| 1670 | // } |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1671 | avifRWStreamWriteU8(s, 0); |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1672 | |
Joe Drago | 345aaa1 | 2019-09-25 13:42:12 -0700 | [diff] [blame] | 1673 | avifRWStreamFinishBox(s, av1C); |
Joe Drago | 1585797 | 2019-02-13 17:48:28 -0800 | [diff] [blame] | 1674 | } |