Manuel pratique d'écriture ASS / incrustation libass
1. Modèle d'en-tête (à copier directement)
[Script Info]
ScriptType: v4.00+
PlayResX: 2560
PlayResY: 1080
WrapStyle: 2
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.709
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: X,Microsoft YaHei,100,&H00F4F1EA,&H00F4F1EA,&H00140F11,&HFF000000,0,0,0,0,100,100,0,0,1,0,0,5,30,30,30,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.39,0:00:03.75,X,,0,0,0,,{\an5\pos(500,300)\fnAnton\fs150\1c&H00F4F1EA&\bord3\3c&H00140F11&\frz-4\fscx118\fscy118\t(0,130,\fscx100\fscy100)\fad(100,100)}WALK
2. Quatre pièges fatals de format (tous rencontrés dans ce projet)
- L'horodatage doit être
H:MM:SS.cc- Correct :
0:00:01.50,0:01:02.30 - Erroné :
0:01.50(libass signaleBad timestamp, la ligne entière est ignorée → pas de sous-titres sur toute la vidéo, mais ffmpeg retourne 0)
- Correct :
- La couleur de la ligne Style ne doit pas se terminer par
&- Correct :
&H00F4F1EA,&H00F4F1EA,&H00140F11,&HFF000000 - Erroné :
&H00F4F1EA&,...→ L'analyse de la ligne Style échoue → pareil, « succès mais pas de texte » - Dans les balises inline des événements, la couleur doit avoir
&:\1c&H00F4F1EA&
- Correct :
- Le répertoire de polices
fontsdirdoit être un chemin relatif + répertoire de travail- Placez
fonts/dans le répertoire de sortie,cddans ce répertoire puisass=x.ass:fontsdir=fonts. - Les chemins absolus avec lettre de lecteur et deux-points sont risqués dans le filtergraph ; les chemins relatifs avec caractères chinois fonctionnent (le cwd peut être en chinois, mais les paramètres du filtre doivent être en ASCII de préférence).
- Placez
- Le fichier ASS doit être écrit en
utf-8-sig, avec un nom de fichier ASCII- Les noms de fichier en chinois sont instables dans les paramètres du filtre ; utilisez
_s4.asspar exemple.
- Les noms de fichier en chinois sont instables dans les paramètres du filtre ; utilisez
Commande de diagnostic (voir ce que libass lit réellement) :
& $ff -hide_banner -v verbose -f lavfi -i "color=c=0x303040:s=2560x1080:r=30:d=2" `
-vf "ass=_s4.ass:fontsdir=fonts" -c:v libx264 -crf 22 -pix_fmt yuv420p _t.mp4 2>&1 | Select-String 'Bad timestamp|Style|font file'
3. Sémantique des couleurs / alpha
- Couleur :
&HBBGGRR&(ordre Bleu-Vert-Rouge), ex : blanc&H00F4F1EA&, noir d'encre&H00140F11&, rose&H00B35CFF&, lavande&H00FFA4C4&, crème&H00D7EEF8&, or&H0054C9FF&. - Transparence
\1a:&H00&complètement opaque ;&HFF&complètement transparent (plus le hexadécimal est élevé, plus c'est transparent). Les clignotements défaillants de la version de développement initiale étaient dus à un alpha inversé. - La PrimaryColour de la ligne Style est
&HAABBGGRR(AA en premier, sans&final).
4. Polices
- libass utilise fontconfig/directwrite, le nom de famille doit être exact. Vérifiez avec PIL :
from PIL import ImageFont
ImageFont.truetype(r"...\fonts\Anton-Regular.ttf", 40).getname() # ('Anton','Regular')
- Polices de ce projet : Anton / Archivo Black / Bebas Neue / Permanent Marker / Silkscreen / Rubik Glitch / Bungee / Monoton / Pirata One / UnifrakturCook ; les polices système Georgia, Microsoft YaHei peuvent être utilisées directement avec
\fnGeorgia,\fnMicrosoft YaHei. - Permanent Marker dans libass fait environ 0,79× fs, donc agrandissez la taille de police / la taille des blocs de couleur selon ce facteur.
- En cas de glyphes manquants (comme ♥ ★), le remplacement revient automatiquement à ArialMT, ce qui peut s'afficher mais avec une forme différente ; pour une précision maximale, utilisez
\fnMicrosoft YaHei.
5. Référence rapide des balises d'animation
| Balise | Utilisation |
|---|---|
\fad(in,out) | Fondu en millisecondes |
\move(x1,y1,x2,y2,t1,t2) | Déplacement (à choisir avec \pos, move est prioritaire) |
\t(t1,t2,\tag...) | Interpolation temporelle, ex \t(0,130,\fscx100\fscy100\blur(0)) |
\frz / \fscx / \fscy | Rotation/échelle (les valeurs négatives peuvent faire un flip) |
\blur(n) | Flou (efficace pour un grand mot qui devient flou puis net) |
\clip(x1,y1,x2,y2) | Recadrage rectangulaire (pour les effets de coupe, de balayage, d'apparition) |
\org(x,y) | Centre de rotation/échelle |
\p1 + dessin | Rectangle m 0 0 l w 0 w h 0 h, etc. |
Piège : Deux \t consécutifs sur la même propriété n'en prennent qu'un (ex : animation de rétrécissement d'une ligne de règle). Solution : diviser en deux événements / deux redessins.
Piège : Les coordonnées du dessin \p1 sont ancrées en haut à gauche, et libass décale également l'ensemble vers le haut de h/2 ; utilisez \an7\pos(haut-gauche) + coordonnées absolues pour la stabilité, et \org pour la rotation/le balayage.
Piège : Pour les surlignements qui « balayent » derrière le mot, utilisez une couche de dessin \fscx0 → \t(0,150,\fscx100) avec \org comme origine.
6. Commandes de rendu
6.1 Incrustation directe (la plus courante)
cd répertoire_de_sortie
& $ff -v error -y -i _base_30s.mp4 -vf "ass=_s4.ass:fontsdir=fonts" `
-c:v libx264 -preset medium -crf 16 -pix_fmt yuv420p -movflags +faststart OUT.mp4
30 s / 2560×1080 / volume de sous-titres chinois ≈ 1–3 minutes.
6.2 Calque de superposition transparent (pour logiciel de montage)
& $ff -v error -y -f lavfi -i "[email protected]:s=2560x1080:r=30,format=rgba" `
-vf "fps=30,ass=_sub.ass:alpha=1" -t 154.6 `
-c:v libvpx-vp9 -pix_fmt yuva420p -b:v 0 -crf 30 -row-mt 1 -auto-alt-ref 0 `
-metadata:s:v:0 alpha_mode=1 OUT_alpha.webm
- Le décodeur doit être
-c:v libvpx-vp9; le décodeur natif vp9 perd l'alpha. - Alternative « superposition par écran » mp4 priorisant la compatibilité : fond noir + texte lumineux, le logiciel de montage utilise le mode Screen/Lighten.
6.3 Capture d'image de contrôle (ne regardez pas les logs de rendu, regardez l'image)
& $ff -v error -ss 12.4 -i OUT.mp4 -frames:v 1 -q:v 2 -y _check.jpg
Attention : lorsque
-ssest placé avant-i(recherche en entrée), dans certains cas la timeline ASS est redirigée par rapport à la sortie, ce qui donne un aperçu vide ; pour la capture de vérification, utilisez la recherche côté sortie (-i OUT.mp4 -ss 12.4) ou extrayez un segment entier puis choisissez.
7. Tableau de dépannage rapide
| Phénomène | Cause | Correction |
|---|---|---|
| Aucun texte sur toute la vidéo | Format d'horodatage / Esperluette finale dans la couleur Style | Passez à H:MM:SS.cc ; supprimez le & final dans les couleurs Style |
| Certaines phrases manquent | Horodatage de la phrase erroné ou start>end | Vérifiez phrase par phrase |
| Texte très petit / police incorrecte | Nom de famille de police erroné | Vérifiez avec PIL getname() |
Bloc \p1 décalé vers le haut/bas | Ancrage du dessin + décalage libass de h/2 | Utilisez \an7 + coordonnées absolues en haut à gauche + étalonnage par image test |
| Centre de rotation incorrect | Le centre par défaut n'est pas celui souhaité | Ajoutez \org(x,y) |
| Fondu non effectué / clignotement inversé | Sémantique alpha inversée | &H00 opaque, &HFF transparent |
| Rendu de 30 s n'affiche que les 4 premières phrases | La boucle ne parcourt que les premières phrases | Vérifiez la plage de la boucle de génération d'événements |
| Échec du rendu avec chemin/nom de fichier chinois | Caractères non-ASCII dans les paramètres du filtre | Utilisez un nom de fichier ASCII pour ASS et les vidéos + cwd en chinois |
8. Structure du générateur (Python)
def ts(sec):
h=int(sec//3600); m=int(sec%3600//60)
return "%d:%02d:%05.2f" % (h,m,sec-h*3600-m*60)
class Ass:
def __init__(self,path,w=2560,h=1080): self.ev=[]; self.path=path; self.w=w; self.h=h
def add(self,layer,t0,t1,tags,text): self.ev.append((layer,t0,t1,tags,text))
def write(self):
head = open("template_header.ass",encoding="utf-8").read() # ou intégré dans le code
lines=[]
for layer,t0,t1,tags,text in sorted(self.ev,key=lambda e:(e[0],e[1])):
lines.append("Dialogue: %d,%s,%s,X,,0,0,0,,{%s}%s"%(layer,ts(t0),ts(t1),tags,text))
open(self.path,"w",encoding="utf-8-sig",newline="\n").write(head+"\n".join(lines)+"\n")
Implémentations de référence : sous-titres/build/styles_anim.py (S2/S4/S6), flare_wide.py, neon_wide.py, avoid_side2.py.