in libheif/plugins/encoder_vvenc.cc [359:625]
static struct heif_error vvenc_encode_image(void* encoder_raw, const struct heif_image* image,
heif_image_input_class input_class)
{
struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw;
int bit_depth = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
bool isGreyscale = (heif_image_get_colorspace(image) == heif_colorspace_monochrome);
heif_chroma chroma = heif_image_get_chroma_format(image);
if (bit_depth != 8) {
return heif_error{
heif_error_Encoder_plugin_error,
heif_suberror_Unsupported_image_type,
kError_unsupported_bit_depth
};
}
int input_width = heif_image_get_width(image, heif_channel_Y);
int input_height = heif_image_get_height(image, heif_channel_Y);
int input_chroma_width = 0;
int input_chroma_height = 0;
uint32_t encoded_width, encoded_height;
vvenc_query_encoded_size(encoder_raw, input_width, input_height, &encoded_width, &encoded_height);
vvencChromaFormat vvencChroma;
int chroma_stride_shift = 0;
int chroma_height_shift = 0;
if (isGreyscale) {
vvencChroma = VVENC_CHROMA_400;
}
else if (chroma == heif_chroma_420) {
vvencChroma = VVENC_CHROMA_420;
chroma_stride_shift = 1;
chroma_height_shift = 1;
input_chroma_width = (input_width + 1) / 2;
input_chroma_height = (input_height + 1) / 2;
}
else if (chroma == heif_chroma_422) {
vvencChroma = VVENC_CHROMA_422;
chroma_stride_shift = 1;
chroma_height_shift = 0;
input_chroma_width = (input_width + 1) / 2;
input_chroma_height = input_height;
}
else if (chroma == heif_chroma_444) {
vvencChroma = VVENC_CHROMA_444;
chroma_stride_shift = 0;
chroma_height_shift = 0;
input_chroma_width = input_width;
input_chroma_height = input_height;
}
else {
return heif_error{
heif_error_Encoder_plugin_error,
heif_suberror_Unsupported_image_type,
kError_unsupported_chroma
};
}
if (chroma != heif_chroma_monochrome) {
int w = heif_image_get_width(image, heif_channel_Y);
int h = heif_image_get_height(image, heif_channel_Y);
if (chroma != heif_chroma_444) { w = (w + 1) / 2; }
if (chroma == heif_chroma_420) { h = (h + 1) / 2; }
assert(heif_image_get_width(image, heif_channel_Cb) == w);
assert(heif_image_get_width(image, heif_channel_Cr) == w);
assert(heif_image_get_height(image, heif_channel_Cb) == h);
assert(heif_image_get_height(image, heif_channel_Cr) == h);
(void) w;
(void) h;
}
vvenc_config params;
int ret = vvenc_init_default(¶ms, encoded_width, encoded_height, 25, 0, encoder->quality, VVENC_MEDIUM);
if (ret != VVENC_OK) {
// TODO: cleanup memory
return heif_error{
heif_error_Encoder_plugin_error,
heif_suberror_Encoder_encoding,
kError_unspecified_error
};
}
params.m_inputBitDepth[0] = bit_depth;
params.m_inputBitDepth[1] = bit_depth;
params.m_outputBitDepth[0] = bit_depth;
params.m_outputBitDepth[1] = bit_depth;
params.m_internalBitDepth[0] = bit_depth;
params.m_internalBitDepth[1] = bit_depth;
vvencEncoder* vvencoder = vvenc_encoder_create();
ret = vvenc_encoder_open(vvencoder, ¶ms);
if (ret != VVENC_OK) {
// TODO: cleanup memory
return heif_error{
heif_error_Encoder_plugin_error,
heif_suberror_Encoder_encoding,
kError_unspecified_error
};
}
struct heif_color_profile_nclx* nclx = nullptr;
heif_error err = heif_image_get_nclx_color_profile(image, &nclx);
if (err.code != heif_error_Ok) {
nclx = nullptr;
}
// make sure NCLX profile is deleted at end of function
auto nclx_deleter = std::unique_ptr<heif_color_profile_nclx, void (*)(heif_color_profile_nclx*)>(nclx, heif_nclx_color_profile_free);
#if 0
if (nclx) {
config->vui.fullrange = nclx->full_range_flag;
}
else {
config->vui.fullrange = 1;
}
if (nclx &&
(input_class == heif_image_input_class_normal ||
input_class == heif_image_input_class_thumbnail)) {
config->vui.colorprim = nclx->color_primaries;
config->vui.transfer = nclx->transfer_characteristics;
config->vui.colormatrix = nclx->matrix_coefficients;
}
config->qp = ((100 - encoder->quality) * 51 + 50) / 100;
config->lossless = encoder->lossless ? 1 : 0;
config->width = encoded_width;
config->height = encoded_height;
#endif
// Note: it is ok to cast away the const, as the image content is not changed.
// However, we have to guarantee that there are no plane pointers or stride values kept over calling the svt_encode_image() function.
/*
err = heif_image_extend_padding_to_size(const_cast<struct heif_image*>(image),
param->sourceWidth,
param->sourceHeight);
if (err.code) {
return err;
}
*/
vvencYUVBuffer* yuvbuf = vvenc_YUVBuffer_alloc();
vvenc_YUVBuffer_alloc_buffer(yuvbuf, vvencChroma, encoded_width, encoded_height);
vvencAccessUnit* au = vvenc_accessUnit_alloc();
const int auSizeScale = (vvencChroma <= VVENC_CHROMA_420 ? 2 : 3);
vvenc_accessUnit_alloc_payload(au, auSizeScale * encoded_width * encoded_height + 1024);
// vvenc_init_pass( encoder, pass, statsfilename );
int16_t* yptr = nullptr;
int16_t* cbptr = nullptr;
int16_t* crptr = nullptr;
int ystride = 0;
int cbstride = 0;
int crstride = 0;
if (isGreyscale) {
int stride;
const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
copy_plane(yptr, ystride, data, stride, input_width, input_height, encoded_width, encoded_height);
yuvbuf->planes[0].ptr = yptr;
yuvbuf->planes[0].width = encoded_width;
yuvbuf->planes[0].height = encoded_height;
yuvbuf->planes[0].stride = ystride;
}
else {
int stride;
const uint8_t* data;
data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
copy_plane(yptr, ystride, data, stride, input_width, input_height, encoded_width, encoded_height);
data = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride);
copy_plane(cbptr, cbstride, data, stride, input_chroma_width, input_chroma_height,
encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift);
data = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride);
copy_plane(crptr, crstride, data, stride, input_chroma_width, input_chroma_height,
encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift);
yuvbuf->planes[0].ptr = yptr;
yuvbuf->planes[0].width = encoded_width;
yuvbuf->planes[0].height = encoded_height;
yuvbuf->planes[0].stride = ystride;
yuvbuf->planes[1].ptr = cbptr;
yuvbuf->planes[1].width = encoded_width >> chroma_stride_shift;
yuvbuf->planes[1].height = encoded_height >> chroma_height_shift;
yuvbuf->planes[1].stride = cbstride;
yuvbuf->planes[2].ptr = crptr;
yuvbuf->planes[2].width = encoded_width >> chroma_stride_shift;
yuvbuf->planes[2].height = encoded_height >> chroma_height_shift;
yuvbuf->planes[2].stride = crstride;
}
//yuvbuf->cts = frame->pts;
//yuvbuf->ctsValid = true;
bool encDone;
ret = vvenc_encode(vvencoder, yuvbuf, au, &encDone);
if (ret != VVENC_OK) {
vvenc_encoder_close(vvencoder);
vvenc_YUVBuffer_free(yuvbuf, true); // release storage and payload memory
vvenc_accessUnit_free(au, true); // release storage and payload memory
return heif_error{
heif_error_Encoder_plugin_error,
heif_suberror_Encoder_encoding,
kError_unspecified_error
};
}
if (au->payloadUsedSize > 0) {
append_chunk_data(encoder, au);
}
while (!encDone) {
ret = vvenc_encode(vvencoder, nullptr, au, &encDone);
if (ret != VVENC_OK) {
vvenc_encoder_close(vvencoder);
vvenc_YUVBuffer_free(yuvbuf, true); // release storage and payload memory
vvenc_accessUnit_free(au, true); // release storage and payload memory
return heif_error{
heif_error_Encoder_plugin_error,
heif_suberror_Encoder_encoding,
kError_unspecified_error
};
}
if (au->payloadUsedSize > 0) {
append_chunk_data(encoder, au);
}
}
vvenc_encoder_close(vvencoder);
vvenc_YUVBuffer_free(yuvbuf, true); // release storage and payload memory
vvenc_accessUnit_free(au, true); // release storage and payload memory
/*
delete[] yptr;
delete[] cbptr;
delete[] crptr;
*/
return heif_error_ok;
}