[Spice-devel,v5,08/20] server: Add GStreamer 1.0 support.

Submitted by Francois Gouget on Aug. 27, 2015, 7:01 p.m.

Details

Message ID alpine.DEB.2.20.1508272009070.22172@amboise
State New
Headers show

Not browsing as part of any series.

Commit Message

Francois Gouget Aug. 27, 2015, 7:01 p.m.
By default GStreamer 1.0 is used if available, otherwise GStreamer 0.10 is used and Spice is compiled without GStreamer support only as a last resort.
It's possible to explicitly require a specific Gstreamer version with configure --enable-gstreamer=1.0 and --enable-gstreamer=0.10; or for any version with --enable-gstreamer=yes; or to disable GStreamer support with --disable-gstreamer.

Signed-off-by: Francois Gouget <fgouget@codeweavers.com>
---

Changes since take 4:
 - Fixed setting the deadline vp8enc property (it's in microseconds, 
   not nanoseconds).

 - Store the GStreamer 1.0 buffer format string in the 
   SpiceFormatForGStreamer structure.


 configure.ac               | 35 ++++++++++++++----
 server/Makefile.am         | 12 +++++--
 server/gstreamer_encoder.c | 88 ++++++++++++++++++++++++++++++++++++++++------
 server/red_dispatcher.c    |  2 +-
 4 files changed, 117 insertions(+), 20 deletions(-)

Patch hide | download patch | download mbox

diff --git a/configure.ac b/configure.ac
index d775bc1..39297ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -81,14 +81,32 @@  SPICE_CHECK_SMARTCARD([SMARTCARD])
 AM_CONDITIONAL(SUPPORT_SMARTCARD, test "x$have_smartcard" = "xyes")
 
 AC_ARG_ENABLE(gstreamer,
-              AS_HELP_STRING([--enable-gstreamer=@<:@auto/yes/no@:>@],
-                             [Enable GStreamer 0.10 support]),
+              AS_HELP_STRING([--enable-gstreamer=@<:@auto/0.10/1.0/yes/no@:>@],
+                             [Enable GStreamer support]),
               [],
               [enable_gstreamer="auto"])
 
-if test "x$enable_gstreamer" != "xno"; then
+if test "x$enable_gstreamer" != "xno" && test "x$enable_gstreamer" != "x0.10"; then
+    PKG_CHECK_MODULES(GSTREAMER_1_0, [gstreamer-1.0, gstreamer-app-1.0],
+                      [enable_gstreamer="1.0"
+                       have_gstreamer_1_0="yes"],
+                      [have_gstreamer_1_0="no"])
+    if test "x$have_gstreamer_1_0" = "xyes"; then
+        AC_SUBST(GSTREAMER_1_0_CFLAGS)
+        AC_SUBST(GSTREAMER_1_0_LIBS)
+        AS_VAR_APPEND([SPICE_REQUIRES], [" gstreamer-1.0 gstreamer-app-1.0"])
+        AC_DEFINE([HAVE_GSTREAMER_1_0], [1], [Define if supporting GStreamer 1.0])
+    elif test "x$enable_gstreamer" = "x1.0"; 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.])
+    fi
+else
+    have_gstreamer_1_0="no"
+fi
+AM_CONDITIONAL(SUPPORT_GSTREAMER_1_0, test "x$have_gstreamer_1_0" = "xyes")
+
+if test "x$enable_gstreamer" != "xno" && test "x$enable_gstreamer" != "x1.0"; then
     PKG_CHECK_MODULES(GSTREAMER_0_10, [gstreamer-0.10, gstreamer-app-0.10],
-                      [enable_gstreamer="yes"
+                      [enable_gstreamer="0.10"
                        have_gstreamer_0_10="yes"],
                       [have_gstreamer_0_10="no"])
     if test "x$have_gstreamer_0_10" = "xyes"; then
@@ -96,7 +114,7 @@  if test "x$enable_gstreamer" != "xno"; then
         AC_SUBST(GSTREAMER_0_10_LIBS)
         AS_VAR_APPEND([SPICE_REQUIRES], [" gstreamer-0.10 gstreamer-app-0.10"])
         AC_DEFINE([HAVE_GSTREAMER_0_10], [1], [Define if supporting GStreamer 0.10])
-    elif test "x$enable_gstreamer" = "xyes"; then
+    elif test "x$enable_gstreamer" = "x0.10"; then
         AC_MSG_ERROR([GStreamer 0.10 support requested but not found. You may set GSTREAMER_0_10_CFLAGS and GSTREAMER_0_10_LIBS to avoid the need to call pkg-config.])
     fi
 else
@@ -104,6 +122,11 @@  else
 fi
 AM_CONDITIONAL(SUPPORT_GSTREAMER_0_10, test "x$have_gstreamer_0_10" = "xyes")
 
+if test "x$enable_gstreamer" = "xyes"; then
+    AC_MSG_ERROR("GStreamer support requested but not found")
+fi
+AS_IF([test "x$enable_gstreamer" = "xauto"], [enable_gstreamer="no"])
+
 AC_ARG_ENABLE([automated_tests],
               AS_HELP_STRING([--enable-automated-tests], [Enable automated tests using spicy-screenshot (part of spice--gtk)]),,
               [enable_automated_tests="no"])
@@ -343,7 +366,7 @@  echo "
 
         Smartcard:                ${have_smartcard}
 
-        GStreamer 0.10:           ${have_gstreamer_0_10}
+        GStreamer:                ${enable_gstreamer}
 
         SASL support:             ${enable_sasl}
 
diff --git a/server/Makefile.am b/server/Makefile.am
index 9dad6f6..091e94d 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -12,6 +12,7 @@  AM_CPPFLAGS =					\
 	$(SLIRP_CFLAGS)				\
 	$(SMARTCARD_CFLAGS)			\
 	$(GSTREAMER_0_10_CFLAGS)		\
+	$(GSTREAMER_1_0_CFLAGS)			\
 	$(SPICE_PROTOCOL_CFLAGS)		\
 	$(SSL_CFLAGS)				\
 	$(VISIBILITY_HIDDEN_CFLAGS)		\
@@ -43,7 +44,8 @@  libspice_server_la_LIBADD =						\
 	$(PIXMAN_LIBS)							\
 	$(SASL_LIBS)							\
 	$(SLIRP_LIBS)							\
-	$(GSTREAMER_0_10_LIBS)							\
+	$(GSTREAMER_0_10_LIBS)						\
+	$(GSTREAMER_1_0_LIBS)						\
 	$(SSL_LIBS)							\
 	$(Z_LIBS)							\
 	$(SPICE_NONPKGCONFIG_LIBS)					\
@@ -146,7 +148,13 @@  endif
 
 if SUPPORT_GSTREAMER_0_10
 libspice_server_la_SOURCES +=	\
-	gstreamer_encoder.c		\
+	gstreamer_encoder.c			\
+	$(NULL)
+endif
+
+if SUPPORT_GSTREAMER_1_0
+libspice_server_la_SOURCES +=	\
+	gstreamer_encoder.c			\
 	$(NULL)
 endif
 
diff --git a/server/gstreamer_encoder.c b/server/gstreamer_encoder.c
index 425be55..f20d0f8 100644
--- a/server/gstreamer_encoder.c
+++ b/server/gstreamer_encoder.c
@@ -38,6 +38,7 @@  typedef struct GstEncoder GstEncoder;
 
 typedef struct {
     SpiceBitmapFmt spice_format;
+    const char *format;
     uint32_t bpp;
     uint32_t depth;
     uint32_t endianness;
@@ -49,6 +50,9 @@  typedef struct {
 struct GstVideoBuffer {
     VideoBuffer base;
     GstBuffer *gst_buffer;
+#ifndef HAVE_GSTREAMER_0_10
+    GstMapInfo map_info;
+#endif
     gboolean persistent;
 };
 
@@ -108,6 +112,9 @@  static inline GstVideoBuffer* gst_video_buffer_ref(GstVideoBuffer *buffer)
 static void gst_video_buffer_unref(GstVideoBuffer *buffer)
 {
     if (--buffer->base.ref_count == 0) {
+#ifndef HAVE_GSTREAMER_0_10
+        gst_buffer_unmap(buffer->gst_buffer, &buffer->map_info);
+#endif
         gst_buffer_unref(buffer->gst_buffer);
         if (!buffer->persistent) {
             free(buffer);
@@ -191,13 +198,15 @@  static void adjust_bit_rate(GstEncoder *encoder)
 /* A helper for gst_encoder_encode_frame(). */
 static SpiceFormatForGStreamer *map_format(SpiceBitmapFmt format)
 {
-    /* See GStreamer's section-types-definitions.html document */
+    /* See GStreamer's part-mediatype-video-raw.txt and
+     * section-types-definitions.html documents.
+     */
     static SpiceFormatForGStreamer format_map[] =  {
-        {SPICE_BITMAP_FMT_RGBA, 32, 24, 4321, 0xff000000, 0xff0000, 0xff00},
+        {SPICE_BITMAP_FMT_RGBA, "BGRA", 32, 24, 4321, 0xff000000, 0xff0000, 0xff00},
         /* TODO: Test the other formats */
-        {SPICE_BITMAP_FMT_32BIT, 32, 24, 4321, 0xff000000, 0xff0000, 0xff00},
-        {SPICE_BITMAP_FMT_24BIT, 24, 24, 4321, 0xff0000, 0xff00, 0xff},
-        {SPICE_BITMAP_FMT_16BIT, 16, 15, 4321, 0x001f, 0x03E0, 0x7C00},
+        {SPICE_BITMAP_FMT_32BIT, "BGRx", 32, 24, 4321, 0xff000000, 0xff0000, 0xff00},
+        {SPICE_BITMAP_FMT_24BIT, "BGR", 24, 24, 4321, 0xff0000, 0xff00, 0xff},
+        {SPICE_BITMAP_FMT_16BIT, "BGR15", 16, 15, 4321, 0x001f, 0x03E0, 0x7C00},
     };
 
     int i;
@@ -242,7 +251,11 @@  static gboolean construct_pipeline(GstEncoder *encoder, const SpiceBitmap *bitma
     switch (encoder->base.codec_type)
     {
     case SPICE_VIDEO_CODEC_TYPE_MJPEG:
+#ifdef HAVE_GSTREAMER_0_10
         gstenc_name = "ffenc_mjpeg";
+#else
+        gstenc_name = "avenc_mjpeg";
+#endif
         break;
     case SPICE_VIDEO_CODEC_TYPE_VP8:
         gstenc_name = "vp8enc";
@@ -251,9 +264,14 @@  static gboolean construct_pipeline(GstEncoder *encoder, const SpiceBitmap *bitma
         spice_warning("unsupported codec type %d", encoder->base.codec_type);
         return FALSE;
     }
+#ifdef HAVE_GSTREAMER_0_10
+    const gchar *converter = "ffmpegcolorspace";
+#else
+    const gchar *converter = "videoconvert";
+#endif
 
     GError *err = NULL;
-    gchar *desc = g_strdup_printf("appsrc name=src format=2 do-timestamp=true ! ffmpegcolorspace ! %s name=encoder ! appsink name=sink", gstenc_name);
+    gchar *desc = g_strdup_printf("appsrc name=src format=2 do-timestamp=true ! %s ! %s name=encoder ! appsink name=sink", converter, 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);
@@ -272,8 +290,13 @@  static gboolean construct_pipeline(GstEncoder *encoder, const SpiceBitmap *bitma
 
     /* Configure the encoders for a zero-frame latency, and real-time speed */
     adjust_bit_rate(encoder);
-    g_object_set(G_OBJECT(encoder->gstenc), "bitrate", encoder->bit_rate, NULL);
-    if (encoder->base.codec_type == SPICE_VIDEO_CODEC_TYPE_VP8) {
+    switch (encoder->base.codec_type) {
+    case SPICE_VIDEO_CODEC_TYPE_MJPEG:
+        g_object_set(G_OBJECT(encoder->gstenc),
+                     "bitrate", encoder->bit_rate,
+                     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();
@@ -282,13 +305,28 @@  static gboolean construct_pipeline(GstEncoder *encoder, const SpiceBitmap *bitma
 #endif
         g_object_set(G_OBJECT(encoder->gstenc),
                      "resize-allowed", TRUE, /* for very low bit rates */
+#ifdef HAVE_GSTREAMER_0_10
                      "mode", 1, /* CBR */
+                     "bitrate", encoder->bit_rate,
                      "max-latency", 0, /* zero-frame latency */
                      "error-resilient", TRUE, /* for client frame drops */
                      "speed", 7, /* ultrafast */
+#else
+                     "end-usage", 1, /* CBR */
+                     "target-bitrate", encoder->bit_rate,
+                     "lag-in-frames", 0, /* zero-frame latency */
+                     "error-resilient", 1, /* for client frame drops */
+                     "deadline", 1000000 / get_source_fps(encoder) / 2, /* usec */
+#endif
                      "threads", core_count - 1,
                      NULL);
-   }
+        break;
+        }
+    default:
+        spice_warning("unknown encoder type %d", encoder->base.codec_type);
+        reset_pipeline(encoder);
+        return FALSE;
+    }
 
     /* Set the source caps */
     set_appsrc_caps(encoder);
@@ -368,7 +406,14 @@  static int push_raw_frame(GstEncoder *encoder, const SpiceBitmap *bitmap,
     const uint32_t stream_stride = (src->right - src->left) * encoder->format->bpp / 8;
     uint32_t len = stream_stride * height;
     GstBuffer *buffer = gst_buffer_new_and_alloc(len);
-    uint8_t *dst = GST_BUFFER_DATA(buffer);
+#ifdef HAVE_GSTREAMER_0_10
+    uint8_t *b = GST_BUFFER_DATA(buffer);
+#else
+    GstMapInfo map;
+    gst_buffer_map(buffer, &map, GST_MAP_WRITE);
+    uint8_t *b = map.data;
+#endif
+    uint8_t *dst = b;
 
     /* Note that we should not reorder the lines, even if top_down is false.
      * It just changes the number of lines to skip at the start of the bitmap.
@@ -412,8 +457,11 @@  static int push_raw_frame(GstEncoder *encoder, const SpiceBitmap *bitmap,
         }
         spice_assert(len == 0);
     }
-
+#ifdef HAVE_GSTREAMER_0_10
     gst_buffer_set_caps(buffer, encoder->src_caps);
+#else
+    gst_buffer_unmap(buffer, &map);
+#endif
     GST_BUFFER_OFFSET(buffer) = encoder->frame++;
 
     GstFlowReturn ret = gst_app_src_push_buffer(encoder->appsrc, buffer);
@@ -436,6 +484,7 @@  static int pull_compressed_buffer(GstEncoder *encoder, GstVideoBuffer **buffer)
     } else {
         video_buffer = create_gst_video_buffer(FALSE);
     }
+#ifdef HAVE_GSTREAMER_0_10
     video_buffer->gst_buffer = gst_app_sink_pull_buffer(encoder->appsink);
     if (video_buffer->gst_buffer) {
         video_buffer->base.data = GST_BUFFER_DATA(video_buffer->gst_buffer);
@@ -444,6 +493,23 @@  static int pull_compressed_buffer(GstEncoder *encoder, GstVideoBuffer **buffer)
         return VIDEO_ENCODER_FRAME_ENCODE_DONE;
     }
     video_buffer->base.unref(video_buffer);
+#else
+    GstSample *sample = gst_app_sink_pull_sample(encoder->appsink);
+    if (sample) {
+        video_buffer->gst_buffer = gst_sample_get_buffer(sample);
+        if (video_buffer->gst_buffer) {
+            gst_buffer_ref(video_buffer->gst_buffer);
+            video_buffer->base.size = gst_buffer_get_size(video_buffer->gst_buffer);
+            gst_buffer_map(video_buffer->gst_buffer, &video_buffer->map_info,
+                           GST_MAP_READ);
+            video_buffer->base.data = video_buffer->map_info.data;
+            *buffer = video_buffer;
+            gst_sample_unref(sample);
+            return VIDEO_ENCODER_FRAME_ENCODE_DONE;
+        }
+        gst_sample_unref(sample);
+    }
+#endif
     return VIDEO_ENCODER_FRAME_UNSUPPORTED;
 }
 
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index acceb53..105bdd3 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -258,7 +258,7 @@  static const EnumNames video_encoder_names[] = {
 
 static create_video_encoder_proc video_encoder_procs[] = {
     &create_mjpeg_encoder,
-#ifdef HAVE_GSTREAMER_0_10
+#if defined(HAVE_GSTREAMER_0_10) || defined(HAVE_GSTREAMER_1_0)
     &create_gstreamer_encoder,
 #else
     NULL,