Auto-Cut – gemessene Stille & Füllmaterial-Trimmen
Grundsätze
- Schneide VOR dem Erstellen von Szenen/Untertiteln. Ein Schnitt danach wirft alle Zeitstempel
(Untertitel, Zooms, SFX) durcheinander. Ausgabe dieses Schritts:
assets/<quelle>.cut.mp4+assets/transcript.cut.json– jeder spätere Schritt verwendet die geschnittene Version. - Schneiden ist ZWINGEND, wenn der Brief
autoCutaktiviert – es ist kein Vorschlag. Wenn nichts geschnitten werden kann, musst du im Bericht einen konkreten Grund nennen (das Video war bereits straff). - Die Schwellwerte liegen auf dem Server, nicht in deinem Kopf. Führe NICHT selbst
silencedetectaus und wähle KEINEN dB-Schwellwert nach Gefühl. Zwei gemessene Fakten haben diesen Workflow widerlegt:- Der richtige Schwellwert ist eine Eigenschaft der DATEI, keine Konstante. Bei einer echten Datei fand -40dB 0 Stille, -30dB 13, -25dB 21. Eine fest codierte Zahl ist entweder nutzlos oder verschluckt Sprache.
- Lautstärke allein kann nicht zwischen „Pause“ und „leises Sprechen“ unterscheiden. Bei der gemessenen
Quelldatei meldete
silencedetectbei -30dB 48,6s „Stille“, aber nur 18,2s davon lagen in einer echten Lücke zwischen Wörtern; die anderen 30,4s befanden sich in Wörtern (Silbengrenzen, stimmlose vietnamesische Endungen c/t/p/ch). Das Schneiden allein anhand der Lautstärke verschluckt Sprache. Der Server übernimmt die Messung (autoTrim.ts) und die Kandidatengenerierung (deadWeight.ts). Du übernimmst die Überprüfung. Diese Aufteilung ist der Kern.
- Zwei Arten von Füllmaterial werden geschnitten: Stille (maschinell gemessen, durch das Transkript abgesichert) und inhaltliches Füllmaterial (Füllwörter, Stottern, wiederholte Takes – vom Server vorgeschlagen, von dir freigegeben; plus wiederholte AUSSAGEN, die nur du finden kannst – siehe Schritt 3).
Schritt 0 – Voraussetzung: ein Transkript mit Wort-Zeitstempeln
Die gesamte Absicherung hängt davon ab. Der Server sucht nach assets/transcript.raw.json, dann
assets/transcript.json. Ohne eines läuft die Analyse zwar, kommt aber mit guarded: false zurück, die
Füllmaterial-Liste ist leer und die Zahlen sind in beiden Richtungen Schätzwerte (er kann einen schlechten
Schnitt durchlassen und einen guten ablehnen). Transkribiere zuerst – schneide niemals einen vietnamesischen
Sprecher ohne Absicherung.
Schritt 1 – Analysieren (kostenlos, wiederholbar, ohne Kodierung)
POST http://localhost:6869/api/projects/<id>/auto-trim/analyze
body: {} # oder { "source": "assets/face.mp4", "level": "tight" }
level ist natural | default | tight und standardmäßig auf brief.autoCutLevel gesetzt. Antwort:
silence–durationSec, der gewähltethresholdDb+thresholdNote(warum dieser Schwellwert gewonnen hat), gemessenernoiseFloorDb, diesilencesdie geschnitten werden,keepRanges,removedSec, die vollständigesweep-Tabelle undwordGuard(wie viele Sekunden das Transkript blockiert hat).deadWeight–candidates[]mitkind(filler | stutter | repeat-take | hesitation),start/end,text,confidence,reason,context; plustotalSecundbyKind.guarded–truenur, wenn ein Transkript gefunden wurde. Wennfalse, behebe das vor dem Schneiden.
Es wird nichts kodiert, du kannst es also so oft aufrufen, wie du möchtest (eine 217s-Quelle analysiert in etwa einer Sekunde).
Schritt 2 – überprüfe JEDEN Kandidaten (deine Aufgabe, nicht die der Maschine)
Die Liste ist deterministisch: gleiches Transkript, gleiche Kandidaten, gleiche Konfidenz. Was sie nicht kann, ist Bedeutung zu verstehen. Gehe sie einzeln durch und behalte nur die, die du verteidigen würdest:
- Niedrige Konfidenz bedeutet „zuerst den Kontext lesen“, nicht „wahrscheinlich in Ordnung“. Vietnamesische Füllwörter
überschneiden sich fast immer mit echten Wörtern.
đó,ấy,thế,mà,làsind sowohl Füllpartikel als auch echte Demonstrativpronomen/Konjunktionen. - Verbindungssätze (
hoặc là,tức là,bởi vì là,với lại là) sind die Falle. Gemessen an einem echten Transkript: „…có thể là ok ứng dụng nó / Hoặc là / Tham khảo để tìm cách…“ ist eine echte Alternative – das Wegschneiden zerstört einen Zweig des Satzes. Aber „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…“ ist ein abgebrochener Satz und sollte entfernt werden. Die Oberflächenform ist identisch; nur die Bedeutung trennt sie. Genau deshalb kommen diese mit einer niedrigen Basis-Konfidenz und einem Grund, der sagt: LIES DEN KONTEXT. repeat-take-Kandidaten: Überprüfe, ob der spätere Take wirklich der vollständigere ist, bevor du ihn freigibst.hesitation-Kandidaten sind reine Stille zwischen Wörtern – normalerweise sicher, aber überprüfe, ob du nicht eine bewusste Pause vor einem Höhepunkt entfernst.
Lehne großzügig ab. Ein zurückgelassenes Füllwort kostet 0,4s; ein geschnittenes echtes Wort macht den Satz unsinnig und der Zuschauer hört es sofort.
Schritt 3 – WIEDERHOLTE AUSSAGEN (nur du kannst sie finden – zwingend)
Kein Detektor erfasst dies, und es ist das Füllmaterial, über das sich Benutzer am meisten beschweren. Lies das Transkript als gesprochene Rede und analysiere es semantisch:
- Gruppiere Sätze, die die GLEICHE AUSSAGE machen – die Wörter müssen nicht übereinstimmen, nur der Inhalt. Ein Sprecher wiederholt eine Aussage oft 2-3 Mal: zuerst kurz/stolpernd, später vollständiger. Berücksichtige auch NICHT-benachbarte Sätze (macht Aussage A, schweift ab, kommt zu Aussage A detaillierter zurück).
- Behalte genau EINE Version pro Gruppe – die VOLLSTÄNDIGSTE:
- Frühere kurze Version, spätere vollständigere Wiederholung -> behalte die spätere, schneide die frühere.
- Späterer Satz ist nur ein kurzes Echo („đúng vậy, như tôi nói…“) -> behalte die frühere, schneide das Echo.
- Zwei gleichwertige Versionen -> behalte die flüssigere Lieferung (weniger Stolperer, keine Füllwörter).
- Überprüfe den Fluss nach dem Schneiden: lies den erhaltenen Text von Anfang bis Ende. Wenn ein erhaltener Satz sich auf einen geschnittenen bezieht („như vừa nói“), behalte entweder beide oder wähle die Version ohne den Rückverweis.
- Wandle jede Entscheidung in einen Bereich
{start, end}um und sende ihn mit den genehmigten Kandidaten in Schritt 4. - Erstelle eine Tabelle vor dem Schneiden (füge sie in den Bericht ein):
Zeitstempel | geschnittener Satz | Grund | erhaltener Satz– damit der Benutzer jede Entscheidung überprüfen kann.
Schritt 4 – Anwenden (geht durch die Render-Warteschlange)
POST http://localhost:6869/api/projects/<id>/auto-trim/apply
body: { "cutCandidates": [{ "start": 51.81, "end": 52.25 }, …] } # NUR was du freigegeben hast
-> 202 { job }
Sende eine leere Liste, wenn du nichts freigegeben hast – die gemessenen Stille werden trotzdem geschnitten. Rufe dann
GET /api/jobs/<jobId> auf, bis es abgeschlossen ist. Der Job:
- Analysiert erneut mit der Transkript-Absicherung, führt die Stillebereiche mit deinen freigegebenen Bereichen zusammen (Überlappungen werden zusammengeführt, sodass nichts doppelt gezählt wird).
- Verwirft jeden freigegebenen Bereich, der einen Wortmittelpunkt verschlucken würde – ein letztes Sicherheitsnetz über deiner Überprüfung, und es protokolliert jeden Bereich, den es ablehnt. Abgelehnte Bereiche im Log bedeuten, dass du etwas freigegeben hast, das auf echter Sprache sitzt; gehe zurück und lies diese Stelle erneut.
- Schneidet in EINEM ffmpeg-Durchlauf (
trim/atrim + setpts/asetpts + concat), schreibtassets/<stamm>.cut.mp4. - Ordnet jeden Zeitstempel in die geschnittene Zeitleiste neu zu ->
assets/transcript.cut.json. - Überprüft die AUSGABE mit den neu zugeordneten Wörtern und schreibt
assets/auto-trim-report.json.
Wenn nichts zu schneiden ist, erstellt der Job bewusst KEINE .cut.mp4 (eine neu kodierte identische Kopie würde nur an
Qualität verlieren) – output im Bericht ist null und du verwendest weiterhin die Quelle.
Schritt 5 – Lies den Bericht (der Job ist erst erledigt, wenn du es tust)
assets/auto-trim-report.json enthält: Dauer vorher/nachher, removed.silenceSec vs.
removed.approvedSec, den gewählten Schwellwert und warum, abgelehnte Kandidaten und verification.
verdict: "pass"– das Ergebnis entspricht dem Profil für diese Stufe.verdict: "fail"– der Job wurde trotzdem abgeschlossen und die Datei ist nutzbar, hat aber NICHT das Profil erfüllt. Das Log sagt dies explizit. Gib mehr Füllmaterial frei (Schritt 2/3) und führe „apply“ erneut aus, oder gib im endgültigen Bericht an, warum du es akzeptierst. Melde den Schnitt niemals als erledigt, während du einen Fehler ignorierst.
Die Bestehenskriterien sind nicht die alte Regel „keine Stille > 0,8s“ – Messungen haben gezeigt, dass diese Regel viel zu
lasch ist. Eine echte Datei bestand sie, während sie 13 verbleibende Stille von 0,45–0,67s mit insgesamt 6,7s (4,2 % der
Laufzeit) enthielt, was offensichtlich träge klingt. Die Profile begrenzen jetzt sowohl die längste einzelne Stille als
auch das Gesamtverhältnis (default: 0,5s / 3 %).
Schritt 6 – Verwende die geschnittene Version überall
Ab hier lesen Untertitel, Tasten-Layout, SFX-Timing und Zooms assets/transcript.cut.json und die
.cut.mp4. Die QC-Prüfung dead-air misst das zusammengebaute Video erneut am Transkript und SCHLÄGT FEHL, wenn
brief.autoCut aktiv ist und die tote Luft immer noch über dem Profil liegt – das ist die Absicherung, nicht der Plan.
Der abschließende Bericht muss enthalten: die Schnitttabelle aus Schritt 3, die entfernten Sekunden aufgeteilt in Stille
vs. freigegebenes Füllmaterial und das Verifizierungsurteil. Nimm die Zahlen aus auto-trim-report.json.
Bekannte Probleme
- Verwenden des ALTEN Transkripts nach dem Schneiden -> Untertitel driften gegen Ende immer weiter aus dem Takt.
Wechsle immer zu
transcript.cut.json. - Nur Stille schneiden und wiederholte Aussagen ignorieren – das, was Benutzer „Füllmaterial“ nennen, ist meist Schritt 3.
- Den gerenderten Entwurf anstelle der Quelle schneiden -> Qualitätsverlust durch eine zweite Kodierung. Schneide immer
von der Quelle (der Server weigert sich, die Quelle zu überschreiben, und überspringt Dateien, die bereits
*.cut.*heißen, wenn er einen Standard wählt). - Analysieren einer
.cut.mp4, während das einzige Transkript das rohe ist -> die Absicherung ist auf die falsche Zeitleiste ausgerichtet. Gleiche die Quelldatei mit dem Transkript ab, das sie beschreibt. - SFX-/Erzähldateien mit eigener Vorlaufstille -> verwende
data-media-start, um innerhalb von HyperFrames zu trimmen (siehe „SFX-Lautstärke und Vorlaufstille“ im Noti-TikTok-VN-Skill), kodiere eine Audiodatei nicht nur für 0,3s Vorlaufstille neu. - Zusammenfügen von geschnittenen Segmenten ohne Überblendung -> ein Klick an jeder Nahtstelle. Behoben in
autoTrim.ts; die Regel steht unten und gilt für jeden neuen Code, der Audio schneidet oder verkettet.
Audio: 30ms-Überblendung an jeder Schnittkante (harte Regel)
Ein Schnitt landet mitten in der Wellenform. Das letzte Sample eines Segments und das erste Sample des nächsten sind unverwandt, daher ist die Nahtstelle ein vertikaler Sprung, und ein Sprung ist ein Klick. Die Ursache ist weder der Encoder noch die Quelle – es ist die Nahtstelle selbst, daher überlebt sie jede nachgelagerte Neukodierung.
Wie sehr es darauf ankommt, hängt davon ab, WO der Schnitt landet, und der Unterschied ist groß. Gemessen verlustfrei an einer echten 278s-Sprecher-Aufnahme, Vergleich desselben Schnitts mit und ohne Überblendung (größter Sample-zu-Sample-Sprung an der Nahtstelle; alles über ~0,02 ist hörbar):
| Art der Nahtstelle | Vorher | Nachher |
|---|---|---|
Stille-Trimmen, 40 Nahtstellen (balanced) | Median 0,0040, schlechteste 0,0113 – keine hörbar | Median 0,0000 |
| Mitten im Sprechen, 4 Nahtstellen (Füllmaterial / wiederholte Aussagen) | 0,1195, 0,0744, 0,0232, 0,0064 – 3 von 4 hörbar | alle ≤ 0,0008 |
Zum Vergleich: Der lauteste Sprung irgendwo in normaler Sprache in diesen Dateien war 0,06–0,13. Ein Sprung mitten im Sprechen ohne Überblendung kann also ein größerer Sprung sein als alles, was der Inhalt selbst erzeugt, während ein Stille-Sprung bei weitem nicht hörbar ist.
Überspringe die Überblendung nicht aufgrund der ersten Zeile. Reines Stille-Trimmen ist der Fall, in dem es zufällig nicht darauf ankommt; Schritt 3 (wiederholte Aussagen) und Clip-Extraktion schneiden direkt durch Sprache, und diese Nahtstellen klicken.
Behebung: Überblende beide Kanten jedes Segments über 30ms. Lang genug, um den Sprung zu beseitigen, viel zu kurz, um
al s Überblendung hörbar zu sein. Verwende audioCutFade() in apps/server/src/util.ts; baue den Filter nicht selbst.
[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]
Drei Dinge, die es stillschweigend unwirksam machen:
- Reihenfolge.
afademuss NACHasetpts=PTS-STARTPTSkommen.afadelieststvon der eigenen Uhr des Streams; bei ursprünglichen Zeitstempeln liegtst=0in der Vergangenheit und die Einblendung wird nie ausgelöst. - Länge.
Dist die Segmentdauer, also ist der Start der AusblendungD - 0.030. Begrenze die Ausblendung aufD/2, da sich sonst die beiden Überblendungen bei einem kurzen Segment überschneiden und es verschlucken. - Extrahieren eines Clips, nicht nur Zusammenfügen. Ein Clip, der aus einem längeren Video ausgeschnitten wird, hat
dieselben beiden rohen Kanten, auch wenn nichts zusammengefügt wird –
reframe.tsblendet sie über-afaus.
Ein Hinweis zum Selbstmessen: Vergleiche nicht zwei AAC-Kodierungen. Die Neukodierung verändert Samples überall, was
die 40 echten Nahtstellen unter 294 zufälligen Differenzbereichen begrub und einen 54ms-Offset zwischen den beiden
Dateien erzeugte. Rendere stattdessen beide Varianten direkt nach pcm_s16le – dann sind die beiden sample-ausgerichtet
und die Nahtstellen sitzen genau an den kumulativen Segmentlängen.