UNPKG

@omnimedia/omnitool

Version:

open source video processing tools

94 lines (75 loc) 2.06 kB
import {ms} from "../../../units/ms.js" import {fps} from "../../../units/fps.js" import {Playback} from "./parts/playback.js" import {Driver} from "../../../driver/driver.js" import {TimelineFile} from "../../parts/basics.js" import {AudioLevelObserver} from "./parts/types.js" import {DecoderSource} from "../../../driver/fns/schematic.js" type ResolveMedia = (hash: string) => DecoderSource export class VideoPlayer { canvas: HTMLCanvasElement playback: Playback readonly audio: {levels: AudioLevelObserver} #pendingSeek: number | null = null #flushTask: Promise<void> | null = null constructor( private driver: Driver, resolveMedia: ResolveMedia, timeline: TimelineFile ) { this.playback = new Playback(driver, timeline, resolveMedia) this.audio = {levels: this.playback.audioLevels} this.canvas = driver.compositor.pixi.renderer.canvas } async play() { await this.playback.start() } pause() { this.playback.pause() } seek(timeMs: number) { this.#pendingSeek = timeMs return this.#flushTask ??= this.#flushSeeks().finally(() => this.#flushTask = null) } setFPS(value: number) { this.playback.setFps(fps(value)) } resize(width: number, height: number) { this.driver.resize(width, height) } get isSeeking() { return this.#flushTask !== null } get isPlaying() { return this.playback.isPlaying } get duration() { return this.playback.duration } get currentTime() { return this.playback.currentTime } get playbackRate() { return this.playback.playbackRate } set playbackRate(rate: number) { this.playback.playbackRate = rate } /** call this whenever your timeline state changes */ async update(timeline: TimelineFile) { this.playback.update(timeline) if (!this.isPlaying) await this.seek(this.currentTime) } async #flushSeeks() { while (this.#pendingSeek !== null) { const next = this.#pendingSeek this.#pendingSeek = null const layers = await this.playback.seek(ms(next)) const frame = await this.driver.composite(layers) frame.close() } } }