adaptivedemux: Add 'backoff' logic for HTTP request

So that the user can configure waits between retries

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8128>
This commit is contained in:
Thibault Saunier 2024-12-12 08:48:04 -03:00 committed by GStreamer Marge Bot
parent 6d0c0d5d29
commit 8c16be5901
6 changed files with 133 additions and 15 deletions

View file

@ -1431,6 +1431,34 @@
"readable": true,
"type": "guint",
"writable": true
},
"retry-backoff-factor": {
"blurb": "Exponential retry backoff factor in seconds",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "1.79769e+308",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gdouble",
"writable": true
},
"retry-backoff-max": {
"blurb": "Maximum backoff delay in seconds",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "60",
"max": "1.79769e+308",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gdouble",
"writable": true
}
}
}

View file

@ -167,6 +167,9 @@ struct _GstAdaptiveDemuxPrivate
/* The maximum number of times HTTP request can be required before considering
* failed */
gint max_retries;
/* The backoff factor and max for the HTTP request retries */
gdouble retry_backoff_factor;
gdouble retry_backoff_max;
};

View file

@ -1241,8 +1241,8 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
GST_LOG_OBJECT (stream,
"Scheduling delayed load_a_fragment() call");
stream->pending_cb_id =
gst_adaptive_demux_loop_call_delayed (demux->
priv->scheduler_task, wait_time,
gst_adaptive_demux_loop_call_delayed (demux->priv->
scheduler_task, wait_time,
(GSourceFunc) gst_adaptive_demux2_stream_load_a_fragment,
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
return;
@ -1281,10 +1281,16 @@ on_download_error (DownloadRequest * request, DownloadRequestState state,
again:
/* wait a short time in case the server needs a bit to recover */
GST_LOG_OBJECT (stream,
"Scheduling delayed load_a_fragment() call to retry in 10 milliseconds");
g_assert (stream->pending_cb_id == 0);
stream->pending_cb_id = gst_adaptive_demux_loop_call_delayed (demux->priv->scheduler_task, 10 * GST_MSECOND, /* Retry in 10 ms */
GstClockTime delay =
gst_adaptive_demux_retry_delay (demux, stream->download_error_count,
10 * GST_MSECOND);
GST_DEBUG_OBJECT (stream,
"Scheduling delayed reload_manifest_cb() %d call in %" GST_TIMEP_FORMAT,
stream->download_error_count, &delay);
stream->pending_cb_id =
gst_adaptive_demux_loop_call_delayed (demux->priv->scheduler_task, delay,
(GSourceFunc) gst_adaptive_demux2_stream_load_a_fragment,
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
}
@ -2030,12 +2036,15 @@ gst_adaptive_demux2_stream_load_a_fragment (GstAdaptiveDemux2Stream * stream)
}
}
/* Wait half the fragment duration before retrying */
GST_LOG_OBJECT (stream, "Scheduling delayed reload_manifest_cb() call");
GstClockTime delay = gst_adaptive_demux_retry_delay (stream->demux,
stream->download_error_count, stream->fragment.duration / 2);
GST_DEBUG_OBJECT (stream,
"Scheduling delayed reload_manifest_cb() call in %"
GST_TIMEP_FORMAT, &delay);
g_assert (stream->pending_cb_id == 0);
stream->pending_cb_id =
gst_adaptive_demux_loop_call_delayed (demux->priv->scheduler_task,
stream->fragment.duration / 2,
delay,
(GSourceFunc) gst_adaptive_demux2_stream_reload_manifest_cb,
gst_object_ref (stream), (GDestroyNotify) gst_object_unref);
return FALSE;

View file

@ -114,6 +114,8 @@ GST_DEBUG_CATEGORY_EXTERN (adaptivedemux2_debug);
#define DEFAULT_FAILED_COUNT 3
#define DEFAULT_MAX_RETRIES 3
#define DEFAULT_RETRY_BACKOFF_FACTOR 0.0
#define DEFAULT_RETRY_BACKOFF_MAX 60.0
#define DEFAULT_CONNECTION_BITRATE 0
#define DEFAULT_BANDWIDTH_TARGET_RATIO 0.8f
@ -135,6 +137,8 @@ enum
PROP_0,
PROP_CONNECTION_SPEED,
PROP_MAX_RETRIES,
PROP_RETRY_BACKOFF_FACTOR,
PROP_RETRY_BACKOFF_MAX,
PROP_BANDWIDTH_TARGET_RATIO,
PROP_CONNECTION_BITRATE,
PROP_MIN_BITRATE,
@ -329,6 +333,12 @@ gst_adaptive_demux_set_property (GObject * object, guint prop_id,
case PROP_BUFFERING_LOW_WATERMARK_FRAGMENTS:
demux->buffering_low_watermark_fragments = g_value_get_double (value);
break;
case PROP_RETRY_BACKOFF_FACTOR:
demux->priv->retry_backoff_factor = g_value_get_double (value);
break;
case PROP_RETRY_BACKOFF_MAX:
demux->priv->retry_backoff_max = g_value_get_double (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -388,6 +398,12 @@ gst_adaptive_demux_get_property (GObject * object, guint prop_id,
case PROP_CURRENT_LEVEL_TIME_AUDIO:
g_value_set_uint64 (value, demux->current_level_time_audio);
break;
case PROP_RETRY_BACKOFF_FACTOR:
g_value_set_double (value, demux->priv->retry_backoff_factor);
break;
case PROP_RETRY_BACKOFF_MAX:
g_value_set_double (value, demux->priv->retry_backoff_max);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -527,7 +543,41 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass)
g_object_class_install_property (gobject_class, PROP_MAX_RETRIES,
g_param_spec_int ("max-retries", "Maximum Retries",
"Maximum number of retries for HTTP requests (-1=infinite)",
-1, G_MAXUINT, DEFAULT_MAX_RETRIES,
-1, G_MAXINT, DEFAULT_MAX_RETRIES,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstAdaptiveDemux2:retry-backoff-factor:
*
* A backoff factor to apply between attempts after the second try
* (most errors are resolved immediately by a second try without a delay).
* souphttpsrc will sleep for:
*
* ```
* {backoff factor} * (2 ** ({number of previous retries}))
* ``
*
* seconds
*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, PROP_RETRY_BACKOFF_FACTOR,
g_param_spec_double ("retry-backoff-factor", "Backoff Factor",
"Exponential retry backoff factor in seconds", 0.0, G_MAXDOUBLE,
DEFAULT_RETRY_BACKOFF_FACTOR,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstAdaptiveDemux2:retry-backoff-max:
*
* Maximum retry backoff delay in seconds
*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, PROP_RETRY_BACKOFF_MAX,
g_param_spec_double ("retry-backoff-max", "Maximum retry Backoff delay",
"Maximum backoff delay in seconds", 0.0, G_MAXDOUBLE,
DEFAULT_RETRY_BACKOFF_MAX,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class,
@ -613,6 +663,8 @@ gst_adaptive_demux_init (GstAdaptiveDemux * demux,
demux->current_level_time_video = DEFAULT_CURRENT_LEVEL_TIME_VIDEO;
demux->current_level_time_audio = DEFAULT_CURRENT_LEVEL_TIME_AUDIO;
demux->priv->max_retries = DEFAULT_MAX_RETRIES;
demux->priv->retry_backoff_factor = DEFAULT_RETRY_BACKOFF_FACTOR;
demux->priv->retry_backoff_max = DEFAULT_RETRY_BACKOFF_MAX;
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
@ -3991,3 +4043,21 @@ gst_adaptive_demux_max_retries (GstAdaptiveDemux * self)
return res;
}
GstClockTime
gst_adaptive_demux_retry_delay (GstAdaptiveDemux * self, gint retry,
GstClockTime default_delay)
{
GST_OBJECT_LOCK (self);
gdouble backoff_factor = self->priv->retry_backoff_factor;
gdouble backoff_max = self->priv->retry_backoff_max;
GST_OBJECT_UNLOCK (self);
GstClockTime delay = default_delay;
if (backoff_factor > 0) {
GstClockTime backoff_delay = (backoff_factor * (1 << retry)) * GST_SECOND;
delay = MIN (backoff_delay, (backoff_max * GST_SECOND));
}
return delay;
}

View file

@ -482,6 +482,7 @@ gdouble gst_adaptive_demux_play_rate (GstAdaptiveDemux *demux);
void gst_adaptive_demux2_manual_manifest_update (GstAdaptiveDemux * demux);
GstAdaptiveDemuxLoop *gst_adaptive_demux_get_loop (GstAdaptiveDemux *demux);
gint gst_adaptive_demux_max_retries (GstAdaptiveDemux *self);
GstClockTime gst_adaptive_demux_retry_delay (GstAdaptiveDemux * self, gint retry, GstClockTime default_delay);
G_END_DECLS

View file

@ -482,8 +482,15 @@ handle_download_error (GstHLSDemuxPlaylistLoader * pl,
/* The error callback may have provided a new playlist to load, which
* will have scheduled a state update immediately. In that case,
* don't trigger our own delayed retry */
if (priv->pending_cb_id == 0)
schedule_next_playlist_load (pl, priv, 100 * GST_MSECOND);
if (priv->pending_cb_id == 0) {
GstClockTime delay =
gst_adaptive_demux_retry_delay (priv->demux, priv->download_error_count,
100 * GST_MSECOND);
GST_DEBUG_OBJECT (pl,
"Scheduling delayed next playlist download in %" GST_TIMEP_FORMAT,
&delay);
schedule_next_playlist_load (pl, priv, delay);
}
}
static void