UNPKG

@omnimedia/omnitool

Version:

open source video processing tools

97 lines (76 loc) 1.71 kB
import {Pub, pub} from "@e280/stz" import {ms, Ms} from "../../../units/ms.js" import {fps, Fps} from "../../../units/fps.js" export type RealtimeGenerator = { play(): void pause(): void setFPS(v: Fps): void isPlaying(): boolean ticks(): AsyncGenerator<void> onTick: Pub<[]> } export const realtime = (): RealtimeGenerator => { let playing = false let frameRate = fps(60) let frameDuration = 1000 / frameRate let lastNow = 0 let lastComposite = 0 const onTick = pub() let resolveTick: (() => void) | null = null const loop = (now: number) => { requestAnimationFrame(loop) if (!playing) return lastNow = now while (now - lastComposite >= frameDuration) { lastComposite += frameDuration resolveTick?.() resolveTick = null onTick() } } async function* ticks(): AsyncGenerator<void> { lastNow = performance.now() lastComposite = lastNow requestAnimationFrame(loop) while (true) { await new Promise<void>(r => resolveTick = r) yield } } return { play() { if (playing) return playing = true lastNow = performance.now() lastComposite = lastNow }, pause() { playing = false }, setFPS(v: Fps) { frameRate = v frameDuration = 1000 / frameRate }, isPlaying() { return playing }, ticks, onTick } } export type FixedStepOptions = { fps: Fps duration: Ms } export const fixedStep = async ( opts: FixedStepOptions, onFrame: (t: Ms, index: number) => Promise<void> | void ) => { const dt = ms(1000 / opts.fps) const durationInSeconds = opts.duration / 1000 const total = Math.ceil(durationInSeconds * opts.fps) for (let i = 0; i < total; i++) { const t = ms(i * dt) await onFrame(t, i) } }