[Spice-devel] [PATCH spice-gtk] gstreamer: Fallback to S/W decoder if H/W one is not working (v2)
Kasireddy, Vivek
vivek.kasireddy at intel.com
Sun Nov 5 05:13:37 UTC 2023
Acked-by: Vivek Kasireddy <vivek.kasireddy at intel.com>
>
> 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 at gmail.com>
> ---
> 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
More information about the Spice-devel
mailing list