Videoproduktion mit Code
Behandle ein Video als eine reine Funktion render(t) plus eine gestufte, überprüfbare Pipeline. Fast jede Fehlerart in dieser Aufgabenklasse ist ein Einheiten-/Vorzeichen-/Kanal-/Metrikfehler, der stillschweigend ausgeliefert wird – daher ist der folgende Workflow darauf ausgelegt, jeden einzelnen beobachtbar zu machen.
0. Friere den Vertrag ein, bevor du etwas zeichnest
- Eine Zeitquelle. Alles lebt innerhalb von
render(t); die Frame↔Zeit-Zuordnung (t = frame/fps) ist die gesamte öffentliche API. Keine CSS-Animation, keine Timer, kein Frame-zu-Frame-Zustand. Ergebnis: Frames sind parallelisierbar, fortsetzbar, vergleichbar, und ein Fehler ist auf einen Zeitbereich eingrenzbar. - Determinismus. Gesäter PRNG (
hash(i)/mulberry32), niemalsMath.random; niemalsDate.now; bette Schriftarten und Texturen als Data-URIs ein, sodass kein Netzwerk-/Schriftarten-Fallback Pixel mitten im Lauf ändern kann. - Schreibe die Spezifikation als Behauptungen, nicht als Prosa. Dauer, fps, Auflösung, Codec, Pixelformat, Lautstärke, True Peak, Sicherheitsbereich/Ränder, verbotene Techniken, Dateipfade, CLI-Flags, Exit-Codes, JSON-Ausgabeform. Wenn eine Zahl in der Aufgabenbeschreibung steht, muss sie in einer Prüfung landen.
- Entscheide die Naht-Richtlinie im Voraus – Einzelaufnahme / Schleife / verkettete Segmente. Für eine Schleife: jeder Oszillator ist
sin(2π·k·t/D)mit ganzzahligemk; der Film konvergiert zurück zu seinem eigenen ersten Zustand (nicht „sieht ähnlich aus“); Event-Tabellen und Bildschirmzähler/-beschriftungen werden innerhalb des finalen Übergangs zurückgesetzt; überprüferender(0)vsrender(D)-Rendervorgänge. - Budget pro Frame. Messe
render(t), bevor du Inhalte erstellst (ein 1080p-Canvas-Zeichenvorgang dauert 2–30 ms; 3 Subframes × 5400 Frames multipliziert das). Notiere das Budget und halte es ein. - Friere das Interface ein, dann hashe es. Canvas-Größe, Seiten-/API-Oberfläche, Worker-CLI, Ausgabepfade. Die gesamte Qualitätskontrolle ist an diesen Revisions-Hash gebunden – ein gegen einen veralteten Hash qualitätsgeprüfter Render ist wertlos.
- Protokolliere bewusste Abweichungen. Jede Stelle, an der die Realität von der Aufgabenbeschreibung abweicht (ein Gitterwackeln bei „sauberen“ Linien, Mikro-Typografie kleiner als das angegebene Minimum, eine Federrate außerhalb des angegebenen Bereichs) wird in einer kurzen Liste mit einem Grund festgehalten, sodass ein Prüfer urteilen und nicht entdecken kann.
1. Drei Sonden am ersten Tag
| Sonde | Behauptung | Warum |
|---|---|---|
| Aufnahmeintegrität | exakte Pixel, Alpha == 255, Farbbereich, erster/letzter Frame vorhanden | Transluzente Akkumulation, Bereichs-/Farbraumfehler ruinieren stillschweigend den ganzen Film |
| Kosten | render(t) ms-Verteilung pro Szene, Speicher | Architekturänderungen sind jetzt billig, später teuer |
| Bewegung | frameübergreifende Differenzreihe über die gesamte Dauer + längster eingefrorener Lauf | Zeigt die Totzeitbereiche sofort an |
Regeln, die aus echten Fehlern entstanden sind:
- Die Weiß-Komposit-Falle. Die Überprüfung von Frames durch einen Viewer, der Alpha auf Weiß legt, verbirgt Transluzenz; die Codierung legt auf Schwarz. Stelle sicher, dass der Überprüfungspfad genau wie der Auslieferungspfad kompositiert, und bewerte ein Pixel zwischen beiden.
- Wissen, was eine Metrik sehen kann. Nach einer 12-fachen Verkleinerung ändert die Bewegung eines dünnen Strichs im Subpixel-Bereich die mittlere absolute Differenz um ~0.03/255, während eine Änderung der Gesamthelligkeit sie um ~1.5/255 verschiebt. Wenn die Akzeptanzmetrik ein herunterskalierter Mittelwert ist, ist der billigste wirkliche Hebel ein globaler Wertekanal (z. B. ein 2 Hz ±3 % Leuchtkraftpuls im Takt), nicht stärkeres Wackeln.
- Überprüfe die Metrik vor dem Asset. Wenn eine Prüfung fehlschlägt, gib zuerst das rohe Signal aus (Hüllkurve bei 1 ms, Pixelzeilenprofil, Bildserie). Die Hälfte der Fälle von „das Asset ist falsch“ stellt sich als der Detektor heraus.
- Mache Detektoren selbstbeschreibend. Modelliere und kompensiere systematische Verzerrungen (Filtergruppenverzögerung, kausale Analyse-Fensterverzögerung); bevorzuge den Kandidaten am nächsten zum Ereignis vor dem lautesten; stimme niemals einen Schwellenwert, bis er besteht.
- Exakte vs. ungefähre Behauptungen. „render(0) ≡ render(D), PSNR ∞“ ist ein Beweis; „Erster vs. letzter codierter Frame unterscheiden sich um 2/255 (ein Frame Bewegung)“ ist eine Beobachtung. Verwische die beiden niemals in einem Bericht.
Mitgeliefert: scripts/av_audit.mjs implementiert die Container-/Bewegungs-/Schleifen-/Lautstärke-/Bogen-Hälfte davon (nur ffmpeg, keine npm-Abhängigkeiten).
2. Der Überprüfungszyklus ist ein Feature, kein Overhead
- Rastere es: Hunderte von Standbildern → ein Dutzend Kontaktbögen, auf einmal überprüft.
- Backe Hilfslinien ein: Sicherheitsbereich-Box, Zentrum-Kreuz, Zeitcode, Ereignis-ID. Der Prüfer sollte das Layout nicht im Kopf behalten müssen.
- Taste nach Ereignis ab, nicht nach festem Intervall: einen Frame bei jedem beabsichtigten Ereignis und einen in der Mitte einer Bewegung.
- Schaue am genauesten an den Nähten: Szenenübergänge, Schleifen-Wrap, Segmentgrenzen, erster/letzter Frame.
- Isoliere und zoome ein verdächtiges Element auf eine leere Leinwand bei 4–6×; die meisten Fehler sind im Vollbild unsichtbar.
- Artefakte sind wegwerfbar: ein Befehl regeneriert sie; flicke niemals von Hand einen Überprüfungs-Frame.
- Führe eine Problemliste (überladen / außerhalb des Takts / überlappend / außerhalb des Sicherheitsbereichs / unlesbar / eingefroren) und überprüfe nach jeder Fehlerbehebungsrunde erneut.
3. Bewegungs- und Schleifen-Handwerk
- Umgebungs- ≠ Aktion. Jede Szene benötigt einen kontinuierlichen Kanal mit einer anderen Frequenz als die Aktion (Drift, Welle, Leerlaufdrehung, Wertimpuls) mit Nulldurchgängen, die auf den Takt ausgerichtet sind, sodass Aktionen immer noch sauber landen. Dies erfüllt „nichts ist jemals eingefroren“.
- Eine Easing-Familie. Kritisches gedämpfte Feder
1-(1+x)e^-x(überschwingt nie) für Bewegungen; exponentielle Ease-Out/In für Wischbewegungen, Drücke, Ausgänge. Lasse die Landung mit dem Ereignis zusammenfallen: Start = Ereignis − Dauer, und normalisiere, sodass der Fortschritt genau beim Ereignis 1 ist. - Bewegungsunschärfe an zwei Stellen: innerhalb der Frame-Funktion (2–4 Verschluss-Samples gemittelt mit einem gleitenden Mittelwert –
alpha = 1/(k+1)– der vollständig undurchsichtig endet), dichter um Ereignisse; plus einer zeitlichen Mischung auf Codiererseite. Beweise die Filterfenster-Semantik mit einer indexcodierten Testumgebung (führe Frames zu, deren Pixel die Framenummer codieren, und lese zurück, welche Indizes gemittelt wurden), niemals aus der Dokumentation. - Kontinuität durch Konstruktion. Führe ein Geometrie-/Zustandsmodell über Szenen hinweg und transformiere es (Morph, Materialtausch, harte Kante-Wisch-Flut, Scan-Linie), anstatt zu schneiden oder aufzulösen.
- Längenerhaltende Morphs: parametrisiere Formen nach Winkel/Bogenlänge, sodass Mischungen wachsen statt skalieren.
- Standardmäßig verbotene Liste (lösche, es sei denn, es wird angefordert): Partikel, Glüheffekte, Linsenfackeln, Kamera-Wackeln, RGB-Split, Überblendungen, generische Verläufe, hüpfende Überschwinger.
4. Rendern und Codieren
- Format vs. Ziel: messe zuerst den Durchsatz. Verlustfreie PNG-Frames sind mehrere MB groß und belasten die Leitung; hochwertiges JPEG ist ~1,5× schneller. Entscheide einmal, dann behalte es bei.
- Pipe, nicht zwischenspeichern: sende Frames an ffmpeg stdin (
-f image2pipe -framerate <fps*sub> -vcodec png -i -). Mische mittmix, wähle den letzten Frame der Gruppe, passe die Zeit an, trimme mit-frames:v <n>. - Parallelisiere nach Kosten, nicht nach Framecount. Messe ms pro Szene, unterteile dann in Blöcke von ungefähr gleicher Zeit; gleichmäßige Framecounts lassen den langsamsten Worker die Wanduhr bestimmen.
- Segmente müssen Concat-sicher sein: wiederhole 2 Grenzframes an jedem Ende, identische Codierparameter und Zeitbasis, überprüfe Frames/Dauer mit ffprobe, dann
-c copyconcat. - Stufe die Pipeline so, dass ein Fehler nur eine Stufe kostet: Rendern → Mischen → Codieren → Audio → Muxen → Messen; jede Stufe schreibt eine Datei.
- Behalte einen verlustfreien Video-Master zwischen den Stufen (
-c copyüberall) und führe die Lautstärkebearbeitung zuletzt durch. - Prüfe eine unbekannte Toolchain, bevor du dich darauf verlässt: Filter-/Codiererlisten, Browser-Ausführbarkeitsdateien, Versionen; erlaube
$FFMPEG/$FFPROBE-Überschreibungen; bevorzuge fest installierte lokale Binärdateien gegenüber PATH-Schätzungen. Ein reduziertes „Media“-ffmpeg, das mit einem Browser ausgeliefert wird, hat meistens keinx264/tmix/loudnorm. - Erfasse Kind-Prozess-Ausgaben über Dateideskriptoren, nicht über Pipes. In einer eingeschränkten Sandbox können
spawn/execSyncmitstdio: 'pipe'mitEPERMfehlschlagen, während dieselbe Binärdatei von einer Shell aus einwandfrei läuft. Übergebe stattdessen offene Dateihandles:spawnSync(cmd, a, { stdio: ['ignore', fdOut, fdErr] }), lese dann die Dateien – und verwendestdio: 'ignore'für reine Existenz-/Versionsprüfungen. Leite stdout und stderr zu separaten Dateien um, sodass binäre Framedaten nicht mit Log-Text vermischt werden. - Beende mit einem Ende-zu-Ende-Neubau aus der Quelle, sodass die ausgelieferte Datei kein glücklicher Cache ist.
5. Audio und Muxen
- Gleiche Zeitbasis und gleicher Determinismus wie das Bild (Offline-Render, gesät, keine Echtzeitabhängigkeit).
- Eine Cue-Tabelle (
{t, name}) als einzige Quelle der Wahrheit für jedes hörbare/sichtbare Ereignis; SFX liegen auf einem eigenen Bus, leiser als Musik. - Normalisiere mit Zwei-Pass-Loudnorm (
print_format=json→ measured_* → zweiter Durchlauf mitlinear=true), dann überprüfe mit einer unabhängigen dritten Messung; behaupte integrierte Lautstärke innerhalb ±0,5 LU und True Peak ≤ Ziel. - Schleifen: blende das Ende (~40 ms) auf exakt Null aus, erstes Sample nicht klickend; messe die Ausrichtung von perkussiven Schlägen auf das Raster und melde sie.
- Muxe
-c:v copy -c:a aac -b:a <spec>, dann ffprobe den Container als Behauptung, nicht als Blick. -b:a Nkist ein Ziel, kein Minimum für ffmegs nativen AAC-Codierer (gemessene ~180–250 kbps gegen eine 320k-Anfrage bei ruhigem Material;-aac_coder twoloophilft). Lies die deklariertebit_ratezurück und melde den gemessenen Durchschnitt, anstatt die angeforderte Zahl zu wiederholen, und sage dies explizit, wenn die Spezifikation eine Bitrate verlangt.
Mitgeliefert: scripts/mux_normalize.mjs.
6. Halte die Zeichenebene billig und deterministisch
- Cache alles Zeitinvariante: eingebackene Hintergründe, Textmasken, Muster, Path2D. Cache-Schlüssel enthalten jeden Parameter, der Pixel ändern kann.
- Canvas-Pools müssen von den Quell-Canvas-Abmessungen dimensioniert werden, nicht von der logischen Inhaltsgröße – andernfalls dehnt oder beschneidet
drawImagestillschweigend (dies führte zu abgeschnittenen/verzerrten Schrifttypen). - Vermeide
shadowBlur/filterin der Frame-Schleife; bevorzuge Geometrie. - Speicher: ein 1080p-Canvas ist ~8 MB; begrenze den Pool.
- Denke daran, dass die Anzahl der Subframes das Framebudget linear vervielfacht.
7. Zusammenarbeit
- Teile Autor vs. Prüfer. Der Prüfer besitzt die QC-Skripte und die Autorität, das auslieferbare Produkt durchfallen zu lassen, und prüft unabhängig Dimensionen, die der Autor nicht geprüft hat (Alpha, Wiedergabetreue vs. Seite, Lautstärke, Schleifenschluss). Dies fängt die Klasse von Fehlern, die der Autor nicht sieht.
- Disjunkte Schreibbereiche + ein eingefrorenes Interface, sodass parallele Arbeit nicht kollidieren kann; hashe die Revision und führe QC nach jeder Änderung erneut aus.
- Scheitere laut: Nicht-Null-Exit-Codes, eine
PASS/FAIL-Zeile pro Behauptung, JSON für alles Maschinenlesbare, keine stillen Fallbacks.
8. Anti-Patterns
- Farbe durch ein weiß-kompositiertes Überprüfungsbild beurteilen.
- Das Asset bearbeiten, um eine Metrik zu erfüllen, die du nie charakterisiert hast.
- Schwellenwert eines Detektors einstellen, anstatt sein Modell zu reparieren.
- Den gesamten Render neu starten, um eine Frage zu beantworten, die eine 20-Sekunden-Seiten-Sonde beantwortet.
- Gleichmäßige Frame-Anzahl-Aufteilung über Szenen mit sehr unterschiedlichen Kosten.
- Ein Überprüfungspfad, der sich vom Auslieferungspfad unterscheidet (Maßstab, Farbbereich, Komposit).
- „Es sah gut aus“ anstelle einer Behauptung; Fehler, die mit Exit-Code 0 enden.
- Text-/Asset-Operationen mit Tools, die Nicht-ASCII verfälschen (überprüfe die Codierung nach der Bearbeitung).
- Überprüfungsframes von Hand reparieren, anstatt die Quelle zu reparieren.
- Eine Zustandsdiskontinuität hinter einer Überblendung verstecken.
9. Checklisten
Vor dem Flug – Spezifikation → Behauptungen; render(t)-Signatur eingefroren; Naht-Richtlinie + Schleifen-Algebra; Assets eingebettet; drei Sonden existieren; Budget pro Frame festgelegt; FFMPEG/FFPROBE aufgelöst und funktionsfähigkeitsgeprüft.
Während des Flugs – nach jeder Szene: Kosten-Sonde, Bewegungsserie, ereignisgesteuerter Überprüfungsraster; Problemliste geschlossen; Syntax-/Typ-Prüfung plus eine Nicht-ASCII-Prüfung auf bearbeiteten Quellen; hashe die Revision.
Vor der Auslieferung – Neubau aus der Quelle; vollständiger Render (kostengewichtet, Grenz-Frames, Concat überprüft); Lautstärke zwei Durchgänge + dritte Überprüfung; unabhängiges Messskript → PASS/FAIL; berichte exakte vs. ungefähre Behauptungen explizit; lösche temporäre Skripte/Bildschirmfotos und dokumentiere den Ein-Befehl-Neuaufbau für jedes.
Definition von erledigt – Container-Metadaten exakt; Nahtverhalten bewiesen; Grenze für eingefrorene Läufe eingehalten; Lautstärke/True Peak eingehalten; Bildschirmtext entspricht wörtlich der Aufgabenbeschreibung; ein Befehl baut aus der Quelle neu auf; alle QC grün auf dem eingefrorenen Hash.
10. Mitgelieferte Skripte
Beide sind einteilig, ohne Abhängigkeiten (nur ffmpeg/ffprobe), lösen ihre Tools von $FFMPEG/$FFPROBE → PATH → gemeinsame Speicherorte, geben eine PASS/FAIL-Zeile pro Behauptung aus, geben JSON mit --json aus und enden mit einem Exit-Code ungleich Null bei Fehlschlag. Lösen ihr Verzeichnis aus dem Pfad dieser Fähigkeit selbst auf.
# QC einer fertigen Datei: Container vs. Erwartungen, Grenze für eingefrorene Läufe, Schleifennaht,
# Lautstärke/True Peak, optionale Kontaktbögen zur manuellen Überprüfung
node av_audit.mjs film.mp4 --expect-duration 30 --expect-fps 60 --expect-size 1920x1080 \
--static-limit 0.5 --sheets ./sheets # Exit 0 = alle Prüfungen bestanden
# Normalisiere eine WAV auf ein Ziel und muxe sie auf ein codiertes Video (Videostream kopiert),
# dann überprüfe mit einem dritten unabhängigen Messdurchlauf
node mux_normalize.mjs silent.mp4 score.wav film.mp4 --lufs -14 --tp -1 --abr 320k
av_audit.mjs verwendet bewusst eine niedrigauflösende (160×90) MAD-Serie: Sie sieht ganzframe-Wertänderungen und Blockbewegungen, nicht Subpixel-Bewegungen dünner Linien – kombiniere sie mit der Metrikempfindlichkeitsregel in §1. Für eine Schleife behandle first_vs_last_psnr_db als Beobachtung über einen Frame Bewegung und beweise die Exaktheit separat durch den Vergleich zweier Render des Renderers selbst.
Einzeiler-Zusammenfassung – Erfolg hängt von drei Dingen ab, keines davon Zeichenfähigkeit: die Zeitfunktion ist rein und überprüfbar, die Metriken werden verstanden, bevor sie optimiert werden, und die Überprüfungs- und Auslieferungspfade sind derselbe Pfad.