adaptivedemux2: Improve minimum buffering threshold
authorEdward Hervey <edward@centricular.com>
Fri, 21 Oct 2022 15:24:41 +0000 (17:24 +0200)
committerEdward Hervey <bilboed@bilboed.com>
Mon, 31 Oct 2022 15:16:50 +0000 (16:16 +0100)
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>

subprojects/gst-plugins-good/docs/gst_plugins_cache.json
subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux-stream.c
subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.c
subprojects/gst-plugins-good/ext/adaptivedemux2/gstadaptivedemux.h
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/gsthlsdemux.c
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.c
subprojects/gst-plugins-good/ext/adaptivedemux2/hls/m3u8.h

index a110f53..550568f 100644 (file)
                         "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",
index 0594da2..cfcb3bc 100644 (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;
index 28bdb4a..f4ff4d5 100644 (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));
index ea4a473..186e21d 100644 (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;
 };
 
 /**
index eebafdc..9e5f868 100644 (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;
 
index 3a68d16..511ce43 100644 (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)
 {
index c6a22a9..63238fd 100644 (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,