Auto-Cut - Détourage mesuré des silences et du superflu
Principes
- Couper AVANT de construire les scènes/légendes. Couper après fausse chaque horodatage
(légendes, zooms, effets sonores). Sortie de cette étape :
assets/<source>.cut.mp4+assets/transcript.cut.json- chaque étape ultérieure utilise la version coupée. - La coupe est OBLIGATOIRE lorsque le brief active autoCut - ce n'est pas une suggestion. Si rien ne peut être coupé, vous devez indiquer une raison concrète (la vidéo était déjà serrée) dans le rapport.
- Les seuils vivent sur le serveur, pas dans votre tête. N'exécutez PAS
silencedetectvous-même et ne choisissez PAS de seuil en dB à l'instinct. Deux faits mesurés ont tué ce workflow :- Le bon seuil est une propriété du FICHIER, pas une constante. Sur un fichier réel, -40dB a trouvé 0 silences, -30dB en a trouvé 13, -25dB en a trouvé 21. Un nombre codé en dur est soit inutile, soit mange la parole.
- La force seule ne peut pas distinguer « pause » de « parler doucement ». Sur le fichier source mesuré,
silencedetectà -30dB a signalé 48,6s de « silence » mais seulement 18,2s se trouvaient dans un vrai trou entre les mots ; les 30,4s restantes étaient à l'intérieur des mots (coupures de syllabes, finales vietnamiennes non voisées c/t/p/ch). Couper uniquement sur le niveau sonore avale la parole. Le serveur effectue les mesures (autoTrim.ts) et la génération des candidats (deadWeight.ts). Vous effectuez la révision. C'est cette séparation qui fait tout l'intérêt.
- Deux types de superflu sont coupés : les silences (mesurés par la machine, protégés par la transcription) et le superflu de contenu (tics de langage, bégaiements, prises reformulées - proposés par le serveur, approuvés par vous ; plus les POINTS répétés, que vous seul pouvez trouver - voir Étape 3).
Étape 0 - Prérequis : une transcription avec horodatages des mots
Toute la protection en dépend. Le serveur cherche assets/transcript.raw.json, puis
assets/transcript.json. Sans cela, l'analyse s'exécute mais revient avec guarded: false, la
liste de superflu est vide, et les chiffres sont des suppositions dans les deux sens (elle peut approuver une
mauvaise coupe et en rejeter une bonne). Transcrire d'abord - ne coupez jamais une tête parlante vietnamienne sans protection.
Étape 1 - Analyser (gratuit, répétable, sans encodage)
POST http://localhost:6869/api/projects/<id>/auto-trim/analyze
body: {} # ou { "source": "assets/face.mp4", "level": "tight" }
level est natural | default | tight et par défaut brief.autoCutLevel. Réponse :
silence-durationSec, lethresholdDbchoisi +thresholdNote(pourquoi ce seuil a gagné),noiseFloorDbmesuré, lessilencesqui seront coupés,keepRanges,removedSec, la tablesweepcomplète, etwordGuard(combien de secondes la transcription a opposé son veto).deadWeight-candidates[]aveckind(filler | stutter | repeat-take | hesitation),start/end,text,confidence,reason,context; plustotalSecetbyKind.guarded-trueseulement si une transcription a été trouvée. Sifalse, corrigez cela avant de couper.
Rien n'est encodé, vous pouvez donc l'appeler aussi souvent que vous le souhaitez (une source de 217s s'analyse en environ une seconde).
Étape 2 - RÉVISER chaque candidat (votre travail, pas celui de la machine)
La liste est déterministe : même transcription, mêmes candidats, même confiance. Ce qu'elle ne peut pas faire, c'est comprendre le sens. Parcourez-les un par un et ne gardez que ceux que vous défendriez :
- Une faible confiance signifie « lisez d'abord le contexte », pas « probablement ça va ». Les tics de langage vietnamiens
entrent presque toujours en collision avec de vrais mots.
đó,ấy,thế,mà,làsont à la fois des particules de remplissage et de vrais démonstratifs/conjonctions. - Les phrases de liaison (
hoặc là,tức là,bởi vì là,với lại là) sont le piège. Mesuré sur une transcription réelle : « …có thể là ok ứng dụng nó / Hoặc là / Tham khảo để tìm cách… » est une véritable alternative - la couper détruit une branche de la phrase. Mais « Còn trường hợp mà mọi người có thể nghe / Hoặc là / Còn trường hợp mà người AI không ứng dụng được… » est une phrase abandonnée et devrait disparaître. La forme de surface est identique ; seul le sens les sépare. C'est exactement pourquoi celles-ci reviennent avec une faible confiance de base et une raison qui dit LISEZ LE CONTEXTE. - Les candidats
repeat-take: vérifiez que la prise ultérieure est vraiment la plus complète avant d'approuver. - Les candidats
hesitationsont du silence pur entre les mots - généralement sûrs, mais vérifiez que vous ne supprimez pas une pause délibérée avant une chute.
Rejetez librement. Un tic de langage laissé coûte 0,4s ; un vrai mot coupé rend la phrase absurde et le spectateur l'entend immédiatement.
Étape 3 - POINTS RÉPÉTÉS (vous seul pouvez les trouver - obligatoire)
Aucun détecteur ne les détecte, et c'est le superflu dont les utilisateurs se plaignent le plus. Lisez la transcription comme un discours et analysez-la sémantiquement :
- Groupez les phrases qui font le MÊME POINT - les mots n'ont pas besoin de correspondre, seulement le contenu. Un orateur reformule souvent un point 2 à 3 fois : d'abord court/hésitant, plus complet ensuite. Considérez aussi les phrases NON adjacentes (fait le point A, digresse, revient au point A plus en détail).
- Gardez EXACTEMENT UNE version par groupe - la PLUS COMPLÈTE :
- Version courte plus tôt, reformulation plus complète plus tard -> gardez la plus tardive, coupez la plus tôt.
- La phrase ultérieure n'est qu'un court écho (« đúng vậy, như tôi nói… ») -> gardez la plus tôt, coupez l'écho.
- Deux versions équivalentes -> gardez la livraison la plus fluide (moins de bégaiements, pas de tics).
- Vérifiez le flux après la coupe : relisez le texte conservé du début à la fin. Si une phrase conservée fait référence (« như vừa nói ») à une phrase que vous avez coupée, gardez les deux ou choisissez la version sans la référence arrière.
- Transformez chaque décision en une plage
{start, end}et envoyez-la avec les candidats approuvés à l'Étape 4. - Construisez un tableau avant de couper (mettez-le dans le rapport) :
horodatage | phrase coupée | raison | phrase conservée- afin que l'utilisateur puisse réviser chaque décision.
Étape 4 - Appliquer (passe par la file d'attente de rendu)
POST http://localhost:6869/api/projects/<id>/auto-trim/apply
body: { "cutCandidates": [{ "start": 51.81, "end": 52.25 }, …] } # UNIQUEMENT ce que vous avez approuvé
-> 202 { job }
Envoyez une liste vide si vous n'avez rien approuvé - les silences mesurés seront quand même coupés. Puis interrogez
GET /api/jobs/<jobId> jusqu'à ce qu'il se termine. Le job :
- Réanalyse avec la protection de la transcription, fusionne les plages de silence avec vos plages approuvées (les chevauchements sont fusionnés, donc rien n'est compté deux fois).
- Supprime toute plage approuvée qui avalerait le milieu d'un mot - un dernier filet de sécurité sur votre révision, et il enregistre chaque plage qu'il rejette. Les plages rejetées dans le journal signifient que vous avez approuvé quelque chose qui se trouve sur de la vraie parole ; revenez en arrière et relisez cet endroit.
- Coupe en UN passage ffmpeg (
trim/atrim + setpts/asetpts + concat), en écrivantassets/<stem>.cut.mp4. - Remappe chaque horodatage dans la timeline coupée ->
assets/transcript.cut.json. - Vérifie la SORTIE avec les mots remappés et écrit
assets/auto-trim-report.json.
Si rien ne vaut la peine d'être coupé, le job ne produit délibérément PAS de .cut.mp4 (une copie identique
ré-encodée ne fait que perdre en qualité) - output dans le rapport est null et vous continuez à utiliser la source.
Étape 5 - Lire le rapport (le travail n'est pas terminé tant que vous ne l'avez pas fait)
assets/auto-trim-report.json contient : durée avant/après, removed.silenceSec vs
removed.approvedSec, le seuil choisi et pourquoi, les candidats rejetés, et verification.
verdict: "pass"- le résultat correspond au profil pour ce niveau.verdict: "fail"- le travail s'est quand même terminé et le fichier est utilisable, mais il n'a PAS atteint le profil. Le journal le dit explicitement. Approuvez plus de superflu (Étape 2/3) et réexécutez apply, ou indiquez dans le rapport final pourquoi vous l'acceptez. Ne signalez jamais la coupe comme terminée en ignorant un échec.
Les critères de réussite ne sont pas l'ancienne règle « pas de silence > 0,8s » - les mesures ont montré que cette règle est bien trop
laxiste. Un fichier réel l'a réussie tout en portant 13 silences résiduels de 0,45 à 0,67s totalisant 6,7s (4,2 %
du temps d'exécution), ce qui sonne clairement comme un boulet. Les profils limitent désormais À LA FOIS le silence unique le plus long
ET le rapport total (default: 0,5 s / 3 %).
Étape 6 - Utilisez la version coupée partout
À partir de là, les légendes, la disposition des clés, le timing des effets sonores et les zooms lisent assets/transcript.cut.json et le
.cut.mp4. La vérification de contrôle qualité dead-air remesure la vidéo assemblée par rapport à la transcription et ÉCHOUE
lorsque brief.autoCut est activé et que le silence mort dépasse encore le profil - c'est le filet de sécurité, pas le
plan.
Le rapport final doit inclure : le tableau de coupe de l'Étape 3, les secondes supprimées divisées en silence vs superflu
approuvé, et le verdict de vérification. Prenez les chiffres de auto-trim-report.json.
Problèmes connus
- Utiliser l'ANCIENNE transcription après la coupe -> les légendes dérivent hors de synchronisation vers la fin.
Passez toujours à
transcript.cut.json. - Couper uniquement les silences et ignorer les points répétés - ce que les utilisateurs appellent « superflu » est pour la plupart l'Étape 3.
- Couper la version rendue au lieu de la source -> la qualité chute à cause d'un second encodage. Coupez toujours
à partir de la source (le serveur refuse d'écraser la source et ignore les fichiers déjà nommés
*.cut.*lors du choix d'une valeur par défaut). - Analyser un
.cut.mp4alors que la seule transcription est la brute -> la protection est alignée sur la mauvaise timeline. Faites correspondre le fichier source à la transcription qui le décrit. - Fichiers d'effets sonores/narration avec leur propre silence de tête -> utilisez
data-media-startpour couper à l'intérieur des HyperFrames (voir « SFX loudness and lead silence » dans la compétence noti-tiktok-vn), ne ré-encodez pas un fichier audio juste pour 0,3s de silence de tête. - Joindre des segments coupés sans fondu -> un clic à chaque jointure. Corrigé dans
autoTrim.ts; la règle est ci-dessous, et elle s'applique à tout nouveau code qui coupe ou concatène de l'audio.
Audio : fondu de 30 ms à chaque bord de coupe (règle stricte)
Une coupe atterrit au milieu d'une forme d'onde. Le dernier échantillon d'un segment et le premier échantillon du suivant sont sans rapport, donc la jointure est une marche verticale, et une marche est un clic. La cause n'est pas l'encodeur et pas la source - c'est la jointure elle-même, donc elle survit à tout ré-encodage en aval.
Combien cela compte dépend de LÀ OÙ atterrit la coupe, et la différence est grande. Mesuré sans perte sur une véritable tête parlante de 278s, en comparant la même coupe avec et sans le fondu (plus grand pas d'échantillon à échantillon à la jointure ; tout ce qui dépasse ~0,02 est audible) :
| Type de jointure | Avant | Après |
|---|---|---|
Détourage des silences, 40 jointures (balanced) | médiane 0,0040, pire 0,0113 - aucune audible | médiane 0,0000 |
| En cours de parole, 4 jointures (superflu / points répétés) | 0,1195, 0,0744, 0,0232, 0,0064 - 3 sur 4 audibles | tous ≤ 0,0008 |
Pour donner une échelle, le pas le plus fort n'importe où dans la parole normale de ces fichiers était de 0,06 à 0,13. Donc une jointure en cours de parole sans fondu peut être un saut plus important que tout ce que le contenu lui-même produit, tandis qu'une jointure de silence n'est nulle part audible.
Ne sautez pas le fondu en vous basant sur cette première ligne. Le détourage de silence pur est le cas où cela ne compte pas par hasard ; l'Étape 3 (points répétés) et l'extraction de clips coupent directement à travers la parole, et ce sont ces jointures qui cliquent.
Correction : fondez les deux bords de chaque segment sur 30 ms. Assez long pour tuer le pas, bien trop court pour être entendu
comme un fondu. Utilisez audioCutFade() dans apps/server/src/util.ts ; ne bricolez pas le filtre.
[0:a]atrim=start=S:end=E,asetpts=PTS-STARTPTS,afade=t=in:st=0:d=0.030,afade=t=out:st=D-0.030:d=0.030[aN]
Trois choses qui le font silencieusement ne rien faire :
- Ordre.
afadedoit venir APRÈSasetpts=PTS-STARTPTS.afadelitstsur l'horloge du flux lui-même ; sur les horodatages d'origine,st=0est dans le passé et le fondu entrant ne se déclenche jamais. - Longueur.
Dest la durée du segment, donc le début du fondu sortant estD - 0.030. Limitez le fondu àD/2ou les deux fondus se chevauchent sur un segment court et l'engloutissent. - Extraction d'un clip, pas seulement une jointure. Un clip extrait d'une vidéo plus longue a les deux mêmes bords bruts
même si rien n'est joint -
reframe.tsles fondus via-af.
Une note sur la façon de mesurer cela vous-même : ne comparez pas deux encodages AAC. Le ré-encodage perturbe les échantillons
partout, ce qui a enterré les 40 vraies jointures parmi 294 régions de différences factices et a mis un décalage
54 ms entre les deux fichiers. Rendez plutôt les deux variantes directement en pcm_s16le - alors les deux sont
alignés sur les échantillons et les jointures se situent exactement aux longueurs de segment cumulées.