| // Copyright 2019 Joe Drago. All rights reserved. | |
| // SPDX-License-Identifier: BSD-2-Clause | |
| #include "avif/internal.h" | |
| #include "rav1e.h" | |
| #include <string.h> | |
| struct avifCodecInternal | |
| { | |
| RaContext * rav1eContext; | |
| RaChromaSampling chromaSampling; | |
| int yShift; | |
| }; | |
| static void rav1eCodecDestroyInternal(avifCodec * codec) | |
| { | |
| if (codec->internal->rav1eContext) { | |
| rav1e_context_unref(codec->internal->rav1eContext); | |
| codec->internal->rav1eContext = NULL; | |
| } | |
| avifFree(codec->internal); | |
| } | |
| // Official support wasn't added until v0.4.0 | |
| static avifBool rav1eSupports400(void) | |
| { | |
| const char * rav1eVersionString = rav1e_version_short(); | |
| // Check major version > 0 | |
| int majorVersion = atoi(rav1eVersionString); | |
| if (majorVersion > 0) { | |
| return AVIF_TRUE; | |
| } | |
| // Check minor version >= 4 | |
| const char * minorVersionString = strchr(rav1eVersionString, '.'); | |
| if (!minorVersionString) { | |
| return AVIF_FALSE; | |
| } | |
| ++minorVersionString; | |
| if (!(*minorVersionString)) { | |
| return AVIF_FALSE; | |
| } | |
| int minorVersion = atoi(minorVersionString); | |
| return minorVersion >= 4; | |
| } | |
| static avifResult rav1eCodecEncodeImage(avifCodec * codec, | |
| avifEncoder * encoder, | |
| const avifImage * image, | |
| avifBool alpha, | |
| uint32_t addImageFlags, | |
| avifCodecEncodeOutput * output) | |
| { | |
| avifResult result = AVIF_RESULT_UNKNOWN_ERROR; | |
| RaConfig * rav1eConfig = NULL; | |
| RaFrame * rav1eFrame = NULL; | |
| if (!codec->internal->rav1eContext) { | |
| if (codec->csOptions->count > 0) { | |
| // None are currently supported! | |
| return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; | |
| } | |
| const avifBool supports400 = rav1eSupports400(); | |
| RaPixelRange rav1eRange; | |
| if (alpha) { | |
| rav1eRange = (image->alphaRange == AVIF_RANGE_FULL) ? RA_PIXEL_RANGE_FULL : RA_PIXEL_RANGE_LIMITED; | |
| codec->internal->chromaSampling = supports400 ? RA_CHROMA_SAMPLING_CS400 : RA_CHROMA_SAMPLING_CS420; | |
| codec->internal->yShift = 1; | |
| } else { | |
| rav1eRange = (image->yuvRange == AVIF_RANGE_FULL) ? RA_PIXEL_RANGE_FULL : RA_PIXEL_RANGE_LIMITED; | |
| codec->internal->yShift = 0; | |
| switch (image->yuvFormat) { | |
| case AVIF_PIXEL_FORMAT_YUV444: | |
| codec->internal->chromaSampling = RA_CHROMA_SAMPLING_CS444; | |
| break; | |
| case AVIF_PIXEL_FORMAT_YUV422: | |
| codec->internal->chromaSampling = RA_CHROMA_SAMPLING_CS422; | |
| break; | |
| case AVIF_PIXEL_FORMAT_YUV420: | |
| codec->internal->chromaSampling = RA_CHROMA_SAMPLING_CS420; | |
| codec->internal->yShift = 1; | |
| break; | |
| case AVIF_PIXEL_FORMAT_YUV400: | |
| codec->internal->chromaSampling = supports400 ? RA_CHROMA_SAMPLING_CS400 : RA_CHROMA_SAMPLING_CS420; | |
| codec->internal->yShift = 1; | |
| break; | |
| case AVIF_PIXEL_FORMAT_NONE: | |
| default: | |
| return AVIF_RESULT_UNKNOWN_ERROR; | |
| } | |
| } | |
| rav1eConfig = rav1e_config_default(); | |
| if (rav1e_config_set_pixel_format(rav1eConfig, | |
| (uint8_t)image->depth, | |
| codec->internal->chromaSampling, | |
| (RaChromaSamplePosition)image->yuvChromaSamplePosition, | |
| rav1eRange) < 0) { | |
| goto cleanup; | |
| } | |
| if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { | |
| if (rav1e_config_parse(rav1eConfig, "still_picture", "true") == -1) { | |
| goto cleanup; | |
| } | |
| } | |
| if (rav1e_config_parse_int(rav1eConfig, "width", image->width) == -1) { | |
| goto cleanup; | |
| } | |
| if (rav1e_config_parse_int(rav1eConfig, "height", image->height) == -1) { | |
| goto cleanup; | |
| } | |
| if (rav1e_config_parse_int(rav1eConfig, "threads", encoder->maxThreads) == -1) { | |
| goto cleanup; | |
| } | |
| int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); | |
| int maxQuantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); | |
| if (alpha) { | |
| minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); | |
| maxQuantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); | |
| } | |
| minQuantizer = (minQuantizer * 255) / 63; // Rescale quantizer values as rav1e's QP range is [0,255] | |
| maxQuantizer = (maxQuantizer * 255) / 63; | |
| if (rav1e_config_parse_int(rav1eConfig, "min_quantizer", minQuantizer) == -1) { | |
| goto cleanup; | |
| } | |
| if (rav1e_config_parse_int(rav1eConfig, "quantizer", maxQuantizer) == -1) { | |
| goto cleanup; | |
| } | |
| if (encoder->tileRowsLog2 != 0) { | |
| int tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6); | |
| if (rav1e_config_parse_int(rav1eConfig, "tile_rows", 1 << tileRowsLog2) == -1) { | |
| goto cleanup; | |
| } | |
| } | |
| if (encoder->tileColsLog2 != 0) { | |
| int tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6); | |
| if (rav1e_config_parse_int(rav1eConfig, "tile_cols", 1 << tileColsLog2) == -1) { | |
| goto cleanup; | |
| } | |
| } | |
| if (encoder->speed != AVIF_SPEED_DEFAULT) { | |
| int speed = AVIF_CLAMP(encoder->speed, 0, 10); | |
| if (rav1e_config_parse_int(rav1eConfig, "speed", speed) == -1) { | |
| goto cleanup; | |
| } | |
| } | |
| rav1e_config_set_color_description(rav1eConfig, | |
| (RaMatrixCoefficients)image->matrixCoefficients, | |
| (RaColorPrimaries)image->colorPrimaries, | |
| (RaTransferCharacteristics)image->transferCharacteristics); | |
| codec->internal->rav1eContext = rav1e_context_new(rav1eConfig); | |
| if (!codec->internal->rav1eContext) { | |
| goto cleanup; | |
| } | |
| } | |
| rav1eFrame = rav1e_frame_new(codec->internal->rav1eContext); | |
| int byteWidth = (image->depth > 8) ? 2 : 1; | |
| if (alpha) { | |
| rav1e_frame_fill_plane(rav1eFrame, 0, image->alphaPlane, (size_t)image->alphaRowBytes * image->height, image->alphaRowBytes, byteWidth); | |
| } else { | |
| rav1e_frame_fill_plane(rav1eFrame, 0, image->yuvPlanes[0], (size_t)image->yuvRowBytes[0] * image->height, image->yuvRowBytes[0], byteWidth); | |
| if (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) { | |
| uint32_t uvHeight = (image->height + codec->internal->yShift) >> codec->internal->yShift; | |
| rav1e_frame_fill_plane(rav1eFrame, 1, image->yuvPlanes[1], (size_t)image->yuvRowBytes[1] * uvHeight, image->yuvRowBytes[1], byteWidth); | |
| rav1e_frame_fill_plane(rav1eFrame, 2, image->yuvPlanes[2], (size_t)image->yuvRowBytes[2] * uvHeight, image->yuvRowBytes[2], byteWidth); | |
| } | |
| } | |
| RaFrameTypeOverride frameType = RA_FRAME_TYPE_OVERRIDE_NO; | |
| if (addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) { | |
| frameType = RA_FRAME_TYPE_OVERRIDE_KEY; | |
| } | |
| rav1e_frame_set_type(rav1eFrame, frameType); | |
| RaEncoderStatus encoderStatus = rav1e_send_frame(codec->internal->rav1eContext, rav1eFrame); | |
| if (encoderStatus != RA_ENCODER_STATUS_SUCCESS) { | |
| goto cleanup; | |
| } | |
| RaPacket * pkt = NULL; | |
| for (;;) { | |
| encoderStatus = rav1e_receive_packet(codec->internal->rav1eContext, &pkt); | |
| if (encoderStatus == RA_ENCODER_STATUS_ENCODED) { | |
| continue; | |
| } | |
| if ((encoderStatus != RA_ENCODER_STATUS_SUCCESS) && (encoderStatus != RA_ENCODER_STATUS_NEED_MORE_DATA)) { | |
| goto cleanup; | |
| } else if (pkt) { | |
| if (pkt->data && (pkt->len > 0)) { | |
| avifCodecEncodeOutputAddSample(output, pkt->data, pkt->len, (pkt->frame_type == RA_FRAME_TYPE_KEY)); | |
| } | |
| rav1e_packet_unref(pkt); | |
| pkt = NULL; | |
| } else { | |
| break; | |
| } | |
| } | |
| result = AVIF_RESULT_OK; | |
| cleanup: | |
| if (rav1eFrame) { | |
| rav1e_frame_unref(rav1eFrame); | |
| rav1eFrame = NULL; | |
| } | |
| if (rav1eConfig) { | |
| rav1e_config_unref(rav1eConfig); | |
| rav1eConfig = NULL; | |
| } | |
| return result; | |
| } | |
| static avifBool rav1eCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output) | |
| { | |
| for (;;) { | |
| RaEncoderStatus encoderStatus = rav1e_send_frame(codec->internal->rav1eContext, NULL); // flush | |
| if (encoderStatus != RA_ENCODER_STATUS_SUCCESS) { | |
| return AVIF_FALSE; | |
| } | |
| avifBool gotPacket = AVIF_FALSE; | |
| RaPacket * pkt = NULL; | |
| for (;;) { | |
| encoderStatus = rav1e_receive_packet(codec->internal->rav1eContext, &pkt); | |
| if (encoderStatus == RA_ENCODER_STATUS_ENCODED) { | |
| continue; | |
| } | |
| if ((encoderStatus != RA_ENCODER_STATUS_SUCCESS) && (encoderStatus != RA_ENCODER_STATUS_LIMIT_REACHED)) { | |
| return AVIF_FALSE; | |
| } | |
| if (pkt) { | |
| gotPacket = AVIF_TRUE; | |
| if (pkt->data && (pkt->len > 0)) { | |
| avifCodecEncodeOutputAddSample(output, pkt->data, pkt->len, (pkt->frame_type == RA_FRAME_TYPE_KEY)); | |
| } | |
| rav1e_packet_unref(pkt); | |
| pkt = NULL; | |
| } else { | |
| break; | |
| } | |
| } | |
| if (!gotPacket) { | |
| break; | |
| } | |
| } | |
| return AVIF_TRUE; | |
| } | |
| const char * avifCodecVersionRav1e(void) | |
| { | |
| return rav1e_version_full(); | |
| } | |
| avifCodec * avifCodecCreateRav1e(void) | |
| { | |
| avifCodec * codec = (avifCodec *)avifAlloc(sizeof(avifCodec)); | |
| memset(codec, 0, sizeof(struct avifCodec)); | |
| codec->encodeImage = rav1eCodecEncodeImage; | |
| codec->encodeFinish = rav1eCodecEncodeFinish; | |
| codec->destroyInternal = rav1eCodecDestroyInternal; | |
| codec->internal = (struct avifCodecInternal *)avifAlloc(sizeof(struct avifCodecInternal)); | |
| memset(codec->internal, 0, sizeof(struct avifCodecInternal)); | |
| return codec; | |
| } |