Skip to content

Commit

Permalink
avifRWDataRealloc,avifRWDataSet return avifResult
Browse files Browse the repository at this point in the history
Also avifImageSetProfileICC(), avifImageSetMetadataExif(),
avifImageSetMetadataXMP() and avifCodecEncodeOutputAddSample().
To catch out-of-memory issues.
Bug: #820
  • Loading branch information
y-guyon authored Jul 31, 2023
1 parent 2f25613 commit 397f74c
Show file tree
Hide file tree
Showing 24 changed files with 213 additions and 101 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ List of incompatible ABI changes in this release:
those pointers.
* Check the return value of avifEncoderSetCodecSpecificOption().
* The maxThreads member was added to the avifRGBImage struct.
* Check the return value of avifRGBImageAllocatePixels().
* Check the return value of avifRGBImageAllocatePixels(), avifRWDataRealloc(),
avifRWDataSet(), avifImageSetProfileICC(), avifImageSetMetadataExif() and
avifImageSetMetadataXMP().
* The meaning of the keyframeInterval member of avifEncoder struct has changed
slightly. When set to a value of "n",
* Before: It forces a keyframe on every nth frame.
Expand Down Expand Up @@ -72,7 +74,9 @@ List of incompatible ABI changes in this release:
* avifEncoderSetCodecSpecificOption() now returns avifResult instead of void to
report memory allocation failures.
* At decoding, avifIOStats now returns the same values as at encoding.
* avifRGBImageAllocatePixels() now returns avifResult instead of void to report
* avifRGBImageAllocatePixels(), avifRWDataRealloc(), avifRWDataSet(),
avifImageSetProfileICC(), avifImageSetMetadataExif() and
avifImageSetMetadataXMP() now return avifResult instead of void to report
memory allocation failures.
* avifReadImage(), avifJPEGRead() and avifPNGRead() now remove the trailing zero
byte from read XMP chunks, if any. See avifImageFixXMP().
Expand Down
5 changes: 4 additions & 1 deletion apps/avifdec.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "avifutil.h"
#include "y4m.h"

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -331,7 +332,9 @@ int main(int argc, char * argv[])

if (ignoreICC && (decoder->image->icc.size > 0)) {
printf("[--ignore-icc] Discarding ICC profile.\n");
avifImageSetProfileICC(decoder->image, NULL, 0);
// This cannot fail.
result = avifImageSetProfileICC(decoder->image, NULL, 0);
assert(result == AVIF_RESULT_OK);
}

avifAppFileFormat outputFormat = avifGuessFileFormat(outputFilename);
Expand Down
19 changes: 10 additions & 9 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,10 @@ static avifBool readEntireFile(const char * filename, avifRWData * raw)
size_t fileSize = (size_t)pos;
fseek(f, 0, SEEK_SET);

avifRWDataRealloc(raw, fileSize);
if (avifRWDataRealloc(raw, fileSize) != AVIF_RESULT_OK) {
fclose(f);
return AVIF_FALSE;
}
size_t bytesRead = fread(raw->data, 1, fileSize, f);
fclose(f);

Expand Down Expand Up @@ -1687,14 +1690,12 @@ int main(int argc, char * argv[])
}
}

if (iccOverride.size) {
avifImageSetProfileICC(image, iccOverride.data, iccOverride.size);
}
if (exifOverride.size) {
avifImageSetMetadataExif(image, exifOverride.data, exifOverride.size);
}
if (xmpOverride.size) {
avifImageSetMetadataXMP(image, xmpOverride.data, xmpOverride.size);
if ((iccOverride.size && (avifImageSetProfileICC(image, iccOverride.data, iccOverride.size) != AVIF_RESULT_OK)) ||
(exifOverride.size && (avifImageSetMetadataExif(image, exifOverride.data, exifOverride.size) != AVIF_RESULT_OK)) ||
(xmpOverride.size && (avifImageSetMetadataXMP(image, xmpOverride.data, xmpOverride.size) != AVIF_RESULT_OK))) {
fprintf(stderr, "Error when setting overridden metadata: out of memory.\n");
returnCode = 1;
goto cleanup;
}

if (!image->icc.size && !cicpExplicitlySet && (image->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED) &&
Expand Down
35 changes: 28 additions & 7 deletions apps/shared/avifjpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,10 @@ avifBool avifJPEGRead(const char * inputFilename,
unsigned int iccDataLen;
if (read_icc_profile(&cinfo, &iccDataTmp, &iccDataLen)) {
iccData = iccDataTmp;
avifImageSetProfileICC(avif, iccDataTmp, (size_t)iccDataLen);
if (avifImageSetProfileICC(avif, iccDataTmp, (size_t)iccDataLen) != AVIF_RESULT_OK) {
fprintf(stderr, "Setting ICC profile failed: %s (out of memory)\n", inputFilename);
goto cleanup;
}
}
}

Expand Down Expand Up @@ -418,7 +421,10 @@ avifBool avifJPEGRead(const char * inputFilename,
// Exif orientation, if any, is imported to avif->irot/imir and kept in avif->exif.
// libheif has the same behavior, see
// https://github.com/strukturag/libheif/blob/ea78603d8e47096606813d221725621306789ff2/examples/heif_enc.cc#L403
avifImageSetMetadataExif(avif, marker->data + tagExif.size, marker->data_length - tagExif.size);
if (avifImageSetMetadataExif(avif, marker->data + tagExif.size, marker->data_length - tagExif.size) != AVIF_RESULT_OK) {
fprintf(stderr, "Setting Exif metadata failed: %s (out of memory)\n", inputFilename);
goto cleanup;
}
found = AVIF_TRUE;
}
}
Expand Down Expand Up @@ -493,11 +499,17 @@ avifBool avifJPEGRead(const char * inputFilename,
} else {
memcpy(extendedXMPGUID, guid, AVIF_JPEG_EXTENDED_XMP_GUID_LENGTH);

avifRWDataRealloc(&totalXMP, (size_t)standardXMPSize + totalExtendedXMPSize);
if (avifRWDataRealloc(&totalXMP, (size_t)standardXMPSize + totalExtendedXMPSize) != AVIF_RESULT_OK) {
fprintf(stderr, "XMP extraction failed: out of memory\n");
goto cleanup;
}
memcpy(totalXMP.data, standardXMPData, standardXMPSize);

// Keep track of the bytes that were set.
avifRWDataRealloc(&extendedXMPReadBytes, totalExtendedXMPSize);
if (avifRWDataRealloc(&extendedXMPReadBytes, totalExtendedXMPSize) != AVIF_RESULT_OK) {
fprintf(stderr, "XMP extraction failed: out of memory\n");
goto cleanup;
}
memset(extendedXMPReadBytes.data, 0, extendedXMPReadBytes.size);

foundExtendedXMP = AVIF_TRUE;
Expand Down Expand Up @@ -549,7 +561,10 @@ avifBool avifJPEGRead(const char * inputFilename,
totalXMP.data = NULL;
totalXMP.size = 0;
} else if (standardXMPData) {
avifImageSetMetadataXMP(avif, standardXMPData, standardXMPSize);
if (avifImageSetMetadataXMP(avif, standardXMPData, standardXMPSize) != AVIF_RESULT_OK) {
fprintf(stderr, "XMP extraction failed: out of memory\n");
goto cleanup;
}
}
avifImageFixXMP(avif); // Remove one trailing null character if any.
}
Expand Down Expand Up @@ -619,7 +634,10 @@ avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int
}

avifRWData exif = { NULL, 0 };
avifRWDataRealloc(&exif, AVIF_JPEG_EXIF_HEADER_LENGTH + avif->exif.size - exifTiffHeaderOffset);
if (avifRWDataRealloc(&exif, AVIF_JPEG_EXIF_HEADER_LENGTH + avif->exif.size - exifTiffHeaderOffset) != AVIF_RESULT_OK) {
fprintf(stderr, "Error writing JPEG metadata: out of memory\n");
goto cleanup;
}
memcpy(exif.data, AVIF_JPEG_EXIF_HEADER, AVIF_JPEG_EXIF_HEADER_LENGTH);
memcpy(exif.data + AVIF_JPEG_EXIF_HEADER_LENGTH, avif->exif.data + exifTiffHeaderOffset, avif->exif.size - exifTiffHeaderOffset);
// Make sure the Exif orientation matches the irot/imir values.
Expand Down Expand Up @@ -665,7 +683,10 @@ avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int
fprintf(stderr, "Warning writing JPEG metadata: XMP payload is too big and was dropped\n");
} else {
avifRWData xmp = { NULL, 0 };
avifRWDataRealloc(&xmp, AVIF_JPEG_STANDARD_XMP_TAG_LENGTH + avif->xmp.size);
if (avifRWDataRealloc(&xmp, AVIF_JPEG_STANDARD_XMP_TAG_LENGTH + avif->xmp.size) != AVIF_RESULT_OK) {
fprintf(stderr, "Error writing JPEG metadata: out of memory\n");
goto cleanup;
}
memcpy(xmp.data, AVIF_JPEG_STANDARD_XMP_TAG, AVIF_JPEG_STANDARD_XMP_TAG_LENGTH);
memcpy(xmp.data + AVIF_JPEG_STANDARD_XMP_TAG_LENGTH, avif->xmp.data, avif->xmp.size);
jpeg_write_marker(&cinfo, JPEG_APP0 + 1, xmp.data, (unsigned int)xmp.size);
Expand Down
25 changes: 20 additions & 5 deletions apps/shared/avifpng.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
// AVIF_FALSE is returned if fewer than numExpectedBytes hexadecimal pairs are converted.
static avifBool avifHexStringToBytes(const char * hexString, size_t hexStringLength, size_t numExpectedBytes, avifRWData * bytes)
{
avifRWDataRealloc(bytes, numExpectedBytes);
if (avifRWDataRealloc(bytes, numExpectedBytes) != AVIF_RESULT_OK) {
fprintf(stderr, "Metadata extraction failed: out of memory\n");
return AVIF_FALSE;
}
size_t numBytes = 0;
for (size_t i = 0; (i + 1 < hexStringLength) && (numBytes < numExpectedBytes);) {
if (hexString[i] == '\n') {
Expand Down Expand Up @@ -124,7 +127,10 @@ static avifBool avifExtractExifAndXMP(png_structp png, png_infop info, avifBool
return AVIF_FALSE;
}
// Avoid avifImageSetMetadataExif() that sets irot/imir.
avifRWDataSet(&avif->exif, exif, exifSize);
if (avifRWDataSet(&avif->exif, exif, exifSize) != AVIF_RESULT_OK) {
fprintf(stderr, "Exif extraction failed: out of memory\n");
return AVIF_FALSE;
}
// According to the Extensions to the PNG 1.2 Specification, Version 1.5.0, section 3.7:
// "It is recommended that unless a decoder has independent knowledge of the validity of the Exif data,
// the data should be considered to be of historical value only."
Expand Down Expand Up @@ -190,7 +196,10 @@ static avifBool avifExtractExifAndXMP(png_structp png, png_infop info, avifBool
fprintf(stderr, "XMP extraction failed: empty XML:com.adobe.xmp payload\n");
return AVIF_FALSE;
}
avifImageSetMetadataXMP(avif, (const uint8_t *)text->text, textLength);
if (avifImageSetMetadataXMP(avif, (const uint8_t *)text->text, textLength) != AVIF_RESULT_OK) {
fprintf(stderr, "XMP extraction failed: out of memory\n");
return AVIF_FALSE;
}
*ignoreXMP = AVIF_TRUE; // Ignore any other XMP chunk.
}
}
Expand Down Expand Up @@ -361,7 +370,10 @@ avifBool avifPNGRead(const char * inputFilename,
// When the sRGB / iCCP chunk is present, applications that recognize it and are capable of color management
// must ignore the gAMA and cHRM chunks and use the sRGB / iCCP chunk instead.
if (png_get_iCCP(png, info, &iccpProfileName, &iccpCompression, &iccpData, &iccpDataLen) == PNG_INFO_iCCP) {
avifImageSetProfileICC(avif, iccpData, iccpDataLen);
if (avifImageSetProfileICC(avif, iccpData, iccpDataLen) != AVIF_RESULT_OK) {
fprintf(stderr, "Setting ICC profile failed: out of memory.\n");
goto cleanup;
}
} else if (allowChangingCicp) {
if (png_get_sRGB(png, info, &srgbIntent) == PNG_INFO_sRGB) {
// srgbIntent ignored
Expand Down Expand Up @@ -653,7 +665,10 @@ avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint3
fprintf(stderr, "Error writing PNG: XMP metadata is too big\n");
goto cleanup;
}
avifRWDataRealloc(&xmp, avif->xmp.size + 1);
if (avifRWDataRealloc(&xmp, avif->xmp.size + 1) != AVIF_RESULT_OK) {
fprintf(stderr, "Error writing PNG: out of memory\n");
goto cleanup;
}
memcpy(xmp.data, avif->xmp.data, avif->xmp.size);
xmp.data[avif->xmp.size] = '\0';
png_text * text = &texts[numTextMetadataChunks++];
Expand Down
10 changes: 6 additions & 4 deletions apps/shared/iccmaker.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,9 @@ avifBool avifGenerateRGBICC(avifRWData * icc, float gamma, const float primaries
}

computeMD5(buffer, sizeof(iccColorTemplate));
avifRWDataRealloc(icc, iccColorLength);
memcpy(icc->data, buffer, iccColorLength);
if (avifRWDataSet(icc, buffer, iccColorLength) != AVIF_RESULT_OK) {
return AVIF_FALSE;
}

return AVIF_TRUE;
}
Expand All @@ -469,8 +470,9 @@ avifBool avifGenerateGrayICC(avifRWData * icc, float gamma, const float white[2]
}

computeMD5(buffer, sizeof(iccGrayTemplate));
avifRWDataRealloc(icc, iccGrayLength);
memcpy(icc->data, buffer, iccGrayLength);
if (avifRWDataSet(icc, buffer, iccGrayLength) != AVIF_RESULT_OK) {
return AVIF_FALSE;
}

return AVIF_TRUE;
}
5 changes: 4 additions & 1 deletion apps/shared/y4m.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,10 @@ avifBool y4mRead(const char * inputFilename, avifImage * avif, avifAppSourceTimi
frame.displayFilename = inputFilename;

avifRWData raw = AVIF_DATA_EMPTY;
avifRWDataRealloc(&raw, Y4M_MAX_LINE_SIZE);
if (avifRWDataRealloc(&raw, Y4M_MAX_LINE_SIZE) != AVIF_RESULT_OK) {
fprintf(stderr, "Out of memory\n");
goto cleanup;
}

if (iter && *iter) {
// Continue reading FRAMEs from this y4m stream
Expand Down
11 changes: 6 additions & 5 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,9 @@ typedef struct avifRWData
// clang-format on

// The avifRWData input must be zero-initialized before being manipulated with these functions.
AVIF_API void avifRWDataRealloc(avifRWData * raw, size_t newSize);
AVIF_API void avifRWDataSet(avifRWData * raw, const uint8_t * data, size_t len);
// If AVIF_RESULT_OUT_OF_MEMORY is returned, raw is left unchanged.
AVIF_API avifResult avifRWDataRealloc(avifRWData * raw, size_t newSize);
AVIF_API avifResult avifRWDataSet(avifRWData * raw, const uint8_t * data, size_t len);
AVIF_API void avifRWDataFree(avifRWData * raw);

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -565,14 +566,14 @@ AVIF_API avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcIma
AVIF_API avifResult avifImageSetViewRect(avifImage * dstImage, const avifImage * srcImage, const avifCropRect * rect); // shallow copy, no metadata
AVIF_API void avifImageDestroy(avifImage * image);

AVIF_API void avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize);
AVIF_API avifResult avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize);
// Sets Exif metadata. Attempts to parse the Exif metadata for Exif orientation. Sets
// image->transformFlags, image->irot and image->imir if the Exif metadata is parsed successfully,
// otherwise leaves image->transformFlags, image->irot and image->imir unchanged.
// Warning: If the Exif payload is set and invalid, avifEncoderWrite() may return AVIF_RESULT_INVALID_EXIF_PAYLOAD.
AVIF_API void avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize);
AVIF_API avifResult avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize);
// Sets XMP metadata.
AVIF_API void avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize);
AVIF_API avifResult avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize);

AVIF_API avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes); // Ignores any pre-existing planes
AVIF_API void avifImageFreePlanes(avifImage * image, avifPlanesFlags planes); // Ignores already-freed planes
Expand Down
2 changes: 1 addition & 1 deletion include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ typedef struct avifCodecEncodeOutput
} avifCodecEncodeOutput;

avifCodecEncodeOutput * avifCodecEncodeOutputCreate(void);
void avifCodecEncodeOutputAddSample(avifCodecEncodeOutput * encodeOutput, const uint8_t * data, size_t len, avifBool sync);
avifResult avifCodecEncodeOutputAddSample(avifCodecEncodeOutput * encodeOutput, const uint8_t * data, size_t len, avifBool sync);
void avifCodecEncodeOutputDestroy(avifCodecEncodeOutput * encodeOutput);

// ---------------------------------------------------------------------------
Expand Down
14 changes: 7 additions & 7 deletions src/avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,10 @@ avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifP
avifImageFreePlanes(dstImage, AVIF_PLANES_ALL);
avifImageCopyNoAlloc(dstImage, srcImage);

avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size);
AVIF_CHECKRES(avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size));

avifRWDataSet(&dstImage->exif, srcImage->exif.data, srcImage->exif.size);
avifImageSetMetadataXMP(dstImage, srcImage->xmp.data, srcImage->xmp.size);
AVIF_CHECKRES(avifRWDataSet(&dstImage->exif, srcImage->exif.data, srcImage->exif.size));
AVIF_CHECKRES(avifImageSetMetadataXMP(dstImage, srcImage->xmp.data, srcImage->xmp.size));

if ((planes & AVIF_PLANES_YUV) && srcImage->yuvPlanes[AVIF_CHAN_Y]) {
if ((srcImage->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) &&
Expand Down Expand Up @@ -295,14 +295,14 @@ void avifImageDestroy(avifImage * image)
avifFree(image);
}

void avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize)
avifResult avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize)
{
avifRWDataSet(&image->icc, icc, iccSize);
return avifRWDataSet(&image->icc, icc, iccSize);
}

void avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize)
avifResult avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize)
{
avifRWDataSet(&image->xmp, xmp, xmpSize);
return avifRWDataSet(&image->xmp, xmp, xmpSize);
}

avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes)
Expand Down
12 changes: 10 additions & 2 deletions src/codec_aom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,8 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
break;
}
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY));
AVIF_CHECKRES(
avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY)));
}
}

Expand Down Expand Up @@ -1187,7 +1188,14 @@ static avifBool aomCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput *
}
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
gotPacket = AVIF_TRUE;
avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY));
const avifResult result = avifCodecEncodeOutputAddSample(output,
pkt->data.frame.buf,
pkt->data.frame.sz,
(pkt->data.frame.flags & AOM_FRAME_IS_KEY));
if (result != AVIF_RESULT_OK) {
avifDiagnosticsPrintf(codec->diag, "avifCodecEncodeOutputAddSample() failed: %s", avifResultToString(result));
return AVIF_FALSE;
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions src/codec_avm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,8 @@ static avifResult avmCodecEncodeImage(avifCodec * codec,
break;
}
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY));
AVIF_CHECKRES(
avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY)));
}
}

Expand Down Expand Up @@ -1059,7 +1060,14 @@ static avifBool avmCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput *
}
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
gotPacket = AVIF_TRUE;
avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY));
const avifResult result = avifCodecEncodeOutputAddSample(output,
pkt->data.frame.buf,
pkt->data.frame.sz,
(pkt->data.frame.flags & AOM_FRAME_IS_KEY));
if (result != AVIF_RESULT_OK) {
avifDiagnosticsPrintf(codec->diag, "avifCodecEncodeOutputAddSample() failed: %s", avifResultToString(result));
return AVIF_FALSE;
}
}
}

Expand Down
Loading

0 comments on commit 397f74c

Please sign in to comment.