Video folgt der Animation: Video time und Video clock am Sprite #404

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

Wunsch vom 2026-10-11 im Chat:

ich würde jetzt gerne ein "video schneiden" - dazu würde ich einfach eine animation erstellen und folgendes machen: clips die verwende würde enablen/disablen, dazu text einblendungen und transitions. aber mein problem: eine video im sprite wird einfach abgespielt sobald enabled: wie kann ich sicherstellen dass ein frame eines videos immer zu einem bestimmten zeitpunkt in einer animation angezeigt wird?

Ziel: Die Animation gibt die Zeit des Videos vor, das Video folgt. Zu jedem Frame einer Animation steht fest, welches Bild eines Videos zu sehen ist: beim Abspielen, beim Scrubben im Animations-Tab und im laufenden Spiel. Vom User so freigegeben (Entscheidung 1 im Chat: „passt“). Der Export als Videodatei ist ein eigenes Issue (#405).

Heute (gelesen im Code, Stand ce933a4)

  • Die Uhr ist das <video> selbst. Ein Player entsteht, sobald ein Sprite das erste Mal nach dem Bild fragt (VideoLibrary.start, src/viewport/VideoLibrary.ts:129), und läuft dann in Echtzeit. Nur der Start einer Partie springt an den Clip-Anfang zurück (startRun, Zeile 64).
  • Enable/Disable ist Glückssache: Ein ausgeschaltetes Sprite fragt nicht mehr, der Player läuft unsichtbar weiter. Unter 2 s wieder an: Das Video ist inzwischen weitergelaufen. Über 2 s (IDLE_MS): Der Player ist verworfen, der Clip fängt von vorn an, und zwar erst, wenn er geladen ist.
  • Ein Player gehört zum Clip, nicht zum Sprite (videoKey in src/core/model/video.ts:82). Zwei Sprites mit demselben Clip zeigen immer dasselbe Bild.
  • Beim Scrubben im Animations-Tab läuft das Video einfach weiter.
  • Schon da und weiter nutzbar: texture ist keybar (springt wie jede Nicht-Zahl hart von Key zu Key), enabled, alpha, tint und Shader-Parameter auch.

Plan

1. Modell: zwei neue Eigenschaften am Sprite

  • Video clock (videoClock): free (Standard, Verhalten wie heute) oder driven.
  • Video time (videoTime): Sekunden ab Start des Clips bzw. des Videos, Standard 0. Eine Zahl, also mit Keys und Easing animierbar wie jede andere.
  • Beide Zeilen zeigt der Inspector nur, wenn Texture ein Video oder einen Clip trägt.
  • Zeit außerhalb des Clips: Mit Loop am Video läuft sie im Kreis (10 s bei einem 4-s-Clip = 2 s), ohne Loop bleibt das letzte Bild stehen. Unter 0 zeigt das erste Bild. Die Regel ist eine reine Funktion neben videoTimeAt.

Was man damit macht:

Keys auf Video time Ergebnis
Frame 0 → 0 s, Frame 300 → 10 s (bei 30 fps), linear läuft normal, bildgenau zur Animation
zwei Keys mit gleichem Wert, oder step Standbild
flachere bzw. steilere Steigung Zeitlupe bzw. Zeitraffer
Easing/Curve Rampen

Ein Schnitt sieht dann so aus: Clip A und Clip B je auf einem Sprite, enabled als Key zum Umschalten, Video time je Sprite gekeyt, darüber Texte, Übergänge über alpha, tint oder Shader-Parameter. Oder ein Sprite, das per texture-Key zwischen Clips wechselt.

2. Ein Player pro Sprite, wenn driven

  • Der Schlüssel eines gesteuerten Players trägt zusätzlich die Id des Trägers (Objekt in der Szene, in der Vorschau des Animations-Tabs, Entity im Spiel). Damit kämpfen zwei Sprites nicht um dieselbe Uhr, und derselbe Clip kann zweimal versetzt laufen.
  • free bleibt beim heutigen Schlüssel pro Clip.
  • Das Aufräumen nach 2 s ohne Nachfrage bleibt. Ein ausgeschaltetes, gesteuertes Sprite verliert seinen Player also. Beim Einschalten wird an die geforderte Zeit gesprungen statt von vorn begonnen. Bis das Bild da ist, zeigt das Sprite nichts (wie heute beim ersten Laden). Ob das beim Schneiden stört, sehen wir am echten Material. Wenn ja: Player eines in der Szene liegenden, ausgeschalteten Sprites vorab laden.

3. Nachführen: Abspielen und Springen sind zwei Wege

Jeder Träger meldet pro Frame die Soll-Zeit. Eine reine Funktion entscheidet aus Soll-Zeit, Ist-Zeit und dem Tempo der Soll-Zeit, was der Player tut:

  • Die Zeit läuft vorwärts mit 0,25× bis 4×: Das Video spielt mit genau diesem playbackRate. Kleine Abweichungen (über einen Frame) werden über ein paar Prozent Tempo nachgeregelt, erst ab einer Schwelle (Vorschlag 0,25 s) wird gesprungen. Grund: Ein Sprung pro Frame ist zu teuer. Er muss vom letzten Keyframe aus dekodieren, bei unserem Abstand von 15 Frames sind das bis zu 14 Bilder.
  • Die Zeit steht, springt oder läuft rückwärts (Standbild, Scrubben, Pingpong zurück): Das Video hält an und springt an die Soll-Zeit. Das Bild wechselt, sobald requestVideoFrameCallback den Frame meldet. Läuft schon ein Sprung, wird nur der neueste Wunsch gemerkt und danach angesprungen, damit Scrubben sich nicht staut.
  • Die Schwellen sind Startwerte und werden am echten Material nachgemessen.

4. Wo das wirkt

  • Im Spiel: Play animation schreibt videoTime über das AnimationSystem wie jede andere Eigenschaft. Ein Behavior kann videoTime genauso setzen. Eine pausierte Partie hält die Zeit an, das Video steht also von selbst.
  • Im Animations-Tab: Die Vorschau zeichnet über dieselbe Bahn wie die Szene (previewHolder), Abspielen und Scrubben folgen also ohne Extraweg.
  • Im Scene-Tab: Ein gesteuertes Sprite zeigt das Standbild bei seiner Video time, es läuft nicht.
  • PREVIEW im Inspector: wie der Scene-Tab.

5. Ton

  • Zu hören ist ein gesteuertes Video nur, wenn es vorwärts mit etwa 1× läuft (0,9× bis 1,1×). Bei Standbild, Scrubben, Zeitlupe und Zeitraffer ist es stumm.
  • Im Spiel über den Master-Bus wie heute.
  • Im Animations-Tab beim Abspielen ebenfalls hörbar, damit man nach Ton schneiden kann. Das ist neu, heute sind alle Vorschauen im Editor stumm. Beim Scrubben stumm.

6. Tests

  • Unit: die Regel „Zeit außerhalb des Clips“ (Loop, kein Loop, unter 0) und die Nachführ-Entscheidung (Tempo, Nachregeln, Sprung ab Schwelle, Standbild, rückwärts, nur der neueste Sprung).
  • e2e 66 um einen Check erweitern: Sprite auf driven, Animation mit Keys auf Video time, an zwei Stellen scrubben und per Pixel prüfen, dass das Bild zur Zeit passt (das Testvideo ist bei 0 s rot und bei 0,5 s grün). Dazu zwei Sprites mit demselben Clip und versetzter Zeit zeigen verschiedene Bilder.
  • PHP nicht betroffen.

Nicht dabei

  • Export als Videodatei (Animation und Game-View-Aufnahme): #405
  • Graph-Nodes zum Steuern (Play, Pause, Seek, „Video ended“), wie in #402 als Folge-Issue vorgemerkt
  • Rückwärts abspielen mit Ton
  • Ein Keyframe-Abstand unter 15 Frames. Wird Scrubben bei 1080p zäh, ist das eine Zeile in VideoFormat.php, kostet aber Dateigröße, und schon hochgeladene Videos müssten neu umgewandelt werden.

Grenzen

  • Bildgenau, solange es live im Browser läuft. Kommt ein Frame beim Abspielen zu spät, zeigt Chrome das vorige Bild weiter. Bildgenau in jedem Fall ist erst der Export (#405), weil der auf jedes Bild wartet.
  • Nicht gemessen: wie flüssig Scrubben bei 1080p auf der Maschine des Users ist.
Wunsch vom 2026-10-11 im Chat: > ich würde jetzt gerne ein "video schneiden" - dazu würde ich einfach eine animation erstellen und folgendes machen: clips die verwende würde enablen/disablen, dazu text einblendungen und transitions. aber mein problem: eine video im sprite wird einfach abgespielt sobald enabled: wie kann ich sicherstellen dass ein frame eines videos immer zu einem bestimmten zeitpunkt in einer animation angezeigt wird? Ziel: **Die Animation gibt die Zeit des Videos vor, das Video folgt.** Zu jedem Frame einer Animation steht fest, welches Bild eines Videos zu sehen ist: beim Abspielen, beim Scrubben im Animations-Tab und im laufenden Spiel. Vom User so freigegeben (Entscheidung 1 im Chat: „passt“). Der Export als Videodatei ist ein eigenes Issue (#405). ## Heute (gelesen im Code, Stand ce933a4) - **Die Uhr ist das `<video>` selbst.** Ein Player entsteht, sobald ein Sprite das erste Mal nach dem Bild fragt (`VideoLibrary.start`, `src/viewport/VideoLibrary.ts:129`), und läuft dann in Echtzeit. Nur der Start einer Partie springt an den Clip-Anfang zurück (`startRun`, Zeile 64). - **Enable/Disable ist Glückssache:** Ein ausgeschaltetes Sprite fragt nicht mehr, der Player läuft unsichtbar weiter. Unter 2 s wieder an: Das Video ist inzwischen weitergelaufen. Über 2 s (`IDLE_MS`): Der Player ist verworfen, der Clip fängt von vorn an, und zwar erst, wenn er geladen ist. - **Ein Player gehört zum Clip, nicht zum Sprite** (`videoKey` in `src/core/model/video.ts:82`). Zwei Sprites mit demselben Clip zeigen immer dasselbe Bild. - **Beim Scrubben im Animations-Tab** läuft das Video einfach weiter. - Schon da und weiter nutzbar: `texture` ist keybar (springt wie jede Nicht-Zahl hart von Key zu Key), `enabled`, `alpha`, `tint` und Shader-Parameter auch. ## Plan ### 1. Modell: zwei neue Eigenschaften am Sprite - **`Video clock`** (`videoClock`): `free` (Standard, Verhalten wie heute) oder `driven`. - **`Video time`** (`videoTime`): Sekunden ab Start des Clips bzw. des Videos, Standard 0. Eine Zahl, also mit Keys und Easing animierbar wie jede andere. - Beide Zeilen zeigt der Inspector nur, wenn `Texture` ein Video oder einen Clip trägt. - **Zeit außerhalb des Clips:** Mit Loop am Video läuft sie im Kreis (`10 s` bei einem 4-s-Clip = 2 s), ohne Loop bleibt das letzte Bild stehen. Unter 0 zeigt das erste Bild. Die Regel ist eine reine Funktion neben `videoTimeAt`. Was man damit macht: | Keys auf `Video time` | Ergebnis | |---|---| | Frame 0 → 0 s, Frame 300 → 10 s (bei 30 fps), linear | läuft normal, bildgenau zur Animation | | zwei Keys mit gleichem Wert, oder `step` | Standbild | | flachere bzw. steilere Steigung | Zeitlupe bzw. Zeitraffer | | Easing/Curve | Rampen | Ein Schnitt sieht dann so aus: Clip A und Clip B je auf einem Sprite, `enabled` als Key zum Umschalten, `Video time` je Sprite gekeyt, darüber Texte, Übergänge über `alpha`, `tint` oder Shader-Parameter. Oder ein Sprite, das per `texture`-Key zwischen Clips wechselt. ### 2. Ein Player pro Sprite, wenn `driven` - Der Schlüssel eines gesteuerten Players trägt zusätzlich die Id des Trägers (Objekt in der Szene, in der Vorschau des Animations-Tabs, Entity im Spiel). Damit kämpfen zwei Sprites nicht um dieselbe Uhr, und derselbe Clip kann zweimal versetzt laufen. - `free` bleibt beim heutigen Schlüssel pro Clip. - Das Aufräumen nach 2 s ohne Nachfrage bleibt. Ein ausgeschaltetes, gesteuertes Sprite verliert seinen Player also. Beim Einschalten wird an die geforderte Zeit gesprungen statt von vorn begonnen. Bis das Bild da ist, zeigt das Sprite nichts (wie heute beim ersten Laden). Ob das beim Schneiden stört, sehen wir am echten Material. Wenn ja: Player eines in der Szene liegenden, ausgeschalteten Sprites vorab laden. ### 3. Nachführen: Abspielen und Springen sind zwei Wege Jeder Träger meldet pro Frame die Soll-Zeit. Eine reine Funktion entscheidet aus Soll-Zeit, Ist-Zeit und dem Tempo der Soll-Zeit, was der Player tut: - **Die Zeit läuft vorwärts mit 0,25× bis 4×:** Das Video spielt mit genau diesem `playbackRate`. Kleine Abweichungen (über einen Frame) werden über ein paar Prozent Tempo nachgeregelt, erst ab einer Schwelle (Vorschlag 0,25 s) wird gesprungen. Grund: Ein Sprung pro Frame ist zu teuer. Er muss vom letzten Keyframe aus dekodieren, bei unserem Abstand von 15 Frames sind das bis zu 14 Bilder. - **Die Zeit steht, springt oder läuft rückwärts** (Standbild, Scrubben, Pingpong zurück): Das Video hält an und springt an die Soll-Zeit. Das Bild wechselt, sobald `requestVideoFrameCallback` den Frame meldet. Läuft schon ein Sprung, wird nur der neueste Wunsch gemerkt und danach angesprungen, damit Scrubben sich nicht staut. - Die Schwellen sind Startwerte und werden am echten Material nachgemessen. ### 4. Wo das wirkt - **Im Spiel:** `Play animation` schreibt `videoTime` über das `AnimationSystem` wie jede andere Eigenschaft. Ein Behavior kann `videoTime` genauso setzen. Eine pausierte Partie hält die Zeit an, das Video steht also von selbst. - **Im Animations-Tab:** Die Vorschau zeichnet über dieselbe Bahn wie die Szene (`previewHolder`), Abspielen und Scrubben folgen also ohne Extraweg. - **Im Scene-Tab:** Ein gesteuertes Sprite zeigt das Standbild bei seiner `Video time`, es läuft nicht. - **PREVIEW im Inspector:** wie der Scene-Tab. ### 5. Ton - **Zu hören** ist ein gesteuertes Video nur, wenn es vorwärts mit etwa 1× läuft (0,9× bis 1,1×). Bei Standbild, Scrubben, Zeitlupe und Zeitraffer ist es stumm. - **Im Spiel** über den Master-Bus wie heute. - **Im Animations-Tab** beim Abspielen ebenfalls hörbar, damit man nach Ton schneiden kann. Das ist neu, heute sind alle Vorschauen im Editor stumm. Beim Scrubben stumm. ### 6. Tests - **Unit:** die Regel „Zeit außerhalb des Clips“ (Loop, kein Loop, unter 0) und die Nachführ-Entscheidung (Tempo, Nachregeln, Sprung ab Schwelle, Standbild, rückwärts, nur der neueste Sprung). - **e2e 66** um einen Check erweitern: Sprite auf `driven`, Animation mit Keys auf `Video time`, an zwei Stellen scrubben und per Pixel prüfen, dass das Bild zur Zeit passt (das Testvideo ist bei 0 s rot und bei 0,5 s grün). Dazu zwei Sprites mit demselben Clip und versetzter Zeit zeigen verschiedene Bilder. - PHP nicht betroffen. ## Nicht dabei - **Export als Videodatei** (Animation und Game-View-Aufnahme): #405 - Graph-Nodes zum Steuern (Play, Pause, Seek, „Video ended“), wie in #402 als Folge-Issue vorgemerkt - Rückwärts abspielen mit Ton - Ein Keyframe-Abstand unter 15 Frames. Wird Scrubben bei 1080p zäh, ist das eine Zeile in `VideoFormat.php`, kostet aber Dateigröße, und schon hochgeladene Videos müssten neu umgewandelt werden. ## Grenzen - **Bildgenau, solange es live im Browser läuft.** Kommt ein Frame beim Abspielen zu spät, zeigt Chrome das vorige Bild weiter. Bildgenau in jedem Fall ist erst der Export (#405), weil der auf jedes Bild wartet. - **Nicht gemessen:** wie flüssig Scrubben bei 1080p auf der Maschine des Users ist.
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:15 +02:00
Author
Owner

Umgesetzt in c9e8a3f, Wiki in 912ba7b (Videos → „Following an animation“).

So geht's

  1. Sprite mit einem Video oder Clip als Texture. Dann erscheint Video clock, auf Driven stellen.
  2. Darunter Video time, Sekunden ab Clip-Start. Sie wird in der Animation wie jede Zahl gekeyt, z. B. Frame 0 → 0 s und Frame 300 → 10 s bei 30 fps, linear.
  3. Zwei gleiche Keys oder step = Standbild, flacher = Zeitlupe, steiler = Zeitraffer, Easing/Kurven = Rampen.

Was gebaut ist

  • Zwei neue Zeilen am Sprite, sichtbar nur, wenn Texture ein Video trägt. Hinter dem Clip-Ende läuft die Zeit mit Loop im Kreis, ohne Loop bleibt das letzte Bild stehen.
  • Eigener Player pro gesteuertem Sprite. Derselbe Clip kann zweimal versetzt laufen.
  • Nachführen (src/viewport/videoFollow.ts): Läuft die Animation vorwärts (0,25× bis 4×), spielt das Video mit diesem Tempo und wird nachgeregelt. Standbild, Scrubben und Rückwärts springen, dabei zählt nur der neueste Sprung.
  • Ton nur bei etwa 1×, jetzt auch beim Abspielen im Animations-Tab. Beim Scrubben stumm.
  • Wo es wirkt: Szene, Game-View, Inspector-PREVIEW, Shader-Vorschau, Animations-Tab. Im Spiel setzt Play animation die Video time.

Fund dabei

Nach dem Scrubben lief das Video im Animations-Tab weiter. Die Vorschau dort wurde nur neu gezeichnet, wenn sich der Playhead bewegt, danach hat niemand mehr nach dem Video gefragt. AnimationEditor.tick zeichnet sie jetzt jeden Frame, solange sie ein Video zeigt.

Tests

tsc, eslint, Prettier                 → sauber
Unit (neu: video-clock, video-follow) → 4 Dateien, 42 Tests grün
e2e 66 (melektaus-atx)                → ALL PASSED, 22 Checks (3 neu)
Gate (melektaus-atx)                  → CHECK EXIT 0, 297 Dateien, 4546 Tests
  • 66i: zwei Sprites mit demselben Clip stehen bei 0,2 s rot und bei 0,7 s grün, jedes mit eigenem Player.
  • 66j: Scrubben auf Frame 2, 7, 12 und zurück auf 3 zeigt rot, grün, blau, rot, und jede Farbe steht 0,7 s später noch.
  • 66k: Beim Abspielen liegt das Video 10 bis 30 ms hinter der Soll-Zeit, Tempo 1,00 bis 1,03, nicht stumm.
  • Gegenproben: Ein Sprung, der das Tempo behält, macht den Sprung-Test rot. Wird driven nicht erkannt, werden 66i, 66j und 66k rot.
  • Jedes Sprite hat zwei Standardwerte mehr (videoClock: 'free', videoTime: 0). 8 Tests in 5 Dateien, die die alte Liste festschreiben, sind angepasst.

Nicht geprüft

  • Play animation in einer laufenden Partie im Browser (gleicher Weg wie in der Szene, aber nicht gemessen).
  • Echtes 1080p-Material beim Scrubben.
  • Der CI-Lauf zu c9e8a3f ist noch nicht gelesen.

Export als Videodatei bleibt #405.

Umgesetzt in c9e8a3f, Wiki in 912ba7b (*Videos* → „Following an animation“). ## So geht's 1. Sprite mit einem Video oder Clip als **Texture**. Dann erscheint **Video clock**, auf **Driven** stellen. 2. Darunter **Video time**, Sekunden ab Clip-Start. Sie wird in der Animation wie jede Zahl gekeyt, z. B. Frame 0 → 0 s und Frame 300 → 10 s bei 30 fps, linear. 3. Zwei gleiche Keys oder `step` = Standbild, flacher = Zeitlupe, steiler = Zeitraffer, Easing/Kurven = Rampen. ## Was gebaut ist - **Zwei neue Zeilen am Sprite**, sichtbar nur, wenn `Texture` ein Video trägt. Hinter dem Clip-Ende läuft die Zeit mit Loop im Kreis, ohne Loop bleibt das letzte Bild stehen. - **Eigener Player pro gesteuertem Sprite.** Derselbe Clip kann zweimal versetzt laufen. - **Nachführen** (`src/viewport/videoFollow.ts`): Läuft die Animation vorwärts (0,25× bis 4×), spielt das Video mit diesem Tempo und wird nachgeregelt. Standbild, Scrubben und Rückwärts springen, dabei zählt nur der neueste Sprung. - **Ton** nur bei etwa 1×, jetzt auch beim Abspielen im Animations-Tab. Beim Scrubben stumm. - **Wo es wirkt:** Szene, Game-View, Inspector-PREVIEW, Shader-Vorschau, Animations-Tab. Im Spiel setzt `Play animation` die Video time. ## Fund dabei Nach dem Scrubben lief das Video im Animations-Tab weiter. Die Vorschau dort wurde nur neu gezeichnet, wenn sich der Playhead bewegt, danach hat niemand mehr nach dem Video gefragt. `AnimationEditor.tick` zeichnet sie jetzt jeden Frame, solange sie ein Video zeigt. ## Tests ``` tsc, eslint, Prettier → sauber Unit (neu: video-clock, video-follow) → 4 Dateien, 42 Tests grün e2e 66 (melektaus-atx) → ALL PASSED, 22 Checks (3 neu) Gate (melektaus-atx) → CHECK EXIT 0, 297 Dateien, 4546 Tests ``` - **66i:** zwei Sprites mit demselben Clip stehen bei 0,2 s rot und bei 0,7 s grün, jedes mit eigenem Player. - **66j:** Scrubben auf Frame 2, 7, 12 und zurück auf 3 zeigt rot, grün, blau, rot, und jede Farbe steht 0,7 s später noch. - **66k:** Beim Abspielen liegt das Video 10 bis 30 ms hinter der Soll-Zeit, Tempo 1,00 bis 1,03, nicht stumm. - **Gegenproben:** Ein Sprung, der das Tempo behält, macht den Sprung-Test rot. Wird `driven` nicht erkannt, werden 66i, 66j und 66k rot. - Jedes Sprite hat zwei Standardwerte mehr (`videoClock: 'free'`, `videoTime: 0`). 8 Tests in 5 Dateien, die die alte Liste festschreiben, sind angepasst. ## Nicht geprüft - `Play animation` in einer laufenden Partie im Browser (gleicher Weg wie in der Szene, aber nicht gemessen). - Echtes 1080p-Material beim Scrubben. - Der CI-Lauf zu c9e8a3f ist noch nicht gelesen. Export als Videodatei bleibt #405.
admin moved this to Done in Development on 2026-10-11 14:29:30 +02:00
admin moved this to Next in Development on 2026-10-11 17:13:03 +02:00
Author
Owner

Nachträge nach dem Abschluss, aus dem Ausprobieren. Drei Commits, Wiki in ec66887, 5cd20e6, 011c64d (Videos).

43fd22a: Kein Aufblitzen beim Schnitt

  • Ein gesteuertes Sprite hält seinen Player bereit, auch ausgeschaltet oder außerhalb des Bildes: stumm, pausiert, auf der Video time. Vorher entstand der Player erst beim Einschalten, bis zum Laden war das Sprite weiß.
  • Im Spiel warf collectWorldItems (src/viewport/items.ts) ausgeschaltete Objekte ganz aus der Zeichenliste. Ausgeschaltete Sprites mit Driven bleiben jetzt drin, als ausgeschaltet markiert.
  • VideoLibrary.wear() zählt als Nachfrage. Sonst hat der Aufräumer den Player nach 2–4 s verworfen, weil ein ausgeschaltetes Sprite nicht gezeichnet wird.
  • Tempo-Messung (videoFollow.ts): Fenster wächst bis 500 ms und gleitet dann mit, statt fest 100 ms. Bei 1/60-s-Schritten der Animationszeit schwankte das alte Fenster um ±17 %, die playbackRate sprang und der Ton setzte aus.
  • Nebenbei, CI-Wackler e2e 34 (Stream-Check): Der Decoder bekam jedes 20-ms-Paket einzeln und wartete jedes Mal auf die Antwort. Jetzt gehen alle geladenen Pakete eines Stücks auf einmal hinein (src/viewport/streaming.ts).

843b110: Kein Rücksprung an den Dateianfang

Lag der letzte Key knapp vor dem Dateiende, spielte das Video bis „ended“, und play() sprang laut HTML-Spec an den Anfang der Datei. Das ergab 1–3 Blitzer mit dem ersten Bild. Ein beendetes Video gilt jetzt als „am Ende“ und wird gehalten.

dd60a79: Kein Weiß am Anfang von Play

Die Game-View legt ihre Player erst beim Zeichnen an, und nur bei sichtbarem Tab. Jetzt wartet Play bei Sekunde 0, bis jedes gesteuerte Video ein Bild hat (höchstens 3 s, Hinweis „Loading the videos…“). Ein gesteuertes Video ohne Bild wird gar nicht gezeichnet statt weiß. Das exportierte Spiel wartet genauso.

Tests

e2e 66 (melektaus-atx)   → ALL PASSED, 27 Checks (66m, 66n, 66o, 66p neu)
Gate vor dd60a79         → CHECK EXIT 0, 297 Dateien, 4558 Tests
  • Gegenproben: 66n findet ohne den Fix an der Zeichenliste keinen Player. 66o sieht mit dem alten Code den Rücksprung auf 0. 66p sieht mit dem alten Code 14 weiße Frames.
  • CI zu dd60a79: Gate grün, Stream-Check in e2e 34 grün. Im suite-Job ist 66k rot (einmal Tempo 1,11, erlaubt ±0,1). Das wird jetzt behoben.

Nicht geprüft

  • Den Schnitt in einer laufenden Partie im Bild. 66n prüft nur, dass der Player bereitsteht.
  • Das Warten im exportierten Spiel im Browser.
Nachträge nach dem Abschluss, aus dem Ausprobieren. Drei Commits, Wiki in ec66887, 5cd20e6, 011c64d (*Videos*). ## 43fd22a: Kein Aufblitzen beim Schnitt - **Ein gesteuertes Sprite hält seinen Player bereit**, auch ausgeschaltet oder außerhalb des Bildes: stumm, pausiert, auf der Video time. Vorher entstand der Player erst beim Einschalten, bis zum Laden war das Sprite weiß. - **Im Spiel** warf `collectWorldItems` (`src/viewport/items.ts`) ausgeschaltete Objekte ganz aus der Zeichenliste. Ausgeschaltete Sprites mit `Driven` bleiben jetzt drin, als ausgeschaltet markiert. - **`VideoLibrary.wear()` zählt als Nachfrage.** Sonst hat der Aufräumer den Player nach 2–4 s verworfen, weil ein ausgeschaltetes Sprite nicht gezeichnet wird. - **Tempo-Messung** (`videoFollow.ts`): Fenster wächst bis 500 ms und gleitet dann mit, statt fest 100 ms. Bei 1/60-s-Schritten der Animationszeit schwankte das alte Fenster um ±17 %, die `playbackRate` sprang und der Ton setzte aus. - **Nebenbei, CI-Wackler e2e 34 (Stream-Check):** Der Decoder bekam jedes 20-ms-Paket einzeln und wartete jedes Mal auf die Antwort. Jetzt gehen alle geladenen Pakete eines Stücks auf einmal hinein (`src/viewport/streaming.ts`). ## 843b110: Kein Rücksprung an den Dateianfang Lag der letzte Key knapp vor dem Dateiende, spielte das Video bis „ended“, und `play()` sprang laut HTML-Spec an den Anfang der Datei. Das ergab 1–3 Blitzer mit dem ersten Bild. Ein beendetes Video gilt jetzt als „am Ende“ und wird gehalten. ## dd60a79: Kein Weiß am Anfang von Play Die Game-View legt ihre Player erst beim Zeichnen an, und nur bei sichtbarem Tab. Jetzt wartet Play bei Sekunde 0, bis jedes gesteuerte Video ein Bild hat (höchstens 3 s, Hinweis „Loading the videos…“). Ein gesteuertes Video ohne Bild wird gar nicht gezeichnet statt weiß. Das exportierte Spiel wartet genauso. ## Tests ``` e2e 66 (melektaus-atx) → ALL PASSED, 27 Checks (66m, 66n, 66o, 66p neu) Gate vor dd60a79 → CHECK EXIT 0, 297 Dateien, 4558 Tests ``` - **Gegenproben:** 66n findet ohne den Fix an der Zeichenliste keinen Player. 66o sieht mit dem alten Code den Rücksprung auf 0. 66p sieht mit dem alten Code 14 weiße Frames. - **CI zu dd60a79:** Gate grün, Stream-Check in e2e 34 grün. Im `suite`-Job ist **66k** rot (einmal Tempo 1,11, erlaubt ±0,1). Das wird jetzt behoben. ## Nicht geprüft - Den Schnitt in einer laufenden Partie im Bild. 66n prüft nur, dass der Player bereitsteht. - Das Warten im exportierten Spiel im Browser.
Author
Owner

Nachtrag 3a1e420 „Read a driven video's speed on the frame clock so 1x stays 1x":

  • Ursache: Die Tempo-Messung eines gesteuerten Videos nahm ihre Uhrzeit beim Zeichnen aus performance.now(), die Animationszeit läuft aber mit dem Zeitstempel des Frames. Bei einem ausgelasteten Frame passten beide kurz nicht zusammen, eine 1×-Zeit wurde als bis zu 1,09 gelesen. Außerhalb von 0,9–1,1 ist das Video stumm, der Ton konnte also kurz aussetzen. In CI war deshalb e2e 66k ab und zu rot.
  • Fix: Die Messung nimmt jetzt document.timeline.currentTime, dieselbe Uhr wie die Zeit. debugInfo() liefert neu speed, und 66k prüft Tempo und Nachregeln getrennt.
Unit video-library            → 8 Tests grün (1 neu), Gegenprobe mit performance.now() rot (1,089)
e2e 66 (melektaus-atx)        → ALL PASSED, 27 Checks, speed in allen 5 Messungen 1,000
Gate (melektaus-atx)          → CHECK EXIT 0, 297 Dateien, 4562 Tests

Ob der Wackler in CI wirklich weg ist, zeigen erst die nächsten Läufe.

Nachtrag `3a1e420` „Read a driven video's speed on the frame clock so 1x stays 1x": - **Ursache:** Die Tempo-Messung eines gesteuerten Videos nahm ihre Uhrzeit beim Zeichnen aus `performance.now()`, die Animationszeit läuft aber mit dem Zeitstempel des Frames. Bei einem ausgelasteten Frame passten beide kurz nicht zusammen, eine 1×-Zeit wurde als bis zu 1,09 gelesen. Außerhalb von 0,9–1,1 ist das Video stumm, der Ton konnte also kurz aussetzen. In CI war deshalb e2e 66k ab und zu rot. - **Fix:** Die Messung nimmt jetzt `document.timeline.currentTime`, dieselbe Uhr wie die Zeit. `debugInfo()` liefert neu `speed`, und 66k prüft Tempo und Nachregeln getrennt. ``` Unit video-library → 8 Tests grün (1 neu), Gegenprobe mit performance.now() rot (1,089) e2e 66 (melektaus-atx) → ALL PASSED, 27 Checks, speed in allen 5 Messungen 1,000 Gate (melektaus-atx) → CHECK EXIT 0, 297 Dateien, 4562 Tests ``` Ob der Wackler in CI wirklich weg ist, zeigen erst die nächsten Läufe.
admin moved this to Done in Development on 2026-10-11 17:57:57 +02:00
Author
Owner

Nachtrag 36ffd9a (Wiki 4fdb69b): Ein Videobild bleibt nach einem Sprung nicht mehr leer.

Fehler

Beim Schneiden blieb ein gesteuertes Video manchmal leer, bis man auf ein anderes Frame und wieder zurück sprang.

Ursache

  • Seit b9fcf48 galt ein Sprung erst als fertig, wenn Chrome danach einen Videoframe liefert.
  • Für manche Sprünge liefert Chrome keinen. Der Player hat dann für immer gewartet, die View hat das Sprite nach 0,3 s weggelassen.
  • Erst der nächste Sprung brachte wieder einen Frame.

Fix

  • Ein Sprung gilt nach dem seeked als fertig, sobald der nächste Frame da ist, spätestens nach 150 ms. Schlimmster Fall ist also ein Bild, das bis zu 150 ms später kommt.
  • __gm.videos() meldet jetzt auch landing. So lässt sich ein leeres Bild von einem Player unterscheiden, der noch lädt.

Tests

  • Unit video-library: neuer Test mit einem Video, das nach dem Sprung keinen Frame liefert. Mit dem alten Code wird er rot.
  • e2e 66r schneidet zehnmal schnell aus und ein. Der Check war auch mit dem alten Code grün: In Chrome headless ließ sich der Fehler nicht nachstellen (Testvideo 320×180 in Software dekodiert, deine Videos 1080p60 H.264, vermutlich in Hardware).
  • Im Editor von dir bestätigt.
  • Gate auf melektaus-atx: CHECK EXIT 0, 4563 Tests.
Nachtrag `36ffd9a` (Wiki `4fdb69b`): **Ein Videobild bleibt nach einem Sprung nicht mehr leer.** ## Fehler Beim Schneiden blieb ein gesteuertes Video manchmal leer, bis man auf ein anderes Frame und wieder zurück sprang. ## Ursache - Seit `b9fcf48` galt ein Sprung erst als fertig, wenn Chrome danach einen Videoframe liefert. - Für manche Sprünge liefert Chrome keinen. Der Player hat dann für immer gewartet, die View hat das Sprite nach 0,3 s weggelassen. - Erst der nächste Sprung brachte wieder einen Frame. ## Fix - Ein Sprung gilt nach dem `seeked` als fertig, sobald der nächste Frame da ist, **spätestens nach 150 ms**. Schlimmster Fall ist also ein Bild, das bis zu 150 ms später kommt. - `__gm.videos()` meldet jetzt auch `landing`. So lässt sich ein leeres Bild von einem Player unterscheiden, der noch lädt. ## Tests - Unit `video-library`: neuer Test mit einem Video, das nach dem Sprung keinen Frame liefert. Mit dem alten Code wird er rot. - e2e 66r schneidet zehnmal schnell aus und ein. Der Check war auch mit dem alten Code grün: **In Chrome headless ließ sich der Fehler nicht nachstellen** (Testvideo 320×180 in Software dekodiert, deine Videos 1080p60 H.264, vermutlich in Hardware). - Im Editor von dir bestätigt. - Gate auf `melektaus-atx`: `CHECK EXIT 0`, 4563 Tests.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#404