[Spice-devel] [spice v14 10/29] server: Handle and recover from GStreamer encoding errors

Francois Gouget fgouget at codeweavers.com
Wed May 4 09:42:40 UTC 2016


This avoids getting stuck if the codec is buggy or fails to encode a
frame for whatever reason (e.g. odd frame size).

Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
 server/gstreamer-encoder.c | 105 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 90 insertions(+), 15 deletions(-)

diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c
index c61afea..0e69b05 100644
--- a/server/gstreamer-encoder.c
+++ b/server/gstreamer-encoder.c
@@ -76,6 +76,11 @@ typedef struct SpiceGstEncoder {
 #   define SPICE_GST_VIDEO_PIPELINE_CAPS     0x4
     uint32_t set_pipeline;
 
+    /* Output buffer */
+    GMutex outbuf_mutex;
+    GCond outbuf_cond;
+    VideoBuffer *outbuf;
+
     /* The bit rate target for the outgoing network stream. (bits per second) */
     uint64_t bit_rate;
 
@@ -215,6 +220,56 @@ static void set_appsrc_caps(SpiceGstEncoder *encoder)
     gst_app_src_set_caps(encoder->appsrc, encoder->src_caps);
 }
 
+static GstBusSyncReply handle_pipeline_message(GstBus *bus, GstMessage *msg, gpointer video_encoder)
+{
+    SpiceGstEncoder *encoder = video_encoder;
+
+    if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) {
+        GError *err = NULL;
+        gchar *debug_info = NULL;
+        gst_message_parse_error(msg, &err, &debug_info);
+        spice_warning("GStreamer error from element %s: %s",
+                      GST_OBJECT_NAME(msg->src), err->message);
+        if (debug_info) {
+            spice_debug("debug details: %s", debug_info);
+            g_free(debug_info);
+        }
+        g_clear_error(&err);
+
+        /* Unblock the main thread */
+        g_mutex_lock(&encoder->outbuf_mutex);
+        encoder->outbuf = (VideoBuffer*)create_gst_video_buffer();
+        g_cond_signal(&encoder->outbuf_cond);
+        g_mutex_unlock(&encoder->outbuf_mutex);
+    }
+    return GST_BUS_PASS;
+}
+
+static GstFlowReturn new_sample(GstAppSink *gstappsink, gpointer video_encoder)
+{
+    SpiceGstEncoder *encoder = (SpiceGstEncoder*)video_encoder;
+    SpiceGstVideoBuffer *outbuf = create_gst_video_buffer();
+
+    GstSample *sample = gst_app_sink_pull_sample(encoder->appsink);
+    if (sample) {
+        outbuf->gst_buffer = gst_sample_get_buffer(sample);
+        gst_buffer_ref(outbuf->gst_buffer);
+        gst_sample_unref(sample);
+        if (gst_buffer_map(outbuf->gst_buffer, &outbuf->map, GST_MAP_READ)) {
+            outbuf->base.data = outbuf->map.data;
+            outbuf->base.size = gst_buffer_get_size(outbuf->gst_buffer);
+        }
+    }
+
+    /* Notify the main thread that the output buffer is ready */
+    g_mutex_lock(&encoder->outbuf_mutex);
+    encoder->outbuf = (VideoBuffer*)outbuf;
+    g_cond_signal(&encoder->outbuf_cond);
+    g_mutex_unlock(&encoder->outbuf_mutex);
+
+    return GST_FLOW_OK;
+}
+
 static int physical_core_count = 0;
 static int get_physical_core_count(void)
 {
@@ -294,6 +349,22 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
     encoder->gstenc = gst_bin_get_by_name(GST_BIN(encoder->pipeline), "encoder");
     encoder->appsink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(encoder->pipeline), "sink"));
 
+#ifdef HAVE_GSTREAMER_0_10
+    GstAppSinkCallbacks appsink_cbs = {NULL, NULL, &new_sample, NULL, {NULL}};
+#else
+    GstAppSinkCallbacks appsink_cbs = {NULL, NULL, &new_sample, {NULL}};
+#endif
+    gst_app_sink_set_callbacks(encoder->appsink, &appsink_cbs, encoder, NULL);
+
+    /* Hook into the bus so we can handle errors */
+    GstBus *bus = gst_element_get_bus(encoder->pipeline);
+#ifdef HAVE_GSTREAMER_0_10
+    gst_bus_set_sync_handler(bus, handle_pipeline_message, encoder);
+#else
+    gst_bus_set_sync_handler(bus, handle_pipeline_message, encoder, NULL);
+#endif
+    gst_object_unref(bus);
+
     if (encoder->base.codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG) {
         /* See https://bugzilla.gnome.org/show_bug.cgi?id=753257 */
         spice_debug("removing the pipeline clock");
@@ -497,23 +568,21 @@ static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
 static int pull_compressed_buffer(SpiceGstEncoder *encoder,
                                   VideoBuffer **outbuf)
 {
-    GstSample *sample = gst_app_sink_pull_sample(encoder->appsink);
-    if (sample) {
-        SpiceGstVideoBuffer *buffer = create_gst_video_buffer();
-        buffer->gst_buffer = gst_sample_get_buffer(sample);
-        if (buffer->gst_buffer &&
-            gst_buffer_map(buffer->gst_buffer, &buffer->map, GST_MAP_READ)) {
-            buffer->base.data = buffer->map.data;
-            buffer->base.size = gst_buffer_get_size(buffer->gst_buffer);
-            *outbuf = (VideoBuffer*)buffer;
-            gst_buffer_ref(buffer->gst_buffer);
-            gst_sample_unref(sample);
-            return VIDEO_ENCODER_FRAME_ENCODE_DONE;
-        }
-        buffer->base.free((VideoBuffer*)buffer);
-        gst_sample_unref(sample);
+    g_mutex_lock(&encoder->outbuf_mutex);
+    while (!encoder->outbuf) {
+        g_cond_wait(&encoder->outbuf_cond, &encoder->outbuf_mutex);
+    }
+    *outbuf = encoder->outbuf;
+    encoder->outbuf = NULL;
+    g_mutex_unlock(&encoder->outbuf_mutex);
+
+    if ((*outbuf)->data) {
+        return VIDEO_ENCODER_FRAME_ENCODE_DONE;
     }
+
     spice_debug("failed to pull the compressed buffer");
+    (*outbuf)->free(*outbuf);
+    *outbuf = NULL;
     return VIDEO_ENCODER_FRAME_UNSUPPORTED;
 }
 
@@ -523,7 +592,11 @@ static int pull_compressed_buffer(SpiceGstEncoder *encoder,
 static void spice_gst_encoder_destroy(VideoEncoder *video_encoder)
 {
     SpiceGstEncoder *encoder = (SpiceGstEncoder*)video_encoder;
+
     free_pipeline(encoder);
+    g_mutex_clear(&encoder->outbuf_mutex);
+    g_cond_clear(&encoder->outbuf_cond);
+
     free(encoder);
 }
 
@@ -644,6 +717,8 @@ VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type,
         encoder->cbs = *cbs;
     }
     encoder->starting_bit_rate = starting_bit_rate;
+    g_mutex_init(&encoder->outbuf_mutex);
+    g_cond_init(&encoder->outbuf_cond);
 
     /* All the other fields are initialized to zero by spice_new0(). */
 
-- 
2.8.0.rc3



More information about the Spice-devel mailing list