[Spice-devel] [PATCH v7 10/23] server: Add VP8 support to the GStreamer video encoder
Francois Gouget
fgouget at codeweavers.com
Wed Dec 16 07:19:11 PST 2015
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
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 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
--
2.6.2
More information about the Spice-devel
mailing list