Signed-off-by: Francois Gouget <fgouget@xxxxxxxxxxxxxxx> --- configure.ac | 4 +++ server/gstreamer-encoder.c | 73 +++++++++++++++++++++++++++++++++++++++------- server/video-encoder.h | 2 +- 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 174f01f..a902e84 100644 --- a/configure.ac +++ b/configure.ac @@ -130,6 +130,10 @@ AC_SUBST([SPICE_PROTOCOL_MIN_VER]) PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.22]) AS_VAR_APPEND([SPICE_REQUIRES], [" glib-2.0 >= 2.22"]) +AC_CHECK_LIB(glib-2.0, g_get_num_processors, + AC_DEFINE([HAVE_G_GET_NUMPROCESSORS], 1, [Defined if we have g_get_num_processors()]),, + $GLIB2_LIBS) + PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7) AC_SUBST(PIXMAN_CFLAGS) AC_SUBST(PIXMAN_LIBS) diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c index edf87df..6dab5f9 100644 --- a/server/gstreamer-encoder.c +++ b/server/gstreamer-encoder.c @@ -192,10 +192,26 @@ static void set_appsrc_caps(SpiceGstEncoder *encoder) /* A helper for spice_gst_encoder_encode_frame() */ static gboolean create_pipeline(SpiceGstEncoder *encoder) { + const gchar* gstenc_name; + switch (encoder->base.codec_type) + { + case SPICE_VIDEO_CODEC_TYPE_MJPEG: + gstenc_name = "avenc_mjpeg"; + break; + case SPICE_VIDEO_CODEC_TYPE_VP8: + gstenc_name = "vp8enc"; + 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; - const gchar *desc = "appsrc name=src format=2 do-timestamp=true ! videoconvert ! avenc_mjpeg name=encoder ! appsink name=sink"; + gchar *desc = g_strdup_printf("appsrc name=src format=2 do-timestamp=true ! videoconvert ! %s name=encoder ! appsink name=sink", gstenc_name); spice_debug("GStreamer pipeline: %s", desc); encoder->pipeline = gst_parse_launch_full(desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &err); + g_free(desc); if (!encoder->pipeline || err) { spice_warning("GStreamer error: %s", err->message); g_clear_error(&err); @@ -221,18 +237,45 @@ static gboolean configure_pipeline(SpiceGstEncoder *encoder, /* Configure the encoder bitrate, frame latency, etc. */ adjust_bit_rate(encoder); - g_object_set(G_OBJECT(encoder->gstenc), - "bitrate", encoder->bit_rate, - "max-threads", 1, /* zero-frame latency */ - NULL); + switch (encoder->base.codec_type) { + case SPICE_VIDEO_CODEC_TYPE_MJPEG: + g_object_set(G_OBJECT(encoder->gstenc), + "bitrate", encoder->bit_rate, + "max-threads", 1, /* zero-frame latency */ + NULL); + + /* 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); + break; + case SPICE_VIDEO_CODEC_TYPE_VP8: { + /* See http://www.webmproject.org/docs/encoder-parameters/ */ +#ifdef HAVE_G_GET_NUMPROCESSORS + int core_count = g_get_num_processors(); +#else + int core_count = 1; +#endif + g_object_set(G_OBJECT(encoder->gstenc), + "resize-allowed", TRUE, /* for very low bit rates */ + "target-bitrate", encoder->bit_rate, + "end-usage", 1, /* CBR */ + "lag-in-frames", 0, /* zero-frame latency */ + "error-resilient", 1, /* for client frame drops */ + "deadline", 1000000 / get_source_fps(encoder) / 2, /* usec */ + "threads", core_count - 1, + NULL); + break; + } + default: + /* gstreamer_encoder_new() should have rejected this codec type */ + spice_warning("unknown encoder type %d", encoder->base.codec_type); + free_pipeline(encoder); + return FALSE; + } /* Set the source caps */ set_appsrc_caps(encoder); - /* 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); - /* Start playing */ spice_debug("setting state to PLAYING"); if (gst_element_set_state(encoder->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { @@ -250,6 +293,15 @@ static void reconfigure_pipeline(SpiceGstEncoder *encoder) if (!is_pipeline_configured(encoder)) { return; } + if (encoder->base.codec_type == SPICE_VIDEO_CODEC_TYPE_VP8) { + /* vp8enc fails to account for caps changes that modify the frame size + * and complains about the buffer size. + * So recreate the pipeline from scratch. + */ + free_pipeline(encoder); + return; + } + if (gst_element_set_state(encoder->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { spice_debug("GStreamer error: could not pause the pipeline, rebuilding it instead"); free_pipeline(encoder); @@ -499,7 +551,8 @@ VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type, VideoEncoderRateControlCbs *cbs, void *cbs_opaque) { - 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/video-encoder.h b/server/video-encoder.h index b8a21e1..286ba28 100644 --- a/server/video-encoder.h +++ b/server/video-encoder.h @@ -179,6 +179,6 @@ VideoEncoder* gstreamer_encoder_new(SpiceVideoCodecType codec_type, void *cbs_opaque); #endif -#define VIDEO_ENCODER_DEFAULT_PREFERENCE "spice:mjpeg;gstreamer:mjpeg" +#define VIDEO_ENCODER_DEFAULT_PREFERENCE "spice:mjpeg;gstreamer:mjpeg;gstreamer:vp8" #endif -- 2.6.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel