Struggling with range responses
Posted: 08 Mar 2025 20:50
I have created a file server for mp4 files, initially for clients to download or to stream and play (the entire file at once) with vlc.
It seemed more efficient to use range responses as supported by FileStream from axum-extra as I believe this should allow seeking backwards or forwards through the video with it raising an error (as it currently does when streaming the file in its entirety) but currently testing a ranged response seems to be slower to start with ffplay or vlc compared to a full download and crashes when trying to move backwards or forwards through the video.
Currently the route to handle the range response stream copying from some examples looks like:
The output from vlc on the client side is:
Would really appreciate any pointers for what I am missing here. Possibly bad handling of ranged headers?
It seemed more efficient to use range responses as supported by FileStream from axum-extra as I believe this should allow seeking backwards or forwards through the video with it raising an error (as it currently does when streaming the file in its entirety) but currently testing a ranged response seems to be slower to start with ffplay or vlc compared to a full download and crashes when trying to move backwards or forwards through the video.
Currently the route to handle the range response stream copying from some examples looks like:
Code: Select all
pub async fn stream(file_name: Query<FileName>, headers: HeaderMap) -> Response {
let range_header = headers
.get(header::RANGE)
.and_then(|value| value.to_str().ok());
let (start, end) = if let Some(range) = range_header {
if let Some(range) = parse_range_header(range) {
range
} else {
return (StatusCode::RANGE_NOT_SATISFIABLE, "Invalid Range").into_response();
}
} else {
(0, 0) // default range end = 0, if end = 0 end == file size - 1
};
let file_name = &file_name.filename;
let file_dir = var("VIDEO_SAVE_PATH").unwrap_or("/home".to_string());
let formated_path = format!("{file_dir}/{file_name}");
FileStream::<ReaderStream<File>>::try_range_response(formated_path, start, end)
.await
.map_err(|e| (StatusCode::NOT_FOUND, format!("File not found: {e}")))
.into_response()
}
fn parse_range_header(range: &str) -> Option<(u64, u64)> {
let range = range.strip_prefix("bytes=")?;
let mut parts = range.split('-');
let start = parts.next()?.parse::<u64>().ok()?;
let end = parts
.next()
.and_then(|s| s.parse::<u64>().ok())
.unwrap_or(0);
if start > end {
return None;
}
Some((start, end))
}
Code: Select all
VLC media player 3.0.16 Vetinari (revision 3.0.13-8-g41878ff4f2)
[0000563386aee580] main libvlc: Running vlc with the default interface. Use 'cvlc' to use vlc without interface.
Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway.
[00007494280044d0] gl gl: Initialized libplacebo v4.192.1 (API v192)
libva info: VA-API version 1.14.0
libva error: vaGetDriverNameByIndex() failed with unknown libva error, driver_name = (null)
[00007494280044d0] glconv_vaapi_x11 gl error: vaInitialize: unknown libva error
libva info: VA-API version 1.14.0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_14
libva info: va_openDriver() returns 0
[0000749440111130] avcodec decoder: Using Intel iHD driver for Intel(R) Gen Graphics - 22.3.1 () for hardware decoding
[0000749440111130] main decoder error: Timestamp conversion failed for 2533334: no reference clock
[0000749440111130] main decoder error: Could not convert timestamp 0 for FFmpeg
[h264 @ 0x7494401d8a40] get_buffer() failed
[h264 @ 0x7494401d8a40] thread_get_buffer() failed
[h264 @ 0x7494401d8a40] decode_slice_header error
[h264 @ 0x7494401d8a40] no frame!