[Spice-devel] [spice v11 06/27] server: Add VP8 support to the GStreamer video encoder
Francois Gouget
fgouget at codeweavers.com
Fri Mar 25 12:37:29 UTC 2016
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
configure.ac | 1 +
server/gstreamer-encoder.c | 74 ++++++++++++++++++++++++++++++++++++++++------
server/reds.c | 4 ++-
3 files changed, 69 insertions(+), 10 deletions(-)
diff --git a/configure.ac b/configure.ac
index 3142bf3..e60a005 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 d6eb1eb..5e6e52c 100644
--- a/server/gstreamer-encoder.c
+++ b/server/gstreamer-encoder.c
@@ -191,11 +191,42 @@ 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.
+ * - resize-allowed allows trading resolution for low bitrates while
+ * min-quantizer ensures the bitrate does not get needlessly high.
+ * - 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 resize-allowed=true error-resilient=true 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);
@@ -221,12 +252,27 @@ static gboolean configure_pipeline(SpiceGstEncoder *encoder,
/* Configure the encoder bitrate */
adjust_bit_rate(encoder);
- g_object_set(G_OBJECT(encoder->gstenc),
- "bitrate", (gint)encoder->bit_rate, 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);
+ switch (encoder->base.codec_type)
+ {
+ case SPICE_VIDEO_CODEC_TYPE_MJPEG:
+ g_object_set(G_OBJECT(encoder->gstenc),
+ "bitrate", (gint)encoder->bit_rate,
+ 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:
+ 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);
+ return FALSE;
+ }
/* Set the source caps */
set_appsrc_caps(encoder);
@@ -247,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);
@@ -511,7 +566,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 d9d5e06..4bb72dc 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3424,7 +3424,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)
@@ -3509,11 +3509,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.7.0
More information about the Spice-devel
mailing list