# 🎯 DESIGN - Pipeline d'encodage audio minimal mais robuste **Date**: 2025-01-27 **Mission**: P0 - Rendre le stream-server fonctionnel --- ## 1. ARCHITECTURE ### 1.1 Vue d'ensemble ``` ┌─────────────────┐ │ API Routes │ │ /transcode │ │ /job/{id} │ │ /hls/{id}/... │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ TranscodingEngine│ │ - Queue │ │ - JobManager │ │ - WorkerPool │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ Worker Pool │ │ (N workers) │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ FFmpeg Process │ │ (HLS output) │ └─────────────────┘ ``` ### 1.2 Composants principaux #### **TranscodingEngine** - **Responsabilité**: Orchestrer le transcodage - **Composants**: - `PriorityQueue`: File d'attente des jobs - `JobManager`: Stockage et suivi des jobs - `WorkerPool`: Pool de workers parallèles #### **JobManager** - **Responsabilité**: Stocker et suivre l'état des jobs - **Stockage**: `HashMap` (en mémoire pour P0) - **Méthodes**: - `enqueue(job) -> Uuid` - `get_status(job_id) -> Option` - `update_status(job_id, status)` #### **WorkerPool** - **Responsabilité**: Traiter les jobs en parallèle - **Implémentation**: Tokio tasks avec canal async - **Workers**: N workers (configurable, défaut: `num_cpus()`) #### **Pipeline** - **Responsabilité**: Exécuter FFmpeg et gérer les erreurs - **Fonction**: `transcode_to_hls(input_path, output_dir, codec, bitrate) -> Result` --- ## 2. PIPELINE D'ENCODAGE ### 2.1 Fonction principale ```rust async fn transcode_to_hls( input_path: &Path, output_dir: &Path, codec: AudioCodec, bitrate: u32, profile: &QualityProfile, ) -> Result ``` **Étapes**: 1. Créer le répertoire de sortie `{output_dir}/{job_id}/` 2. Construire la commande FFmpeg 3. Exécuter FFmpeg avec timeout 4. Vérifier que `index.m3u8` et segments existent 5. Retourner `TranscodeResult` avec chemins ### 2.2 Structure de sortie HLS ``` {output_dir}/ {job_id}/ index.m3u8 # Master playlist HLS segment_00001.ts segment_00002.ts segment_00003.ts ... ``` ### 2.3 Format du manifest HLS ```m3u8 #EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:6 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:6.0, segment_00001.ts #EXTINF:6.0, segment_00002.ts ... #EXT-X-ENDLIST ``` --- ## 3. COMMAND BUILDER FFMPEG ### 3.1 Arguments par codec #### **AAC** ```bash ffmpeg -i input.wav \ -c:a aac \ -b:a 192k \ -ar 44100 \ -ac 2 \ -f hls \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename segment_%05d.ts \ index.m3u8 ``` #### **MP3** ```bash ffmpeg -i input.wav \ -c:a libmp3lame \ -b:a 192k \ -ar 44100 \ -ac 2 \ -f hls \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename segment_%05d.ts \ index.m3u8 ``` #### **Opus** ```bash ffmpeg -i input.wav \ -c:a libopus \ -b:a 192k \ -ar 48000 \ -ac 2 \ -f hls \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename segment_%05d.ts \ index.m3u8 ``` #### **FLAC** ```bash ffmpeg -i input.wav \ -c:a flac \ -compression_level 5 \ -ar 44100 \ -ac 2 \ -f hls \ -hls_time 6 \ -hls_playlist_type vod \ -hls_segment_filename segment_%05d.ts \ index.m3u8 ``` ### 3.2 Améliorations du builder - ✅ Ajouter `-hls_list_size 0` pour VOD (liste complète) - ✅ Valider que FFmpeg est installé au démarrage - ✅ Gérer les erreurs de construction de commande --- ## 4. GESTION DES ERREURS ### 4.1 Types d'erreurs ```rust #[derive(Debug, thiserror::Error)] pub enum TranscodeError { #[error("Input file not found: {0}")] InputNotFound(PathBuf), #[error("FFmpeg execution failed: {0}")] FfmpegError(String), #[error("Transcoding timed out after {0} seconds")] Timeout(u64), #[error("Output directory creation failed: {0}")] OutputDirError(String), #[error("HLS manifest not generated")] ManifestMissing, #[error("Insufficient disk space")] DiskSpaceError, } ``` ### 4.2 Gestion du timeout - **Timeout**: 5 minutes par défaut - **Action**: Tuer le processus FFmpeg si timeout - **Implémentation**: Utiliser `Command::kill()` sur le child process ### 4.3 Retry logic - **Max retries**: 3 - **Conditions de retry**: - Erreur temporaire (timeout, IO error) - Pas de retry sur erreur permanente (fichier corrompu) --- ## 5. API REST ### 5.1 POST `/v1/stream/transcode` **Request**: ```json { "file": "", "codec": "aac", "bitrate": 192000, "quality_profile": "high" } ``` **Response**: ```json { "job_id": "550e8400-e29b-41d4-a716-446655440000", "status": "queued", "message": "Job submitted successfully" } ``` ### 5.2 GET `/v1/stream/job/{id}` **Response**: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "progress": 45.5, "created_at": "2025-01-27T10:00:00Z", "started_at": "2025-01-27T10:00:05Z", "completed_at": null, "error": null } ``` ### 5.3 GET `/v1/stream/hls/{job_id}/index.m3u8` **Response**: Contenu du fichier `index.m3u8` avec `Content-Type: application/vnd.apple.mpegurl` ### 5.4 GET `/v1/stream/hls/{job_id}/{segment}` **Response**: Contenu binaire du segment `.ts` avec `Content-Type: video/mp2t` --- ## 6. INTÉGRATION DANS APPSTATE ### 6.1 Ajout de TranscodingEngine ```rust pub struct AppState { // ... existing fields ... pub transcoding_engine: Arc, } ``` ### 6.2 Initialisation dans `AppState::new()` ```rust let transcoding_engine = Arc::new( TranscodingEngine::new(num_cpus::get()) ); transcoding_engine.start(); ``` --- ## 7. LOGS STRUCTURÉS ### 7.1 Événements à logger - `job_queued`: Job soumis - `job_started`: Worker commence le traitement - `job_progress`: Progression (toutes les 10%) - `job_completed`: Job terminé avec succès - `job_failed`: Job échoué avec erreur ### 7.2 Format des logs ```rust tracing::info!( job_id = %job.id, status = "queued", priority = ?job.priority, "Job queued for transcoding" ); ``` --- ## 8. TESTS ### 8.1 Tests unitaires - ✅ Test command builder pour chaque codec - ✅ Test pipeline avec fichier WAV de test - ✅ Test job manager en mémoire ### 8.2 Tests d'intégration - ✅ Upload fichier → job → index.m3u8 généré - ✅ Vérification existence segments - ✅ Test timeout FFmpeg --- ## 9. LIMITATIONS P0 - ❌ Pas de multi-bitrate adaptatif (ABR) - ❌ Pas de DRM - ❌ Pas de persistance en base de données (mémoire uniquement) - ❌ Pas de métriques Prometheus détaillées - ❌ Pas de cleanup automatique des anciens jobs --- **Prochaine étape**: Phase 3 - Implémentation