Files
pmomusic/.kilo/plans/1775302116634-sunny-nebula.md
Eric Coissac 8924552696 Add StreamType for multi-client support and improved UPnP control
- 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
2026-04-04 14:37:32 +02:00

5.7 KiB

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:

    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:
      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)