[Spice-devel] [PATCH v7 17/23] server: Add support for GStreamer 0.10
Francois Gouget
fgouget at codeweavers.com
Wed Dec 16 07:19:59 PST 2015
configure will use GStreamer 1.0 if present and fall back to
GStreamer 0.10 otherwise.
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
configure.ac | 36 ++++++++++++----
server/Makefile.am | 8 ++++
server/gstreamer-encoder.c | 102 +++++++++++++++++++++++++++++++++++++--------
server/reds.c | 2 +-
server/video-encoder.h | 2 +-
5 files changed, 123 insertions(+), 27 deletions(-)
Note:
* Obviously this patch can be skipped without impacting the rest of the
series.
diff --git a/configure.ac b/configure.ac
index b23c992..704026c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,28 +70,48 @@ dnl Check optional features
SPICE_CHECK_SMARTCARD
AC_ARG_ENABLE(gstreamer,
- AS_HELP_STRING([--enable-gstreamer=@<:@auto/yes/no@:>@],
- [Enable GStreamer 1.0 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
SPICE_CHECK_GSTREAMER(GSTREAMER_1_0, 1.0, [gstreamer-1.0 gstreamer-base-1.0 gstreamer-app-1.0 gstreamer-video-1.0],
- [enable_gstreamer="yes"
+ [enable_gstreamer="1.0"
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])
SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_1_0, [gst-plugins-ugly 1.0], [x264enc])
],
- [if test "x$enable_gstreamer" = "xyes"; then
+ [if 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
])
fi
AM_CONDITIONAL(HAVE_GSTREAMER_1_0, test "x$have_gstreamer_1_0" = "xyes")
-if test x"$gstreamer_missing" != x; then
- SPICE_WARNING([The following GStreamer $enable_gstreamer tools/elements are missing:$gstreamer_missing. The GStreamer video encoder can be built but may not work.])
+if test "x$enable_gstreamer" != "xno" && test "x$enable_gstreamer" != "x1.0"; then
+ SPICE_CHECK_GSTREAMER(GSTREAMER_0_10, 0.10, [gstreamer-0.10 gstreamer-base-0.10 gstreamer-app-0.10 gstreamer-video-0.10],
+ [enable_gstreamer="0.10"
+ SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_0_10, [gst-plugins-base 0.10], [appsrc appsink])
+ SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_0_10, [gstreamer-ffmpeg 0.10], [ffmpegcolorspace ffenc_mjpeg])
+ SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_0_10, [gst-plugins-bad 0.10], [vp8enc])
+ SPICE_CHECK_GSTREAMER_ELEMENTS($GST_INSPECT_0_10, [gst-plugins-ugly 0.10], [x264enc])
+ ],
+ [if 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
+ ])
fi
+AM_CONDITIONAL(HAVE_GSTREAMER_0_10, test "x$have_gstreamer_0_10" = "xyes")
+
+AS_IF([test "x$enable_gstreamer" = "xyes"],
+ [AC_MSG_ERROR("GStreamer support requested but not found")],
+ [test "x$enable_gstreamer" = "xauto"],
+ [enable_gstreamer="no"
+])
+AS_IF([test x"$missing_gstreamer_elements" = xyes],
+ [SPICE_WARNING([The GStreamer video encoder can be built but may not work.])
+])
AC_ARG_ENABLE([automated_tests],
AS_HELP_STRING([--enable-automated-tests], [Enable automated tests using spicy-screenshot (part of spice--gtk)]),,
@@ -318,7 +338,7 @@ AC_MSG_NOTICE([
LZ4 support: ${enable_lz4}
Smartcard: ${have_smartcard}
- GStreamer 1.0: ${have_gstreamer_1_0}
+ GStreamer: ${enable_gstreamer}
SASL support: ${enable_sasl}
Automated tests: ${enable_automated_tests}
Manual: ${have_asciidoc}
diff --git a/server/Makefile.am b/server/Makefile.am
index c80378b..63892e4 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
$(SASL_CFLAGS) \
$(SLIRP_CFLAGS) \
$(SMARTCARD_CFLAGS) \
+ $(GSTREAMER_0_10_CFLAGS) \
$(GSTREAMER_1_0_CFLAGS) \
$(SPICE_PROTOCOL_CFLAGS) \
$(SSL_CFLAGS) \
@@ -43,6 +44,7 @@ libspice_server_la_LIBADD = \
$(PIXMAN_LIBS) \
$(SASL_LIBS) \
$(SLIRP_LIBS) \
+ $(GSTREAMER_0_10_LIBS) \
$(GSTREAMER_1_0_LIBS) \
$(SSL_LIBS) \
$(Z_LIBS) \
@@ -152,6 +154,12 @@ libspice_server_la_SOURCES += \
$(NULL)
endif
+if HAVE_GSTREAMER_0_10
+libspice_server_la_SOURCES += \
+ gstreamer-encoder.c \
+ $(NULL)
+endif
+
if HAVE_GSTREAMER_1_0
libspice_server_la_SOURCES += \
gstreamer-encoder.c \
diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c
index bcf28e7..12512ca 100644
--- a/server/gstreamer-encoder.c
+++ b/server/gstreamer-encoder.c
@@ -33,19 +33,28 @@
#define SPICE_GST_DEFAULT_FPS 30
-#define DO_ZERO_COPY
+#ifndef HAVE_GSTREAMER_0_10
+# define DO_ZERO_COPY
+#endif
typedef struct {
SpiceBitmapFmt spice_format;
const char *format;
uint32_t bpp;
+ uint32_t depth;
+ uint32_t endianness;
+ uint32_t blue_mask;
+ uint32_t green_mask;
+ uint32_t red_mask;
} SpiceFormatForGStreamer;
typedef struct SpiceGstVideoBuffer {
VideoBuffer base;
GstBuffer *gst_buffer;
+#ifndef HAVE_GSTREAMER_0_10
GstMapInfo map;
+#endif
} SpiceGstVideoBuffer;
typedef struct {
@@ -265,6 +274,9 @@ typedef struct SpiceGstEncoder {
static void gst_video_buffer_free(VideoBuffer *video_buffer)
{
SpiceGstVideoBuffer *buffer = (SpiceGstVideoBuffer*)video_buffer;
+#ifndef HAVE_GSTREAMER_0_10
+ gst_buffer_unmap(buffer->gst_buffer, &buffer->map);
+#endif
gst_buffer_unref(buffer->gst_buffer);
free(buffer);
}
@@ -700,11 +712,11 @@ static SpiceFormatForGStreamer *map_format(SpiceBitmapFmt format)
* section-types-definitions.html documents.
*/
static SpiceFormatForGStreamer format_map[] = {
- {SPICE_BITMAP_FMT_RGBA, "BGRA", 32},
+ {SPICE_BITMAP_FMT_RGBA, "BGRA", 32, 24, 4321, 0xff000000, 0xff0000, 0xff00},
/* TODO: Test the other formats */
- {SPICE_BITMAP_FMT_32BIT, "BGRx", 32},
- {SPICE_BITMAP_FMT_24BIT, "BGR", 24},
- {SPICE_BITMAP_FMT_16BIT, "BGR15", 16},
+ {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;
@@ -726,8 +738,18 @@ static void set_appsrc_caps(SpiceGstEncoder *encoder)
gst_caps_unref(encoder->src_caps);
}
encoder->src_caps = gst_caps_new_simple(
+#ifdef HAVE_GSTREAMER_0_10
+ "video/x-raw-rgb",
+ "bpp", G_TYPE_INT, encoder->format->bpp,
+ "depth", G_TYPE_INT, encoder->format->depth,
+ "endianness", G_TYPE_INT, encoder->format->endianness,
+ "red_mask", G_TYPE_INT, encoder->format->red_mask,
+ "green_mask", G_TYPE_INT, encoder->format->green_mask,
+ "blue_mask", G_TYPE_INT, encoder->format->blue_mask,
+#else
"video/x-raw",
"format", G_TYPE_STRING, encoder->format->format,
+#endif
"width", G_TYPE_INT, encoder->width,
"height", G_TYPE_INT, encoder->height,
"framerate", GST_TYPE_FRACTION, get_source_fps(encoder), 1,
@@ -740,7 +762,11 @@ static const gchar* get_gst_codec_name(SpiceGstEncoder *encoder)
switch (encoder->base.codec_type)
{
case SPICE_VIDEO_CODEC_TYPE_MJPEG:
+#ifdef HAVE_GSTREAMER_0_10
+ return "ffenc_mjpeg";
+#else
return "avenc_mjpeg";
+#endif
case SPICE_VIDEO_CODEC_TYPE_VP8:
return "vp8enc";
case SPICE_VIDEO_CODEC_TYPE_H264:
@@ -754,13 +780,18 @@ static const gchar* get_gst_codec_name(SpiceGstEncoder *encoder)
static gboolean create_pipeline(SpiceGstEncoder *encoder)
{
+#ifdef HAVE_GSTREAMER_0_10
+ const gchar *converter = "ffmpegcolorspace";
+#else
+ const gchar *converter = "videoconvert";
+#endif
const gchar* gstenc_name = get_gst_codec_name(encoder);
if (!gstenc_name) {
return FALSE;
}
GError *err = NULL;
- gchar *desc = g_strdup_printf("appsrc name=src format=2 do-timestamp=true ! videoconvert ! %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);
@@ -868,10 +899,17 @@ static gboolean configure_pipeline(SpiceGstEncoder *encoder,
g_object_set(G_OBJECT(encoder->gstenc),
"resize-allowed", TRUE, /* for very low bit rates */
"min-quantizer", 10, /* seems virtually lossless */
+#ifdef HAVE_GSTREAMER_0_10
+ "mode", 1, /* CBR */
+ "max-latency", 0, /* zero-frame latency */
+ "error-resilient", TRUE, /* for client frame drops */
+ "speed", 7, /* ultrafast */
+#else
"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 */
+#endif
"threads", core_count - 1,
NULL);
break;
@@ -1072,6 +1110,7 @@ static inline int chunk_copy(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap
return TRUE;
}
+#ifndef HAVE_GSTREAMER_0_10
/* A helper for push_raw_frame() */
static uint8_t *allocate_and_map_memory(gsize size, GstMapInfo *map, GstBuffer *buffer)
{
@@ -1088,6 +1127,7 @@ static uint8_t *allocate_and_map_memory(gsize size, GstMapInfo *map, GstBuffer *
}
return map->data;
}
+#endif
/* A helper for spice_gst_encoder_encode_frame() */
static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
@@ -1096,9 +1136,14 @@ static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
const uint32_t height = src->bottom - src->top;
const uint32_t stream_stride = (src->right - src->left) * encoder->format->bpp / 8;
uint32_t len = stream_stride * height;
+#ifdef HAVE_GSTREAMER_0_10
+ GstBuffer *buffer = gst_buffer_new_and_alloc(len);
+ uint8_t *dst = GST_BUFFER_DATA(buffer);
+#else
GstBuffer *buffer = gst_buffer_new();
/* TODO Use GST_MAP_INFO_INIT once GStreamer 1.4.5 is no longer relevant */
GstMapInfo map = { .memory = NULL };
+#endif
/* 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.
@@ -1110,15 +1155,19 @@ static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
/* We have to do a line-by-line copy because for each we have to leave
* out pixels on the left or right.
*/
+#ifndef HAVE_GSTREAMER_0_10
uint8_t *dst = allocate_and_map_memory(len, &map, buffer);
if (!dst) {
return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
+#endif
chunk_offset += src->left * encoder->format->bpp / 8;
if (!line_copy(encoder, bitmap, chunk_offset, stream_stride, height, dst)) {
+#ifndef HAVE_GSTREAMER_0_10
gst_memory_unmap(map.memory, &map);
gst_memory_unref(map.memory);
+#endif
gst_buffer_unref(buffer);
return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
@@ -1137,23 +1186,30 @@ static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
*/
#endif
- if (len) {
- uint8_t *dst = allocate_and_map_memory(len, &map, buffer);
- if (!dst) {
- return VIDEO_ENCODER_FRAME_UNSUPPORTED;
- }
- if (!chunk_copy(encoder, bitmap, chunk_index, chunk_offset, len, dst)) {
- gst_memory_unmap(map.memory, &map);
- gst_memory_unref(map.memory);
- gst_buffer_unref(buffer);
- return VIDEO_ENCODER_FRAME_UNSUPPORTED;
- }
+#ifndef HAVE_GSTREAMER_0_10
+ if (len && !allocate_and_map_memory(len, &map, buffer)) {
+ return VIDEO_ENCODER_FRAME_UNSUPPORTED;
+ }
+ uint8_t *dst = map.data;
+#endif
+
+ if (!chunk_copy(encoder, bitmap, chunk_index, chunk_offset, len, dst)) {
+#ifndef HAVE_GSTREAMER_0_10
+ gst_memory_unmap(map.memory, &map);
+ gst_memory_unref(map.memory);
+#endif
+ gst_buffer_unref(buffer);
+ return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
}
+#ifdef HAVE_GSTREAMER_0_10
+ gst_buffer_set_caps(buffer, encoder->src_caps);
+#else
if (map.memory) {
gst_memory_unmap(map.memory, &map);
gst_buffer_append_memory(buffer, map.memory);
}
+#endif
GST_BUFFER_OFFSET(buffer) = encoder->frame++;
GstFlowReturn ret = gst_app_src_push_buffer(encoder->appsrc, buffer);
@@ -1169,6 +1225,17 @@ static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
static int pull_compressed_buffer(SpiceGstEncoder *encoder,
VideoBuffer **video_buffer)
{
+#ifdef HAVE_GSTREAMER_0_10
+ SpiceGstVideoBuffer *buffer = create_gst_video_buffer();
+ buffer->gst_buffer = gst_app_sink_pull_buffer(encoder->appsink);
+ if (buffer->gst_buffer) {
+ buffer->base.data = GST_BUFFER_DATA(buffer->gst_buffer);
+ buffer->base.size = GST_BUFFER_SIZE(buffer->gst_buffer);
+ *video_buffer = (VideoBuffer*)buffer;
+ return VIDEO_ENCODER_FRAME_ENCODE_DONE;
+ }
+ buffer->base.free((VideoBuffer*)buffer);
+#else
GstSample *sample = gst_app_sink_pull_sample(encoder->appsink);
if (sample) {
SpiceGstVideoBuffer *buffer = create_gst_video_buffer();
@@ -1185,6 +1252,7 @@ static int pull_compressed_buffer(SpiceGstEncoder *encoder,
buffer->base.free((VideoBuffer*)buffer);
gst_sample_unref(sample);
}
+#endif
spice_debug("failed to pull the compressed buffer");
return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
diff --git a/server/reds.c b/server/reds.c
index a2c812e..ad16c60 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3412,7 +3412,7 @@ static const EnumNames video_encoder_names[] = {
static new_video_encoder_t video_encoder_procs[] = {
&mjpeg_encoder_new,
-#ifdef HAVE_GSTREAMER_1_0
+#if defined(HAVE_GSTREAMER_1_0) || defined(HAVE_GSTREAMER_0_10)
&gstreamer_encoder_new,
#else
NULL,
diff --git a/server/video-encoder.h b/server/video-encoder.h
index edfb392..5ec8f57 100644
--- a/server/video-encoder.h
+++ b/server/video-encoder.h
@@ -186,7 +186,7 @@ VideoEncoder* mjpeg_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
void *cbs_opaque);
-#ifdef HAVE_GSTREAMER_1_0
+#if defined(HAVE_GSTREAMER_1_0) || defined(HAVE_GSTREAMER_0_10)
VideoEncoder* gstreamer_encoder_new(SpiceVideoCodecType codec_type,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs,
--
2.6.2
More information about the Spice-devel
mailing list