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

Christophe Fergeau cfergeau at redhat.com
Mon May 2 15:16:34 UTC 2016


On Tue, Apr 19, 2016 at 09:49:39AM +0200, Francois Gouget wrote:
> 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 04a74cb..f9b3579 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;
>  
> @@ -214,6 +219,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;

'err' must be set to NULL here.

> +        gchar *debug_info;
> +        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)
>  {
> @@ -293,6 +348,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

This bit can go to the last patch

> +    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);

Can go in the last patch as well.

> +#else
> +    gst_bus_set_sync_handler(bus, handle_pipeline_message, encoder, NULL);

Using _sync_handler which runs in a different thread seems quite
complicated. Have you tried using gst_bus_add_watch() (we have a
mainloop in the display thread where this code runs if I'm not
mistaken), or even gst_bus_pop_filtered() if all that you need is to
catch encoding errors?

Christophe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20160502/bbfb3c5c/attachment.sig>


More information about the Spice-devel mailing list