-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmediastreamtrackprocessor.js
58 lines (58 loc) · 2.65 KB
/
mediastreamtrackprocessor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
if (!self.MediaStreamTrackProcessor) {
self.MediaStreamTrackProcessor = class MediaStreamTrackProcessor {
constructor({track}) {
if (track.kind == "video") {
this.readable = new ReadableStream({
async start(controller) {
this.video = document.createElement("video");
this.video.srcObject = new MediaStream([track]);
await Promise.all([this.video.play(), new Promise(r => this.video.onloadedmetadata = r)]);
this.track = track;
this.canvas = new OffscreenCanvas(this.video.videoWidth, this.video.videoHeight);
this.ctx = this.canvas.getContext('2d', {desynchronized: true});
this.t1 = performance.now();
},
async pull(controller) {
while (performance.now() - this.t1 < 1000 / track.getSettings().frameRate) {
await new Promise(r => requestAnimationFrame(r));
}
this.t1 = performance.now();
this.ctx.drawImage(this.video, 0, 0);
controller.enqueue(new VideoFrame(this.canvas, {timestamp: this.t1}));
}
});
} else if (track.kind == "audio") {
this.readable = new ReadableStream({
async start(controller) {
this.ac = new AudioContext;
this.arrays = [];
function worklet() {
registerProcessor("mstp-shim", class Processor extends AudioWorkletProcessor {
process(input) { this.port.postMessage(input); return true; }
});
}
await this.ac.audioWorklet.addModule(`data:text/javascript,(${worklet.toString()})()`);
this.node = new AudioWorkletNode(this.ac, "mstp-shim");
this.ac.createMediaStreamSource(new MediaStream([track])).connect(this.node);
this.node.port.addEventListener("message", ({data}) => data[0][0] && this.arrays.push(data));
},
async pull(controller) {
while (!this.arrays.length) await new Promise(r => this.node.port.onmessage = r);
const [channels] = this.arrays.shift();
const joined = new Float32Array(channels.reduce((a, b) => a + b.length, 0));
channels.reduce((offset, a) => (joined.set(a, offset), offset + a.length), 0);
controller.enqueue(new AudioData({
format: "f32-planar",
sampleRate: this.ac.sampleRate,
numberOfFrames: channels[0].length,
numberOfChannels: channels.length,
timestamp: this.ac.currentTime * 1e6 | 0,
data: joined,
transfer: [joined.buffer]
}));
}
});
}
}
};
}