getUserMedia
标签: 重要
# Basic getUserMedia demo (opens new window)
View source on GitHub (opens new window)
// Put variables in global scope to make them available to the browser console.
const constraints = window.constraints = {
audio: false,
video: true
};
function handleSuccess(stream) {
const video = document.querySelector('video');
const videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints);
console.log(`Using video device: ${videoTracks[0].label}`);
window.stream = stream; // make variable available to browser console
video.srcObject = stream;
}
function handleError(error) {
if (error.name === 'ConstraintNotSatisfiedError') {
const v = constraints.video;
errorMsg(`The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`);
} else if (error.name === 'PermissionDeniedError') {
errorMsg('Permissions have not been granted to use your camera and ' +
'microphone, you need to allow the page access to your devices in ' +
'order for the demo to work.');
}
errorMsg(`getUserMedia error: ${error.name}`, error);
}
function errorMsg(msg, error) {
const errorElement = document.querySelector('#errorMsg');
errorElement.innerHTML += `<p>${msg}</p>`;
if (typeof error !== 'undefined') {
console.error(error);
}
}
async function init(e) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleSuccess(stream);
e.target.disabled = true;
} catch (e) {
handleError(e);
}
}
document.querySelector('#showVideo').addEventListener('click', e => init(e));
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
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
# Use getUserMedia with canvas (opens new window)
View source on GitHub (opens new window)
button.onclick = function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
};
const constraints = {
audio: false,
video: true
};
function handleSuccess(stream) {
window.stream = stream; // make stream available to browser console
video.srcObject = stream;
}
function handleError(error) {
console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Use getUserMedia with canvas and CSS filters (opens new window)
View source on GitHub (opens new window)
snapshotButton.onclick = function() {
canvas.className = filterSelect.value;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
};
filterSelect.onchange = function() {
video.className = filterSelect.value;
};
const constraints = {
audio: false,
video: true
};
function handleSuccess(stream) {
window.stream = stream; // make stream available to browser console
video.srcObject = stream;
}
function handleError(error) {
console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
style:
.none {
-webkit-filter: none;
filter: none;
}
.blur {
-webkit-filter: blur(3px);
filter: blur(3px);
}
.grayscale {
-webkit-filter: grayscale(1);
filter: grayscale(1);
}
.invert {
-webkit-filter: invert(1);
filter: invert(1);
}
.sepia {
-webkit-filter: sepia(1);
filter: sepia(1);
}
button#snapshot {
margin: 0 10px 25px 0;
width: 110px;
}
video {
object-fit: cover;
}
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
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
# Choose camera resolution (opens new window)
View source on GitHub (opens new window)
eightKButton.onclick = () => {
getMedia(eightKConstraints);
};
const eightKConstraints = {
video: {width: {exact: 7680}, height: {exact: 4320}}
};
function gotStream(mediaStream) {
stream = window.stream = mediaStream; // stream available to console
video.srcObject = mediaStream;
}
function constraintChange(e) {
widthOutput.textContent = e.target.value;
const track = window.stream.getVideoTracks()[0];
let constraints;
if (aspectLock.checked) {
constraints = {
width: {exact: e.target.value},
aspectRatio: {
exact: video.videoWidth / video.videoHeight
}
};
} else {
constraints = {width: {exact: e.target.value}};
}
// ! track.applyConstraints
track.applyConstraints(constraints)
.then(() => {
console.log('applyConstraint success');
displayVideoDimensions('applyConstraints');
})
.catch(err => {
errorMessage('applyConstraints', err.name);
});
}
widthInput.onchange = constraintChange;
function getMedia(constraints) {
if (stream) {
stream.getTracks().forEach(track => {
track.stop();
});
}
navigator.mediaDevices.getUserMedia(constraints)
.then(gotStream)
.catch(e => {
errorMessage('getUserMedia', e.message, e.name);
});
}
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
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
# Audio-only getUserMedia() output to local audio element (opens new window)
View source on GitHub (opens new window)
const constraints = window.constraints = {
audio: true,
video: false
};
function handleSuccess(stream) {
const audioTracks = stream.getAudioTracks();
console.log('Using audio device: ' + audioTracks[0].label);
stream.oninactive = function() {
console.log('Stream ended');
};
window.stream = stream; // make variable available to browser console
audio.srcObject = stream;
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Audio-only getUserMedia() displaying volume (opens new window)
View source on GitHub (opens new window)
// Meter class that generates a number correlated to audio volume.
// The meter class itself displays nothing, but it makes the
// instantaneous and time-decaying volumes available for inspection.
// It also reports on the fraction of samples that were at or near
// the top of the measurement range.
function SoundMeter(context) {
this.context = context;
this.instant = 0.0;
this.slow = 0.0;
this.clip = 0.0;
this.script = context.createScriptProcessor(2048, 1, 1);
const that = this;
this.script.onaudioprocess = function(event) {
const input = event.inputBuffer.getChannelData(0);
let i;
let sum = 0.0;
let clipcount = 0;
for (i = 0; i < input.length; ++i) {
sum += input[i] * input[i];
if (Math.abs(input[i]) > 0.99) {
clipcount += 1;
}
}
that.instant = Math.sqrt(sum / input.length);
that.slow = 0.95 * that.slow + 0.05 * that.instant;
that.clip = clipcount / input.length;
};
}
SoundMeter.prototype.connectToSource = function(stream, callback) {
try {
this.mic = this.context.createMediaStreamSource(stream);
this.mic.connect(this.script);
// necessary to make sample run, but should not be.
this.script.connect(this.context.destination);
if (typeof callback !== 'undefined') {
callback(null);
}
} catch (e) {
console.error(e);
if (typeof callback !== 'undefined') {
callback(e);
}
}
};
SoundMeter.prototype.stop = function() {
this.mic.disconnect();
this.script.disconnect();
};
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
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
startButton.onclick = start;
stopButton.onclick = stop;
function handleSuccess(stream) {
// Put variables in global scope to make them available to the
// browser console.
window.stream = stream;
// !SoundMeter
const soundMeter = window.soundMeter = new SoundMeter(window.audioContext);
soundMeter.connectToSource(stream, function(e) {
if (e) {
alert(e);
return;
}
meterRefresh = setInterval(() => {
instantMeter.value = instantValueDisplay.innerText =
soundMeter.instant.toFixed(2);
slowMeter.value = slowValueDisplay.innerText =
soundMeter.slow.toFixed(2);
clipMeter.value = clipValueDisplay.innerText =
soundMeter.clip;
}, 200);
});
}
function start() {
try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
// ! AudioContext
window.audioContext = new AudioContext();
} catch (e) {
alert('Web Audio API not supported.');
}
navigator.mediaDevices
.getUserMedia(constraints)
.then(handleSuccess)
.catch(handleError);
}
function stop() {
window.stream.getTracks().forEach(track => track.stop());
window.soundMeter.stop();
}
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
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
# Record stream (opens new window)
View source on GitHub (opens new window)
let mediaRecorder;
let recordedBlobs;
recordButton.addEventListener('click', () => {
if (recordButton.textContent === 'Start Recording') {
startRecording();
} else {
stopRecording();
}
});
const playButton = document.querySelector('button#play');
playButton.addEventListener('click', () => {
const superBuffer = new Blob(recordedBlobs, {type: 'video/webm'});
recordedVideo.src = null;
recordedVideo.srcObject = null;
recordedVideo.src = window.URL.createObjectURL(superBuffer);
recordedVideo.controls = true;
recordedVideo.play();
});
downloadButton.addEventListener('click', () => {
const blob = new Blob(recordedBlobs, {type: 'video/webm'});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'test.webm';
document.body.appendChild(a);
a.click();
// for firefox
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
});
function handleDataAvailable(event) {
console.log('handleDataAvailable', event);
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
}
function startRecording() {
recordedBlobs = [];
let options = {mimeType: 'video/webm;codecs=vp9,opus'};
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
options = {mimeType: 'video/webm;codecs=vp8,opus'};
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
options = {mimeType: 'video/webm'};
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
options = {mimeType: ''};
}
}
}
try {
mediaRecorder = new MediaRecorder(window.stream, options);
} catch (e) {
console.error('Exception while creating MediaRecorder:', e);
errorMsgElement.innerHTML = `Exception while creating MediaRecorder: ${JSON.stringify(e)}`;
return;
}
console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
mediaRecorder.onstop = (event) => {
console.log('Recorder stopped: ', event);
console.log('Recorded Blobs: ', recordedBlobs);
};
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();
console.log('MediaRecorder started', mediaRecorder);
}
function stopRecording() {
mediaRecorder.stop();
}
function handleSuccess(stream) {
recordButton.disabled = false;
console.log('getUserMedia() got stream:', stream);
window.stream = stream;
const gumVideo = document.querySelector('video#gum');
gumVideo.srcObject = stream;
}
async function init(constraints) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleSuccess(stream);
} catch (e) {
console.error('navigator.getUserMedia error:', e);
errorMsgElement.innerHTML = `navigator.getUserMedia error:${e.toString()}`;
}
}
document.querySelector('button#start').addEventListener('click', async () => {
const hasEchoCancellation = document.querySelector('#echoCancellation').checked;
const constraints = {
audio: {
echoCancellation: {exact: hasEchoCancellation}
},
video: {
width: 1280, height: 720
}
};
console.log('Using media constraints:', constraints);
await init(constraints);
});
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
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
# Screensharing with getDisplayMedia (opens new window)
View source on GitHub (opens new window)
// Polyfill in Firefox.
// See https://blog.mozilla.org/webrtc/getdisplaymedia-now-available-in-adapter-js/
if (adapter.browserDetails.browser == 'firefox') {
adapter.browserShim.shimGetDisplayMedia(window, 'screen');
}
function handleSuccess(stream) {
const video = document.querySelector('video');
video.srcObject = stream;
// demonstrates how to detect that the user has stopped
// sharing the screen via the browser UI.
stream.getVideoTracks()[0].addEventListener('ended', () => {
startButton.disabled = false;
});
}
const startButton = document.getElementById('startButton');
startButton.addEventListener('click', () => {
navigator.mediaDevices.getDisplayMedia({video: true})
.then(handleSuccess, handleError);
});
if ((navigator.mediaDevices && 'getDisplayMedia' in navigator.mediaDevices)) {
startButton.disabled = false;
} else {
errorMsg('getDisplayMedia is not supported');
}
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
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
# Control camera pan, tilt, and zoom (opens new window)
View source on GitHub (opens new window)
// Put variables in global scope to make them available to the browser console.
const constraints = window.constraints = {
video: {
pan: true, tilt: true, zoom: true
}
};
function handleSuccess(stream) {
const video = document.querySelector('video');
const videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints);
console.log(`Using video device: ${videoTracks[0].label}`);
video.srcObject = stream;
// make track variable available to browser console.
const [track] = [window.track] = stream.getVideoTracks();
const capabilities = track.getCapabilities();
const settings = track.getSettings();
for (const ptz of ['pan', 'tilt', 'zoom']) {
// Check whether camera supports pan/tilt/zoom.
if (!(ptz in settings)) {
errorMsg(`Camera does not support ${ptz}.`);
continue;
}
// Map it to a slider element.
const input = document.querySelector(`input[name=${ptz}]`);
input.min = capabilities[ptz].min;
input.max = capabilities[ptz].max;
input.step = capabilities[ptz].step;
input.value = settings[ptz];
input.disabled = false;
input.oninput = async event => {
try {
const constraints = {advanced: [{[ptz]: input.value}]};
await track.applyConstraints(constraints);
} catch (err) {
console.error('applyConstraints() failed: ', err);
}
};
}
}
async function init(e) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleSuccess(stream);
e.target.disabled = true;
} catch (e) {
handleError(e);
}
}
document.querySelector('#showVideo').addEventListener('click', e => init(e));
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
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
# Control exposure (opens new window)
View source on GitHub (opens new window)
// Put variables in global scope to make them available to the browser console.
const constraints = window.constraints = {
audio: false,
video: true
};
function handleSuccess(stream) {
const video = document.querySelector('video');
const videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints);
console.log(`Using video device: ${videoTracks[0].label}`);
video.srcObject = stream;
// make track variable available to browser console.
[window.track] = stream.getVideoTracks();
loadProperties();
}
function loadProperties(refreshValuesOnly) {
const track = window.track;
const capabilities = track.getCapabilities();
const settings = track.getSettings();
console.log('Capabilities: ', capabilities);
console.log('Settings: ', settings);
for (const property of ['exposureMode', 'exposureTime', 'exposureCompensation', 'brightness', 'whiteBalanceMode']) {
// Check whether camera supports exposure.
if (!(property in settings)) {
errorMsg(`Camera does not support ${property}.`);
continue;
}
let element;
if (Array.isArray(capabilities[property])) {
// Map it to a select element.
const select = document.querySelector(`select[name=${property}]`);
element = select;
if (capabilities[property] && !refreshValuesOnly) {
for (const mode of capabilities[property]) {
select.insertAdjacentHTML('afterbegin', `<option value="${mode}">${mode}</option>`);
}
}
} else {
// Map it to a slider element.
const input = document.querySelector(`input[name=${property}]`);
element = input;
input.min = capabilities[property].min;
input.max = capabilities[property].max;
input.step = capabilities[property].step;
}
element.value = settings[property];
element.disabled = false;
if (!refreshValuesOnly) {
element.oninput = async event => {
try {
const constraints = {advanced: [{[property]: element.value}]};
await track.applyConstraints(constraints);
console.log('Did successfully apply new constraints: ', constraints);
console.log('New camera settings: ', track.getSettings());
} catch (err) {
console.error('applyConstraints() failed: ', err);
}
};
}
}
}
async function init(e) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleSuccess(stream);
e.target.disabled = true;
} catch (e) {
handleError(e);
}
}
document.querySelector('#showVideo').addEventListener('click', e => init(e));
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
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
编辑 (opens new window)
上次更新: 2022/07/21, 16:40:55