MediaRecorder API 简介[译]

2,750 阅读4分钟

在网络上,我们可以从用户的相机、麦克风甚至桌面捕获媒体流。我们可以使用这些媒体流通过 WebRTC 进行实时视频聊天,通过 MediaRecorder API 我们还可以直接在浏览器中记录和保存用户的音频或视频。

下面我们使用 HTML、CSS 和 JavaScript 构建一个简单的录音机应用来了解一下 MediaRecorder API 。在撰写本文时,支持的浏览器包括 Firefox,Chrome 和Opera。Edge 和 Safari 后续也会支持。 我们先来创建一个文件夹,再创建一个 HTML 文件及 CSS 文件供我们使用。 开始 要构建此应用,我们只需要一个文本编辑器和一个支持 MediaRecorded API 的浏览器。我们将 CSS 文件命名为 web-recorder-style.css

HTML 代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Web Recorder</title>
    <link rel="stylesheet" href="./web-recorder-style.css" />
  </head>
  <body>
    <header>
      <h1>Web Recorder</h1>
    </header>
    <main>
      <div class="controls">
        <button type="button" id="mic">Get Microphone</button>
        <button type="button" id="record" hidden>Record</button>
      </div>
      <ul id="recordings"></ul>
    </main>
    <script></script>
  </body>
</html>

CSS 代码:

*{box-sizing: border-box;}
html,
body{min-height: 100vh; margin: 0; padding: 0;}
body{font-family: Helvetica, Arial, sans-serif; color: #0d122b; display: flex; flex-direction: column; padding-left: 1em; padding-right: 1em;}
h1{text-align: center; font-weight: 100;}
header{border-bottom: 1px solid #0d122b; margin-bottom: 2em;}
main{flex-grow: 2;}
.controls{text-align: center;}
button{font-size: 18px; font-weight: 200; padding: 1em; width: 200px; background: transparent; border: 4px solid #f22f46; border-radius: 4px; transition: all 0.4s ease 0s; cursor: pointer; color: #f22f46; margin-bottom: 2em;}
button:hover,
button:focus{background: #f22f46; color: #fff;}
#recordings{list-style-type: none; text-align: center; padding: 0; max-width: 600px; margin: 0 auto;}
#recordings li{display: flex; flex-direction: column; margin-bottom: 1em;}
#recordings audio{border-radius: 4px; margin: 0 auto 0.5em;}
a{color: #0d122b;}
.error{color: #f22f46; text-align: center;}

在浏览器打开页面你会看到如下内容:

图片

现在让我们来看看 MediaRecorder API。

MediaRecorder API

在使用 MediaRecorder API 时需要有媒体流。我们可以从 <video><audio> 中获取,也可以通过调用 getUserMedia 来捕获用户的摄像头和麦克风。一旦获得流信息,便可以初始化 MediaRecorder

在录音过程中,MediaRecorder 会广播 dataavailable 事件,并将记录的数据作为事件的一部分。我们可以监听这些事件并将数据块存放在数组中。录音完成后,可以将数组里的数据合成 Blob 对象。通过调用 MediaRecorder 对象上的 startstop 来控制录制的开始和结束。

下面,就让我们来实践一下。

getUserMedia

首先,我们与页面结合起来,通过按钮来获取用户的麦克风媒体流。在页面 <script> 标签里写上以下内容

window.addEventListener('DOMContentLoaded', () => {
  const getMic = document.getElementById('mic');
  const recordButton = document.getElementById('record');
  const list = document.getElementById('recordings');

});

接下来,我们要检查浏览器是否支持。如果不支持,我们在页面显示提示信息。

  window.addEventListener('DOMContentLoaded', () => {
    const getMic = document.getElementById('mic');
    const recordButton = document.getElementById('record');
    const list = document.getElementById('recordings');

    if ('MediaRecorder' in window) {
      // everything is good, let's go ahead
    } else {
      renderError("Sorry, your browser doesn't support the MediaRecorder API, so this demo will not work.");
    }
  });

在事件之后,我们添加 renderErro 方法。

function renderError(message) {
  const main = document.querySelector('main');
  main.innerHTML = `<div class="error"><p>${message}</p></div>`;
}

如果可以访问 MediaRecorder ,那么我们需要麦克风权限来进行录制。这里会用到 getUserMedia API。我们不会直接请求麦克风,而是由用户点击按钮来使用麦克风并进行授权。

if ('MediaRecorder' in window) {
  getMic.addEventListener('click', async () => {
    getMic.setAttribute('hidden', 'hidden');
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: true,
        video: false
      });
      console.log(stream);
    } catch {
      renderError(
        'You denied access to the microphone so this demo will not work.'
      );
    }
  });
} else {

调用 navigator.mediaDevices.getUserMedia 将返回一个 promise ,如果用户允许访问,我们便可以成功获取媒体流。

用户可能会禁止访问麦克风,我们使用 try/catch 处理异常。如果用户禁止则会执行 catch 里的 renderError 方法。

保存文件并在浏览器里打开。点击「Get Microphone」按钮。会显示是否允许使用麦克风的提示,允许后在控件台会看到打印出的 MediaStream 信息。

图片

录音

现在我们获得了麦克风的使用权限,下来我们要定义一些变量供后面使用。首先,MIME 类型使用 auto/webm ,这个类型被浏览器广泛支持。我们还需要创建一个名为 chunks 的数组用来保存录音中的数据。

MediaRecorder 通过用户麦克风获取的媒体流及我们定义的 MIME 类型选项进行初始化。我们把之前的 console.log 替换掉:

try {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: false
  });
  const mimeType = 'audio/webm';
  let chunks = [];
  const recorder = new MediaRecorder(stream, { type: mimeType });

我们为创建好的 MediaRecorder 设置一些监听事件。录音机会广播很多不同的事件,很多与它本身的交互有关,所以我们可以监听到开始、暂停、恢复和停止的事件。最主要的事件是 dataavailable,它会在录音过程中定期广播,这个事件中包含一段录音,我们可以把他存到定义好的 chunks 数组中。

const recorder = new MediaRecorder(stream, { type: mimeType });
recorder.addEventListener('dataavailable', event => {
  if (typeof event.data === 'undefined') return;
  if (event.data.size === 0) return;
  chunks.push(event.data);
});
recorder.addEventListener('stop', () => {
  const recording = new Blob(chunks, {
    type: mimeType
  });
  renderRecording(recording, list);
  chunks = [];
});

我们很快就会实现 renderRecording 方法。现在我们需要实现按钮的开始和停止。

我们需要取消隐藏录制按钮,然后在点击时启动或停止录制,具体取决于录音机的状态。代码如下:

   chunks = [];
 });
 recordButton.removeAttribute('hidden');
 recordButton.addEventListener('click', () => {
   if (recorder.state === 'inactive') {
     recorder.start();
     recordButton.innerText = 'Stop';
   } else {
     recorder.stop();
     recordButton.innerText = 'Record';
   }
 });

我们将把录音在 <audio> 元素上呈现并提供下载链接,以便用户可以将他们的录音下载下来。我们可以使用 URL.createObjectUrl 方法将 Blob 转成 URL。转成的 URL 可以做为 <audio>src 及锚点元素的 href 使用。为保证文件能够下载,我们设置 download 属性。

renderRecording 方法主要创建 DOM 元素及在录制时创建文件名。我们将它放到 renderError 方法下面。

  function renderRecording(blob, list) {
    const blobUrl = URL.createObjectURL(blob);
    const li = document.createElement('li');
    const audio = document.createElement('audio');
    const anchor = document.createElement('a');
    anchor.setAttribute('href', blobUrl);
    const now = new Date();
    anchor.setAttribute(
      'download',
      `recording-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDay().toString().padStart(2, '0')}--${now.getHours().toString().padStart(2, '0')}-${now.getMinutes().toString().padStart(2, '0')}-${now.getSeconds().toString().padStart(2, '0')}.webm`
    );
    anchor.innerText = 'Download';
    audio.setAttribute('src', blobUrl);
    audio.setAttribute('controls', 'controls');
    li.appendChild(audio);
    li.appendChild(anchor);
    list.appendChild(li);
  }

图片

测试

在浏览器中打开页面并点击「Get Microphone」按钮。在权限对话框点击「允许」,然后点击「Record」。自己录一段信息并播放。

WebM 文件

如果您下载了其中一个录音,您可能会发现没有能够播放 WebM 文件的媒体播放器。WebM 是音频和视频的开源格式,它主要被浏览器所支持。如果你有 VLC 播放器便可以播放,不然的话你就需要将它转成 MP3 或 WAV 文件。

现在你的浏览器变成录音机了

MediaRecorder API 是浏览器新增的强大功能。在本文我们已经看到了它的录音能力,但它不仅于此。我们的应用没有保存功能,刷新页面后录音会丢失。我们可以使用 IndexedDb 或发送到服务器保存。如果 WebM 不是你想要的格式,可以考虑在前端进行重新编码,尽管这可能是 WebAssembly 的工作...

这里有一个 live demoGithub 源码在这里

作者:Phil Nash

译者:Mark Wong

原文:www.twilio.com/blog/medias…