Migrate from single-client direct OGG-FLAC sink to multi-client broadcast streaming sink. - Replace DirectOggFlacHandle with OggFlacStreamHandle for multi-client support - Update sink implementation to use StreamingOggFlacSink with proper backpressure - Change connection model from 'connect()' to 'subscribe()' - Adjust stream handling to support independent client streams - Update stream endpoint to always respond with 200 chunked instead of range requests - Add tracing for stream lifecycle events - Reduce OGG channel capacity to strict backpressure (1) - Add Drop implementation for stream cleanup - Improve logging and error handling for concurrent access This change enables multiple simultaneous clients to connect to the same audio stream without interfering with each other, while maintaining proper TCP backpressure and stream lifecycle management.
63 lines
2.0 KiB
Rust
63 lines
2.0 KiB
Rust
//! Handler HTTP GET /api/webrenderer/{id}/stream
|
|
//!
|
|
//! Sert le flux OGG-FLAC d'une instance WebRenderer.
|
|
//!
|
|
//! Safari envoie parfois Range: bytes=0-N avant de jouer.
|
|
//! On ignore ce header et on répond toujours 200 chunked (flux live infini).
|
|
|
|
use axum::{
|
|
body::Body,
|
|
extract::{Path, State},
|
|
http::{
|
|
HeaderMap, StatusCode,
|
|
header::{CACHE_CONTROL, CONNECTION, CONTENT_TYPE, TRANSFER_ENCODING},
|
|
},
|
|
response::{IntoResponse, Response},
|
|
};
|
|
use std::sync::Arc;
|
|
use tokio_util::io::ReaderStream;
|
|
use tracing::info;
|
|
|
|
use crate::registry::RendererRegistry;
|
|
|
|
/// GET /api/webrenderer/{id}/stream
|
|
pub async fn stream_handler(
|
|
State(registry): State<Arc<RendererRegistry>>,
|
|
Path(instance_id): Path<String>,
|
|
headers: HeaderMap,
|
|
) -> impl IntoResponse {
|
|
info!(instance_id = %instance_id, "FLAC stream client connecting");
|
|
|
|
// Ignorer le header Range — flux live infini, non seekable.
|
|
// On ne répond jamais 206 ni 416 : toujours 200 chunked.
|
|
// Safari (et d'autres clients) envoient parfois Range: bytes=0-N ;
|
|
// répondre 416 ou 206 leur fait croire à une ressource finie.
|
|
if let Some(range) = headers.get("range") {
|
|
info!(instance_id = %instance_id, "Range header ignored (live stream): {:?}", range);
|
|
}
|
|
|
|
let stream = match registry.get_stream(&instance_id) {
|
|
Some(s) => s,
|
|
None => {
|
|
return (
|
|
StatusCode::NOT_FOUND,
|
|
format!("No WebRenderer instance for id={}", instance_id),
|
|
)
|
|
.into_response()
|
|
}
|
|
};
|
|
|
|
info!(instance_id = %instance_id, "FLAC stream started");
|
|
|
|
Response::builder()
|
|
.status(StatusCode::OK)
|
|
.header(CONTENT_TYPE, "audio/ogg; codecs=flac")
|
|
.header(CACHE_CONTROL, "no-store, no-transform")
|
|
.header(CONNECTION, "keep-alive")
|
|
.header(TRANSFER_ENCODING, "chunked")
|
|
.header("X-Content-Type-Options", "nosniff")
|
|
.body(Body::from_stream(ReaderStream::new(stream)))
|
|
.unwrap()
|
|
.into_response()
|
|
}
|