152 lines
5.7 KiB
Markdown
152 lines
5.7 KiB
Markdown
|
|
# 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<AtomicBool>,
|
||
|
|
```
|
||
|
|
|
||
|
|
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)
|