Signed-off-by: Francois Gouget <fgouget@xxxxxxxxxxxxxxx> --- configure.ac | 1 + server/gstreamer-encoder.c | 67 ++++++++++++++++++++++++++++++++++++++++------ server/reds.c | 4 ++- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 9904bc8..9bd8ccb 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,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.]) diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c index e6fddd8..4c50791 100644 --- a/server/gstreamer-encoder.c +++ b/server/gstreamer-encoder.c @@ -192,11 +192,44 @@ static void set_appsrc_caps(SpiceGstEncoder *encoder) /* A helper for spice_gst_encoder_encode_frame() */ 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); @@ -210,9 +243,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 | @@ -225,8 +260,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() */ @@ -526,7 +576,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)) { diff --git a/server/reds.c b/server/reds.c index 01ca725..4230f91 100644 --- a/server/reds.c +++ b/server/reds.c @@ -3515,7 +3515,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) @@ -3602,11 +3602,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, }; -- 2.8.0.rc3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel