mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 00:36:51 +00:00
svthevcenc: Add new SVT-HEVC encoder element
The SVT-HEVC (Scalable Video Technology[0] for HEVC) Encoder is an open source video coding technology[1] that is highly optimized for Intel Xeon Scalable processors and Intel Xeon D processors. [0] https://01.org/svt [1] https://github.com/OpenVisualCloud/SVT-HEVC
This commit is contained in:
parent
3f2240498b
commit
663aeb2131
7 changed files with 2713 additions and 0 deletions
|
@ -52,6 +52,7 @@ subdir('soundtouch')
|
|||
subdir('spandsp')
|
||||
subdir('srt')
|
||||
subdir('srtp')
|
||||
subdir('svthevcenc')
|
||||
subdir('teletextdec')
|
||||
subdir('ttml')
|
||||
subdir('voaacenc')
|
||||
|
|
2273
ext/svthevcenc/gstsvthevcenc.c
Normal file
2273
ext/svthevcenc/gstsvthevcenc.c
Normal file
File diff suppressed because it is too large
Load diff
139
ext/svthevcenc/gstsvthevcenc.h
Normal file
139
ext/svthevcenc/gstsvthevcenc.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
/* GStreamer H265 encoder plugin
|
||||
* Copyright (C) 2019 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_SVTHEVC_ENC_H__
|
||||
#define __GST_SVTHEVC_ENC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/video/gstvideoencoder.h>
|
||||
|
||||
#include <EbApi.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GST_TYPE_SVTHEVC_ENC \
|
||||
(gst_svthevc_enc_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstSvtHevcEnc, gst_svthevc_enc, GST, SVTHEVC_ENC, GstVideoEncoder)
|
||||
#define GST_SVTHEVC_ENC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SVTHEVC_ENC,GstSvtHevcEncClass))
|
||||
#define GST_IS_SVTHEVC_ENC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SVTHEVC_ENC))
|
||||
|
||||
typedef enum svt_eos_status
|
||||
{
|
||||
EOS_NOT_REACHED = 0,
|
||||
EOS_REACHED,
|
||||
EOS_TOTRIGGER
|
||||
} SVT_EOS_STATUS;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_SVTHEVC_ENC_B_PYRAMID_FLAT,
|
||||
GST_SVTHEVC_ENC_B_PYRAMID_2LEVEL_HIERARCHY,
|
||||
GST_SVTHEVC_ENC_B_PYRAMID_3LEVEL_HIERARCHY,
|
||||
GST_SVTHEVC_ENC_B_PYRAMID_4LEVEL_HIERARCHY,
|
||||
} GstSvtHevcEncBPyramid;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_SVTHEVC_ENC_BASE_LAYER_MODE_BFRAME,
|
||||
GST_SVTHEVC_ENC_BASE_LAYER_MODE_PFRAME,
|
||||
} GstSvtHevcEncBaseLayerMode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_SVTHEVC_ENC_RC_CQP,
|
||||
GST_SVTHEVC_ENC_RC_VBR,
|
||||
} GstSvtHevcEncRC;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_SVTHEVC_ENC_TUNE_SQ,
|
||||
GST_SVTHEVC_ENC_TUNE_OQ,
|
||||
GST_SVTHEVC_ENC_TUNE_VMAF,
|
||||
} GstSvtHevcEncTune;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_SVTHEVC_ENC_PRED_STRUCT_LOW_DELAY_P,
|
||||
GST_SVTHEVC_ENC_PRED_STRUCT_LOW_DELAY_B,
|
||||
GST_SVTHEVC_ENC_PRED_STRUCT_RANDOM_ACCESS,
|
||||
} GstSvtHevcEncPredStruct;
|
||||
|
||||
struct _GstSvtHevcEnc
|
||||
{
|
||||
GstVideoEncoder element;
|
||||
|
||||
/*< private > */
|
||||
const gchar *svthevc_version;
|
||||
EB_H265_ENC_CONFIGURATION enc_params;
|
||||
EB_COMPONENTTYPE *svt_handle;
|
||||
|
||||
EB_BUFFERHEADERTYPE *in_buf;
|
||||
|
||||
SVT_EOS_STATUS svt_eos_flag;
|
||||
|
||||
GstClockTime dts_offset;
|
||||
GstVideoCodecFrame *first_frame;
|
||||
gboolean push_header;
|
||||
gboolean first_buffer;
|
||||
gboolean update_latency;
|
||||
|
||||
/* Internally used for convert stride to multiple of pstride */
|
||||
GstBufferPool *internal_pool;
|
||||
GstVideoInfo *aligned_info;
|
||||
|
||||
/* properties */
|
||||
gboolean insert_vui;
|
||||
gboolean aud;
|
||||
GstSvtHevcEncBPyramid hierarchical_level;
|
||||
guint la_depth;
|
||||
guint enc_mode;
|
||||
GstSvtHevcEncRC rc_mode;
|
||||
guint qp_i;
|
||||
guint qp_max;
|
||||
guint qp_min;
|
||||
gboolean scene_change_detection;
|
||||
GstSvtHevcEncTune tune;
|
||||
GstSvtHevcEncBaseLayerMode base_layer_switch_mode;
|
||||
guint bitrate;
|
||||
gint keyintmax;
|
||||
gboolean enable_open_gop;
|
||||
guint config_interval;
|
||||
guint cores;
|
||||
gint socket;
|
||||
guint tile_row;
|
||||
guint tile_col;
|
||||
GstSvtHevcEncPredStruct pred_structure;
|
||||
guint vbv_maxrate;
|
||||
guint vbv_bufsize;
|
||||
|
||||
guint profile;
|
||||
guint tier;
|
||||
guint level;
|
||||
|
||||
/* input description */
|
||||
GstVideoCodecState *input_state;
|
||||
|
||||
/* configuration changed while playing */
|
||||
gboolean reconfig;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_SVTHEVC_ENC_H__ */
|
18
ext/svthevcenc/meson.build
Normal file
18
ext/svthevcenc/meson.build
Normal file
|
@ -0,0 +1,18 @@
|
|||
svthevcenc_sources = [
|
||||
'gstsvthevcenc.c',
|
||||
]
|
||||
|
||||
svthevcenc_dep = dependency('SvtHevcEnc', version : '>= 1.4.1', required: get_option('svthevcenc'))
|
||||
|
||||
if svthevcenc_dep.found()
|
||||
gstsvthevcenc = library('gstsvthevcenc',
|
||||
svthevcenc_sources,
|
||||
c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
|
||||
include_directories : [configinc],
|
||||
dependencies : [gstbase_dep, gstpbutils_dep, gstvideo_dep, gstcodecparsers_dep, svthevcenc_dep],
|
||||
install : true,
|
||||
install_dir : plugins_install_dir,
|
||||
)
|
||||
pkgconfig.generate(gstsvthevcenc, install_dir : plugins_pkgconfig_install_dir)
|
||||
plugins += [gstsvthevcenc]
|
||||
endif
|
|
@ -144,6 +144,7 @@ option('soundtouch', type : 'feature', value : 'auto', description : 'Audio pitc
|
|||
option('spandsp', type : 'feature', value : 'auto', description : 'Packet loss concealment audio plugin')
|
||||
option('srt', type : 'feature', value : 'auto', description : 'Secure, Reliable, Transport client/server network source/sink plugin')
|
||||
option('srtp', type : 'feature', value : 'auto', description : 'Secure RTP codec plugin')
|
||||
option('svthevcenc', type : 'feature', value : 'auto', description : 'Scalable Video Technology for HEVC encoder plugin')
|
||||
option('teletext', type : 'feature', value : 'auto', description : 'Teletext plugin')
|
||||
option('tinyalsa', type : 'feature', value : 'auto', description : 'TinyALSA plugin')
|
||||
option('transcode', type : 'feature', value : 'auto', description : 'Transcode plugin')
|
||||
|
|
280
tests/check/elements/svthevcenc.c
Normal file
280
tests/check/elements/svthevcenc.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Yeongjin Jeong <yeongjin.jeong@navercorp.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/check/gstharness.h>
|
||||
|
||||
GST_START_TEST (test_encode_simple)
|
||||
{
|
||||
GstHarness *h;
|
||||
GstCaps *outcaps, *caps;
|
||||
gint i = 0;
|
||||
|
||||
h = gst_harness_new_parse ("svthevcenc speed=9 bitrate=1000 ! h265parse");
|
||||
|
||||
gst_harness_add_src_parse (h, "videotestsrc is-live=true ! "
|
||||
"capsfilter caps=\"video/x-raw,width=320,height=240,framerate=25/1\"",
|
||||
TRUE);
|
||||
|
||||
/* Push 25 buffers into the encoder */
|
||||
fail_unless_equals_int (GST_FLOW_OK,
|
||||
gst_harness_src_crank_and_push_many (h, 25, 25));
|
||||
|
||||
/* EOS will cause the remaining buffers to be drained */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
fail_unless_equals_int (gst_harness_buffers_received (h), 25);
|
||||
|
||||
outcaps =
|
||||
gst_caps_from_string
|
||||
("video/x-h265,width=(int)320,height=(int)240,framerate=(fraction)25/1");
|
||||
|
||||
caps = gst_pad_get_current_caps (h->sinkpad);
|
||||
fail_unless (caps != NULL);
|
||||
fail_unless (gst_caps_can_intersect (caps, outcaps));
|
||||
|
||||
for (i = 0; i < 25; i++) {
|
||||
GstBuffer *buffer = gst_harness_pull (h);
|
||||
|
||||
fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer),
|
||||
gst_util_uint64_scale (1, GST_SECOND, 25));
|
||||
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
gst_caps_unref (outcaps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reuse)
|
||||
{
|
||||
GstHarness *h;
|
||||
GstCaps *srccaps, *outcaps, *caps;
|
||||
GstBuffer *in_buf = NULL;
|
||||
GstFlowReturn ret;
|
||||
GstSegment seg;
|
||||
gint loop, i;
|
||||
|
||||
h = gst_harness_new_parse ("svthevcenc speed=9 bitrate=1000");
|
||||
|
||||
srccaps =
|
||||
gst_caps_from_string
|
||||
("video/x-raw,format=I420,width=(int)320,height=(int)240,framerate=(fraction)25/1");
|
||||
outcaps =
|
||||
gst_caps_from_string
|
||||
("video/x-h265,width=(int)320,height=(int)240,framerate=(fraction)25/1");
|
||||
|
||||
in_buf = gst_buffer_new_and_alloc ((320 * 240) * 3 / 2);
|
||||
gst_buffer_memset (in_buf, 0, 0, -1);
|
||||
|
||||
GST_BUFFER_DURATION (in_buf) = gst_util_uint64_scale (1, GST_SECOND, 25);
|
||||
GST_BUFFER_DTS (in_buf) = GST_CLOCK_TIME_NONE;
|
||||
|
||||
gst_segment_init (&seg, GST_FORMAT_TIME);
|
||||
|
||||
for (loop = 0; loop < 2; loop++) {
|
||||
gst_harness_play (h);
|
||||
|
||||
fail_unless (gst_harness_push_event (h,
|
||||
gst_event_new_stream_start ("test")));
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_caps (srccaps)));
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_segment (&seg)));
|
||||
|
||||
for (i = 0; i < 25; i++) {
|
||||
GST_BUFFER_PTS (in_buf) = gst_util_uint64_scale (i, GST_SECOND, 25);
|
||||
|
||||
ret = gst_harness_push (h, gst_buffer_ref (in_buf));
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
}
|
||||
|
||||
/* EOS will cause the remaining buffers to be drained */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
fail_unless_equals_int (gst_harness_buffers_received (h), (loop + 1) * 25);
|
||||
|
||||
caps = gst_pad_get_current_caps (h->sinkpad);
|
||||
fail_unless (caps != NULL);
|
||||
fail_unless (gst_caps_can_intersect (caps, outcaps));
|
||||
|
||||
for (i = 0; i < 25; i++) {
|
||||
GstBuffer *buffer = gst_harness_pull (h);
|
||||
|
||||
fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer),
|
||||
gst_util_uint64_scale (1, GST_SECOND, 25));
|
||||
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
gst_caps_unref (caps);
|
||||
|
||||
ASSERT_SET_STATE (h->element, GST_STATE_READY, GST_STATE_CHANGE_SUCCESS);
|
||||
}
|
||||
gst_caps_unref (srccaps);
|
||||
gst_caps_unref (outcaps);
|
||||
gst_buffer_unref (in_buf);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_no_encoding)
|
||||
{
|
||||
GstHarness *h;
|
||||
GstCaps *caps;
|
||||
GstEvent *event;
|
||||
|
||||
h = gst_harness_new_parse ("svthevcenc");
|
||||
fail_unless (h != NULL);
|
||||
|
||||
gst_harness_play (h);
|
||||
|
||||
caps = gst_caps_from_string
|
||||
("video/x-raw,format=I420,width=(int)320,height=(int)240,framerate=(fraction)25/1");
|
||||
gst_harness_set_src_caps (h, caps);
|
||||
|
||||
/* Check if draining are performed well without a buffer push */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
|
||||
do {
|
||||
gboolean term = FALSE;
|
||||
event = gst_harness_pull_event (h);
|
||||
/* wait until get EOS event */
|
||||
if (event) {
|
||||
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
|
||||
term = TRUE;
|
||||
|
||||
gst_event_unref (event);
|
||||
|
||||
if (term)
|
||||
break;
|
||||
}
|
||||
} while (TRUE);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
#define MAX_PUSH_BUFFER 300
|
||||
|
||||
GST_START_TEST (test_reconfigure)
|
||||
{
|
||||
GstHarness *h;
|
||||
GstElement *svthevcenc;
|
||||
GstCaps *caps;
|
||||
GstEvent *event;
|
||||
GstBuffer *in_buf, *out_buf = NULL;
|
||||
GstFlowReturn ret;
|
||||
gint i = 0;
|
||||
|
||||
h = gst_harness_new_parse ("svthevcenc ! h265parse");
|
||||
fail_unless (h != NULL);
|
||||
|
||||
svthevcenc = gst_harness_find_element (h, "svthevcenc");
|
||||
g_object_set (svthevcenc, "speed", 9, NULL);
|
||||
|
||||
gst_harness_play (h);
|
||||
|
||||
caps = gst_caps_from_string
|
||||
("video/x-raw,format=I420,width=(int)320,height=(int)240,framerate=(fraction)25/1");
|
||||
gst_harness_set_src_caps (h, caps);
|
||||
|
||||
in_buf = gst_buffer_new_and_alloc ((320 * 240) * 3 / 2);
|
||||
gst_buffer_memset (in_buf, 0, 0, -1);
|
||||
|
||||
GST_BUFFER_DURATION (in_buf) = GST_SECOND;
|
||||
GST_BUFFER_DTS (in_buf) = GST_CLOCK_TIME_NONE;
|
||||
|
||||
/* Push bufffers until get encoder output */
|
||||
do {
|
||||
fail_if (i > MAX_PUSH_BUFFER);
|
||||
|
||||
GST_BUFFER_PTS (in_buf) = i * GST_SECOND;
|
||||
|
||||
ret = gst_harness_push (h, gst_buffer_ref (in_buf));
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
|
||||
out_buf = gst_harness_try_pull (h);
|
||||
i++;
|
||||
} while (out_buf == NULL);
|
||||
gst_buffer_unref (out_buf);
|
||||
|
||||
/* Change property for reconfig */
|
||||
g_object_set (svthevcenc, "speed", 8, NULL);
|
||||
|
||||
/* Push bufffers until get encoder output */
|
||||
do {
|
||||
fail_if (i > 2 * MAX_PUSH_BUFFER);
|
||||
|
||||
GST_BUFFER_PTS (in_buf) = i * GST_SECOND;
|
||||
|
||||
ret = gst_harness_push (h, gst_buffer_ref (in_buf));
|
||||
fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
|
||||
gst_flow_get_name (ret));
|
||||
|
||||
out_buf = gst_harness_try_pull (h);
|
||||
i++;
|
||||
} while (out_buf == NULL);
|
||||
gst_buffer_unref (out_buf);
|
||||
gst_buffer_unref (in_buf);
|
||||
|
||||
/* push EOS to drain all buffers */
|
||||
fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
|
||||
|
||||
do {
|
||||
gboolean term = FALSE;
|
||||
event = gst_harness_pull_event (h);
|
||||
/* wait until get EOS event */
|
||||
if (event) {
|
||||
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
|
||||
term = TRUE;
|
||||
|
||||
gst_event_unref (event);
|
||||
|
||||
if (term)
|
||||
break;
|
||||
}
|
||||
} while (out_buf == NULL);
|
||||
|
||||
gst_object_unref (svthevcenc);
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
svthevcenc_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("svthevcenc");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_encode_simple);
|
||||
tcase_add_test (tc_chain, test_reuse);
|
||||
tcase_add_test (tc_chain, test_no_encoding);
|
||||
tcase_add_test (tc_chain, test_reconfigure);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (svthevcenc);
|
|
@ -37,6 +37,7 @@ base_tests = [
|
|||
[['elements/mxfmux.c']],
|
||||
[['elements/nvenc.c'], false, [gmodule_dep, gstgl_dep]],
|
||||
[['elements/nvdec.c'], not gstgl_dep.found(), [gmodule_dep, gstgl_dep]],
|
||||
[['elements/svthevcenc.c'], not svthevcenc_dep.found(), [svthevcenc_dep]],
|
||||
[['elements/pcapparse.c'], false, [libparser_dep]],
|
||||
[['elements/pnm.c']],
|
||||
[['elements/rtponvifparse.c']],
|
||||
|
|
Loading…
Reference in a new issue