Skip to content

Commit 6985cf6

Browse files
committed
feat(video): add preliminary changes for video support, re #695
1 parent a437abd commit 6985cf6

File tree

5 files changed

+133
-1
lines changed

5 files changed

+133
-1
lines changed
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export function createWebcodecVideo(id, modV) {
2+
return Promise(async (resolve, reject) => {
3+
const url = modV.store.state.videos[id];
4+
const video = document.createElement("video");
5+
video.setAttribute("crossorigin", "anonymous");
6+
video.setAttribute("loop", true);
7+
video.onerror(reject);
8+
video.muted = true;
9+
10+
video.onloadedmetadata = async () => {
11+
const stream = video.captureStream();
12+
const [track] = stream.getVideoTracks();
13+
14+
// eslint-disable-next-line
15+
const processor = new MediaStreamTrackProcessor(track);
16+
const frameStream = processor.readable;
17+
18+
// Transfer the readable stream to the worker.
19+
// NOTE: transferring frameStream and reading it in the worker is more
20+
// efficient than reading frameStream here and transferring VideoFrames individually.
21+
this.$modV.store.dispatch(
22+
"videos/assignVideoStream",
23+
{
24+
id,
25+
stream: frameStream,
26+
width: video.videoWidth || 256,
27+
height: video.videoHeight || 256
28+
},
29+
[frameStream]
30+
);
31+
32+
resolve();
33+
};
34+
35+
video.setAttribute("src", url);
36+
await video.play();
37+
});
38+
}

src/application/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import PromiseWorker from "promise-worker-transferable";
1515
import Vue from "vue";
1616
import { ipcRenderer } from "electron";
1717
import { app } from "@electron/remote";
18+
import { createWebcodecVideo } from "./createWebcodecVideo";
1819

1920
let imageBitmap;
2021
const imageBitmapQueue = [];
@@ -27,6 +28,7 @@ export default class ModV {
2728
setupBeatDetektor = setupBeatDetektor;
2829
setupMidi = setupMidi;
2930
windowHandler = windowHandler;
31+
createWebcodecVideo = createWebcodecVideo;
3032
use = use;
3133
debug = false;
3234
features = Vue.observable({
@@ -76,6 +78,10 @@ export default class ModV {
7678
// console.log(`⚙️%c ${type}`, "color: red");
7779
// }
7880

81+
if (type === "createWebcodecVideo") {
82+
this.createWebcodecVideo();
83+
}
84+
7985
if (e.data.type === "tick" && this.ready) {
8086
this.tick(e.data.payload);
8187
return;

src/application/worker/store/modules/dataTypes.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,22 @@ const state = {
4848
textureDefinition.id = id;
4949
}
5050

51-
if (type === "canvas" || type == "group") {
51+
if (type === "video") {
52+
const { path } = options;
53+
let id;
54+
try {
55+
({ id } = await store.dispatch("images/createVideoFromPath", {
56+
path
57+
}));
58+
} catch (e) {
59+
console.error(e);
60+
}
61+
62+
textureDefinition.location = "videos/video";
63+
textureDefinition.id = id;
64+
}
65+
66+
if (type === "canvas" || type === "group") {
5267
const { id } = options;
5368
textureDefinition.location = "outputs/auxillaryCanvas";
5469
textureDefinition.id = id;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import Vue from "vue";
2+
import uuidv4 from "uuid/v4";
3+
4+
const state = {};
5+
6+
const getters = {
7+
video: state => id => state[id]
8+
};
9+
10+
const actions = {
11+
createVideoFromPath({ commit }, { path: filePath }) {
12+
const id = uuidv4();
13+
const path = `modv://${filePath}`;
14+
15+
if (typeof window !== "undefined") {
16+
self.postMessage({
17+
type: "createWebcodecVideo",
18+
id,
19+
path
20+
});
21+
}
22+
23+
commit("CREATE_VIDEO", { id, path });
24+
return { id };
25+
},
26+
27+
assignVideoStream({ commit }, { id, stream, width, height }) {
28+
commit("UPDATE_VIDEO", { id, stream, width, height });
29+
}
30+
};
31+
32+
const mutations = {
33+
CREATE_VIDEO(state, { id, path }) {
34+
Vue.set(state, id, path);
35+
},
36+
37+
UPDATE_VIDEO(state, video) {
38+
const { id } = video;
39+
state[id] = { ...state[id], ...video };
40+
}
41+
};
42+
43+
export default {
44+
namespaced: true,
45+
state,
46+
getters,
47+
actions,
48+
mutations
49+
};

src/background/background.js

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { app, protocol } from "electron";
22
import { APP_SCHEME } from "./background-constants";
3+
// import { getMediaManager } from "./media-manager";
34
import { openFile } from "./open-file";
45
import { createWindow } from "./windows";
56

@@ -39,10 +40,33 @@ app.on("activate", async () => {
3940
createWindow("mainWindow");
4041
});
4142

43+
// https://stackoverflow.com/a/66673831
44+
function fileHandler(req, callback) {
45+
// const { mediaDirectoryPath } = getMediaManager();
46+
const requestedPath = req.url;
47+
// Write some code to resolve path, calculate absolute path etc
48+
const check = true; // requestedPath.indexOf(mediaDirectoryPath) > -1;
49+
50+
if (!check) {
51+
callback({
52+
// -6 is FILE_NOT_FOUND
53+
// https://source.chromium.org/chromium/chromium/src/+/master:net/base/net_error_list.h
54+
error: -6
55+
});
56+
return;
57+
}
58+
59+
callback({
60+
path: requestedPath
61+
});
62+
}
63+
4264
// This method will be called when Electron has finished
4365
// initialization and is ready to create browser windows.
4466
// Some APIs can only be used after this event occurs.
4567
app.on("ready", async () => {
68+
protocol.registerFileProtocol("modv", fileHandler);
69+
4670
app.commandLine.appendSwitch(
4771
"disable-backgrounding-occluded-windows",
4872
"true"

0 commit comments

Comments
 (0)