streaming: Add VP8 support to the GStreamer video encoder

Signed-off-by: Francois Gouget <fgouget@codeweavers.com>
This commit is contained in:
Francois Gouget 2015-11-13 17:27:20 +01:00 committed by Christophe Fergeau
parent ce6113d838
commit 6dc0dadf8d
3 changed files with 63 additions and 9 deletions

View File

@ -78,6 +78,7 @@ if test "x$enable_gstreamer" != "xno"; then
[enable_gstreamer="yes"
SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_1_0, [gst-plugins-base 1.0], [appsrc videoconvert appsink])
SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_1_0, [gstreamer-libav 1.0], [avenc_mjpeg])
SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_1_0, [gst-plugins-good 1.0], [vp8enc])
],
[if test "x$enable_gstreamer" = "xyes"; then
AC_MSG_ERROR([GStreamer 1.0 support requested but not found. You may set GSTREAMER_1_0_CFLAGS and GSTREAMER_1_0_LIBS to avoid the need to call pkg-config.])

View File

@ -191,11 +191,44 @@ static void set_appsrc_caps(SpiceGstEncoder *encoder)
static gboolean create_pipeline(SpiceGstEncoder *encoder)
{
gchar *gstenc;
switch (encoder->base.codec_type)
{
case SPICE_VIDEO_CODEC_TYPE_MJPEG:
/* Set max-threads to ensure zero-frame latency */
gstenc = g_strdup("avenc_mjpeg max-threads=1");
break;
case SPICE_VIDEO_CODEC_TYPE_VP8: {
/* See http://www.webmproject.org/docs/encoder-parameters/
* - Set end-usage to get a constant bitrate to help with streaming.
* - min-quantizer ensures the bitrate does not get needlessly high.
* - resize-allowed would be useful for low bitrate situations but
* the decoder does not return a frame of the expected size so
* avoid it.
* - error-resilient minimises artifacts in case the client drops a
* frame.
* - Set lag-in-frames, deadline and cpu-used to match
* "Profile Realtime". lag-in-frames ensures zero-frame latency,
* deadline turns on realtime behavior, and cpu-used targets a 75%
* CPU usage.
* - deadline is supposed to be set in microseconds but in practice
* it behaves like a boolean.
*/
gstenc = g_strdup_printf("vp8enc end-usage=cbr min-quantizer=10 error-resilient=default lag-in-frames=0 deadline=1 cpu-used=4");
break;
}
default:
/* gstreamer_encoder_new() should have rejected this codec type */
spice_warning("unsupported codec type %d", encoder->base.codec_type);
return FALSE;
}
GError *err = NULL;
/* Set max-threads to ensure zero-frame latency */
const gchar *desc = "appsrc is-live=true format=time do-timestamp=true name=src ! videoconvert ! avenc_mjpeg max-threads=1 name=encoder ! appsink name=sink";
gchar *desc = g_strdup_printf("appsrc is-live=true format=time do-timestamp=true name=src ! videoconvert ! %s name=encoder ! appsink name=sink", gstenc);
spice_debug("GStreamer pipeline: %s", desc);
encoder->pipeline = gst_parse_launch_full(desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &err);
g_free(gstenc);
g_free(desc);
if (!encoder->pipeline || err) {
spice_warning("GStreamer error: %s", err->message);
g_clear_error(&err);
@ -209,9 +242,11 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
encoder->gstenc = gst_bin_get_by_name(GST_BIN(encoder->pipeline), "encoder");
encoder->appsink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(encoder->pipeline), "sink"));
/* See https://bugzilla.gnome.org/show_bug.cgi?id=753257 */
spice_debug("removing the pipeline clock");
gst_pipeline_use_clock(GST_PIPELINE(encoder->pipeline), NULL);
if (encoder->base.codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG) {
/* See https://bugzilla.gnome.org/show_bug.cgi?id=753257 */
spice_debug("removing the pipeline clock");
gst_pipeline_use_clock(GST_PIPELINE(encoder->pipeline), NULL);
}
set_pipeline_changes(encoder, SPICE_GST_VIDEO_PIPELINE_STATE |
SPICE_GST_VIDEO_PIPELINE_BITRATE |
@ -224,8 +259,23 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
static void set_gstenc_bitrate(SpiceGstEncoder *encoder)
{
adjust_bit_rate(encoder);
g_object_set(G_OBJECT(encoder->gstenc),
"bitrate", (gint)encoder->bit_rate, NULL);
switch (encoder->base.codec_type)
{
case SPICE_VIDEO_CODEC_TYPE_MJPEG:
g_object_set(G_OBJECT(encoder->gstenc),
"bitrate", (gint)encoder->bit_rate,
NULL);
break;
case SPICE_VIDEO_CODEC_TYPE_VP8:
g_object_set(G_OBJECT(encoder->gstenc),
"target-bitrate", (gint)encoder->bit_rate,
NULL);
break;
default:
/* gstreamer_encoder_new() should have rejected this codec type */
spice_warning("unsupported codec type %d", encoder->base.codec_type);
free_pipeline(encoder);
}
}
/* A helper for spice_gst_encoder_encode_frame() */
@ -528,7 +578,8 @@ VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs)
{
spice_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG, NULL);
spice_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG ||
codec_type == SPICE_VIDEO_CODEC_TYPE_VP8, NULL);
GError *err = NULL;
if (!gst_init_check(NULL, NULL, &err)) {

View File

@ -3492,7 +3492,7 @@ err:
}
static const char default_renderer[] = "sw";
static const char default_video_codecs[] = "spice:mjpeg;gstreamer:mjpeg";
static const char default_video_codecs[] = "spice:mjpeg;gstreamer:mjpeg;gstreamer:vp8";
/* new interface */
SPICE_GNUC_VISIBLE SpiceServer *spice_server_new(void)
@ -3579,11 +3579,13 @@ static new_video_encoder_t video_encoder_procs[] = {
static const EnumNames video_codec_names[] = {
{SPICE_VIDEO_CODEC_TYPE_MJPEG, "mjpeg"},
{SPICE_VIDEO_CODEC_TYPE_VP8, "vp8"},
{0, NULL},
};
static int video_codec_caps[] = {
SPICE_DISPLAY_CAP_CODEC_MJPEG,
SPICE_DISPLAY_CAP_CODEC_VP8,
};