gstreamer/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.c

800 lines
33 KiB
C
Raw Normal View History

/*
* Copyright(c) 2019 Intel Corporation
* Authors: Jun Tian <jun.tian@intel.com> Xavier Hallade <xavier.hallade@intel.com>
* SPDX - License - Identifier: LGPL-2.1-or-later
*/
/**
* SECTION:element-gstsvtav1enc
*
* The svtav1enc element does AV1 encoding using Scalable
* Video Technology for AV1 Encoder (SVT-AV1 Encoder).
*
* <refsect2>
* <title>Example launch line</title>
* |[
* gst-launch-1.0 -e videotestsrc ! video/x-raw ! svtav1enc ! matroskamux ! filesink location=out.mkv
* ]|
* Encodes test input into AV1 compressed data which is then packaged in out.mkv
* </refsect2>
*/
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideoencoder.h>
#include "gstsvtav1enc.h"
GST_DEBUG_CATEGORY_STATIC(gst_svtav1enc_debug_category);
#define GST_CAT_DEFAULT gst_svtav1enc_debug_category
#define GST_SVTAV1ENC_TYPE_INTRA_REFRESH_TYPE (gst_svtav1enc_intra_refresh_type_get_type())
static GType gst_svtav1enc_intra_refresh_type_get_type(void) {
static GType intra_refresh_type = 0;
static const GEnumValue intra_refresh[] = {
{SVT_AV1_FWDKF_REFRESH, "Open GOP", "CRA"},
{SVT_AV1_KF_REFRESH, "Closed GOP", "IDR"},
{0, NULL, NULL},
};
if (!intra_refresh_type) {
intra_refresh_type = g_enum_register_static("GstSvtAv1EncIntraRefreshType", intra_refresh);
}
return intra_refresh_type;
}
#define GST_SVTAV1ENC_TYPE_RATE_CONTROL_MODE (gst_svtav1enc_rate_control_mode_get_type())
static GType gst_svtav1enc_rate_control_mode_get_type(void) {
static GType rate_control_mode_type = 0;
static const GEnumValue rate_control_mode[] = {
{SVT_AV1_RC_MODE_CQP_OR_CRF,
"Constant quantization parameter/constant rate factor",
"cqp-or-crf"},
{SVT_AV1_RC_MODE_VBR, "Variable bitrate", "vbr"},
{SVT_AV1_RC_MODE_CBR, "Constant bitrate", "cbr"},
{0, NULL, NULL},
};
if (!rate_control_mode_type) {
rate_control_mode_type = g_enum_register_static("GstSvtAv1EncRateControlMode",
rate_control_mode);
}
return rate_control_mode_type;
}
/* default configuration */
static EbSvtAv1EncConfiguration default_configuration;
/* prototypes */
static void gst_svtav1enc_set_property(GObject *object, guint property_id, const GValue *value,
GParamSpec *pspec);
static void gst_svtav1enc_get_property(GObject *object, guint property_id, GValue *value,
GParamSpec *pspec);
static void gst_svtav1enc_finalize(GObject *object);
static void gst_svtav1enc_allocate_svt_buffers(GstSvtAv1Enc *svtav1enc);
static void gst_svtav1enc_deallocate_svt_buffers(GstSvtAv1Enc *svtav1enc);
static gboolean gst_svtav1enc_configure_svt(GstSvtAv1Enc *svtav1enc);
static GstFlowReturn gst_svtav1enc_encode(GstSvtAv1Enc *svtav1enc, GstVideoCodecFrame *frame);
static gboolean gst_svtav1enc_send_eos(GstSvtAv1Enc *svtav1enc);
static GstFlowReturn gst_svtav1enc_dequeue_encoded_frames(GstSvtAv1Enc *svtav1enc,
gboolean closing_encoder,
gboolean output_frames);
static gboolean gst_svtav1enc_open(GstVideoEncoder *encoder);
static gboolean gst_svtav1enc_close(GstVideoEncoder *encoder);
static gboolean gst_svtav1enc_start(GstVideoEncoder *encoder);
static gboolean gst_svtav1enc_stop(GstVideoEncoder *encoder);
static gboolean gst_svtav1enc_set_format(GstVideoEncoder *encoder, GstVideoCodecState *state);
static GstFlowReturn gst_svtav1enc_handle_frame(GstVideoEncoder *encoder,
GstVideoCodecFrame *frame);
static GstFlowReturn gst_svtav1enc_finish(GstVideoEncoder *encoder);
static gboolean gst_svtav1enc_propose_allocation(GstVideoEncoder *encoder, GstQuery *query);
static gboolean gst_svtav1enc_flush(GstVideoEncoder *encoder);
enum {
PROP_0,
PROP_PRESET,
PROP_RATE_CONTROL_MODE,
PROP_TARGET_BITRATE,
PROP_MAX_BITRATE,
PROP_MAX_QP_ALLOWED,
PROP_MIN_QP_ALLOWED,
PROP_QP,
PROP_MAXIMUM_BUFFER_SIZE,
PROP_ADAPTIVE_QUANTIZATION,
PROP_INTRA_PERIOD_LENGTH,
PROP_INTRA_REFRESH_TYPE,
PROP_LOGICAL_PROCESSORS,
PROP_TARGET_SOCKET
};
#define PROP_PRESET_DEFAULT 8
#define PROP_RATE_CONTROL_MODE_DEFAULT SVT_AV1_RC_MODE_CQP_OR_CRF
#define PROP_TARGET_BITRATE_DEFAULT 2000
#define PROP_MAX_BITRATE_DEFAULT 0
#define PROP_QP_MAX_QP_ALLOWED_DEFAULT 63
#define PROP_QP_MIN_QP_ALLOWED_DEFAULT 0
#define PROP_QP_DEFAULT 50
#define PROP_MAXIMUM_BUFFER_SIZE_DEFAULT 1000
#define PROP_ADAPTIVE_QUANTIZATION_DEFAULT 2
#define PROP_INTRA_PERIOD_LENGTH_DEFAULT -2
#define PROP_INTRA_REFRESH_TYPE_DEFAULT 1
#define PROP_LOGICAL_PROCESSORS_DEFAULT 0
#define PROP_TARGET_SOCKET_DEFAULT -1
/* pad templates */
static GstStaticPadTemplate gst_svtav1enc_sink_pad_template = GST_STATIC_PAD_TEMPLATE(
"sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS("video/x-raw, "
"format = (string) {I420, I420_10LE}, "
"width = (int) [64, 3840], "
"height = (int) [64, 2160], "
"framerate = (fraction) [0, MAX]"));
static GstStaticPadTemplate gst_svtav1enc_src_pad_template = GST_STATIC_PAD_TEMPLATE(
"src", GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS("video/x-av1, "
"stream-format = (string) obu-stream, "
"alignment = (string) tu, "
"width = (int) [64, 3840], "
"height = (int) [64, 2160], "
"framerate = (fraction) [0, MAX]"));
/* class initialization */
G_DEFINE_TYPE_WITH_CODE(GstSvtAv1Enc, gst_svtav1enc, GST_TYPE_VIDEO_ENCODER,
GST_DEBUG_CATEGORY_INIT(gst_svtav1enc_debug_category, "svtav1enc", 0,
"SVT-AV1 encoder element"));
/* this mutex is required to avoid race conditions in SVT-AV1 memory allocations, which aren't thread-safe */
G_LOCK_DEFINE_STATIC(init_mutex);
static void gst_svtav1enc_class_init(GstSvtAv1EncClass *klass) {
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
GstVideoEncoderClass *video_encoder_class = GST_VIDEO_ENCODER_CLASS(klass);
gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass),
&gst_svtav1enc_src_pad_template);
gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass),
&gst_svtav1enc_sink_pad_template);
gst_element_class_set_static_metadata(
GST_ELEMENT_CLASS(klass),
"SvtAv1Enc",
"Codec/Encoder/Video",
"Scalable Video Technology for AV1 Encoder (SVT-AV1 Encoder)",
"Jun Tian <jun.tian@intel.com> Xavier Hallade <xavier.hallade@intel.com>");
gobject_class->set_property = gst_svtav1enc_set_property;
gobject_class->get_property = gst_svtav1enc_get_property;
gobject_class->finalize = gst_svtav1enc_finalize;
video_encoder_class->open = GST_DEBUG_FUNCPTR(gst_svtav1enc_open);
video_encoder_class->close = GST_DEBUG_FUNCPTR(gst_svtav1enc_close);
video_encoder_class->start = GST_DEBUG_FUNCPTR(gst_svtav1enc_start);
video_encoder_class->stop = GST_DEBUG_FUNCPTR(gst_svtav1enc_stop);
video_encoder_class->set_format = GST_DEBUG_FUNCPTR(gst_svtav1enc_set_format);
video_encoder_class->handle_frame = GST_DEBUG_FUNCPTR(gst_svtav1enc_handle_frame);
video_encoder_class->finish = GST_DEBUG_FUNCPTR(gst_svtav1enc_finish);
video_encoder_class->propose_allocation = GST_DEBUG_FUNCPTR(gst_svtav1enc_propose_allocation);
video_encoder_class->flush = GST_DEBUG_FUNCPTR(gst_svtav1enc_flush);
g_object_class_install_property(
gobject_class,
PROP_PRESET,
g_param_spec_uint("preset",
"Preset",
"Quality vs density tradeoff point"
" that the encoding is to be performed at"
" (0 is the highest quality, 13 is the highest speed) ",
0,
13,
PROP_PRESET_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class,
PROP_RATE_CONTROL_MODE,
g_param_spec_enum("rate-control-mode",
"Rate-control mode",
"Rate Control Mode",
GST_SVTAV1ENC_TYPE_RATE_CONTROL_MODE,
PROP_RATE_CONTROL_MODE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_TARGET_BITRATE,
g_param_spec_uint("target-bitrate",
"Target bitrate",
"Target bitrate in kbits/sec. Only used when in CBR and VBR mode",
0,
100000,
PROP_TARGET_BITRATE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_MAX_BITRATE,
g_param_spec_uint("max-bitrate",
"Maximum bitrate",
"Maximum bitrate in kbits/sec. Only used when in CBQ mode",
0,
100000,
PROP_MAX_BITRATE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING));
g_object_class_install_property(
gobject_class,
PROP_MAX_QP_ALLOWED,
g_param_spec_uint("max-qp-allowed",
"Max Quantization parameter",
"Maximum QP value allowed for rate control use"
" Only used in VBR mode.",
0,
63,
PROP_MAX_QP_ALLOWED,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_MIN_QP_ALLOWED,
g_param_spec_uint("min-qp-allowed",
"Min Quantization parameter",
"Minimum QP value allowed for rate control use"
" Only used in VBR mode.",
0,
63,
PROP_MIN_QP_ALLOWED,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class,
PROP_QP,
g_param_spec_uint("qp",
"Quantization parameter",
"Quantization parameter used in CQP mode",
0,
63,
PROP_QP_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class,
PROP_MAXIMUM_BUFFER_SIZE,
g_param_spec_uint("maximum-buffer-size",
"Maximum Buffer Size",
"Maximum buffer size in milliseconds."
" Only used in CBR mode.",
20,
10000,
PROP_MAXIMUM_BUFFER_SIZE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_ADAPTIVE_QUANTIZATION,
g_param_spec_uint("adaptive-quantization",
"Adaptive Quantization",
"Adaptive quantization within a frame using segmentation.",
0,
2,
PROP_ADAPTIVE_QUANTIZATION_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_INTRA_PERIOD_LENGTH,
g_param_spec_int("intra-period-length",
"Intra Period Length",
"Period of Intra Frames insertion (-2 is auto, -1 no updates)",
-2,
G_MAXINT,
PROP_INTRA_PERIOD_LENGTH_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(gobject_class,
PROP_INTRA_REFRESH_TYPE,
g_param_spec_enum("intra-refresh-type",
"Intra refresh type",
"CRA (open GOP)"
"or IDR frames (closed GOP)",
GST_SVTAV1ENC_TYPE_INTRA_REFRESH_TYPE,
PROP_INTRA_REFRESH_TYPE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_LOGICAL_PROCESSORS,
g_param_spec_uint("logical-processors",
"Logical Processors",
"Number of logical CPU cores to be used. 0: auto",
0,
G_MAXUINT,
PROP_LOGICAL_PROCESSORS_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property(
gobject_class,
PROP_TARGET_SOCKET,
g_param_spec_int("target-socket",
"Target socket",
"Target CPU socket to run on. -1: all available",
-1,
15,
PROP_TARGET_SOCKET_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void gst_svtav1enc_init(GstSvtAv1Enc *svtav1enc) {
svtav1enc->svt_config = g_new0(EbSvtAv1EncConfiguration, 1);
memcpy(svtav1enc->svt_config, &default_configuration, sizeof(default_configuration));
}
static void gst_svtav1enc_set_property(GObject *object, guint property_id, const GValue *value,
GParamSpec *pspec) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(object);
/* TODO: support reconfiguring on the fly when possible */
if (svtav1enc->state) {
GST_ERROR_OBJECT(svtav1enc,
"encoder state has been set before properties, this isn't supported yet.");
return;
}
GST_LOG_OBJECT(svtav1enc, "setting property %u", property_id);
switch (property_id) {
case PROP_PRESET: svtav1enc->svt_config->enc_mode = g_value_get_uint(value); break;
case PROP_RATE_CONTROL_MODE:
svtav1enc->svt_config->rate_control_mode = g_value_get_enum(value);
break;
case PROP_TARGET_BITRATE:
svtav1enc->svt_config->target_bit_rate = g_value_get_uint(value) * 1000;
break;
case PROP_MAX_BITRATE:
svtav1enc->svt_config->max_bit_rate = g_value_get_uint(value) * 1000;
break;
case PROP_MAX_QP_ALLOWED:
svtav1enc->svt_config->max_qp_allowed = g_value_get_uint(value);
break;
case PROP_MIN_QP_ALLOWED:
svtav1enc->svt_config->min_qp_allowed = g_value_get_uint(value);
break;
case PROP_QP: svtav1enc->svt_config->qp = g_value_get_uint(value); break;
case PROP_MAXIMUM_BUFFER_SIZE:
svtav1enc->svt_config->maximum_buffer_size_ms = g_value_get_uint(value);
break;
case PROP_ADAPTIVE_QUANTIZATION:
svtav1enc->svt_config->enable_adaptive_quantization = g_value_get_uint(value);
break;
case PROP_INTRA_PERIOD_LENGTH:
svtav1enc->svt_config->intra_period_length = g_value_get_int(value) - 1;
break;
case PROP_INTRA_REFRESH_TYPE:
svtav1enc->svt_config->intra_refresh_type = g_value_get_enum(value);
break;
case PROP_LOGICAL_PROCESSORS:
svtav1enc->svt_config->logical_processors = g_value_get_uint(value);
break;
case PROP_TARGET_SOCKET: svtav1enc->svt_config->target_socket = g_value_get_int(value); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break;
}
}
static void gst_svtav1enc_get_property(GObject *object, guint property_id, GValue *value,
GParamSpec *pspec) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(object);
GST_LOG_OBJECT(svtav1enc, "getting property %u", property_id);
switch (property_id) {
case PROP_PRESET: g_value_set_uint(value, svtav1enc->svt_config->enc_mode); break;
case PROP_RATE_CONTROL_MODE:
g_value_set_enum(value, svtav1enc->svt_config->rate_control_mode);
break;
case PROP_TARGET_BITRATE:
g_value_set_uint(value, svtav1enc->svt_config->target_bit_rate / 1000);
break;
case PROP_MAX_BITRATE:
g_value_set_uint(value, svtav1enc->svt_config->max_bit_rate / 1000);
break;
case PROP_MAX_QP_ALLOWED: g_value_set_uint(value, svtav1enc->svt_config->max_qp_allowed); break;
case PROP_MIN_QP_ALLOWED: g_value_set_uint(value, svtav1enc->svt_config->min_qp_allowed); break;
case PROP_QP: g_value_set_uint(value, svtav1enc->svt_config->qp); break;
case PROP_MAXIMUM_BUFFER_SIZE:
g_value_set_uint(value, svtav1enc->svt_config->maximum_buffer_size_ms);
break;
case PROP_ADAPTIVE_QUANTIZATION:
g_value_set_uint(value, svtav1enc->svt_config->enable_adaptive_quantization);
break;
case PROP_INTRA_PERIOD_LENGTH:
g_value_set_int(value, svtav1enc->svt_config->intra_period_length + 1);
break;
case PROP_INTRA_REFRESH_TYPE:
g_value_set_enum(value, svtav1enc->svt_config->intra_refresh_type);
break;
case PROP_LOGICAL_PROCESSORS:
g_value_set_uint(value, svtav1enc->svt_config->logical_processors);
break;
case PROP_TARGET_SOCKET: g_value_set_int(value, svtav1enc->svt_config->target_socket); break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break;
}
}
static void gst_svtav1enc_finalize(GObject *object) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(object);
GST_DEBUG_OBJECT(svtav1enc, "finalizing svtav1enc");
g_free(svtav1enc->svt_config);
G_OBJECT_CLASS(gst_svtav1enc_parent_class)->finalize(object);
}
static void gst_svtav1enc_allocate_svt_buffers(GstSvtAv1Enc *svtav1enc) {
svtav1enc->input_buf = g_new0(EbBufferHeaderType, 1);
svtav1enc->input_buf->p_buffer = (uint8_t *)g_new0(EbSvtIOFormat, 1);
svtav1enc->input_buf->size = sizeof(EbBufferHeaderType);
svtav1enc->input_buf->p_app_private = NULL;
svtav1enc->input_buf->pic_type = EB_AV1_INVALID_PICTURE;
svtav1enc->input_buf->metadata = NULL;
}
static void gst_svtav1enc_deallocate_svt_buffers(GstSvtAv1Enc *svtav1enc) {
if (svtav1enc->input_buf) {
g_free(svtav1enc->input_buf->p_buffer);
svtav1enc->input_buf->p_buffer = NULL;
g_free(svtav1enc->input_buf);
svtav1enc->input_buf = NULL;
}
}
static gboolean gst_svtav1enc_configure_svt(GstSvtAv1Enc *svtav1enc) {
if (!svtav1enc->state) {
GST_WARNING_OBJECT(svtav1enc, "no state, can't configure encoder yet");
return FALSE;
}
/* set properties out of GstVideoInfo */
const GstVideoInfo *info = &svtav1enc->state->info;
svtav1enc->svt_config->encoder_bit_depth = GST_VIDEO_INFO_COMP_DEPTH(info, 0);
svtav1enc->svt_config->source_width = GST_VIDEO_INFO_WIDTH(info);
svtav1enc->svt_config->source_height = GST_VIDEO_INFO_HEIGHT(info);
svtav1enc->svt_config->frame_rate_numerator = GST_VIDEO_INFO_FPS_N(info) > 0
? GST_VIDEO_INFO_FPS_N(info)
: 1;
svtav1enc->svt_config->frame_rate_denominator = GST_VIDEO_INFO_FPS_D(info) > 0
? GST_VIDEO_INFO_FPS_D(info)
: 1;
GST_LOG_OBJECT(svtav1enc,
"width %d, height %d, framerate %d",
svtav1enc->svt_config->source_width,
svtav1enc->svt_config->source_height,
svtav1enc->svt_config->frame_rate_numerator /
svtav1enc->svt_config->frame_rate_denominator);
/* TODO: better handle HDR metadata when GStreamer will have such support
* https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/issues/400 */
if (GST_VIDEO_INFO_COLORIMETRY(info).matrix == GST_VIDEO_COLOR_MATRIX_BT2020 &&
GST_VIDEO_INFO_COMP_DEPTH(info, 0) > 8) {
svtav1enc->svt_config->high_dynamic_range_input = TRUE;
}
EbErrorType res = svt_av1_enc_set_parameter(svtav1enc->svt_encoder, svtav1enc->svt_config);
if (res != EB_ErrorNone) {
GST_ELEMENT_ERROR(svtav1enc,
LIBRARY,
INIT,
(NULL),
("svt_av1_enc_set_parameter failed with error %d", res));
return FALSE;
}
return TRUE;
}
static gboolean gst_svtav1enc_start_svt(GstSvtAv1Enc *svtav1enc) {
G_LOCK(init_mutex);
EbErrorType res = svt_av1_enc_init(svtav1enc->svt_encoder);
G_UNLOCK(init_mutex);
if (res != EB_ErrorNone) {
GST_ELEMENT_ERROR(
svtav1enc, LIBRARY, INIT, (NULL), ("svt_av1_enc_init failed with error %d", res));
return FALSE;
}
return TRUE;
}
static GstFlowReturn gst_svtav1enc_encode(GstSvtAv1Enc *svtav1enc, GstVideoCodecFrame *frame) {
GstFlowReturn ret = GST_FLOW_OK;
EbErrorType res = EB_ErrorNone;
EbBufferHeaderType *input_buffer = svtav1enc->input_buf;
EbSvtIOFormat *input_picture_buffer = (EbSvtIOFormat *)svtav1enc->input_buf->p_buffer;
GstVideoFrame video_frame;
if (!gst_video_frame_map(
&video_frame, &svtav1enc->state->info, frame->input_buffer, GST_MAP_READ)) {
GST_ELEMENT_ERROR(svtav1enc, LIBRARY, ENCODE, (NULL), ("couldn't map input frame"));
return GST_FLOW_ERROR;
}
input_picture_buffer->y_stride = GST_VIDEO_FRAME_COMP_STRIDE(&video_frame, 0) /
GST_VIDEO_FRAME_COMP_PSTRIDE(&video_frame, 0);
input_picture_buffer->cb_stride = GST_VIDEO_FRAME_COMP_STRIDE(&video_frame, 1) /
GST_VIDEO_FRAME_COMP_PSTRIDE(&video_frame, 1);
input_picture_buffer->cr_stride = GST_VIDEO_FRAME_COMP_STRIDE(&video_frame, 2) /
GST_VIDEO_FRAME_COMP_PSTRIDE(&video_frame, 2);
input_picture_buffer->luma = GST_VIDEO_FRAME_PLANE_DATA(&video_frame, 0);
input_picture_buffer->cb = GST_VIDEO_FRAME_PLANE_DATA(&video_frame, 1);
input_picture_buffer->cr = GST_VIDEO_FRAME_PLANE_DATA(&video_frame, 2);
input_buffer->n_filled_len = GST_VIDEO_FRAME_SIZE(&video_frame);
/* Fill in Buffers Header control data */
input_buffer->flags = 0;
input_buffer->p_app_private = NULL;
input_buffer->pts = frame->pts;
input_buffer->pic_type = EB_AV1_INVALID_PICTURE;
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME(frame)) {
input_buffer->pic_type = EB_AV1_KEY_PICTURE;
}
input_buffer->metadata = NULL;
res = svt_av1_enc_send_picture(svtav1enc->svt_encoder, input_buffer);
if (res != EB_ErrorNone) {
GST_ELEMENT_ERROR(
svtav1enc, LIBRARY, ENCODE, (NULL), ("error in sending picture to encoder"));
ret = GST_FLOW_ERROR;
}
gst_video_frame_unmap(&video_frame);
return ret;
}
static gboolean gst_svtav1enc_send_eos(GstSvtAv1Enc *svtav1enc) {
EbErrorType ret = EB_ErrorNone;
EbBufferHeaderType input_buffer;
input_buffer.n_alloc_len = 0;
input_buffer.n_filled_len = 0;
input_buffer.n_tick_count = 0;
input_buffer.p_app_private = NULL;
input_buffer.flags = EB_BUFFERFLAG_EOS;
input_buffer.p_buffer = NULL;
input_buffer.metadata = NULL;
ret = svt_av1_enc_send_picture(svtav1enc->svt_encoder, &input_buffer);
if (ret != EB_ErrorNone) {
GST_ELEMENT_ERROR(svtav1enc, LIBRARY, ENCODE, (NULL), ("couldn't send EOS frame."));
return FALSE;
}
return (ret == EB_ErrorNone);
}
static gboolean gst_svtav1enc_flush(GstVideoEncoder *encoder) {
GstFlowReturn ret = gst_svtav1enc_dequeue_encoded_frames(GST_SVTAV1ENC(encoder), TRUE, FALSE);
return (ret != GST_FLOW_ERROR);
}
static GstFlowReturn gst_svtav1enc_dequeue_encoded_frames(GstSvtAv1Enc *svtav1enc,
gboolean done_sending_pics,
gboolean output_frames) {
GstFlowReturn ret = GST_FLOW_OK;
EbErrorType res = EB_ErrorNone;
gboolean encode_at_eos = FALSE;
do {
GstVideoCodecFrame *frame = NULL;
EbBufferHeaderType *output_buf = NULL;
res = svt_av1_enc_get_packet(svtav1enc->svt_encoder, &output_buf, done_sending_pics);
if (output_buf != NULL)
encode_at_eos = ((output_buf->flags & EB_BUFFERFLAG_EOS) == EB_BUFFERFLAG_EOS);
if (res == EB_ErrorMax) {
GST_ELEMENT_ERROR(svtav1enc, LIBRARY, ENCODE, (NULL), ("encode failed"));
return GST_FLOW_ERROR;
} else if (res != EB_NoErrorEmptyQueue && output_frames && output_buf) {
// AV1 has no frame re-ordering so always get the oldest frame
frame = gst_video_encoder_get_oldest_frame(GST_VIDEO_ENCODER(svtav1enc));
if (output_buf->pic_type == EB_AV1_KEY_PICTURE ||
output_buf->pic_type == EB_AV1_INTRA_ONLY_PICTURE) {
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT(frame);
}
if ((ret = gst_video_encoder_allocate_output_frame(
GST_VIDEO_ENCODER(svtav1enc), frame, output_buf->n_filled_len)) !=
GST_FLOW_OK) {
svt_av1_enc_release_out_buffer(&output_buf);
gst_video_codec_frame_unref(frame);
return ret;
}
gst_buffer_fill(
frame->output_buffer, 0, output_buf->p_buffer, output_buf->n_filled_len);
frame->pts = frame->output_buffer->pts = output_buf->pts;
GST_LOG_OBJECT(svtav1enc,
"#frame:%u pts:%" G_GINT64_FORMAT " SliceType:%d\n",
frame->system_frame_number,
(frame->pts),
output_buf->pic_type);
svt_av1_enc_release_out_buffer(&output_buf);
output_buf = NULL;
ret = gst_video_encoder_finish_frame(GST_VIDEO_ENCODER(svtav1enc), frame);
}
} while (res == EB_ErrorNone && !encode_at_eos && ret == GST_FLOW_OK);
return ret;
}
static gboolean gst_svtav1enc_open(GstVideoEncoder *encoder) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
/* don't overwrite the application's configuration */
EbSvtAv1EncConfiguration svt_config;
GST_DEBUG_OBJECT(svtav1enc, "open");
EbErrorType res = svt_av1_enc_init_handle(&svtav1enc->svt_encoder, NULL, &svt_config);
if (res != EB_ErrorNone) {
GST_ELEMENT_ERROR(svtav1enc,
LIBRARY,
INIT,
(NULL),
("svt_av1_enc_init_handle failed with error %d", res));
return FALSE;
}
return TRUE;
}
static gboolean gst_svtav1enc_close(GstVideoEncoder *encoder) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GST_DEBUG_OBJECT(svtav1enc, "close");
svt_av1_enc_deinit_handle(svtav1enc->svt_encoder);
svtav1enc->svt_encoder = NULL;
return TRUE;
}
static gboolean gst_svtav1enc_start(GstVideoEncoder *encoder) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GST_DEBUG_OBJECT(svtav1enc, "start");
gst_svtav1enc_allocate_svt_buffers(svtav1enc);
return TRUE;
}
static gboolean gst_svtav1enc_stop(GstVideoEncoder *encoder) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GST_DEBUG_OBJECT(svtav1enc, "stop");
if (svtav1enc->state)
gst_video_codec_state_unref(svtav1enc->state);
svtav1enc->state = NULL;
svt_av1_enc_deinit(svtav1enc->svt_encoder);
gst_svtav1enc_deallocate_svt_buffers(svtav1enc);
return TRUE;
}
static gboolean gst_svtav1enc_set_format(GstVideoEncoder *encoder, GstVideoCodecState *state) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GstClockTime min_latency_frames = 0;
GstCaps *src_caps = NULL;
GstVideoCodecState *output_state;
GST_DEBUG_OBJECT(svtav1enc, "set_format");
if (svtav1enc->state) {
gst_video_codec_state_unref(svtav1enc->state);
svt_av1_enc_deinit(svtav1enc->svt_encoder);
}
svtav1enc->state = gst_video_codec_state_ref(state);
if (!gst_svtav1enc_configure_svt(svtav1enc))
return FALSE;
if (!gst_svtav1enc_start_svt(svtav1enc))
return FALSE;
uint32_t fps = svtav1enc->svt_config->frame_rate_numerator /
svtav1enc->svt_config->frame_rate_denominator;
fps = fps > 120 ? 120 : fps;
fps = fps < 24 ? 24 : fps;
min_latency_frames = ((fps * 5) >> 2);
gst_video_encoder_set_latency(encoder,
min_latency_frames * GST_SECOND /
(svtav1enc->svt_config->frame_rate_numerator /
svtav1enc->svt_config->frame_rate_denominator),
-1);
src_caps = gst_static_pad_template_get_caps(&gst_svtav1enc_src_pad_template);
output_state = gst_video_encoder_set_output_state(
GST_VIDEO_ENCODER(encoder), src_caps, svtav1enc->state);
gst_caps_unref(src_caps);
gst_video_codec_state_unref(output_state);
GST_DEBUG_OBJECT(svtav1enc, "output caps: %" GST_PTR_FORMAT, svtav1enc->state->caps);
return gst_video_encoder_negotiate(encoder);
}
static GstFlowReturn gst_svtav1enc_handle_frame(GstVideoEncoder *encoder,
GstVideoCodecFrame *frame) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GstFlowReturn ret = GST_FLOW_OK;
GST_DEBUG_OBJECT(svtav1enc, "handle_frame");
ret = gst_svtav1enc_encode(svtav1enc, frame);
gst_video_codec_frame_unref(frame);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT(svtav1enc, "gst_svtav1enc_encode returned %d", ret);
return ret;
}
return gst_svtav1enc_dequeue_encoded_frames(svtav1enc, FALSE, TRUE);
}
static GstFlowReturn gst_svtav1enc_finish(GstVideoEncoder *encoder) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GST_DEBUG_OBJECT(svtav1enc, "finish");
gst_svtav1enc_send_eos(svtav1enc);
return gst_svtav1enc_dequeue_encoded_frames(svtav1enc, TRUE, TRUE);
}
static gboolean gst_svtav1enc_propose_allocation(GstVideoEncoder *encoder, GstQuery *query) {
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder);
GST_DEBUG_OBJECT(svtav1enc, "propose_allocation");
gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
return GST_VIDEO_ENCODER_CLASS(gst_svtav1enc_parent_class)->propose_allocation(encoder, query);
}
static gboolean plugin_init(GstPlugin *plugin) {
EbComponentType *handle = NULL;
EbErrorType res;
res = svt_av1_enc_init_handle(&handle, NULL, &default_configuration);
if (res != EB_ErrorNone) {
GST_ERROR("Failed to initialize SVT AV1 handle");
return FALSE;
}
svt_av1_enc_deinit(handle);
return gst_element_register(plugin, "svtav1enc", GST_RANK_SECONDARY, GST_TYPE_SVTAV1ENC);
}
#ifndef VERSION
#define VERSION "1.0"
#endif
#ifndef PACKAGE
#define PACKAGE "gstreamer-svt-av1"
#endif
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "SVT-AV1 Encoder plugin for GStreamer"
#endif
#ifndef GST_PACKAGE_ORIGIN
#define GST_PACKAGE_ORIGIN "https://gitlab.com/AOMediaCodec"
#endif
GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, svtav1enc,
"Scalable Video Technology for AV1 Encoder (SVT-AV1 Encoder)", plugin_init,
VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)