Skip to content

Commit fc4db2a

Browse files
committed
wip
1 parent cf6e7aa commit fc4db2a

6 files changed

Lines changed: 146 additions & 91 deletions

File tree

src/rs-core/adaptive/mod.rs

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const BOLA_MIN_LOW_BUFFER: f64 = 3.0;
1717
const BOLA_MAX_LOW_BUFFER: f64 = 10.0;
1818
const ABANDON_MIN_PROGRESS_DURATION_MS: f64 = 500.0;
1919
const ABANDON_MIN_PROGRESS_SAMPLES: u32 = 3;
20+
const ABANDON_MIN_REPLACEMENT_GAIN_S: f64 = 0.25;
2021

2122
impl AdaptiveQualitySelector {
2223
/// Creates new `AdaptiveQualitySelector`.
@@ -113,11 +114,24 @@ impl AdaptiveQualitySelector {
113114
pending_bytes_total: Option<u32>,
114115
progress_duration_ms: f64,
115116
progress_samples: u32,
116-
buffer_level: f64,
117+
playback_rate: f64,
118+
segment_duration: Option<f64>,
119+
buffer_starvation_delay: f64,
117120
) -> bool {
121+
if !playback_rate.is_finite() || playback_rate <= 0. || buffer_starvation_delay <= 0. {
122+
return false;
123+
}
124+
125+
let min_progress_duration_ms = segment_duration
126+
.filter(|duration| duration.is_finite() && *duration > 0.)
127+
.map(|duration| 1000. * (duration / (playback_rate * 2.)))
128+
.filter(|duration| duration.is_finite() && *duration > 0.)
129+
.map(|duration| duration.max(ABANDON_MIN_PROGRESS_DURATION_MS))
130+
.unwrap_or(ABANDON_MIN_PROGRESS_DURATION_MS);
131+
118132
if !pending_quality.is_better_than(desired_quality)
119133
|| desired_variant_bandwidth >= pending_variant_bandwidth
120-
|| progress_duration_ms < ABANDON_MIN_PROGRESS_DURATION_MS
134+
|| progress_duration_ms < min_progress_duration_ms
121135
|| progress_samples < ABANDON_MIN_PROGRESS_SAMPLES
122136
{
123137
return false;
@@ -145,8 +159,9 @@ impl AdaptiveQualitySelector {
145159
let remaining_download_time = (remaining_bytes as f64) * 8. / measured_bandwidth;
146160
let replacement_download_time = replacement_total_bytes * 8. / measured_bandwidth;
147161

148-
remaining_download_time > buffer_level
149-
&& replacement_download_time + 0.25 < remaining_download_time
162+
remaining_download_time > buffer_starvation_delay
163+
&& replacement_download_time < buffer_starvation_delay
164+
&& replacement_download_time + ABANDON_MIN_REPLACEMENT_GAIN_S < remaining_download_time
150165
}
151166

152167
pub(crate) fn reset(&mut self) {
@@ -232,3 +247,72 @@ fn compute_bola_variant_id(
232247
})
233248
.map(|(variant, _)| variant.id())
234249
}
250+
251+
#[cfg(test)]
252+
mod tests {
253+
use super::AdaptiveQualitySelector;
254+
use crate::media_element::SegmentQualityContext;
255+
256+
#[test]
257+
fn does_not_abandon_before_half_a_segment_elapsed() {
258+
let selector = AdaptiveQualitySelector::new(5_000_000.);
259+
let pending_quality = SegmentQualityContext::new(2., 10);
260+
let desired_quality = SegmentQualityContext::new(1., 11);
261+
262+
assert!(!selector.should_abandon_media_request(
263+
&pending_quality,
264+
&desired_quality,
265+
4_000_000,
266+
2_000_000,
267+
400_000,
268+
Some(1_000_000),
269+
1_900.,
270+
4,
271+
1.,
272+
Some(4.),
273+
3.,
274+
));
275+
}
276+
277+
#[test]
278+
fn abandons_when_replacement_beats_starvation_and_remaining_time() {
279+
let selector = AdaptiveQualitySelector::new(5_000_000.);
280+
let pending_quality = SegmentQualityContext::new(2., 10);
281+
let desired_quality = SegmentQualityContext::new(1., 11);
282+
283+
assert!(selector.should_abandon_media_request(
284+
&pending_quality,
285+
&desired_quality,
286+
4_000_000,
287+
1_000_000,
288+
250_000,
289+
Some(1_000_000),
290+
2_100.,
291+
4,
292+
1.,
293+
Some(4.),
294+
3.,
295+
));
296+
}
297+
298+
#[test]
299+
fn does_not_abandon_when_replacement_would_miss_starvation_deadline() {
300+
let selector = AdaptiveQualitySelector::new(5_000_000.);
301+
let pending_quality = SegmentQualityContext::new(2., 10);
302+
let desired_quality = SegmentQualityContext::new(1., 11);
303+
304+
assert!(!selector.should_abandon_media_request(
305+
&pending_quality,
306+
&desired_quality,
307+
4_000_000,
308+
2_000_000,
309+
250_000,
310+
Some(1_000_000),
311+
2_100.,
312+
4,
313+
1.,
314+
Some(4.),
315+
1.8,
316+
));
317+
}
318+
}

src/rs-core/dispatcher/core/mod.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,8 +1172,12 @@ impl Dispatcher {
11721172
let Some(pl_store) = self.playlist_store.as_ref() else {
11731173
return;
11741174
};
1175-
1176-
let buffer_level = self.media_element_ref.last_buffer_gap();
1175+
if pl_store.is_variant_locked() || !self.media_element_ref.can_monitor_abr_requests() {
1176+
return;
1177+
}
1178+
let Some(buffer_starvation_delay) = self.media_element_ref.starvation_delay() else {
1179+
return;
1180+
};
11771181
let Some(pending_request) = self.requester.pending_segment_request(MediaType::Video) else {
11781182
return;
11791183
};
@@ -1200,6 +1204,9 @@ impl Dispatcher {
12001204
) else {
12011205
return;
12021206
};
1207+
let segment_duration = pending_context
1208+
.time_info()
1209+
.map(|time_info| time_info.duration());
12031210

12041211
if self.adaptive_selector.should_abandon_media_request(
12051212
pending_quality_context,
@@ -1210,7 +1217,9 @@ impl Dispatcher {
12101217
pending_request.bytes_total(),
12111218
pending_request.progress_duration_ms(),
12121219
pending_request.progress_samples(),
1213-
buffer_level,
1220+
self.media_element_ref.wanted_speed(),
1221+
segment_duration,
1222+
buffer_starvation_delay,
12141223
) {
12151224
log_info!("Core: Abandoning pending higher-quality video segment request");
12161225
self.abort_segment_requests_with_type(MediaType::Video);

src/rs-core/media_element/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,27 @@ impl MediaElementReference {
201201
.unwrap_or(0.)
202202
}
203203

204+
/// Returns whether current playback conditions make an ABR abandon decision meaningful.
205+
pub(crate) fn can_monitor_abr_requests(&self) -> bool {
206+
let Some(observation) = self.last_observation.as_ref() else {
207+
return false;
208+
};
209+
observation.ready_state() >= 1
210+
&& !observation.paused()
211+
&& !observation.seeking()
212+
&& !observation.ended()
213+
&& self.wanted_speed.is_finite()
214+
&& self.wanted_speed > 0.
215+
}
216+
217+
/// Returns the effective time left before starvation at the current playback rate.
218+
pub(crate) fn starvation_delay(&self) -> Option<f64> {
219+
if !self.can_monitor_abr_requests() {
220+
return None;
221+
}
222+
Some(self.last_buffer_gap() / self.wanted_speed)
223+
}
224+
204225
/// Perform a seek, that is, move the current position to another one.
205226
///
206227
/// Note that depending on that `MediaElementReference`'s state, seeks might

src/wasm/abi/wasm-functions.json

Lines changed: 0 additions & 84 deletions
This file was deleted.

src/wasm/abi/wasm-functions.jsonc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,21 @@
11311131
"returnType": "void",
11321132
},
11331133
},
1134+
{
1135+
"wasmSymbol": "__web_event__request_progress",
1136+
"doc": ["Forward in-flight network request progress to wasm."],
1137+
"wasm": {
1138+
"kind": "method",
1139+
"args": [
1140+
{ "name": "ptr", "type": "number" },
1141+
{ "name": "requestId", "type": "number" },
1142+
{ "name": "bytesLoaded", "type": "number" },
1143+
{ "name": "bytesTotal", "type": "number" },
1144+
{ "name": "durationMs", "type": "number" },
1145+
],
1146+
"returnType": "void",
1147+
},
1148+
},
11341149
{
11351150
"wasmSymbol": "__web_event__request_failed",
11361151
"doc": ["Forward a failed network request completion to wasm."],

src/wasm/js/generatedTypes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,16 @@ export interface WaspWasmExports {
451451
finalUrlLen: number,
452452
durationMs: number,
453453
): void;
454+
/**
455+
* Forward in-flight network request progress to wasm.
456+
*/
457+
__web_event__request_progress(
458+
ptr: number,
459+
requestId: number,
460+
bytesLoaded: number,
461+
bytesTotal: number,
462+
durationMs: number,
463+
): void;
454464
/**
455465
* Forward a failed network request completion to wasm.
456466
*/

0 commit comments

Comments
 (0)