@omnimedia/omnitool 1.1.0-43 → 1.1.0-44
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 +17 -18
- package/s/demo/demo.bundle.ts +20 -40
- package/s/demo/routines/export-test.ts +8 -0
- package/s/demo/routines/playback-test.ts +21 -0
- package/s/demo/routines/timeline-setup.ts +24 -0
- package/s/demo/routines/transcode-test.ts +2 -0
- package/s/driver/driver-worker.ts +9 -0
- package/s/driver/driver.test.ts +1 -1
- package/s/driver/driver.ts +8 -22
- package/s/driver/fns/schematic.ts +6 -2
- package/s/driver/fns/work.ts +2 -133
- package/s/driver/parts/compositor.ts +178 -0
- package/s/driver/parts/machina.ts +19 -20
- package/s/index.ts +1 -0
- package/s/timeline/index.ts +1 -1
- package/s/timeline/parts/media.ts +11 -2
- package/s/timeline/parts/{compositor → renderers}/parts/html-tree.ts +2 -2
- package/s/timeline/parts/{compositor → renderers}/parts/tree-builder.ts +4 -2
- package/s/timeline/parts/{compositor → renderers}/playback.ts +4 -13
- package/s/timeline/parts/{compositor → renderers}/samplers/html.ts +19 -5
- package/s/timeline/parts/{compositor → renderers}/samplers/webcodecs.ts +1 -1
- package/s/timeline/parts/resource-pool.ts +8 -4
- package/s/timeline/parts/resource.ts +1 -0
- package/s/timeline/sugar/o.ts +8 -4
- package/s/timeline/sugar/omni-test.ts +14 -14
- package/s/timeline/sugar/omni.ts +2 -2
- package/s/timeline/utils/datafile.ts +14 -4
- package/s/timeline/utils/dummy-data.ts +3 -3
- package/x/demo/WebGLRenderer-Q3OV2JVE.js +2 -0
- package/x/demo/WebGLRenderer-Q3OV2JVE.js.map +7 -0
- package/x/demo/WebGPURenderer-FUFF62QA.js +2 -0
- package/x/demo/WebGPURenderer-FUFF62QA.js.map +7 -0
- package/x/demo/browserAll-PGQYU756.js +2 -0
- package/x/demo/browserAll-PGQYU756.js.map +7 -0
- package/x/demo/chunk-2RBLPWNG.js +393 -0
- package/x/demo/chunk-2RBLPWNG.js.map +7 -0
- package/x/demo/chunk-6DBMQOFE.js +42 -0
- package/x/demo/chunk-6DBMQOFE.js.map +7 -0
- package/x/demo/chunk-LAJHJD2S.js +2 -0
- package/x/demo/chunk-LAJHJD2S.js.map +7 -0
- package/x/demo/chunk-LQU5JKKZ.js +269 -0
- package/x/demo/chunk-LQU5JKKZ.js.map +7 -0
- package/x/demo/chunk-RFNLITDQ.js +327 -0
- package/x/demo/chunk-RFNLITDQ.js.map +7 -0
- package/x/demo/chunk-TBWCKYN2.js +2 -0
- package/x/demo/chunk-TBWCKYN2.js.map +7 -0
- package/x/demo/chunk-TLDBHU4V.js +15 -0
- package/x/demo/chunk-TLDBHU4V.js.map +7 -0
- package/x/demo/chunk-X2GHKWPJ.js +157 -0
- package/x/demo/chunk-X2GHKWPJ.js.map +7 -0
- package/x/demo/demo.bundle.js +18 -27
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +2378 -534
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/routines/export-test.d.ts +2 -0
- package/x/demo/routines/export-test.js +7 -0
- package/x/demo/routines/export-test.js.map +1 -0
- package/x/demo/routines/playback-test.d.ts +3 -0
- package/x/demo/routines/playback-test.js +17 -0
- package/x/demo/routines/playback-test.js.map +1 -0
- package/x/demo/routines/timeline-setup.d.ts +6 -0
- package/x/demo/routines/timeline-setup.js +13 -0
- package/x/demo/routines/timeline-setup.js.map +1 -0
- package/x/demo/routines/transcode-test.js +2 -0
- package/x/demo/routines/transcode-test.js.map +1 -1
- package/x/demo/webworkerAll-3YNCLHCR.js +2 -0
- package/x/demo/webworkerAll-3YNCLHCR.js.map +7 -0
- package/x/driver/WebGLRenderer-OMRWYQIV.js +2 -0
- package/x/driver/WebGLRenderer-OMRWYQIV.js.map +7 -0
- package/x/driver/WebGPURenderer-KQJB2OJJ.js +2 -0
- package/x/driver/WebGPURenderer-KQJB2OJJ.js.map +7 -0
- package/x/driver/browserAll-YBZEJCN3.js +2 -0
- package/x/driver/browserAll-YBZEJCN3.js.map +7 -0
- package/x/driver/chunk-3L3MB5NY.js +393 -0
- package/x/driver/chunk-3L3MB5NY.js.map +7 -0
- package/x/driver/chunk-42BQ4XKE.js +269 -0
- package/x/driver/chunk-42BQ4XKE.js.map +7 -0
- package/x/driver/chunk-4HAYG3N5.js +327 -0
- package/x/driver/chunk-4HAYG3N5.js.map +7 -0
- package/x/driver/chunk-BFBY7VYB.js +42 -0
- package/x/driver/chunk-BFBY7VYB.js.map +7 -0
- package/x/driver/chunk-KM6O72WE.js +157 -0
- package/x/driver/chunk-KM6O72WE.js.map +7 -0
- package/x/driver/chunk-N6HD4WYJ.js +2 -0
- package/x/driver/chunk-N6HD4WYJ.js.map +7 -0
- package/x/driver/chunk-WCZ2O3UN.js +15 -0
- package/x/driver/chunk-WCZ2O3UN.js.map +7 -0
- package/x/driver/chunk-XWNSF3WJ.js +2 -0
- package/x/driver/chunk-XWNSF3WJ.js.map +7 -0
- package/x/driver/driver-worker.d.ts +1 -0
- package/x/driver/driver-worker.js +6 -0
- package/x/driver/driver-worker.js.map +1 -0
- package/x/driver/driver.d.ts +5 -4
- package/x/driver/driver.js +10 -20
- package/x/driver/driver.js.map +1 -1
- package/x/driver/driver.test.js +1 -1
- package/x/driver/driver.test.js.map +1 -1
- package/x/driver/driver.worker.bundle.min.js +119 -3504
- package/x/driver/driver.worker.bundle.min.js.map +4 -4
- package/x/driver/fns/host.d.ts +1 -2
- package/x/driver/fns/schematic.d.ts +6 -1
- package/x/driver/fns/work.d.ts +2 -4
- package/x/driver/fns/work.js +1 -100
- package/x/driver/fns/work.js.map +1 -1
- package/x/driver/parts/compositor.d.ts +15 -0
- package/x/driver/parts/compositor.js +152 -0
- package/x/driver/parts/compositor.js.map +1 -0
- package/x/driver/parts/machina.d.ts +0 -20
- package/x/driver/parts/machina.js +6 -10
- package/x/driver/parts/machina.js.map +1 -1
- package/x/driver/webworkerAll-BKJQW6P7.js +2 -0
- package/x/driver/webworkerAll-BKJQW6P7.js.map +7 -0
- package/x/features/speech/transcribe/parts/prep-audio.d.ts +1 -1
- package/x/features/speech/transcribe/worker.bundle.min.js +899 -899
- package/x/features/speech/transcribe/worker.bundle.min.js.map +4 -4
- package/x/index.d.ts +1 -0
- package/x/index.html +2 -2
- package/x/index.js +1 -0
- package/x/index.js.map +1 -1
- package/x/timeline/index.d.ts +1 -1
- package/x/timeline/index.js +1 -1
- package/x/timeline/index.js.map +1 -1
- package/x/timeline/parts/filmstrip.d.ts +1 -1
- package/x/timeline/parts/media.d.ts +2 -0
- package/x/timeline/parts/media.js +10 -2
- package/x/timeline/parts/media.js.map +1 -1
- package/x/timeline/parts/renderers/export.js.map +1 -0
- package/x/timeline/parts/{compositor → renderers}/parts/html-tree.js +2 -2
- package/x/timeline/parts/renderers/parts/html-tree.js.map +1 -0
- package/x/timeline/parts/renderers/parts/schedulers.js.map +1 -0
- package/x/timeline/parts/{compositor → renderers}/parts/tree-builder.js +4 -2
- package/x/timeline/parts/renderers/parts/tree-builder.js.map +1 -0
- package/x/timeline/parts/renderers/parts/webcodecs-tree.js.map +1 -0
- package/x/timeline/parts/{compositor → renderers}/playback.d.ts +2 -4
- package/x/timeline/parts/{compositor → renderers}/playback.js +5 -14
- package/x/timeline/parts/renderers/playback.js.map +1 -0
- package/x/timeline/parts/{compositor → renderers}/samplers/html.js +14 -5
- package/x/timeline/parts/renderers/samplers/html.js.map +1 -0
- package/x/timeline/parts/{compositor → renderers}/samplers/webcodecs.js +1 -1
- package/x/timeline/parts/renderers/samplers/webcodecs.js.map +1 -0
- package/x/timeline/parts/resource-pool.d.ts +2 -0
- package/x/timeline/parts/resource-pool.js +7 -4
- package/x/timeline/parts/resource-pool.js.map +1 -1
- package/x/timeline/parts/resource.d.ts +1 -0
- package/x/timeline/sugar/o.d.ts +4 -1
- package/x/timeline/sugar/o.js +4 -4
- package/x/timeline/sugar/o.js.map +1 -1
- package/x/timeline/sugar/omni-test.js +14 -7
- package/x/timeline/sugar/omni-test.js.map +1 -1
- package/x/timeline/sugar/omni.d.ts +1 -2
- package/x/timeline/sugar/omni.js +2 -2
- package/x/timeline/sugar/omni.js.map +1 -1
- package/x/timeline/utils/datafile.d.ts +4 -3
- package/x/timeline/utils/datafile.js +16 -5
- package/x/timeline/utils/datafile.js.map +1 -1
- package/x/timeline/utils/dummy-data.d.ts +1 -2
- package/x/timeline/utils/dummy-data.js +4 -2
- package/x/timeline/utils/dummy-data.js.map +1 -1
- package/x/timeline/parts/compositor/export.js.map +0 -1
- package/x/timeline/parts/compositor/parts/html-tree.js.map +0 -1
- package/x/timeline/parts/compositor/parts/schedulers.js.map +0 -1
- package/x/timeline/parts/compositor/parts/tree-builder.js.map +0 -1
- package/x/timeline/parts/compositor/parts/webcodecs-tree.js.map +0 -1
- package/x/timeline/parts/compositor/playback.js.map +0 -1
- package/x/timeline/parts/compositor/samplers/html.js.map +0 -1
- package/x/timeline/parts/compositor/samplers/webcodecs.js.map +0 -1
- /package/s/timeline/parts/{compositor → renderers}/export.ts +0 -0
- /package/s/timeline/parts/{compositor → renderers}/parts/schedulers.ts +0 -0
- /package/s/timeline/parts/{compositor → renderers}/parts/webcodecs-tree.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/export.d.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/export.js +0 -0
- /package/x/timeline/parts/{compositor → renderers}/parts/html-tree.d.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/parts/schedulers.d.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/parts/schedulers.js +0 -0
- /package/x/timeline/parts/{compositor → renderers}/parts/tree-builder.d.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/parts/webcodecs-tree.d.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/parts/webcodecs-tree.js +0 -0
- /package/x/timeline/parts/{compositor → renderers}/samplers/html.d.ts +0 -0
- /package/x/timeline/parts/{compositor → renderers}/samplers/webcodecs.d.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnimedia/omnitool",
|
|
3
|
-
"version": "1.1.0-
|
|
3
|
+
"version": "1.1.0-44",
|
|
4
4
|
"description": "open source video processing tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Przemysław Gałęzki",
|
|
@@ -23,29 +23,28 @@
|
|
|
23
23
|
"test-debug": "node inspect x/tests.test.js"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@e280/science": "^0.
|
|
27
|
-
"@e280/scute": "^0.
|
|
28
|
-
"@types/node": "^
|
|
26
|
+
"@e280/science": "^0.1.4",
|
|
27
|
+
"@e280/scute": "^0.1.2",
|
|
28
|
+
"@types/node": "^25.0.3",
|
|
29
29
|
"http-server": "^14.1.1",
|
|
30
30
|
"npm-run-all": "^4.1.5",
|
|
31
|
-
"typescript": "^5.9.
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@e280/comrade": "^0.
|
|
35
|
-
"@e280/renraku": "^0.5.
|
|
36
|
-
"@e280/sly": "^0.2.
|
|
37
|
-
"@e280/strata": "^0.2.
|
|
38
|
-
"@e280/stz": "^0.2.
|
|
39
|
-
"@huggingface/transformers": "^3.
|
|
34
|
+
"@e280/comrade": "^0.1.0",
|
|
35
|
+
"@e280/renraku": "^0.5.3",
|
|
36
|
+
"@e280/sly": "^0.2.5",
|
|
37
|
+
"@e280/strata": "^0.2.5",
|
|
38
|
+
"@e280/stz": "^0.2.15",
|
|
39
|
+
"@huggingface/transformers": "^3.8.1",
|
|
40
40
|
"comrade": "^0.0.3",
|
|
41
41
|
"gl-transitions": "^1.43.0",
|
|
42
|
-
"gsap": "^3.
|
|
43
|
-
"lit": "^3.3.
|
|
44
|
-
"mediabunny": "^1.
|
|
45
|
-
"mp4-muxer": "^5.2.
|
|
46
|
-
"pixi.js": "^8.
|
|
47
|
-
"wavesurfer.js": "^7.
|
|
48
|
-
"web-demuxer": "^2.3.8"
|
|
42
|
+
"gsap": "^3.14.2",
|
|
43
|
+
"lit": "^3.3.2",
|
|
44
|
+
"mediabunny": "^1.27.3",
|
|
45
|
+
"mp4-muxer": "^5.2.2",
|
|
46
|
+
"pixi.js": "^8.14.3",
|
|
47
|
+
"wavesurfer.js": "^7.12.1"
|
|
49
48
|
},
|
|
50
49
|
"homepage": "https://github.com/omni-media/omnitool#readme",
|
|
51
50
|
"repository": {
|
package/s/demo/demo.bundle.ts
CHANGED
|
@@ -1,48 +1,20 @@
|
|
|
1
1
|
|
|
2
2
|
import {Driver} from "../driver/driver.js"
|
|
3
|
+
import {exportTest} from "./routines/export-test.js"
|
|
4
|
+
import {playbackTest} from "./routines/playback-test.js"
|
|
3
5
|
import {waveformTest} from "./routines/waveform-test.js"
|
|
6
|
+
import {TimelineSchemaTest } from "./routines/timeline-setup.js"
|
|
4
7
|
import {filmstripTest} from "./routines/filmstrip-test.js"
|
|
5
|
-
import {transcriberTest} from "./routines/transcriber-test.js"
|
|
6
8
|
import {setupTranscodeTest} from "./routines/transcode-test.js"
|
|
7
|
-
import {Datafile, Omni, VideoPlayer} from "../timeline/index.js"
|
|
8
9
|
|
|
9
10
|
const driver = await Driver.setup({workerUrl: new URL("../driver/driver.worker.bundle.min.js", import.meta.url)})
|
|
10
11
|
const results = document.querySelector(".results")!
|
|
11
12
|
|
|
12
13
|
const fetchButton = document.querySelector(".fetch")
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const playButton = document.querySelector(".play") as HTMLButtonElement
|
|
16
|
-
const stopButton = document.querySelector(".stop") as HTMLButtonElement
|
|
17
|
-
const seekButton = document.querySelector(".seek") as HTMLButtonElement
|
|
14
|
+
const fileInput = document.querySelector(".file-input") as HTMLInputElement
|
|
18
15
|
|
|
19
16
|
fetchButton?.addEventListener("click", startDemoFetch)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const omni = new Omni(driver)
|
|
23
|
-
const file = await fetch("/assets/temp/gl.mp4")
|
|
24
|
-
const buffer = await file.arrayBuffer()
|
|
25
|
-
const uint = new Uint8Array(buffer)
|
|
26
|
-
|
|
27
|
-
const {videoA} = await omni.load({videoA: Datafile.make(uint)})
|
|
28
|
-
const timeline = omni.timeline(o =>
|
|
29
|
-
o.sequence(
|
|
30
|
-
o.stack(
|
|
31
|
-
o.video(videoA, {duration: 5000}),
|
|
32
|
-
o.audio(videoA, {duration: 8000})
|
|
33
|
-
),
|
|
34
|
-
o.video(videoA, {duration: 7000})
|
|
35
|
-
))
|
|
36
|
-
|
|
37
|
-
const player = await VideoPlayer.create(driver, timeline)
|
|
38
|
-
document.body.appendChild(player.canvas)
|
|
39
|
-
|
|
40
|
-
playButton.addEventListener("click", () => player.play())
|
|
41
|
-
stopButton.addEventListener("click", () => player.pause())
|
|
42
|
-
seekButton.addEventListener("change", async (e: Event) => {
|
|
43
|
-
const target = e.target as HTMLInputElement
|
|
44
|
-
await player.seek(+target.value)
|
|
45
|
-
})
|
|
17
|
+
fileInput?.addEventListener("input", startDemoImport)
|
|
46
18
|
|
|
47
19
|
waveformTest(driver)
|
|
48
20
|
// const transcriber = await transcriberTest(driver)
|
|
@@ -55,14 +27,22 @@ waveformTest(driver)
|
|
|
55
27
|
}
|
|
56
28
|
|
|
57
29
|
// transcoding tests
|
|
58
|
-
async function startDemoImport()
|
|
30
|
+
async function startDemoImport(e: Event)
|
|
59
31
|
{
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
32
|
+
const file = fileInput.files?.[0]
|
|
33
|
+
if(file) {
|
|
34
|
+
const transcode = setupTranscodeTest(driver, file)
|
|
35
|
+
await filmstripTest(file)
|
|
36
|
+
run(transcode, file.name)
|
|
37
|
+
// await transcriber.transcribe(file)
|
|
38
|
+
|
|
39
|
+
const {timeline, omni} = await TimelineSchemaTest(driver, file)
|
|
40
|
+
|
|
41
|
+
playbackTest(driver, timeline, omni)
|
|
42
|
+
exportTest(omni, timeline)
|
|
43
|
+
}
|
|
44
|
+
// const [fileHandle] = await window.showOpenFilePicker()
|
|
45
|
+
// const file = await fileHandle.getFile()
|
|
66
46
|
}
|
|
67
47
|
|
|
68
48
|
async function startDemoFetch()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import {Omni, TimelineFile} from "../../timeline/index.js"
|
|
2
|
+
|
|
3
|
+
export function exportTest(omni: Omni, timeline: TimelineFile) {
|
|
4
|
+
const exportButton = document.querySelector(".export") as HTMLButtonElement
|
|
5
|
+
exportButton!.addEventListener("click", () => {
|
|
6
|
+
omni.render(timeline).then(() => console.log("done"))
|
|
7
|
+
})
|
|
8
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import {Driver} from "../../driver/driver.js"
|
|
3
|
+
import {O, Omni, TimelineFile, VideoPlayer} from "../../timeline/index.js"
|
|
4
|
+
|
|
5
|
+
export async function playbackTest(driver: Driver, timeline: TimelineFile, omni: Omni) {
|
|
6
|
+
const playButton = document.querySelector(".play") as HTMLButtonElement
|
|
7
|
+
const stopButton = document.querySelector(".stop") as HTMLButtonElement
|
|
8
|
+
const seekButton = document.querySelector(".seek") as HTMLButtonElement
|
|
9
|
+
const o = new O({project: timeline})
|
|
10
|
+
const player = await VideoPlayer.create(driver, timeline, (hash) => omni.resources.require(hash).url)
|
|
11
|
+
document.body.appendChild(player.canvas)
|
|
12
|
+
|
|
13
|
+
playButton.addEventListener("click", () => player.play())
|
|
14
|
+
stopButton.addEventListener("click", () => player.pause())
|
|
15
|
+
seekButton.addEventListener("change", async (e: Event) => {
|
|
16
|
+
const target = e.target as HTMLInputElement
|
|
17
|
+
await player.seek(+target.value)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
player.update(o.state.project)
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
import {Driver} from "../../driver/driver.js"
|
|
3
|
+
import {Datafile, Item, Omni} from "../../timeline/index.js"
|
|
4
|
+
|
|
5
|
+
export async function TimelineSchemaTest(driver: Driver, file: File) {
|
|
6
|
+
const omni = new Omni(driver)
|
|
7
|
+
const {videoA} = await omni.load({videoA: Datafile.make(file)})
|
|
8
|
+
const timeline = omni.timeline(o => {
|
|
9
|
+
const text = o.text("content", {duration: 1000})
|
|
10
|
+
const style = o.textStyle({fill: "green", fontSize: 100})
|
|
11
|
+
o.set<Item.Text>(text.id, {styleId: style.id})
|
|
12
|
+
|
|
13
|
+
return o.sequence(
|
|
14
|
+
o.stack(
|
|
15
|
+
text,
|
|
16
|
+
o.video(videoA, {duration: 3000, start: 3000}),
|
|
17
|
+
o.audio(videoA, {duration: 1000, start: 3000})
|
|
18
|
+
),
|
|
19
|
+
o.gap(500),
|
|
20
|
+
o.video(videoA, {duration: 7000, start: 5000})
|
|
21
|
+
)})
|
|
22
|
+
|
|
23
|
+
return {timeline, omni}
|
|
24
|
+
}
|
|
@@ -15,10 +15,12 @@ export function setupTranscodeTest(driver: Driver, source: DecoderSource) {
|
|
|
15
15
|
async onFrame(frame) {
|
|
16
16
|
const composed = await driver.composite([
|
|
17
17
|
{
|
|
18
|
+
id: 0,
|
|
18
19
|
kind: "image",
|
|
19
20
|
frame
|
|
20
21
|
},
|
|
21
22
|
{
|
|
23
|
+
id: 1,
|
|
22
24
|
kind: "text",
|
|
23
25
|
content: "omnitool",
|
|
24
26
|
style: {
|
package/s/driver/driver.test.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {Science, test, expect} from "@e280/science"
|
|
|
5
5
|
const workerUrl = new URL("./driver.worker.bundle.js", import.meta.url)
|
|
6
6
|
|
|
7
7
|
export default Science.suite({
|
|
8
|
-
"driver hello world": test(async() => {
|
|
8
|
+
"driver hello world": test.skip(async() => {
|
|
9
9
|
const driver = await Driver.setup({workerUrl})
|
|
10
10
|
expect(driver.machina.count).is(0)
|
|
11
11
|
await driver.thread.work.hello()
|
package/s/driver/driver.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {Comrade, tune, Thread} from "@e280/comrade"
|
|
|
2
2
|
import {ALL_FORMATS, Input, type StreamTargetChunk} from "mediabunny"
|
|
3
3
|
|
|
4
4
|
import {Machina} from "./parts/machina.js"
|
|
5
|
+
import {Compositor} from "./parts/compositor.js"
|
|
5
6
|
import {setupDriverHost} from "./fns/host.js"
|
|
6
7
|
import {loadDecoderSource} from "./utils/load-decoder-source.js"
|
|
7
8
|
import {DecoderInput, DriverSchematic, Composition, EncoderInput, DecoderSource} from "./fns/schematic.js"
|
|
@@ -18,12 +19,14 @@ export class Driver {
|
|
|
18
19
|
workerUrl: options?.workerUrl ?? "/node_modules/@omnimedia/omnitool/x/driver/driver.worker.bundle.min.js",
|
|
19
20
|
setupHost: setupDriverHost(machina),
|
|
20
21
|
})
|
|
21
|
-
|
|
22
|
+
const compositor = await Compositor.setup()
|
|
23
|
+
return new this(machina, thread, compositor)
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
constructor(
|
|
25
27
|
public machina: Machina,
|
|
26
|
-
public thread: Thread<DriverSchematic
|
|
28
|
+
public thread: Thread<DriverSchematic>,
|
|
29
|
+
public compositor: Compositor
|
|
27
30
|
) {}
|
|
28
31
|
|
|
29
32
|
async hello() {
|
|
@@ -37,7 +40,8 @@ export class Driver {
|
|
|
37
40
|
})
|
|
38
41
|
|
|
39
42
|
const audioTrack = await input.getPrimaryAudioTrack()
|
|
40
|
-
|
|
43
|
+
if (!audioTrack) throw new Error("primary audio track not found")
|
|
44
|
+
return await audioTrack.computeDuration()
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
async getVideoDuration(source: DecoderSource) {
|
|
@@ -99,26 +103,8 @@ export class Driver {
|
|
|
99
103
|
async composite(
|
|
100
104
|
composition: Composition,
|
|
101
105
|
) {
|
|
102
|
-
|
|
103
|
-
return await this.thread.work.composite[tune]({transfer})(composition)
|
|
106
|
+
return await this.compositor.composite(composition)
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
#collectTransferablesFromComposition(composition: Composition) {
|
|
107
|
-
const transferables: Transferable[] = []
|
|
108
|
-
|
|
109
|
-
const visit = (node: Composition) => {
|
|
110
|
-
if (Array.isArray(node)) {
|
|
111
|
-
for (const child of node)
|
|
112
|
-
visit(child)
|
|
113
|
-
}
|
|
114
|
-
else if (node && typeof node === 'object' && 'kind' in node) {
|
|
115
|
-
if (node.kind === 'image' && node.frame instanceof VideoFrame)
|
|
116
|
-
transferables.push(node.frame)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
visit(composition)
|
|
121
|
-
return transferables
|
|
122
|
-
}
|
|
123
109
|
}
|
|
124
110
|
|
|
@@ -4,6 +4,7 @@ import {AsSchematic} from "@e280/comrade"
|
|
|
4
4
|
import type {AudioEncodingConfig, StreamTargetChunk, VideoEncodingConfig} from "mediabunny"
|
|
5
5
|
|
|
6
6
|
import {Mat6} from "../../timeline/utils/matrix.js"
|
|
7
|
+
import {Id} from "../../timeline/index.js"
|
|
7
8
|
|
|
8
9
|
export type DriverSchematic = AsSchematic<{
|
|
9
10
|
|
|
@@ -26,8 +27,6 @@ export type DriverSchematic = AsSchematic<{
|
|
|
26
27
|
}): Promise<void>
|
|
27
28
|
|
|
28
29
|
encode(input: EncoderInput & {bridge: WritableStream<StreamTargetChunk>}): Promise<void>
|
|
29
|
-
|
|
30
|
-
composite(input: Composition): Promise<VideoFrame>
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
// happens on the main thread
|
|
@@ -73,6 +72,7 @@ export interface MuxOpts {
|
|
|
73
72
|
export type Composition = Layer | (Layer | Composition)[]
|
|
74
73
|
|
|
75
74
|
export type TextLayer = {
|
|
75
|
+
id: Id
|
|
76
76
|
kind: 'text'
|
|
77
77
|
content: string
|
|
78
78
|
style?: TextStyleOptions
|
|
@@ -80,12 +80,14 @@ export type TextLayer = {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export type ImageLayer = {
|
|
83
|
+
id: Id
|
|
83
84
|
kind: 'image'
|
|
84
85
|
frame: VideoFrame
|
|
85
86
|
matrix?: Mat6
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
export type TransitionLayer = {
|
|
90
|
+
id: Id
|
|
89
91
|
kind: 'transition'
|
|
90
92
|
name: string
|
|
91
93
|
progress: number
|
|
@@ -94,10 +96,12 @@ export type TransitionLayer = {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
export type GapLayer = {
|
|
99
|
+
id: Id
|
|
97
100
|
kind: 'gap'
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
export type Audio = {
|
|
104
|
+
id: Id
|
|
101
105
|
kind: "audio"
|
|
102
106
|
data: AudioData
|
|
103
107
|
}
|
package/s/driver/fns/work.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {Comrade} from "@e280/comrade"
|
|
2
|
-
import {
|
|
2
|
+
import {Sprite, Text, Texture, DOMAdapter, WebWorkerAdapter} from "pixi.js"
|
|
3
3
|
import {Input, ALL_FORMATS, VideoSampleSink, Output, Mp4OutputFormat, VideoSampleSource, VideoSample, AudioSampleSink, AudioSampleSource, AudioSample, StreamTarget, BlobSource, UrlSource} from "mediabunny"
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {makeTransition} from "../../features/transition/transition.js"
|
|
7
|
-
import {Composition, DecoderSource, DriverSchematic, Layer} from "./schematic.js"
|
|
5
|
+
import {DecoderSource, DriverSchematic} from "./schematic.js"
|
|
8
6
|
|
|
9
7
|
DOMAdapter.set(WebWorkerAdapter)
|
|
10
8
|
|
|
@@ -111,136 +109,7 @@ export const setupDriverWork = (
|
|
|
111
109
|
await Promise.all(promises)
|
|
112
110
|
await output.finalize()
|
|
113
111
|
},
|
|
114
|
-
|
|
115
|
-
async composite(composition) {
|
|
116
|
-
const {stage, renderer} = await renderPIXI(1920, 1080)
|
|
117
|
-
stage.removeChildren()
|
|
118
|
-
|
|
119
|
-
const {dispose} = await renderLayer(composition, stage)
|
|
120
|
-
renderer.render(stage)
|
|
121
|
-
|
|
122
|
-
// make sure browser support webgl/webgpu otherwise it might take much longer to construct frame
|
|
123
|
-
// if its very slow on eg edge try chrome
|
|
124
|
-
const frame = new VideoFrame(renderer.canvas, {
|
|
125
|
-
timestamp: 0,
|
|
126
|
-
duration: 0,
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
renderer.clear()
|
|
130
|
-
dispose()
|
|
131
|
-
|
|
132
|
-
shell.transfer = [frame]
|
|
133
|
-
return frame
|
|
134
|
-
}
|
|
135
112
|
}))
|
|
136
113
|
)
|
|
137
114
|
|
|
138
|
-
// TODO suspicious global, probably bad
|
|
139
|
-
let pixi: {
|
|
140
|
-
renderer: Renderer
|
|
141
|
-
stage: Container
|
|
142
|
-
} | null = null
|
|
143
|
-
|
|
144
|
-
async function renderPIXI(width: number, height: number) {
|
|
145
|
-
if (pixi)
|
|
146
|
-
return pixi
|
|
147
|
-
|
|
148
|
-
const renderer = await autoDetectRenderer({
|
|
149
|
-
width,
|
|
150
|
-
height,
|
|
151
|
-
preference: "webgl", // webgl and webgl2 causes memory leaks on chrome
|
|
152
|
-
background: "black",
|
|
153
|
-
preferWebGLVersion: 2
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
const stage = new Container()
|
|
157
|
-
pixi = {renderer, stage}
|
|
158
|
-
|
|
159
|
-
return pixi
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const transitions: Map<string, ReturnType<typeof makeTransition>> = new Map()
|
|
163
|
-
|
|
164
115
|
type RenderableObject = Sprite | Text | Texture
|
|
165
|
-
|
|
166
|
-
async function renderLayer(
|
|
167
|
-
layer: Layer | Composition,
|
|
168
|
-
parent: Container,
|
|
169
|
-
) {
|
|
170
|
-
if (Array.isArray(layer)) {
|
|
171
|
-
layer.reverse()
|
|
172
|
-
const disposers: (() => void)[] = []
|
|
173
|
-
for (const child of layer) {
|
|
174
|
-
const result = await renderLayer(child, parent)
|
|
175
|
-
disposers.push(result.dispose)
|
|
176
|
-
}
|
|
177
|
-
return {dispose: () => disposers.forEach(d => d())}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
switch (layer.kind) {
|
|
181
|
-
case 'text':
|
|
182
|
-
return renderTextLayer(layer, parent)
|
|
183
|
-
case 'image':
|
|
184
|
-
return renderImageLayer(layer, parent)
|
|
185
|
-
case 'transition':
|
|
186
|
-
return renderTransitionLayer(layer, parent)
|
|
187
|
-
case 'gap': {
|
|
188
|
-
pixi?.renderer.clear()
|
|
189
|
-
return {dispose: () => {}}
|
|
190
|
-
}
|
|
191
|
-
default:
|
|
192
|
-
console.warn('Unknown layer kind', (layer as any).kind)
|
|
193
|
-
return {dispose: () => {}}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function renderTextLayer(
|
|
198
|
-
layer: Extract<Layer, {kind: 'text'}>,
|
|
199
|
-
parent: Container,
|
|
200
|
-
) {
|
|
201
|
-
const text = new Text({
|
|
202
|
-
text: layer.content,
|
|
203
|
-
style: layer.style
|
|
204
|
-
})
|
|
205
|
-
applyTransform(text, layer.matrix)
|
|
206
|
-
parent.addChild(text)
|
|
207
|
-
return {dispose: () => text.destroy(true)}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function renderImageLayer(
|
|
211
|
-
layer: Extract<Layer, {kind: 'image'}>,
|
|
212
|
-
parent: Container,
|
|
213
|
-
) {
|
|
214
|
-
const texture = Texture.from(layer.frame)
|
|
215
|
-
const sprite = new Sprite(texture)
|
|
216
|
-
applyTransform(sprite, layer.matrix)
|
|
217
|
-
parent.addChild(sprite)
|
|
218
|
-
return {dispose: () => {
|
|
219
|
-
sprite.destroy(true)
|
|
220
|
-
texture.destroy(true)
|
|
221
|
-
layer.frame.close()
|
|
222
|
-
}}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function renderTransitionLayer(
|
|
226
|
-
{from, to, progress, name}: Extract<Layer, {kind: 'transition'}>,
|
|
227
|
-
parent: Container,
|
|
228
|
-
) {
|
|
229
|
-
const transition = transitions.get(name) ??
|
|
230
|
-
(transitions.set(name, makeTransition({
|
|
231
|
-
name: "circle",
|
|
232
|
-
renderer: pixi!.renderer
|
|
233
|
-
})),
|
|
234
|
-
transitions.get(name)!
|
|
235
|
-
)
|
|
236
|
-
const texture = transition.render({from, to, progress, width: from.displayWidth, height: from.displayHeight})
|
|
237
|
-
const sprite = new Sprite(texture)
|
|
238
|
-
parent.addChild(sprite)
|
|
239
|
-
return {dispose: () => sprite.destroy(false)}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function applyTransform(target: Sprite | Text, worldMatrix?: Mat6) {
|
|
243
|
-
if (!worldMatrix) return
|
|
244
|
-
const mx = mat6ToMatrix(worldMatrix)
|
|
245
|
-
target.setFromMatrix(mx)
|
|
246
|
-
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import {autoDetectRenderer, Container, Renderer, Sprite, Text, Texture} from "pixi.js"
|
|
2
|
+
|
|
3
|
+
import {Composition, Layer} from "../fns/schematic.js"
|
|
4
|
+
import {Mat6, mat6ToMatrix} from "../../timeline/utils/matrix.js"
|
|
5
|
+
import {makeTransition} from "../../features/transition/transition.js"
|
|
6
|
+
|
|
7
|
+
export class Compositor {
|
|
8
|
+
|
|
9
|
+
static async setup() {
|
|
10
|
+
const renderer = await autoDetectRenderer({
|
|
11
|
+
width: 1920,
|
|
12
|
+
height: 1080,
|
|
13
|
+
preference: "webgl", // webgl and webgl2 causes memory leaks on chrome
|
|
14
|
+
background: "black",
|
|
15
|
+
preferWebGLVersion: 2
|
|
16
|
+
})
|
|
17
|
+
const stage = new Container()
|
|
18
|
+
stage.interactive = true
|
|
19
|
+
return new this({renderer, stage})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
constructor(public pixi: {renderer: Renderer, stage: Container}) {}
|
|
23
|
+
|
|
24
|
+
#transitions: Map<string, ReturnType<typeof makeTransition>> = new Map()
|
|
25
|
+
#objects = new Map<number, Container>()
|
|
26
|
+
|
|
27
|
+
async composite(
|
|
28
|
+
composition: Composition,
|
|
29
|
+
) {
|
|
30
|
+
const {stage, renderer} = this.pixi
|
|
31
|
+
|
|
32
|
+
this.#cleanup(this.#collectIds(composition))
|
|
33
|
+
const {dispose} = await this.#renderLayer(composition, stage)
|
|
34
|
+
renderer.render(stage)
|
|
35
|
+
|
|
36
|
+
// make sure browser support webgl/webgpu otherwise it might take much longer to construct frame
|
|
37
|
+
// if its very slow on eg edge try chrome
|
|
38
|
+
const frame = new VideoFrame(renderer.canvas, {
|
|
39
|
+
timestamp: 0,
|
|
40
|
+
duration: 0,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
dispose()
|
|
44
|
+
|
|
45
|
+
return frame
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async #renderLayer(
|
|
49
|
+
layer: Layer | Composition,
|
|
50
|
+
parent: Container,
|
|
51
|
+
) {
|
|
52
|
+
if (Array.isArray(layer)) {
|
|
53
|
+
layer.reverse()
|
|
54
|
+
const disposers: (() => void)[] = []
|
|
55
|
+
for (const child of layer) {
|
|
56
|
+
const result = await this.#renderLayer(child, parent)
|
|
57
|
+
disposers.push(result.dispose)
|
|
58
|
+
}
|
|
59
|
+
return {dispose: () => disposers.forEach(d => d())}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
switch (layer.kind) {
|
|
63
|
+
case 'text':
|
|
64
|
+
return this.#renderTextLayer(layer, parent)
|
|
65
|
+
case 'image':
|
|
66
|
+
return this.#renderImageLayer(layer, parent)
|
|
67
|
+
case 'transition':
|
|
68
|
+
return this.#renderTransitionLayer(layer, parent)
|
|
69
|
+
case 'gap': {
|
|
70
|
+
this.pixi?.renderer.clear()
|
|
71
|
+
return {dispose: () => {}}
|
|
72
|
+
}
|
|
73
|
+
default:
|
|
74
|
+
console.warn('Unknown layer kind', (layer as any).kind)
|
|
75
|
+
return {dispose: () => {}}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#renderTextLayer(
|
|
80
|
+
layer: Extract<Layer, {kind: 'text'}>,
|
|
81
|
+
parent: Container,
|
|
82
|
+
) {
|
|
83
|
+
const text = this.#findOrCreate<Text>(layer)!
|
|
84
|
+
this.#applyTransform(text, layer.matrix)
|
|
85
|
+
parent.addChild(text)
|
|
86
|
+
return {
|
|
87
|
+
dispose: () => {}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#renderImageLayer(
|
|
92
|
+
layer: Extract<Layer, {kind: 'image'}>,
|
|
93
|
+
parent: Container,
|
|
94
|
+
) {
|
|
95
|
+
const texture = Texture.from(layer.frame)
|
|
96
|
+
const sprite = this.#findOrCreate<Sprite>(layer)!
|
|
97
|
+
sprite.texture = texture
|
|
98
|
+
this.#applyTransform(sprite, layer.matrix)
|
|
99
|
+
parent.addChild(sprite)
|
|
100
|
+
return {
|
|
101
|
+
dispose: () => {
|
|
102
|
+
texture.destroy(true)
|
|
103
|
+
layer.frame.close()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#renderTransitionLayer(
|
|
109
|
+
{from, to, progress, name}: Extract<Layer, {kind: 'transition'}>,
|
|
110
|
+
parent: Container,
|
|
111
|
+
) {
|
|
112
|
+
const transition = this.#transitions.get(name) ??
|
|
113
|
+
(this.#transitions.set(name, makeTransition({
|
|
114
|
+
name: "circle",
|
|
115
|
+
renderer: this.pixi.renderer
|
|
116
|
+
})),
|
|
117
|
+
this.#transitions.get(name)!
|
|
118
|
+
)
|
|
119
|
+
const texture = transition.render({from, to, progress, width: from.displayWidth, height: from.displayHeight})
|
|
120
|
+
const sprite = new Sprite(texture)
|
|
121
|
+
parent.addChild(sprite)
|
|
122
|
+
return {dispose: () => sprite.destroy(false)}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#applyTransform(target: Sprite | Text, worldMatrix?: Mat6) {
|
|
126
|
+
if (!worldMatrix) return
|
|
127
|
+
const mx = mat6ToMatrix(worldMatrix)
|
|
128
|
+
target.setFromMatrix(mx)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#findOrCreate<T = Container>(layer: Layer) {
|
|
132
|
+
const object = this.#objects.get(layer.id)
|
|
133
|
+
if(!object) {
|
|
134
|
+
switch (layer.kind) {
|
|
135
|
+
case 'text': {
|
|
136
|
+
const text = new Text({
|
|
137
|
+
text: layer.content,
|
|
138
|
+
style: layer.style
|
|
139
|
+
})
|
|
140
|
+
text.onmouseenter = () => console.log("enter text")
|
|
141
|
+
return this.#objects
|
|
142
|
+
.set(layer.id, text)
|
|
143
|
+
.get(layer.id) as T
|
|
144
|
+
}
|
|
145
|
+
case 'image': {
|
|
146
|
+
const sprite = new Sprite()
|
|
147
|
+
sprite.onmouseenter = () => console.log("enter")
|
|
148
|
+
return this.#objects
|
|
149
|
+
.set(layer.id, sprite)
|
|
150
|
+
.get(layer.id) as T
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} else return object as T
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#collectIds(layers: Layer | Composition): Set<number> {
|
|
157
|
+
const result = new Set<number>()
|
|
158
|
+
const traverse = (node: Layer | Composition) => {
|
|
159
|
+
if (Array.isArray(node)) {
|
|
160
|
+
for (const child of node) traverse(child)
|
|
161
|
+
} else {
|
|
162
|
+
result.add(node.id)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
traverse(layers)
|
|
166
|
+
return result
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#cleanup(activeIds: Set<number>) {
|
|
170
|
+
for (const id of this.#objects.keys()) {
|
|
171
|
+
if (!activeIds.has(id)) {
|
|
172
|
+
const obj = this.#objects.get(id)!
|
|
173
|
+
obj.destroy(true)
|
|
174
|
+
this.#objects.delete(id)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|