# Spec : Migration AV1-SVT + Opus **Date :** 2026-03-22 **Objectif :** Remplacer le pipeline H.265/AAC par AV1-SVT/Opus avec sélection automatique du CRF via `ab-av1 crf-search`. Passer la sortie en MKV. Simplifier la structure (suppression du chunking, de l'entrelacement automatique, du remux MP4). --- ## Contexte `vid_convert.py` est un script Python standalone de conversion vidéo pour l'archivage de demux DVD/BluRay. Il orchestre FFmpeg, mkvmerge et des outils tiers pour produire un fichier optimisé qualité/poids. **Sources attendues :** demux HEVC (H.265) en conteneur MKV ou MP4, ou fichiers DVD (MPEG-2, SD). État actuel : H.265 (libx265), AAC-LC (libfdk_aac), encodage en chunks de 300s, sortie MP4. --- ## Changements ### 1. Codec vidéo : libx265 → libsvtav1 - Encodeur : `libsvtav1` - Pixel format : `yuv420p10le` (10 bits, inchangé) - Paramètres SVT-AV1 via `-svtav1-params` (remplace `-x265-params`) - Suppression de la logique de chunks (300s) : encodage en une seule passe - Sortie intermédiaire : `{file}_video.mkv` (une seule piste vidéo, remplace les multiples `{file}_video_t*.mkv`) ### 2. Sélection du CRF via ab-av1 Nouvelle fonction `find_crf(file, enc_options)` : - Commande : `ab-av1 crf-search --input {file} --encoder libsvtav1 --vmaf 96 --enc {enc_options}` - Cible VMAF : **96** (codé en dur) - **Pour les sources HDR10**, ajouter `--vmaf-model path=/usr/share/vmaf/model/vmaf_4k_v0.6.1.json` si disponible, afin d'utiliser un modèle VMAF calibré HDR. Si le modèle est absent, `ab-av1` utilise le modèle par défaut (acceptable, légère imprécision sur HDR). - **Parsing de la sortie :** `ab-av1 crf-search` émet une ligne finale du type `crf 32 VMAF 96.21 ...`. On extrait le CRF en cherchant une ligne contenant à la fois `crf` et `VMAF` et en lisant le token suivant `crf`. - Fallback à CRF 32 si la commande échoue ou si le parsing ne trouve rien. **`enc_options`** est une chaîne de paramètres SVT-AV1 au format `key=value:key=value`, construite dans le `main` : | Flag CLI | Option SVT-AV1 | |---|---| | (base) | `preset=3:tune=0` (VQ, qualité perçue — défaut pour tous les contenus) | | `--animation` | aucun changement de tune (VQ reste optimal) | | `--interlaced` | aucun paramètre SVT-AV1 (yadif géré côté FFmpeg) | Les métadonnées HDR (mastering display, HDR10+) sont passées **séparément** à `convert_video` via `-svtav1-params`, et non via `enc_options`. `enc_options` ne contient que les options de qualité/preset. ### 3. Codec audio : libfdk_aac → libopus - Encodeur : `libopus` (doit être compilé dans FFmpeg — vérifier avec `ffmpeg -codecs | grep opus`) - Mode VBR (`-vbr on`) - Bitrate adaptatif : - ≤ 2 canaux : 128k - 6 canaux (5.1) : 320k - 8 canaux (7.1) : 450k - Multicanal : `-mapping_family 1` - Workaround `5.1(side)` → `5.1` conservé - Normalisation de volume (`volumedetect` + filtre `volume=`) inchangée ### 4. HDR10 statique Les métadonnées mastering display et content-light sont passées via `-svtav1-params` : ``` mastering-display=G(x,y)B(x,y)R(x,y)WP(x,y)L(max,min):content-light=maxcll,maxfall ``` La logique de calcul des coordonnées chromatiques (depuis `side_data_list`) est conservée. ### 5. HDR10+ - **Extraction :** `hdr10plus_parser` (binaire inclus) - **Conteneur source :** si le fichier source est en MKV, la commande d'extraction doit omettre `-vbsf hevc_mp4toannexb` (ce bitstream filter est uniquement nécessaire pour les sources HEVC en MP4). `get_infos` détecte le conteneur via `ffprobe format_name` et adapte la commande. - **Injection :** via `hdr10plus-json={path}` dans `-svtav1-params` (SVT-AV1 natif) ### 6. Dolby Vision - `get_infos` détecte la présence d'un stream DV (entrée `DOVI` dans `side_data_list`) - **Profile 8** (dual-layer, cas le plus fréquent) : fallback automatique vers HDR10 — la couche HDR10 est encodée normalement, l'enhancement layer DV est perdu - **Profile 5** (RPU uniquement, sans couche HDR10 indépendante) : avertissement loggé ; la couche de base est traitée telle quelle (HDR10 si les métadonnées sont présentes dans le stream, sinon SDR) - Dans tous les cas, un message indique : `"Dolby Vision détecté (Profile X). Encodage sans couche DV."` - Pas d'intégration `dovi_tool` ### 7. Entrelacement - `is_interlaced()` supprimée - Nouvel argument CLI `--interlaced` (flag booléen) - Le filtre `yadif` dans `convert_video` n'est appliqué que si `--interlaced` est passé ### 8. Assemblage MKV simplifié - `create_mkv` assemble : `{file}_video.mkv` + N `{file}_audio_*.mka` + N `{file}_subtitle_*.mkv` → `NEW_{filename}.mkv` - Le fichier de sortie est directement nommé `NEW_{filename}.mkv` (suppression du nom intermédiaire `_FINAL.mkv`) - Nettoyage des fichiers temporaires : `{file}_video.mkv`, `{file}_audio_*.mka`, `{file}_subtitle_*.mkv` supprimés après assemblage - `mkv_to_mp4` supprimée ### 9. Corrections - Ligne 308 : `infos = get_infos(file)` décommenté - `interlaced = False` orphelin supprimé - `-t` / `--starttime` supprimé --- ## Arguments CLI finaux | Argument | Description | |---|---| | `f_input` | Fichier source | | `-d` / `--debug` | Logging DEBUG | | `-s` / `--stabilise` | Stabilisation vidéo (vidstab) | | `-a` / `--animation` | Tuning animation (tune=0) | | `-c` / `--vhs` | Restauration VHS (hqdn3d + unsharp) | | `--interlaced` | Forcer le désentrelacement (yadif) | --- ## Flux d'exécution ``` get_infos → [stabilization] → cropping → volume_audio → find_crf → extract_subs (×N) → convert_audio (×N) → convert_video → create_mkv ``` `stabilization` n'est exécutée que si `--stabilise` est passé, avant le crop. --- ## Dépendances | Outil | Usage | Changement | |---|---|---| | `ffmpeg` + `ffprobe` | Encodage, analyse | Inchangé (libopus requis) | | `ab-av1` | Sélection CRF | **Nouveau** | | `hdr10plus_parser` | Extraction HDR10+ | Inchangé | | `mkvmerge` | Assemblage final | Inchangé | | `libfdk_aac` | Audio AAC | **Supprimé** | | `libopus` | Audio Opus | **Nouveau** (dans FFmpeg) | --- ## Hors scope - Dolby Vision Profile 5 avec `dovi_tool` - DV Profile 10 pour AV1 (expérimental, non viable) - Interface utilisateur, tests automatisés