diff --git a/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.c b/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.c index f2dde2539c..6caa695d6d 100644 --- a/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.c +++ b/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.c @@ -24,286 +24,335 @@ #include #include "gstsvtav1enc.h" -GST_DEBUG_CATEGORY_STATIC (gst_svtav1enc_debug_category); +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); +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); +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_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); +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); /* helpers */ -gint compare_video_code_frame_and_pts (const void *video_codec_frame_ptr, - const void *pts_ptr); +gint compare_video_code_frame_and_pts(const void *video_codec_frame_ptr, const void *pts_ptr); -enum -{ - PROP_0, - PROP_ENCMODE, - 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 +enum { + PROP_0, + PROP_ENCMODE, + 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 8 -#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 +#define PROP_ENCMODE_DEFAULT 8 +#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_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]") - ); +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")); +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); +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); +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_src_pad_template); - gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass), - &gst_svtav1enc_sink_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 Xavier Hallade "); + 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 Xavier Hallade "); - 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->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); + 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->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_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, 12 is the highest speed) ", - 0, 12, PROP_ENCMODE_DEFAULT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + 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, 12 is the highest speed) ", + 0, + 12, + PROP_ENCMODE_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_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_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_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_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_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_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)); + 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)); + /* 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_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_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_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_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_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)); + 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; +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; - EbErrorType res = - svt_av1_enc_init_handle(&svtav1enc->svt_encoder, NULL, svtav1enc->svt_config); - if (res != EB_ErrorNone) { - GST_ELEMENT_ERROR (svtav1enc, LIBRARY, INIT, (NULL), ("svt_av1_enc_init_handle failed with error %d", res)); - GST_OBJECT_UNLOCK (svtav1enc); - return; - } - /* setting configuration here since svt_av1_enc_init_handle overrides it */ - GST_OBJECT_UNLOCK (svtav1enc); + EbErrorType res = svt_av1_enc_init_handle(&svtav1enc->svt_encoder, NULL, svtav1enc->svt_config); + if (res != EB_ErrorNone) { + GST_ELEMENT_ERROR(svtav1enc, + LIBRARY, + INIT, + (NULL), + ("svt_av1_enc_init_handle failed with error %d", res)); + GST_OBJECT_UNLOCK(svtav1enc); + return; + } + /* setting configuration here since svt_av1_enc_init_handle overrides it */ + GST_OBJECT_UNLOCK(svtav1enc); } -void -gst_svtav1enc_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec) -{ - GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (object); +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; - } + /* 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); + 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; + 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; @@ -311,491 +360,414 @@ gst_svtav1enc_set_property (GObject * object, guint property_id, svtav1enc->svt_config->intra_refresh_type = g_value_get_int(value); break; case PROP_B_PYRAMID: - svtav1enc->svt_config->hierarchical_levels = g_value_get_uint (value); - break; + 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_QP: - svtav1enc->svt_config->qp = g_value_get_uint (value); - break; + case PROP_QP: svtav1enc->svt_config->qp = g_value_get_uint(value); break; case PROP_DEBLOCKING: - svtav1enc->svt_config->enable_dlf_flag = g_value_get_boolean (value); - break; - case PROP_RC_MODE: - svtav1enc->svt_config->rate_control_mode = g_value_get_uint (value); - break; + svtav1enc->svt_config->enable_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; + 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_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; - case PROP_LOOKAHEAD: - svtav1enc->svt_config->look_ahead_distance = g_value_get_int (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } + 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; + case PROP_LOOKAHEAD: svtav1enc->svt_config->look_ahead_distance = 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); +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); + 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_B_PYRAMID: - g_value_set_uint (value, svtav1enc->svt_config->hierarchical_levels); - break; + switch (property_id) { + case PROP_ENCMODE: g_value_set_uint(value, svtav1enc->svt_config->enc_mode); break; + case PROP_B_PYRAMID: g_value_set_uint(value, svtav1enc->svt_config->hierarchical_levels); break; - - - - case PROP_PRED_STRUCTURE: - g_value_set_uint (value, svtav1enc->svt_config->pred_structure); - 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; + 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; + 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->enable_dlf_flag == 1); - break; - case PROP_RC_MODE: - g_value_set_uint (value, svtav1enc->svt_config->rate_control_mode); - break; + g_value_set_boolean(value, svtav1enc->svt_config->enable_dlf_flag == 1); + 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; + 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_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; - case PROP_LOOKAHEAD: - g_value_set_int (value, svtav1enc->svt_config->look_ahead_distance); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } + 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; + case PROP_LOOKAHEAD: g_value_set_int(value, svtav1enc->svt_config->look_ahead_distance); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; + } } -void -gst_svtav1enc_dispose (GObject * object) -{ - GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (object); +void gst_svtav1enc_dispose(GObject *object) { + GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(object); - GST_DEBUG_OBJECT (svtav1enc, "dispose"); + 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; + /* 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); + G_OBJECT_CLASS(gst_svtav1enc_parent_class)->dispose(object); } -void -gst_svtav1enc_finalize (GObject * object) -{ - GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (object); +void gst_svtav1enc_finalize(GObject *object) { + GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(object); - GST_DEBUG_OBJECT (svtav1enc, "finalizing svtav1enc"); + GST_DEBUG_OBJECT(svtav1enc, "finalizing svtav1enc"); - GST_OBJECT_LOCK (svtav1enc); - svt_av1_enc_deinit_handle(svtav1enc->svt_encoder); - svtav1enc->svt_encoder = NULL; - g_free (svtav1enc->svt_config); - GST_OBJECT_UNLOCK (svtav1enc); + GST_OBJECT_LOCK(svtav1enc); + svt_av1_enc_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); + 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; - svtav1enc->input_buf->metadata = NULL; +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; + svtav1enc->input_buf->metadata = NULL; - return TRUE; + 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; - } +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; - 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; -} - -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; -} - -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; -} - -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); -} - -gboolean -gst_svtav1enc_flush (GstVideoEncoder * encoder) -{ - GstFlowReturn ret = - gst_svtav1enc_dequeue_encoded_frames (GST_SVTAV1ENC (encoder), TRUE, - FALSE); - - return (ret != GST_FLOW_ERROR); -} - -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:%lld pts:%" - G_GINT64_FORMAT " SliceType:%d\n", svtav1enc->frame_count, - (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); - - svtav1enc->frame_count++; +gboolean gst_svtav1enc_configure_svt(GstSvtAv1Enc *svtav1enc) { + if (!svtav1enc->state) { + GST_WARNING_OBJECT(svtav1enc, "no state, can't configure encoder yet"); + return FALSE; } - } while (res == EB_ErrorNone && !encode_at_eos && ret == GST_FLOW_OK); + /* 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; + 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); - return ret; + /* 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_stop (GstVideoEncoder * encoder) -{ - GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder); +gboolean gst_svtav1enc_start_svt(GstSvtAv1Enc *svtav1enc) { + G_LOCK(init_mutex); + EbErrorType res = svt_av1_enc_init(svtav1enc->svt_encoder); + G_UNLOCK(init_mutex); - GST_DEBUG_OBJECT (svtav1enc, "stop"); - - 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); - svt_av1_enc_deinit(svtav1enc->svt_encoder); - /* Destruct the buffer memory pool */ - gst_svthevenc_deallocate_svt_buffers (svtav1enc); - GST_OBJECT_UNLOCK (svtav1enc); - - return TRUE; + 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 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"); +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 (svtav1enc->state) - gst_video_codec_state_unref (svtav1enc->state); - svtav1enc->state = gst_video_codec_state_ref (state); + 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; + } - gst_svtav1enc_configure_svt (svtav1enc); - gst_svtav1enc_allocate_svt_buffers (svtav1enc); - gst_svtav1enc_start_svt (svtav1enc); + 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); - 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; + 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); - min_latency_frames = ((fps * 5) >> 2); + input_buffer->n_filled_len = GST_VIDEO_FRAME_SIZE(&video_frame); - gst_video_encoder_set_latency(encoder, - min_latency_frames * GST_SECOND / - (svtav1enc->svt_config->frame_rate_numerator / - svtav1enc->svt_config->frame_rate_denominator), - -1); + /* 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; - 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); + if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME(frame)) { + input_buffer->pic_type = EB_AV1_KEY_PICTURE; + } - GST_DEBUG_OBJECT (svtav1enc, "output caps: %" GST_PTR_FORMAT, - svtav1enc->state->caps); + input_buffer->metadata = NULL; - return gst_video_encoder_negotiate (encoder); -} + 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); -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); +gboolean gst_svtav1enc_send_eos(GstSvtAv1Enc *svtav1enc) { + EbErrorType ret = EB_ErrorNone; - GST_DEBUG_OBJECT (svtav1enc, "finish"); + 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; - gst_svtav1enc_send_eos (svtav1enc); + ret = svt_av1_enc_send_picture(svtav1enc->svt_encoder, &input_buffer); - return gst_svtav1enc_dequeue_encoded_frames (svtav1enc, TRUE, TRUE); + 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_propose_allocation (GstVideoEncoder * encoder, GstQuery * query) -{ - GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC (encoder); +gboolean gst_svtav1enc_flush(GstVideoEncoder *encoder) { + GstFlowReturn ret = gst_svtav1enc_dequeue_encoded_frames(GST_SVTAV1ENC(encoder), TRUE, FALSE); - 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); + return (ret != GST_FLOW_ERROR); } -static gboolean -plugin_init (GstPlugin * plugin) -{ - return gst_element_register (plugin, "svtav1enc", GST_RANK_SECONDARY, - GST_TYPE_SVTAV1ENC); +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:%lld pts:%" G_GINT64_FORMAT " SliceType:%d\n", + svtav1enc->frame_count, + (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); + + svtav1enc->frame_count++; + } + + } while (res == EB_ErrorNone && !encode_at_eos && ret == GST_FLOW_OK); + + return ret; +} + +static gboolean gst_svtav1enc_stop(GstVideoEncoder *encoder) { + GstSvtAv1Enc *svtav1enc = GST_SVTAV1ENC(encoder); + + GST_DEBUG_OBJECT(svtav1enc, "stop"); + + 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); + svt_av1_enc_deinit(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; + GstVideoCodecState *output_state; + GST_DEBUG_OBJECT(svtav1enc, "set_format"); + + if (svtav1enc->state) + gst_video_codec_state_unref(svtav1enc->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 = 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) { + return gst_element_register(plugin, "svtav1enc", GST_RANK_SECONDARY, GST_TYPE_SVTAV1ENC); } #ifndef VERSION @@ -811,8 +783,6 @@ plugin_init (GstPlugin * plugin) #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) +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) diff --git a/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.h b/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.h index 8ad8a9bcf3..e528cde2f2 100644 --- a/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.h +++ b/subprojects/gst-plugins-bad/ext/svtav1/gstsvtav1enc.h @@ -14,44 +14,37 @@ #include #include - G_BEGIN_DECLS -#define GST_TYPE_SVTAV1ENC \ - (gst_svtav1enc_get_type()) -#define GST_SVTAV1ENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SVTAV1ENC,GstSvtAv1Enc)) +#define GST_TYPE_SVTAV1ENC (gst_svtav1enc_get_type()) +#define GST_SVTAV1ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_SVTAV1ENC, GstSvtAv1Enc)) #define GST_SVTAV1ENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SVTAV1ENC,GstSvtHevcEncClass)) -#define GST_IS_SVTAV1ENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SVTAV1ENC)) -#define GST_IS_SVTAV1ENC_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SVTAV1ENC)) + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_SVTAV1ENC, GstSvtHevcEncClass)) +#define GST_IS_SVTAV1ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_SVTAV1ENC)) +#define GST_IS_SVTAV1ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SVTAV1ENC)) -typedef struct _GstSvtAv1Enc -{ - GstVideoEncoder video_encoder; +typedef struct _GstSvtAv1Enc { + GstVideoEncoder video_encoder; - /* SVT-AV1 Encoder Handle */ - EbComponentType *svt_encoder; + /* SVT-AV1 Encoder Handle */ + EbComponentType *svt_encoder; - /* GStreamer Codec state */ - GstVideoCodecState *state; + /* GStreamer Codec state */ + GstVideoCodecState *state; - /* SVT-AV1 configuration */ - EbSvtAv1EncConfiguration *svt_config; + /* SVT-AV1 configuration */ + EbSvtAv1EncConfiguration *svt_config; - EbBufferHeaderType *input_buf; + EbBufferHeaderType *input_buf; - long long int frame_count; - int dts_offset; + long long int frame_count; + int dts_offset; } GstSvtAv1Enc; -typedef struct _GstSvtAv1EncClass -{ - GstVideoEncoderClass video_encoder_class; +typedef struct _GstSvtAv1EncClass { + GstVideoEncoderClass video_encoder_class; } GstSvtAv1EncClass; -GType gst_svtav1enc_get_type (void); +GType gst_svtav1enc_get_type(void); G_END_DECLS #endif