- Introduce `Streamtype` enum (Continuous vs Finite) to distinguish radio streams from finite tracks - Enrich `TrackBoundary` sync marker with stream type for proper pause behavior per mode (silence vs backpressure) - Update all sources and sinks to pass `StreamType` when creating track boundaries - Radio Paradise, HTTP source → Continuous (infinite) - Improve UPnP control architecture: pause sends silence for radio, blocks pipeline via backpressure for tracks - Prepare groundwork for multi-client DSP architecture with shared source and per-DSP pipelines
5.7 KiB
Plan: pmowebrenderer - Améliorations et Multi-client avec DSP
Objectifs
-
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
-
Améliorer la performance - Latence - Réduire le délai entre l'envoi et la lecture
-
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
- Analyser les handlers existants dans
handlers.rs - Identifier les problèmes avec Play/Pause/Seek:
- Timing des transitions d'état
- Gestion des erreurs
- Synchronisation entre clients HTTP et état UPnP
- 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
- Réduire le buffer dans
StreamingOggFlacSink - Optimiser le pacing (actuellement max 0.5s ahead)
- Améliorer la directité du chemin audio
Phase 3: Architecture Multi-client avec DSP
-
Refactorer le pipeline pour supporter plusieurs clients comme décrit ci-dessus
-
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
-
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-clienthandlers.rs- Amélioration UPnP controlstream.rs- Gestion multi-clientstate.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:
-
État de lecture distingué:
PlaybackMode::Radio- ignore les chunks entrants pendant pausePlaybackMode::Track- bloque la consommation (backpressure)
-
Dans SharedSinkContext:
pub enum PlaybackMode { Radio, // Flux infini - ignore chunks pendant pause Track, // Piste finie - block par backpressure } pub playback_mode: PlaybackMode, pub is_paused: Arc<AtomicBool>, -
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
- Radio: Si
-
Transition automatique:
- Détecter le type de contenu via les métadonnées du TrackBoundary
- Enrichir TrackBoundary avec un champ
stream_type: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)