This commit refactors the WebRenderer to use a server-side streaming architecture with OGG-FLAC sink instead of the previous WebSocket-based approach. The changes include: - Replaced WebSocket communication with HTTP streaming using DirectOggFlacSink - Implemented a new pipeline architecture with dedicated handlers for UPnP commands - Added new modules for registration, registry, and streaming - Updated the renderer to work with a pipeline control system - Removed old WebSocket session management - Added support for HTTP streaming with gapless playback - Updated dependencies and features for the new architecture The WebRenderer now acts as a MediaRenderer UPnP device that serves audio streams via HTTP endpoints, with commands relayed to the audio pipeline through a new control system.
149 lines
5.1 KiB
Rust
Executable File
149 lines
5.1 KiB
Rust
Executable File
//! Nodes du pipeline audio
|
|
//!
|
|
//! Ce module contient tous les types de nodes disponibles pour construire
|
|
//! un pipeline audio, ainsi que les traits et structures de support.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use crate::type_constraints::{TypeMismatch, TypeRequirement};
|
|
use crate::AudioSegment;
|
|
|
|
/// Taille par défaut du buffer de channel MPSC pour les nodes
|
|
/// Cette valeur détermine combien de segments audio peuvent être mis en attente
|
|
/// avant que le producteur soit bloqué (backpressure).
|
|
pub const DEFAULT_CHANNEL_SIZE: usize = 16;
|
|
|
|
/// Durée par défaut des chunks audio en millisecondes
|
|
/// Cette valeur détermine la latence de traitement et le compromis efficacité/réactivité.
|
|
/// 50ms offre un bon équilibre pour la plupart des applications de lecture audio.
|
|
pub const DEFAULT_CHUNK_DURATION_MS: f64 = 50.0;
|
|
|
|
// Modules actifs
|
|
pub mod audio_sink;
|
|
pub mod converter_nodes;
|
|
pub mod file_source;
|
|
pub mod flac_file_sink;
|
|
pub mod http_source;
|
|
pub mod resampling_node;
|
|
pub mod timer_buffer_node;
|
|
pub mod timer_node;
|
|
pub mod position_tracker_node;
|
|
|
|
// Modules temporairement désactivés
|
|
/*
|
|
pub mod buffer_node;
|
|
pub mod chromecast_sink;
|
|
pub mod decoder_node;
|
|
pub mod disk_sink;
|
|
pub mod dsp_node;
|
|
pub mod mpd_sink;
|
|
pub mod sink_node;
|
|
pub mod source_node;
|
|
pub mod volume_node;
|
|
*/
|
|
|
|
/// Trait de base pour tous les nodes audio
|
|
///
|
|
/// Tous les nodes du pipeline implémentent ce trait pour permettre
|
|
/// une interface uniforme de traitement des chunks audio.
|
|
#[async_trait::async_trait]
|
|
pub trait AudioNode: Send + Sync {
|
|
/// Push un chunk vers ce node
|
|
///
|
|
/// # Erreurs
|
|
///
|
|
/// Retourne `AudioError::SendError` si l'envoi échoue
|
|
async fn push(&mut self, chunk: Arc<AudioSegment>) -> Result<(), AudioError>;
|
|
|
|
/// Ferme le node proprement
|
|
async fn close(&mut self);
|
|
}
|
|
|
|
/// Trait pour les nodes qui déclarent leurs types acceptés/produits
|
|
///
|
|
/// Ce trait permet de vérifier la compatibilité des types entre nodes
|
|
/// avant de les connecter dans un pipeline.
|
|
///
|
|
/// # Exemples
|
|
///
|
|
/// ```no_run
|
|
/// use pmoaudio::{FileSource, FlacFileSink, TypedAudioNode};
|
|
/// use pmoaudio::type_constraints::check_compatibility;
|
|
///
|
|
/// // Vérifier la compatibilité avant de connecter
|
|
/// let source = FileSource::new("input.flac");
|
|
/// let (sink, tx) = FlacFileSink::new("output.flac");
|
|
///
|
|
/// let source_output = source.output_type();
|
|
/// let sink_input = sink.input_type();
|
|
///
|
|
/// match check_compatibility(&source_output, &sink_input) {
|
|
/// Ok(()) => println!("Types compatibles!"),
|
|
/// Err(e) => eprintln!("Types incompatibles: {}", e),
|
|
/// }
|
|
/// ```
|
|
pub trait TypedAudioNode {
|
|
/// Retourne les types que ce node peut accepter en entrée
|
|
///
|
|
/// Pour les sources (qui ne consomment rien), retourne `None`.
|
|
fn input_type(&self) -> Option<TypeRequirement>;
|
|
|
|
/// Retourne les types que ce node peut produire en sortie
|
|
///
|
|
/// Pour les sinks (qui ne produisent rien), retourne `None`.
|
|
fn output_type(&self) -> Option<TypeRequirement>;
|
|
|
|
/// Vérifie si ce node peut accepter les chunks d'un producer donné
|
|
///
|
|
/// # Erreurs
|
|
///
|
|
/// Retourne `AudioError::TypeMismatch` si les types sont incompatibles
|
|
fn can_accept_from(&self, producer: &dyn TypedAudioNode) -> Result<(), AudioError> {
|
|
match (producer.output_type(), self.input_type()) {
|
|
(Some(prod), Some(cons)) => crate::type_constraints::check_compatibility(&prod, &cons)
|
|
.map_err(|e| AudioError::TypeMismatch(e)),
|
|
(None, Some(_)) => Err(AudioError::TypeMismatch(TypeMismatch {
|
|
producer: TypeRequirement::any(), // Placeholder
|
|
consumer: self.input_type().unwrap(),
|
|
incompatible_type: None,
|
|
})),
|
|
_ => Ok(()), // Si pas de contrainte, toujours compatible
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Erreurs possibles dans le pipeline audio
|
|
#[derive(Debug, Clone)]
|
|
pub enum AudioError {
|
|
/// Échec d'envoi d'un chunk à travers un channel
|
|
SendError,
|
|
/// Échec de réception d'un chunk depuis un channel
|
|
ReceiveError,
|
|
/// Erreur de traitement avec message descriptif
|
|
ProcessingError(String),
|
|
/// Incompatibilité de types entre nodes
|
|
TypeMismatch(TypeMismatch),
|
|
/// Un nœud enfant s'est terminé prématurément (anormal dans un pipeline descendant)
|
|
ChildFinished,
|
|
/// Un nœud enfant est mort (channel fermé pendant un send)
|
|
ChildDied,
|
|
/// Erreur d'I/O (fichier, réseau, etc.)
|
|
IoError(String),
|
|
}
|
|
|
|
impl std::fmt::Display for AudioError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
AudioError::SendError => write!(f, "Failed to send audio chunk"),
|
|
AudioError::ReceiveError => write!(f, "Failed to receive audio chunk"),
|
|
AudioError::ProcessingError(msg) => write!(f, "Processing error: {}", msg),
|
|
AudioError::TypeMismatch(tm) => write!(f, "{}", tm),
|
|
AudioError::ChildFinished => write!(f, "Child node finished prematurely"),
|
|
AudioError::ChildDied => write!(f, "Child node died unexpectedly"),
|
|
AudioError::IoError(msg) => write!(f, "I/O error: {}", msg),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for AudioError {}
|