[Spice-devel,v7,10/23] server: Add VP8 support to the GStreamer video encoder

Submitted by Francois Gouget on Dec. 16, 2015, 3:19 p.m.

Details

Message ID alpine.DEB.2.20.1512161125040.16981@amboise
State New
Headers show
Series "Add GStreamer support for video streaming" ( rev: 1 ) in Spice

Not browsing as part of any series.

Commit Message

Francois Gouget Dec. 16, 2015, 3:19 p.m.
Signed-off-by: Francois Gouget <fgouget@codeweavers.com>
---
 configure.ac               |  4 +++
 server/gstreamer-encoder.c | 73 +++++++++++++++++++++++++++++++++++++++-------
 server/video-encoder.h     |  2 +-
 3 files changed, 68 insertions(+), 11 deletions(-)

Patch hide | download patch | download mbox

diff --git a/configure.ac b/configure.ac
index 20cf429..b23c992 100644
--- a/configure.ac
+++ b/configure.ac
@@ -129,6 +129,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 d74c8f7..11be91b 100644
--- a/server/gstreamer-encoder.c
+++ b/server/gstreamer-encoder.c
@@ -222,10 +222,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);
@@ -251,18 +267,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) {
@@ -280,6 +323,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);
@@ -647,7 +699,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 8f19039..db520eb 100644
--- a/server/video-encoder.h
+++ b/server/video-encoder.h
@@ -193,6 +193,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