如何使用MediaStream API录制音频 (详细指南)

707 阅读3分钟

How to Record Audio Using the MediaStream API

媒体捕获和流API(又称MediaStream API)允许你从用户的麦克风中录制音频,然后将录制的音频或媒体元素作为轨道获得。然后你可以在录制后直接播放这些音轨,或者将媒体上传到你的服务器上。

在本教程中,我们将创建一个网站,使用媒体流API让用户录制一些东西,然后将录制的音频上传到服务器保存。用户也将能够看到并播放所有上传的录音。

你可以在这个GitHub存储库中找到本教程的完整代码。

设置服务器

我们首先要创建一个Node.jsExpress服务器。所以首先要确保下载并安装Node.js,如果你的机器上没有的话。

创建一个目录

创建一个新的目录,用来存放项目,并切换到该目录。

mkdir recording-tutorial
cd recording-tutorial

初始化项目

然后,用npm初始化该项目。

npm init -y

选项-y ,用默认值创建package.json

安装依赖项

接下来,我们将为我们创建的服务器安装Express和nodemon,以便在有任何变化时重启服务器。

npm i express nodemon

创建Express服务器

现在我们可以开始创建一个简单的服务器。在项目的根目录下创建index.js ,内容如下。

const path = require('path');
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.use(express.static('public/assets'));

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});

这就创建了一个服务器,除非在环境中设置了端口,否则它将在端口3000 上运行,并且它暴露了一个目录public/assets --我们将很快创建这个目录--它将存放JavaScript和CSS文件以及图片。

添加一个脚本

最后,在package.json ,在scripts 下添加一个start 脚本。

"scripts": {
  "start": "nodemon index.js"
},

启动网络服务器

让我们测试一下我们的服务器。运行下面的程序来启动服务器。

npm start

而服务器应该在3000端口启动。你可以尝试在localhost:3000 ,但你会看到一个消息说 "无法GET /",因为我们还没有定义任何路由。

创建录音页面

接下来,我们将创建一个页面,作为网站的主页面。用户将使用这个页面来录制、查看和播放录音。

创建public 目录,并在里面创建一个index.html 文件,内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Record</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
  <link href="/css/index.css" rel="stylesheet" />
</head>
<body class="pt-5">
  <div class="container">
    <h1 class="text-center">Record Your Voice</h1>
    <div class="record-button-container text-center mt-5">
      <button class="bg-transparent border btn record-button rounded-circle shadow-sm text-center" id="recordButton">
        <img src="/images/microphone.png" alt="Record" class="img-fluid" />
      </button>
    </div>
  </div>
</body>
</html>

这个页面使用Bootstrap 5进行风格设计。现在,该页面只是显示一个按钮,用户可以用它来录音。

请注意,我们正在使用一个图片作为麦克风。你可以在Iconscout上下载该图标,也可以使用GitHub仓库中的修改版本。

下载该图标并将其放置在public/assets/images 内,名称为microphone.png

添加样式

我们也要链接样式表index.css ,所以创建一个public/assets/css/index.css ,内容如下。

.record-button {
  height: 8em;
  width: 8em;
  border-color: #f3f3f3 !important;
}

.record-button:hover {
  box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
}

创建路由

最后,我们只需要在index.js 中添加新的路由。在app.listen 前添加以下内容。

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public/index.html'));
});

如果服务器还没有运行,用npm start ,启动服务器。然后在浏览器中转到localhost:3000 。你会看到一个记录按钮。

Record Page

现在,这个按钮什么也不做。我们将需要绑定一个点击事件,以触发录音。

创建一个public/assets/js/record.js 文件,内容如下。

//initialize elements we'll use
const recordButton = document.getElementById('recordButton');
const recordButtonImage = recordButton.firstElementChild;

let chunks = []; //will be used later to record audio
let mediaRecorder = null; //will be used later to record audio
let audioBlob = null; //the blob that will hold the recorded audio

我们正在初始化我们以后要使用的变量。然后创建一个record 函数,它将是recordButton 的点击事件的事件监听器。

function record() {
  //TODO start recording
}

recordButton.addEventListener('click', record);

我们也要把这个函数作为事件监听器附加到记录按钮上。

媒体录制

为了开始录制,我们需要使用mediaDevices.getUserMedia()方法。

这个方法允许我们获得一个流,只有在用户为网站提供许可的情况下,才能录制用户的音频和/或视频。getUserMedia 方法允许我们访问本地输入设备。

getUserMedia 该方法接受一个MediaStreamConstraints对象作为参数,该对象包括一组约束条件,指定我们将从 。这些约束条件可以是具有布尔值的音频和视频。getUserMedia

如果值为false,意味着我们对访问这个设备或录制这个媒体不感兴趣。

getUserMedia 返回一个承诺。如果用户允许网站录音,承诺的履行程序会收到一个MediaStream对象,我们可以用它来媒体捕获用户的视频或音频流。

媒体捕获和流

为了使用MediaStream API对象来捕捉媒体轨迹,我们需要使用MediaRecorder接口。我们需要创建一个该接口的新对象,该对象在构造函数中接受MediaStream对象,并允许我们通过其方法轻松控制录制。

record 函数中,添加以下内容。

//check if browser supports getUserMedia
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
  alert('Your browser does not support recording!');
  return;
}

// browser supports getUserMedia
// change image in button
recordButtonImage.src = `/images/${mediaRecorder && mediaRecorder.state === 'recording' ? 'microphone' : 'stop'}.png`;
if (!mediaRecorder) {
  // start recording
  navigator.mediaDevices.getUserMedia({
    audio: true,
  })
    .then((stream) => {
      mediaRecorder = new MediaRecorder(stream);
      mediaRecorder.start();
      mediaRecorder.ondataavailable = mediaRecorderDataAvailable;
      mediaRecorder.onstop = mediaRecorderStop;
    })
    .catch((err) => {
      alert(`The following error occurred: ${err}`);
      // change image in button
      recordButtonImage.src = '/images/microphone.png';
    });
} else {
  // stop recording
  mediaRecorder.stop();
}

浏览器支持

我们首先要检查navigator.mediaDevicesnavigator.mediaDevices.getUserMedia 是否被定义,因为有些浏览器如Internet Explorer、Android上的Chrome或其他浏览器并不支持它

此外,使用getUserMedia 需要安全的网站,这意味着要么是使用HTTPS加载的页面,file:// ,要么是从localhost 。因此,如果页面没有安全加载,mediaDevicesgetUserMedia 将被未定义。

开始记录

如果条件是假的(也就是说,mediaDevicesgetUserMedia 都支持),我们首先要把录音按钮的图像改为stop.png ,你可以从IconscoutGitHub仓库下载,并把它放在public/assets/images

然后,我们要检查mediaRecorder (我们在文件的开头定义的)是否为空。

如果它是空的,就意味着没有正在进行的录制。所以,我们使用getUserMedia 得到一个MediaStream实例来开始录制。

我们传递给它一个只有键audio 和值true 的对象,因为我们只是在录制音频。

这就是浏览器提示用户允许网站访问麦克风的地方。如果用户允许,履行处理程序中的代码将被执行。

mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
mediaRecorder.ondataavailable = mediaRecorderDataAvailable;
mediaRecorder.onstop = mediaRecorderStop;

这里我们要创建一个新的MediaRecorder ,把它分配给我们在文件开头定义的mediaRecorder

我们把从getUserMedia 收到的数据流传给构造器。然后,我们使用mediaRecorder.start()开始录制。

最后,我们将事件处理程序(我们将很快创建)与两个事件绑定,dataavailablestop

我们还添加了一个catch 处理程序,以防用户不允许网站访问麦克风或可能抛出的任何其他异常

停止录音

如果mediaRecorder 不为空,这一切都会发生。如果它是空的,这意味着有一个正在进行的录音,而用户正在结束它。所以,我们使用mediaRecorder.stop()方法来停止录音。

} else {
  //stop recording
  mediaRecorder.stop();
}

继续阅读如何使用MediaStream API录制音频onSitePoint.