Video-Export: Animation als Videodatei und Game-View-Aufnahme #405

Closed
opened 2026-10-11 13:02:26 +02:00 by meleki · 3 comments
Owner

Wunsch vom 2026-10-11 im Chat (zu #404):

export als videodatei: eine animation als video exportieren ist ein eigenes issue. es soll auch möglich sein eine game view aufnahme zu machen und das als video zu exportieren

Zwei Wege zu einer Videodatei:

  1. Animation exportieren: bildgenau, Frame für Frame gerendert. Das ist der Abschluss des Schneidens aus #404.
  2. Game-View aufnehmen: in Echtzeit, Start und Stopp per Knopf, das Spiel läuft dabei normal.

Dieses Issue sammelt den Wunsch und einen ersten Plan. Bevor gebaut wird, braucht es die Entscheidungen unten. Es setzt auf #404 auf (gesteuerte Videos), Weg 2 aber nur zum Teil.

Vorab gemessen (2026-10-11, Chromium 154 im CI-Image game-maker-ci, Linux)

Ergebnis
MediaRecorder WebM VP9+Opus, VP8+Opus unterstützt
MediaRecorder MP4 H.264 ohne Ton unterstützt
MediaRecorder MP4 H.264+AAC nicht unterstützt
VideoEncoder H.264, VP9, AV1 bei 1080p unterstützt
AudioEncoder Opus unterstützt
AudioEncoder AAC nicht unterstützt

Das Chrome des Users unter Windows kann AAC vermutlich, gemessen ist das nicht. Der Plan verlässt sich deshalb nur auf WebM mit VP9 und Opus und lässt den Server das Ziel-MP4 machen.

Plan

Gemeinsamer Ausgang

  • Der Browser erzeugt WebM (VP9 + Opus) und lädt es über die bestehende Video-Route hoch (bis 2 GB, gestreamt).
  • Der Container game-maker-video macht daraus wie jeden Upload MP4 H.264 + AAC, mit Posterbild und Daten.
  • Ergebnis ist ein Video-Asset im Projekt („ export“ bzw. „Recording “). Am Asset gibt es einen Knopf Download, der die fertige MP4 herunterlädt.
  • So gibt es nur einen Weg zur Umwandlung, und ein Export lässt sich gleich wieder als Clip verwenden.

Weg 1: Animation exportieren (bildgenau)

  • Knopf Export video im Animations-Tab, Dialog mit Größe (bis 1080p, Standard: Spielgröße des Projekts), fps (Standard: fps der Animation) und Bereich (ganze Animation oder In/Out).
  • Gerendert wird offline, ein Bild nach dem anderen, unabhängig von der Echtzeit: Animationszeit setzen, warten, bis jedes gesteuerte Video (#404) sein Bild gemeldet hat, die Szene in eine Textur rendern, als VideoFrame an den VideoEncoder (VP9) geben. Damit ist jedes Bild exakt, auch wenn ein Bild länger als 1/fps dauert.
  • Verpacken in WebM: Der VideoEncoder liefert nur kodierte Stücke, keine Datei. Dafür braucht es einen Muxer, das wäre eine neue Abhängigkeit (siehe Entscheidung 2).
  • Fortschrittsbalken mit Abbrechen. Der Editor ist währenddessen gesperrt.

Weg 2: Game-View aufnehmen (Echtzeit)

  • Knopf Record im Game-Tab neben Play/Pause, mit laufender Zeit. Stopp beendet die Aufnahme und lädt sie hoch.
  • Bild über canvas.captureStream() des Spiel-Canvas, Ton über einen MediaStreamAudioDestinationNode am Master-Bus, beides zusammen in einen MediaRecorder (WebM VP9+Opus).
  • Nur das Spielbild, ohne Editor-Oberfläche. Ob der Canvas dafür ausreicht oder das Spiel in eine eigene Textur gerendert werden muss, ist beim Bau zu klären: Der Game-Tab zeichnet in denselben Canvas wie die UI.
  • Was ruckelt, ist auch in der Aufnahme ruckelig. Das ist bei Echtzeit-Aufnahme so.

Tests

  • Unit: Bildzeiten des Exports (Frame n → Animationszeit, In/Out, fps-Umrechnung), Dateiname und Asset-Daten.
  • e2e: kurze Animation (z. B. 10 Frames, Farbwechsel) exportieren, warten, bis die Umwandlung fertig ist, dann das Ergebnis-Video abspielen und per Pixel prüfen, ob Frame 0 und Frame 9 die richtigen Farben haben. Für Weg 2: 1 s aufnehmen, prüfen, dass ein Video-Asset mit etwa 1 s Länge entsteht.
  • PHP: nicht betroffen, die Route gibt es schon.

Entscheidungen des Users

  1. Wohin das Ergebnis geht: Video-Asset im Projekt mit Download-Knopf (Vorschlag), nur Download, oder beides zur Wahl im Dialog.
  2. Muxer für Weg 1: eine kleine Bibliothek im Browser (Vorschlag, z. B. Mediabunny, Version und Größe vor dem Einbau prüfen). Alternative ohne neue Abhängigkeit: die einzelnen Bilder an den Server schicken und dort mit ffmpeg zusammensetzen. Das ist deutlich mehr Datenverkehr und ein neuer Job-Typ im Worker.
  3. Ton im Animations-Export: erst ohne (Vorschlag), oder gleich mit. Mit Ton heißt: den Ton der gesteuerten Videos und der Sounds im Takt der Animation offline mischen. Das ist der aufwändigste Teil.
  4. Reihenfolge: Weg 2 zuerst (Vorschlag, kleiner und braucht #404 nicht), oder Weg 1 zuerst, weil er den Schnitt aus #404 abschließt.

Nicht dabei

  • Andere Zielformate als MP4 H.264 + AAC (z. B. GIF, WebM zum Herunterladen)
  • Alpha im Export
  • Untertitel als eigene Spur
Wunsch vom 2026-10-11 im Chat (zu #404): > export als videodatei: eine animation als video exportieren ist ein eigenes issue. es soll auch möglich sein eine game view aufnahme zu machen und das als video zu exportieren Zwei Wege zu einer Videodatei: 1. **Animation exportieren:** bildgenau, Frame für Frame gerendert. Das ist der Abschluss des Schneidens aus #404. 2. **Game-View aufnehmen:** in Echtzeit, Start und Stopp per Knopf, das Spiel läuft dabei normal. Dieses Issue sammelt den Wunsch und einen ersten Plan. Bevor gebaut wird, braucht es die Entscheidungen unten. Es setzt auf #404 auf (gesteuerte Videos), Weg 2 aber nur zum Teil. ## Vorab gemessen (2026-10-11, Chromium 154 im CI-Image `game-maker-ci`, Linux) | | Ergebnis | |---|---| | `MediaRecorder` WebM VP9+Opus, VP8+Opus | unterstützt | | `MediaRecorder` MP4 H.264 ohne Ton | unterstützt | | `MediaRecorder` MP4 H.264+AAC | **nicht** unterstützt | | `VideoEncoder` H.264, VP9, AV1 bei 1080p | unterstützt | | `AudioEncoder` Opus | unterstützt | | `AudioEncoder` AAC | **nicht** unterstützt | Das Chrome des Users unter Windows kann AAC vermutlich, gemessen ist das nicht. Der Plan verlässt sich deshalb nur auf WebM mit VP9 und Opus und lässt den Server das Ziel-MP4 machen. ## Plan ### Gemeinsamer Ausgang - Der Browser erzeugt **WebM (VP9 + Opus)** und lädt es über die bestehende Video-Route hoch (bis 2 GB, gestreamt). - Der Container `game-maker-video` macht daraus wie jeden Upload **MP4 H.264 + AAC**, mit Posterbild und Daten. - Ergebnis ist ein **Video-Asset im Projekt** („<Animation> export“ bzw. „Recording <Zeit>“). Am Asset gibt es einen Knopf **Download**, der die fertige MP4 herunterlädt. - So gibt es nur einen Weg zur Umwandlung, und ein Export lässt sich gleich wieder als Clip verwenden. ### Weg 1: Animation exportieren (bildgenau) - Knopf **Export video** im Animations-Tab, Dialog mit Größe (bis 1080p, Standard: Spielgröße des Projekts), fps (Standard: fps der Animation) und Bereich (ganze Animation oder In/Out). - Gerendert wird **offline**, ein Bild nach dem anderen, unabhängig von der Echtzeit: Animationszeit setzen, warten, bis jedes gesteuerte Video (#404) sein Bild gemeldet hat, die Szene in eine Textur rendern, als `VideoFrame` an den `VideoEncoder` (VP9) geben. Damit ist jedes Bild exakt, auch wenn ein Bild länger als 1/fps dauert. - **Verpacken in WebM:** Der `VideoEncoder` liefert nur kodierte Stücke, keine Datei. Dafür braucht es einen Muxer, das wäre eine neue Abhängigkeit (siehe Entscheidung 2). - Fortschrittsbalken mit Abbrechen. Der Editor ist währenddessen gesperrt. ### Weg 2: Game-View aufnehmen (Echtzeit) - Knopf **Record** im Game-Tab neben Play/Pause, mit laufender Zeit. Stopp beendet die Aufnahme und lädt sie hoch. - Bild über `canvas.captureStream()` des Spiel-Canvas, Ton über einen `MediaStreamAudioDestinationNode` am Master-Bus, beides zusammen in einen `MediaRecorder` (WebM VP9+Opus). - **Nur das Spielbild**, ohne Editor-Oberfläche. Ob der Canvas dafür ausreicht oder das Spiel in eine eigene Textur gerendert werden muss, ist beim Bau zu klären: Der Game-Tab zeichnet in denselben Canvas wie die UI. - Was ruckelt, ist auch in der Aufnahme ruckelig. Das ist bei Echtzeit-Aufnahme so. ### Tests - **Unit:** Bildzeiten des Exports (Frame n → Animationszeit, In/Out, fps-Umrechnung), Dateiname und Asset-Daten. - **e2e:** kurze Animation (z. B. 10 Frames, Farbwechsel) exportieren, warten, bis die Umwandlung fertig ist, dann das Ergebnis-Video abspielen und per Pixel prüfen, ob Frame 0 und Frame 9 die richtigen Farben haben. Für Weg 2: 1 s aufnehmen, prüfen, dass ein Video-Asset mit etwa 1 s Länge entsteht. - **PHP:** nicht betroffen, die Route gibt es schon. ## Entscheidungen des Users 1. **Wohin das Ergebnis geht:** Video-Asset im Projekt mit Download-Knopf (Vorschlag), nur Download, oder beides zur Wahl im Dialog. 2. **Muxer für Weg 1:** eine kleine Bibliothek im Browser (Vorschlag, z. B. Mediabunny, Version und Größe vor dem Einbau prüfen). Alternative ohne neue Abhängigkeit: die einzelnen Bilder an den Server schicken und dort mit ffmpeg zusammensetzen. Das ist deutlich mehr Datenverkehr und ein neuer Job-Typ im Worker. 3. **Ton im Animations-Export:** erst ohne (Vorschlag), oder gleich mit. Mit Ton heißt: den Ton der gesteuerten Videos und der Sounds im Takt der Animation offline mischen. Das ist der aufwändigste Teil. 4. **Reihenfolge:** Weg 2 zuerst (Vorschlag, kleiner und braucht #404 nicht), oder Weg 1 zuerst, weil er den Schnitt aus #404 abschließt. ## Nicht dabei - Andere Zielformate als MP4 H.264 + AAC (z. B. GIF, WebM zum Herunterladen) - Alpha im Export - Untertitel als eigene Spur
admin added this to the Development project 2026-10-11 13:04:52 +02:00
admin moved this to Next in Development on 2026-10-11 13:05:20 +02:00
Author
Owner

Plan nach den Entscheidungen vom 2026-10-11

Umfang jetzt: nur die Aufnahme der Game-View, gesteuert über zwei Nodes. Kein Animations-Export, kein Record-Knopf im Editor. Der Plan im Issue-Text oben gilt damit nur noch zum Teil. Maßgeblich ist dieser Kommentar.

Entscheidungen des Users

  1. Ergebnis im Editor: Video-Asset im Projekt + Download. Im exportierten Spiel nur Download.
  2. Writer: eigener Matroska-Writer, keine neue Abhängigkeit.
  3. Ton: genau das, was am Master herauskommt, hinter dem Limiter.
  4. Gesteuert wird nur über die Nodes Start recording / Stop recording.
  5. 720p/1080p: Höhe fest, die Breite ergibt sich aus dem Seitenverhältnis der Kamera.
  6. Partie endet ohne Stop recording (Esc, Stop game): Aufnahme wird automatisch beendet und gespeichert. Bei Load scene und Restart game läuft sie weiter.
  7. Start recording während einer laufenden Aufnahme: wird ignoriert, Warnung im Log.
  8. Pause im Editor hält die Aufnahme an. Hält das Spiel selbst seine Zeit an (Game-Time-Nodes), läuft die Aufnahme weiter.

Nodes (Kategorie Action)

  • Start recording: Exec rein und raus.
    • Name (Text, optional): leer ergibt Recording 2026-10-11 16:42:05.
    • Resolution: Game view (Standard), 720p, 1080p.
    • FPS: 60 (Standard), 30.
  • Stop recording: Exec rein und raus. Ohne laufende Aufnahme tut er nichts, mit einer Zeile im Log.
  • Wie bei Stop game setzen die Nodes nur eine Anfrage an der World. Ausgeführt wird sie vom Host, also von PlaySession im Editor und von Player im exportierten Spiel.

Aufnahme

  • Bild: Nur das Kamerabild, ohne Hinweistext, Laufzeile, Fullscreen-Knopf, Performance-Overlay und Letterbox-Ränder.
    • Game view: die Größe der View auf dem Bildschirm beim Start.
    • 720p/1080p: Höhe 720 bzw. 1080, die Breite folgt der Kamera (auf eine gerade Zahl gerundet, das verlangen die Encoder).
    • Die Größe bleibt während der Aufnahme fest. Ändert sich die View, wird auf diese Größe skaliert.
  • Video: VideoEncoder. H.264, wenn der Rechner es in Hardware kann, sonst VP9.
  • Ton: Ein Worklet greift hinter dem Limiter ab, AudioEncoder macht Opus daraus (wie beim Bake).
  • Datei: Matroska (.mkv), stückweise geschrieben, nicht als ein großer Puffer im Speicher.
  • Grenze: Der Upload nimmt bis 2 GB. Kurz davor wird die Aufnahme beendet und gespeichert, mit Warnung im Log.

Ergebnis

  • Editor: Upload über die bestehende Video-Route. Der Worker macht daraus wie bei jedem Upload eine MP4 H.264 + AAC. Im Projekt entsteht ein Video-Asset mit dem Namen aus dem Node. Am Video-Asset gibt es einen Knopf Download für die MP4.
  • Exportiertes Spiel: Die Datei wird direkt heruntergeladen. Weil es dort keinen Server-Weg gibt, ist das die .mkv aus dem Browser, keine MP4.

Reihenfolge

  1. Machbarkeitsprobe:
    • Bild ohne Zurücklesen in den Encoder bringen, auch bei 1080p, wenn die View auf dem Bildschirm kleiner ist.
    • Tempo von H.264 und VP9 bei 1080p60 messen.
    • Opus vom Master abgreifen.
    • Matroska schreiben, hochladen und vom Worker in MP4 umwandeln lassen.
  2. Matroska-Writer mit Unit-Tests.
  3. Aufnahme-Kern, Nodes, Host im Editor, Asset und Download-Knopf.
  4. Exportiertes Spiel (Download).
  5. Doku im Wiki (Videos, Behaviors).

Tests

  • Unit: Größen (fest/720p/1080p, gerade Breite), Standardname, Writer (eigene Datei wieder einlesen), Zeitstempel über eine Pause hinweg, zweites Start recording wird ignoriert.
  • e2e: Ein Behavior startet die Aufnahme, nach etwa 1 s stoppt es sie. Geprüft wird: Ein Video-Asset von etwa 1 s entsteht, es hat eine Tonspur, und die Farbe im Bild stimmt.
  • PHP: nicht betroffen, die Route gibt es schon.

Offen / nicht gemessen

  • Ob H.264 in Hardware bei dir unter Windows 1080p60 in Echtzeit schafft. Der CI-Chrome läuft headless unter Linux und sagt darüber wenig.
  • Ob die .mkv aus dem exportierten Spiel im Windows-Player abspielt (H.264/VP9 mit Opus). In Chrome und VLC geht Matroska.
## Plan nach den Entscheidungen vom 2026-10-11 **Umfang jetzt: nur die Aufnahme der Game-View, gesteuert über zwei Nodes.** Kein Animations-Export, kein Record-Knopf im Editor. Der Plan im Issue-Text oben gilt damit nur noch zum Teil. Maßgeblich ist dieser Kommentar. ### Entscheidungen des Users 1. Ergebnis im Editor: **Video-Asset im Projekt + Download**. Im exportierten Spiel **nur Download**. 2. Writer: **eigener Matroska-Writer**, keine neue Abhängigkeit. 3. Ton: **genau das, was am Master herauskommt**, hinter dem Limiter. 4. Gesteuert wird nur über die Nodes `Start recording` / `Stop recording`. 5. 720p/1080p: **Höhe fest**, die Breite ergibt sich aus dem Seitenverhältnis der Kamera. 6. Partie endet ohne `Stop recording` (Esc, `Stop game`): Aufnahme wird **automatisch beendet und gespeichert**. Bei `Load scene` und `Restart game` läuft sie weiter. 7. `Start recording` während einer laufenden Aufnahme: **wird ignoriert, Warnung im Log**. 8. Pause im Editor hält die Aufnahme an. Hält das Spiel selbst seine Zeit an (Game-Time-Nodes), läuft die Aufnahme weiter. ### Nodes (Kategorie Action) - **`Start recording`**: Exec rein und raus. - *Name* (Text, optional): leer ergibt `Recording 2026-10-11 16:42:05`. - *Resolution*: `Game view` (Standard), `720p`, `1080p`. - *FPS*: `60` (Standard), `30`. - **`Stop recording`**: Exec rein und raus. Ohne laufende Aufnahme tut er nichts, mit einer Zeile im Log. - Wie bei `Stop game` setzen die Nodes nur eine Anfrage an der World. Ausgeführt wird sie vom Host, also von `PlaySession` im Editor und von `Player` im exportierten Spiel. ### Aufnahme - **Bild:** Nur das Kamerabild, ohne Hinweistext, Laufzeile, Fullscreen-Knopf, Performance-Overlay und Letterbox-Ränder. - `Game view`: die Größe der View auf dem Bildschirm beim Start. - `720p`/`1080p`: Höhe 720 bzw. 1080, die Breite folgt der Kamera (auf eine gerade Zahl gerundet, das verlangen die Encoder). - Die Größe bleibt während der Aufnahme fest. Ändert sich die View, wird auf diese Größe skaliert. - **Video:** `VideoEncoder`. H.264, wenn der Rechner es in Hardware kann, sonst VP9. - **Ton:** Ein Worklet greift hinter dem Limiter ab, `AudioEncoder` macht Opus daraus (wie beim Bake). - **Datei:** Matroska (`.mkv`), stückweise geschrieben, nicht als ein großer Puffer im Speicher. - **Grenze:** Der Upload nimmt bis 2 GB. Kurz davor wird die Aufnahme beendet und gespeichert, mit Warnung im Log. ### Ergebnis - **Editor:** Upload über die bestehende Video-Route. Der Worker macht daraus wie bei jedem Upload eine MP4 H.264 + AAC. Im Projekt entsteht ein Video-Asset mit dem Namen aus dem Node. Am Video-Asset gibt es einen Knopf **Download** für die MP4. - **Exportiertes Spiel:** Die Datei wird direkt heruntergeladen. Weil es dort keinen Server-Weg gibt, ist das die `.mkv` aus dem Browser, keine MP4. ### Reihenfolge 1. **Machbarkeitsprobe:** - Bild ohne Zurücklesen in den Encoder bringen, auch bei 1080p, wenn die View auf dem Bildschirm kleiner ist. - Tempo von H.264 und VP9 bei 1080p60 messen. - Opus vom Master abgreifen. - Matroska schreiben, hochladen und vom Worker in MP4 umwandeln lassen. 2. Matroska-Writer mit Unit-Tests. 3. Aufnahme-Kern, Nodes, Host im Editor, Asset und Download-Knopf. 4. Exportiertes Spiel (Download). 5. Doku im Wiki (*Videos*, *Behaviors*). ### Tests - **Unit:** Größen (fest/720p/1080p, gerade Breite), Standardname, Writer (eigene Datei wieder einlesen), Zeitstempel über eine Pause hinweg, zweites `Start recording` wird ignoriert. - **e2e:** Ein Behavior startet die Aufnahme, nach etwa 1 s stoppt es sie. Geprüft wird: Ein Video-Asset von etwa 1 s entsteht, es hat eine Tonspur, und die Farbe im Bild stimmt. - **PHP:** nicht betroffen, die Route gibt es schon. ### Offen / nicht gemessen - Ob H.264 in Hardware bei dir unter Windows 1080p60 in Echtzeit schafft. Der CI-Chrome läuft headless unter Linux und sagt darüber wenig. - Ob die `.mkv` aus dem exportierten Spiel im Windows-Player abspielt (H.264/VP9 mit Opus). In Chrome und VLC geht Matroska.
Author
Owner

Machbarkeitsprobe (2026-10-11)

Gemessen im Test-Chrome auf melektaus-atx: Chromium 154, WSL, ANGLE D3D12 auf der RTX 4070. Reines WebGL2 + WebCodecs, bewegtes Bild mit Rauschen, 240 Frames bei 60 fps.

Dieser Chrome hat keinen Hardware-Encoder (prefer-hardware ist weder für H.264 noch für VP9 unterstützt). Alle Zahlen unten sind also Software-Kodierung. Unter Windows nimmt Chrome vermutlich den Encoder der Grafikkarte, gemessen ist das nicht.

Bild in den Encoder

Weg Main-Thread pro Frame Ergebnis
VideoFrame direkt aus dem WebGL-Canvas, 1080p 0,2 ms geht
in eine Textur rendern, asynchron zurücklesen (PBO), 1080p 7,2 ms im Mittel, p95 19 ms zu teuer
dasselbe bei 720p 2,4 ms
in Streifen in den Canvas rendern, auf der GPU in einen 2D-Canvas in Zielgröße kopieren, 1080p aus einem Canvas mit 1920x950 (2 Streifen) 0,3 ms geht, Pixel stimmen ohne Naht
  • Abgriff mitten im Frame geht: Erst Rot zeichnen und abgreifen, dann Blau darüber. Das abgegriffene Bild ist Rot. Damit lässt sich nur das Spiel zeichnen und abgreifen, die Oberfläche kommt erst danach. Menüs, Fenster und Overlays über der Game-View landen so nicht in der Aufnahme, und auf dem Bildschirm muss nichts ausgeblendet werden.
  • Ein Ausschnitt per visibleRect geht.
  • Der Encoder skaliert ein kleineres Bild selbst hoch (1280x720 → 1920x1080), das wird aber unscharf. Deshalb die Streifen.

Encoder (Software)

1080p60 720p60
H.264 (avc1.64002A) hält mit, 2 von 240 Frames verworfen, Verzug im Mittel 24 ms hält mit, 0 verworfen
VP9 12 von 240 verworfen, Verzug 140 ms hält mit

H.264 liefert die description (avcC), die Matroska braucht. Opus 48 kHz Stereo ist unterstützt.

Was daraus folgt

  • Bild: Vor dem Zeichnen der Oberfläche wird nur der Inhalt der Game-View gezeichnet, skaliert auf die Zielgröße und, wenn der Canvas zu klein ist, in Streifen. Dann wird kopiert, abgegriffen, und erst danach kommt die normale Oberfläche.
  • Codec: Immer H.264, Chrome nimmt dafür von selbst Hardware, wenn es sie gibt. VP9 nur, wenn H.264 fehlt.
  • Kommt der Encoder nicht nach, wird der Frame verworfen. Der Worker macht beim Umwandeln daraus eine Datei mit fester Bildrate.
  • Grenze bei 720p/1080p, wenn die Zielgröße größer ist als die View auf dem Bildschirm: Formen und Sprites werden in voller Auflösung gezeichnet. Text und Kamera-Shader entstehen aber in der Bildschirmgröße und werden hochskaliert, sind in der Aufnahme also weicher.
## Machbarkeitsprobe (2026-10-11) Gemessen im Test-Chrome auf `melektaus-atx`: Chromium 154, WSL, ANGLE D3D12 auf der RTX 4070. Reines WebGL2 + WebCodecs, bewegtes Bild mit Rauschen, 240 Frames bei 60 fps. **Dieser Chrome hat keinen Hardware-Encoder** (`prefer-hardware` ist weder für H.264 noch für VP9 unterstützt). Alle Zahlen unten sind also Software-Kodierung. Unter Windows nimmt Chrome vermutlich den Encoder der Grafikkarte, gemessen ist das nicht. ### Bild in den Encoder | Weg | Main-Thread pro Frame | Ergebnis | |---|---|---| | `VideoFrame` direkt aus dem WebGL-Canvas, 1080p | 0,2 ms | geht | | in eine Textur rendern, asynchron zurücklesen (PBO), 1080p | 7,2 ms im Mittel, p95 19 ms | zu teuer | | dasselbe bei 720p | 2,4 ms | | | **in Streifen in den Canvas rendern, auf der GPU in einen 2D-Canvas in Zielgröße kopieren**, 1080p aus einem Canvas mit 1920x950 (2 Streifen) | **0,3 ms** | geht, Pixel stimmen ohne Naht | - **Abgriff mitten im Frame geht:** Erst Rot zeichnen und abgreifen, dann Blau darüber. Das abgegriffene Bild ist Rot. Damit lässt sich nur das Spiel zeichnen und abgreifen, die Oberfläche kommt erst danach. Menüs, Fenster und Overlays über der Game-View landen so nicht in der Aufnahme, und auf dem Bildschirm muss nichts ausgeblendet werden. - Ein Ausschnitt per `visibleRect` geht. - Der Encoder skaliert ein kleineres Bild selbst hoch (1280x720 → 1920x1080), das wird aber unscharf. Deshalb die Streifen. ### Encoder (Software) | | 1080p60 | 720p60 | |---|---|---| | H.264 (`avc1.64002A`) | hält mit, 2 von 240 Frames verworfen, Verzug im Mittel 24 ms | hält mit, 0 verworfen | | VP9 | 12 von 240 verworfen, Verzug 140 ms | hält mit | H.264 liefert die `description` (avcC), die Matroska braucht. Opus 48 kHz Stereo ist unterstützt. ### Was daraus folgt - **Bild:** Vor dem Zeichnen der Oberfläche wird nur der Inhalt der Game-View gezeichnet, skaliert auf die Zielgröße und, wenn der Canvas zu klein ist, in Streifen. Dann wird kopiert, abgegriffen, und erst danach kommt die normale Oberfläche. - **Codec:** Immer H.264, Chrome nimmt dafür von selbst Hardware, wenn es sie gibt. VP9 nur, wenn H.264 fehlt. - **Kommt der Encoder nicht nach**, wird der Frame verworfen. Der Worker macht beim Umwandeln daraus eine Datei mit fester Bildrate. - **Grenze bei 720p/1080p, wenn die Zielgröße größer ist als die View auf dem Bildschirm:** Formen und Sprites werden in voller Auflösung gezeichnet. Text und Kamera-Shader entstehen aber in der Bildschirmgröße und werden hochskaliert, sind in der Aufnahme also weicher.
admin moved this to Done in Development on 2026-10-11 21:09:08 +02:00
Author
Owner

Umgesetzt in e191dc4 (Wiki 59eda02)

Bedienung

  • Start recording (Action-Node) mit Name (leer → Recording <Zeitstempel>), Resolution (Game view, 720p, 1080p) und FPS (60 oder 30).
  • Stop recording beendet die Aufnahme.
  • Bei 720p/1080p ist die Höhe fest, die Breite folgt dem Seitenverhältnis der Kamera.
  • Editor: Die Aufnahme geht über die bestehende Video-Route hoch und wird zur MP4. Nach dem Ende der Partie entsteht ein Video-Asset mit dem Namen aus dem Node. Im Video-Tab gibt es neu den Knopf Download.
  • Exportiertes Spiel: Die .mkv wird direkt heruntergeladen.

Verhalten (wie entschieden)

  • Ein zweites Start recording wird ignoriert, mit Warnung im Log.
  • Load scene und Restart game lassen die Aufnahme weiterlaufen, die Editor-Pause hält sie an.
  • Endet die Partie ohne Stop recording (Esc, Stop game), wird gespeichert.
  • Bei 2 GB stoppt die Aufnahme mit einer Zeile im Log.

Technik

  • Bild: nur das Kamerabild, ohne Hinweise, Overlays und Letterbox. Gezeichnet wird in eine eigene Render-Textur in Zielgröße, zurückgelesen über Pixel-Puffer mit Fence (PictureReader). Der Editor wartet nicht auf die GPU. Sind alle 3 Puffer belegt, wird ein Bild verworfen und gezählt.
  • Kein Flackern: Eine erste Fassung hat Kacheln in den sichtbaren Canvas gezeichnet. Mit dem Low-Latency-Canvas (desynchronized) kam dieser Zwischenstand auf den Bildschirm. Mit ?vsync war es weg, das hat die Ursache bestätigt. Die Aufnahme fasst den sichtbaren Canvas jetzt nicht mehr an. Bestätigt im Editor ohne ?vsync.
  • Ton: der Master hinter dem Limiter, abgegriffen im FX-Worklet, kodiert als Opus.
  • Codec: H.264, wenn vorhanden, sonst VP9.
  • Datei: eigener Matroska-Writer, keine neue Abhängigkeit. Die Datei nennt Bildrate und BT.709, sonst macht ffmpeg aus 30 fps 59,94.

Tests

  • Unit recording + matroska: 21 grün, der Matroska-Test liest die Datei mit einem eigenen EBML-Leser wieder ein. Dazu die Fälle im Node-Katalog.
  • e2e 67 (neu): 1080p/30 aus einem 1280×800-Fenster → MP4 1920×1080, 30 fps, richtige Länge, mit Ton, 0 verworfene Bilder. Jedes 4. Pixel geprüft, ein grünes Viertel unten links fängt ein gedrehtes Bild. Ein zweites Start wird ignoriert, eine mit Esc beendete Aufnahme wird mit Zeitnamen gespeichert.
  • e2e 60j: Das exportierte Spiel lädt eine .mkv mit EBML-Kennung herunter.
  • Gate auf melektaus-atx: CHECK EXIT 0, 299 Dateien, 4586 Tests.

Nicht geprüft

  • Der Hardware-Encoder unter Windows (der Test-Chrome kodiert in Software) und was das Zurücklesen dort kostet. Verworfene Bilder zeigt __gm.recording() unter takes[].dropped.
  • Pause, verdeckter Tab, Load scene/Restart während einer Aufnahme, der VP9-Ausweg und die 2-GB-Grenze haben keinen e2e-Check.

Fund außerhalb von #405

Das Poster-JPEG des Workers ist bei jedem Upload farblich daneben (Rot 255 → 229, ffmpeg rechnet mit BT.601). Betrifft nur das Vorschaubild, nicht angefasst.

Der Animations-Export aus dem ursprünglichen Issue entfällt, so entschieden am 2026-10-11.

## Umgesetzt in `e191dc4` (Wiki `59eda02`) ### Bedienung - **`Start recording`** (Action-Node) mit *Name* (leer → `Recording <Zeitstempel>`), *Resolution* (`Game view`, `720p`, `1080p`) und *FPS* (60 oder 30). - **`Stop recording`** beendet die Aufnahme. - Bei 720p/1080p ist die Höhe fest, die Breite folgt dem Seitenverhältnis der Kamera. - **Editor:** Die Aufnahme geht über die bestehende Video-Route hoch und wird zur MP4. Nach dem Ende der Partie entsteht ein Video-Asset mit dem Namen aus dem Node. Im Video-Tab gibt es neu den Knopf **Download**. - **Exportiertes Spiel:** Die `.mkv` wird direkt heruntergeladen. ### Verhalten (wie entschieden) - Ein zweites `Start recording` wird ignoriert, mit Warnung im Log. - `Load scene` und `Restart game` lassen die Aufnahme weiterlaufen, die Editor-Pause hält sie an. - Endet die Partie ohne `Stop recording` (Esc, `Stop game`), wird gespeichert. - Bei 2 GB stoppt die Aufnahme mit einer Zeile im Log. ### Technik - **Bild:** nur das Kamerabild, ohne Hinweise, Overlays und Letterbox. Gezeichnet wird in eine eigene Render-Textur in Zielgröße, zurückgelesen über Pixel-Puffer mit Fence (`PictureReader`). Der Editor wartet nicht auf die GPU. Sind alle 3 Puffer belegt, wird ein Bild verworfen und gezählt. - **Kein Flackern:** Eine erste Fassung hat Kacheln in den sichtbaren Canvas gezeichnet. Mit dem Low-Latency-Canvas (`desynchronized`) kam dieser Zwischenstand auf den Bildschirm. Mit `?vsync` war es weg, das hat die Ursache bestätigt. Die Aufnahme fasst den sichtbaren Canvas jetzt nicht mehr an. Bestätigt im Editor ohne `?vsync`. - **Ton:** der Master hinter dem Limiter, abgegriffen im FX-Worklet, kodiert als Opus. - **Codec:** H.264, wenn vorhanden, sonst VP9. - **Datei:** eigener Matroska-Writer, keine neue Abhängigkeit. Die Datei nennt Bildrate und BT.709, sonst macht ffmpeg aus 30 fps 59,94. ### Tests - Unit `recording` + `matroska`: 21 grün, der Matroska-Test liest die Datei mit einem eigenen EBML-Leser wieder ein. Dazu die Fälle im Node-Katalog. - **e2e 67 (neu):** 1080p/30 aus einem 1280×800-Fenster → MP4 1920×1080, 30 fps, richtige Länge, mit Ton, 0 verworfene Bilder. Jedes 4. Pixel geprüft, ein grünes Viertel unten links fängt ein gedrehtes Bild. Ein zweites Start wird ignoriert, eine mit Esc beendete Aufnahme wird mit Zeitnamen gespeichert. - **e2e 60j:** Das exportierte Spiel lädt eine `.mkv` mit EBML-Kennung herunter. - Gate auf `melektaus-atx`: `CHECK EXIT 0`, 299 Dateien, 4586 Tests. ### Nicht geprüft - Der Hardware-Encoder unter Windows (der Test-Chrome kodiert in Software) und was das Zurücklesen dort kostet. Verworfene Bilder zeigt `__gm.recording()` unter `takes[].dropped`. - Pause, verdeckter Tab, `Load scene`/`Restart` während einer Aufnahme, der VP9-Ausweg und die 2-GB-Grenze haben keinen e2e-Check. ### Fund außerhalb von #405 Das Poster-JPEG des Workers ist bei jedem Upload farblich daneben (Rot 255 → 229, ffmpeg rechnet mit BT.601). Betrifft nur das Vorschaubild, nicht angefasst. Der Animations-Export aus dem ursprünglichen Issue entfällt, so entschieden am 2026-10-11.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#405