This repository has been archived by the owner on Jan 18, 2019. It is now read-only.
/
filter-and-record-live-video.js
132 lines (113 loc) · 4.6 KB
/
filter-and-record-live-video.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
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// This example records an altered video stream with MediaRecorder, and uses
// the resulting blob as the source of a video tag.
// The altered video stream is generated by getting the user's camera video,
// render it to a canvas and apply a filter to it there.
//
// The relevant functions used are:
//
// navigator.mediaDevices.getUserMedia -> to get a video stream from the webcam
// requestAnimationFrame -> to create a render loop (better than setTimeout)
// CanvasRenderingContext2D.drawImage -> to draw the video stream
// CanvasRenderingContext2D.getImageData -> to read the canvas pixels
// CanvasRenderingContext2D.putImageData -> to write the canvas pixels
// CanvasRenderingContext2D.captureStream -> get a video stream from a canvas
// MediaRecorder (contructor) -> create a MediaRecorder with a stream
// MediaRecorder.ondataavailable -> event to listen to when a record is ready
// MediaRecorder.start -> start recording
// MediaRecorder.stop -> stop recording (this will generate a blob of data)
// URL.createObjectURL -> to create a URL from a blob, which we can use as src
var recordButton, stopButton, recorder, recordingLabel;
window.onload = function () {
recordButton = document.getElementById('record');
stopButton = document.getElementById('stop');
recordingLabel = document.getElementById('recording');
recordButton.addEventListener('click', startRecording);
stopButton.addEventListener('click', stopRecording);
// get video stream from user's webcam
navigator.mediaDevices.getUserMedia({
video: true
})
.then(function (stream) {
recordButton.disabled = false;
// We need to create a video element and pipe the stream into it so we
// can know when we have data in the stream, and its width/height
// (and adjust the canvas element accordingly).
// Note that this video doesn't need to be attached to the DOM for this
// to work.
var video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.addEventListener('loadedmetadata', function () {
initCanvas(video);
});
// we need to play the video to trigger the loadedmetadata event
video.play();
});
};
function initCanvas(video) {
var width = video.videoWidth;
var height = video.videoHeight;
// NOTE: In order to make the example simpler, we have opted to use a 2D
// context. In a real application, you should use WebGL to render the
// video and shaders to make filters, since it will be much faster.
// You can see an example of this in Boo! https://github.com/mozdevs/boo
var canvas = document.getElementById('canvas');
canvas.width = width;
canvas.height = height;
// use requestAnimationFrame to render the video as often as possible
var ctx = canvas.getContext('2d');
var draw = function () {
// create a renderAnimationFrame loop
requestAnimationFrame(draw);
// draw the video data into the canvas
ctx.drawImage(video, 0, 0, width, height);
// apply a custom filter to the image
applyFilter(ctx, width, height);
};
draw();
initRecorderWithCanvas(canvas);
}
function applyFilter(ctx, width, height) {
// read pixels
var imageData = ctx.getImageData(0, 0, width, height);
var data = imageData.data; // data is an array of pixels in RGBA
// modify pixels
for (var i = 0; i < data.length; i+=4) {
var average = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = average >= 128 ? 255 : 0; // red
data[i + 1] = average >= 128 ? 255 : 0; // green
data[i + 2] = average >= 128 ? 255 : 0; // blue
// note: i+3 is the alpha channel, we are skipping that one
}
// render pixels back
ctx.putImageData(imageData, 0, 0);
}
function initRecorderWithCanvas(canvas) {
// create a new MediaRecorder and pipe the canvas stream to it
var stream = canvas.captureStream(24); // build a 24 fps stream
recorder = new MediaRecorder(stream);
// listen to dataavailable, which gets triggered whenever we have
// an audio blob available
recorder.addEventListener('dataavailable', function (evt) {
updateVideo(evt.data);
});
}
function startRecording() {
recordButton.disabled = true;
stopButton.disabled = false;
recordingLabel.style = 'display:inline';
recorder.start();
}
function stopRecording() {
recordButton.disabled = false;
stopButton.disabled = true;
recordingLabel.style = 'display:none';
// eventually this will trigger the dataavailable event
recorder.stop();
}
function updateVideo(blob) {
var video = document.getElementById('video');
// use the blob from MediaRecorder as source for the video tag
video.src = URL.createObjectURL(blob);
video.controls = true;
video.play();
}