From 57873655c68f4f9d052f6307f09ba5931d892915 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Tue, 3 Nov 2020 17:48:02 +0100 Subject: [PATCH] hlsdemux: Re-use streams if possible When switching variants, try to re-use existing streams/pads instead of creating new ones. When dealing with urisourcebin and decodebin3 this is not only the expected way but also avoids a lot of buffering/hang issues. Change-Id: I32cf158a0695e5af7de3a2e6001a0e9748f9e2b3 Part-of: --- ext/hls/gsthlsdemux.c | 208 ++++++++++++++++++++++++++++++++++++++++-- ext/hls/gsthlsdemux.h | 6 ++ 2 files changed, 208 insertions(+), 6 deletions(-) diff --git a/ext/hls/gsthlsdemux.c b/ext/hls/gsthlsdemux.c index e5aa43921..b6f67ab94 100644 --- a/ext/hls/gsthlsdemux.c +++ b/ext/hls/gsthlsdemux.c @@ -328,6 +328,10 @@ gst_hls_demux_get_bitrate (GstHLSDemux * hlsdemux) { GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (hlsdemux); + /* FIXME !!! + * + * No, there isn't a single output :D */ + /* Valid because hlsdemux only has a single output */ if (demux->streams) { GstAdaptiveDemuxStream *stream = demux->streams->data; @@ -667,6 +671,12 @@ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist, return; } +#ifdef TIZEN_FEATURE_UPSTREAM + GST_DEBUG_OBJECT (demux, + "is_primary_playlist:%d selected:%d playlist name '%s'", + is_primary_playlist, selected, playlist->name); +#endif + stream = gst_adaptive_demux_stream_new (demux, gst_hls_demux_create_pad (hlsdemux)); @@ -697,6 +707,81 @@ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist, #endif } #endif + +#ifdef TIZEN_FEATURE_UPSTREAM +static GstHLSDemuxStream * +find_adaptive_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist) +{ + GList *tmp; + + GST_DEBUG_OBJECT (demux, "Looking for existing stream for '%s' %s", + playlist->name, playlist->uri); + + for (tmp = demux->streams; tmp; tmp = tmp->next) { + GstHLSDemuxStream *hlsstream = (GstHLSDemuxStream *) tmp->data; + if (hlsstream->playlist == playlist) + return hlsstream; + } + + return NULL; +} + +/* Returns TRUE if the previous and current (to switch to) variant are compatible. + * + * That is: + * * They have the same number of streams + * * The streams are of the same type + */ +static gboolean +new_variant_is_compatible (GstAdaptiveDemux * demux) +{ + GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); + GstHLSVariantStream *previous = hlsdemux->previous_variant; + GstHLSVariantStream *current = hlsdemux->current_variant; + gint i; + + GST_DEBUG_OBJECT (demux, + "Checking whether new variant is compatible with previous"); + + for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) { + GList *mlist = current->media[i]; + if (g_list_length (previous->media[i]) != g_list_length (current->media[i])) { + GST_LOG_OBJECT (demux, "Number of medias for type %s don't match", + gst_hls_media_type_get_name (i)); + return FALSE; + } + + /* Check if all new media were present in previous (if not there are new ones) */ + while (mlist != NULL) { + GstHLSMedia *media = mlist->data; + if (!gst_hls_variant_find_matching_media (previous, media)) { + GST_LOG_OBJECT (demux, + "New stream of type %s present. Variant not compatible", + gst_hls_media_type_get_name (i)); + return FALSE; + } + mlist = mlist->next; + } + + /* Check if all old media are present in current (if not some have gone) */ + mlist = previous->media[i]; + while (mlist != NULL) { + GstHLSMedia *media = mlist->data; + if (!gst_hls_variant_find_matching_media (current, media)) { + GST_LOG_OBJECT (demux, + "Old stream of type %s gone. Variant not compatible", + gst_hls_media_type_get_name (i)); + return FALSE; + } + mlist = mlist->next; + } + } + + GST_DEBUG_OBJECT (demux, "Variants are compatible"); + + return TRUE; +} +#endif static gboolean gst_hls_demux_setup_streams (GstAdaptiveDemux * demux) { @@ -708,7 +793,62 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux) GST_WARNING_OBJECT (demux, "Can't configure streams - no variant selected"); return FALSE; } +#ifdef TIZEN_FEATURE_UPSTREAM + GST_DEBUG_OBJECT (demux, "Setting up streams"); + if (hlsdemux->streams_aware && hlsdemux->previous_variant && + new_variant_is_compatible (demux)) { + GstHLSDemuxStream *hlsstream; + GST_DEBUG_OBJECT (demux, "Have a previous variant, Re-using streams"); + + /* Carry over the main playlist */ + hlsstream = + find_adaptive_stream_for_playlist (demux, + hlsdemux->previous_variant->m3u8); + if (G_UNLIKELY (hlsstream == NULL)) + goto no_match_error; + + gst_m3u8_unref (hlsstream->playlist); + hlsstream->playlist = gst_m3u8_ref (playlist->m3u8); + + for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) { + GList *mlist = playlist->media[i]; + while (mlist != NULL) { + GstHLSMedia *media = mlist->data; + GstHLSMedia *old_media = + gst_hls_variant_find_matching_media (hlsdemux->previous_variant, + media); + + if (G_UNLIKELY (old_media == NULL)) { + GST_FIXME_OBJECT (demux, "Handle new stream !"); + goto no_match_error; + } + if (!g_strcmp0 (media->uri, old_media->uri)) + GST_DEBUG_OBJECT (demux, "Identical stream !"); + if (media->mtype == GST_HLS_MEDIA_TYPE_AUDIO || + media->mtype == GST_HLS_MEDIA_TYPE_VIDEO || + media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES) { + hlsstream = + find_adaptive_stream_for_playlist (demux, old_media->playlist); + if (!hlsstream) + goto no_match_error; + + GST_DEBUG_OBJECT (demux, "Found matching stream"); + gst_m3u8_unref (hlsstream->playlist); + hlsstream->playlist = gst_m3u8_ref (media->playlist); + } else { + GST_DEBUG_OBJECT (demux, "Skipping stream of type %s", + gst_hls_media_type_get_name (media->mtype)); + } + + mlist = mlist->next; + } + } + + return TRUE; + } + /* FIXME : This seems wrong and assumes there's only one stream :( */ +#endif gst_hls_demux_clear_all_pending_data (hlsdemux); /* 1 output for the main playlist */ @@ -730,23 +870,34 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux) if (media->uri == NULL /* || media->mtype != GST_HLS_MEDIA_TYPE_AUDIO */ ) { /* No uri means this is a placeholder for a stream * contained in another mux */ +#ifdef TIZEN_FEATURE_UPSTREAM + GST_LOG_OBJECT (demux, "Skipping stream %s type %s with no URI", + media->name, gst_hls_media_type_get_name (media->mtype)); +#else GST_LOG_OBJECT (demux, "Skipping stream %s type %d with no URI", media->name, media->mtype); +#endif mlist = mlist->next; continue; } +#ifdef TIZEN_FEATURE_UPSTREAM + GST_LOG_OBJECT (demux, "media of type %s - %s, uri: %s", + gst_hls_media_type_get_name (i), media->name, media->uri); +#else GST_LOG_OBJECT (demux, "media of type %d - %s, uri: %s", i, media->name, media->uri); +#endif + #ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING create_stream_for_playlist (demux, media->playlist, FALSE, media->mtype); #else create_stream_for_playlist (demux, media->playlist, FALSE, - (media->mtype == GST_HLS_MEDIA_TYPE_VIDEO || - media->mtype == GST_HLS_MEDIA_TYPE_AUDIO || + (media->mtype == GST_HLS_MEDIA_TYPE_VIDEO + || media->mtype == GST_HLS_MEDIA_TYPE_AUDIO #ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG - media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES), media); + || media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES), media); #else - media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES)); + || media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES)); #endif #endif @@ -755,6 +906,15 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux) } return TRUE; + +#ifdef TIZEN_FEATURE_UPSTREAM +no_match_error: + { + /* POST ERROR MESSAGE */ + GST_ERROR_OBJECT (demux, "Should not happen ! Could not find old stream"); + return FALSE; + } +#endif } static const gchar * @@ -796,15 +956,34 @@ gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux, gst_hls_variant_find_matching_media (variant, old_media); if (new_media) { +#ifdef TIZEN_FEATURE_UPSTREAM + GST_LOG_OBJECT (hlsdemux, "Found matching GstHLSMedia"); + GST_LOG_OBJECT (hlsdemux, "old_media '%s' '%s'", old_media->name, + old_media->uri); + GST_LOG_OBJECT (hlsdemux, "new_media '%s' '%s'", new_media->name, + new_media->uri); +#endif new_media->playlist->sequence = old_media->playlist->sequence; new_media->playlist->sequence_position = old_media->playlist->sequence_position; +#ifdef TIZEN_FEATURE_UPSTREAM + } else { + GST_LOG_OBJECT (hlsdemux, + "Didn't find a matching variant for '%s' '%s'", old_media->name, + old_media->uri); +#endif } mlist = mlist->next; } } - +#ifdef TIZEN_FEATURE_UPSTREAM + if (hlsdemux->previous_variant) + gst_hls_variant_stream_unref (hlsdemux->previous_variant); + /* Steal the reference */ + hlsdemux->previous_variant = hlsdemux->current_variant; +#else gst_hls_variant_stream_unref (hlsdemux->current_variant); +#endif } hlsdemux->current_variant = gst_hls_variant_stream_ref (variant); @@ -1096,8 +1275,13 @@ gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux, return GST_FLOW_OK; } +#ifdef TIZEN_FEATURE_UPSTREAM + GST_DEBUG_OBJECT (stream->pad, + "Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob); +#else GST_DEBUG_OBJECT (hlsdemux, "Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob); +#endif hls_stream->stream_type = caps_to_reader (caps); gst_hlsdemux_tsreader_set_type (&hls_stream->tsreader, @@ -1436,7 +1620,19 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux) gst_hls_variant_stream_unref (demux->current_variant); demux->current_variant = NULL; } +#ifdef TIZEN_FEATURE_UPSTREAM + if (demux->previous_variant != NULL) { + gst_hls_variant_stream_unref (demux->previous_variant); + demux->previous_variant = NULL; + } +#endif demux->srcpad_counter = 0; +#ifdef TIZEN_FEATURE_UPSTREAM + demux->streams_aware = GST_OBJECT_PARENT (demux) + && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (demux), + GST_BIN_FLAG_STREAMS_AWARE); + GST_DEBUG_OBJECT (demux, "Streams aware : %d", demux->streams_aware); +#endif gst_hls_demux_clear_all_pending_data (demux); GST_M3U8_CLIENT_UNLOCK (hlsdemux->client); @@ -2209,4 +2405,4 @@ gst_hlsdemux_set_language_tags (GstAdaptiveDemuxStream *stream, const gchar *lan return TRUE; } -#endif \ No newline at end of file +#endif diff --git a/ext/hls/gsthlsdemux.h b/ext/hls/gsthlsdemux.h index 670b97067..548927a6f 100644 --- a/ext/hls/gsthlsdemux.h +++ b/ext/hls/gsthlsdemux.h @@ -150,6 +150,12 @@ struct _GstHLSDemux GstHLSMasterPlaylist *master; GstHLSVariantStream *current_variant; +#ifdef TIZEN_FEATURE_UPSTREAM + /* The previous variant, used to transition streams over */ + GstHLSVariantStream *previous_variant; + + gboolean streams_aware; +#endif }; struct _GstHLSDemuxClass -- 2.34.1