veza/veza-stream-server/docs/STREAM_ENCODING_PIPELINE.md
okinrev b7955a680c P0: stabilisation backend/chat/stream + nouvelle base migrations v1
Backend Go:
- Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN.
- Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError).
- Sécurisation de config.go, CORS, statuts de santé et monitoring.
- Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles).
- Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés.
- Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*.

Chat server (Rust):
- Refonte du pipeline JWT + sécurité, audit et rate limiting avancé.
- Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing).
- Nettoyage des panics, gestion d’erreurs robuste, logs structurés.
- Migrations chat alignées sur le schéma UUID et nouvelles features.

Stream server (Rust):
- Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core.
- Transactions P0 pour les jobs et segments, garanties d’atomicité.
- Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION).

Documentation & audits:
- TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services.
- Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3).
- Scripts de reset et de cleanup pour la lab DB et la V1.

Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
2025-12-06 11:14:38 +01:00

13 KiB

🎵 Pipeline d'Encodage Audio - Documentation Technique

Date: 2025-01-27
Service: veza-stream-server
Version: 1.0.0


📋 Table des Matières

  1. Architecture
  2. Format Manifest HLS
  3. Mapping Qualité → Codec/Bitrate
  4. Gestion des Erreurs
  5. Tests
  6. Instructions de Développement

🏗️ Architecture

Vue d'ensemble

Le pipeline d'encodage audio est composé de plusieurs couches :

┌─────────────────────────────────────────────────────────┐
│                    API REST Layer                        │
│  POST /admin/stream/encode/:track_id/:quality            │
│  GET  /admin/stream/status/:track_id                     │
│  GET  /stream/:track_id/:quality/index.m3u8             │
│  GET  /stream/:track_id/:quality/segment_*.ts           │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Encoding Service Layer                      │
│  - Vérifie track.source_path en DB                      │
│  - Crée répertoire /data/streams/<track_id>/<quality>/  │
│  - Crée EncodeJob et l'envoie dans la queue             │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Encoder Pool (Worker Threads)               │
│  - Queue: async_channel::Receiver<EncodeJob>              │
│  - Workers: Vec<EncoderWorker> (1 thread = 1 FFmpeg)     │
│  - Nombre: min(nb_cpu / 2, 8)                           │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              FFmpeg Execution                            │
│  - Build command avec FfmpegCommandBuilder               │
│  - Spawn processus FFmpeg                                │
│  - Capture stderr en streaming                           │
│  - Parse progression (FfmpegProgress)                    │
│  - Détecte crashes et erreurs                            │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Post-Processing                             │
│  - Parse manifest .m3u8 pour extraire segments          │
│  - Calcule durée par segment                             │
│  - Insère segments en DB (stream_segments)              │
│  - Met à jour stream_jobs.status = 'done'                │
└─────────────────────────────────────────────────────────┘

Composants Principaux

1. EncodeJob

Structure représentant un job d'encodage :

  • track_id: UUID du track
  • input_path: Chemin vers le fichier source
  • output_dir: Répertoire de sortie pour segments HLS
  • codec: Codec audio (AAC, Opus, MP3, FLAC)
  • bitrate: Bitrate en bps
  • quality: Qualité (low, medium, high, hi_res)

2. EncoderPool

Pool de workers qui traitent les jobs d'encodage :

  • Queue asynchrone (async_channel)
  • Workers threads dédiés
  • Nombre de workers: min(nb_cpu / 2, 8)
  • Chaque worker spawn un processus FFmpeg

3. EncodingService

Service de haut niveau pour :

  • Lancer des encodages
  • Vérifier les statuts
  • Gérer les répertoires de sortie

📄 Format Manifest HLS

Structure d'un Manifest Media Playlist

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:4.000,
segment_00000.ts
#EXTINF:4.000,
segment_00001.ts
#EXTINF:4.000,
segment_00002.ts
#EXT-X-ENDLIST

Tags HLS Utilisés

  • #EXTM3U: Identifie le fichier comme playlist M3U8
  • #EXT-X-VERSION:3: Version du protocole HLS
  • #EXT-X-TARGETDURATION:6: Durée maximale d'un segment (secondes)
  • #EXT-X-MEDIA-SEQUENCE:0: Numéro de séquence du premier segment
  • #EXT-X-PLAYLIST-TYPE:VOD: Type de playlist (VOD = Video On Demand)
  • #EXTINF:4.000,: Durée du segment suivant (secondes)
  • #EXT-X-ENDLIST: Marque la fin de la playlist (VOD uniquement)

Structure des Fichiers de Sortie

/data/streams/
  <track_id>/
    low/
      index.m3u8
      segment_00000.ts
      segment_00001.ts
      ...
    medium/
      index.m3u8
      segment_00000.ts
      segment_00001.ts
      ...
    high/
      index.m3u8
      segment_00000.ts
      segment_00001.ts
      ...
    hi_res/
      index.m3u8
      segment_00000.ts
      segment_00001.ts
      ...

🎚️ Mapping Qualité → Codec/Bitrate

Qualité Codec Bitrate Sample Rate Channels HLS Segment Time
low AAC 64kbps 22.05kHz 1 (mono) 4s
medium AAC 128kbps 44.1kHz 2 (stereo) 4s
high AAC 192kbps 44.1kHz 2 (stereo) 4s
hi_res AAC 320kbps 48kHz 2 (stereo) 4s

Rationale

  • Low (64kbps, mono): Optimisé pour faible bande passante (mobile, 3G)
  • Medium (128kbps, stéréo): Qualité standard pour streaming web
  • High (192kbps, stéréo): Haute qualité pour audiophiles
  • Hi-Res (320kbps, stéréo): Qualité maximale (premium)

Note: Opus pourra être ajouté plus tard pour ultra-low latency (<5ms).


⚠️ Gestion des Erreurs

Erreurs FFmpeg Courantes

Erreur Cause Solution
FFmpeg exited with status: 1 Fichier source invalide ou codec manquant Vérifier le fichier source, installer codecs
HLS manifest not generated FFmpeg a échoué silencieusement Vérifier stderr FFmpeg, logs
Encoding timed out Fichier trop long ou CPU saturé Augmenter timeout ou réduire workers
Failed to create output directory Permissions insuffisantes Vérifier permissions /data/streams

Statuts de Job

  • pending: Job en attente dans la queue
  • encoding: Job en cours de traitement
  • done: Job terminé avec succès
  • error: Job échoué (voir error_message)

Retry Logic

  • Max retries: 3
  • Conditions: Erreurs temporaires (timeout, IO)
  • Pas de retry: Erreurs permanentes (fichier invalide, codec manquant)

🧪 Tests

Tests Unitaires

Command Builder

#[test]
fn test_build_hls_command() {
    let cmd = FfmpegCommandBuilder::new()
        .input("input.wav")
        .output("output/index.m3u8")
        .audio_codec(AudioCodec::AAC)
        .bitrate(128000)
        .container(ContainerFormat::HLS)
        .hls_time(4)
        .build()
        .unwrap();
    
    // Vérifier que la commande contient les bonnes options
    assert!(cmd.get_args().contains(&"-threads".to_string()));
    assert!(cmd.get_args().contains(&"-map".to_string()));
}

Progress Parser

#[test]
fn test_parse_ffmpeg_progress() {
    let line = "size=1024kB time=00:00:05.12 bitrate=128.0kbits/s speed=10.2x";
    let progress = FfmpegProgress::parse(line).unwrap();
    
    assert_eq!(progress.time, Some(Duration::from_millis(5120)));
    assert_eq!(progress.speed, Some(10.2));
}

Tests d'Intégration

Setup

# Créer un fichier audio de test
ffmpeg -f lavfi -i "sine=frequency=440:duration=10" /tmp/test_audio.wav

Test Complet

#[tokio::test]
#[ignore] // Nécessite FFmpeg installé
async fn test_encode_track() {
    let db_pool = create_test_pool().await;
    let encoder_pool = FfmpegEncoderPool::new(db_pool.clone(), None).await.unwrap();
    let service = EncodingService::new(
        encoder_pool,
        db_pool,
        "/tmp/streams_test"
    );
    
    // Encoder un track de test
    service.encode_track(track_id, "medium").await.unwrap();
    
    // Attendre la fin (timeout 60s)
    tokio::time::sleep(Duration::from_secs(60)).await;
    
    // Vérifier que le manifest existe
    let manifest_path = PathBuf::from("/tmp/streams_test")
        .join(track_id.to_string())
        .join("medium")
        .join("index.m3u8");
    
    assert!(manifest_path.exists());
    
    // Vérifier que les segments sont en DB
    let segments = sqlx::query!(
        "SELECT COUNT(*) FROM stream_segments WHERE track_id = $1 AND quality = 'medium'",
        track_id
    )
    .fetch_one(&db_pool)
    .await
    .unwrap();
    
    assert!(segments.count.unwrap_or(0) > 0);
}

🛠️ Instructions de Développement

Prérequis

# Installer FFmpeg
sudo apt-get update
sudo apt-get install -y ffmpeg

# Vérifier la version
ffmpeg -version
# Version minimale: 4.0+

Configuration

Variables d'Environnement

# Répertoire de sortie pour les segments HLS
export STREAM_OUTPUT_DIR=/data/streams

# URL de la base de données
export DATABASE_URL=postgresql://veza:password@localhost:5432/veza_db

Migrations SQL

# Appliquer les migrations
psql $DATABASE_URL -f migrations/001_create_stream_jobs.sql
psql $DATABASE_URL -f migrations/002_create_stream_segments.sql

Compilation

cd veza-stream-server
cargo build --release

Exécution

# Démarrer le serveur
cargo run --release

# Ou avec variables d'environnement
STREAM_OUTPUT_DIR=/data/streams cargo run --release

Tests

# Tests unitaires (sans FFmpeg)
cargo test

# Tests d'intégration (nécessite FFmpeg)
cargo test -- --ignored

Monitoring

Logs

Les logs sont émis via tracing :

  • info!: Événements importants (démarrage workers, jobs terminés)
  • debug!: Progression FFmpeg (time, speed)
  • warn!: Erreurs non-critiques (retry, timeout)
  • error!: Erreurs critiques (échec encodage, crash FFmpeg)

Métriques Prometheus

# Nombre de jobs en cours
stream_encoding_jobs_active{quality="medium"} 2

# Nombre de jobs terminés
stream_encoding_jobs_completed_total{quality="medium"} 150

# Nombre de jobs échoués
stream_encoding_jobs_failed_total{quality="medium"} 5

# Temps moyen d'encodage (secondes)
stream_encoding_duration_seconds{quality="medium"} 45.2

Dépannage

FFmpeg non trouvé

# Vérifier que FFmpeg est installé
which ffmpeg

# Vérifier les permissions
ls -la /data/streams

Erreurs de DB

# Vérifier la connexion
psql $DATABASE_URL -c "SELECT COUNT(*) FROM stream_jobs;"

# Vérifier les tables
psql $DATABASE_URL -c "\dt stream_*"

Workers bloqués

# Vérifier les processus FFmpeg
ps aux | grep ffmpeg

# Tuer les processus bloqués
pkill -9 ffmpeg

📊 Performance

Benchmarks

Fichier Source Durée Qualité Temps Encodage CPU Usage
3 min MP3 (128kbps) 3:00 medium ~15s 25%
5 min WAV (44.1kHz) 5:00 high ~30s 40%
10 min FLAC (96kHz) 10:00 hi_res ~90s 60%

Optimisations

  1. Pool de Workers: Limiter à min(nb_cpu / 2, 8) pour éviter saturation
  2. Isolation CPU: -threads 1 par processus FFmpeg
  3. Cache: Réutiliser les encodeurs si possible (future feature)
  4. Priorité: Jobs urgents en tête de queue

🔮 Roadmap

Phase 1 (Actuel)

  • Pool d'encodeurs FFmpeg
  • Support HLS avec segments
  • API REST complète
  • Persistance en DB

Phase 2 (Future)

  • Support Opus pour ultra-low latency
  • Adaptive bitrate (ABR) automatique
  • Cache des encodeurs réutilisables
  • Cleanup automatique des segments anciens

Phase 3 (Future)

  • Streaming live (pas seulement VOD)
  • Support DASH en plus de HLS
  • Hardware acceleration (GPU encoding)
  • Multi-pass encoding pour meilleure qualité

📚 Références


Auteur: Veza Stream Server Team
Dernière mise à jour: 2025-01-27