-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
477 lines (437 loc) · 18.3 KB
/
index.html
File metadata and controls
477 lines (437 loc) · 18.3 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
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>getUserMedia Examples - addpipe.com</title>
<meta name="description" content="getUserMedia code and constraints examples">
<link rel="stylesheet" href="css/atom-one-dark.min.css">
<script src="js/highlight.min.js"></script>
<!-- Plausible Analytics -->
<script defer data-domain="addpipe.com" src="https://plausible.io/js/script.js"></script>
<style>
body {
line-height: 1.5;
font-family: sans-serif;
word-wrap: break-word;
overflow-wrap: break-word;
color:black;
margin:2em;
}
h1 {
text-decoration: underline red;
text-decoration-thickness: 3px;
text-underline-offset: 6px;
font-size: 220%;
font-weight: bold;
}
h2 {
font-weight: bold;
color: #005A9C;
font-size: 140%;
text-transform: uppercase;
}
p {
margin: 1em 0;
}
pre {
padding: 0px;
border:0px;
border-radius: 0px;
}
red {
color: red;
}
.example-section {
padding: 25px 0;
}
.table-of-contents{
padding: 0 5px;
}
.table-of-contents li {
list-style: none;
}
.code-block-wrapper {
position: relative;
}
.copy-code-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background-color: #f8f9fa;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0.3rem 0.6rem;
font-size: 0.8rem;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.2s;
z-index: 10;
}
.copy-code-button:hover {
opacity: 1;
}
</style>
</head>
<body>
<h1>getUserMedia Examples </h1>
<p class="description">Made by the <a href="https://addpipe.com" target="_blank">Pipe Video Recording Platform</a></p>
<p class="description">These examples demonstrate the powerful <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia" target="_blank"><code>getUserMedia</code> method</a>, which gives web applications secure access to cameras and microphones. To run <code>getUserMedia</code>, your web page needs to be in a <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts" target="_blank">secure context</a>, the page must not be under a <a href="https://blog.addpipe.com/camera-and-microphone-access-in-cross-oirigin-iframes-with-feature-policy/" target="_blank">Permissions Policy</a> that's limiting device access, and you need to have user permission at the browser level and, in some cases, at the operating system level. When first run, the browser will prompt for permissions, and you can manage these settings through browser preferences or OS privacy controls.
<p>Originally part of WebRTC, <code>getUserMedia</code> has now become essential for tasks such as video recording, audio recording, taking pictures, and enumerating available devices.</p>
<p>The examples below showcase how <code>getUserMedia</code> enables a range of key scenarios, making it a vital tool for modern web applications.</p>
<h2>table of contents </h2>
<ul class="table-of-contents">
<li><a href="#videoAndAudio">Example 1: Capture Audio and Video</a></li>
<li><a href="#stoppingTheStream">Example 2: Stop Device Access</a></li>
<li><a href="#audioOnlyConstraint">Example 3: Audio Only Constraint</a></li>
<li><a href="#videoOnlyConstraint">Example 4: Video Only Constraint</a></li>
<li><a href="#listingSpecificDevice">Example 5: List Available Devices</a></li>
<li><a href="#recordingClip">Example 6: Recording a Video Clip (MediaRecorder)</a></li>
<li><a href="#captureVideo">Example 7: Capture Video + Take a Snapshot</a></li>
<li><a href="#requestAnAspectRatio">Example 8: Requesting a Certain Aspect Ratio</a></li>
<li><a href="#requestPortraitVideoStream">Example 9: Requesting a Portrait Video Stream With facingMode:user</a></li>
<li><a href="#basicVideoPreview">Example 10: Basic Video Preview </a></li>
<li><a href="#asyncPlusAudioOnly">Example 11: Async/Await + Audio Only</a></li>
</ul>
<br />
<br />
<h4 id="videoAndAudio">🎥🎤 Example 1: Capture Audio and Video</h4>
<p>This example demonstrates the basic use of the <code>getUserMedia</code> method to access both the user's camera and microphone simultaneously. The code requests permission for both video and audio streams using the <code>{ video: true, audio: true }</code> constraint, then creates a video element that displays the camera stream in real-time. </p>
<pre><code class="example"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Capture Audio and Video</title>
</head>
<body>
<video id="video" autoplay playsinline> </video>
<script>
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
const video = document.createElement('video');
video.autoplay = true;
video.srcObject = stream;
document.body.appendChild(video);
})
.catch(error => {
console.error('Error accessing camera and microphone:', error);
});
</script>
</body>
</html></code></pre>
<p>Key points:</p>
<ul>
<li>Uses <code>{ video: true, audio: true }</code> to request both camera and microphone access;</li>
<li>Creates a video element with autoplay to display the stream from the camera;</li>
<li>Includes basic error handling with <code>.catch()</code> for permission denials or device issues.</li>
</ul>
<br />
<br />
<h4 id="stoppingTheStream">🛑 Example 2: Stop Device Access</h4>
<p>Demonstrates how to properly stop media streams by calling <code>getTracks().forEach(track => track.stop())</code> on the stream object. This is essential for releasing camera and microphone resources when they're no longer needed.</p>
<pre><code class="example"><script>
stream.getTracks().forEach(track => track.stop());
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Essential for freeing up camera/microphone for other applications;</li>
<li>Should always be called when device access is no longer needed;</li>
<li>iOS, Android, and macOS will show a green dot prominently when the camera is accessed.</li>
</ul>
<br />
<br />
<h4 id="audioOnlyConstraint">🎤 Example 3: Audio Only Constraint</h4>
<p>Shows how to request access to only the microphone using <code>{ audio: true }</code> without requesting video. </p>
<pre><code class="example"><script>
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
console.log('Microphone access granted');
})
.catch(error => {
console.error('Error accessing microphone:', error);
});
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Logs success message to console when microphone permission is granted;</li>
<li>Useful for voice recording or audio chat applications.</li>
</ul>
<br />
<br />
<h4 id="videoOnlyConstraint">📸 Example 4: Video Only Constraint</h4>
<p>Demonstrates requesting access to only the camera using <code>{ video: true }</code> without audio. </p>
<pre><code class="example"><script>
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
const video = document.getElementById('video');
video.srcObject = stream;
})
.catch(error => {
console.error('Error accessing camera:', error);
});
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Suitable for applications that need only camera access like taking photos or scanning QR codes.</li>
</ul>
<br />
<br />
<h4 id="listingSpecificDevice">🔍 Example 5: List Available Devices</h4>
<p>Uses <code>navigator.mediaDevices.enumerateDevices()</code> to list all available media input devices (cameras and microphones) on the user's system and get their device ID.</p>
<pre><code class="example"><script>
navigator.mediaDevices.enumerateDevices().then(devices => {
devices.forEach(device => {
console.log(device.kind + ": " + device.label + " id = " + device.deviceId);
});
});
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Logs device kind, label, and deviceId for each camera and microphone;</li>
<li>Helpful for giving users acess to a custom made device selection UI.</li>
</ul>
<br />
<br />
<h4 id="recordingClip">📸 Example 6: Recording a Video Clip (MediaRecorder)</h4>
<p>Shows how to record video and audio using the <code>MediaRecorder</code> interface of the <code>MediaStream Recording API</code>.</p>
<pre><code class="example"><video id="preview" autoplay playsinline width="320" height="240"></video>
<button id="record">Start Recording</button>
<button id="stop" disabled>Stop & Download</button>
<script>
const videoEl = document.getElementById('preview'),
recordBtn = document.getElementById('record'),
stopBtn = document.getElementById('stop');
let recorder, recordedChunks = [];
// 1. Get both video and audio
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
videoEl.srcObject = stream;
// 2. Setup MediaRecorder
recorder = new MediaRecorder(stream);
recorder.ondataavailable = e => {
if (e.data && e.data.size > 0) {
recordedChunks.push(e.data);
}
};
recorder.onstop = () => {
// Create a Blob from recorded chunks
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
// Create download link
const a = document.createElement('a');
a.style.display = 'block';
a.href = url;
a.download = 'capture.webm';
a.textContent = 'Download recording';
document.body.appendChild(a);
};
})
.catch(console.error);
// 3. Wire up buttons
recordBtn.addEventListener('click', () => {
recordedChunks = [];
recorder.start();
recordBtn.disabled = true;
stopBtn.disabled = false;
});
stopBtn.addEventListener('click', () => {
recorder.stop();
recordBtn.disabled = false;
stopBtn.disabled = true;
});
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Grabs audio + video;</li>
<li>Pipes it to a <video> for live preview;</li>
<li>Uses <code>MediaRecorder</code> to capture chunks;</li>
<li>When stopped, bundles into a <code>.webm</code> and offers a download link.</li>
</ul>
<br />
<br />
<h4 id="captureVideo">📸 Example 7: Capture Video + Take a Snapshot</h4>
<p>Combines live video preview with snapshot functionality using HTML5 canvas. </p>
<pre><code class="example"><video id="cam" autoplay playsinline width="320" height="240"></video>
<button id="snap">Take Snapshot</button>
<canvas id="photo" width="320" height="240"></canvas>
<script>
const video = document.getElementById('cam');
const canvas = document.getElementById('photo');
const snapBtn = document.getElementById('snap');
const ctx = canvas.getContext('2d');
navigator.mediaDevices.getUserMedia({ video: { facingMode: "user" } })
.then(stream => {
video.srcObject = stream;
})
.catch(console.error);
snapBtn.addEventListener('click', () => {
// Draw current video frame onto canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
});
</script>
</code></pre>
<p>Highlights:</p>
<ul>
<li><code>facingMode: "user"</code> requests the front-facing camera on devices that support it;</li>
<li>Snapshot uses <canvas>’s drawImage() to “freeze” the current video frame.</li>
</ul>
<br />
<br />
<h4 id="requestAnAspectRatio">📸 Example 8: Requesting a Certain Aspect Ratio (Square)</h4>
<p>Shows how to request a specific aspect ratio (1:1) from the camera using the <code>aspectRatio</code> constraint and displays the actual achieved dimensions.</p>
<pre><code class="example"><script>
const videoElem = document.getElementById('preview');
const infoElem = document.getElementById('info');
async function startCamera() {
try {
// Desired aspect ratio: 1:1 (square)
const desiredRatio = 1 / 1;
const stream = await navigator.mediaDevices.getUserMedia({
video: {
aspectRatio: { ideal: desiredRatio }
}
});
videoElem.srcObject = stream;
// Inspect actual settings
const track = stream.getVideoTracks()[0];
const settings = track.getSettings();
infoElem.textContent =
`Got ${settings.width}×${settings.height} ` +
`(aspectRatio: ${settings.aspectRatio.toFixed(2)})`;
} catch (err) {
console.error('Error opening camera:', err);
infoElem.textContent = 'Unable to access camera with desired aspect ratio.';
}
}
startCamera();
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Specifies desired aspect ratio with <code>aspectRatio: { ideal: 1/1 }</code>;</li>
<li>Displays actual achieved resolution using <code>track.getSettings()</code>.</li>
</ul>
<br />
<br />
<h4 id="requestPortraitVideoStream">📱 Example 9: Requesting a Portrait Video Stream With facingMode:user</h4>
<p>Demonstrates capturing video in portrait orientation using the front-facing camera by specifying <code>facingMode: "user"</code> and swapped width/height dimensions (720x1280).</p>
<pre><code class="example"><script>
navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 720 },
height: { ideal: 1280 },
facingMode: "user"
},
audio: false
})
.then(stream => {
const video = document.getElementById('video');
video.srcObject = stream;
})
.catch(error => {
console.error('Error accessing camera:', error);
});
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Swaps dimensions (720×1280) to achieve portrait orientation;</li>
<li>Disables audio with <code>audio: false</code> for video-only capture.</li>
</ul>
<br />
<br />
<h4 id="basicVideoPreview"> 🎥 Example 10: Basic Video Preview (Promise-based)</h4>
<p>A simple example showing how to display a live video preview from the camera using promise-based <code>getUserMedia</code> with basic error handling.</p>
<pre><code class="example"><video id="preview" autoplay playsinline width="640" height="480"></video>
<script>
const videoEl = document.getElementById('preview');
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
// Attach the stream to the video element
videoEl.srcObject = stream;
})
.catch(err => {
console.error('Error accessing camera:', err);
});
</script>
</code></pre>
<p>What it does:</p>
<ul>
<li>Requests camera access;</li>
<li>If granted, pipes the live video into the <video> element;</li>
<li>Handles errors (e.g. user denies permission).</li>
</ul>
<br />
<br />
<h4 id="asyncPlusAudioOnly">🎤 Example 11: Async/Await + Audio Only</h4>
<p>Shows modern async/await syntax for accessing the microphone, with start/stop buttons to control audio capture</p>
<pre><code class="example"><button id="start">Start Mic</button>
<button id="stop" disabled>Stop Mic</button>
<audio id="player" controls></audio>
<script>
const startBtn = document.getElementById('start');
const stopBtn = document.getElementById('stop');
const player = document.getElementById('player');
let mediaStream;
startBtn.addEventListener('click', async () => {
try {
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
player.srcObject = mediaStream;
startBtn.disabled = true;
stopBtn.disabled = false;
} catch (err) {
console.error('Microphone access error:', err);
}
});
stopBtn.addEventListener('click', () => {
// Stop all audio tracks
mediaStream.getTracks().forEach(track => track.stop());
startBtn.disabled = false;
stopBtn.disabled = true;
});
</script>
</code></pre>
<p>Key points:</p>
<ul>
<li>Uses <code>async/await</code> for cleaner control flow;</li>
<li>Demonstrates enabling/disabling controls and stopping tracks.</li>
</ul>
<br />
<br />
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('pre > code').forEach(codeBlock => {
if (!navigator.clipboard) return;
// Create wrapper div
const wrapper = document.createElement('div');
wrapper.className = 'code-block-wrapper';
codeBlock.parentNode.parentNode.insertBefore(wrapper, codeBlock.parentNode);
wrapper.appendChild(codeBlock.parentNode);
// Create button
const btn = document.createElement('button');
btn.type = 'button';
btn.innerText = 'Copy';
btn.className = 'copy-code-button';
btn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(codeBlock.innerText);
btn.innerText = 'Copied';
setTimeout(() => (btn.innerText = 'Copy'), 2000);
} catch (err) {
btn.innerText = 'Copy failed';
console.error('Copy failed', err);
setTimeout(() => (btn.innerText = 'Copy failed'), 2000);
}
});
wrapper.appendChild(btn);
});
});
</script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
</html>