# Plan: pmowebrenderer - Améliorations et Multi-client avec DSP ## Objectifs 1. **Améliorer l'intégration UPnP Control** - Meilleur fonctionnement des commandes Play/Pause/Seek - Contrôle piloté près de la sortie (streaming) plutôt qu'au début du pipeline - Pour Pause: latence actuelle trop importante - Play/Pause/Seek doivent fonctionner simultanément sur tous les clients 2. **Améliorer la performance - Latence** - Réduire le délai entre l'envoi et la lecture 3. **Ajouter le support multi-client avec DSP** - Chaque client peut avoir son propre pipeline DSP ## Comportement UPnP Control ### Mode Radio (flux infini) - Pas de pause possible, hanya next ou stop - Seek n'a pas de sens ### Architecture ``` PlayerSource → ResamplingNode → ToI24Node ├──→ [DSP Client 1] → StreamingOggFlacSink 1 ├──→ [DSP Client 2] → StreamingOggFlacSink 2 └──→ ... (dynamique) ``` Le control point UPnP voit UN seul Media Renderer. Les commandes Play/Pause/Seek affectent TOUTES les sorties client simultanément. ## État Actuel Le pipeline actuel est linéaire pour un seul client: ``` PlayerSource → ResamplingNode (96kHz) → ToI24Node → StreamingOggFlacSink ``` **Note importante:** Utiliser les crates pmoaudio et pmoaudio-ext existantes. Il est possible d'avoir plusieurs `StreamingOggFlacSink` consommant le même flux. Après ToI24Node, brancher en étoiles les différents DSP pour les différents clients. ## Plan d'Implémentation ### Phase 1: Amélioration UPnP Control 1. **Analyser les handlers existants** dans `handlers.rs` 2. **Identifier les problèmes** avec Play/Pause/Seek: - Timing des transitions d'état - Gestion des erreurs - Synchronisation entre clients HTTP et état UPnP 3. **Améliorer la fiabilité** des commandes - Piloter le contrôle près de la sortie (streaming) - Différerencier le comportement radio vs piste finie ### Phase 2: Amélioration Latence 1. **Réduire le buffer** dans `StreamingOggFlacSink` 2. **Optimiser le pacing** (actuellement max 0.5s ahead) 3. **Améliorer la directité** du chemin audio ### Phase 3: Architecture Multi-client avec DSP 1. **Refactorer le pipeline** pour supporter plusieurs clients comme décrit ci-dessus 2. **Créer un système de DSP** dans pmoaudio ou pmoaudio-ext: - Interface commune pour les effets audio - Config des DSP via PMOconfig - Room correction: equalizer, FIR filter, delay, gain 3. **Gérer le cycle de vie**: - Création du pipeline par client - Nettoyage lors de la déconnexion - Partage de la source commune entre clients ## Fichiers à Modifier - `pipeline.rs` - Refactoring pour multi-client - `handlers.rs` - Amélioration UPnP control - `stream.rs` - Gestion multi-client - `state.rs` - État par client - pmoaudio ou pmoaudio-ext pour les mécanismes DSP ## Défis Potentiels - Performance CPU avec plusieurs clients - Synchronisation des clients avec le même contenu - Gestion du gapless entre les pistes avec multi-client ### Gestion de la Pause **Option recommandée: Silence (zéros)** - Pendant la pause, continuer à envoyer des zéros encodés en FLAC - Le client HTTP maintient sa connexion TCP alive - Pas de reconnexion nécessaire quand on reprend la lecture - Avantage: Seamless pour le client **Pourquoi pas réduction du sample rate:** - Le header FLAC définit le sample rate en固定entête - Changer le sample rate en cours de flux invalidate le flux entier - Rebuild du flux serait plus complexe que le gain obtenu - FLAC compresse très bien les zéros de toute façon (beaucoup de répétitions) **Autre option envisagée mais non recommandée:** - Suspendre l'envoi: Le client HTTP va timeout et se déconnecter - Segment OGG avec metadata: Complexe à implémenter, nécessite modification du client ### Phase 0: StreamingOggFlacSink avec contrôle Pause **Distinction Radio vs Pistes finies:** | Mode | Comportement pendant Pause | |------|---------------------------| | **Radio (flux infini)** | Les chunks qui arrivent sont ignorés/perdus. On envoie du silence. La source continue à produire mais on n'en tient pas compte. | | **Pistes finies** | On bloque la consommation des chunks. Par backpressure, le pipeline en amont s'arrête (TimerBufferNode arrête d'envoyer). La lecture est truly arrêtée. | **Architecture actuelle analysée:** ``` AudioSegment → StreamingOggFlacSink → FLAC encoder → OGG wrapper → timed_broadcast → clients ``` **Implémentation suggérée:** 1. **État de lecture distingué:** - `PlaybackMode::Radio` - ignore les chunks entrants pendant pause - `PlaybackMode::Track` - bloque la consommation (backpressure) 2. **Dans SharedSinkContext:** ```rust pub enum PlaybackMode { Radio, // Flux infini - ignore chunks pendant pause Track, // Piste finie - block par backpressure } pub playback_mode: PlaybackMode, pub is_paused: Arc, ``` 3. **Traitement différent selon le mode:** - **Radio**: Si `is_paused`, envoyer silence (zéros) mais perdre les chunks entrants - **Track**: Si `is_paused`, ne pas consommer les chunks → backpressure → arrêt du pipeline en amont 4. **Transition automatique:** - Détecter le type de contenu via les métadonnées du TrackBoundary - **Enrichir TrackBoundary** avec un champ `stream_type`: ```rust pub enum StreamType { Continuous, // Radio/webcast - flux infini Finite, // Piste/album - flux avec fin définie } pub stream_type: StreamType, ``` - Si durée inconnue = Radio (Continuous), si durée connue = Track (Finite)