@omnimedia/omnitool 1.0.0 → 1.1.0-10

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.
Files changed (324) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -2
  3. package/package.json +59 -27
  4. package/s/_archive/types.ts +107 -0
  5. package/s/context.ts +7 -0
  6. package/s/demo/demo.bundle.ts +68 -0
  7. package/s/demo/demo.css +59 -0
  8. package/s/demo/routines/filmstrip-test.ts +68 -0
  9. package/s/demo/routines/load-video.ts +7 -0
  10. package/s/demo/routines/transcode-test.ts +46 -0
  11. package/s/demo/routines/transcriber-test.ts +34 -0
  12. package/s/demo/routines/transitions-test.ts +43 -0
  13. package/s/demo/routines/waveform-test.ts +12 -0
  14. package/s/driver/driver.test.ts +15 -0
  15. package/s/driver/driver.ts +124 -0
  16. package/s/driver/driver.worker.bundle.ts +7 -0
  17. package/s/driver/fns/host.ts +13 -0
  18. package/s/driver/fns/schematic.ts +106 -0
  19. package/s/driver/fns/work.ts +249 -0
  20. package/s/driver/parts/constants.ts +17 -0
  21. package/s/driver/parts/machina.ts +27 -0
  22. package/s/driver/utils/load-decoder-source.ts +12 -0
  23. package/s/driver/utils/sleep.ts +3 -0
  24. package/s/features/speech/transcribe/default-spec.ts +11 -0
  25. package/s/features/speech/transcribe/parts/load-pipe.ts +19 -0
  26. package/s/features/speech/transcribe/parts/prep-audio.ts +23 -0
  27. package/s/features/speech/transcribe/parts/transcribe.ts +70 -0
  28. package/s/features/speech/transcribe/transcriber.ts +46 -0
  29. package/s/features/speech/transcribe/types.ts +82 -0
  30. package/s/features/speech/transcribe/worker.bundle.ts +40 -0
  31. package/s/features/transition/parts/fragment.ts +24 -0
  32. package/s/features/transition/parts/types.ts +94 -0
  33. package/s/features/transition/parts/uniforms.ts +29 -0
  34. package/s/features/transition/parts/vertex.ts +31 -0
  35. package/s/features/transition/transition.ts +60 -0
  36. package/s/index.html.ts +58 -0
  37. package/s/index.ts +2 -39
  38. package/s/tests.test.ts +8 -0
  39. package/s/timeline/index.ts +15 -0
  40. package/s/timeline/parts/basics.ts +17 -0
  41. package/s/timeline/parts/compositor/export.ts +77 -0
  42. package/s/timeline/parts/compositor/parts/html-tree.ts +37 -0
  43. package/s/timeline/parts/compositor/parts/schedulers.ts +85 -0
  44. package/s/timeline/parts/compositor/parts/tree-builder.ts +184 -0
  45. package/s/timeline/parts/compositor/parts/webcodecs-tree.ts +30 -0
  46. package/s/timeline/parts/compositor/playback.ts +81 -0
  47. package/s/timeline/parts/compositor/samplers/html.ts +115 -0
  48. package/s/timeline/parts/compositor/samplers/webcodecs.ts +60 -0
  49. package/s/timeline/parts/filmstrip.ts +159 -0
  50. package/s/timeline/parts/item.ts +90 -0
  51. package/s/timeline/parts/media.ts +35 -0
  52. package/s/timeline/parts/resource-pool.ts +27 -0
  53. package/s/timeline/parts/resource.ts +11 -0
  54. package/s/timeline/parts/waveform.ts +62 -0
  55. package/s/timeline/sugar/builders.ts +102 -0
  56. package/s/timeline/sugar/o.ts +150 -0
  57. package/s/timeline/sugar/omni-test.ts +38 -0
  58. package/s/timeline/sugar/omni.ts +40 -0
  59. package/s/timeline/types.ts +29 -0
  60. package/s/timeline/utils/audio-stream.ts +15 -0
  61. package/s/timeline/utils/checksum.ts +20 -0
  62. package/s/timeline/utils/datafile.ts +21 -0
  63. package/s/timeline/utils/dummy-data.ts +7 -0
  64. package/s/timeline/utils/matrix.ts +33 -0
  65. package/s/timeline/utils/video-cursor.ts +40 -0
  66. package/s/tools/common/loader.ts +26 -0
  67. package/s/tools/common/transformer-pipeline.ts +26 -0
  68. package/s/tools/speech-recognition/common/model.ts +26 -0
  69. package/s/tools/speech-recognition/whisper/fns/host.ts +25 -0
  70. package/s/tools/speech-recognition/whisper/fns/schematic.ts +23 -0
  71. package/s/tools/speech-recognition/whisper/fns/work.ts +91 -0
  72. package/s/tools/speech-recognition/whisper/parts/types.ts +38 -0
  73. package/s/tools/speech-recognition/whisper/parts/worker.bundle.ts +7 -0
  74. package/s/tools/speech-recognition/whisper/tool.ts +70 -0
  75. package/x/context.d.ts +4 -0
  76. package/x/context.js +6 -0
  77. package/x/context.js.map +1 -0
  78. package/x/demo/demo.bundle.d.ts +1 -0
  79. package/x/demo/demo.bundle.js +55 -0
  80. package/x/demo/demo.bundle.js.map +1 -0
  81. package/x/demo/demo.bundle.min.js +120 -0
  82. package/x/demo/demo.bundle.min.js.map +7 -0
  83. package/x/demo/demo.css +59 -0
  84. package/x/demo/routines/filmstrip-test.d.ts +1 -0
  85. package/x/demo/routines/filmstrip-test.js +62 -0
  86. package/x/demo/routines/filmstrip-test.js.map +1 -0
  87. package/x/demo/routines/load-video.d.ts +1 -0
  88. package/x/demo/routines/load-video.js +6 -0
  89. package/x/demo/routines/load-video.js.map +1 -0
  90. package/x/demo/routines/transcode-test.d.ts +6 -0
  91. package/x/demo/routines/transcode-test.js +40 -0
  92. package/x/demo/routines/transcode-test.js.map +1 -0
  93. package/x/demo/routines/transcriber-test.d.ts +4 -0
  94. package/x/demo/routines/transcriber-test.js +33 -0
  95. package/x/demo/routines/transcriber-test.js.map +1 -0
  96. package/x/demo/routines/transitions-test.d.ts +5 -0
  97. package/x/demo/routines/transitions-test.js +35 -0
  98. package/x/demo/routines/transitions-test.js.map +1 -0
  99. package/x/demo/routines/waveform-test.d.ts +1 -0
  100. package/x/demo/routines/waveform-test.js +11 -0
  101. package/x/demo/routines/waveform-test.js.map +1 -0
  102. package/x/driver/driver.d.ts +20 -0
  103. package/x/driver/driver.js +104 -0
  104. package/x/driver/driver.js.map +1 -0
  105. package/x/driver/driver.test.d.ts +5 -0
  106. package/x/driver/driver.test.js +12 -0
  107. package/x/driver/driver.test.js.map +1 -0
  108. package/x/driver/driver.worker.bundle.d.ts +1 -0
  109. package/x/driver/driver.worker.bundle.js +4 -0
  110. package/x/driver/driver.worker.bundle.js.map +1 -0
  111. package/x/driver/driver.worker.bundle.min.js +3537 -0
  112. package/x/driver/driver.worker.bundle.min.js.map +7 -0
  113. package/x/driver/fns/host.d.ts +25 -0
  114. package/x/driver/fns/host.js +7 -0
  115. package/x/driver/fns/host.js.map +1 -0
  116. package/x/driver/fns/schematic.d.ts +84 -0
  117. package/x/driver/fns/schematic.js +2 -0
  118. package/x/driver/fns/schematic.js.map +1 -0
  119. package/x/driver/fns/work.d.ts +26 -0
  120. package/x/driver/fns/work.js +201 -0
  121. package/x/driver/fns/work.js.map +1 -0
  122. package/x/driver/parts/constants.d.ts +2 -0
  123. package/x/driver/parts/constants.js +17 -0
  124. package/x/driver/parts/constants.js.map +1 -0
  125. package/x/driver/parts/machina.d.ts +23 -0
  126. package/x/driver/parts/machina.js +14 -0
  127. package/x/driver/parts/machina.js.map +1 -0
  128. package/x/driver/utils/load-decoder-source.d.ts +3 -0
  129. package/x/driver/utils/load-decoder-source.js +11 -0
  130. package/x/driver/utils/load-decoder-source.js.map +1 -0
  131. package/x/driver/utils/sleep.d.ts +1 -0
  132. package/x/driver/utils/sleep.js +4 -0
  133. package/x/driver/utils/sleep.js.map +1 -0
  134. package/x/features/speech/transcribe/default-spec.d.ts +2 -0
  135. package/x/features/speech/transcribe/default-spec.js +8 -0
  136. package/x/features/speech/transcribe/default-spec.js.map +1 -0
  137. package/x/features/speech/transcribe/parts/load-pipe.d.ts +2 -0
  138. package/x/features/speech/transcribe/parts/load-pipe.js +13 -0
  139. package/x/features/speech/transcribe/parts/load-pipe.js.map +1 -0
  140. package/x/features/speech/transcribe/parts/prep-audio.d.ts +5 -0
  141. package/x/features/speech/transcribe/parts/prep-audio.js +21 -0
  142. package/x/features/speech/transcribe/parts/prep-audio.js.map +1 -0
  143. package/x/features/speech/transcribe/parts/transcribe.d.ts +5 -0
  144. package/x/features/speech/transcribe/parts/transcribe.js +56 -0
  145. package/x/features/speech/transcribe/parts/transcribe.js.map +1 -0
  146. package/x/features/speech/transcribe/transcriber.d.ts +5 -0
  147. package/x/features/speech/transcribe/transcriber.js +33 -0
  148. package/x/features/speech/transcribe/transcriber.js.map +1 -0
  149. package/x/features/speech/transcribe/types.d.ts +66 -0
  150. package/x/features/speech/transcribe/types.js.map +1 -0
  151. package/x/features/speech/transcribe/worker.bundle.d.ts +1 -0
  152. package/x/features/speech/transcribe/worker.bundle.js +33 -0
  153. package/x/features/speech/transcribe/worker.bundle.js.map +1 -0
  154. package/x/features/speech/transcribe/worker.bundle.min.js +2916 -0
  155. package/x/features/speech/transcribe/worker.bundle.min.js.map +7 -0
  156. package/x/features/transition/parts/fragment.d.ts +1 -0
  157. package/x/features/transition/parts/fragment.js +25 -0
  158. package/x/features/transition/parts/fragment.js.map +1 -0
  159. package/x/features/transition/parts/types.d.ts +23 -0
  160. package/x/features/transition/parts/types.js +2 -0
  161. package/x/features/transition/parts/types.js.map +1 -0
  162. package/x/features/transition/parts/uniforms.d.ts +31 -0
  163. package/x/features/transition/parts/uniforms.js +27 -0
  164. package/x/features/transition/parts/uniforms.js.map +1 -0
  165. package/x/features/transition/parts/vertex.d.ts +1 -0
  166. package/x/features/transition/parts/vertex.js +32 -0
  167. package/x/features/transition/parts/vertex.js.map +1 -0
  168. package/x/features/transition/transition.d.ts +5 -0
  169. package/x/features/transition/transition.js +50 -0
  170. package/x/features/transition/transition.js.map +1 -0
  171. package/x/index.d.ts +2 -9
  172. package/x/index.html +115 -0
  173. package/x/index.html.d.ts +2 -0
  174. package/x/index.html.js +52 -0
  175. package/x/index.html.js.map +1 -0
  176. package/x/index.js +2 -29
  177. package/x/index.js.map +1 -1
  178. package/x/tests.test.d.ts +1 -0
  179. package/x/tests.test.js +6 -0
  180. package/x/tests.test.js.map +1 -0
  181. package/x/timeline/index.d.ts +11 -0
  182. package/x/timeline/index.js +12 -0
  183. package/x/timeline/index.js.map +1 -0
  184. package/x/timeline/parts/basics.d.ts +12 -0
  185. package/x/timeline/parts/basics.js +2 -0
  186. package/x/timeline/parts/basics.js.map +1 -0
  187. package/x/timeline/parts/compositor/export.d.ts +9 -0
  188. package/x/timeline/parts/compositor/export.js +64 -0
  189. package/x/timeline/parts/compositor/export.js.map +1 -0
  190. package/x/timeline/parts/compositor/parts/html-tree.d.ts +3 -0
  191. package/x/timeline/parts/compositor/parts/html-tree.js +40 -0
  192. package/x/timeline/parts/compositor/parts/html-tree.js.map +1 -0
  193. package/x/timeline/parts/compositor/parts/schedulers.d.ts +15 -0
  194. package/x/timeline/parts/compositor/parts/schedulers.js +64 -0
  195. package/x/timeline/parts/compositor/parts/schedulers.js.map +1 -0
  196. package/x/timeline/parts/compositor/parts/tree-builder.d.ts +37 -0
  197. package/x/timeline/parts/compositor/parts/tree-builder.js +147 -0
  198. package/x/timeline/parts/compositor/parts/tree-builder.js.map +1 -0
  199. package/x/timeline/parts/compositor/parts/webcodecs-tree.d.ts +3 -0
  200. package/x/timeline/parts/compositor/parts/webcodecs-tree.js +28 -0
  201. package/x/timeline/parts/compositor/parts/webcodecs-tree.js.map +1 -0
  202. package/x/timeline/parts/compositor/playback.d.ts +19 -0
  203. package/x/timeline/parts/compositor/playback.js +71 -0
  204. package/x/timeline/parts/compositor/playback.js.map +1 -0
  205. package/x/timeline/parts/compositor/samplers/html.d.ts +3 -0
  206. package/x/timeline/parts/compositor/samplers/html.js +106 -0
  207. package/x/timeline/parts/compositor/samplers/html.js.map +1 -0
  208. package/x/timeline/parts/compositor/samplers/webcodecs.d.ts +2 -0
  209. package/x/timeline/parts/compositor/samplers/webcodecs.js +55 -0
  210. package/x/timeline/parts/compositor/samplers/webcodecs.js.map +1 -0
  211. package/x/timeline/parts/filmstrip.d.ts +39 -0
  212. package/x/timeline/parts/filmstrip.js +117 -0
  213. package/x/timeline/parts/filmstrip.js.map +1 -0
  214. package/x/timeline/parts/item.d.ts +68 -0
  215. package/x/timeline/parts/item.js +16 -0
  216. package/x/timeline/parts/item.js.map +1 -0
  217. package/x/timeline/parts/media.d.ts +10 -0
  218. package/x/timeline/parts/media.js +30 -0
  219. package/x/timeline/parts/media.js.map +1 -0
  220. package/x/timeline/parts/resource-pool.d.ts +7 -0
  221. package/x/timeline/parts/resource-pool.js +19 -0
  222. package/x/timeline/parts/resource-pool.js.map +1 -0
  223. package/x/timeline/parts/resource.d.ts +8 -0
  224. package/x/timeline/parts/resource.js +2 -0
  225. package/x/timeline/parts/resource.js.map +1 -0
  226. package/x/timeline/parts/waveform.d.ts +8 -0
  227. package/x/timeline/parts/waveform.js +51 -0
  228. package/x/timeline/parts/waveform.js.map +1 -0
  229. package/x/timeline/sugar/builders.d.ts +1 -0
  230. package/x/timeline/sugar/builders.js +104 -0
  231. package/x/timeline/sugar/builders.js.map +1 -0
  232. package/x/timeline/sugar/o.d.ts +32 -0
  233. package/x/timeline/sugar/o.js +114 -0
  234. package/x/timeline/sugar/o.js.map +1 -0
  235. package/x/timeline/sugar/omni-test.d.ts +1 -0
  236. package/x/timeline/sugar/omni-test.js +22 -0
  237. package/x/timeline/sugar/omni-test.js.map +1 -0
  238. package/x/timeline/sugar/omni.d.ts +14 -0
  239. package/x/timeline/sugar/omni.js +28 -0
  240. package/x/timeline/sugar/omni.js.map +1 -0
  241. package/x/timeline/types.d.ts +24 -0
  242. package/x/timeline/types.js +2 -0
  243. package/x/timeline/types.js.map +1 -0
  244. package/x/timeline/utils/audio-stream.d.ts +6 -0
  245. package/x/timeline/utils/audio-stream.js +17 -0
  246. package/x/timeline/utils/audio-stream.js.map +1 -0
  247. package/x/timeline/utils/checksum.d.ts +8 -0
  248. package/x/timeline/utils/checksum.js +21 -0
  249. package/x/timeline/utils/checksum.js.map +1 -0
  250. package/x/timeline/utils/datafile.d.ts +9 -0
  251. package/x/timeline/utils/datafile.js +20 -0
  252. package/x/timeline/utils/datafile.js.map +1 -0
  253. package/x/timeline/utils/dummy-data.d.ts +2 -0
  254. package/x/timeline/utils/dummy-data.js +3 -0
  255. package/x/timeline/utils/dummy-data.js.map +1 -0
  256. package/x/timeline/utils/matrix.d.ts +8 -0
  257. package/x/timeline/utils/matrix.js +26 -0
  258. package/x/timeline/utils/matrix.js.map +1 -0
  259. package/x/timeline/utils/video-cursor.d.ts +10 -0
  260. package/x/timeline/utils/video-cursor.js +36 -0
  261. package/x/timeline/utils/video-cursor.js.map +1 -0
  262. package/x/tools/common/loader.d.ts +19 -0
  263. package/x/tools/common/loader.js +18 -0
  264. package/x/tools/common/loader.js.map +1 -0
  265. package/x/tools/common/transformer-pipeline.d.ts +8 -0
  266. package/x/tools/common/transformer-pipeline.js +24 -0
  267. package/x/tools/common/transformer-pipeline.js.map +1 -0
  268. package/x/tools/speech-recognition/common/model.d.ts +14 -0
  269. package/x/tools/speech-recognition/common/model.js +16 -0
  270. package/x/tools/speech-recognition/common/model.js.map +1 -0
  271. package/x/tools/speech-recognition/whisper/fns/host.d.ts +13 -0
  272. package/x/tools/speech-recognition/whisper/fns/host.js +19 -0
  273. package/x/tools/speech-recognition/whisper/fns/host.js.map +1 -0
  274. package/x/tools/speech-recognition/whisper/fns/schematic.d.ts +19 -0
  275. package/x/tools/speech-recognition/whisper/fns/schematic.js +2 -0
  276. package/x/tools/speech-recognition/whisper/fns/schematic.js.map +1 -0
  277. package/x/tools/speech-recognition/whisper/fns/work.d.ts +12 -0
  278. package/x/tools/speech-recognition/whisper/fns/work.js +74 -0
  279. package/x/tools/speech-recognition/whisper/fns/work.js.map +1 -0
  280. package/x/tools/speech-recognition/whisper/parts/types.d.ts +31 -0
  281. package/x/tools/speech-recognition/whisper/parts/types.js +2 -0
  282. package/x/tools/speech-recognition/whisper/parts/types.js.map +1 -0
  283. package/x/tools/speech-recognition/whisper/parts/worker.bundle.d.ts +1 -0
  284. package/x/tools/speech-recognition/whisper/parts/worker.bundle.js +4 -0
  285. package/x/tools/speech-recognition/whisper/parts/worker.bundle.js.map +1 -0
  286. package/x/tools/speech-recognition/whisper/parts/worker.bundle.min.js +8 -0
  287. package/x/tools/speech-recognition/whisper/parts/worker.bundle.min.js.map +7 -0
  288. package/x/tools/speech-recognition/whisper/tool.d.ts +12 -0
  289. package/x/tools/speech-recognition/whisper/tool.js +63 -0
  290. package/x/tools/speech-recognition/whisper/tool.js.map +1 -0
  291. package/s/parts/compositor.ts +0 -5
  292. package/s/parts/export.ts +0 -5
  293. package/s/parts/video-decoder.ts +0 -27
  294. package/s/parts/video-encoder.ts +0 -15
  295. package/s/tools/generate-id.ts +0 -7
  296. package/s/tools/mp4boxjs/LICENSE.md +0 -24
  297. package/s/tools/mp4boxjs/demuxer.ts +0 -106
  298. package/s/tools/mp4boxjs/mp4box.adapter.ts +0 -148
  299. package/s/tools/mp4boxjs/mp4box.js +0 -8206
  300. package/s/types.ts +0 -10
  301. package/x/parts/compositor.d.ts +0 -4
  302. package/x/parts/compositor.js +0 -5
  303. package/x/parts/compositor.js.map +0 -1
  304. package/x/parts/export.d.ts +0 -7
  305. package/x/parts/export.js +0 -5
  306. package/x/parts/export.js.map +0 -1
  307. package/x/parts/video-decoder.d.ts +0 -8
  308. package/x/parts/video-decoder.js +0 -20
  309. package/x/parts/video-decoder.js.map +0 -1
  310. package/x/parts/video-encoder.d.ts +0 -6
  311. package/x/parts/video-encoder.js +0 -12
  312. package/x/parts/video-encoder.js.map +0 -1
  313. package/x/tools/generate-id.d.ts +0 -1
  314. package/x/tools/generate-id.js +0 -8
  315. package/x/tools/generate-id.js.map +0 -1
  316. package/x/tools/mp4boxjs/demuxer.d.ts +0 -24
  317. package/x/tools/mp4boxjs/demuxer.js +0 -88
  318. package/x/tools/mp4boxjs/demuxer.js.map +0 -1
  319. package/x/tools/mp4boxjs/mp4box.adapter.d.ts +0 -128
  320. package/x/tools/mp4boxjs/mp4box.adapter.js +0 -11
  321. package/x/tools/mp4boxjs/mp4box.adapter.js.map +0 -1
  322. package/x/types.d.ts +0 -7
  323. package/x/types.js.map +0 -1
  324. /package/x/{types.js → features/speech/transcribe/types.js} +0 -0
@@ -0,0 +1,15 @@
1
+
2
+ import {Driver} from "./driver.js"
3
+ import {Science, test, expect} from "@e280/science"
4
+
5
+ const workerUrl = new URL("./driver.worker.bundle.js", import.meta.url)
6
+
7
+ export default Science.suite({
8
+ "driver hello world": test(async() => {
9
+ const driver = await Driver.setup({workerUrl})
10
+ expect(driver.machina.count).is(0)
11
+ await driver.thread.work.hello()
12
+ expect(driver.machina.count).is(1)
13
+ }),
14
+ })
15
+
@@ -0,0 +1,124 @@
1
+ import {Comrade, tune, Thread} from "@e280/comrade"
2
+ import {ALL_FORMATS, Input, type StreamTargetChunk} from "mediabunny"
3
+
4
+ import {Machina} from "./parts/machina.js"
5
+ import {setupDriverHost} from "./fns/host.js"
6
+ import {loadDecoderSource} from "./utils/load-decoder-source.js"
7
+ import {DecoderInput, DriverSchematic, Composition, EncoderInput, DecoderSource} from "./fns/schematic.js"
8
+
9
+ export type DriverOptions = {
10
+ workerUrl: URL | string
11
+ }
12
+
13
+ export class Driver {
14
+ static async setup(options: DriverOptions) {
15
+ const machina = new Machina()
16
+ const thread = await Comrade.thread<DriverSchematic>({
17
+ label: "OmnitoolDriver",
18
+ workerUrl: options.workerUrl,
19
+ setupHost: setupDriverHost(machina),
20
+ })
21
+ return new this(machina, thread)
22
+ }
23
+
24
+ constructor(
25
+ public machina: Machina,
26
+ public thread: Thread<DriverSchematic>
27
+ ) {}
28
+
29
+ async hello() {
30
+ return this.thread.work.hello()
31
+ }
32
+
33
+ async getAudioDuration(source: DecoderSource) {
34
+ const input = new Input({
35
+ source: await loadDecoderSource(source),
36
+ formats: ALL_FORMATS
37
+ })
38
+
39
+ const audioTrack = await input.getPrimaryAudioTrack()
40
+ return await audioTrack?.computeDuration()
41
+ }
42
+
43
+ async getVideoDuration(source: DecoderSource) {
44
+ const input = new Input({
45
+ source: await loadDecoderSource(source),
46
+ formats: ALL_FORMATS
47
+ })
48
+
49
+ const videoTrack = await input.getPrimaryVideoTrack()
50
+ return await videoTrack?.computeDuration()
51
+ }
52
+
53
+ decodeVideo(input: DecoderInput) {
54
+ let lastFrame: VideoFrame | null = null
55
+ const videoTransform = new TransformStream<VideoFrame, VideoFrame>({
56
+ async transform(chunk, controller) {
57
+ const frame = await input.onFrame?.(chunk) ?? chunk
58
+ // below code is to prevent mem leaks and hardware accelerated decoder stall
59
+ lastFrame?.close()
60
+ controller.enqueue(frame)
61
+ lastFrame = frame
62
+ }
63
+ })
64
+ this.thread.work.decodeVideo[tune]({transfer: [videoTransform.writable]})({
65
+ source: input.source,
66
+ video: videoTransform.writable,
67
+ start: input.start,
68
+ end: input.end
69
+ })
70
+ return videoTransform.readable
71
+ }
72
+
73
+ decodeAudio(input: DecoderInput) {
74
+ const audioTransform = new TransformStream<AudioData, AudioData>()
75
+ this.thread.work.decodeAudio[tune]({transfer: [audioTransform.writable]})({
76
+ source: input.source,
77
+ audio: audioTransform.writable,
78
+ start: input.start,
79
+ end: input.end
80
+ })
81
+ return audioTransform.readable
82
+ }
83
+
84
+ async encode({video, audio, config}: EncoderInput) {
85
+ const handle = await window.showSaveFilePicker()
86
+ const writable = await handle.createWritable()
87
+ // making bridge because file picker writable is not transferable
88
+ const bridge = new WritableStream<StreamTargetChunk>({
89
+ async write(chunk) {
90
+ await writable.write(chunk)
91
+ },
92
+ async close() {
93
+ await writable.close()
94
+ }
95
+ })
96
+ return await this.thread.work.encode[tune]({transfer: [audio ?? [], video ?? [], bridge]})({video, audio, config, bridge})
97
+ }
98
+
99
+ async composite(
100
+ composition: Composition,
101
+ ) {
102
+ const transfer = this.#collectTransferablesFromComposition(composition)
103
+ return await this.thread.work.composite[tune]({transfer})(composition)
104
+ }
105
+
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
+ }
124
+
@@ -0,0 +1,7 @@
1
+
2
+ import {Comrade} from "@e280/comrade"
3
+ import {setupDriverWork} from "./fns/work.js"
4
+ import {DriverSchematic} from "./fns/schematic.js"
5
+
6
+ await Comrade.worker<DriverSchematic>(setupDriverWork)
7
+
@@ -0,0 +1,13 @@
1
+
2
+ import {Comrade} from "@e280/comrade"
3
+ import {Machina} from "../parts/machina.js"
4
+ import {DriverSchematic} from "./schematic.js"
5
+
6
+ export const setupDriverHost = (machina: Machina) => (
7
+ Comrade.host<DriverSchematic>(_shell => ({
8
+ async world() {
9
+ machina.count++
10
+ },
11
+ }))
12
+ )
13
+
@@ -0,0 +1,106 @@
1
+
2
+ import {AsSchematic} from "@e280/comrade"
3
+ import type {AudioEncodingConfig, StreamTargetChunk, VideoEncodingConfig} from "mediabunny"
4
+
5
+ import {Mat6} from "../../timeline/utils/matrix.js"
6
+
7
+ export type DriverSchematic = AsSchematic<{
8
+
9
+ // happens on the web worker
10
+ work: {
11
+ hello(): Promise<void>
12
+
13
+ decodeAudio(input: {
14
+ source: DecoderSource
15
+ audio: WritableStream<AudioData>
16
+ start?: number
17
+ end?: number
18
+ }): Promise<void>
19
+
20
+ decodeVideo(input: {
21
+ source: DecoderSource
22
+ video: WritableStream<VideoFrame>
23
+ start?: number
24
+ end?: number
25
+ }): Promise<void>
26
+
27
+ encode(input: EncoderInput & {bridge: WritableStream<StreamTargetChunk>}): Promise<void>
28
+
29
+ composite(input: Composition): Promise<VideoFrame>
30
+ }
31
+
32
+ // happens on the main thread
33
+ host: {
34
+ world(): Promise<void>
35
+ }
36
+ }>
37
+
38
+ export interface EncoderInput {
39
+ video?: ReadableStream<VideoFrame>
40
+ audio?: ReadableStream<AudioData>
41
+ config: RenderConfig
42
+ }
43
+
44
+ export interface RenderConfig {
45
+ video: VideoEncodingConfig
46
+ audio: AudioEncodingConfig
47
+ }
48
+
49
+ export type DecoderSource = Blob | string | URL
50
+
51
+ export interface DecoderInput {
52
+ source: DecoderSource
53
+ start?: number
54
+ end?: number
55
+ onFrame?: (frame: VideoFrame) => Promise<VideoFrame>
56
+ }
57
+
58
+ export interface MuxOpts {
59
+ config: {
60
+ video: {
61
+ width: number
62
+ height: number
63
+ },
64
+ audio?: {
65
+ codec: "opus" | "aac"
66
+ numberOfChannels: number
67
+ sampleRate: number
68
+ }
69
+ }
70
+ }
71
+
72
+ export type Composition = Layer | (Layer | Composition)[]
73
+
74
+ export type TextLayer = {
75
+ kind: 'text'
76
+ content: string
77
+ fontSize?: number
78
+ color?: string
79
+ matrix?: Mat6
80
+ }
81
+
82
+ export type ImageLayer = {
83
+ kind: 'image'
84
+ frame: VideoFrame
85
+ matrix?: Mat6
86
+ }
87
+
88
+ export type TransitionLayer = {
89
+ kind: 'transition'
90
+ name: string
91
+ progress: number
92
+ from: VideoFrame
93
+ to: VideoFrame
94
+ }
95
+
96
+ export type GapLayer = {
97
+ kind: 'gap'
98
+ }
99
+
100
+ export type Audio = {
101
+ kind: "audio"
102
+ data: AudioData
103
+ }
104
+
105
+ export type Layer = TextLayer | ImageLayer | TransitionLayer | GapLayer
106
+
@@ -0,0 +1,249 @@
1
+ import {Comrade} from "@e280/comrade"
2
+ import {autoDetectRenderer, Container, Renderer, Sprite, Text, Texture, DOMAdapter, WebWorkerAdapter, Matrix} from "pixi.js"
3
+ import {Input, ALL_FORMATS, VideoSampleSink, Output, Mp4OutputFormat, VideoSampleSource, VideoSample, AudioSampleSink, AudioSampleSource, AudioSample, StreamTarget, BlobSource, UrlSource} from "mediabunny"
4
+
5
+ import {Mat6, mat6ToMatrix} from "../../timeline/utils/matrix.js"
6
+ import {makeTransition} from "../../features/transition/transition.js"
7
+ import {Composition, DecoderSource, DriverSchematic, Layer} from "./schematic.js"
8
+
9
+ DOMAdapter.set(WebWorkerAdapter)
10
+
11
+ const loadSource = async (source: DecoderSource) => {
12
+ if(source instanceof Blob) {
13
+ return new BlobSource(source)
14
+ } else {
15
+ return new UrlSource(source)
16
+ }
17
+ }
18
+
19
+ export const setupDriverWork = (
20
+ Comrade.work<DriverSchematic>(shell => ({
21
+ async hello() {
22
+ await shell.host.world()
23
+ },
24
+
25
+ async decodeAudio({source, audio, start, end}) {
26
+ const input = new Input({
27
+ source: await loadSource(source),
28
+ formats: ALL_FORMATS
29
+ })
30
+
31
+ const audioTrack = await input.getPrimaryAudioTrack()
32
+ const audioDecodable = await audioTrack?.canDecode()
33
+ const audioWriter = audio.getWriter()
34
+
35
+ if (audioDecodable && audioTrack) {
36
+ const sink = new AudioSampleSink(audioTrack)
37
+ for await (const sample of sink.samples(start, end)) {
38
+ const frame = sample.toAudioData()
39
+ await audioWriter.write(frame)
40
+ sample.close()
41
+ frame.close()
42
+ }
43
+ await audioWriter.close()
44
+ }
45
+ },
46
+
47
+ async decodeVideo({source, video, start, end}) {
48
+ const input = new Input({
49
+ source: await loadSource(source),
50
+ formats: ALL_FORMATS
51
+ })
52
+
53
+ const videoTrack = await input.getPrimaryVideoTrack()
54
+ const videoDecodable = await videoTrack?.canDecode()
55
+ const videoWriter = video.getWriter()
56
+
57
+ if (videoDecodable && videoTrack) {
58
+ const sink = new VideoSampleSink(videoTrack)
59
+ for await (const sample of sink.samples(start, end)) {
60
+ const frame = sample.toVideoFrame()
61
+ await videoWriter.write(frame)
62
+ sample.close()
63
+ frame.close()
64
+ }
65
+ await videoWriter.close()
66
+ }
67
+ },
68
+
69
+ async encode({video, audio, config, bridge}) {
70
+ const output = new Output({
71
+ format: new Mp4OutputFormat(),
72
+ target: new StreamTarget(bridge, {chunked: true})
73
+ })
74
+ // since AudioSample is not transferable it fails to transfer encoder bitrate config
75
+ // so it needs to be hardcoded not set through constants eg QUALITY_LOW
76
+
77
+ const promises = []
78
+
79
+ if(video) {
80
+ const videoSource = new VideoSampleSource(config.video)
81
+ output.addVideoTrack(videoSource)
82
+ const videoReader = video.getReader()
83
+ promises.push((async () => {
84
+ while (true) {
85
+ const {done, value} = await videoReader.read()
86
+ if (done) break
87
+ const sample = new VideoSample(value)
88
+ await videoSource.add(sample)
89
+ sample.close()
90
+ }
91
+ })())
92
+ }
93
+
94
+ if(audio) {
95
+ const audioSource = new AudioSampleSource(config.audio)
96
+ output.addAudioTrack(audioSource)
97
+ const audioReader = audio.getReader()
98
+ promises.push((async () => {
99
+ while (true) {
100
+ const {done, value} = await audioReader.read()
101
+ if (done) break
102
+ const sample = new AudioSample(value)
103
+ await audioSource.add(sample)
104
+ sample.close()
105
+ value.close()
106
+ }
107
+ })())
108
+ }
109
+
110
+ await output.start()
111
+ await Promise.all(promises)
112
+ await output.finalize()
113
+ },
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
+ }))
136
+ )
137
+
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
+ type RenderableObject = Sprite | Text | Texture
165
+
166
+ async function renderLayer(
167
+ layer: Layer | Composition,
168
+ parent: Container,
169
+ ) {
170
+ if (Array.isArray(layer)) {
171
+ const disposers: (() => void)[] = []
172
+ for (const child of layer) {
173
+ const result = await renderLayer(child, parent)
174
+ disposers.push(result.dispose)
175
+ }
176
+ return {dispose: () => disposers.forEach(d => d())}
177
+ }
178
+
179
+ switch (layer.kind) {
180
+ case 'text':
181
+ return renderTextLayer(layer, parent)
182
+ case 'image':
183
+ return renderImageLayer(layer, parent)
184
+ case 'transition':
185
+ return renderTransitionLayer(layer, parent)
186
+ case 'gap': {
187
+ pixi?.renderer.clear()
188
+ return {dispose: () => {}}
189
+ }
190
+ default:
191
+ console.warn('Unknown layer kind', (layer as any).kind)
192
+ return {dispose: () => {}}
193
+ }
194
+ }
195
+
196
+ function renderTextLayer(
197
+ layer: Extract<Layer, {kind: 'text'}>,
198
+ parent: Container,
199
+ ) {
200
+ const text = new Text({
201
+ text: layer.content,
202
+ style: {
203
+ fontFamily: 'sans-serif',
204
+ fontSize: layer.fontSize ?? 48,
205
+ fill: layer.color ?? 'white'
206
+ }
207
+ })
208
+ applyTransform(text, layer.matrix)
209
+ parent.addChild(text)
210
+ return {dispose: () => text.destroy(true)}
211
+ }
212
+
213
+ function renderImageLayer(
214
+ layer: Extract<Layer, {kind: 'image'}>,
215
+ parent: Container,
216
+ ) {
217
+ const texture = Texture.from(layer.frame)
218
+ const sprite = new Sprite(texture)
219
+ applyTransform(sprite, layer.matrix)
220
+ parent.addChild(sprite)
221
+ return {dispose: () => {
222
+ sprite.destroy(true)
223
+ texture.destroy(true)
224
+ layer.frame.close()
225
+ }}
226
+ }
227
+
228
+ function renderTransitionLayer(
229
+ {from, to, progress, name}: Extract<Layer, {kind: 'transition'}>,
230
+ parent: Container,
231
+ ) {
232
+ const transition = transitions.get(name) ??
233
+ (transitions.set(name, makeTransition({
234
+ name: "circle",
235
+ renderer: pixi!.renderer
236
+ })),
237
+ transitions.get(name)!
238
+ )
239
+ const texture = transition.render({from, to, progress, width: from.displayWidth, height: from.displayHeight})
240
+ const sprite = new Sprite(texture)
241
+ parent.addChild(sprite)
242
+ return {dispose: () => sprite.destroy(false)}
243
+ }
244
+
245
+ function applyTransform(target: Sprite | Text, worldMatrix?: Mat6) {
246
+ if (!worldMatrix) return
247
+ const mx = mat6ToMatrix(worldMatrix)
248
+ target.setFromMatrix(mx)
249
+ }
@@ -0,0 +1,17 @@
1
+ // for later: https://github.com/gpac/mp4box.js/issues/243
2
+ export const encoderDefaultConfig: VideoEncoderConfig = {
3
+ codec: "avc1.640034",
4
+ avc: {format: "annexb"},
5
+ width: 1280,
6
+ height: 720,
7
+ bitrate: 9_000_000, // 9 Mbps
8
+ framerate: 60,
9
+ bitrateMode: "variable",
10
+ hardwareAcceleration: "no-preference" // prefer-hardware seems like 2x slower from what i been testing
11
+ }
12
+
13
+ export const audioEncoderDefaultConfig: AudioEncoderConfig = {
14
+ codec: "opus",
15
+ numberOfChannels: 2,
16
+ sampleRate: 44100
17
+ }
@@ -0,0 +1,27 @@
1
+ import {WebMediaInfo} from "web-demuxer"
2
+
3
+ type Events =
4
+ | {type: "config", config: {audio: AudioDecoderConfig, video: VideoDecoderConfig}}
5
+ | {type: "info", data: WebMediaInfo}
6
+ | {type: "encoderQueueSize", size: number}
7
+
8
+ type Handler = (event: Events) => void
9
+
10
+ export class Machina {
11
+ count = 0
12
+
13
+ #handlers = new Map<number, Handler>()
14
+
15
+ register(id: number, handler: Handler) {
16
+ this.#handlers.set(id, handler)
17
+ }
18
+
19
+ unregister(id: number) {
20
+ this.#handlers.delete(id)
21
+ }
22
+
23
+ dispatch(id: number, event: Events) {
24
+ this.#handlers.get(id)?.(event)
25
+ }
26
+ }
27
+
@@ -0,0 +1,12 @@
1
+ import {BlobSource, UrlSource} from "mediabunny"
2
+ import {DecoderSource} from "../fns/schematic.js"
3
+
4
+ // only streamable sources
5
+ export async function loadDecoderSource(source: DecoderSource): Promise<UrlSource | BlobSource> {
6
+ if(source instanceof Blob) {
7
+ return new BlobSource(source)
8
+ } else {
9
+ return new UrlSource(source)
10
+ }
11
+ }
12
+
@@ -0,0 +1,3 @@
1
+ export function sleep(ms: number) {
2
+ return new Promise(resolve => setTimeout(resolve, ms));
3
+ }
@@ -0,0 +1,11 @@
1
+
2
+ import {TranscriberSpec} from "./types.js"
3
+
4
+ export const defaultTranscriberSpec = (): TranscriberSpec => ({
5
+ model: "onnx-community/whisper-tiny_timestamped",
6
+ dtype: "q4",
7
+ device: "wasm",
8
+ chunkLength: 20,
9
+ strideLength: 3,
10
+ })
11
+
@@ -0,0 +1,19 @@
1
+
2
+ import {pipeline} from "@huggingface/transformers"
3
+
4
+ import {TranscriberPipeOptions} from "../types.js"
5
+
6
+ export async function loadPipe(options: TranscriberPipeOptions) {
7
+ const {spec, onLoading} = options
8
+
9
+ const pipe = await pipeline("automatic-speech-recognition", spec.model, {
10
+ device: spec.device,
11
+ dtype: spec.dtype,
12
+ progress_callback: (data: any) => {
13
+ onLoading({total: data.total, progress: data.progress})
14
+ },
15
+ })
16
+
17
+ return pipe
18
+ }
19
+
@@ -0,0 +1,23 @@
1
+
2
+ import {Driver} from "../../../../driver/driver.js"
3
+
4
+ export async function prepAudio(driver: Driver, source: Blob) {
5
+ const arrayBuffer = await source.arrayBuffer()
6
+ const audioCTX = new AudioContext({sampleRate: 16000})
7
+ const audioData = await audioCTX.decodeAudioData(arrayBuffer)
8
+ let audio: Float32Array
9
+ if (audioData.numberOfChannels === 2) {
10
+ const SCALING_FACTOR = Math.sqrt(2)
11
+ const left = audioData.getChannelData(0)
12
+ const right = audioData.getChannelData(1)
13
+ audio = new Float32Array(left.length)
14
+ for (let i = 0; i < audioData.length; ++i) {
15
+ audio[i] = (SCALING_FACTOR * (left[i] + right[i])) / 2
16
+ }
17
+ } else {
18
+ audio = audioData.getChannelData(0)
19
+ }
20
+ const duration = await driver.getAudioDuration(source)
21
+ return {audio, duration}
22
+ }
23
+