简介
随着HTML5的推出,引入了可以访问设备硬件的API,包括MediaDevicesAPI。此接口提供对音频和视频等媒体输入设备的访问。
在这个接口的帮助下,开发者可以访问音视频设备,在浏览器中直播和显示视频提要。在本教程中,您将从用户设备访问视频提要,并使用getUserMedia
方法在浏览器中显示它。
getUserMedia
接口利用媒体输入设备生成MediaStream.此MediaStream包含请求的媒体类型,无论是音频还是视频。使用API返回的流,可以在浏览器上显示视频提要,这对于在浏览器上进行实时通信非常有用。
与媒体流录制API,配合使用时,您可以录制并存储在浏览器上捕获的媒体数据。与其他新推出的接口一样,该接口只支持安全来源,但也支持localhost
和文件URL。
前提条件
- 具备基本的JavaScript知识。如果您不熟悉JavaScript](https://www.digitalocean.com/community/tutorial_series/how-to-code-in-javascript),可以尝试阅读[How to Code in Java]系列。
本教程将首先解释概念并使用Codepen演示示例。在最后一步中,您将为浏览器创建一个功能正常的视频源。
第一步-检查设备支持
首先,您将了解如何检查用户的浏览器是否支持mediaDevices
接口。此接口存在于navigator接口中,包含用户代理的当前状态和标识。可以使用以下代码执行检查,这些代码可以粘贴到Codesen中:
1if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
2 console.log("Let's get this party started")
3}
首先检查Navigator
中是否存在mediaDevices
接口,然后检查mediaDevices
中是否存在getUserMedia
接口。如果返回`true‘,您就可以开始了。
第二步-请求用户权限
确认浏览器支持getUserMedia
后,您需要申请许可才能使用用户代理上的媒体输入设备。一般情况下,用户授权后,会返回一个Promise
,解析为一个媒体流。当用户拒绝访问这些设备时,不会返回该Promise
。
将以下行粘贴到Codesen中以请求权限:
1navigator.mediaDevices.getUserMedia({video: true})
作为getUserMedia
方法的参数提供的对象称为constraints
。这决定了您正在请求访问哪些媒体输入设备。例如,如果对象包含dio:true
,则会要求用户授予对音频输入设备的访问权限。
Step 3 -了解媒体约束
本节将涵盖_合同
_的一般概念。constraints
对象是一个MediaStreamConstraints
对象,它指定了要请求的媒体类型以及每种媒体类型的要求。您可以使用constraints
对象来指定对请求的流的要求,比如要使用的流的分辨率(Front
,back
)。
请求时必须指定audio
或avio
。如果在用户浏览器上找不到请求的媒体类型,则返回NotFoundError
。
如果您想请求1280 x 720
分辨率的视频流,可以更新constraints
对象如下:
1{
2 video: {
3 width: 1280,
4 height: 720,
5 }
6}
在此更新中,浏览器将尝试匹配流的指定质量设置。如果视频设备无法提供此分辨率,浏览器将返回其他可用分辨率。
为了确保浏览器返回的分辨率不低于提供的分辨率,您必须使用min
属性。下面是如何更新constraints
对象以包含min
属性的方法:
1{
2 video: {
3 width: {
4 min: 1280,
5 },
6 height: {
7 min: 720,
8 }
9 }
10}
这样可以保证返回的流分辨率至少为1280 x 720
。如果无法满足此最低要求,则承诺将被拒绝,并返回OverconstrainedError
。
在某些情况下,您可能会担心保存数据,并需要流不超过设置的分辨率。当用户使用有限的计划时,这会派上用场。要启用此功能,请更新Constraints对象,使其包含一个Max
字段:
1{
2 video: {
3 width: {
4 min: 1280,
5 max: 1920,
6 },
7 height: {
8 min: 720,
9 max: 1080
10 }
11 }
12}
有了这些设置,浏览器会确保返回的流不会低于1280 x 720
,也不会超过1920 x 1080
。
其他可以使用的术语包括精确
和理想
。ideal
设置通常与min
和Max
属性一起使用,以查找与提供的理想值最接近的最佳设置。
您可以更新约束以使用ideal
关键字:
1{
2 video: {
3 width: {
4 min: 1280,
5 ideal: 1920,
6 max: 2560,
7 },
8 height: {
9 min: 720,
10 ideal: 1080,
11 max: 1440
12 }
13 }
14}
要告诉浏览器在设备上使用前置或后置摄像头,可以在avio
对象中指定一个facingMode
属性:
1{
2 video: {
3 width: {
4 min: 1280,
5 ideal: 1920,
6 max: 2560,
7 },
8 height: {
9 min: 720,
10 ideal: 1080,
11 max: 1440
12 },
13 facingMode: 'user'
14 }
15}
此设置将在所有设备中始终使用前置摄像头。若要使用移动端的后置摄像头,可以将facingMode
属性修改为Environmental
。
1{
2 video: {
3 ...
4 facingMode: {
5 exact: 'environment'
6 }
7 }
8}
第四步-使用枚举设备
方法
当调用EumerateDevices
方法时,它返回用户PC上所有可用的输入媒体设备。
通过该方法,您可以为用户提供用于流式传输音频或视频内容的输入媒体设备的选项。此方法返回解析为MediaDeviceInfo数组的Promise
,该数组包含有关每个设备的信息。
下面的代码片段显示了如何使用此方法的示例:
1async function getDevices() {
2 const devices = await navigator.mediaDevices.enumerateDevices();
3}
每台设备的响应示例如下所示:
1{
2 deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj",
3 groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875",
4 kind: "audiooutput",
5 label: "",
6}
<$>[备注] 注意: 除非有可用的流,或者用户已经授予设备访问权限,否则不会返回标签。 <$>
Step 5-在浏览器上显示视频流
您已经完成了请求和访问媒体设备的过程,配置了包含所需分辨率的约束条件,并选择了录制视频所需的摄像机。
在完成所有这些步骤之后,您至少需要查看流是否根据配置的设置进行传递。为了确保这一点,您将使用<Video>
元素在浏览器上显示视频流。
如前所述,getUserMedia
方法返回一个可以解析为流的Promise
。可以使用createObjectURL
方法将返回的流转换为对象URL。此URL将被设置为视频源。
您将创建一个简短的演示,让用户从可用的视频设备列表中进行选择。使用enumerateDevices
方法。
这是一个navigator.mediaDevices
方法。它列出了可用的媒体设备,如麦克风和摄像头。它返回一个Promise
,可解析为一个对象数组,详细说明可用的媒体设备。
创建index.html
文件,并使用以下代码更新内容:
1[label index.html]
2<!doctype html>
3<html lang="en">
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport"
7 content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
8 <meta http-equiv="X-UA-Compatible" content="ie=edge">
9 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
10 <link rel="stylesheet" href="style.css">
11 <title>Document</title>
12</head>
13<body>
14<div class="display-cover">
15 <video autoplay></video>
16 <canvas class="d-none"></canvas>
17
18 <div class="video-options">
19 <select name="" id="" class="custom-select">
20 <option value="">Select camera</option>
21 </select>
22 </div>
23
24 <img class="screenshot-image d-none" alt="">
25
26 <div class="controls">
27 <button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button>
28 <button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button>
29 <button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button>
30 </div>
31</div>
32
33<script src="https://unpkg.com/feather-icons"></script>
34<script src="script.js"></script>
35</body>
36</html>
在上面的代码片段中,您已经为视频设置了需要的元素和几个控件。还包括一个按钮,用于截取当前视频提要的屏幕截图。
现在,让我们稍微调整一下这些组件的样式。
创建一个style.css
文件,并将以下样式复制到其中。Bootstrap被包括在内,以减少您需要编写的CSS数量,以使组件运行。
1[label style.css]
2.screenshot-image {
3 width: 150px;
4 height: 90px;
5 border-radius: 4px;
6 border: 2px solid whitesmoke;
7 box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
8 position: absolute;
9 bottom: 5px;
10 left: 10px;
11 background: white;
12}
13
14.display-cover {
15 display: flex;
16 justify-content: center;
17 align-items: center;
18 width: 70%;
19 margin: 5% auto;
20 position: relative;
21}
22
23video {
24 width: 100%;
25 background: rgba(0, 0, 0, 0.2);
26}
27
28.video-options {
29 position: absolute;
30 left: 20px;
31 top: 30px;
32}
33
34.controls {
35 position: absolute;
36 right: 20px;
37 top: 20px;
38 display: flex;
39}
40
41.controls > button {
42 width: 45px;
43 height: 45px;
44 text-align: center;
45 border-radius: 100%;
46 margin: 0 6px;
47 background: transparent;
48}
49
50.controls > button:hover svg {
51 color: white !important;
52}
53
54@media (min-width: 300px) and (max-width: 400px) {
55 .controls {
56 flex-direction: column;
57 }
58
59 .controls button {
60 margin: 5px 0 !important;
61 }
62}
63
64.controls > button > svg {
65 height: 20px;
66 width: 18px;
67 text-align: center;
68 margin: 0 auto;
69 padding: 0;
70}
71
72.controls button:nth-child(1) {
73 border: 2px solid #D2002E;
74}
75
76.controls button:nth-child(1) svg {
77 color: #D2002E;
78}
79
80.controls button:nth-child(2) {
81 border: 2px solid #008496;
82}
83
84.controls button:nth-child(2) svg {
85 color: #008496;
86}
87
88.controls button:nth-child(3) {
89 border: 2px solid #00B541;
90}
91
92.controls button:nth-child(3) svg {
93 color: #00B541;
94}
95
96.controls > button {
97 width: 45px;
98 height: 45px;
99 text-align: center;
100 border-radius: 100%;
101 margin: 0 6px;
102 background: transparent;
103}
104
105.controls > button:hover svg {
106 color: white;
107}
下一步是向演示中添加功能。您将使用枚举设备
方法获取可用的视频设备,并将其设置为选择元素中的选项。创建一个名为script.js
的文件,并使用以下代码片段对其进行更新:
1[label script.js]
2feather.replace();
3
4const controls = document.querySelector('.controls');
5const cameraOptions = document.querySelector('.video-options>select');
6const video = document.querySelector('video');
7const canvas = document.querySelector('canvas');
8const screenshotImage = document.querySelector('img');
9const buttons = [...controls.querySelectorAll('button')];
10let streamStarted = false;
11
12const [play, pause, screenshot] = buttons;
13
14const constraints = {
15 video: {
16 width: {
17 min: 1280,
18 ideal: 1920,
19 max: 2560,
20 },
21 height: {
22 min: 720,
23 ideal: 1080,
24 max: 1440
25 },
26 }
27};
28
29const getCameraSelection = async () => {
30 const devices = await navigator.mediaDevices.enumerateDevices();
31 const videoDevices = devices.filter(device => device.kind === 'videoinput');
32 const options = videoDevices.map(videoDevice => {
33 return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
34 });
35 cameraOptions.innerHTML = options.join('');
36};
37
38play.onclick = () => {
39 if (streamStarted) {
40 video.play();
41 play.classList.add('d-none');
42 pause.classList.remove('d-none');
43 return;
44 }
45 if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
46 const updatedConstraints = {
47 ...constraints,
48 deviceId: {
49 exact: cameraOptions.value
50 }
51 };
52 startStream(updatedConstraints);
53 }
54};
55
56const startStream = async (constraints) => {
57 const stream = await navigator.mediaDevices.getUserMedia(constraints);
58 handleStream(stream);
59};
60
61const handleStream = (stream) => {
62 video.srcObject = stream;
63 play.classList.add('d-none');
64 pause.classList.remove('d-none');
65 screenshot.classList.remove('d-none');
66 streamStarted = true;
67};
68
69getCameraSelection();
在上面的代码片段中,有几件事正在发生。让我们来分析一下它们:
1.fefether.place()
:该方法调用实例化Feather,,为Web开发设置的图标。
2.constraints
变量保存流的初始配置。这将扩展到包括用户选择的媒体设备。
3.getCameraSelection
:该函数调用枚举设备
方法。然后,从解析的Promise
中过滤数组,选择视频输入设备。根据过滤后的结果,为<SELECT>
元素创建<Option>
。
4.在play
按钮的onclick
监听器内调用getUserMedia
方法。在这里,您将在启动流之前检查用户的浏览器是否支持此方法。
5.接下来,调用带有constraints
参数的startStream
函数。使用提供的constraints
调用getUserMedia
方法。handleStream
使用解析后的Promise
中的流调用。该方法将返回流设置为视频元素的srcObject
。
接下来,您将向页面上的按钮控件添加单击侦听器,以pause
、stop
和screenshots
。此外,您还将向
元素添加一个侦听器
使用以下代码更新script.js
文件:
1[label script.js]
2...
3cameraOptions.onchange = () => {
4 const updatedConstraints = {
5 ...constraints,
6 deviceId: {
7 exact: cameraOptions.value
8 }
9 };
10 startStream(updatedConstraints);
11};
12
13const pauseStream = () => {
14 video.pause();
15 play.classList.remove('d-none');
16 pause.classList.add('d-none');
17};
18
19const doScreenshot = () => {
20 canvas.width = video.videoWidth;
21 canvas.height = video.videoHeight;
22 canvas.getContext('2d').drawImage(video, 0, 0);
23 screenshotImage.src = canvas.toDataURL('image/webp');
24 screenshotImage.classList.remove('d-none');
25};
26
27pause.onclick = pauseStream;
28screenshot.onclick = doScreenshot;
现在,当您在浏览器中打开index.html
文件时,点击播放 按钮将启动该流。
以下是一个完整的演示:
https://codepen.io/chrisbeast/pen/ebYwpX
结论
本教程介绍了getUserMedia
接口。这是HTML5的一个有趣的补充,它简化了在网络上捕获媒体的过程。
该接口带有一个参数(constraints
),可以用来配置音视频输入设备的访问。它还可用于指定您的应用程序所需的视频分辨率。
您可以进一步扩展Demo,为用户提供保存截图的选项,以及借助MediaStream录制API录制和存储视频和音频数据。