| // Copyright 2020 Joe Drago. All rights reserved. |
| // SPDX-License-Identifier: BSD-2-Clause |
| |
| #include "avif/avif.h" |
| |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| int main(int argc, char * argv[]) |
| { |
| if (argc != 3) { |
| fprintf(stderr, "avif_example_encode [encodeYUVDirectly:0/1] [output.avif]\n"); |
| return 1; |
| } |
| avifBool encodeYUVDirectly = AVIF_FALSE; |
| if (argv[1][0] == '1') { |
| encodeYUVDirectly = AVIF_TRUE; |
| } |
| const char * outputFilename = argv[2]; |
| |
| int returnCode = 1; |
| avifEncoder * encoder = NULL; |
| avifRWData avifOutput = AVIF_DATA_EMPTY; |
| avifRGBImage rgb; |
| memset(&rgb, 0, sizeof(rgb)); |
| |
| avifImage * image = avifImageCreate(128, 128, 8, AVIF_PIXEL_FORMAT_YUV444); // these values dictate what goes into the final AVIF |
| if (!image) { |
| fprintf(stderr, "Out of memory\n"); |
| goto cleanup; |
| } |
| // Configure image here: (see avif/avif.h) |
| // * colorPrimaries |
| // * transferCharacteristics |
| // * matrixCoefficients |
| // * avifImageSetProfileICC() |
| // * avifImageSetMetadataExif() |
| // * avifImageSetMetadataXMP() |
| // * yuvRange |
| // * alphaPremultiplied |
| // * transforms (transformFlags, pasp, clap, irot, imir) |
| |
| if (encodeYUVDirectly) { |
| // If you have YUV(A) data you want to encode, use this path |
| printf("Encoding raw YUVA data\n"); |
| |
| const avifResult allocateResult = avifImageAllocatePlanes(image, AVIF_PLANES_ALL); |
| if (allocateResult != AVIF_RESULT_OK) { |
| fprintf(stderr, "Failed to allocate the planes: %s\n", avifResultToString(allocateResult)); |
| goto cleanup; |
| } |
| |
| // Fill your YUV(A) data here |
| memset(image->yuvPlanes[AVIF_CHAN_Y], 255, image->yuvRowBytes[AVIF_CHAN_Y] * image->height); |
| memset(image->yuvPlanes[AVIF_CHAN_U], 128, image->yuvRowBytes[AVIF_CHAN_U] * image->height); |
| memset(image->yuvPlanes[AVIF_CHAN_V], 128, image->yuvRowBytes[AVIF_CHAN_V] * image->height); |
| memset(image->alphaPlane, 255, image->alphaRowBytes * image->height); |
| } else { |
| // If you have RGB(A) data you want to encode, use this path |
| printf("Encoding from converted RGBA\n"); |
| |
| avifRGBImageSetDefaults(&rgb, image); |
| // Override RGB(A)->YUV(A) defaults here: |
| // depth, format, chromaDownsampling, avoidLibYUV, ignoreAlpha, alphaPremultiplied, etc. |
| |
| // Alternative: set rgb.pixels and rgb.rowBytes yourself, which should match your chosen rgb.format |
| // Be sure to use uint16_t* instead of uint8_t* for rgb.pixels/rgb.rowBytes if (rgb.depth > 8) |
| avifResult allocationResult = avifRGBImageAllocatePixels(&rgb); |
| if (allocationResult != AVIF_RESULT_OK) { |
| fprintf(stderr, "Allocation of RGB samples failed: %s\n", avifResultToString(allocationResult)); |
| goto cleanup; |
| } |
| |
| // Fill your RGB(A) data here |
| memset(rgb.pixels, 255, rgb.rowBytes * image->height); |
| |
| avifResult convertResult = avifImageRGBToYUV(image, &rgb); |
| if (convertResult != AVIF_RESULT_OK) { |
| fprintf(stderr, "Failed to convert to YUV(A): %s\n", avifResultToString(convertResult)); |
| goto cleanup; |
| } |
| } |
| |
| encoder = avifEncoderCreate(); |
| if (!encoder) { |
| fprintf(stderr, "Out of memory\n"); |
| goto cleanup; |
| } |
| // Configure your encoder here (see avif/avif.h): |
| // * maxThreads |
| // * quality |
| // * qualityAlpha |
| // * tileRowsLog2 |
| // * tileColsLog2 |
| // * speed |
| // * keyframeInterval |
| // * timescale |
| encoder->quality = 60; |
| encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS; |
| |
| // Call avifEncoderAddImage() for each image in your sequence |
| // Only set AVIF_ADD_IMAGE_FLAG_SINGLE if you're not encoding a sequence |
| // Use avifEncoderAddImageGrid() instead with an array of avifImage* to make a grid image |
| avifResult addImageResult = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); |
| if (addImageResult != AVIF_RESULT_OK) { |
| fprintf(stderr, "Failed to add image to encoder: %s\n", avifResultToString(addImageResult)); |
| goto cleanup; |
| } |
| |
| avifResult finishResult = avifEncoderFinish(encoder, &avifOutput); |
| if (finishResult != AVIF_RESULT_OK) { |
| fprintf(stderr, "Failed to finish encode: %s\n", avifResultToString(finishResult)); |
| goto cleanup; |
| } |
| |
| printf("Encode success: %zu total bytes\n", avifOutput.size); |
| |
| FILE * f = fopen(outputFilename, "wb"); |
| size_t bytesWritten = fwrite(avifOutput.data, 1, avifOutput.size, f); |
| fclose(f); |
| if (bytesWritten != avifOutput.size) { |
| fprintf(stderr, "Failed to write %zu bytes\n", avifOutput.size); |
| goto cleanup; |
| } |
| printf("Wrote: %s\n", outputFilename); |
| |
| returnCode = 0; |
| cleanup: |
| if (image) { |
| avifImageDestroy(image); |
| } |
| if (encoder) { |
| avifEncoderDestroy(encoder); |
| } |
| avifRWDataFree(&avifOutput); |
| avifRGBImageFreePixels(&rgb); // Only use in conjunction with avifRGBImageAllocatePixels() |
| return returnCode; |
| } |