libheif/plugins/encoder_svt.cc (663 lines of code) (raw):
/*
* HEIF codec.
* Copyright (c) 2022 Dirk Farin <dirk.farin@gmail.com>
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libheif/heif.h"
#include "libheif/heif_plugin.h"
#include "encoder_svt.h"
#include <vector>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <memory>
#include "svt-av1/EbSvtAv1.h"
#include "svt-av1/EbSvtAv1Enc.h"
struct encoder_struct_svt
{
int speed = 12; // 0-13
int quality;
int min_q = 0;
int max_q = 63;
int qp = -1;
bool qp_set = false;
int threads = 4;
int tile_rows = 1; // 1,2,4,8,16,32,64
int tile_cols = 1; // 1,2,4,8,16,32,64
heif_chroma chroma = heif_chroma_420;
// --- output
std::vector<uint8_t> compressed_data;
bool data_read = false;
};
//static const char* kError_out_of_memory = "Out of memory";
static const char* kParam_min_q = "min-q";
static const char* kParam_max_q = "max-q";
static const char* kParam_qp = "qp";
static const char* kParam_threads = "threads";
static const char* kParam_speed = "speed";
static const char* kParam_chroma = "chroma";
static const char* const kParam_chroma_valid_values[] = {
"420", "422", "444", nullptr
};
static int valid_tile_num_values[] = {1, 2, 4, 8, 16, 32, 64};
static struct heif_error heif_error_codec_library_error = {heif_error_Encoder_plugin_error,
heif_suberror_Unspecified,
"SVT-AV1 error"};
static const int SVT_PLUGIN_PRIORITY = 40;
#define MAX_PLUGIN_NAME_LENGTH 80
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
static void svt_set_default_parameters(void* encoder);
static const char* svt_plugin_name()
{
plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "SVT-AV1 encoder %s", svt_av1_get_version());
return plugin_name;
}
int int_log2(int pow2_value)
{
int input_value = pow2_value;
(void) input_value;
int v = 0;
while (pow2_value > 1) {
pow2_value >>= 1;
v++;
}
// check that computation is correct
assert(input_value == (1 << v));
return v;
}
#define MAX_NPARAMETERS 10
static struct heif_encoder_parameter svt_encoder_params[MAX_NPARAMETERS];
static const struct heif_encoder_parameter* svt_encoder_parameter_ptrs[MAX_NPARAMETERS + 1];
static void svt_init_parameters()
{
struct heif_encoder_parameter* p = svt_encoder_params;
const struct heif_encoder_parameter** d = svt_encoder_parameter_ptrs;
int i = 0;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_speed;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 12;
p->has_default = true;
p->integer.have_minimum_maximum = true;
p->integer.minimum = 0;
p->integer.maximum = 13;
p->integer.valid_values = nullptr;
p->integer.num_valid_values = 0;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_threads;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 4;
p->has_default = true;
p->integer.have_minimum_maximum = true;
p->integer.minimum = 1;
p->integer.maximum = 16;
p->integer.valid_values = nullptr;
p->integer.num_valid_values = 0;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = "tile-rows";
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 4;
p->has_default = true;
p->integer.have_minimum_maximum = false;
p->integer.valid_values = valid_tile_num_values;
p->integer.num_valid_values = 7;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = "tile-cols";
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 4;
p->has_default = true;
p->integer.have_minimum_maximum = false;
p->integer.valid_values = valid_tile_num_values;
p->integer.num_valid_values = 7;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = heif_encoder_parameter_name_quality;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 50;
p->has_default = true;
p->integer.have_minimum_maximum = true;
p->integer.minimum = 0;
p->integer.maximum = 100;
p->integer.valid_values = NULL;
p->integer.num_valid_values = 0;
d[i++] = p++;
/*
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = heif_encoder_parameter_name_lossless;
p->type = heif_encoder_parameter_type_boolean;
p->boolean.default_value = false;
p->has_default = true;
d[i++] = p++;
*/
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_chroma;
p->type = heif_encoder_parameter_type_string;
p->string.default_value = "420";
p->has_default = true;
p->string.valid_values = kParam_chroma_valid_values;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_qp;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 50;
p->has_default = false;
p->integer.have_minimum_maximum = true;
p->integer.minimum = 0;
p->integer.maximum = 63;
p->integer.valid_values = nullptr;
p->integer.num_valid_values = 0;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_min_q;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 0;
p->has_default = true;
p->integer.have_minimum_maximum = true;
p->integer.minimum = 0;
p->integer.maximum = 63;
p->integer.valid_values = nullptr;
p->integer.num_valid_values = 0;
d[i++] = p++;
assert(i < MAX_NPARAMETERS);
p->version = 2;
p->name = kParam_max_q;
p->type = heif_encoder_parameter_type_integer;
p->integer.default_value = 63;
p->has_default = true;
p->integer.have_minimum_maximum = true;
p->integer.minimum = 0;
p->integer.maximum = 63;
p->integer.valid_values = nullptr;
p->integer.num_valid_values = 0;
d[i++] = p++;
d[i++] = nullptr;
}
const struct heif_encoder_parameter** svt_list_parameters(void* encoder)
{
return svt_encoder_parameter_ptrs;
}
static void svt_init_plugin()
{
svt_init_parameters();
}
static void svt_cleanup_plugin()
{
}
struct heif_error svt_new_encoder(void** enc)
{
auto* encoder = new encoder_struct_svt();
struct heif_error err = heif_error_ok;
*enc = encoder;
// set default parameters
svt_set_default_parameters(encoder);
return err;
}
void svt_free_encoder(void* encoder_raw)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
delete encoder;
}
struct heif_error svt_set_parameter_quality(void* encoder_raw, int quality)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (quality < 0 || quality > 100) {
return heif_error_invalid_parameter_value;
}
encoder->quality = quality;
return heif_error_ok;
}
struct heif_error svt_get_parameter_quality(void* encoder_raw, int* quality)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
*quality = encoder->quality;
return heif_error_ok;
}
struct heif_error svt_set_parameter_lossless(void* encoder_raw, int enable)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (enable) {
encoder->min_q = 0;
encoder->max_q = 0;
encoder->qp = 0;
encoder->qp_set = true;
encoder->quality = 100; // not really required, but to be consistent
}
return heif_error_ok;
}
struct heif_error svt_get_parameter_lossless(void* encoder_raw, int* enable)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
*enable = (encoder->min_q == 0 && encoder->max_q == 0 &&
((encoder->qp_set && encoder->qp == 0) || encoder->quality == 100));
return heif_error_ok;
}
struct heif_error svt_set_parameter_logging_level(void* encoder_raw, int logging)
{
#if 0
struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
if (logging<0 || logging>4) {
return heif_error_invalid_parameter_value;
}
encoder->logLevel = logging;
#endif
return heif_error_ok;
}
struct heif_error svt_get_parameter_logging_level(void* encoder_raw, int* loglevel)
{
#if 0
struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
*loglevel = encoder->logLevel;
#else
*loglevel = 0;
#endif
return heif_error_ok;
}
#define set_value(paramname, paramvar) if (strcmp(name, paramname)==0) { encoder->paramvar = value; return heif_error_ok; }
#define get_value(paramname, paramvar) if (strcmp(name, paramname)==0) { *value = encoder->paramvar; return heif_error_ok; }
struct heif_error svt_set_parameter_integer(void* encoder_raw, const char* name, int value)
{
struct encoder_struct_svt* encoder = (struct encoder_struct_svt*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
return svt_set_parameter_quality(encoder, value);
}
else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
return svt_set_parameter_lossless(encoder, value);
}
else if (strcmp(name, kParam_qp) == 0) {
encoder->qp = value;
encoder->qp_set = true;
return heif_error_ok;
}
set_value(kParam_min_q, min_q);
set_value(kParam_max_q, max_q);
set_value(kParam_threads, threads);
set_value(kParam_speed, speed);
set_value("tile-rows", tile_rows);
set_value("tile-cols", tile_cols);
return heif_error_unsupported_parameter;
}
struct heif_error svt_get_parameter_integer(void* encoder_raw, const char* name, int* value)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
return svt_get_parameter_quality(encoder, value);
}
else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
return svt_get_parameter_lossless(encoder, value);
}
get_value(kParam_min_q, min_q);
get_value(kParam_max_q, max_q);
get_value(kParam_qp, qp); // TODO: what if qp was not set ?
get_value(kParam_threads, threads);
get_value(kParam_speed, speed);
get_value("tile-rows", tile_rows);
get_value("tile-cols", tile_cols);
return heif_error_unsupported_parameter;
}
struct heif_error svt_set_parameter_boolean(void* encoder_raw, const char* name, int value)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
return svt_set_parameter_lossless(encoder, value);
}
//set_value(kParam_realtime, realtime_mode);
return heif_error_unsupported_parameter;
}
struct heif_error svt_get_parameter_boolean(void* encoder_raw, const char* name, int* value)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
return svt_get_parameter_lossless(encoder, value);
}
//get_value(kParam_realtime, realtime_mode);
return heif_error_unsupported_parameter;
}
struct heif_error svt_set_parameter_string(void* encoder_raw, const char* name, const char* value)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (strcmp(name, kParam_chroma) == 0) {
if (strcmp(value, "420") == 0) {
encoder->chroma = heif_chroma_420;
return heif_error_ok;
}
else if (strcmp(value, "422") == 0) {
encoder->chroma = heif_chroma_422;
return heif_error_ok;
}
else if (strcmp(value, "444") == 0) {
encoder->chroma = heif_chroma_444;
return heif_error_ok;
}
else {
return heif_error_invalid_parameter_value;
}
}
return heif_error_unsupported_parameter;
}
static void save_strcpy(char* dst, int dst_size, const char* src)
{
strncpy(dst, src, dst_size - 1);
dst[dst_size - 1] = 0;
}
struct heif_error svt_get_parameter_string(void* encoder_raw, const char* name,
char* value, int value_size)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (strcmp(name, kParam_chroma) == 0) {
switch (encoder->chroma) {
case heif_chroma_420:
save_strcpy(value, value_size, "420");
break;
case heif_chroma_422:
save_strcpy(value, value_size, "422");
break;
case heif_chroma_444:
save_strcpy(value, value_size, "444");
break;
default:
assert(false);
return heif_error_invalid_parameter_value;
}
return heif_error_ok;
}
return heif_error_unsupported_parameter;
}
static void svt_set_default_parameters(void* encoder)
{
for (const struct heif_encoder_parameter** p = svt_encoder_parameter_ptrs; *p; p++) {
const struct heif_encoder_parameter* param = *p;
if (param->has_default) {
switch (param->type) {
case heif_encoder_parameter_type_integer:
svt_set_parameter_integer(encoder, param->name, param->integer.default_value);
break;
case heif_encoder_parameter_type_boolean:
svt_set_parameter_boolean(encoder, param->name, param->boolean.default_value);
break;
case heif_encoder_parameter_type_string:
// NOLINTNEXTLINE(build/include_what_you_use)
svt_set_parameter_string(encoder, param->name, param->string.default_value);
break;
}
}
}
}
void svt_query_input_colorspace(heif_colorspace* colorspace, heif_chroma* chroma)
{
*colorspace = heif_colorspace_YCbCr;
*chroma = heif_chroma_420;
}
void svt_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace, heif_chroma* chroma)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
*colorspace = heif_colorspace_YCbCr;
*chroma = encoder->chroma;
}
void svt_query_encoded_size(void* encoder_raw, uint32_t input_width, uint32_t input_height,
uint32_t* encoded_width, uint32_t* encoded_height)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
// SVT-AV1 (as of version 1.2.1) can only create image sizes matching the chroma format. Add padding if necessary.
if (input_width < 64) {
*encoded_width = 64;
}
else if (encoder->chroma == heif_chroma_420 && (input_width & 1) == 1) {
*encoded_width = input_width + 1;
}
else {
*encoded_width = input_width;
}
if (input_height < 64) {
*encoded_height = 64;
}
else if (encoder->chroma != heif_chroma_444 && (input_height & 1) == 1) {
*encoded_height = input_height + 1;
}
else {
*encoded_height = input_height;
}
}
struct heif_error svt_encode_image(void* encoder_raw, const struct heif_image* image,
heif_image_input_class input_class)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
EbErrorType res = EB_ErrorNone;
encoder->compressed_data.clear();
int w = heif_image_get_width(image, heif_channel_Y);
int h = heif_image_get_height(image, heif_channel_Y);
uint32_t encoded_width, encoded_height;
svt_query_encoded_size(encoder_raw, w, h, &encoded_width, &encoded_height);
// 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.
heif_error err = heif_image_extend_padding_to_size(const_cast<struct heif_image*>(image),
(int) encoded_width,
(int) encoded_height);
if (err.code) {
return err;
}
const heif_chroma chroma = heif_image_get_chroma_format(image);
int bitdepth_y = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
uint8_t yShift = 0;
EbColorFormat color_format = EB_YUV420;
if (input_class == heif_image_input_class_alpha) {
color_format = EB_YUV420;
//chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN;
yShift = 1;
}
else {
switch (chroma) {
case heif_chroma_444:
color_format = EB_YUV444;
//chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
break;
case heif_chroma_422:
color_format = EB_YUV422;
//chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
break;
case heif_chroma_420:
color_format = EB_YUV420;
//chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN; // TODO: set to CENTER when AV1 and svt supports this
yShift = 1;
break;
default:
return heif_error_codec_library_error;
}
}
// --- initialize the encoder
EbComponentType* svt_encoder = nullptr;
EbSvtAv1EncConfiguration svt_config;
memset(&svt_config, 0, sizeof(EbSvtAv1EncConfiguration));
res = svt_av1_enc_init_handle(&svt_encoder, nullptr, &svt_config);
if (res != EB_ErrorNone) {
//goto cleanup;
return heif_error_codec_library_error;
}
svt_config.encoder_color_format = color_format;
svt_config.encoder_bit_depth = (uint8_t) bitdepth_y;
//svt_config.is_16bit_pipeline = bitdepth_y > 8;
struct heif_color_profile_nclx* nclx = nullptr;
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 (nclx) {
svt_config.color_description_present_flag = true;
#if SVT_AV1_VERSION_MAJOR >= 1
svt_config.color_primaries = static_cast<EbColorPrimaries>(nclx->color_primaries);
svt_config.transfer_characteristics = static_cast<EbTransferCharacteristics>(nclx->transfer_characteristics);
svt_config.matrix_coefficients = static_cast<EbMatrixCoefficients>(nclx->matrix_coefficients);
svt_config.color_range = nclx->full_range_flag ? EB_CR_FULL_RANGE : EB_CR_STUDIO_RANGE;
#else
svt_config.color_primaries = static_cast<uint8_t>(nclx->color_primaries);
svt_config.transfer_characteristics = static_cast<uint8_t>(nclx->transfer_characteristics);
svt_config.matrix_coefficients = static_cast<uint8_t>(nclx->matrix_coefficients);
svt_config.color_range = nclx->full_range_flag ? 1 : 0;
#endif
// Follow comment in svt header: set if input is HDR10 BT2020 using SMPTE ST2084.
svt_config.high_dynamic_range_input = (bitdepth_y == 10 && // TODO: should this be >8 ?
nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0 &&
nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance);
}
else {
svt_config.color_description_present_flag = false;
}
svt_config.source_width = encoded_width;
svt_config.source_height = encoded_height;
svt_config.logical_processors = encoder->threads;
// disable 2-pass
svt_config.rc_stats_buffer = SvtAv1FixedBuf {nullptr, 0};
svt_config.rate_control_mode = 0; // constant rate factor
//svt_config.enable_adaptive_quantization = 0; // 2 is CRF (the default), 0 would be CQP
int qp;
if (encoder->qp_set) {
qp = encoder->qp;
}
else {
qp = ((100 - encoder->quality) * 63 + 50) / 100;
}
svt_config.qp = qp;
svt_config.min_qp_allowed = encoder->min_q;
svt_config.max_qp_allowed = encoder->max_q;
svt_config.tile_rows = int_log2(encoder->tile_rows);
svt_config.tile_columns = int_log2(encoder->tile_cols);
svt_config.enc_mode = (int8_t) encoder->speed;
if (color_format == EB_YUV422 || bitdepth_y > 10) {
svt_config.profile = PROFESSIONAL_PROFILE;
}
else if (color_format == EB_YUV444) {
svt_config.profile = HIGH_PROFILE;
}
res = svt_av1_enc_set_parameter(svt_encoder, &svt_config);
if (res == EB_ErrorBadParameter) {
svt_av1_enc_deinit(svt_encoder);
svt_av1_enc_deinit_handle(svt_encoder);
return heif_error_codec_library_error;
}
res = svt_av1_enc_init(svt_encoder);
if (res != EB_ErrorNone) {
svt_av1_enc_deinit(svt_encoder);
svt_av1_enc_deinit_handle(svt_encoder);
return heif_error_codec_library_error;
}
// --- copy libheif image to svt image
EbBufferHeaderType input_buffer;
input_buffer.p_buffer = (uint8_t*) (new EbSvtIOFormat());
memset(input_buffer.p_buffer, 0, sizeof(EbSvtIOFormat));
input_buffer.size = sizeof(EbBufferHeaderType);
input_buffer.p_app_private = nullptr;
input_buffer.pic_type = EB_AV1_INVALID_PICTURE;
input_buffer.metadata = nullptr;
auto* input_picture_buffer = (EbSvtIOFormat*) input_buffer.p_buffer;
int bytesPerPixel = bitdepth_y > 8 ? 2 : 1;
if (input_class == heif_image_input_class_alpha) {
int stride;
input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
input_picture_buffer->y_stride = stride / bytesPerPixel;
input_buffer.n_filled_len = stride * encoded_height;
}
else {
int stride;
input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
input_picture_buffer->y_stride = stride / bytesPerPixel;
input_buffer.n_filled_len = stride * encoded_height;
uint32_t uvHeight = (h + yShift) >> yShift;
input_picture_buffer->cb = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Cb, &stride);
input_buffer.n_filled_len += stride * uvHeight;
input_picture_buffer->cb_stride = stride / bytesPerPixel;
input_picture_buffer->cr = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Cr, &stride);
input_buffer.n_filled_len += stride * uvHeight;
input_picture_buffer->cr_stride = stride / bytesPerPixel;
}
input_buffer.flags = 0;
input_buffer.pts = 0;
EbAv1PictureType frame_type = EB_AV1_KEY_PICTURE;
input_buffer.pic_type = frame_type;
res = svt_av1_enc_send_picture(svt_encoder, &input_buffer);
if (res != EB_ErrorNone) {
delete input_buffer.p_buffer;
svt_av1_enc_deinit(svt_encoder);
svt_av1_enc_deinit_handle(svt_encoder);
return heif_error_codec_library_error;
}
// --- flush encoder
EbErrorType ret = EB_ErrorNone;
EbBufferHeaderType flush_input_buffer;
flush_input_buffer.n_alloc_len = 0;
flush_input_buffer.n_filled_len = 0;
flush_input_buffer.n_tick_count = 0;
flush_input_buffer.p_app_private = nullptr;
flush_input_buffer.flags = EB_BUFFERFLAG_EOS;
flush_input_buffer.p_buffer = nullptr;
flush_input_buffer.metadata = nullptr;
ret = svt_av1_enc_send_picture(svt_encoder, &flush_input_buffer);
if (ret != EB_ErrorNone) {
delete input_buffer.p_buffer;
svt_av1_enc_deinit(svt_encoder);
svt_av1_enc_deinit_handle(svt_encoder);
return heif_error_codec_library_error;
}
// --- read compressed picture
int encode_at_eos = 0;
uint8_t done_sending_pics = true;
do {
EbBufferHeaderType* output_buf = nullptr;
res = svt_av1_enc_get_packet(svt_encoder, &output_buf, (uint8_t) done_sending_pics);
if (output_buf != nullptr) {
encode_at_eos = ((output_buf->flags & EB_BUFFERFLAG_EOS) == EB_BUFFERFLAG_EOS);
if (output_buf->p_buffer && (output_buf->n_filled_len > 0)) {
uint8_t* data = output_buf->p_buffer;
uint32_t n = output_buf->n_filled_len;
size_t oldSize = encoder->compressed_data.size();
encoder->compressed_data.resize(oldSize + n);
memcpy(encoder->compressed_data.data() + oldSize, data, n);
encoder->data_read = false;
// (output_buf->pic_type == EB_AV1_KEY_PICTURE));
}
svt_av1_enc_release_out_buffer(&output_buf);
}
} while (res == EB_ErrorNone && !encode_at_eos);
delete input_buffer.p_buffer;
svt_av1_enc_deinit(svt_encoder);
svt_av1_enc_deinit_handle(svt_encoder);
if (!done_sending_pics && ((res == EB_ErrorNone) || (res == EB_NoErrorEmptyQueue))) {
return heif_error_ok;
}
else {
return (res == EB_ErrorNone ? heif_error_ok : heif_error_codec_library_error);
}
}
struct heif_error svt_get_compressed_data(void* encoder_raw, uint8_t** data, int* size,
enum heif_encoded_data_type* type)
{
auto* encoder = (struct encoder_struct_svt*) encoder_raw;
if (encoder->data_read) {
*data = nullptr;
*size = 0;
}
else {
*data = encoder->compressed_data.data();
*size = (int) encoder->compressed_data.size();
encoder->data_read = true;
}
return heif_error_ok;
}
static const struct heif_encoder_plugin encoder_plugin_svt
{
/* plugin_api_version */ 3,
/* compression_format */ heif_compression_AV1,
/* id_name */ "svt",
/* priority */ SVT_PLUGIN_PRIORITY,
/* supports_lossy_compression */ true,
/* supports_lossless_compression */ false,
/* get_plugin_name */ svt_plugin_name,
/* init_plugin */ svt_init_plugin,
/* cleanup_plugin */ svt_cleanup_plugin,
/* new_encoder */ svt_new_encoder,
/* free_encoder */ svt_free_encoder,
/* set_parameter_quality */ svt_set_parameter_quality,
/* get_parameter_quality */ svt_get_parameter_quality,
/* set_parameter_lossless */ svt_set_parameter_lossless,
/* get_parameter_lossless */ svt_get_parameter_lossless,
/* set_parameter_logging_level */ svt_set_parameter_logging_level,
/* get_parameter_logging_level */ svt_get_parameter_logging_level,
/* list_parameters */ svt_list_parameters,
/* set_parameter_integer */ svt_set_parameter_integer,
/* get_parameter_integer */ svt_get_parameter_integer,
/* set_parameter_boolean */ svt_set_parameter_boolean,
/* get_parameter_boolean */ svt_get_parameter_boolean,
/* set_parameter_string */ svt_set_parameter_string,
/* get_parameter_string */ svt_get_parameter_string,
/* query_input_colorspace */ svt_query_input_colorspace,
/* encode_image */ svt_encode_image,
/* get_compressed_data */ svt_get_compressed_data,
/* query_input_colorspace (v2) */ svt_query_input_colorspace2,
/* query_encoded_size (v3) */ svt_query_encoded_size
};
const struct heif_encoder_plugin* get_encoder_plugin_svt()
{
return &encoder_plugin_svt;
}
#if PLUGIN_SvtEnc
heif_plugin_info plugin_info{
1,
heif_plugin_type_encoder,
&encoder_plugin_svt
};
#endif