mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-30 12:49:40 +00:00
vpxenc: Add new bit-per-pixel property to select a better "default" bitrate
As part of this also change the default bitrate value to 0. The default value was 256000 previously. In reality, if the property was not set the bitrate value would be scaled according to the resolution which is not very intuitive behavior. It is better to use 0 for this purpose. Now together with newly introduced property "bits-per-pixel" 0 means to assign the bitrate according to resolution/framerate. The default bitrates are now - 1.2Mbps for VP8 720p@30fps - 0.8Mbps for VP9 720p@30fps and scaled accordingly for different resolutions/framerates. Previously the default bitrate was also not scaled according to the framerate but only took the resolution into account. This also fixes the side effect of setting bitrate to 0. Previously encoder would not produce any data at all. Addition from Sebastian Dröge <sebastian@centricular.com> to assume 30fps if no framerate is given in the caps instead of not calculating any bitrate at all. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/611>
This commit is contained in:
parent
b90dd2d755
commit
7b390a8bbd
5 changed files with 231 additions and 16 deletions
|
@ -101,6 +101,8 @@ static GstFlowReturn gst_vp9_enc_handle_invisible_frame_buffer (GstVPXEnc * enc,
|
|||
static void gst_vp9_enc_set_frame_user_data (GstVPXEnc * enc,
|
||||
GstVideoCodecFrame * frame, vpx_image_t * image);
|
||||
|
||||
#define DEFAULT_BITS_PER_PIXEL 0.0289
|
||||
|
||||
static void
|
||||
gst_vp9_enc_class_init (GstVP9EncClass * klass)
|
||||
{
|
||||
|
@ -152,6 +154,7 @@ gst_vp9_enc_init (GstVP9Enc * gst_vp9_enc)
|
|||
} else {
|
||||
gst_vpx_enc->have_default_config = TRUE;
|
||||
}
|
||||
gst_vpx_enc->bits_per_pixel = DEFAULT_BITS_PER_PIXEL;
|
||||
}
|
||||
|
||||
static vpx_codec_iface_t *
|
||||
|
|
|
@ -47,7 +47,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_vpxenc_debug);
|
|||
#define DEFAULT_PROFILE 0
|
||||
|
||||
#define DEFAULT_RC_END_USAGE VPX_VBR
|
||||
#define DEFAULT_RC_TARGET_BITRATE 256000
|
||||
#define DEFAULT_RC_TARGET_BITRATE 0
|
||||
#define DEFAULT_RC_MIN_QUANTIZER 4
|
||||
#define DEFAULT_RC_MAX_QUANTIZER 63
|
||||
|
||||
|
@ -102,6 +102,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_vpxenc_debug);
|
|||
#define DEFAULT_TIMEBASE_N 0
|
||||
#define DEFAULT_TIMEBASE_D 1
|
||||
|
||||
#define DEFAULT_BITS_PER_PIXEL 0.0434
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
@ -148,7 +150,8 @@ enum
|
|||
PROP_TUNING,
|
||||
PROP_CQ_LEVEL,
|
||||
PROP_MAX_INTRA_BITRATE_PCT,
|
||||
PROP_TIMEBASE
|
||||
PROP_TIMEBASE,
|
||||
PROP_BITS_PER_PIXEL
|
||||
};
|
||||
|
||||
|
||||
|
@ -368,7 +371,8 @@ gst_vpx_enc_class_init (GstVPXEncClass * klass)
|
|||
|
||||
g_object_class_install_property (gobject_class, PROP_RC_TARGET_BITRATE,
|
||||
g_param_spec_int ("target-bitrate", "Target bitrate",
|
||||
"Target bitrate (in bits/sec)",
|
||||
"Target bitrate (in bits/sec) (0: auto - bitrate depends on "
|
||||
"resolution, see \"bits-per-pixel\" property for more info)",
|
||||
0, G_MAXINT, DEFAULT_RC_TARGET_BITRATE,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
|
@ -638,6 +642,13 @@ gst_vpx_enc_class_init (GstVPXEncClass * klass)
|
|||
0, 1, G_MAXINT, 1, DEFAULT_TIMEBASE_N, DEFAULT_TIMEBASE_D,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_BITS_PER_PIXEL,
|
||||
g_param_spec_float ("bits-per-pixel", "Bits per pixel",
|
||||
"Factor to convert number of pixels to bitrate value "
|
||||
"(only has an effect if target-bitrate=0)",
|
||||
0.0, G_MAXFLOAT, DEFAULT_BITS_PER_PIXEL,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_vpxenc_debug, "vpxenc", 0, "VPX Encoder");
|
||||
|
||||
gst_type_mark_as_plugin_api (GST_VPX_ENC_END_USAGE_TYPE);
|
||||
|
@ -657,7 +668,7 @@ gst_vpx_enc_init (GstVPXEnc * gst_vpx_enc)
|
|||
|
||||
gst_vpx_enc->cfg.rc_end_usage = DEFAULT_RC_END_USAGE;
|
||||
gst_vpx_enc->cfg.rc_target_bitrate = DEFAULT_RC_TARGET_BITRATE / 1000;
|
||||
gst_vpx_enc->rc_target_bitrate_set = FALSE;
|
||||
gst_vpx_enc->rc_target_bitrate_auto = DEFAULT_RC_TARGET_BITRATE == 0;
|
||||
gst_vpx_enc->cfg.rc_min_quantizer = DEFAULT_RC_MIN_QUANTIZER;
|
||||
gst_vpx_enc->cfg.rc_max_quantizer = DEFAULT_RC_MAX_QUANTIZER;
|
||||
gst_vpx_enc->cfg.rc_dropframe_thresh = DEFAULT_RC_DROPFRAME_THRESH;
|
||||
|
@ -705,6 +716,7 @@ gst_vpx_enc_init (GstVPXEnc * gst_vpx_enc)
|
|||
gst_vpx_enc->max_intra_bitrate_pct = DEFAULT_MAX_INTRA_BITRATE_PCT;
|
||||
gst_vpx_enc->timebase_n = DEFAULT_TIMEBASE_N;
|
||||
gst_vpx_enc->timebase_d = DEFAULT_TIMEBASE_D;
|
||||
gst_vpx_enc->bits_per_pixel = DEFAULT_BITS_PER_PIXEL;
|
||||
|
||||
gst_vpx_enc->cfg.g_profile = DEFAULT_PROFILE;
|
||||
|
||||
|
@ -734,6 +746,42 @@ gst_vpx_enc_finalize (GObject * object)
|
|||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_vpx_enc_set_auto_bitrate (GstVPXEnc * encoder)
|
||||
{
|
||||
if (encoder->input_state != NULL) {
|
||||
guint size;
|
||||
guint pixels_per_sec;
|
||||
guint target_bitrate;
|
||||
guint fps_n, fps_d;
|
||||
|
||||
if (GST_VIDEO_INFO_FPS_D (&encoder->input_state->info) != 0) {
|
||||
fps_n = GST_VIDEO_INFO_FPS_N (&encoder->input_state->info);
|
||||
fps_d = GST_VIDEO_INFO_FPS_D (&encoder->input_state->info);
|
||||
} else {
|
||||
/* otherwise assume 30 frames per second as a fallback */
|
||||
fps_n = 30;
|
||||
fps_d = 1;
|
||||
}
|
||||
|
||||
size =
|
||||
GST_VIDEO_INFO_WIDTH (&encoder->input_state->info) *
|
||||
GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info);
|
||||
pixels_per_sec = size * fps_n / fps_d;
|
||||
target_bitrate = pixels_per_sec * encoder->bits_per_pixel;
|
||||
|
||||
GST_DEBUG_OBJECT (encoder,
|
||||
"Setting autobitrate for %ux%ux @ %u/%ufps %.4f = %ubps",
|
||||
GST_VIDEO_INFO_WIDTH (&encoder->input_state->info),
|
||||
GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info),
|
||||
GST_VIDEO_INFO_FPS_N (&encoder->input_state->info),
|
||||
GST_VIDEO_INFO_FPS_D (&encoder->input_state->info),
|
||||
encoder->bits_per_pixel, target_bitrate);
|
||||
|
||||
encoder->cfg.rc_target_bitrate = target_bitrate / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_vpx_enc_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
|
@ -753,8 +801,13 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
|
|||
global = TRUE;
|
||||
break;
|
||||
case PROP_RC_TARGET_BITRATE:
|
||||
gst_vpx_enc->cfg.rc_target_bitrate = g_value_get_int (value) / 1000;
|
||||
gst_vpx_enc->rc_target_bitrate_set = TRUE;
|
||||
if (g_value_get_int (value) == 0) {
|
||||
gst_vpx_enc_set_auto_bitrate (gst_vpx_enc);
|
||||
gst_vpx_enc->rc_target_bitrate_auto = TRUE;
|
||||
} else {
|
||||
gst_vpx_enc->cfg.rc_target_bitrate = g_value_get_int (value) / 1000;
|
||||
gst_vpx_enc->rc_target_bitrate_auto = FALSE;
|
||||
}
|
||||
global = TRUE;
|
||||
break;
|
||||
case PROP_RC_MIN_QUANTIZER:
|
||||
|
@ -1097,6 +1150,13 @@ gst_vpx_enc_set_property (GObject * object, guint prop_id,
|
|||
gst_vpx_enc->timebase_n = gst_value_get_fraction_numerator (value);
|
||||
gst_vpx_enc->timebase_d = gst_value_get_fraction_denominator (value);
|
||||
break;
|
||||
case PROP_BITS_PER_PIXEL:
|
||||
gst_vpx_enc->bits_per_pixel = g_value_get_float (value);
|
||||
if (gst_vpx_enc->rc_target_bitrate_auto) {
|
||||
gst_vpx_enc_set_auto_bitrate (gst_vpx_enc);
|
||||
global = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1318,6 +1378,9 @@ gst_vpx_enc_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
gst_value_set_fraction (value, gst_vpx_enc->timebase_n,
|
||||
gst_vpx_enc->timebase_d);
|
||||
break;
|
||||
case PROP_BITS_PER_PIXEL:
|
||||
g_value_set_float (value, gst_vpx_enc->bits_per_pixel);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -1452,14 +1515,6 @@ gst_vpx_enc_set_format (GstVideoEncoder * video_encoder,
|
|||
}
|
||||
|
||||
encoder->cfg.g_profile = gst_vpx_enc_get_downstream_profile (encoder);
|
||||
|
||||
/* Scale default bitrate to our size */
|
||||
if (!encoder->rc_target_bitrate_set)
|
||||
encoder->cfg.rc_target_bitrate =
|
||||
gst_util_uint64_scale (DEFAULT_RC_TARGET_BITRATE,
|
||||
GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info),
|
||||
320 * 240 * 1000);
|
||||
|
||||
encoder->cfg.g_w = GST_VIDEO_INFO_WIDTH (info);
|
||||
encoder->cfg.g_h = GST_VIDEO_INFO_HEIGHT (info);
|
||||
|
||||
|
@ -1641,6 +1696,10 @@ gst_vpx_enc_set_format (GstVideoEncoder * video_encoder,
|
|||
gst_video_codec_state_unref (encoder->input_state);
|
||||
encoder->input_state = gst_video_codec_state_ref (state);
|
||||
|
||||
/* Scale default bitrate to our size */
|
||||
if (encoder->rc_target_bitrate_auto)
|
||||
gst_vpx_enc_set_auto_bitrate (encoder);
|
||||
|
||||
/* prepare cached image buffer setup */
|
||||
image = &encoder->image;
|
||||
memset (image, 0, sizeof (*image));
|
||||
|
|
|
@ -68,7 +68,7 @@ struct _GstVPXEnc
|
|||
/* properties */
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
gboolean have_default_config;
|
||||
gboolean rc_target_bitrate_set;
|
||||
gboolean rc_target_bitrate_auto;
|
||||
gint n_ts_target_bitrate;
|
||||
gint n_ts_rate_decimator;
|
||||
gint n_ts_layer_id;
|
||||
|
@ -100,6 +100,9 @@ struct _GstVPXEnc
|
|||
unsigned int timebase_n;
|
||||
unsigned int timebase_d;
|
||||
|
||||
/* Bits per Pixel */
|
||||
gfloat bits_per_pixel;
|
||||
|
||||
/* state */
|
||||
gboolean inited;
|
||||
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/check/gstharness.h>
|
||||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
|
@ -90,6 +91,7 @@ cleanup_vp8enc (GstElement * vp8enc)
|
|||
gst_check_teardown_element (vp8enc);
|
||||
}
|
||||
|
||||
|
||||
GST_START_TEST (test_encode_simple)
|
||||
{
|
||||
GstElement *vp8enc;
|
||||
|
@ -152,6 +154,106 @@ GST_START_TEST (test_encode_simple)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
#define gst_caps_new_i420(w, h) gst_caps_new_i420_full (w, h, 30, 1, 1, 1)
|
||||
static GstCaps *
|
||||
gst_caps_new_i420_full (gint width, gint height, gint fps_n, gint fps_d,
|
||||
gint par_n, gint par_d)
|
||||
{
|
||||
GstVideoInfo info;
|
||||
gst_video_info_init (&info);
|
||||
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_I420, width, height);
|
||||
GST_VIDEO_INFO_FPS_N (&info) = fps_n;
|
||||
GST_VIDEO_INFO_FPS_D (&info) = fps_d;
|
||||
GST_VIDEO_INFO_PAR_N (&info) = par_n;
|
||||
GST_VIDEO_INFO_PAR_D (&info) = par_d;
|
||||
return gst_video_info_to_caps (&info);
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_harness_create_video_buffer_from_info (GstHarness * h, gint value,
|
||||
GstVideoInfo * info, GstClockTime timestamp, GstClockTime duration)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
gsize size;
|
||||
|
||||
size = GST_VIDEO_INFO_SIZE (info);
|
||||
|
||||
buf = gst_harness_create_buffer (h, size);
|
||||
gst_buffer_memset (buf, 0, value, size);
|
||||
g_assert (buf != NULL);
|
||||
|
||||
gst_buffer_add_video_meta_full (buf,
|
||||
GST_VIDEO_FRAME_FLAG_NONE,
|
||||
GST_VIDEO_INFO_FORMAT (info),
|
||||
GST_VIDEO_INFO_WIDTH (info),
|
||||
GST_VIDEO_INFO_HEIGHT (info),
|
||||
GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
|
||||
|
||||
GST_BUFFER_PTS (buf) = timestamp;
|
||||
GST_BUFFER_DURATION (buf) = duration;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_harness_create_video_buffer_full (GstHarness * h, gint value,
|
||||
guint width, guint height, GstClockTime timestamp, GstClockTime duration)
|
||||
{
|
||||
GstVideoInfo info;
|
||||
|
||||
gst_video_info_init (&info);
|
||||
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_I420, width, height);
|
||||
|
||||
return gst_harness_create_video_buffer_from_info (h, value, &info,
|
||||
timestamp, duration);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_encode_simple_when_bitrate_set_to_zero)
|
||||
{
|
||||
GstHarness *h = gst_harness_new_parse ("vp8enc target-bitrate=0");
|
||||
GstBuffer *buf;
|
||||
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420 (320, 240));
|
||||
|
||||
buf = gst_harness_create_video_buffer_full (h, 0x42,
|
||||
320, 240, 0, gst_util_uint64_scale (GST_SECOND, 1, 30));
|
||||
gst_harness_push (h, buf);
|
||||
gst_buffer_unref (gst_harness_pull (h));
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_autobitrate_changes_with_caps)
|
||||
{
|
||||
gint bitrate = 0;
|
||||
GstHarness *h = gst_harness_new ("vp8enc");
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420_full (1280, 720, 30, 1, 1, 1));
|
||||
|
||||
/* Default settings for 720p @ 30fps ~1.2Mbps */
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 1199000);
|
||||
|
||||
/* Change bits-per-pixel 0.036 to give us ~1Mbps */
|
||||
g_object_set (h->element, "bits-per-pixel", 0.037, NULL);
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 1022000);
|
||||
|
||||
/* Halving the framerate should halve the auto bitrate */
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420_full (1280, 720, 15, 1, 1, 1));
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 511000);
|
||||
|
||||
/* Halving the resolution should quarter the auto bitrate */
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420_full (640, 360, 15, 1, 1, 1));
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 127000);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
vp8enc_suite (void)
|
||||
{
|
||||
|
@ -161,6 +263,8 @@ vp8enc_suite (void)
|
|||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_encode_simple);
|
||||
tcase_add_test (tc_chain, test_encode_simple_when_bitrate_set_to_zero);
|
||||
tcase_add_test (tc_chain, test_autobitrate_changes_with_caps);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,21 @@
|
|||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#define gst_caps_new_i420(w, h) gst_caps_new_i420_full (w, h, 30, 1, 1, 1)
|
||||
static GstCaps *
|
||||
gst_caps_new_i420_full (gint width, gint height, gint fps_n, gint fps_d,
|
||||
gint par_n, gint par_d)
|
||||
{
|
||||
GstVideoInfo info;
|
||||
gst_video_info_init (&info);
|
||||
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_I420, width, height);
|
||||
GST_VIDEO_INFO_FPS_N (&info) = fps_n;
|
||||
GST_VIDEO_INFO_FPS_D (&info) = fps_d;
|
||||
GST_VIDEO_INFO_PAR_N (&info) = par_n;
|
||||
GST_VIDEO_INFO_PAR_D (&info) = par_d;
|
||||
return gst_video_info_to_caps (&info);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_encode_lag_in_frames)
|
||||
{
|
||||
GstHarness *h = gst_harness_new_parse ("vp9enc lag-in-frames=5 cpu-used=8 "
|
||||
|
@ -62,6 +77,36 @@ GST_START_TEST (test_encode_lag_in_frames)
|
|||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_autobitrate_changes_with_caps)
|
||||
{
|
||||
gint bitrate = 0;
|
||||
GstHarness *h = gst_harness_new ("vp9enc");
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420_full (1280, 720, 30, 1, 1, 1));
|
||||
|
||||
/* Default settings for 720p @ 30fps ~0.8Mbps */
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 799000);
|
||||
|
||||
/* Change bits-per-pixel 0.036 to give us ~1Mbps */
|
||||
g_object_set (h->element, "bits-per-pixel", 0.037, NULL);
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 1022000);
|
||||
|
||||
/* Halving the framerate should halve the auto bitrate */
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420_full (1280, 720, 15, 1, 1, 1));
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 511000);
|
||||
|
||||
/* Halving the resolution should quarter the auto bitrate */
|
||||
gst_harness_set_src_caps (h, gst_caps_new_i420_full (640, 360, 15, 1, 1, 1));
|
||||
g_object_get (h->element, "target-bitrate", &bitrate, NULL);
|
||||
fail_unless_equals_int (bitrate, 127000);
|
||||
|
||||
gst_harness_teardown (h);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
vp9enc_suite (void)
|
||||
{
|
||||
|
@ -71,6 +116,7 @@ vp9enc_suite (void)
|
|||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_encode_lag_in_frames);
|
||||
tcase_add_test (tc_chain, test_autobitrate_changes_with_caps);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue