@omnimedia/omnitool 1.1.0-61 → 1.1.0-63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnimedia/omnitool",
3
- "version": "1.1.0-61",
3
+ "version": "1.1.0-63",
4
4
  "description": "open source video processing tools",
5
5
  "license": "MIT",
6
6
  "author": "Przemysław Gałęzki",
@@ -20,24 +20,9 @@ export async function playbackTest(timeline: TimelineFile, omni: Omni, root: HTM
20
20
  scrub.max = String(Math.ceil(player.duration))
21
21
 
22
22
  let isScrubbing = false
23
- let pendingSeek: number | null = null
24
- let seekInFlight = false
25
23
 
26
24
  player.playback.onTick.on(() => setScrubState(player.currentTime, player.duration))
27
25
 
28
- const queueSeek = async (timeMs: number) => {
29
- pendingSeek = timeMs
30
- if (seekInFlight)
31
- return
32
- seekInFlight = true
33
- while (pendingSeek) {
34
- const next = pendingSeek
35
- pendingSeek = null
36
- await player.seek(next)
37
- }
38
- seekInFlight = false
39
- }
40
-
41
26
  const updateTimecode = (currentMs: number, durationMs: number) => {
42
27
  timecode.textContent = `${formatTime(currentMs)} / ${formatTime(durationMs)}`
43
28
  }
@@ -46,13 +31,13 @@ export async function playbackTest(timeline: TimelineFile, omni: Omni, root: HTM
46
31
  isScrubbing = true
47
32
  const next = Math.max(0, Math.min(+scrub.value, player.duration))
48
33
  updateTimecode(next, player.duration)
49
- await queueSeek(next)
34
+ await player.seek(next)
50
35
  })
51
36
 
52
37
  scrub.addEventListener("change", async () => {
53
38
  isScrubbing = false
54
39
  const next = Math.max(0, Math.min(+scrub.value, player.duration))
55
- await queueSeek(next)
40
+ await player.seek(next)
56
41
  })
57
42
 
58
43
  const setScrubState = (timeMs: number, durationMs: number) => {
@@ -39,6 +39,14 @@ export class Playback {
39
39
  this.#samples()
40
40
  }
41
41
 
42
+ update(timeline: TimelineFile) {
43
+ this.timeline = timeline
44
+ }
45
+
46
+ get isPlaying() {
47
+ return this.#controller.isPlaying
48
+ }
49
+
42
50
  async #samples() {
43
51
  for await (const _ of this.#controller.ticks()) {
44
52
  const layers = await this.playVisualSampler?.next(this.currentTime) ?? []
@@ -57,11 +65,10 @@ export class Playback {
57
65
  return await this.seekVisualSampler.sample(this.timeline, time)
58
66
  }
59
67
 
60
- async start(timeline: TimelineFile) {
68
+ async start() {
61
69
  if(this.#controller.isPlaying())
62
70
  return
63
71
 
64
- this.timeline = timeline
65
72
  await this.audioContext.resume()
66
73
 
67
74
  this.#playbackStart = this.currentTime
@@ -12,33 +12,39 @@ export class VideoPlayer {
12
12
  canvas: HTMLCanvasElement
13
13
  playback: Playback
14
14
 
15
+ #pendingSeek: number | null = null
16
+ #flushTask: Promise<void> | null = null
17
+
15
18
  constructor(
16
19
  private driver: Driver,
17
20
  resolveMedia: ResolveMedia,
18
- private timeline: TimelineFile,
21
+ timeline: TimelineFile
19
22
  ) {
20
23
  this.playback = new Playback(driver, timeline, resolveMedia)
21
24
  this.canvas = driver.compositor.pixi.renderer.canvas
22
25
  }
23
26
 
24
27
  async play() {
25
- await this.playback.start(this.timeline)
28
+ await this.playback.start()
26
29
  }
27
30
 
28
31
  pause() {
29
32
  this.playback.pause()
30
33
  }
31
34
 
32
- async seek(timeMs: number) {
33
- const layers = await this.playback.seek(ms(timeMs))
34
- const frame = await this.driver.composite(layers)
35
- frame.close()
35
+ seek(timeMs: number) {
36
+ this.#pendingSeek = timeMs
37
+ return this.#flushTask ??= this.#flushSeeks().finally(() => this.#flushTask = null)
36
38
  }
37
39
 
38
40
  setFPS(value: number) {
39
41
  this.playback.setFps(fps(value))
40
42
  }
41
43
 
44
+ get isPlaying() {
45
+ return this.playback.isPlaying
46
+ }
47
+
42
48
  get duration() {
43
49
  return this.playback.duration
44
50
  }
@@ -50,10 +56,18 @@ export class VideoPlayer {
50
56
  /**
51
57
  call this whenever your timeline state changes
52
58
  */
53
- async update(timeline: TimelineFile) {
54
- this.timeline = timeline
59
+ update(timeline: TimelineFile) {
60
+ this.playback.update(timeline)
55
61
  }
56
62
 
63
+ async #flushSeeks() {
64
+ while (this.#pendingSeek !== null) {
65
+ const next = this.#pendingSeek
66
+ this.#pendingSeek = null
67
+ const layers = await this.playback.seek(ms(next))
68
+ const frame = await this.driver.composite(layers)
69
+ frame.close()
70
+ }
71
+ }
57
72
  }
58
73
 
59
- const toUrl = (src: DecoderSource) => (src instanceof Blob ? URL.createObjectURL(src) : String(src))