Use libsvtav1 C API in Custom Video Applications
This article provides a practical guide on natively integrating the
libsvtav1 (Scalable Video Technology for AV1) C API into a
custom video processing application. We will cover the essential steps,
from initializing the encoder and configuring encoding parameters to
sending raw video frames and retrieving the compressed AV1
bitstream.
Prerequisites and Header Inclusion
To use libsvtav1 in your C/C++ project, you need to link
against the SvtAv1Enc library and include the main API
header:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "EbSvtAv1Enc.h"Step 1: Initialize the Encoder Handle and Configure Parameters
The first step is to declare the encoder handle
(EbComponentType) and the configuration structure
(EbSvtAv1EncConfiguration). You must initialize the handle
with default settings before customizing them.
EbComponentType *svt_handle = NULL;
EbSvtAv1EncConfiguration svt_config;
// 1. Initialize handle with default parameters
EbErrorType return_value = svt_av1_enc_init_handle(&svt_handle, NULL, &svt_config);
if (return_value != EB_ErrorNone) {
fprintf(stderr, "Failed to initialize SVT-AV1 handle\n");
return 1;
}
// 2. Customize video and encoding settings
svt_config.source_width = 1920;
svt_config.source_height = 1080;
svt_config.frame_rate_numerator = 30;
svt_config.frame_rate_denominator = 1;
svt_config.encoder_color_format = EB_YUV420;
svt_config.encoder_bit_depth = 8;
// Set rate control (e.g., Constant Rate Factor - CRF)
svt_config.rate_control_mode = 0; // 0 = CRF/CQP
svt_config.qp = 35; // QP value (0-63)
svt_config.enc_mode = 8; // Preset/Speed (0-13, where 13 is fastest)
// 3. Apply the updated configurations to the handle
return_value = svt_av1_enc_set_parameter(svt_handle, &svt_config);
if (return_value != EB_ErrorNone) {
fprintf(stderr, "Failed to set encoder parameters\n");
return 1;
}
// 4. Initialize the encoder instance
return_value = svt_av1_enc_init(svt_handle);
if (return_value != EB_ErrorNone) {
fprintf(stderr, "Failed to start the encoder\n");
return 1;
}Step 2: Set Up Input and Output Buffers
SVT-AV1 uses the EbBufferHeaderType structure to pass
input (raw YUV frames) and receive output (compressed AV1 packets).
// Allocate the input buffer container
EbBufferHeaderType *input_buffer = NULL;
return_value = svt_av1_to_create_input_buffer(svt_handle, &input_buffer);
// Allocate the structure containing actual raw image data
EbSvtIOFormat *input_picture = (EbSvtIOFormat*)input_buffer->p_buffer;
input_picture->width = svt_config.source_width;
input_picture->height = svt_config.source_height;
input_picture->org_x = 0;
input_picture->org_y = 0;
// Allocate memory for Y, U, and V planes
size_t luma_size = svt_config.source_width * svt_config.source_height;
size_t chroma_size = luma_size / 4; // Assuming YUV 4:2:0
input_picture->luma = (uint8_t*)malloc(luma_size);
input_picture->cb = (uint8_t*)malloc(chroma_size);
input_picture->cr = (uint8_t*)malloc(chroma_size);
input_picture->y_stride = svt_config.source_width;
input_picture->cb_stride = svt_config.source_width / 2;
input_picture->cr_stride = svt_config.source_width / 2;Step 3: The Encoding Loop (Send and Receive)
The encoding process is asynchronous. You send raw frames to the
encoder using svt_av1_enc_send_picture and retrieve encoded
packets using svt_av1_enc_get_packet.
int is_finished = 0;
EbBufferHeaderType *output_buffer = NULL;
while (!is_finished) {
// 1. Populate YUV data into input_picture->luma, cb, and cr from your video source.
// If you reach the end of your source stream, flag the buffer as End of Stream (EOS).
int has_more_frames = get_next_raw_frame(input_picture);
if (!has_more_frames) {
input_buffer->flags = EB_BUFFERFLAG_EOS;
input_buffer->n_filled_len = 0;
} else {
input_buffer->flags = 0;
input_buffer->n_filled_len = luma_size + (chroma_size * 2);
}
// 2. Send the raw frame to the encoder
svt_av1_enc_send_picture(svt_handle, input_buffer);
// 3. Retrieve available encoded packets
EbErrorType packet_status;
do {
// Non-blocking call (0 parameter means non-blocking; 1 is blocking)
packet_status = svt_av1_enc_get_packet(svt_handle, &output_buffer, 0);
if (packet_status == EB_ErrorMax) {
fprintf(stderr, "Error during encoding process\n");
break;
}
if (packet_status == EB_ErrorNone && output_buffer != NULL) {
// Process the compressed output packet
fwrite(output_buffer->p_buffer, 1, output_buffer->n_filled_len, output_file);
// Check if this was the final packet (EOS)
if (output_buffer->flags & EB_BUFFERFLAG_EOS) {
is_finished = 1;
}
// Release the output buffer back to the library
svt_av1_enc_release_out_buffer(&output_buffer);
}
} while (packet_status == EB_ErrorNone && !is_finished);
}Step 4: Deinitialization and Cleanup
Once encoding is complete, you must release all allocated resources to prevent memory leaks.
// Free allocated raw buffers
free(input_picture->luma);
free(input_picture->cb);
free(input_picture->cr);
free(input_buffer);
// Deinitialize and destroy the encoder instance
svt_av1_enc_deinit(svt_handle);
svt_av1_enc_deinit_handle(svt_handle);