In case the H/W decoder is not able to decode the stream (like too high profile) try the S/W version. This is done detecting the failure and trying to recreate the pipeline in case: - we are using a H/W pipeline; - we didn't decode any frame (otherwise it means we lost the beginning or it was not a problem of H/W decoder). Signed-off-by: Frediano Ziglio <freddy77@xxxxxxxxx> --- Changes since v1: - avoid a if/else (Vivek); - fix typo in comment (Vivek). --- src/channel-display-gst.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/channel-display-gst.c b/src/channel-display-gst.c index 2734a546..78b4985e 100644 --- a/src/channel-display-gst.c +++ b/src/channel-display-gst.c @@ -48,6 +48,8 @@ typedef struct SpiceGstDecoder { GstElement *pipeline; GstClock *clock; GstBus *bus; + bool is_hw_pipeline; + bool frame_removed; /* ---------- Decoding and display queues ---------- */ @@ -125,6 +127,7 @@ static void free_gst_frame(SpiceGstFrame *gstframe) /* ---------- GStreamer pipeline ---------- */ static void schedule_frame(SpiceGstDecoder *decoder); +static void try_sw_pipeline(SpiceGstDecoder *decoder); RECORDER(frames_stats, 64, "Frames statistics"); @@ -231,6 +234,7 @@ static guint32 pop_up_to_frame(SpiceGstDecoder *decoder, const SpiceGstFrame *po SpiceGstFrame *gstframe; guint32 freed = 0; + decoder->frame_removed = true; while ((gstframe = g_queue_pop_head(decoder->decoding_queue)) != popframe) { free_gst_frame(gstframe); freed++; @@ -371,6 +375,7 @@ static void free_pipeline(SpiceGstDecoder *decoder) decoder->clock = NULL; gst_object_unref(decoder->pipeline); decoder->pipeline = NULL; + decoder->is_hw_pipeline = false; } static gboolean handle_pipeline_message(GstBus *bus, GstMessage *msg, gpointer video_decoder) @@ -390,8 +395,11 @@ static gboolean handle_pipeline_message(GstBus *bus, GstMessage *msg, gpointer v } g_clear_error(&err); - /* We won't be able to process any more frame anyway */ + bool was_hw = decoder->is_hw_pipeline; free_pipeline(decoder); + if (was_hw && !decoder->frame_removed) { + try_sw_pipeline(decoder); + } break; } case GST_MESSAGE_STREAM_START: { @@ -672,6 +680,7 @@ static bool try_intel_hw_pipeline(SpiceGstDecoder *decoder) } decoder->pipeline = pipeline; + decoder->is_hw_pipeline = true; return launch_pipeline(decoder); err: @@ -704,7 +713,7 @@ err: return false; } -static gboolean create_pipeline(SpiceGstDecoder *decoder) +static gboolean create_pipeline(SpiceGstDecoder *decoder, bool try_hw_pipeline) { GstElement *playbin, *sink; SpiceGstPlayFlags flags; @@ -714,7 +723,7 @@ static gboolean create_pipeline(SpiceGstDecoder *decoder) if (vendor == VENDOR_GPU_DETECTED || vendor == VENDOR_GPU_UNKNOWN) { - if (try_intel_hw_pipeline(decoder)) { + if (try_hw_pipeline && try_intel_hw_pipeline(decoder)) { return TRUE; } } @@ -997,7 +1006,7 @@ VideoDecoder* create_gstreamer_decoder(int codec_type, display_stream *stream) g_mutex_init(&decoder->queues_mutex); decoder->decoding_queue = g_queue_new(); - if (!create_pipeline(decoder)) { + if (!create_pipeline(decoder, true)) { decoder->base.destroy((VideoDecoder*)decoder); decoder = NULL; } @@ -1066,3 +1075,25 @@ gboolean gstvideo_has_codec(int codec_type) gst_plugin_feature_list_free(all_decoders); return TRUE; } + +static void try_sw_pipeline(SpiceGstDecoder *decoder) +{ + // try to create a S/W pipeline + if (!create_pipeline(decoder, false)) { + return; + } + + // replay the old queue + g_mutex_lock(&decoder->queues_mutex); + GList *l = g_queue_peek_head_link(decoder->decoding_queue); + while (l) { + const SpiceGstFrame *gstframe = l->data; + GstBuffer *buf = gst_buffer_ref(gstframe->encoded_buffer); + if (gst_app_src_push_buffer(decoder->appsrc, buf) != GST_FLOW_OK) { + SPICE_DEBUG("GStreamer error: unable to push frame"); + stream_dropped_frame_on_playback(decoder->base.stream); + } + l = l->next; + } + g_mutex_unlock(&decoder->queues_mutex); +} -- 2.41.0