mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-01 17:31:10 +00:00
Remove dead code Address some snake_case PascalCase violations for variables, functions, enums and types Rename lcu fields to sb Rename tu fields to txb Rename cu fields to blk Shorten variable names in prep to apply clang format: e.g. - picture_control_set_ptr -- > pcs_ptr - sequence_control_set_ptr -- > scs_ptr Remove RawStringFormats property from clang-format until it's fixed Apply clang-format on files in source/lib/encoder/codec/ except: - aom_dsp_rtcd.c - aom_dsp_rtcd.h - EbMdRateEstimation.h - EbIntraPrediction.c - EbModeDecisionConfiguration.c - EbMotionEstimation.h - EbPictureDecisionProcess.c - EbRateControlTables.c - EbQMatrices.h - EbEncHandle.c
1111 lines
38 KiB
C
1111 lines
38 KiB
C
/*
|
|
* 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
|
|
|
|
/* 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_dispose (GObject * object);
|
|
static void gst_svtav1enc_finalize (GObject * object);
|
|
|
|
gboolean gst_svtav1enc_allocate_svt_buffers (GstSvtAv1Enc * svtav1enc);
|
|
void gst_svthevenc_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 GstFlowReturn gst_svtav1enc_pre_push (GstVideoEncoder * encoder,
|
|
GstVideoCodecFrame * frame);
|
|
static GstCaps *gst_svtav1enc_getcaps (GstVideoEncoder * encoder,
|
|
GstCaps * filter);
|
|
static gboolean gst_svtav1enc_sink_event (GstVideoEncoder * encoder,
|
|
GstEvent * event);
|
|
static gboolean gst_svtav1enc_src_event (GstVideoEncoder * encoder,
|
|
GstEvent * event);
|
|
static gboolean gst_svtav1enc_negotiate (GstVideoEncoder * encoder);
|
|
static gboolean gst_svtav1enc_decide_allocation (GstVideoEncoder * encoder,
|
|
GstQuery * query);
|
|
static gboolean gst_svtav1enc_propose_allocation (GstVideoEncoder * encoder,
|
|
GstQuery * query);
|
|
static gboolean gst_svtav1enc_flush (GstVideoEncoder * encoder);
|
|
|
|
/* helpers */
|
|
void set_default_svt_configuration (EbSvtAv1EncConfiguration * svt_config);
|
|
gint compare_video_code_frame_and_pts (const void *video_codec_frame_ptr,
|
|
const void *pts_ptr);
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_ENCMODE,
|
|
PROP_SPEEDCONTROL,
|
|
PROP_B_PYRAMID,
|
|
PROP_P_FRAMES,
|
|
PROP_PRED_STRUCTURE,
|
|
PROP_GOP_SIZE,
|
|
PROP_INTRA_REFRESH,
|
|
PROP_QP,
|
|
PROP_QP_MAX,
|
|
PROP_QP_MIN,
|
|
PROP_DEBLOCKING,
|
|
PROP_RC_MODE,
|
|
PROP_BITRATE,
|
|
PROP_LOOKAHEAD,
|
|
PROP_SCD,
|
|
PROP_CORES,
|
|
PROP_SOCKET
|
|
};
|
|
|
|
#define PROP_RC_MODE_CQP 0
|
|
#define PROP_RC_MODE_VBR 1
|
|
|
|
#define PROP_ENCMODE_DEFAULT 7
|
|
#define PROP_SPEEDCONTROL_DEFAULT 60
|
|
#define PROP_HIERARCHICAL_LEVEL_DEFAULT 4
|
|
#define PROP_P_FRAMES_DEFAULT 0
|
|
#define PROP_PRED_STRUCTURE_DEFAULT 2
|
|
#define PROP_GOP_SIZE_DEFAULT -1
|
|
#define PROP_INTRA_REFRESH_DEFAULT 1
|
|
#define PROP_QP_DEFAULT 50
|
|
#define PROP_DEBLOCKING_DEFAULT TRUE
|
|
#define PROP_RC_MODE_DEFAULT PROP_RC_MODE_CQP
|
|
#define PROP_BITRATE_DEFAULT 7000000
|
|
#define PROP_QP_MAX_DEFAULT 63
|
|
#define PROP_QP_MIN_DEFAULT 0
|
|
#define PROP_LOOKAHEAD_DEFAULT (unsigned int)-1
|
|
#define PROP_SCD_DEFAULT FALSE
|
|
#define PROP_AUD_DEFAULT FALSE
|
|
#define PROP_CORES_DEFAULT 0
|
|
#define PROP_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) byte-stream, "
|
|
"alignment = (string) au, "
|
|
"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,
|
|
"debug category for 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->dispose = gst_svtav1enc_dispose;
|
|
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->pre_push = GST_DEBUG_FUNCPTR (gst_svtav1enc_pre_push);
|
|
video_encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_svtav1enc_getcaps);
|
|
video_encoder_class->sink_event =
|
|
GST_DEBUG_FUNCPTR (gst_svtav1enc_sink_event);
|
|
video_encoder_class->src_event = GST_DEBUG_FUNCPTR (gst_svtav1enc_src_event);
|
|
video_encoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_svtav1enc_negotiate);
|
|
video_encoder_class->decide_allocation =
|
|
GST_DEBUG_FUNCPTR (gst_svtav1enc_decide_allocation);
|
|
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_ENCMODE,
|
|
g_param_spec_uint ("speed", "speed (Encoder Mode)",
|
|
"Quality vs density tradeoff point"
|
|
" that the encoding is to be performed at"
|
|
" (0 is the highest quality, 7 is the highest speed) ",
|
|
0, 7, PROP_ENCMODE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_SPEEDCONTROL,
|
|
g_param_spec_uint ("speed-control", "Speed Control (in fps)",
|
|
"Dynamically change the encoding speed preset"
|
|
" to meet this defined average encoding speed (in fps)",
|
|
1, 240, PROP_SPEEDCONTROL_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_B_PYRAMID,
|
|
g_param_spec_uint ("hierarchical-level", "Hierarchical levels",
|
|
"3 : 4 - Level Hierarchy,"
|
|
"4 : 5 - Level Hierarchy",
|
|
3, 4, PROP_HIERARCHICAL_LEVEL_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
//g_object_class_install_property (gobject_class, PROP_P_FRAMES,
|
|
// g_param_spec_boolean ("p-frames", "P Frames",
|
|
// "Use P-frames in the base layer",
|
|
// PROP_P_FRAMES_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
//g_object_class_install_property (gobject_class, PROP_PRED_STRUCTURE,
|
|
// g_param_spec_uint ("pred-struct", "Prediction Structure",
|
|
// "0 : Low Delay P, 1 : Low Delay B"
|
|
// ", 2 : Random Access",
|
|
// 0, 2, PROP_PRED_STRUCTURE_DEFAULT,
|
|
// G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_GOP_SIZE,
|
|
g_param_spec_int ("gop-size", "GOP size",
|
|
"Period of Intra Frames insertion (-1 is auto)",
|
|
-1, 251, PROP_GOP_SIZE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_INTRA_REFRESH,
|
|
g_param_spec_int ("intra-refresh", "Intra refresh type",
|
|
"CRA (open GOP)"
|
|
"or IDR frames (closed GOP)",
|
|
1, 2, PROP_INTRA_REFRESH_DEFAULT,
|
|
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_DEBLOCKING,
|
|
g_param_spec_boolean ("deblocking", "Deblock Filter",
|
|
"Enable Deblocking Loop Filtering",
|
|
PROP_DEBLOCKING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_RC_MODE,
|
|
g_param_spec_uint ("rc", "Rate-control mode",
|
|
"0 : CQP, 1 : VBR",
|
|
0, 1, PROP_RC_MODE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/* TODO: add GST_PARAM_MUTABLE_PLAYING property and handle it? */
|
|
g_object_class_install_property (gobject_class, PROP_BITRATE,
|
|
g_param_spec_uint ("bitrate", "Target bitrate",
|
|
"Target bitrate in bits/sec. Only used when in VBR mode",
|
|
1, G_MAXUINT, PROP_BITRATE_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_QP_MAX,
|
|
g_param_spec_uint ("max-qp", "Max Quantization parameter",
|
|
"Maximum QP value allowed for rate control use"
|
|
" Only used in VBR mode.",
|
|
0, 63, PROP_QP_MAX_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_QP_MIN,
|
|
g_param_spec_uint ("min-qp", "Min Quantization parameter",
|
|
"Minimum QP value allowed for rate control use"
|
|
" Only used in VBR mode.",
|
|
0, 63, PROP_QP_MIN_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_LOOKAHEAD,
|
|
g_param_spec_int ("lookahead", "Look Ahead Distance",
|
|
"Number of frames to look ahead. -1 lets the encoder pick a value",
|
|
-1, 250, PROP_LOOKAHEAD_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_SCD,
|
|
g_param_spec_boolean ("scd", "Scene Change Detection",
|
|
"Enable Scene Change Detection algorithm",
|
|
PROP_SCD_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_CORES,
|
|
g_param_spec_uint ("cores", "Number of logical cores",
|
|
"Number of logical cores to be used. 0: auto",
|
|
0, UINT_MAX, PROP_CORES_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_SOCKET,
|
|
g_param_spec_int ("socket", "Target socket",
|
|
"Target socket to run on. -1: all available",
|
|
-1, 15, PROP_SOCKET_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
static void
|
|
gst_svtav1enc_init (GstSvtAv1Enc * svtav1enc)
|
|
{
|
|
GST_OBJECT_LOCK (svtav1enc);
|
|
svtav1enc->svt_config = g_malloc (sizeof (EbSvtAv1EncConfiguration));
|
|
if (!svtav1enc->svt_config) {
|
|
GST_ERROR_OBJECT (svtav1enc, "insufficient resources");
|
|
GST_OBJECT_UNLOCK (svtav1enc);
|
|
return;
|
|
}
|
|
memset (&svtav1enc->svt_encoder, 0, sizeof (svtav1enc->svt_encoder));
|
|
svtav1enc->frame_count = 0;
|
|
svtav1enc->dts_offset = 0;
|
|
|
|
EbErrorType res =
|
|
eb_init_handle(&svtav1enc->svt_encoder, NULL, svtav1enc->svt_config);
|
|
if (res != EB_ErrorNone) {
|
|
GST_ERROR_OBJECT (svtav1enc, "eb_init_handle failed with error %d", res);
|
|
GST_OBJECT_UNLOCK (svtav1enc);
|
|
return;
|
|
}
|
|
/* setting configuration here since eb_init_handle overrides it */
|
|
set_default_svt_configuration (svtav1enc->svt_config);
|
|
GST_OBJECT_UNLOCK (svtav1enc);
|
|
}
|
|
|
|
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_ENCMODE:
|
|
svtav1enc->svt_config->enc_mode = g_value_get_uint (value);
|
|
break;
|
|
case PROP_GOP_SIZE:
|
|
svtav1enc->svt_config->intra_period_length = g_value_get_int(value) - 1;
|
|
break;
|
|
case PROP_INTRA_REFRESH:
|
|
svtav1enc->svt_config->intra_refresh_type = g_value_get_int(value);
|
|
break;
|
|
case PROP_SPEEDCONTROL:
|
|
if (g_value_get_uint (value) > 0) {
|
|
svtav1enc->svt_config->injector_frame_rate = g_value_get_uint (value);
|
|
svtav1enc->svt_config->speed_control_flag = 1;
|
|
} else {
|
|
svtav1enc->svt_config->injector_frame_rate = 60 << 16;
|
|
svtav1enc->svt_config->speed_control_flag = 0;
|
|
}
|
|
break;
|
|
case PROP_B_PYRAMID:
|
|
svtav1enc->svt_config->hierarchical_levels = g_value_get_uint (value);
|
|
break;
|
|
case PROP_PRED_STRUCTURE:
|
|
svtav1enc->svt_config->pred_structure = g_value_get_uint(value);
|
|
break;
|
|
case PROP_P_FRAMES:
|
|
svtav1enc->svt_config->base_layer_switch_mode = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_QP:
|
|
svtav1enc->svt_config->qp = g_value_get_uint (value);
|
|
break;
|
|
case PROP_DEBLOCKING:
|
|
svtav1enc->svt_config->disable_dlf_flag = !g_value_get_boolean (value);
|
|
break;
|
|
case PROP_RC_MODE:
|
|
svtav1enc->svt_config->rate_control_mode = g_value_get_uint (value);
|
|
break;
|
|
case PROP_BITRATE:
|
|
svtav1enc->svt_config->target_bit_rate = g_value_get_uint (value) * 1000;
|
|
break;
|
|
case PROP_QP_MAX:
|
|
svtav1enc->svt_config->max_qp_allowed = g_value_get_uint (value);
|
|
break;
|
|
case PROP_QP_MIN:
|
|
svtav1enc->svt_config->min_qp_allowed = g_value_get_uint (value);
|
|
break;
|
|
case PROP_LOOKAHEAD:
|
|
svtav1enc->svt_config->look_ahead_distance =
|
|
(unsigned int)g_value_get_int(value);
|
|
break;
|
|
case PROP_SCD:
|
|
svtav1enc->svt_config->scene_change_detection =
|
|
g_value_get_boolean (value);
|
|
break;
|
|
case PROP_CORES:
|
|
svtav1enc->svt_config->logical_processors = g_value_get_uint (value);
|
|
break;
|
|
case PROP_SOCKET:
|
|
svtav1enc->svt_config->target_socket = g_value_get_int (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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_ENCMODE:
|
|
g_value_set_uint (value, svtav1enc->svt_config->enc_mode);
|
|
break;
|
|
case PROP_SPEEDCONTROL:
|
|
if (svtav1enc->svt_config->speed_control_flag) {
|
|
g_value_set_uint (value, svtav1enc->svt_config->injector_frame_rate);
|
|
} else {
|
|
g_value_set_uint (value, 0);
|
|
}
|
|
break;
|
|
case PROP_B_PYRAMID:
|
|
g_value_set_uint (value, svtav1enc->svt_config->hierarchical_levels);
|
|
break;
|
|
case PROP_P_FRAMES:
|
|
g_value_set_boolean (value,
|
|
svtav1enc->svt_config->base_layer_switch_mode == 1);
|
|
break;
|
|
case PROP_PRED_STRUCTURE:
|
|
g_value_set_uint (value, svtav1enc->svt_config->pred_structure);
|
|
break;
|
|
case PROP_GOP_SIZE:
|
|
g_value_set_int (value, svtav1enc->svt_config->intra_period_length + 1);
|
|
break;
|
|
case PROP_INTRA_REFRESH:
|
|
g_value_set_int(value, svtav1enc->svt_config->intra_refresh_type);
|
|
break;
|
|
case PROP_QP:
|
|
g_value_set_uint (value, svtav1enc->svt_config->qp);
|
|
break;
|
|
case PROP_DEBLOCKING:
|
|
g_value_set_boolean (value, svtav1enc->svt_config->disable_dlf_flag == 0);
|
|
break;
|
|
case PROP_RC_MODE:
|
|
g_value_set_uint (value, svtav1enc->svt_config->rate_control_mode);
|
|
break;
|
|
case PROP_BITRATE:
|
|
g_value_set_uint (value, svtav1enc->svt_config->target_bit_rate / 1000);
|
|
break;
|
|
case PROP_QP_MAX:
|
|
g_value_set_uint (value, svtav1enc->svt_config->max_qp_allowed);
|
|
break;
|
|
case PROP_QP_MIN:
|
|
g_value_set_uint (value, svtav1enc->svt_config->min_qp_allowed);
|
|
break;
|
|
case PROP_LOOKAHEAD:
|
|
g_value_set_int(value, (int)svtav1enc->svt_config->look_ahead_distance);
|
|
break;
|
|
case PROP_SCD:
|
|
g_value_set_boolean (value,
|
|
svtav1enc->svt_config->scene_change_detection == 1);
|
|
break;
|
|
case PROP_CORES:
|
|
g_value_set_uint (value, svtav1enc->svt_config->logical_processors);
|
|
break;
|
|
case PROP_SOCKET:
|
|
g_value_set_int (value, svtav1enc->svt_config->target_socket);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_svtav1enc_dispose (GObject * object)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (object);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "dispose");
|
|
|
|
/* clean up as possible. may be called multiple times */
|
|
if (svtav1enc->state)
|
|
gst_video_codec_state_unref (svtav1enc->state);
|
|
svtav1enc->state = NULL;
|
|
|
|
G_OBJECT_CLASS (gst_svtav1enc_parent_class)->dispose (object);
|
|
}
|
|
|
|
void
|
|
gst_svtav1enc_finalize (GObject * object)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (object);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "finalizing svtav1enc");
|
|
|
|
GST_OBJECT_LOCK (svtav1enc);
|
|
eb_deinit_handle(svtav1enc->svt_encoder);
|
|
svtav1enc->svt_encoder = NULL;
|
|
g_free (svtav1enc->svt_config);
|
|
GST_OBJECT_UNLOCK (svtav1enc);
|
|
|
|
G_OBJECT_CLASS (gst_svtav1enc_parent_class)->finalize (object);
|
|
}
|
|
|
|
gboolean
|
|
gst_svtav1enc_allocate_svt_buffers (GstSvtAv1Enc * svtav1enc)
|
|
{
|
|
svtav1enc->input_buf = g_malloc (sizeof (EbBufferHeaderType));
|
|
if (!svtav1enc->input_buf) {
|
|
GST_ERROR_OBJECT (svtav1enc, "insufficient resources");
|
|
return FALSE;
|
|
}
|
|
svtav1enc->input_buf->p_buffer = g_malloc (sizeof (EbSvtIOFormat));
|
|
if (!svtav1enc->input_buf->p_buffer) {
|
|
GST_ERROR_OBJECT (svtav1enc, "insufficient resources");
|
|
return FALSE;
|
|
}
|
|
memset(svtav1enc->input_buf->p_buffer, 0, sizeof(EbSvtIOFormat));
|
|
svtav1enc->input_buf->size = sizeof (EbBufferHeaderType);
|
|
svtav1enc->input_buf->p_app_private = NULL;
|
|
svtav1enc->input_buf->pic_type = EB_AV1_INVALID_PICTURE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gst_svthevenc_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;
|
|
}
|
|
}
|
|
|
|
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 */
|
|
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;
|
|
svtav1enc->svt_config->frame_rate =
|
|
svtav1enc->svt_config->frame_rate_numerator /
|
|
svtav1enc->svt_config->frame_rate_denominator;
|
|
|
|
if (svtav1enc->svt_config->frame_rate < 1000) {
|
|
svtav1enc->svt_config->frame_rate = svtav1enc->svt_config->frame_rate << 16;
|
|
}
|
|
|
|
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);
|
|
|
|
/* pick a default value for the look ahead distance
|
|
* in CQP mode:2*minigop+1. in VBR: intra Period */
|
|
if (svtav1enc->svt_config->look_ahead_distance == (unsigned int) -1) {
|
|
svtav1enc->svt_config->look_ahead_distance =
|
|
(svtav1enc->svt_config->rate_control_mode == PROP_RC_MODE_VBR) ?
|
|
svtav1enc->svt_config->intra_period_length :
|
|
2 * (1 << svtav1enc->svt_config->hierarchical_levels) + 1;
|
|
}
|
|
|
|
/* 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 =
|
|
eb_svt_enc_set_parameter(svtav1enc->svt_encoder, svtav1enc->svt_config);
|
|
if (res != EB_ErrorNone) {
|
|
GST_ERROR_OBJECT (svtav1enc, "eb_svt_enc_set_parameter failed with error %d", res);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_svtav1enc_start_svt (GstSvtAv1Enc * svtav1enc)
|
|
{
|
|
G_LOCK (init_mutex);
|
|
EbErrorType res = eb_init_encoder(svtav1enc->svt_encoder);
|
|
G_UNLOCK (init_mutex);
|
|
|
|
if (res != EB_ErrorNone) {
|
|
GST_ERROR_OBJECT (svtav1enc, "eb_init_encoder failed with error %d", res);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
set_default_svt_configuration (EbSvtAv1EncConfiguration * svt_config)
|
|
{
|
|
memset(svt_config, 0, sizeof(EbSvtAv1EncConfiguration));
|
|
svt_config->source_width = 0;
|
|
svt_config->source_height = 0;
|
|
svt_config->intra_period_length = PROP_GOP_SIZE_DEFAULT - 1;
|
|
svt_config->intra_refresh_type = PROP_INTRA_REFRESH_DEFAULT;
|
|
svt_config->enc_mode = PROP_ENCMODE_DEFAULT;
|
|
svt_config->frame_rate = 25;
|
|
svt_config->frame_rate_denominator = 1;
|
|
svt_config->frame_rate_numerator = 25;
|
|
svt_config->hierarchical_levels = PROP_HIERARCHICAL_LEVEL_DEFAULT;
|
|
svt_config->base_layer_switch_mode = PROP_P_FRAMES_DEFAULT;
|
|
svt_config->pred_structure = PROP_PRED_STRUCTURE_DEFAULT;
|
|
svt_config->scene_change_detection = PROP_SCD_DEFAULT;
|
|
svt_config->look_ahead_distance = (uint32_t)~0;
|
|
svt_config->rate_control_mode = PROP_RC_MODE_DEFAULT;
|
|
svt_config->target_bit_rate = PROP_BITRATE_DEFAULT;
|
|
svt_config->max_qp_allowed = PROP_QP_MAX_DEFAULT;
|
|
svt_config->min_qp_allowed = PROP_QP_MIN_DEFAULT;
|
|
svt_config->qp = PROP_QP_DEFAULT;
|
|
svt_config->use_qp_file = FALSE;
|
|
svt_config->disable_dlf_flag = (PROP_DEBLOCKING_DEFAULT == FALSE);
|
|
svt_config->enable_denoise_flag = FALSE;
|
|
svt_config->film_grain_denoise_strength = FALSE;
|
|
svt_config->enable_warped_motion = FALSE;
|
|
svt_config->use_default_me_hme = TRUE;
|
|
svt_config->enable_hme_flag = TRUE;
|
|
svt_config->enable_hme_level0_flag = TRUE;
|
|
svt_config->enable_hme_level1_flag = FALSE;
|
|
svt_config->enable_hme_level2_flag = FALSE;
|
|
svt_config->ext_block_flag = FALSE;
|
|
svt_config->in_loop_me_flag = TRUE;
|
|
svt_config->search_area_width = 16;
|
|
svt_config->search_area_height = 7;
|
|
svt_config->number_hme_search_region_in_width = 2;
|
|
svt_config->number_hme_search_region_in_height = 2;
|
|
svt_config->hme_level0_total_search_area_width = 64;
|
|
svt_config->hme_level0_total_search_area_height = 25;
|
|
svt_config->hme_level0_search_area_in_width_array[0] = 32;
|
|
svt_config->hme_level0_search_area_in_width_array[1] = 32;
|
|
svt_config->hme_level0_search_area_in_height_array[0] = 12;
|
|
svt_config->hme_level0_search_area_in_height_array[1] = 13;
|
|
svt_config->hme_level1_search_area_in_width_array[0] = 1;
|
|
svt_config->hme_level1_search_area_in_width_array[1] = 1;
|
|
svt_config->hme_level1_search_area_in_height_array[0] = 1;
|
|
svt_config->hme_level1_search_area_in_height_array[1] = 1;
|
|
svt_config->hme_level2_search_area_in_width_array[0] = 1;
|
|
svt_config->hme_level2_search_area_in_width_array[1] = 1;
|
|
svt_config->hme_level2_search_area_in_height_array[0] = 1;
|
|
svt_config->hme_level2_search_area_in_height_array[1] = 1;
|
|
svt_config->channel_id = 0;
|
|
svt_config->active_channel_count = 1;
|
|
svt_config->logical_processors = PROP_CORES_DEFAULT;
|
|
svt_config->target_socket = PROP_SOCKET_DEFAULT;
|
|
svt_config->recon_enabled = FALSE;
|
|
//svt_config->tile_columns = 0;
|
|
//svt_config->tile_rows = 0;
|
|
svt_config->stat_report = FALSE;
|
|
svt_config->high_dynamic_range_input = FALSE;
|
|
svt_config->encoder_bit_depth = 8;
|
|
svt_config->compressed_ten_bit_format = FALSE;
|
|
svt_config->profile = 0;
|
|
svt_config->tier = 0;
|
|
svt_config->level = 0;
|
|
svt_config->injector_frame_rate = PROP_SPEEDCONTROL_DEFAULT;
|
|
svt_config->speed_control_flag = FALSE;
|
|
svt_config->sb_sz = 64;
|
|
svt_config->super_block_size = 128;
|
|
svt_config->partition_depth = 4;
|
|
svt_config->enable_qp_scaling_flag = 0;
|
|
svt_config->use_cpu_flags = CPU_FLAGS_ALL;
|
|
}
|
|
|
|
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_ERROR_OBJECT (svtav1enc, "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 = (void *) frame;
|
|
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;
|
|
}
|
|
|
|
res = eb_svt_enc_send_picture(svtav1enc->svt_encoder, input_buffer);
|
|
if (res != EB_ErrorNone) {
|
|
GST_ERROR_OBJECT (svtav1enc, "Issue %d sending picture to SVT-AV1.", res);
|
|
ret = GST_FLOW_ERROR;
|
|
}
|
|
gst_video_frame_unmap (&video_frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
ret = eb_svt_enc_send_picture(svtav1enc->svt_encoder, &input_buffer);
|
|
|
|
if (ret != EB_ErrorNone) {
|
|
GST_ERROR_OBJECT (svtav1enc, "couldn't send EOS frame.");
|
|
return FALSE;
|
|
}
|
|
|
|
return (ret == EB_ErrorNone);
|
|
}
|
|
|
|
gboolean
|
|
gst_svtav1enc_flush (GstVideoEncoder * encoder)
|
|
{
|
|
GstFlowReturn ret =
|
|
gst_svtav1enc_dequeue_encoded_frames (GST_SVTAV1ENC (encoder), TRUE,
|
|
FALSE);
|
|
|
|
return (ret != GST_FLOW_ERROR);
|
|
}
|
|
|
|
gint
|
|
compare_video_code_frame_and_pts (const void *video_codec_frame_ptr,
|
|
const void *pts_ptr)
|
|
{
|
|
return ((GstVideoCodecFrame *) video_codec_frame_ptr)->pts -
|
|
*((GstClockTime *) pts_ptr);
|
|
}
|
|
|
|
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 {
|
|
GList *pending_frames = NULL;
|
|
GList *frame_list_element = NULL;
|
|
GstVideoCodecFrame *frame = NULL;
|
|
EbBufferHeaderType *output_buf = NULL;
|
|
|
|
res =
|
|
eb_svt_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_ERROR_OBJECT (svtav1enc, "Error while encoding, return\n");
|
|
return GST_FLOW_ERROR;
|
|
} else if (res != EB_NoErrorEmptyQueue && output_frames && output_buf) {
|
|
/* if p_app_private is indeed propagated, get the frame through it
|
|
* it's not currently the case with SVT-AV1
|
|
* so we fallback on using its PTS to find it back */
|
|
if (output_buf->p_app_private) {
|
|
frame = (GstVideoCodecFrame *) output_buf->p_app_private;
|
|
} else {
|
|
pending_frames = gst_video_encoder_get_frames (GST_VIDEO_ENCODER
|
|
(svtav1enc));
|
|
frame_list_element = g_list_find_custom (pending_frames,
|
|
&output_buf->pts, compare_video_code_frame_and_pts);
|
|
|
|
if (frame_list_element == NULL)
|
|
return GST_FLOW_ERROR;
|
|
|
|
frame = (GstVideoCodecFrame *) frame_list_element->data;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
frame->output_buffer =
|
|
gst_buffer_new_allocate (NULL, output_buf->n_filled_len, NULL);
|
|
GST_BUFFER_FLAG_SET(frame->output_buffer, GST_BUFFER_FLAG_LIVE);
|
|
gst_buffer_fill (frame->output_buffer, 0,
|
|
output_buf->p_buffer, output_buf->n_filled_len);
|
|
|
|
|
|
/* SVT-AV1 may return first frames with a negative DTS,
|
|
* offsetting it to start at 0 since GStreamer 1.x doesn't support it */
|
|
if (output_buf->dts + svtav1enc->dts_offset < 0) {
|
|
svtav1enc->dts_offset = -output_buf->dts;
|
|
}
|
|
/* Gstreamer doesn't support negative DTS so we return
|
|
* very small increasing ones for the first frames. */
|
|
if (output_buf->dts < 1) {
|
|
frame->dts = frame->output_buffer->dts =
|
|
output_buf->dts + svtav1enc->dts_offset;
|
|
} else {
|
|
frame->dts = frame->output_buffer->dts =
|
|
(output_buf->dts *
|
|
svtav1enc->svt_config->frame_rate_denominator * GST_SECOND) /
|
|
svtav1enc->svt_config->frame_rate_numerator;
|
|
}
|
|
|
|
frame->pts = frame->output_buffer->pts = output_buf->pts;
|
|
|
|
GST_LOG_OBJECT (svtav1enc, "#frame:%lld dts:%" G_GINT64_FORMAT " pts:%"
|
|
G_GINT64_FORMAT " SliceType:%d\n", svtav1enc->frame_count,
|
|
(frame->dts), (frame->pts), output_buf->pic_type);
|
|
|
|
eb_svt_release_out_buffer(&output_buf);
|
|
output_buf = NULL;
|
|
|
|
ret = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (svtav1enc), frame);
|
|
|
|
if (pending_frames != NULL) {
|
|
g_list_free_full (pending_frames,
|
|
(GDestroyNotify) gst_video_codec_frame_unref);
|
|
}
|
|
|
|
svtav1enc->frame_count++;
|
|
}
|
|
|
|
} while (res == EB_ErrorNone && !encode_at_eos);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_open (GstVideoEncoder * encoder)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "open");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_close (GstVideoEncoder * encoder)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "close");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_start (GstVideoEncoder * encoder)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "start");
|
|
/* starting the encoder is done in set_format,
|
|
* once caps are fully negotiated */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_stop (GstVideoEncoder * encoder)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "stop");
|
|
|
|
GstVideoCodecFrame *remaining_frame = NULL;
|
|
while ((remaining_frame =
|
|
gst_video_encoder_get_oldest_frame (encoder)) != NULL) {
|
|
GST_WARNING_OBJECT (svtav1enc,
|
|
"encoder is being stopped, dropping frame %d",
|
|
remaining_frame->system_frame_number);
|
|
remaining_frame->output_buffer = NULL;
|
|
gst_video_encoder_finish_frame (encoder, remaining_frame);
|
|
}
|
|
|
|
GST_OBJECT_LOCK (svtav1enc);
|
|
if (svtav1enc->state)
|
|
gst_video_codec_state_unref (svtav1enc->state);
|
|
svtav1enc->state = NULL;
|
|
GST_OBJECT_UNLOCK (svtav1enc);
|
|
|
|
GST_OBJECT_LOCK (svtav1enc);
|
|
eb_deinit_encoder(svtav1enc->svt_encoder);
|
|
/* Destruct the buffer memory pool */
|
|
gst_svthevenc_deallocate_svt_buffers (svtav1enc);
|
|
GST_OBJECT_UNLOCK (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;
|
|
GST_DEBUG_OBJECT (svtav1enc, "set_format");
|
|
|
|
/* TODO: handle configuration changes while encoder is running
|
|
* and if there was already a state. */
|
|
svtav1enc->state = gst_video_codec_state_ref (state);
|
|
|
|
gst_svtav1enc_configure_svt (svtav1enc);
|
|
gst_svtav1enc_allocate_svt_buffers (svtav1enc);
|
|
gst_svtav1enc_start_svt (svtav1enc);
|
|
|
|
uint32_t fps = (uint32_t)((svtav1enc->svt_config->frame_rate > 1000) ?
|
|
svtav1enc->svt_config->frame_rate >> 16 : svtav1enc->svt_config->frame_rate);
|
|
fps = fps > 120 ? 120 : fps;
|
|
fps = fps < 24 ? 24 : fps;
|
|
|
|
min_latency_frames = svtav1enc->svt_config->look_ahead_distance + ((fps * 5) >> 2);
|
|
|
|
/* TODO: find a better value for max_latency */
|
|
gst_video_encoder_set_latency (encoder,
|
|
min_latency_frames * GST_SECOND / svtav1enc->svt_config->frame_rate,
|
|
3 * GST_SECOND);
|
|
|
|
src_caps =
|
|
gst_static_pad_template_get_caps (&gst_svtav1enc_src_pad_template);
|
|
gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (encoder), src_caps,
|
|
svtav1enc->state);
|
|
gst_caps_unref (src_caps);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "output caps: %" GST_PTR_FORMAT,
|
|
svtav1enc->state->caps);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
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 GstFlowReturn
|
|
gst_svtav1enc_pre_push (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "pre_push");
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_svtav1enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "getcaps");
|
|
|
|
GstCaps *sink_caps =
|
|
gst_static_pad_template_get_caps (&gst_svtav1enc_sink_pad_template);
|
|
GstCaps *ret =
|
|
gst_video_encoder_proxy_getcaps (GST_VIDEO_ENCODER (svtav1enc),
|
|
sink_caps, filter);
|
|
gst_caps_unref (sink_caps);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_sink_event (GstVideoEncoder * encoder, GstEvent * event)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "sink_event");
|
|
|
|
return
|
|
GST_VIDEO_ENCODER_CLASS (gst_svtav1enc_parent_class)->sink_event
|
|
(encoder, event);
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_src_event (GstVideoEncoder * encoder, GstEvent * event)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "src_event");
|
|
|
|
return
|
|
GST_VIDEO_ENCODER_CLASS (gst_svtav1enc_parent_class)->src_event (encoder,
|
|
event);
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_negotiate (GstVideoEncoder * encoder)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "negotiate");
|
|
|
|
return
|
|
GST_VIDEO_ENCODER_CLASS (gst_svtav1enc_parent_class)->negotiate(encoder);
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_decide_allocation (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "decide_allocation");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_svtav1enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
|
|
{
|
|
GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder);
|
|
|
|
GST_DEBUG_OBJECT (svtav1enc, "propose_allocation");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
plugin_init (GstPlugin * plugin)
|
|
{
|
|
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://github.com/OpenVisualCloud"
|
|
#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)
|