From 5d0b112c0c7975d6dfb907fbef4dc0f5cece97f9 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Mon, 11 Jul 2022 10:31:42 +0200 Subject: [PATCH] adaptivedemux2/hlsdemux2: Handle loss of sync when dowloading. Media playlist updates and fragment downloads happen in an interleaved fashion. When a media playlist update fails *while* a segment is being downloaded, this means we lost synchronization. Properly propagate and handle this Part-of: --- .../adaptivedemux2/gstadaptivedemux-stream.c | 7 ++++- .../ext/adaptivedemux2/hls/gsthlsdemux.c | 27 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c index 6f8f575846..4bf61e7f97 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c @@ -297,7 +297,12 @@ gst_adaptive_demux2_stream_finish_download (GstAdaptiveDemux2Stream * } /* Handle all the possible flow returns here: */ - if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) { + if (ret == GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC) { + /* We lost sync, seek back to live and return */ + GST_WARNING_OBJECT (stream, "Lost sync when downloading"); + gst_adaptive_demux_handle_lost_sync (demux); + return; + } else if (ret == GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT) { /* The sub-class wants to stop the fragment immediately */ stream->fragment.finished = TRUE; ret = klass->finish_fragment (demux, stream); diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c index 9805470ffa..f328cbe261 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c @@ -1476,7 +1476,17 @@ gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux, GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); GstFlowReturn ret = GST_FLOW_OK; - g_assert (hls_stream->current_segment); + /* If current segment is not present, this means that a playlist update + * happened between the moment ::update_fragment_info() was called and the + * moment we received data. And that playlist update couldn't match the + * current position. This will happen in live playback when we are downloading + * too slowly, therefore we try to "catch up" back to live + */ + if (hls_stream->current_segment == NULL) { + GST_WARNING_OBJECT (stream, "Lost sync"); + return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC; + } + GST_DEBUG_OBJECT (stream, "buffer:%p at_eos:%d do_typefind:%d uri:%s", buffer, at_eos, hls_stream->do_typefind, hls_stream->current_segment->uri); @@ -1562,7 +1572,7 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, if (hls_stream->current_key) gst_hls_demux_stream_decrypt_end (hls_stream); - if (stream->last_ret == GST_FLOW_OK) { + if (hls_stream->current_segment && stream->last_ret == GST_FLOW_OK) { if (hls_stream->pending_decrypted_buffer) { if (hls_stream->current_key) { GstMapInfo info; @@ -1606,6 +1616,12 @@ gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux, if (G_UNLIKELY (stream->downloading_header || stream->downloading_index)) return GST_FLOW_OK; + if (hls_stream->current_segment == NULL) { + /* We can't advance, we just return OK for now and let the base class + * trigger a new download (or fail and resync itself) */ + return GST_FLOW_OK; + } + if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) { /* We can update the stream current position with a more accurate value * before advancing. Note that we don't have any period so we can set the @@ -1624,6 +1640,9 @@ gst_hls_demux_data_received (GstAdaptiveDemux * demux, GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream); GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); + if (hls_stream->current_segment == NULL) + return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC; + if (hls_stream->current_offset == -1) hls_stream->current_offset = 0; @@ -2199,8 +2218,8 @@ gst_hls_demux_stream_update_rendition_playlist (GstHLSDemux * demux, { GstFlowReturn ret = GST_FLOW_OK; GstHLSRenditionStream *target_rendition = - stream->pending_rendition ? stream->pending_rendition : stream-> - current_rendition; + stream->pending_rendition ? stream-> + pending_rendition : stream->current_rendition; ret = gst_hls_demux_stream_update_media_playlist (demux, stream, &target_rendition->uri, NULL);