diff --git a/include/avif/internal.h b/include/avif/internal.h index 623dc7c5ac..74eb7d5423 100644 --- a/include/avif/internal.h +++ b/include/avif/internal.h @@ -508,19 +508,19 @@ void avifRWStreamSetOffset(avifRWStream * stream, size_t offset); void avifRWStreamFinishWrite(avifRWStream * stream); // The following functions require byte alignment. -void avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size); -void avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size); -avifBoxMarker avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize); -avifBoxMarker avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags); +avifResult avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size); +avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size); +avifResult avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize, avifBoxMarker * marker); +avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker); void avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker); -void avifRWStreamWriteU8(avifRWStream * stream, uint8_t v); -void avifRWStreamWriteU16(avifRWStream * stream, uint16_t v); -void avifRWStreamWriteU32(avifRWStream * stream, uint32_t v); -void avifRWStreamWriteU64(avifRWStream * stream, uint64_t v); -void avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount); +avifResult avifRWStreamWriteU8(avifRWStream * stream, uint8_t v); +avifResult avifRWStreamWriteU16(avifRWStream * stream, uint16_t v); +avifResult avifRWStreamWriteU32(avifRWStream * stream, uint32_t v); +avifResult avifRWStreamWriteU64(avifRWStream * stream, uint64_t v); +avifResult avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount); // The following functions can write non-aligned bits. -void avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount); -void avifRWStreamWriteVarInt(avifRWStream * stream, uint32_t v); +avifResult avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount); +avifResult avifRWStreamWriteVarInt(avifRWStream * stream, uint32_t v); // This is to make it clear that the box size is currently unknown, and will be determined later (with a call to avifRWStreamFinishBox) #define AVIF_BOX_SIZE_TBD 0 diff --git a/src/stream.c b/src/stream.c index ec6d98f949..c85ca31b54 100644 --- a/src/stream.c +++ b/src/stream.c @@ -317,17 +317,14 @@ avifBool avifROStreamReadAndEnforceVersion(avifROStream * stream, uint8_t enforc // avifRWStream #define AVIF_STREAM_BUFFER_INCREMENT (1024 * 1024) -static void makeRoom(avifRWStream * stream, size_t size) +static avifResult makeRoom(avifRWStream * stream, size_t size) { size_t neededSize = stream->offset + size; size_t newSize = stream->raw->size; while (newSize < neededSize) { newSize += AVIF_STREAM_BUFFER_INCREMENT; } - if (avifRWDataRealloc(stream->raw, newSize) != AVIF_RESULT_OK) { - // TODO(issue #820): Return AVIF_RESULT_OUT_OF_MEMORY instead. - abort(); - } + return avifRWDataRealloc(stream->raw, newSize); } void avifRWStreamStart(avifRWStream * stream, avifRWData * raw) @@ -361,33 +358,34 @@ void avifRWStreamFinishWrite(avifRWStream * stream) } } -void avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size) +avifResult avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - if (!size) { - return; + if (size) { + AVIF_CHECKRES(makeRoom(stream, size)); + memcpy(stream->raw->data + stream->offset, data, size); + stream->offset += size; } - - makeRoom(stream, size); - memcpy(stream->raw->data + stream->offset, data, size); - stream->offset += size; + return AVIF_RESULT_OK; } -void avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size) +avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size) { - avifRWStreamWrite(stream, chars, size); + return avifRWStreamWrite(stream, chars, size); } -avifBoxMarker avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags) +avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - avifBoxMarker marker = stream->offset; + if (marker) { + *marker = stream->offset; + } size_t headerSize = sizeof(uint32_t) + 4 /* size of type */; if (version != -1) { headerSize += 4; } - makeRoom(stream, headerSize); + AVIF_CHECKRES(makeRoom(stream, headerSize)); memset(stream->raw->data + stream->offset, 0, headerSize); uint32_t noSize = avifHTONL((uint32_t)(headerSize + contentSize)); memcpy(stream->raw->data + stream->offset, &noSize, sizeof(uint32_t)); @@ -400,12 +398,12 @@ avifBoxMarker avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, } stream->offset += headerSize; - return marker; + return AVIF_RESULT_OK; } -avifBoxMarker avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize) +avifResult avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize, avifBoxMarker * marker) { - return avifRWStreamWriteFullBox(stream, type, contentSize, -1, 0); + return avifRWStreamWriteFullBox(stream, type, contentSize, -1, 0, marker); } void avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker) @@ -415,63 +413,63 @@ void avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker) memcpy(stream->raw->data + marker, &noSize, sizeof(uint32_t)); } -void avifRWStreamWriteU8(avifRWStream * stream, uint8_t v) +avifResult avifRWStreamWriteU8(avifRWStream * stream, uint8_t v) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - makeRoom(stream, 1); + AVIF_CHECKRES(makeRoom(stream, 1)); stream->raw->data[stream->offset] = v; stream->offset += 1; + return AVIF_RESULT_OK; } -void avifRWStreamWriteU16(avifRWStream * stream, uint16_t v) +avifResult avifRWStreamWriteU16(avifRWStream * stream, uint16_t v) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - size_t size = sizeof(uint16_t); + const size_t size = sizeof(uint16_t); + AVIF_CHECKRES(makeRoom(stream, size)); v = avifHTONS(v); - makeRoom(stream, size); memcpy(stream->raw->data + stream->offset, &v, size); stream->offset += size; + return AVIF_RESULT_OK; } -void avifRWStreamWriteU32(avifRWStream * stream, uint32_t v) +avifResult avifRWStreamWriteU32(avifRWStream * stream, uint32_t v) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - size_t size = sizeof(uint32_t); + const size_t size = sizeof(uint32_t); + AVIF_CHECKRES(makeRoom(stream, size)); v = avifHTONL(v); - makeRoom(stream, size); memcpy(stream->raw->data + stream->offset, &v, size); stream->offset += size; + return AVIF_RESULT_OK; } -void avifRWStreamWriteU64(avifRWStream * stream, uint64_t v) +avifResult avifRWStreamWriteU64(avifRWStream * stream, uint64_t v) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - size_t size = sizeof(uint64_t); + const size_t size = sizeof(uint64_t); + AVIF_CHECKRES(makeRoom(stream, size)); v = avifHTON64(v); - makeRoom(stream, size); memcpy(stream->raw->data + stream->offset, &v, size); stream->offset += size; + return AVIF_RESULT_OK; } -void avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount) +avifResult avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount) { assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. - makeRoom(stream, byteCount); - uint8_t * p = stream->raw->data + stream->offset; - uint8_t * end = p + byteCount; - while (p != end) { - *p = 0; - ++p; - } + AVIF_CHECKRES(makeRoom(stream, byteCount)); + memset(stream->raw->data + stream->offset, 0, byteCount); stream->offset += byteCount; + return AVIF_RESULT_OK; } -void avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount) +avifResult avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount) { assert(((uint64_t)v >> bitCount) == 0); // (uint32_t >> 32 is undefined behavior) while (bitCount) { if (stream->numUsedBitsInPartialByte == 0) { - makeRoom(stream, 1); // Book a new partial byte in the stream. + AVIF_CHECKRES(makeRoom(stream, 1)); // Book a new partial byte in the stream. stream->raw->data[stream->offset] = 0; stream->offset += 1; } @@ -494,30 +492,32 @@ void avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount) stream->numUsedBitsInPartialByte = 0; } } + return AVIF_RESULT_OK; } // Based on https://sqlite.org/src4/doc/trunk/www/varint.wiki. -void avifRWStreamWriteVarInt(avifRWStream * stream, uint32_t v) +avifResult avifRWStreamWriteVarInt(avifRWStream * stream, uint32_t v) { if (v <= 240) { - avifRWStreamWriteBits(stream, v, 8); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, v, 8)); } else if (v <= 2287) { - avifRWStreamWriteBits(stream, (v - 240) / 256 + 241, 8); - avifRWStreamWriteBits(stream, (v - 240) % 256, 8); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v - 240) / 256 + 241, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v - 240) % 256, 8)); } else if (v <= 67823) { - avifRWStreamWriteBits(stream, 249, 8); - avifRWStreamWriteBits(stream, (v - 2288) / 256, 8); - avifRWStreamWriteBits(stream, (v - 2288) % 256, 8); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, 249, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v - 2288) / 256, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v - 2288) % 256, 8)); } else if (v <= 16777215) { - avifRWStreamWriteBits(stream, 250, 8); - avifRWStreamWriteBits(stream, (v >> 0) & 0xff, 8); - avifRWStreamWriteBits(stream, (v >> 8) & 0xff, 8); - avifRWStreamWriteBits(stream, (v >> 16) & 0xff, 8); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, 250, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 0) & 0xff, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 8) & 0xff, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 16) & 0xff, 8)); } else { - avifRWStreamWriteBits(stream, 251, 8); - avifRWStreamWriteBits(stream, (v >> 0) & 0xff, 8); - avifRWStreamWriteBits(stream, (v >> 8) & 0xff, 8); - avifRWStreamWriteBits(stream, (v >> 16) & 0xff, 8); - avifRWStreamWriteBits(stream, (v >> 24) & 0xff, 8); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, 251, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 0) & 0xff, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 8) & 0xff, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 16) & 0xff, 8)); + AVIF_CHECKRES(avifRWStreamWriteBits(stream, (v >> 24) & 0xff, 8)); } + return AVIF_RESULT_OK; } diff --git a/src/write.c b/src/write.c index 55d1bc2f63..623332b639 100644 --- a/src/write.c +++ b/src/write.c @@ -14,12 +14,6 @@ struct ipmaArray avifBool essential[MAX_ASSOCIATIONS]; uint8_t count; }; -static void ipmaPush(struct ipmaArray * ipma, uint8_t assoc, avifBool essential) -{ - ipma->associations[ipma->count] = assoc; - ipma->essential[ipma->count] = essential; - ++ipma->count; -} // Used to store offsets in meta boxes which need to point at mdat offsets that // aren't known yet. When an item's mdat payload is written, all registered fixups @@ -36,7 +30,7 @@ static const size_t alphaURNSize = sizeof(alphaURN); static const char xmpContentType[] = AVIF_CONTENT_TYPE_XMP; static const size_t xmpContentTypeSize = sizeof(xmpContentType); -static void writeConfigBox(avifRWStream * s, const avifCodecConfigurationBox * cfg, const char * configPropName); +static avifResult writeConfigBox(avifRWStream * s, const avifCodecConfigurationBox * cfg, const char * configPropName); // --------------------------------------------------------------------------- // avifSetTileConfiguration @@ -376,16 +370,15 @@ static void avifItemPropertyDedupStart(avifItemPropertyDedup * dedup) // This compares the newly written item property (in the dedup's temporary storage buffer) to // already-written properties (whose offsets/sizes in outputStream are recorded in the dedup). If a -// match is found, the previous item's index is used. If this new property is unique, it is +// match is found, the previous property's index is used. If this new property is unique, it is // assigned the next available property index, written to the output stream, and its offset/size in // the output stream is recorded in the dedup for future comparisons. // -// This function always returns a valid 1-indexed property index for usage in a property association -// (ipma) box later. If the most recent property was a duplicate of a previous property, the return -// value will be the index of the original property, otherwise it will be the index of the newly -// created property. -static uint8_t avifItemPropertyDedupFinish(avifItemPropertyDedup * dedup, avifRWStream * outputStream) +// On success, this function adds to the given ipma box a property association linking the reused +// or newly created property with the item. +static avifResult avifItemPropertyDedupFinish(avifItemPropertyDedup * dedup, avifRWStream * outputStream, struct ipmaArray * ipma, avifBool essential) { + uint8_t propertyIndex = 0; const size_t newPropertySize = avifRWStreamOffset(&dedup->s); for (size_t i = 0; i < dedup->properties.count; ++i) { @@ -393,17 +386,27 @@ static uint8_t avifItemPropertyDedupFinish(avifItemPropertyDedup * dedup, avifRW if ((property->size == newPropertySize) && !memcmp(&outputStream->raw->data[property->offset], dedup->buffer.data, newPropertySize)) { // We've already written this exact property, reuse it - return property->index; + propertyIndex = property->index; + assert(propertyIndex != 0); + break; } } - // Write a new property, and remember its location in the output stream for future deduplication - avifItemProperty * property = (avifItemProperty *)avifArrayPushPtr(&dedup->properties); - property->index = ++dedup->nextIndex; // preincrement so the first new index is 1 (as ipma is 1-indexed) - property->size = newPropertySize; - property->offset = avifRWStreamOffset(outputStream); - avifRWStreamWrite(outputStream, dedup->buffer.data, newPropertySize); - return property->index; + if (propertyIndex == 0) { + // Write a new property, and remember its location in the output stream for future deduplication + avifItemProperty * property = (avifItemProperty *)avifArrayPushPtr(&dedup->properties); + property->index = ++dedup->nextIndex; // preincrement so the first new index is 1 (as ipma is 1-indexed) + property->size = newPropertySize; + property->offset = avifRWStreamOffset(outputStream); + AVIF_CHECKRES(avifRWStreamWrite(outputStream, dedup->buffer.data, newPropertySize)); + propertyIndex = property->index; + } + + AVIF_CHECKERR(ipma->count < MAX_ASSOCIATIONS, AVIF_RESULT_UNKNOWN_ERROR); + ipma->associations[ipma->count] = propertyIndex; + ipma->essential[ipma->count] = essential; + ++ipma->count; + return AVIF_RESULT_OK; } // --------------------------------------------------------------------------- @@ -538,11 +541,11 @@ static avifBool avifEncoderDetectChanges(const avifEncoder * encoder, avifEncode } // Subset of avifEncoderWriteColorProperties() for the properties clli, pasp, clap, irot, imir. -static void avifEncoderWriteExtendedColorProperties(avifRWStream * dedupStream, - avifRWStream * outputStream, - const avifImage * imageMetadata, - struct ipmaArray * ipma, - avifItemPropertyDedup * dedup); +static avifResult avifEncoderWriteExtendedColorProperties(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + struct ipmaArray * ipma, + avifItemPropertyDedup * dedup); // This function is used in two codepaths: // * writing color *item* properties @@ -555,10 +558,10 @@ static void avifEncoderWriteExtendedColorProperties(avifRWStream * dedupStream, // there is no need to build a property association box (ipma), and no way to deduplicate/reuse a // property. In this case, the ipma and dedup properties should/will be set to NULL, and this // function will avoid using them. -static void avifEncoderWriteColorProperties(avifRWStream * outputStream, - const avifImage * imageMetadata, - struct ipmaArray * ipma, - avifItemPropertyDedup * dedup) +static avifResult avifEncoderWriteColorProperties(avifRWStream * outputStream, + const avifImage * imageMetadata, + struct ipmaArray * ipma, + avifItemPropertyDedup * dedup) { // outputStream is the final bitstream that will be output by the libavif encoder API. // dedupStream is either equal to outputStream or to &dedup->s which is a temporary stream used @@ -576,12 +579,13 @@ static void avifEncoderWriteColorProperties(avifRWStream * outputStream, if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker colr = avifRWStreamWriteBox(dedupStream, "colr", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteChars(dedupStream, "prof", 4); // unsigned int(32) colour_type; - avifRWStreamWrite(dedupStream, imageMetadata->icc.data, imageMetadata->icc.size); + avifBoxMarker colr; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "colr", AVIF_BOX_SIZE_TBD, &colr)); + AVIF_CHECKRES(avifRWStreamWriteChars(dedupStream, "prof", 4)); // unsigned int(32) colour_type; + AVIF_CHECKRES(avifRWStreamWrite(dedupStream, imageMetadata->icc.data, imageMetadata->icc.size)); avifRWStreamFinishBox(dedupStream, colr); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_FALSE)); } } @@ -590,38 +594,40 @@ static void avifEncoderWriteColorProperties(avifRWStream * outputStream, if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker colr = avifRWStreamWriteBox(dedupStream, "colr", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteChars(dedupStream, "nclx", 4); // unsigned int(32) colour_type; - avifRWStreamWriteU16(dedupStream, imageMetadata->colorPrimaries); // unsigned int(16) colour_primaries; - avifRWStreamWriteU16(dedupStream, imageMetadata->transferCharacteristics); // unsigned int(16) transfer_characteristics; - avifRWStreamWriteU16(dedupStream, imageMetadata->matrixCoefficients); // unsigned int(16) matrix_coefficients; - avifRWStreamWriteBits(dedupStream, (imageMetadata->yuvRange == AVIF_RANGE_FULL) ? 1 : 0, /*bitCount=*/1); // unsigned int(1) full_range_flag; - avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/7); // unsigned int(7) reserved = 0; + avifBoxMarker colr; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "colr", AVIF_BOX_SIZE_TBD, &colr)); + AVIF_CHECKRES(avifRWStreamWriteChars(dedupStream, "nclx", 4)); // unsigned int(32) colour_type; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->colorPrimaries)); // unsigned int(16) colour_primaries; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->transferCharacteristics)); // unsigned int(16) transfer_characteristics; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->matrixCoefficients)); // unsigned int(16) matrix_coefficients; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, (imageMetadata->yuvRange == AVIF_RANGE_FULL) ? 1 : 0, /*bitCount=*/1)); // unsigned int(1) full_range_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/7)); // unsigned int(7) reserved = 0; avifRWStreamFinishBox(dedupStream, colr); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_FALSE)); } - avifEncoderWriteExtendedColorProperties(dedupStream, outputStream, imageMetadata, ipma, dedup); + return avifEncoderWriteExtendedColorProperties(dedupStream, outputStream, imageMetadata, ipma, dedup); } -static void avifEncoderWriteExtendedColorProperties(avifRWStream * dedupStream, - avifRWStream * outputStream, - const avifImage * imageMetadata, - struct ipmaArray * ipma, - avifItemPropertyDedup * dedup) +static avifResult avifEncoderWriteExtendedColorProperties(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + struct ipmaArray * ipma, + avifItemPropertyDedup * dedup) { // Write Content Light Level Information, if present if (imageMetadata->clli.maxCLL || imageMetadata->clli.maxPALL) { if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker clli = avifRWStreamWriteBox(dedupStream, "clli", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteU16(dedupStream, imageMetadata->clli.maxCLL); // unsigned int(16) max_content_light_level; - avifRWStreamWriteU16(dedupStream, imageMetadata->clli.maxPALL); // unsigned int(16) max_pic_average_light_level; + avifBoxMarker clli; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "clli", AVIF_BOX_SIZE_TBD, &clli)); + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->clli.maxCLL)); // unsigned int(16) max_content_light_level; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->clli.maxPALL)); // unsigned int(16) max_pic_average_light_level; avifRWStreamFinishBox(dedupStream, clli); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_FALSE)); } } @@ -630,61 +636,66 @@ static void avifEncoderWriteExtendedColorProperties(avifRWStream * dedupStream, if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker pasp = avifRWStreamWriteBox(dedupStream, "pasp", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteU32(dedupStream, imageMetadata->pasp.hSpacing); // unsigned int(32) hSpacing; - avifRWStreamWriteU32(dedupStream, imageMetadata->pasp.vSpacing); // unsigned int(32) vSpacing; + avifBoxMarker pasp; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "pasp", AVIF_BOX_SIZE_TBD, &pasp)); + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->pasp.hSpacing)); // unsigned int(32) hSpacing; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->pasp.vSpacing)); // unsigned int(32) vSpacing; avifRWStreamFinishBox(dedupStream, pasp); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_FALSE)); } } if (imageMetadata->transformFlags & AVIF_TRANSFORM_CLAP) { if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker clap = avifRWStreamWriteBox(dedupStream, "clap", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.widthN); // unsigned int(32) cleanApertureWidthN; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.widthD); // unsigned int(32) cleanApertureWidthD; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.heightN); // unsigned int(32) cleanApertureHeightN; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.heightD); // unsigned int(32) cleanApertureHeightD; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.horizOffN); // unsigned int(32) horizOffN; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.horizOffD); // unsigned int(32) horizOffD; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.vertOffN); // unsigned int(32) vertOffN; - avifRWStreamWriteU32(dedupStream, imageMetadata->clap.vertOffD); // unsigned int(32) vertOffD; + avifBoxMarker clap; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "clap", AVIF_BOX_SIZE_TBD, &clap)); + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.widthN)); // unsigned int(32) cleanApertureWidthN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.widthD)); // unsigned int(32) cleanApertureWidthD; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.heightN)); // unsigned int(32) cleanApertureHeightN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.heightD)); // unsigned int(32) cleanApertureHeightD; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.horizOffN)); // unsigned int(32) horizOffN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.horizOffD)); // unsigned int(32) horizOffD; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.vertOffN)); // unsigned int(32) vertOffN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.vertOffD)); // unsigned int(32) vertOffD; avifRWStreamFinishBox(dedupStream, clap); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_TRUE)); } } if (imageMetadata->transformFlags & AVIF_TRANSFORM_IROT) { if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker irot = avifRWStreamWriteBox(dedupStream, "irot", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/6); // unsigned int (6) reserved = 0; - avifRWStreamWriteBits(dedupStream, imageMetadata->irot.angle & 0x3, /*bitCount=*/2); // unsigned int (2) angle; + avifBoxMarker irot; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "irot", AVIF_BOX_SIZE_TBD, &irot)); + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/6)); // unsigned int (6) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, imageMetadata->irot.angle & 0x3, /*bitCount=*/2)); // unsigned int (2) angle; avifRWStreamFinishBox(dedupStream, irot); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_TRUE)); } } if (imageMetadata->transformFlags & AVIF_TRANSFORM_IMIR) { if (dedup) { avifItemPropertyDedupStart(dedup); } - avifBoxMarker imir = avifRWStreamWriteBox(dedupStream, "imir", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/7); // unsigned int (7) reserved = 0; - avifRWStreamWriteBits(dedupStream, imageMetadata->imir.mode ? 1 : 0, /*bitCount=*/1); // unsigned int (1) mode; + avifBoxMarker imir; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "imir", AVIF_BOX_SIZE_TBD, &imir)); + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/7)); // unsigned int (7) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, imageMetadata->imir.mode ? 1 : 0, /*bitCount=*/1)); // unsigned int (1) mode; avifRWStreamFinishBox(dedupStream, imir); if (dedup) { - ipmaPush(ipma, avifItemPropertyDedupFinish(dedup, outputStream), AVIF_TRUE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, ipma, AVIF_TRUE)); } } + return AVIF_RESULT_OK; } // Write unassociated metadata items (EXIF, XMP) to a small meta box inside of a trak box. // These items are implicitly associated with the track they are contained within. -static void avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s) +static avifResult avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s) { // Count how many non-image items (such as EXIF/XMP) are being written uint32_t metadataItemCount = 0; @@ -696,24 +707,27 @@ static void avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s } if (metadataItemCount == 0) { // Don't even bother writing the trak meta box - return; + return AVIF_RESULT_OK; } - avifBoxMarker meta = avifRWStreamWriteFullBox(s, "meta", AVIF_BOX_SIZE_TBD, 0, 0); + avifBoxMarker meta; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "meta", AVIF_BOX_SIZE_TBD, 0, 0, &meta)); - avifBoxMarker hdlr = avifRWStreamWriteFullBox(s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(s, 0); // unsigned int(32) pre_defined = 0; - avifRWStreamWriteChars(s, "pict", 4); // unsigned int(32) handler_type; - avifRWStreamWriteZeros(s, 12); // const unsigned int(32)[3] reserved = 0; - avifRWStreamWriteChars(s, "libavif", 8); // string name; (writing null terminator) + avifBoxMarker hdlr; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0, &hdlr)); + AVIF_CHECKRES(avifRWStreamWriteU32(s, 0)); // unsigned int(32) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(s, "pict", 4)); // unsigned int(32) handler_type; + AVIF_CHECKRES(avifRWStreamWriteZeros(s, 12)); // const unsigned int(32)[3] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(s, "libavif", 8)); // string name; (writing null terminator) avifRWStreamFinishBox(s, hdlr); - avifBoxMarker iloc = avifRWStreamWriteFullBox(s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteBits(s, 4, /*bitCount=*/4); // unsigned int(4) offset_size; - avifRWStreamWriteBits(s, 4, /*bitCount=*/4); // unsigned int(4) length_size; - avifRWStreamWriteBits(s, 0, /*bitCount=*/4); // unsigned int(4) base_offset_size; - avifRWStreamWriteBits(s, 0, /*bitCount=*/4); // unsigned int(4) reserved; - avifRWStreamWriteU16(s, (uint16_t)metadataItemCount); // unsigned int(16) item_count; + avifBoxMarker iloc; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0, &iloc)); + AVIF_CHECKRES(avifRWStreamWriteBits(s, 4, /*bitCount=*/4)); // unsigned int(4) offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 4, /*bitCount=*/4)); // unsigned int(4) length_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/4)); // unsigned int(4) base_offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/4)); // unsigned int(4) reserved; + AVIF_CHECKRES(avifRWStreamWriteU16(s, (uint16_t)metadataItemCount)); // unsigned int(16) item_count; for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) { avifEncoderItem * item = &encoder->data->items.item[trakItemIndex]; if (memcmp(item->type, encoder->data->imageItemType, 4) == 0) { @@ -721,17 +735,18 @@ static void avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s continue; } - avifRWStreamWriteU16(s, item->id); // unsigned int(16) item_ID; - avifRWStreamWriteU16(s, 0); // unsigned int(16) data_reference_index; - avifRWStreamWriteU16(s, 1); // unsigned int(16) extent_count; - avifEncoderItemAddMdatFixup(item, s); // - avifRWStreamWriteU32(s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset; - avifRWStreamWriteU32(s, (uint32_t)item->metadataPayload.size); // unsigned int(length_size*8) extent_length; + AVIF_CHECKRES(avifRWStreamWriteU16(s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(s, 0)); // unsigned int(16) data_reference_index; + AVIF_CHECKRES(avifRWStreamWriteU16(s, 1)); // unsigned int(16) extent_count; + avifEncoderItemAddMdatFixup(item, s); // + AVIF_CHECKRES(avifRWStreamWriteU32(s, 0 /* set later */)); // unsigned int(offset_size*8) extent_offset; + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)item->metadataPayload.size)); // unsigned int(length_size*8) extent_length; } avifRWStreamFinishBox(s, iloc); - avifBoxMarker iinf = avifRWStreamWriteFullBox(s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU16(s, (uint16_t)metadataItemCount); // unsigned int(16) entry_count; + avifBoxMarker iinf; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0, &iinf)); + AVIF_CHECKRES(avifRWStreamWriteU16(s, (uint16_t)metadataItemCount)); // unsigned int(16) entry_count; for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) { avifEncoderItem * item = &encoder->data->items.item[trakItemIndex]; if (memcmp(item->type, encoder->data->imageItemType, 4) == 0) { @@ -739,22 +754,24 @@ static void avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s } assert(!item->hiddenImage); - avifBoxMarker infe = avifRWStreamWriteFullBox(s, "infe", AVIF_BOX_SIZE_TBD, 2, 0); - avifRWStreamWriteU16(s, item->id); // unsigned int(16) item_ID; - avifRWStreamWriteU16(s, 0); // unsigned int(16) item_protection_index; - avifRWStreamWrite(s, item->type, 4); // unsigned int(32) item_type; - avifRWStreamWriteChars(s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator) - if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) - avifRWStreamWriteChars(s, item->infeContentType, item->infeContentTypeSize); + avifBoxMarker infe; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "infe", AVIF_BOX_SIZE_TBD, 2, 0, &infe)); + AVIF_CHECKRES(avifRWStreamWriteU16(s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(s, 0)); // unsigned int(16) item_protection_index; + AVIF_CHECKRES(avifRWStreamWrite(s, item->type, 4)); // unsigned int(32) item_type; + AVIF_CHECKRES(avifRWStreamWriteChars(s, item->infeName, item->infeNameSize)); // string item_name; (writing null terminator) + if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) + AVIF_CHECKRES(avifRWStreamWriteChars(s, item->infeContentType, item->infeContentTypeSize)); } avifRWStreamFinishBox(s, infe); } avifRWStreamFinishBox(s, iinf); avifRWStreamFinishBox(s, meta); + return AVIF_RESULT_OK; } -static void avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t gridRows, uint32_t gridWidth, uint32_t gridHeight) +static avifResult avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t gridRows, uint32_t gridWidth, uint32_t gridHeight) { // ISO/IEC 23008-12 6.6.2.3.2 // aligned(8) class ImageGrid { @@ -771,20 +788,21 @@ static void avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t avifRWStream s; avifRWStreamStart(&s, data); - avifRWStreamWriteU8(&s, 0); // unsigned int(8) version = 0; - avifRWStreamWriteU8(&s, gridFlags); // unsigned int(8) flags; - avifRWStreamWriteU8(&s, (uint8_t)(gridRows - 1)); // unsigned int(8) rows_minus_one; - avifRWStreamWriteU8(&s, (uint8_t)(gridCols - 1)); // unsigned int(8) columns_minus_one; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, 0)); // unsigned int(8) version = 0; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, gridFlags)); // unsigned int(8) flags; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, (uint8_t)(gridRows - 1))); // unsigned int(8) rows_minus_one; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, (uint8_t)(gridCols - 1))); // unsigned int(8) columns_minus_one; if (gridFlags & 1) { - avifRWStreamWriteU32(&s, gridWidth); // unsigned int(FieldLength) output_width; - avifRWStreamWriteU32(&s, gridHeight); // unsigned int(FieldLength) output_height; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, gridWidth)); // unsigned int(FieldLength) output_width; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, gridHeight)); // unsigned int(FieldLength) output_height; } else { uint16_t tmpWidth = (uint16_t)gridWidth; uint16_t tmpHeight = (uint16_t)gridHeight; - avifRWStreamWriteU16(&s, tmpWidth); // unsigned int(FieldLength) output_width; - avifRWStreamWriteU16(&s, tmpHeight); // unsigned int(FieldLength) output_height; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, tmpWidth)); // unsigned int(FieldLength) output_width; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, tmpHeight)); // unsigned int(FieldLength) output_height; } avifRWStreamFinishWrite(&s); + return AVIF_RESULT_OK; } static avifResult avifEncoderDataCreateExifItem(avifEncoderData * data, const avifRWData * exif) @@ -931,7 +949,7 @@ static avifResult avifEncoderAddImageItems(avifEncoder * encoder, if (cellCount > 1) { avifEncoderItem * gridItem = avifEncoderDataCreateItem(encoder->data, "grid", infeName, infeNameSize, 0); - avifWriteGridPayload(&gridItem->metadataPayload, gridCols, gridRows, gridWidth, gridHeight); + AVIF_CHECKRES(avifWriteGridPayload(&gridItem->metadataPayload, gridCols, gridRows, gridWidth, gridHeight)); gridItem->itemCategory = itemCategory; gridItem->gridCols = gridCols; gridItem->gridRows = gridRows; @@ -1357,6 +1375,153 @@ static size_t avifEncoderFindExistingChunk(avifRWStream * s, size_t mdatStartOff return 0; } +static avifResult avifEncoderWriteMediaDataBox(avifEncoder * encoder, + avifRWStream * s, + avifEncoderItemReferenceArray * layeredColorItems, + avifEncoderItemReferenceArray * layeredAlphaItems) +{ + encoder->ioStats.colorOBUSize = 0; + encoder->ioStats.alphaOBUSize = 0; + + avifBoxMarker mdat; + AVIF_CHECKRES(avifRWStreamWriteBox(s, "mdat", AVIF_BOX_SIZE_TBD, &mdat)); + const size_t mdatStartOffset = avifRWStreamOffset(s); + for (uint32_t itemPasses = 0; itemPasses < 3; ++itemPasses) { + // Use multiple passes to pack in the following order: + // * Pass 0: metadata (Exif/XMP) + // * Pass 1: alpha (AV1) + // * Pass 2: all other item data (AV1 color) + // + // See here for the discussion on alpha coming before color: + // https://github.com/AOMediaCodec/libavif/issues/287 + // + // Exif and XMP are packed first as they're required to be fully available + // by avifDecoderParse() before it returns AVIF_RESULT_OK, unless ignoreXMP + // and ignoreExif are enabled. + // + const avifBool metadataPass = (itemPasses == 0); + const avifBool alphaPass = (itemPasses == 1); + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + const avifBool isGrid = (item->gridCols > 0); // Grids store their payload in metadataPayload, so use this to distinguish grid payloads from XMP/Exif + if ((item->metadataPayload.size == 0) && (item->encodeOutput->samples.count == 0)) { + // this item has nothing for the mdat box + continue; + } + if (!isGrid && (metadataPass != (item->metadataPayload.size > 0))) { + // only process metadata (XMP/Exif) payloads when metadataPass is true + continue; + } + if (alphaPass != (item->itemCategory == AVIF_ITEM_ALPHA)) { + // only process alpha payloads when alphaPass is true + continue; + } + + if ((encoder->extraLayerCount > 0) && (item->encodeOutput->samples.count > 0)) { + // Interleave - Pick out AV1 items and interleave them later. + // We always interleave all AV1 items for layered images. + assert(item->encodeOutput->samples.count == item->mdatFixups.count); + + avifEncoderItemReference * ref = (item->itemCategory == AVIF_ITEM_ALPHA) ? avifArrayPushPtr(layeredAlphaItems) + : avifArrayPushPtr(layeredColorItems); + *ref = item; + continue; + } + + size_t chunkOffset = 0; + + // Deduplication - See if an identical chunk to this has already been written + if (item->encodeOutput->samples.count > 0) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[0]; + chunkOffset = avifEncoderFindExistingChunk(s, mdatStartOffset, sample->data.data, sample->data.size); + } else { + chunkOffset = avifEncoderFindExistingChunk(s, mdatStartOffset, item->metadataPayload.data, item->metadataPayload.size); + } + + if (!chunkOffset) { + // We've never seen this chunk before; write it out + chunkOffset = avifRWStreamOffset(s); + if (item->encodeOutput->samples.count > 0) { + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; + AVIF_CHECKRES(avifRWStreamWrite(s, sample->data.data, sample->data.size)); + + if (item->itemCategory == AVIF_ITEM_ALPHA) { + encoder->ioStats.alphaOBUSize += sample->data.size; + } else { + encoder->ioStats.colorOBUSize += sample->data.size; + } + } + } else { + AVIF_CHECKRES(avifRWStreamWrite(s, item->metadataPayload.data, item->metadataPayload.size)); + } + } + + for (uint32_t fixupIndex = 0; fixupIndex < item->mdatFixups.count; ++fixupIndex) { + avifOffsetFixup * fixup = &item->mdatFixups.fixup[fixupIndex]; + size_t prevOffset = avifRWStreamOffset(s); + avifRWStreamSetOffset(s, fixup->offset); + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)chunkOffset)); + avifRWStreamSetOffset(s, prevOffset); + } + } + } + + uint32_t layeredItemCount = AVIF_MAX(layeredColorItems->count, layeredAlphaItems->count); + if (layeredItemCount > 0) { + // Interleave samples of all AV1 items. + // We first write the first layer of all items, + // in which we write first layer of each cell, + // in which we write alpha first and then color. + avifBool hasMoreSample; + uint32_t layerIndex = 0; + do { + hasMoreSample = AVIF_FALSE; + for (uint32_t itemIndex = 0; itemIndex < layeredItemCount; ++itemIndex) { + for (int samplePass = 0; samplePass < 2; ++samplePass) { + // Alpha coming before color + avifEncoderItemReferenceArray * currentItems = (samplePass == 0) ? layeredAlphaItems : layeredColorItems; + if (itemIndex >= currentItems->count) { + continue; + } + + // TODO: Offer the ability for a user to specify which grid cell should be written first. + avifEncoderItem * item = currentItems->ref[itemIndex]; + if (item->encodeOutput->samples.count <= layerIndex) { + // We've already written all samples of this item + continue; + } else if (item->encodeOutput->samples.count > layerIndex + 1) { + hasMoreSample = AVIF_TRUE; + } + avifRWData * data = &item->encodeOutput->samples.sample[layerIndex].data; + size_t chunkOffset = avifEncoderFindExistingChunk(s, mdatStartOffset, data->data, data->size); + if (!chunkOffset) { + // We've never seen this chunk before; write it out + chunkOffset = avifRWStreamOffset(s); + AVIF_CHECKRES(avifRWStreamWrite(s, data->data, data->size)); + if (samplePass == 0) { + encoder->ioStats.alphaOBUSize += data->size; + } else { + encoder->ioStats.colorOBUSize += data->size; + } + } + + size_t prevOffset = avifRWStreamOffset(s); + avifRWStreamSetOffset(s, item->mdatFixups.fixup[layerIndex].offset); + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)chunkOffset)); + avifRWStreamSetOffset(s, prevOffset); + } + } + ++layerIndex; + } while (hasMoreSample); + + assert(layerIndex <= AVIF_MAX_AV1_LAYER_COUNT); + } + avifRWStreamFinishBox(s, mdat); + return AVIF_RESULT_OK; +} + avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) { avifDiagnosticsClearError(&encoder->diag); @@ -1439,22 +1604,23 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) } #endif - avifBoxMarker ftyp = avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteChars(&s, majorBrand, 4); // unsigned int(32) major_brand; - avifRWStreamWriteU32(&s, minorVersion); // unsigned int(32) minor_version; - avifRWStreamWriteChars(&s, "avif", 4); // unsigned int(32) compatible_brands[]; + avifBoxMarker ftyp; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD, &ftyp)); + AVIF_CHECKRES(avifRWStreamWriteChars(&s, majorBrand, 4)); // unsigned int(32) major_brand; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, minorVersion)); // unsigned int(32) minor_version; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "avif", 4)); // unsigned int(32) compatible_brands[]; if (isSequence) { // - avifRWStreamWriteChars(&s, "avis", 4); // ... compatible_brands[] - avifRWStreamWriteChars(&s, "msf1", 4); // ... compatible_brands[] - avifRWStreamWriteChars(&s, "iso8", 4); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "avis", 4)); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "msf1", 4)); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "iso8", 4)); // ... compatible_brands[] } // - avifRWStreamWriteChars(&s, "mif1", 4); // ... compatible_brands[] - avifRWStreamWriteChars(&s, "miaf", 4); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "mif1", 4)); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "miaf", 4)); // ... compatible_brands[] if ((imageMetadata->depth == 8) || (imageMetadata->depth == 10)) { // if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { // - avifRWStreamWriteChars(&s, "MA1B", 4); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "MA1B", 4)); // ... compatible_brands[] } else if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { // - avifRWStreamWriteChars(&s, "MA1A", 4); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "MA1A", 4)); // ... compatible_brands[] } } avifRWStreamFinishBox(&s, ftyp); @@ -1462,49 +1628,52 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // ----------------------------------------------------------------------- // Start meta - avifBoxMarker meta = avifRWStreamWriteFullBox(&s, "meta", AVIF_BOX_SIZE_TBD, 0, 0); + avifBoxMarker meta; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "meta", AVIF_BOX_SIZE_TBD, 0, 0, &meta)); // ----------------------------------------------------------------------- // Write hdlr - avifBoxMarker hdlr = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0; - avifRWStreamWriteChars(&s, "pict", 4); // unsigned int(32) handler_type; - avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0; - avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator) + avifBoxMarker hdlr; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0, &hdlr)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // unsigned int(32) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "pict", 4)); // unsigned int(32) handler_type; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 12)); // const unsigned int(32)[3] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "libavif", 8)); // string name; (writing null terminator) avifRWStreamFinishBox(&s, hdlr); // ----------------------------------------------------------------------- // Write pitm if (encoder->data->primaryItemID != 0) { - avifRWStreamWriteFullBox(&s, "pitm", sizeof(uint16_t), 0, 0); - avifRWStreamWriteU16(&s, encoder->data->primaryItemID); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "pitm", sizeof(uint16_t), 0, 0, /*marker=*/NULL)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, encoder->data->primaryItemID)); // unsigned int(16) item_ID; } // ----------------------------------------------------------------------- // Write iloc - avifBoxMarker iloc = avifRWStreamWriteFullBox(&s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteBits(&s, 4, /*bitCount=*/4); // unsigned int(4) offset_size; - avifRWStreamWriteBits(&s, 4, /*bitCount=*/4); // unsigned int(4) length_size; - avifRWStreamWriteBits(&s, 0, /*bitCount=*/4); // unsigned int(4) base_offset_size; - avifRWStreamWriteBits(&s, 0, /*bitCount=*/4); // unsigned int(4) reserved; + avifBoxMarker iloc; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0, &iloc)); + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 4, /*bitCount=*/4)); // unsigned int(4) offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 4, /*bitCount=*/4)); // unsigned int(4) length_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/4)); // unsigned int(4) base_offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/4)); // unsigned int(4) reserved; avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) item_count; for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { avifEncoderItem * item = &encoder->data->items.item[itemIndex]; - avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID; - avifRWStreamWriteU16(&s, 0); // unsigned int(16) data_reference_index; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) data_reference_index; // Layered Image, write location for all samples if (item->extraLayerCount > 0) { uint32_t layerCount = item->extraLayerCount + 1; - avifRWStreamWriteU16(&s, (uint16_t)layerCount); // unsigned int(16) extent_count; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)layerCount)); // unsigned int(16) extent_count; for (uint32_t i = 0; i < layerCount; ++i) { avifEncoderItemAddMdatFixup(item, &s); - avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset; - avifRWStreamWriteU32(&s, (uint32_t)item->encodeOutput->samples.sample[i].data.size); // unsigned int(length_size*8) extent_length; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0 /* set later */)); // unsigned int(offset_size*8) extent_offset; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)item->encodeOutput->samples.sample[i].data.size)); // unsigned int(length_size*8) extent_length; } continue; } @@ -1522,10 +1691,10 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) contentSize = (uint32_t)item->encodeOutput->samples.sample[0].data.size; } - avifRWStreamWriteU16(&s, 1); // unsigned int(16) extent_count; - avifEncoderItemAddMdatFixup(item, &s); // - avifRWStreamWriteU32(&s, 0 /* set later */); // unsigned int(offset_size*8) extent_offset; - avifRWStreamWriteU32(&s, (uint32_t)contentSize); // unsigned int(length_size*8) extent_length; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // unsigned int(16) extent_count; + avifEncoderItemAddMdatFixup(item, &s); // + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0 /* set later */)); // unsigned int(offset_size*8) extent_offset; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)contentSize)); // unsigned int(length_size*8) extent_length; } avifRWStreamFinishBox(&s, iloc); @@ -1533,20 +1702,22 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // ----------------------------------------------------------------------- // Write iinf - avifBoxMarker iinf = avifRWStreamWriteFullBox(&s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count); // unsigned int(16) entry_count; + avifBoxMarker iinf; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0, &iinf)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count)); // unsigned int(16) entry_count; for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { avifEncoderItem * item = &encoder->data->items.item[itemIndex]; uint32_t flags = item->hiddenImage ? 1 : 0; - avifBoxMarker infe = avifRWStreamWriteFullBox(&s, "infe", AVIF_BOX_SIZE_TBD, 2, flags); - avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID; - avifRWStreamWriteU16(&s, 0); // unsigned int(16) item_protection_index; - avifRWStreamWrite(&s, item->type, 4); // unsigned int(32) item_type; - avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize); // string item_name; (writing null terminator) - if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) - avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize); + avifBoxMarker infe; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "infe", AVIF_BOX_SIZE_TBD, 2, flags, &infe)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) item_protection_index; + AVIF_CHECKRES(avifRWStreamWrite(&s, item->type, 4)); // unsigned int(32) item_type; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize)); // string item_name; (writing null terminator) + if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) + AVIF_CHECKRES(avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize)); } avifRWStreamFinishBox(&s, infe); } @@ -1571,15 +1742,16 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) if (dimgCount > 0) { if (!iref) { - iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0); + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0, &iref)); } - avifBoxMarker refType = avifRWStreamWriteBox(&s, "dimg", AVIF_BOX_SIZE_TBD); - avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID; - avifRWStreamWriteU16(&s, dimgCount); // unsigned int(16) reference_count; + avifBoxMarker refType; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "dimg", AVIF_BOX_SIZE_TBD, &refType)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) from_item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, dimgCount)); // unsigned int(16) reference_count; for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) { avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex]; if (dimgItem->dimgFromID == item->id) { - avifRWStreamWriteU16(&s, dimgItem->id); // unsigned int(16) to_item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, dimgItem->id)); // unsigned int(16) to_item_ID; } } avifRWStreamFinishBox(&s, refType); @@ -1587,12 +1759,13 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) if (item->irefToID != 0) { if (!iref) { - iref = avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0); + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0, &iref)); } - avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD); - avifRWStreamWriteU16(&s, item->id); // unsigned int(16) from_item_ID; - avifRWStreamWriteU16(&s, 1); // unsigned int(16) reference_count; - avifRWStreamWriteU16(&s, item->irefToID); // unsigned int(16) to_item_ID; + avifBoxMarker refType; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD, &refType)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) from_item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // unsigned int(16) reference_count; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->irefToID)); // unsigned int(16) to_item_ID; avifRWStreamFinishBox(&s, refType); } } @@ -1603,10 +1776,12 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // ----------------------------------------------------------------------- // Write iprp -> ipco/ipma - avifBoxMarker iprp = avifRWStreamWriteBox(&s, "iprp", AVIF_BOX_SIZE_TBD); + avifBoxMarker iprp; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "iprp", AVIF_BOX_SIZE_TBD, &iprp)); avifItemPropertyDedup * dedup = avifItemPropertyDedupCreate(); - avifBoxMarker ipco = avifRWStreamWriteBox(&s, "ipco", AVIF_BOX_SIZE_TBD); + avifBoxMarker ipco; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "ipco", AVIF_BOX_SIZE_TBD, &ipco)); for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { avifEncoderItem * item = &encoder->data->items.item[itemIndex]; const avifBool isGrid = (item->gridCols > 0); @@ -1647,48 +1822,52 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // Properties all image items need avifItemPropertyDedupStart(dedup); - avifBoxMarker ispe = avifRWStreamWriteFullBox(&dedup->s, "ispe", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&dedup->s, imageWidth); // unsigned int(32) image_width; - avifRWStreamWriteU32(&dedup->s, imageHeight); // unsigned int(32) image_height; + avifBoxMarker ispe; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "ispe", AVIF_BOX_SIZE_TBD, 0, 0, &ispe)); + AVIF_CHECKRES(avifRWStreamWriteU32(&dedup->s, imageWidth)); // unsigned int(32) image_width; + AVIF_CHECKRES(avifRWStreamWriteU32(&dedup->s, imageHeight)); // unsigned int(32) image_height; avifRWStreamFinishBox(&dedup->s, ispe); - ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, &s, &item->ipma, AVIF_FALSE)); avifItemPropertyDedupStart(dedup); uint8_t channelCount = ((item->itemCategory == AVIF_ITEM_ALPHA) || (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) ? 1 : 3; - avifBoxMarker pixi = avifRWStreamWriteFullBox(&dedup->s, "pixi", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU8(&dedup->s, channelCount); // unsigned int (8) num_channels; + avifBoxMarker pixi; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "pixi", AVIF_BOX_SIZE_TBD, 0, 0, &pixi)); + AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, channelCount)); // unsigned int (8) num_channels; for (uint8_t chan = 0; chan < channelCount; ++chan) { - avifRWStreamWriteU8(&dedup->s, (uint8_t)imageMetadata->depth); // unsigned int (8) bits_per_channel; + AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, (uint8_t)imageMetadata->depth)); // unsigned int (8) bits_per_channel; } avifRWStreamFinishBox(&dedup->s, pixi); - ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, &s, &item->ipma, AVIF_FALSE)); if (item->codec) { avifItemPropertyDedupStart(dedup); - writeConfigBox(&dedup->s, &item->av1C, encoder->data->configPropName); - ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_TRUE); + AVIF_CHECKRES(writeConfigBox(&dedup->s, &item->av1C, encoder->data->configPropName)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, &s, &item->ipma, AVIF_TRUE)); } if (item->itemCategory == AVIF_ITEM_ALPHA) { // Alpha specific properties avifItemPropertyDedupStart(dedup); - avifBoxMarker auxC = avifRWStreamWriteFullBox(&dedup->s, "auxC", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteChars(&dedup->s, alphaURN, alphaURNSize); // string aux_type; + avifBoxMarker auxC; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "auxC", AVIF_BOX_SIZE_TBD, 0, 0, &auxC)); + AVIF_CHECKRES(avifRWStreamWriteChars(&dedup->s, alphaURN, alphaURNSize)); // string aux_type; avifRWStreamFinishBox(&dedup->s, auxC); - ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, &s, &item->ipma, AVIF_FALSE)); } else { // Color specific properties - avifEncoderWriteColorProperties(&s, imageMetadata, &item->ipma, dedup); + AVIF_CHECKRES(avifEncoderWriteColorProperties(&s, imageMetadata, &item->ipma, dedup)); } if (item->extraLayerCount > 0) { // Layered Image Indexing Property avifItemPropertyDedupStart(dedup); - avifBoxMarker a1lx = avifRWStreamWriteBox(&dedup->s, "a1lx", AVIF_BOX_SIZE_TBD); + avifBoxMarker a1lx; + AVIF_CHECKRES(avifRWStreamWriteBox(&dedup->s, "a1lx", AVIF_BOX_SIZE_TBD, &a1lx)); uint32_t layerSize[AVIF_MAX_AV1_LAYER_COUNT - 1] = { 0 }; avifBool largeSize = AVIF_FALSE; @@ -1700,27 +1879,28 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) } } - avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/7); // unsigned int(7) reserved = 0; - avifRWStreamWriteBits(&dedup->s, largeSize ? 1 : 0, /*bitCount=*/1); // unsigned int(1) large_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/7)); // unsigned int(7) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, largeSize ? 1 : 0, /*bitCount=*/1)); // unsigned int(1) large_size; // FieldLength = (large_size + 1) * 16; // unsigned int(FieldLength) layer_size[3]; for (uint32_t layer = 0; layer < AVIF_MAX_AV1_LAYER_COUNT - 1; ++layer) { if (largeSize) { - avifRWStreamWriteU32(&dedup->s, layerSize[layer]); + AVIF_CHECKRES(avifRWStreamWriteU32(&dedup->s, layerSize[layer])); } else { - avifRWStreamWriteU16(&dedup->s, (uint16_t)layerSize[layer]); + AVIF_CHECKRES(avifRWStreamWriteU16(&dedup->s, (uint16_t)layerSize[layer])); } } avifRWStreamFinishBox(&dedup->s, a1lx); - ipmaPush(&item->ipma, avifItemPropertyDedupFinish(dedup, &s), AVIF_FALSE); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, &s, &item->ipma, AVIF_FALSE)); } } avifRWStreamFinishBox(&s, ipco); avifItemPropertyDedupDestroy(dedup); dedup = NULL; - avifBoxMarker ipma = avifRWStreamWriteFullBox(&s, "ipma", AVIF_BOX_SIZE_TBD, 0, 0); + avifBoxMarker ipma; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "ipma", AVIF_BOX_SIZE_TBD, 0, 0, &ipma)); { int ipmaCount = 0; for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { @@ -1729,7 +1909,7 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) ++ipmaCount; } } - avifRWStreamWriteU32(&s, ipmaCount); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, ipmaCount)); // unsigned int(32) entry_count; for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { avifEncoderItem * item = &encoder->data->items.item[itemIndex]; @@ -1737,11 +1917,11 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) continue; } - avifRWStreamWriteU16(&s, item->id); // unsigned int(16) item_ID; - avifRWStreamWriteU8(&s, item->ipma.count); // unsigned int(8) association_count; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, item->ipma.count)); // unsigned int(8) association_count; for (int i = 0; i < item->ipma.count; ++i) { - avifRWStreamWriteBits(&s, item->ipma.essential[i] ? 1 : 0, /*bitCount=*/1); // bit(1) essential; - avifRWStreamWriteBits(&s, item->ipma.associations[i], /*bitCount=*/7); // unsigned int(7) property_index; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, item->ipma.essential[i] ? 1 : 0, /*bitCount=*/1)); // bit(1) essential; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, item->ipma.associations[i], /*bitCount=*/7)); // unsigned int(7) property_index; } } } @@ -1797,20 +1977,22 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // ------------------------------------------------------------------- // Start moov - avifBoxMarker moov = avifRWStreamWriteBox(&s, "moov", AVIF_BOX_SIZE_TBD); - - avifBoxMarker mvhd = avifRWStreamWriteFullBox(&s, "mvhd", AVIF_BOX_SIZE_TBD, 1, 0); - avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time; - avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time; - avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale; - avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration; - avifRWStreamWriteU32(&s, 0x00010000); // template int(32) rate = 0x00010000; // typically 1.0 - avifRWStreamWriteU16(&s, 0x0100); // template int(16) volume = 0x0100; // typically, full volume - avifRWStreamWriteU16(&s, 0); // const bit(16) reserved = 0; - avifRWStreamWriteZeros(&s, 8); // const unsigned int(32)[2] reserved = 0; - avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix)); - avifRWStreamWriteZeros(&s, 24); // bit(32)[6] pre_defined = 0; - avifRWStreamWriteU32(&s, encoder->data->items.count); // unsigned int(32) next_track_ID; + avifBoxMarker moov; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "moov", AVIF_BOX_SIZE_TBD, &moov)); + + avifBoxMarker mvhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "mvhd", AVIF_BOX_SIZE_TBD, 1, 0, &mvhd)); + AVIF_CHECKRES(avifRWStreamWriteU64(&s, now)); // unsigned int(64) creation_time; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, now)); // unsigned int(64) modification_time; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale)); // unsigned int(32) timescale; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, durationInTimescales)); // unsigned int(64) duration; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0x00010000)); // template int(32) rate = 0x00010000; // typically 1.0 + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0x0100)); // template int(16) volume = 0x0100; // typically, full volume + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // const bit(16) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 8)); // const unsigned int(32)[2] reserved = 0; + AVIF_CHECKRES(avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix))); + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 24)); // bit(32)[6] pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, encoder->data->items.count)); // unsigned int(32) next_track_ID; avifRWStreamFinishBox(&s, mvhd); // ------------------------------------------------------------------- @@ -1830,76 +2012,89 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) } } - avifBoxMarker trak = avifRWStreamWriteBox(&s, "trak", AVIF_BOX_SIZE_TBD); - - avifBoxMarker tkhd = avifRWStreamWriteFullBox(&s, "tkhd", AVIF_BOX_SIZE_TBD, 1, 1); - avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time; - avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time; - avifRWStreamWriteU32(&s, itemIndex + 1); // unsigned int(32) track_ID; - avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0; - avifRWStreamWriteU64(&s, durationInTimescales); // unsigned int(64) duration; - avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 2); // const unsigned int(32)[2] reserved = 0; - avifRWStreamWriteU16(&s, 0); // template int(16) layer = 0; - avifRWStreamWriteU16(&s, 0); // template int(16) alternate_group = 0; - avifRWStreamWriteU16(&s, 0); // template int(16) volume = {if track_is_audio 0x0100 else 0}; - avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0; - avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix)); // template int(32)[9] matrix= // { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; - avifRWStreamWriteU32(&s, imageMetadata->width << 16); // unsigned int(32) width; - avifRWStreamWriteU32(&s, imageMetadata->height << 16); // unsigned int(32) height; + avifBoxMarker trak; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "trak", AVIF_BOX_SIZE_TBD, &trak)); + + avifBoxMarker tkhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "tkhd", AVIF_BOX_SIZE_TBD, 1, 1, &tkhd)); + AVIF_CHECKRES(avifRWStreamWriteU64(&s, now)); // unsigned int(64) creation_time; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, now)); // unsigned int(64) modification_time; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, itemIndex + 1)); // unsigned int(32) track_ID; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // const unsigned int(32) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, durationInTimescales)); // unsigned int(64) duration; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 2)); // const unsigned int(32)[2] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template int(16) layer = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template int(16) alternate_group = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template int(16) volume = {if track_is_audio 0x0100 else 0}; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // const unsigned int(16) reserved = 0; + AVIF_CHECKRES(avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix))); // template int(32)[9] matrix= // { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, imageMetadata->width << 16)); // unsigned int(32) width; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, imageMetadata->height << 16)); // unsigned int(32) height; avifRWStreamFinishBox(&s, tkhd); if (item->irefToID != 0) { - avifBoxMarker tref = avifRWStreamWriteBox(&s, "tref", AVIF_BOX_SIZE_TBD); - avifBoxMarker refType = avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD); - avifRWStreamWriteU32(&s, (uint32_t)item->irefToID); + avifBoxMarker tref; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "tref", AVIF_BOX_SIZE_TBD, &tref)); + avifBoxMarker refType; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD, &refType)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)item->irefToID)); avifRWStreamFinishBox(&s, refType); avifRWStreamFinishBox(&s, tref); } - avifBoxMarker edts = avifRWStreamWriteBox(&s, "edts", AVIF_BOX_SIZE_TBD); + avifBoxMarker edts; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "edts", AVIF_BOX_SIZE_TBD, &edts)); uint32_t elstFlags = (encoder->repetitionCount != 0); - avifBoxMarker elst = avifRWStreamWriteFullBox(&s, "elst", AVIF_BOX_SIZE_TBD, 1, elstFlags); - avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; - avifRWStreamWriteU64(&s, framesDurationInTimescales); // unsigned int(64) segment_duration; - avifRWStreamWriteU64(&s, 0); // int(64) media_time; - avifRWStreamWriteU16(&s, 1); // int(16) media_rate_integer; - avifRWStreamWriteU16(&s, 0); // int(16) media_rate_fraction = 0; + avifBoxMarker elst; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "elst", AVIF_BOX_SIZE_TBD, 1, elstFlags, &elst)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, framesDurationInTimescales)); // unsigned int(64) segment_duration; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, 0)); // int(64) media_time; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // int(16) media_rate_integer; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // int(16) media_rate_fraction = 0; avifRWStreamFinishBox(&s, elst); avifRWStreamFinishBox(&s, edts); if (item->itemCategory != AVIF_ITEM_ALPHA) { - avifEncoderWriteTrackMetaBox(encoder, &s); + AVIF_CHECKRES(avifEncoderWriteTrackMetaBox(encoder, &s)); } - avifBoxMarker mdia = avifRWStreamWriteBox(&s, "mdia", AVIF_BOX_SIZE_TBD); - - avifBoxMarker mdhd = avifRWStreamWriteFullBox(&s, "mdhd", AVIF_BOX_SIZE_TBD, 1, 0); - avifRWStreamWriteU64(&s, now); // unsigned int(64) creation_time; - avifRWStreamWriteU64(&s, now); // unsigned int(64) modification_time; - avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale); // unsigned int(32) timescale; - avifRWStreamWriteU64(&s, framesDurationInTimescales); // unsigned int(64) duration; - avifRWStreamWriteU16(&s, 21956); // bit(1) pad = 0; unsigned int(5)[3] language; ("und") - avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0; + avifBoxMarker mdia; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "mdia", AVIF_BOX_SIZE_TBD, &mdia)); + + avifBoxMarker mdhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "mdhd", AVIF_BOX_SIZE_TBD, 1, 0, &mdhd)); + AVIF_CHECKRES(avifRWStreamWriteU64(&s, now)); // unsigned int(64) creation_time; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, now)); // unsigned int(64) modification_time; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale)); // unsigned int(32) timescale; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, framesDurationInTimescales)); // unsigned int(64) duration; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 21956)); // bit(1) pad = 0; unsigned int(5)[3] language; ("und") + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) pre_defined = 0; avifRWStreamFinishBox(&s, mdhd); - avifBoxMarker hdlrTrak = avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 0); // unsigned int(32) pre_defined = 0; - avifRWStreamWriteChars(&s, (item->itemCategory == AVIF_ITEM_ALPHA) ? "auxv" : "pict", 4); // unsigned int(32) handler_type; - avifRWStreamWriteZeros(&s, 12); // const unsigned int(32)[3] reserved = 0; - avifRWStreamWriteChars(&s, "libavif", 8); // string name; (writing null terminator) + avifBoxMarker hdlrTrak; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0, &hdlrTrak)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // unsigned int(32) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, (item->itemCategory == AVIF_ITEM_ALPHA) ? "auxv" : "pict", 4)); // unsigned int(32) handler_type; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 12)); // const unsigned int(32)[3] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "libavif", 8)); // string name; (writing null terminator) avifRWStreamFinishBox(&s, hdlrTrak); - avifBoxMarker minf = avifRWStreamWriteBox(&s, "minf", AVIF_BOX_SIZE_TBD); + avifBoxMarker minf; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "minf", AVIF_BOX_SIZE_TBD, &minf)); - avifBoxMarker vmhd = avifRWStreamWriteFullBox(&s, "vmhd", AVIF_BOX_SIZE_TBD, 0, 1); - avifRWStreamWriteU16(&s, 0); // template unsigned int(16) graphicsmode = 0; (copy over the existing image) - avifRWStreamWriteZeros(&s, 6); // template unsigned int(16)[3] opcolor = {0, 0, 0}; + avifBoxMarker vmhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "vmhd", AVIF_BOX_SIZE_TBD, 0, 1, &vmhd)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template unsigned int(16) graphicsmode = 0; (copy over the existing image) + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 6)); // template unsigned int(16)[3] opcolor = {0, 0, 0}; avifRWStreamFinishBox(&s, vmhd); - avifBoxMarker dinf = avifRWStreamWriteBox(&s, "dinf", AVIF_BOX_SIZE_TBD); - avifBoxMarker dref = avifRWStreamWriteFullBox(&s, "dref", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; - avifRWStreamWriteFullBox(&s, "url ", 0, 0, 1); // flags:1 means data is in this file + avifBoxMarker dinf; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "dinf", AVIF_BOX_SIZE_TBD, &dinf)); + avifBoxMarker dref; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "dref", AVIF_BOX_SIZE_TBD, 0, 0, &dref)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "url ", /*contentSize=*/0, 0, 1, /*marker=*/NULL)); // flags:1 means data is in this file avifRWStreamFinishBox(&s, dref); avifRWStreamFinishBox(&s, dinf); @@ -1908,51 +2103,57 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // (stsd), Time to Sample (stts), Sample to Chunk (stsc), Sample Size (stsz), Chunk Offset (stco). // // Any boxes not listed in the above line are placed in the end (after the "stco" box). - avifBoxMarker stbl = avifRWStreamWriteBox(&s, "stbl", AVIF_BOX_SIZE_TBD); - - avifBoxMarker stsd = avifRWStreamWriteFullBox(&s, "stsd", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; - avifBoxMarker imageItem = avifRWStreamWriteBox(&s, encoder->data->imageItemType, AVIF_BOX_SIZE_TBD); - avifRWStreamWriteZeros(&s, 6); // const unsigned int(8)[6] reserved = 0; - avifRWStreamWriteU16(&s, 1); // unsigned int(16) data_reference_index; - avifRWStreamWriteU16(&s, 0); // unsigned int(16) pre_defined = 0; - avifRWStreamWriteU16(&s, 0); // const unsigned int(16) reserved = 0; - avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 3); // unsigned int(32)[3] pre_defined = 0; - avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->width); // unsigned int(16) width; - avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->height); // unsigned int(16) height; - avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) horizresolution - avifRWStreamWriteU32(&s, 0x00480000); // template unsigned int(32) vertresolution - avifRWStreamWriteU32(&s, 0); // const unsigned int(32) reserved = 0; - avifRWStreamWriteU16(&s, 1); // template unsigned int(16) frame_count = 1; - avifRWStreamWriteChars(&s, "\012AOM Coding", 11); // string[32] compressorname; - avifRWStreamWriteZeros(&s, 32 - 11); // - avifRWStreamWriteU16(&s, 0x0018); // template unsigned int(16) depth = 0x0018; - avifRWStreamWriteU16(&s, (uint16_t)0xffff); // int(16) pre_defined = -1; - writeConfigBox(&s, &item->av1C, encoder->data->configPropName); + avifBoxMarker stbl; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "stbl", AVIF_BOX_SIZE_TBD, &stbl)); + + avifBoxMarker stsd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stsd", AVIF_BOX_SIZE_TBD, 0, 0, &stsd)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + avifBoxMarker imageItem; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, encoder->data->imageItemType, AVIF_BOX_SIZE_TBD, &imageItem)); + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 6)); // const unsigned int(8)[6] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // unsigned int(16) data_reference_index; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // const unsigned int(16) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 3)); // unsigned int(32)[3] pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->width)); // unsigned int(16) width; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->height)); // unsigned int(16) height; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0x00480000)); // template unsigned int(32) horizresolution + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0x00480000)); // template unsigned int(32) vertresolution + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // const unsigned int(32) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // template unsigned int(16) frame_count = 1; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "\012AOM Coding", 11)); // string[32] compressorname; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 32 - 11)); // + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0x0018)); // template unsigned int(16) depth = 0x0018; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)0xffff)); // int(16) pre_defined = -1; + AVIF_CHECKRES(writeConfigBox(&s, &item->av1C, encoder->data->configPropName)); if (item->itemCategory == AVIF_ITEM_COLOR) { - avifEncoderWriteColorProperties(&s, imageMetadata, NULL, NULL); + AVIF_CHECKRES(avifEncoderWriteColorProperties(&s, imageMetadata, NULL, NULL)); } - avifBoxMarker ccst = avifRWStreamWriteFullBox(&s, "ccst", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteBits(&s, 0, /*bitCount=*/1); // unsigned int(1) all_ref_pics_intra; - avifRWStreamWriteBits(&s, 1, /*bitCount=*/1); // unsigned int(1) intra_pred_used; - avifRWStreamWriteBits(&s, 15, /*bitCount=*/4); // unsigned int(4) max_ref_per_pic; - avifRWStreamWriteBits(&s, 0, /*bitCount=*/26); // unsigned int(26) reserved; + avifBoxMarker ccst; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "ccst", AVIF_BOX_SIZE_TBD, 0, 0, &ccst)); + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/1)); // unsigned int(1) all_ref_pics_intra; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 1, /*bitCount=*/1)); // unsigned int(1) intra_pred_used; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 15, /*bitCount=*/4)); // unsigned int(4) max_ref_per_pic; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/26)); // unsigned int(26) reserved; avifRWStreamFinishBox(&s, ccst); if (item->itemCategory == AVIF_ITEM_ALPHA) { - avifBoxMarker auxi = avifRWStreamWriteFullBox(&s, "auxi", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteChars(&s, alphaURN, alphaURNSize); // string aux_track_type; + avifBoxMarker auxi; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "auxi", AVIF_BOX_SIZE_TBD, 0, 0, &auxi)); + AVIF_CHECKRES(avifRWStreamWriteChars(&s, alphaURN, alphaURNSize)); // string aux_track_type; avifRWStreamFinishBox(&s, auxi); } avifRWStreamFinishBox(&s, imageItem); avifRWStreamFinishBox(&s, stsd); - avifBoxMarker stts = avifRWStreamWriteFullBox(&s, "stts", AVIF_BOX_SIZE_TBD, 0, 0); + avifBoxMarker stts; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stts", AVIF_BOX_SIZE_TBD, 0, 0, &stts)); size_t sttsEntryCountOffset = avifRWStreamOffset(&s); uint32_t sttsEntryCount = 0; - avifRWStreamWriteU32(&s, 0); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // unsigned int(32) entry_count; for (uint32_t sampleCount = 0, frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) { avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex]; ++sampleCount; @@ -1962,45 +2163,49 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) continue; } } - avifRWStreamWriteU32(&s, sampleCount); // unsigned int(32) sample_count; - avifRWStreamWriteU32(&s, (uint32_t)frame->durationInTimescales); // unsigned int(32) sample_delta; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, sampleCount)); // unsigned int(32) sample_count; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)frame->durationInTimescales)); // unsigned int(32) sample_delta; sampleCount = 0; ++sttsEntryCount; } size_t prevOffset = avifRWStreamOffset(&s); avifRWStreamSetOffset(&s, sttsEntryCountOffset); - avifRWStreamWriteU32(&s, sttsEntryCount); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, sttsEntryCount)); avifRWStreamSetOffset(&s, prevOffset); avifRWStreamFinishBox(&s, stts); - avifBoxMarker stsc = avifRWStreamWriteFullBox(&s, "stsc", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; - avifRWStreamWriteU32(&s, 1); // unsigned int(32) first_chunk; - avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) samples_per_chunk; - avifRWStreamWriteU32(&s, 1); // unsigned int(32) sample_description_index; + avifBoxMarker stsc; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stsc", AVIF_BOX_SIZE_TBD, 0, 0, &stsc)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) first_chunk; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, item->encodeOutput->samples.count)); // unsigned int(32) samples_per_chunk; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) sample_description_index; avifRWStreamFinishBox(&s, stsc); - avifBoxMarker stsz = avifRWStreamWriteFullBox(&s, "stsz", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 0); // unsigned int(32) sample_size; - avifRWStreamWriteU32(&s, item->encodeOutput->samples.count); // unsigned int(32) sample_count; + avifBoxMarker stsz; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stsz", AVIF_BOX_SIZE_TBD, 0, 0, &stsz)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // unsigned int(32) sample_size; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, item->encodeOutput->samples.count)); // unsigned int(32) sample_count; for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; - avifRWStreamWriteU32(&s, (uint32_t)sample->data.size); // unsigned int(32) entry_size; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)sample->data.size)); // unsigned int(32) entry_size; } avifRWStreamFinishBox(&s, stsz); - avifBoxMarker stco = avifRWStreamWriteFullBox(&s, "stco", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, 1); // unsigned int(32) entry_count; - avifEncoderItemAddMdatFixup(item, &s); // - avifRWStreamWriteU32(&s, 1); // unsigned int(32) chunk_offset; (set later) + avifBoxMarker stco; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stco", AVIF_BOX_SIZE_TBD, 0, 0, &stco)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + avifEncoderItemAddMdatFixup(item, &s); // + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) chunk_offset; (set later) avifRWStreamFinishBox(&s, stco); - avifBoxMarker stss = avifRWStreamWriteFullBox(&s, "stss", AVIF_BOX_SIZE_TBD, 0, 0); - avifRWStreamWriteU32(&s, syncSamplesCount); // unsigned int(32) entry_count; + avifBoxMarker stss; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stss", AVIF_BOX_SIZE_TBD, 0, 0, &stss)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, syncSamplesCount)); // unsigned int(32) entry_count; for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; if (sample->sync) { - avifRWStreamWriteU32(&s, sampleIndex + 1); // unsigned int(32) sample_number; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, sampleIndex + 1)); // unsigned int(32) sample_number; } } avifRWStreamFinishBox(&s, stss); @@ -2021,155 +2226,21 @@ avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) // ----------------------------------------------------------------------- // Write mdat - encoder->ioStats.colorOBUSize = 0; - encoder->ioStats.alphaOBUSize = 0; - avifEncoderItemReferenceArray layeredColorItems; avifEncoderItemReferenceArray layeredAlphaItems; - if (!avifArrayCreate(&layeredColorItems, sizeof(avifEncoderItemReference), 1) || - !avifArrayCreate(&layeredAlphaItems, sizeof(avifEncoderItemReference), 1)) { - return AVIF_RESULT_OUT_OF_MEMORY; + avifResult result = AVIF_RESULT_OK; + if (!avifArrayCreate(&layeredColorItems, sizeof(avifEncoderItemReference), 1)) { + result = AVIF_RESULT_OUT_OF_MEMORY; } - - avifBoxMarker mdat = avifRWStreamWriteBox(&s, "mdat", AVIF_BOX_SIZE_TBD); - const size_t mdatStartOffset = avifRWStreamOffset(&s); - for (uint32_t itemPasses = 0; itemPasses < 3; ++itemPasses) { - // Use multiple passes to pack in the following order: - // * Pass 0: metadata (Exif/XMP) - // * Pass 1: alpha (AV1) - // * Pass 2: all other item data (AV1 color) - // - // See here for the discussion on alpha coming before color: - // https://github.com/AOMediaCodec/libavif/issues/287 - // - // Exif and XMP are packed first as they're required to be fully available - // by avifDecoderParse() before it returns AVIF_RESULT_OK, unless ignoreXMP - // and ignoreExif are enabled. - // - const avifBool metadataPass = (itemPasses == 0); - const avifBool alphaPass = (itemPasses == 1); - - for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { - avifEncoderItem * item = &encoder->data->items.item[itemIndex]; - const avifBool isGrid = (item->gridCols > 0); // Grids store their payload in metadataPayload, so use this to distinguish grid payloads from XMP/Exif - if ((item->metadataPayload.size == 0) && (item->encodeOutput->samples.count == 0)) { - // this item has nothing for the mdat box - continue; - } - if (!isGrid && (metadataPass != (item->metadataPayload.size > 0))) { - // only process metadata (XMP/Exif) payloads when metadataPass is true - continue; - } - if (alphaPass != (item->itemCategory == AVIF_ITEM_ALPHA)) { - // only process alpha payloads when alphaPass is true - continue; - } - - if ((encoder->extraLayerCount > 0) && (item->encodeOutput->samples.count > 0)) { - // Interleave - Pick out AV1 items and interleave them later. - // We always interleave all AV1 items for layered images. - assert(item->encodeOutput->samples.count == item->mdatFixups.count); - - avifEncoderItemReference * ref = (item->itemCategory == AVIF_ITEM_ALPHA) ? avifArrayPushPtr(&layeredAlphaItems) - : avifArrayPushPtr(&layeredColorItems); - *ref = item; - continue; - } - - size_t chunkOffset = 0; - - // Deduplication - See if an identical chunk to this has already been written - if (item->encodeOutput->samples.count > 0) { - avifEncodeSample * sample = &item->encodeOutput->samples.sample[0]; - chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, sample->data.data, sample->data.size); - } else { - chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, item->metadataPayload.data, item->metadataPayload.size); - } - - if (!chunkOffset) { - // We've never seen this chunk before; write it out - chunkOffset = avifRWStreamOffset(&s); - if (item->encodeOutput->samples.count > 0) { - for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { - avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; - avifRWStreamWrite(&s, sample->data.data, sample->data.size); - - if (item->itemCategory == AVIF_ITEM_ALPHA) { - encoder->ioStats.alphaOBUSize += sample->data.size; - } else { - encoder->ioStats.colorOBUSize += sample->data.size; - } - } - } else { - avifRWStreamWrite(&s, item->metadataPayload.data, item->metadataPayload.size); - } - } - - for (uint32_t fixupIndex = 0; fixupIndex < item->mdatFixups.count; ++fixupIndex) { - avifOffsetFixup * fixup = &item->mdatFixups.fixup[fixupIndex]; - size_t prevOffset = avifRWStreamOffset(&s); - avifRWStreamSetOffset(&s, fixup->offset); - avifRWStreamWriteU32(&s, (uint32_t)chunkOffset); - avifRWStreamSetOffset(&s, prevOffset); - } - } + if (!avifArrayCreate(&layeredAlphaItems, sizeof(avifEncoderItemReference), 1)) { + result = AVIF_RESULT_OUT_OF_MEMORY; } - - uint32_t layeredItemCount = AVIF_MAX(layeredColorItems.count, layeredAlphaItems.count); - if (layeredItemCount > 0) { - // Interleave samples of all AV1 items. - // We first write the first layer of all items, - // in which we write first layer of each cell, - // in which we write alpha first and then color. - avifBool hasMoreSample; - uint32_t layerIndex = 0; - do { - hasMoreSample = AVIF_FALSE; - for (uint32_t itemIndex = 0; itemIndex < layeredItemCount; ++itemIndex) { - for (int samplePass = 0; samplePass < 2; ++samplePass) { - // Alpha coming before color - avifEncoderItemReferenceArray * currentItems = (samplePass == 0) ? &layeredAlphaItems : &layeredColorItems; - if (itemIndex >= currentItems->count) { - continue; - } - - // TODO: Offer the ability for a user to specify which grid cell should be written first. - avifEncoderItem * item = currentItems->ref[itemIndex]; - if (item->encodeOutput->samples.count <= layerIndex) { - // We've already written all samples of this item - continue; - } else if (item->encodeOutput->samples.count > layerIndex + 1) { - hasMoreSample = AVIF_TRUE; - } - avifRWData * data = &item->encodeOutput->samples.sample[layerIndex].data; - size_t chunkOffset = avifEncoderFindExistingChunk(&s, mdatStartOffset, data->data, data->size); - if (!chunkOffset) { - // We've never seen this chunk before; write it out - chunkOffset = avifRWStreamOffset(&s); - avifRWStreamWrite(&s, data->data, data->size); - if (samplePass == 0) { - encoder->ioStats.alphaOBUSize += data->size; - } else { - encoder->ioStats.colorOBUSize += data->size; - } - } - - size_t prevOffset = avifRWStreamOffset(&s); - avifRWStreamSetOffset(&s, item->mdatFixups.fixup[layerIndex].offset); - avifRWStreamWriteU32(&s, (uint32_t)chunkOffset); - avifRWStreamSetOffset(&s, prevOffset); - } - } - ++layerIndex; - } while (hasMoreSample); - - assert(layerIndex <= AVIF_MAX_AV1_LAYER_COUNT); + if (result == AVIF_RESULT_OK) { + result = avifEncoderWriteMediaDataBox(encoder, &s, &layeredColorItems, &layeredAlphaItems); } - avifArrayDestroy(&layeredColorItems); avifArrayDestroy(&layeredAlphaItems); - - avifRWStreamFinishBox(&s, mdat); + AVIF_CHECKRES(result); // ----------------------------------------------------------------------- // Finish up stream @@ -2190,35 +2261,38 @@ avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avif // Implementation of section 2.3.3 of AV1 Codec ISO Media File Format Binding specification v1.2.0. // See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax. -static void writeCodecConfig(avifRWStream * s, const avifCodecConfigurationBox * cfg) +static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfigurationBox * cfg) { - avifRWStreamWriteBits(s, 1, /*bitCount=*/1); // unsigned int (1) marker = 1; - avifRWStreamWriteBits(s, 1, /*bitCount=*/7); // unsigned int (7) version = 1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/1)); // unsigned int (1) marker = 1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/7)); // unsigned int (7) version = 1; - avifRWStreamWriteBits(s, cfg->seqProfile, /*bitCount=*/3); // unsigned int (3) seq_profile; - avifRWStreamWriteBits(s, cfg->seqLevelIdx0, /*bitCount=*/5); // unsigned int (5) seq_level_idx_0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->seqProfile, /*bitCount=*/3)); // unsigned int (3) seq_profile; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->seqLevelIdx0, /*bitCount=*/5)); // unsigned int (5) seq_level_idx_0; - avifRWStreamWriteBits(s, cfg->seqTier0, /*bitCount=*/1); // unsigned int (1) seq_tier_0; - avifRWStreamWriteBits(s, cfg->highBitdepth, /*bitCount=*/1); // unsigned int (1) high_bitdepth; - avifRWStreamWriteBits(s, cfg->twelveBit, /*bitCount=*/1); // unsigned int (1) twelve_bit; - avifRWStreamWriteBits(s, cfg->monochrome, /*bitCount=*/1); // unsigned int (1) monochrome; - avifRWStreamWriteBits(s, cfg->chromaSubsamplingX, /*bitCount=*/1); // unsigned int (1) chroma_subsampling_x; - avifRWStreamWriteBits(s, cfg->chromaSubsamplingY, /*bitCount=*/1); // unsigned int (1) chroma_subsampling_y; - avifRWStreamWriteBits(s, cfg->chromaSamplePosition, /*bitCount=*/2); // unsigned int (2) chroma_sample_position; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->seqTier0, /*bitCount=*/1)); // unsigned int (1) seq_tier_0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->highBitdepth, /*bitCount=*/1)); // unsigned int (1) high_bitdepth; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->twelveBit, /*bitCount=*/1)); // unsigned int (1) twelve_bit; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->monochrome, /*bitCount=*/1)); // unsigned int (1) monochrome; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->chromaSubsamplingX, /*bitCount=*/1)); // unsigned int (1) chroma_subsampling_x; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->chromaSubsamplingY, /*bitCount=*/1)); // unsigned int (1) chroma_subsampling_y; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->chromaSamplePosition, /*bitCount=*/2)); // unsigned int (2) chroma_sample_position; - avifRWStreamWriteBits(s, 0, /*bitCount=*/3); // unsigned int (3) reserved = 0; - avifRWStreamWriteBits(s, 0, /*bitCount=*/1); // unsigned int (1) initial_presentation_delay_present; - avifRWStreamWriteBits(s, 0, /*bitCount=*/4); // unsigned int (4) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/3)); // unsigned int (3) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/1)); // unsigned int (1) initial_presentation_delay_present; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/4)); // unsigned int (4) reserved = 0; // According to section 2.2.1 of AV1 Image File Format specification v1.1.0, // there is no need to write any OBU here. // See https://aomediacodec.github.io/av1-avif/v1.1.0.html#av1-configuration-item-property. // unsigned int (8) configOBUs[]; + return AVIF_RESULT_OK; } -static void writeConfigBox(avifRWStream * s, const avifCodecConfigurationBox * cfg, const char * configPropName) +static avifResult writeConfigBox(avifRWStream * s, const avifCodecConfigurationBox * cfg, const char * configPropName) { - avifBoxMarker configBox = avifRWStreamWriteBox(s, configPropName, AVIF_BOX_SIZE_TBD); - writeCodecConfig(s, cfg); + avifBoxMarker configBox; + AVIF_CHECKRES(avifRWStreamWriteBox(s, configPropName, AVIF_BOX_SIZE_TBD, &configBox)); + AVIF_CHECKRES(writeCodecConfig(s, cfg)); avifRWStreamFinishBox(s, configBox); + return AVIF_RESULT_OK; } diff --git a/tests/gtest/avifstreamtest.cc b/tests/gtest/avifstreamtest.cc index 1fc9eafbb5..652583139c 100644 --- a/tests/gtest/avifstreamtest.cc +++ b/tests/gtest/avifstreamtest.cc @@ -25,73 +25,90 @@ TEST(StreamTest, Roundtrip) { EXPECT_EQ(avifRWStreamOffset(&rw_stream), size_t{0}); const uint8_t rw_somedata[] = {3, 1, 4}; - avifRWStreamWrite(&rw_stream, rw_somedata, sizeof(rw_somedata)); + EXPECT_EQ(avifRWStreamWrite(&rw_stream, rw_somedata, sizeof(rw_somedata)), + AVIF_RESULT_OK); const char rw_somechars[] = "somechars"; - avifRWStreamWriteChars(&rw_stream, rw_somechars, sizeof(rw_somechars)); + EXPECT_EQ( + avifRWStreamWriteChars(&rw_stream, rw_somechars, sizeof(rw_somechars)), + AVIF_RESULT_OK); const char rw_box_type[] = "type"; - avifBoxMarker rw_box_marker = - avifRWStreamWriteBox(&rw_stream, rw_box_type, /*contentSize=*/0); + avifBoxMarker rw_box_marker; + EXPECT_EQ(avifRWStreamWriteBox(&rw_stream, rw_box_type, /*contentSize=*/0, + &rw_box_marker), + AVIF_RESULT_OK); const uint8_t rw_someu8 = 0xA; - avifRWStreamWriteU8(&rw_stream, rw_someu8); + EXPECT_EQ(avifRWStreamWriteU8(&rw_stream, rw_someu8), AVIF_RESULT_OK); const int rw_full_box_version = 7; const uint32_t rw_full_box_flags = 0x555; - avifBoxMarker rw_full_box_marker = - avifRWStreamWriteFullBox(&rw_stream, rw_box_type, /*contentSize=*/0, - rw_full_box_version, rw_full_box_flags); + avifBoxMarker rw_full_box_marker; + EXPECT_EQ(avifRWStreamWriteFullBox(&rw_stream, rw_box_type, /*contentSize=*/0, + rw_full_box_version, rw_full_box_flags, + &rw_full_box_marker), + AVIF_RESULT_OK); const uint16_t rw_someu16 = 0xAB; - avifRWStreamWriteU16(&rw_stream, rw_someu16); + EXPECT_EQ(avifRWStreamWriteU16(&rw_stream, rw_someu16), AVIF_RESULT_OK); avifRWStreamFinishBox(&rw_stream, rw_full_box_marker); avifRWStreamFinishBox(&rw_stream, rw_box_marker); const uint32_t rw_someu32 = 0xABCD; - avifRWStreamWriteU32(&rw_stream, rw_someu32); + EXPECT_EQ(avifRWStreamWriteU32(&rw_stream, rw_someu32), AVIF_RESULT_OK); size_t offset = avifRWStreamOffset(&rw_stream); const uint32_t rw_somevarint_1byte = 240; - avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_1byte); + EXPECT_EQ(avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_1byte), + AVIF_RESULT_OK); EXPECT_EQ(avifRWStreamOffset(&rw_stream), offset + 1); offset = avifRWStreamOffset(&rw_stream); const uint32_t rw_somevarint_2bytes = 241; - avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_2bytes); + EXPECT_EQ(avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_2bytes), + AVIF_RESULT_OK); EXPECT_EQ(avifRWStreamOffset(&rw_stream), offset + 2); const uint64_t rw_someu64 = 0xABCDEF01; - avifRWStreamWriteU64(&rw_stream, rw_someu64); + EXPECT_EQ(avifRWStreamWriteU64(&rw_stream, rw_someu64), AVIF_RESULT_OK); const size_t rw_somebitcount = 6; const uint32_t rw_somebits = (1 << rw_somebitcount) - 2; - avifRWStreamWriteBits(&rw_stream, rw_somebits, rw_somebitcount); + EXPECT_EQ(avifRWStreamWriteBits(&rw_stream, rw_somebits, rw_somebitcount), + AVIF_RESULT_OK); const size_t rw_maxbitcount = sizeof(uint32_t) * 8; const uint32_t rw_maxbits = std::numeric_limits::max(); - avifRWStreamWriteBits(&rw_stream, rw_maxbits, rw_maxbitcount); + EXPECT_EQ(avifRWStreamWriteBits(&rw_stream, rw_maxbits, rw_maxbitcount), + AVIF_RESULT_OK); offset = avifRWStreamOffset(&rw_stream); const uint32_t rw_somevarint_3bytes = 2288; - avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_3bytes); + EXPECT_EQ(avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_3bytes), + AVIF_RESULT_OK); const uint32_t rw_somevarint_4bytes = 67824; - avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_4bytes); + EXPECT_EQ(avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_4bytes), + AVIF_RESULT_OK); EXPECT_EQ(avifRWStreamOffset(&rw_stream), offset + 3 + 4); const uint32_t rw_somebit = 1; - avifRWStreamWriteBits(&rw_stream, rw_somebit, /*bitCount=*/1); + EXPECT_EQ(avifRWStreamWriteBits(&rw_stream, rw_somebit, /*bitCount=*/1), + AVIF_RESULT_OK); // Pad till byte alignment. - avifRWStreamWriteBits(&rw_stream, rw_somebit, /*bitCount=*/1); + EXPECT_EQ(avifRWStreamWriteBits(&rw_stream, rw_somebit, /*bitCount=*/1), + AVIF_RESULT_OK); offset = avifRWStreamOffset(&rw_stream); const uint32_t rw_somevarint_5bytes = std::numeric_limits::max(); - avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_5bytes); + EXPECT_EQ(avifRWStreamWriteVarInt(&rw_stream, rw_somevarint_5bytes), + AVIF_RESULT_OK); EXPECT_EQ(avifRWStreamOffset(&rw_stream), offset + 5); const size_t num_zeros = 10000; - avifRWStreamWriteZeros(&rw_stream, /*byteCount=*/num_zeros); + EXPECT_EQ(avifRWStreamWriteZeros(&rw_stream, /*byteCount=*/num_zeros), + AVIF_RESULT_OK); avifRWStreamFinishWrite(&rw_stream);