如何使用 JavaScript 的 getUserMedia() 访问前后摄像头

介绍

HTML5 引入了可以访问设备硬件的API ,包括MediaDevices API。此 API 提供对音频和视频等媒体输入设备的访问。

在此 API 的帮助下,开发人员可以访问音频和视频设备以在浏览器中流式传输和显示实时视频源。在本教程中,您将从用户的设备访问视频源,并使用getUserMedia方法将其显示在浏览器中

getUserMediaAPI使得使用介质的输入设备来产生MediaStream此 MediaStream 包含请求的媒体类型,无论是音频还是视频。使用 API 返回的流,可以在浏览器上显示视频源,这对于浏览器上的实时通信很有用。

当与MediaStream Recording API一起使用时,您可以记录和存储在浏览器上捕获的媒体数据。此 API 仅适用于其他新引入的 API 一样的安全来源,但它也适用于localhost和文件 URL。

先决条件

本教程将首先解释概念并使用Codepen演示示例在最后一步,您将为浏览器创建一个正常运行的视频源。

步骤 1 — 检查设备支持

首先,您将看到如何检查用户的浏览器是否支持该mediaDevicesAPI。此 API 存在于导航器界面中,包含用户代理的当前状态和身份。使用以下可以粘贴到 Codepen 中的代码执行检查:

if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
  console.log("Let's get this party started")
}

首先,如果这个检查mediaDevices的API中存在navigator,然后检查该getUserMediaAPI是内可用mediaDevices如果返回true,您就可以开始了。

第 2 步 – 请求用户许可

确认浏览器支持 后getUserMedia,您需要在用户代理上请求使用媒体输入设备的权限。通常,在用户授予权限后,Promise会返回解析为媒体流的 a。Promise当用户拒绝权限时不会返回值,这会阻止对这些设备的访问。

将以下行粘贴到 Codepen 中以请求许可:

navigator.mediaDevices.getUserMedia({video: true})

作为getUserMedia方法参数提供的对象称为constraints这决定了您请求访问哪些媒体输入设备的权限。例如,如果对象包含audio: true,将要求用户授予对音频输入设备的访问权限。

第 3 步 – 了解媒体约束

本节将介绍 的一般概念contraintsconstraints对象是MediaStreamConstraints指定要请求的媒体类型和每种媒体类型的要求的对象。您可以使用constraints对象为请求的流指定要求,例如要使用的流的分辨率 ( front, back)。

您必须在提出请求时指定audiovideoNotFoundError如果在用户的浏览器上找不到请求的媒体类型,将返回A。

如果您打算请求1280 x 720分辨率的视频流,您可以将constraints对象更新为如下所示:

{
  video: {
    width: 1280,
    height: 720,
  }
}

通过此更新,浏览器将尝试匹配流的指定质量设置。如果视频设备无法提供此分辨率,浏览器将返回其他可用分辨率。

为确保浏览器返回的分辨率不低于所提供的分辨率,您必须使用该min属性。以下是更新constraints对象以包含min属性的方法:

{
  video: {
    width: {
      min: 1280,
    },
    height: {
      min: 720,
    }
  }
}

这将确保返回的流分辨率至少为1280 x 720. 如果不能满足这个最低要求,承诺将被拒绝OverconstrainedError

在某些情况下,您可能会担心保存数据并需要流不超过设置的分辨率。当用户的计划有限时,这会派上用场。要启用此功能,请更新约束对象以包含一个max字段:

{
  video: {
    width: {
      min: 1280,
      max: 1920,
    },
    height: {
      min: 720,
      max: 1080
    }
  }
}

通过这些设置,浏览器将确保返回流不会低于1280 x 720也不会超过1920 x 1080

可以使用的其他术语包括exactidealideal设置通常与minmax属性一起使用以找到最接近提供的理想值的最佳设置。

您可以更新约束以使用ideal关键字:

{
  video: {
    width: {
      min: 1280,
      ideal: 1920,
      max: 2560,
    },
    height: {
      min: 720,
      ideal: 1080,
      max: 1440
    }
  }
}

要告诉浏览器使用设备上的前置或后置(在移动设备上)摄像头,您可以facingModevideo对象中指定一个属性

{
  video: {
    width: {
      min: 1280,
      ideal: 1920,
      max: 2560,
    },
    height: {
      min: 720,
      ideal: 1080,
      max: 1440
    },
    facingMode: 'user'
  }
}

此设置将在所有设备中始终使用前置摄像头。要在移动设备上使用后置摄像头,您可以将该facingMode属性更改environment

{
  video: {
    ...
    facingMode: {
      exact: 'environment'
    }
  }
}

第 4 步 – 使用该enumerateDevices方法

enumerateDevices调用方法时,它会返回用户 PC 上可用的所有可用输入媒体设备。

通过该方法,您可以为用户提供用于流式传输音频或视频内容的输入媒体设备的选项。此方法返回一个已Promise解析的MediaDeviceInfo数组,该数组包含有关每个设备的信息。

下面的代码段显示了如何使用此方法的示例:

async function getDevices() {
  const devices = await navigator.mediaDevices.enumerateDevices();
}

每个设备的示例响应如下所示:

{
  deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj",
  groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875",
  kind: "audiooutput",
  label: "",
}

注意:除非有可用的流可用,或者用户已授予设备访问权限,否则不会返回标签。

步骤 5 — 在浏览器上显示视频流

您已经完成了请求和访问媒体设备的过程,配置了包含所需分辨率的约束,并选择了录制视频所需的摄像机。

完成所有这些步骤后,您至少需要查看流是否根据配置的设置进行传送。为确保这一点,您将使用该<video>元素在浏览器上显示视频流。

如前所述,该getUserMedia方法返回Promise可以解析为流的 。可以使用该createObjectURL方法将返回的流转换为对象 URL 此 URL 将被设置为视频源。

您将创建一个简短的演示,让用户从可用的视频设备列表中进行选择。使用enumerateDevices方法。

这是一种navigator.mediaDevices方法。它列出了可用的媒体设备,例如麦克风和摄像头。它返回一个Promise可解析的对象数组,详细说明可用的媒体设备。

创建一个index.html文件并使用以下代码更新内容:

索引.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>
<body>
<div class="display-cover">
    <video autoplay></video>
    <canvas class="d-none"></canvas>

    <div class="video-options">
        <select name="" id="" class="custom-select">
            <option value="">Select camera</option>
        </select>
    </div>

    <img class="screenshot-image d-none" alt="">

    <div class="controls">
        <button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button>
        <button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button>
        <button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button>
    </div>
</div>

<script src="https://unpkg.com/feather-icons"></script>
<script src="script.js"></script>
</body>
</html>

在上面的代码段中,您已经为视频设置了所需的元素和几个控件。还包括一个按钮,用于截取当前视频源的屏幕截图。

现在,让我们稍微设计一下这些组件。

创建一个style.css文件并将以下样式复制到其中。包含Bootstrap是为了减少您需要编写的 CSS 量来使组件运行。

样式文件
.screenshot-image {
    width: 150px;
    height: 90px;
    border-radius: 4px;
    border: 2px solid whitesmoke;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
    position: absolute;
    bottom: 5px;
    left: 10px;
    background: white;
}

.display-cover {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 70%;
    margin: 5% auto;
    position: relative;
}

video {
    width: 100%;
    background: rgba(0, 0, 0, 0.2);
}

.video-options {
    position: absolute;
    left: 20px;
    top: 30px;
}

.controls {
    position: absolute;
    right: 20px;
    top: 20px;
    display: flex;
}

.controls > button {
    width: 45px;
    height: 45px;
    text-align: center;
    border-radius: 100%;
    margin: 0 6px;
    background: transparent;
}

.controls > button:hover svg {
    color: white !important;
}

@media (min-width: 300px) and (max-width: 400px) {
    .controls {
        flex-direction: column;
    }

    .controls button {
        margin: 5px 0 !important;
    }
}

.controls > button > svg {
    height: 20px;
    width: 18px;
    text-align: center;
    margin: 0 auto;
    padding: 0;
}

.controls button:nth-child(1) {
    border: 2px solid #D2002E;
}

.controls button:nth-child(1) svg {
    color: #D2002E;
}

.controls button:nth-child(2) {
    border: 2px solid #008496;
}

.controls button:nth-child(2) svg {
    color: #008496;
}

.controls button:nth-child(3) {
    border: 2px solid #00B541;
}

.controls button:nth-child(3) svg {
    color: #00B541;
}

.controls > button {
    width: 45px;
    height: 45px;
    text-align: center;
    border-radius: 100%;
    margin: 0 6px;
    background: transparent;
}

.controls > button:hover svg {
    color: white;
}

下一步是向演示添加功能。使用该enumerateDevices方法,您将获得可用的视频设备并将其设置为 select 元素中的选项。创建一个名为的文件script.js并使用以下代码段更新它:

脚本.js
feather.replace();

const controls = document.querySelector('.controls');
const cameraOptions = document.querySelector('.video-options>select');
const video = document.querySelector('video');
const canvas = document.querySelector('canvas');
const screenshotImage = document.querySelector('img');
const buttons = [...controls.querySelectorAll('button')];
let streamStarted = false;

const [play, pause, screenshot] = buttons;

const constraints = {
  video: {
    width: {
      min: 1280,
      ideal: 1920,
      max: 2560,
    },
    height: {
      min: 720,
      ideal: 1080,
      max: 1440
    },
  }
};

const getCameraSelection = async () => {
  const devices = await navigator.mediaDevices.enumerateDevices();
  const videoDevices = devices.filter(device => device.kind === 'videoinput');
  const options = videoDevices.map(videoDevice => {
    return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
  });
  cameraOptions.innerHTML = options.join('');
};

play.onclick = () => {
  if (streamStarted) {
    video.play();
    play.classList.add('d-none');
    pause.classList.remove('d-none');
    return;
  }
  if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
    const updatedConstraints = {
      ...constraints,
      deviceId: {
        exact: cameraOptions.value
      }
    };
    startStream(updatedConstraints);
  }
};

const startStream = async (constraints) => {
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  handleStream(stream);
};

const handleStream = (stream) => {
  video.srcObject = stream;
  play.classList.add('d-none');
  pause.classList.remove('d-none');
  screenshot.classList.remove('d-none');
  streamStarted = true;
};

getCameraSelection();

在上面的代码片段中,发生了几件事。让我们分解它们:

  1. feather.replace():这个方法调用实例化了feather,它是一个用于web开发的图标集。
  2. constraints变量保存流的初始配置。这将扩展到包括用户选择的媒体设备。
  3. getCameraSelection: 这个函数调用enumerateDevices方法。然后,您从解析的数组中过滤Promise并选择视频输入设备。根据过滤结果,您可以<option><select>元素创建
  4. 调用该getUserMedia方法发生在按钮onclick侦听play器中。在这里,您将在开始流之前检查用户的浏览器是否支持此方法。
  5. 接下来,您将调用startStream带有constraints参数的函数。getUserMedia使用提供的constraints. handleStream使用来自已解析的流调用Promise此方法将返回的流设置​​为视频元素的srcObject.

接下来,您将在页面上点击侦听器添加到按钮控件pausestop并采取screenshots此外,您将向<select>元素添加一个侦听器,以更新所选视频设备的流约束。

script.js使用以下代码更新文件:

脚本.js
...
cameraOptions.onchange = () => {
  const updatedConstraints = {
    ...constraints,
    deviceId: {
      exact: cameraOptions.value
    }
  };
  startStream(updatedConstraints);
};

const pauseStream = () => {
  video.pause();
  play.classList.remove('d-none');
  pause.classList.add('d-none');
};

const doScreenshot = () => {
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);
  screenshotImage.src = canvas.toDataURL('image/webp');
  screenshotImage.classList.remove('d-none');
};

pause.onclick = pauseStream;
screenshot.onclick = doScreenshot;

现在,当您index.html在浏览器中打开文件时,单击“播放”按钮将启动流。

这是一个完整的演示:

https://codepen.io/chrisbeast/pen/ebYwpX

结论

本教程介绍了getUserMediaAPI。它是 HTML5 的一个有趣补充,它简化了在网络上捕获媒体的过程。

API 采用一个参数 ( constraints),可用于配置对音频和视频输入设备的访问。它还可用于指定应用程序所需的视频分辨率。

您可以进一步扩展演示,让用户可以选择保存截取的屏幕截图,以及在 MediaStream Recording API 的帮助下录制和存储视频和音频数据。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁