Do not copy input image when encoding with libaom Avoid the heap allocation of aomImage plane buffers and copying of input image in aomCodecEncodeImage(). This requires manually setting up the aom_image_t struct 'aomImage'. Fix https://github.com/AOMediaCodec/libavif/issues/694.
diff --git a/src/codec_aom.c b/src/codec_aom.c index 2212eb6..922fa40 100644 --- a/src/codec_aom.c +++ b/src/codec_aom.c
@@ -727,55 +727,71 @@ #endif } - int yShift = codec->internal->formatInfo.chromaShiftY; - uint32_t uvHeight = (image->height + yShift) >> yShift; - aom_image_t * aomImage = aom_img_alloc(NULL, codec->internal->aomFormat, image->width, image->height, 16); + aom_image_t aomImage; + memset(&aomImage, 0, sizeof(aomImage)); + aomImage.fmt = codec->internal->aomFormat; + aomImage.bit_depth = (image->depth > 8) ? 16 : 8; + aomImage.w = image->width; + aomImage.h = image->height; + aomImage.d_w = image->width; + aomImage.d_h = image->height; + // Get sample size for this format. + unsigned int bps; + if (codec->internal->aomFormat == AOM_IMG_FMT_I420) { + bps = 12; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I422) { + bps = 16; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I444) { + bps = 24; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I42016) { + bps = 24; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I42216) { + bps = 32; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I44416) { + bps = 48; + } else { + bps = 16; + } + aomImage.bps = bps; + avifBool monochromeRequested = AVIF_FALSE; if (alpha) { - aomImage->range = (image->alphaRange == AVIF_RANGE_FULL) ? AOM_CR_FULL_RANGE : AOM_CR_STUDIO_RANGE; - aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, aomImage->range); + aomImage.x_chroma_shift = 1; + aomImage.y_chroma_shift = 1; + aomImage.range = (image->alphaRange == AVIF_RANGE_FULL) ? AOM_CR_FULL_RANGE : AOM_CR_STUDIO_RANGE; + aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, aomImage.range); monochromeRequested = AVIF_TRUE; - for (uint32_t j = 0; j < image->height; ++j) { - uint8_t * srcAlphaRow = &image->alphaPlane[j * image->alphaRowBytes]; - uint8_t * dstAlphaRow = &aomImage->planes[0][j * aomImage->stride[0]]; - memcpy(dstAlphaRow, srcAlphaRow, image->alphaRowBytes); - } + aomImage.planes[0] = image->alphaPlane; + aomImage.stride[0] = image->alphaRowBytes; // Ignore UV planes when monochrome } else { - aomImage->range = (image->yuvRange == AVIF_RANGE_FULL) ? AOM_CR_FULL_RANGE : AOM_CR_STUDIO_RANGE; - aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, aomImage->range); + aomImage.x_chroma_shift = codec->internal->formatInfo.chromaShiftX; + aomImage.y_chroma_shift = codec->internal->formatInfo.chromaShiftY; + aomImage.range = (image->yuvRange == AVIF_RANGE_FULL) ? AOM_CR_FULL_RANGE : AOM_CR_STUDIO_RANGE; + aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, aomImage.range); int yuvPlaneCount = 3; if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { yuvPlaneCount = 1; // Ignore UV planes when monochrome monochromeRequested = AVIF_TRUE; } - int xShift = codec->internal->formatInfo.chromaShiftX; - uint32_t uvWidth = (image->width + xShift) >> xShift; - uint32_t bytesPerPixel = (image->depth > 8) ? 2 : 1; for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { - uint32_t planeWidth = (yuvPlane == AVIF_CHAN_Y) ? image->width : uvWidth; - uint32_t planeHeight = (yuvPlane == AVIF_CHAN_Y) ? image->height : uvHeight; - uint32_t bytesPerRow = bytesPerPixel * planeWidth; - - for (uint32_t j = 0; j < planeHeight; ++j) { - uint8_t * srcRow = &image->yuvPlanes[yuvPlane][j * image->yuvRowBytes[yuvPlane]]; - uint8_t * dstRow = &aomImage->planes[yuvPlane][j * aomImage->stride[yuvPlane]]; - memcpy(dstRow, srcRow, bytesPerRow); - } + aomImage.planes[yuvPlane] = image->yuvPlanes[yuvPlane]; + aomImage.stride[yuvPlane] = image->yuvRowBytes[yuvPlane]; } - aomImage->cp = (aom_color_primaries_t)image->colorPrimaries; - aomImage->tc = (aom_transfer_characteristics_t)image->transferCharacteristics; - aomImage->mc = (aom_matrix_coefficients_t)image->matrixCoefficients; - aomImage->csp = (aom_chroma_sample_position_t)image->yuvChromaSamplePosition; - aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_PRIMARIES, aomImage->cp); - aom_codec_control(&codec->internal->encoder, AV1E_SET_TRANSFER_CHARACTERISTICS, aomImage->tc); - aom_codec_control(&codec->internal->encoder, AV1E_SET_MATRIX_COEFFICIENTS, aomImage->mc); - aom_codec_control(&codec->internal->encoder, AV1E_SET_CHROMA_SAMPLE_POSITION, aomImage->csp); + aomImage.cp = (aom_color_primaries_t)image->colorPrimaries; + aomImage.tc = (aom_transfer_characteristics_t)image->transferCharacteristics; + aomImage.mc = (aom_matrix_coefficients_t)image->matrixCoefficients; + aomImage.csp = (aom_chroma_sample_position_t)image->yuvChromaSamplePosition; + aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_PRIMARIES, aomImage.cp); + aom_codec_control(&codec->internal->encoder, AV1E_SET_TRANSFER_CHARACTERISTICS, aomImage.tc); + aom_codec_control(&codec->internal->encoder, AV1E_SET_MATRIX_COEFFICIENTS, aomImage.mc); + aom_codec_control(&codec->internal->encoder, AV1E_SET_CHROMA_SAMPLE_POSITION, aomImage.csp); } + unsigned char * monoUVPlane = NULL; if (monochromeRequested && !codec->internal->monochromeEnabled) { // The user requested monochrome (via alpha or YUV400) but libaom cannot currently support // monochrome (see chroma_check comment above). Manually set UV planes to 0.5. @@ -783,21 +799,26 @@ // aomImage is always 420 when we're monochrome uint32_t monoUVWidth = (image->width + 1) >> 1; uint32_t monoUVHeight = (image->height + 1) >> 1; + uint32_t channelSize = avifImageUsesU16(image) ? 2 : 1; + uint32_t monoUVRowBytes = channelSize * monoUVWidth; + size_t monoUVSize = (size_t)monoUVHeight * monoUVRowBytes; - for (int yuvPlane = 1; yuvPlane < 3; ++yuvPlane) { - if (image->depth > 8) { - const uint16_t half = 1 << (image->depth - 1); - for (uint32_t j = 0; j < monoUVHeight; ++j) { - uint16_t * dstRow = (uint16_t *)&aomImage->planes[yuvPlane][j * aomImage->stride[yuvPlane]]; - for (uint32_t i = 0; i < monoUVWidth; ++i) { - dstRow[i] = half; - } + monoUVPlane = avifAlloc(monoUVSize); + if (image->depth > 8) { + const uint16_t half = 1 << (image->depth - 1); + for (uint32_t j = 0; j < monoUVHeight; ++j) { + uint16_t * dstRow = (uint16_t *)&monoUVPlane[(size_t)j * monoUVRowBytes]; + for (uint32_t i = 0; i < monoUVWidth; ++i) { + dstRow[i] = half; } - } else { - const uint8_t half = 128; - size_t planeSize = (size_t)monoUVHeight * aomImage->stride[yuvPlane]; - memset(aomImage->planes[yuvPlane], half, planeSize); } + } else { + const uint8_t half = 128; + memset(monoUVPlane, half, monoUVSize); + } + for (int yuvPlane = 1; yuvPlane < 3; ++yuvPlane) { + aomImage.planes[yuvPlane] = monoUVPlane; + aomImage.stride[yuvPlane] = monoUVRowBytes; } } @@ -805,7 +826,9 @@ if (addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) { encodeFlags |= AOM_EFLAG_FORCE_KF; } - if (aom_codec_encode(&codec->internal->encoder, aomImage, 0, 1, encodeFlags) != AOM_CODEC_OK) { + aom_codec_err_t encodeErr = aom_codec_encode(&codec->internal->encoder, &aomImage, 0, 1, encodeFlags); + avifFree(monoUVPlane); + if (encodeErr != AOM_CODEC_OK) { avifDiagnosticsPrintf(codec->diag, "aom_codec_encode() failed: %s: %s", aom_codec_error(&codec->internal->encoder), @@ -824,8 +847,6 @@ } } - aom_img_free(aomImage); - if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { // Flush and clean up encoder resources early to save on overhead when encoding alpha or grid images