From 33db765f454b9eed9c641f12055cb2006299f7a8 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Fri, 21 Oct 2022 17:24:41 +0200 Subject: [PATCH] adaptivedemux2: Improve minimum buffering threshold Previously the minimum buffering threshold was hardcoded to a specific value (10s). This is suboptimal this an actual value will depend on the actual stream being played. This commit sets the low watermark threshold in time to 0, which is an automatic mode. Subclasses can provide a stream `recommended_buffering_threshold` when update_stream_info() is called. Currently implemented for HLS, where we recommended 1.5 average segment duration. This will result in buffering being at 100% when the 2nd segment has been downloaded (minus a bit already being consumed downstream) Part-of: --- .../docs/gst_plugins_cache.json | 4 +- .../adaptivedemux2/gstadaptivedemux-stream.c | 37 +++++++++++++++++-- .../ext/adaptivedemux2/gstadaptivedemux.c | 4 +- .../ext/adaptivedemux2/gstadaptivedemux.h | 3 ++ .../ext/adaptivedemux2/hls/gsthlsdemux.c | 4 ++ .../ext/adaptivedemux2/hls/m3u8.c | 12 ++++++ .../ext/adaptivedemux2/hls/m3u8.h | 3 ++ 7 files changed, 60 insertions(+), 7 deletions(-) diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index a110f533fc..550568f317 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -1349,12 +1349,12 @@ "writable": true }, "low-watermark-time": { - "blurb": "Low watermark for parsed data below which downloads are resumed (in ns, 0=disable)", + "blurb": "Low watermark for parsed data below which downloads are resumed (in ns, 0=automatic)", "conditionally-available": false, "construct": false, "construct-only": false, "controllable": false, - "default": "10000000000", + "default": "0", "max": "18446744073709551615", "min": "0", "mutable": "playing", diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c index 0594da2be4..cfcb3bcd83 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c @@ -149,6 +149,19 @@ gst_adaptive_demux2_stream_add_track (GstAdaptiveDemux2Stream * stream, return FALSE; } + if (stream->demux->buffering_low_watermark_time) + track->buffering_threshold = stream->demux->buffering_low_watermark_time; + else if (GST_CLOCK_TIME_IS_VALID (stream->recommended_buffering_threshold)) + track->buffering_threshold = + MIN (10 * GST_SECOND, stream->recommended_buffering_threshold); + else { + /* Using a starting default, can be overriden later in + * ::update_stream_info() */ + GST_DEBUG_OBJECT (stream, + "Setting default 10s buffering threshold on new track"); + track->buffering_threshold = 10 * GST_SECOND; + } + stream->tracks = g_list_append (stream->tracks, gst_adaptive_demux_track_ref (track)); if (stream->demux) { @@ -762,6 +775,7 @@ gst_adaptive_demux2_stream_parse_buffer (GstAdaptiveDemux2Stream * stream, */ static void calculate_track_thresholds (GstAdaptiveDemux * demux, + GstAdaptiveDemux2Stream * stream, GstClockTime fragment_duration, GstClockTime * low_threshold, GstClockTime * high_threshold) { @@ -773,6 +787,15 @@ calculate_track_thresholds (GstAdaptiveDemux * demux, *low_threshold = demux->buffering_low_watermark_time; } + if (*low_threshold == 0) { + /* This implies both low level properties were 0, the default is 10s unless + * the subclass has specified a recommended buffering threshold */ + *low_threshold = 10 * GST_SECOND; + if (GST_CLOCK_TIME_IS_VALID (stream->recommended_buffering_threshold)) + *low_threshold = + MIN (stream->recommended_buffering_threshold, *low_threshold); + } + *high_threshold = demux->buffering_high_watermark_fragments * fragment_duration; if (*high_threshold == 0 || (demux->buffering_high_watermark_time != 0 @@ -800,9 +823,11 @@ calculate_track_thresholds (GstAdaptiveDemux * demux, (*low_threshold != 0 && *low_threshold > *high_threshold)) { *high_threshold = *low_threshold; } + GST_OBJECT_UNLOCK (demux); } +#define ABSDIFF(a,b) ((a) < (b) ? (b) - (a) : (a) - (b)) static gboolean gst_adaptive_demux2_stream_wait_for_output_space (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstClockTime fragment_duration) @@ -816,8 +841,13 @@ gst_adaptive_demux2_stream_wait_for_output_space (GstAdaptiveDemux * demux, GstClockTime low_threshold = 0, high_threshold = 0; GList *iter; - calculate_track_thresholds (demux, fragment_duration, + calculate_track_thresholds (demux, stream, fragment_duration, &low_threshold, &high_threshold); + GST_DEBUG_OBJECT (stream, + "Thresholds low:%" GST_TIME_FORMAT " high:%" GST_TIME_FORMAT + " recommended:%" GST_TIME_FORMAT, GST_TIME_ARGS (low_threshold), + GST_TIME_ARGS (high_threshold), + GST_TIME_ARGS (stream->recommended_buffering_threshold)); /* If there are no tracks at all, don't wait. If there are no active * tracks, keep filling until at least one track is full. If there @@ -826,8 +856,9 @@ gst_adaptive_demux2_stream_wait_for_output_space (GstAdaptiveDemux * demux, for (iter = stream->tracks; iter; iter = iter->next) { GstAdaptiveDemuxTrack *track = (GstAdaptiveDemuxTrack *) iter->data; - /* Update the buffering threshold */ - if (low_threshold != track->buffering_threshold) { + /* Update the buffering threshold if it changed by more than a second */ + if (ABSDIFF (low_threshold, track->buffering_threshold) > GST_SECOND) { + GST_DEBUG_OBJECT (stream, "Updating threshold"); /* The buffering threshold for this track changed, make sure to * re-check buffering status */ update_buffering = TRUE; diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c index 28bdb4a07b..f4ff4d5f74 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c @@ -122,7 +122,7 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug); #define DEFAULT_MAX_BUFFERING_TIME (30 * GST_SECOND) #define DEFAULT_BUFFERING_HIGH_WATERMARK_TIME (30 * GST_SECOND) -#define DEFAULT_BUFFERING_LOW_WATERMARK_TIME (10 * GST_SECOND) +#define DEFAULT_BUFFERING_LOW_WATERMARK_TIME 0 /* Automatic */ #define DEFAULT_BUFFERING_HIGH_WATERMARK_FRAGMENTS 0.0 #define DEFAULT_BUFFERING_LOW_WATERMARK_FRAGMENTS 0.0 @@ -486,7 +486,7 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass) PROP_BUFFERING_LOW_WATERMARK_TIME, g_param_spec_uint64 ("low-watermark-time", "Low buffering watermark size (ns)", - "Low watermark for parsed data below which downloads are resumed (in ns, 0=disable)", + "Low watermark for parsed data below which downloads are resumed (in ns, 0=automatic)", 0, G_MAXUINT64, DEFAULT_BUFFERING_LOW_WATERMARK_TIME, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS)); diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h index ea4a473146..186e21d1b2 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h @@ -359,6 +359,9 @@ struct _GstAdaptiveDemux2Stream /* OR'd set of stream types in this stream */ GstStreamType stream_type; + + /* The buffering threshold recommended by the subclass */ + GstClockTime recommended_buffering_threshold; }; /** diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c index eebafdcded..9e5f8682d5 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c @@ -2425,6 +2425,10 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemux2Stream * stream) stream->fragment.duration = file->duration; + stream->recommended_buffering_threshold = + gst_hls_media_playlist_recommended_buffering_threshold + (hlsdemux_stream->playlist); + if (discont) stream->discont = TRUE; diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c index 3a68d16b54..511ce43d7b 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c @@ -1473,6 +1473,18 @@ gst_hls_media_playlist_get_seek_range (GstHLSMediaPlaylist * m3u8, return TRUE; } +GstClockTime +gst_hls_media_playlist_recommended_buffering_threshold (GstHLSMediaPlaylist * + playlist) +{ + if (!playlist->duration || !GST_CLOCK_TIME_IS_VALID (playlist->duration) + || playlist->segments->len == 0) + return GST_CLOCK_TIME_NONE; + + /* The recommended buffering threshold is 1.5 average segment duration */ + return 3 * (playlist->duration / playlist->segments->len) / 2; +} + GstHLSRenditionStream * gst_hls_rendition_stream_ref (GstHLSRenditionStream * media) { diff --git a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h index c6a22a91f3..63238fdb72 100644 --- a/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h +++ b/subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h @@ -225,6 +225,9 @@ gst_hls_media_playlist_seek (GstHLSMediaPlaylist *playlist, void gst_hls_media_playlist_dump (GstHLSMediaPlaylist* self); +GstClockTime +gst_hls_media_playlist_recommended_buffering_threshold (GstHLSMediaPlaylist *playlist); + typedef enum { GST_HLS_RENDITION_STREAM_TYPE_INVALID = -1,