-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathwebrtc-server.js
More file actions
105 lines (90 loc) · 3.07 KB
/
Copy pathwebrtc-server.js
File metadata and controls
105 lines (90 loc) · 3.07 KB
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const { createCanvas, createImageData } = require("canvas");
const { hsv } = require("color-space");
const { performance } = require("perf_hooks");
const { mount } = require("./lib/server/rest/connectionsapi");
const WebRtcConnectionManager = require("./lib/server/connections/webrtcconnectionmanager");
const {
RTCVideoSink,
RTCVideoSource,
i420ToRgba,
rgbaToI420
} = require("wrtc").nonstandard;
const width = 640;
const height = 360;
module.exports = function init(app, prefix) {
const connectionManager = WebRtcConnectionManager.create({ beforeOffer });
mount(app, connectionManager, prefix);
}
function beforeOffer(peerConnection) {
const source = new RTCVideoSource();
const track = source.createTrack();
const transceiver = peerConnection.addTransceiver(track);
const sink = new RTCVideoSink(transceiver.receiver.track);
let lastFrame = null;
function onFrame({ frame }) {
lastFrame = frame;
}
sink.addEventListener("frame", onFrame);
// TODO(mroberts): Is pixelFormat really necessary?
const canvas = createCanvas(width, height);
const context = canvas.getContext("2d", { pixelFormat: "RGBA24" });
context.fillStyle = "white";
context.fillRect(0, 0, width, height);
let hue = 0;
const interval = setInterval(() => {
if (lastFrame) {
const lastFrameCanvas = createCanvas(lastFrame.width, lastFrame.height);
const lastFrameContext = lastFrameCanvas.getContext("2d", {
pixelFormat: "RGBA24"
});
const rgba = new Uint8ClampedArray(
lastFrame.width * lastFrame.height * 4
);
const rgbaFrame = createImageData(
rgba,
lastFrame.width,
lastFrame.height
);
i420ToRgba(lastFrame, rgbaFrame);
lastFrameContext.putImageData(rgbaFrame, 0, 0);
context.drawImage(lastFrameCanvas, 0, 0);
} else {
context.fillStyle = "rgba(255, 255, 255, 0.025)";
context.fillRect(0, 0, width, height);
}
hue = ++hue % 360;
const [r, g, b] = hsv.rgb([hue, 100, 100]);
const thisTime = performance.now();
context.font = "60px Sans-serif";
context.strokeStyle = "black";
context.lineWidth = 1;
context.fillStyle = `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(
b
)}, 1)`;
context.textAlign = "center";
context.save();
context.translate(width / 2, height / 2);
context.rotate(thisTime / 1000);
context.strokeText("node-webrtc", 0, 0);
context.fillText("node-webrtc", 0, 0);
context.restore();
const rgbaFrame = context.getImageData(0, 0, width, height);
const i420Frame = {
width,
height,
data: new Uint8ClampedArray(1.5 * width * height)
};
rgbaToI420(rgbaFrame, i420Frame);
source.onFrame(i420Frame);
});
// NOTE(mroberts): This is a hack so that we can get a callback when the
// RTCPeerConnection is closed. In the future, we can subscribe to
// "connectionstatechange" events.
const { close } = peerConnection;
peerConnection.close = function() {
clearInterval(interval);
sink.stop();
track.stop();
return close.apply(this, arguments);
};
}