Skip to content
Snippets Groups Projects
Commit 581ca654 authored by Frediano Ziglio's avatar Frediano Ziglio
Browse files

gstreamer: Fallback to S/W decoder if H/W one is not working


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: default avatarFrediano Ziglio <freddy77@gmail.com>
Acked-by: default avatarVivek Kasireddy <vivek.kasireddy@intel.com>
parent d29a0a04
No related branches found
No related tags found
No related merge requests found
Pipeline #1026976 passed
...@@ -48,6 +48,8 @@ typedef struct SpiceGstDecoder { ...@@ -48,6 +48,8 @@ typedef struct SpiceGstDecoder {
GstElement *pipeline; GstElement *pipeline;
GstClock *clock; GstClock *clock;
GstBus *bus; GstBus *bus;
bool is_hw_pipeline;
bool frame_removed;
/* ---------- Decoding and display queues ---------- */ /* ---------- Decoding and display queues ---------- */
...@@ -125,6 +127,7 @@ static void free_gst_frame(SpiceGstFrame *gstframe) ...@@ -125,6 +127,7 @@ static void free_gst_frame(SpiceGstFrame *gstframe)
/* ---------- GStreamer pipeline ---------- */ /* ---------- GStreamer pipeline ---------- */
static void schedule_frame(SpiceGstDecoder *decoder); static void schedule_frame(SpiceGstDecoder *decoder);
static void try_sw_pipeline(SpiceGstDecoder *decoder);
RECORDER(frames_stats, 64, "Frames statistics"); RECORDER(frames_stats, 64, "Frames statistics");
...@@ -231,6 +234,7 @@ static guint32 pop_up_to_frame(SpiceGstDecoder *decoder, const SpiceGstFrame *po ...@@ -231,6 +234,7 @@ static guint32 pop_up_to_frame(SpiceGstDecoder *decoder, const SpiceGstFrame *po
SpiceGstFrame *gstframe; SpiceGstFrame *gstframe;
guint32 freed = 0; guint32 freed = 0;
decoder->frame_removed = true;
while ((gstframe = g_queue_pop_head(decoder->decoding_queue)) != popframe) { while ((gstframe = g_queue_pop_head(decoder->decoding_queue)) != popframe) {
free_gst_frame(gstframe); free_gst_frame(gstframe);
freed++; freed++;
...@@ -371,6 +375,7 @@ static void free_pipeline(SpiceGstDecoder *decoder) ...@@ -371,6 +375,7 @@ static void free_pipeline(SpiceGstDecoder *decoder)
decoder->clock = NULL; decoder->clock = NULL;
gst_object_unref(decoder->pipeline); gst_object_unref(decoder->pipeline);
decoder->pipeline = NULL; decoder->pipeline = NULL;
decoder->is_hw_pipeline = false;
} }
static gboolean handle_pipeline_message(GstBus *bus, GstMessage *msg, gpointer video_decoder) 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 ...@@ -390,8 +395,11 @@ static gboolean handle_pipeline_message(GstBus *bus, GstMessage *msg, gpointer v
} }
g_clear_error(&err); 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); free_pipeline(decoder);
if (was_hw && !decoder->frame_removed) {
try_sw_pipeline(decoder);
}
break; break;
} }
case GST_MESSAGE_STREAM_START: { case GST_MESSAGE_STREAM_START: {
...@@ -672,6 +680,7 @@ static bool try_intel_hw_pipeline(SpiceGstDecoder *decoder) ...@@ -672,6 +680,7 @@ static bool try_intel_hw_pipeline(SpiceGstDecoder *decoder)
} }
decoder->pipeline = pipeline; decoder->pipeline = pipeline;
decoder->is_hw_pipeline = true;
return launch_pipeline(decoder); return launch_pipeline(decoder);
err: err:
...@@ -704,7 +713,7 @@ err: ...@@ -704,7 +713,7 @@ err:
return false; return false;
} }
static gboolean create_pipeline(SpiceGstDecoder *decoder) static gboolean create_pipeline(SpiceGstDecoder *decoder, bool try_hw_pipeline)
{ {
GstElement *playbin, *sink; GstElement *playbin, *sink;
SpiceGstPlayFlags flags; SpiceGstPlayFlags flags;
...@@ -714,7 +723,7 @@ static gboolean create_pipeline(SpiceGstDecoder *decoder) ...@@ -714,7 +723,7 @@ static gboolean create_pipeline(SpiceGstDecoder *decoder)
if (vendor == VENDOR_GPU_DETECTED || if (vendor == VENDOR_GPU_DETECTED ||
vendor == VENDOR_GPU_UNKNOWN) { vendor == VENDOR_GPU_UNKNOWN) {
if (try_intel_hw_pipeline(decoder)) { if (try_hw_pipeline && try_intel_hw_pipeline(decoder)) {
return TRUE; return TRUE;
} }
} }
...@@ -997,7 +1006,7 @@ VideoDecoder* create_gstreamer_decoder(int codec_type, display_stream *stream) ...@@ -997,7 +1006,7 @@ VideoDecoder* create_gstreamer_decoder(int codec_type, display_stream *stream)
g_mutex_init(&decoder->queues_mutex); g_mutex_init(&decoder->queues_mutex);
decoder->decoding_queue = g_queue_new(); decoder->decoding_queue = g_queue_new();
if (!create_pipeline(decoder)) { if (!create_pipeline(decoder, true)) {
decoder->base.destroy((VideoDecoder*)decoder); decoder->base.destroy((VideoDecoder*)decoder);
decoder = NULL; decoder = NULL;
} }
...@@ -1066,3 +1075,25 @@ gboolean gstvideo_has_codec(int codec_type) ...@@ -1066,3 +1075,25 @@ gboolean gstvideo_has_codec(int codec_type)
gst_plugin_feature_list_free(all_decoders); gst_plugin_feature_list_free(all_decoders);
return TRUE; 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);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment