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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3240>
This commit is contained in:
Edward Hervey 2022-10-21 17:24:41 +02:00 committed by Edward Hervey
parent d8bdd9429b
commit 33db765f45
7 changed files with 60 additions and 7 deletions

View file

@ -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",

View file

@ -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;

View file

@ -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));

View file

@ -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;
};
/**

View file

@ -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;

View file

@ -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)
{

View file

@ -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,