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:
Mikhail Fludkov 2015-11-26 17:52:29 +01:00 committed by GStreamer Merge Bot
parent b90dd2d755
commit 7b390a8bbd
5 changed files with 231 additions and 16 deletions

View file

@ -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 *

View file

@ -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));

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}