Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode alpha as 4:2:0 with SVT #2004

Merged
merged 1 commit into from
Feb 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions src/codec_svt.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "svt-av1/EbSvtAv1Enc.h"

#include <stdint.h>
#include <string.h>

// The SVT_AV1_VERSION_MAJOR, SVT_AV1_VERSION_MINOR, SVT_AV1_VERSION_PATCHLEVEL, and
Expand Down Expand Up @@ -76,6 +77,7 @@ static avifResult svtCodecEncodeImage(avifCodec * codec,

avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
EbColorFormat color_format = EB_YUV420;
uint8_t * uvPlanes = NULL; // 4:2:0 U and V placeholder for alpha because SVT-AV1 does not support 4:0:0.
EbBufferHeaderType * input_buffer = NULL;
EbErrorType res = EB_ErrorNone;

Expand All @@ -98,6 +100,7 @@ static avifResult svtCodecEncodeImage(avifCodec * codec,
y_shift = 1;
break;
case AVIF_PIXEL_FORMAT_YUV400:
// Setting color_format = EB_YUV400; results in "Svt[error]: Instance 1: Only support 420 now".
case AVIF_PIXEL_FORMAT_NONE:
case AVIF_PIXEL_FORMAT_COUNT:
default:
Expand Down Expand Up @@ -198,16 +201,38 @@ static avifResult svtCodecEncodeImage(avifCodec * codec,
}
EbSvtIOFormat * input_picture_buffer = (EbSvtIOFormat *)input_buffer->p_buffer;

int bytesPerPixel = image->depth > 8 ? 2 : 1;
const uint32_t bytesPerPixel = image->depth > 8 ? 2 : 1;
const uint32_t uvHeight = (image->height + y_shift) >> y_shift;
if (alpha) {
input_picture_buffer->y_stride = image->alphaRowBytes / bytesPerPixel;
input_picture_buffer->luma = image->alphaPlane;
input_buffer->n_filled_len = image->alphaRowBytes * image->height;

#if SVT_AV1_CHECK_VERSION(1, 8, 0)
// Simulate 4:2:0 UV planes. SVT-AV1 does not support 4:0:0 samples.
const uint32_t uvWidth = (image->width + y_shift) >> y_shift;
const uint32_t uvRowBytes = uvWidth * bytesPerPixel;
const uint32_t uvSize = uvRowBytes * uvHeight;
uvPlanes = avifAlloc(uvSize);
if (uvPlanes == NULL) {
goto cleanup;
}
memset(uvPlanes, 0, uvSize);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: It would be "realistic" to set the uvPlanes buffer to the neutral value (8-bit: 128; 10-bit: 512; 12-bit: 2048), corresponding to 0.5. But we can't use memset to do that for high bit depths. Given that the AVIF decoders will ignore the UV planes in the alpha auxiliary image, I think it is fine to just set the uvPlanes buffer to 0.

input_picture_buffer->cb = uvPlanes;
input_buffer->n_filled_len += uvSize;
input_picture_buffer->cr = uvPlanes;
input_buffer->n_filled_len += uvSize;
input_picture_buffer->cb_stride = uvWidth;
input_picture_buffer->cr_stride = uvWidth;
#else
// This workaround was not needed before SVT-AV1 1.8.0.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is unusual that older versions work better. Do you know why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I asked about it here but got no answer.

// See https://github.com/AOMediaCodec/libavif/issues/1992.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems better to reference the SVT-AV1 bug report instead: https://gitlab.com/AOMediaCodec/SVT-AV1/-/issues/2146

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two are referencing each other, and we have more control on a GitHub issue than on a GitLab issue. If someone looks into that again, they can easily find the two reports.

(void)uvPlanes;
#endif
} else {
input_picture_buffer->y_stride = image->yuvRowBytes[0] / bytesPerPixel;
input_picture_buffer->luma = image->yuvPlanes[0];
input_buffer->n_filled_len = image->yuvRowBytes[0] * image->height;
uint32_t uvHeight = (image->height + y_shift) >> y_shift;
input_picture_buffer->cb = image->yuvPlanes[1];
input_buffer->n_filled_len += image->yuvRowBytes[1] * uvHeight;
input_picture_buffer->cr = image->yuvPlanes[2];
Expand All @@ -232,6 +257,9 @@ static avifResult svtCodecEncodeImage(avifCodec * codec,

result = dequeue_frame(codec, output, AVIF_FALSE);
cleanup:
if (uvPlanes) {
avifFree(uvPlanes);
}
if (input_buffer) {
if (input_buffer->p_buffer) {
avifFree(input_buffer->p_buffer);
Expand Down
Loading