录制用户的音频 & 视频

录制音频

许多浏览器现在都能访问用户的音频和视频输入。

简单的版本

最简易的方式就是让用户自己提供预先录制的文件。其实现步骤是:创建一个简单的文件输入元素,然后添加一个表示我们只接受音频文件的accept过滤器,在理想情况下,我们可以直接从麦克风获取这些文件。

1
<input type="file" accept="audio/*" captrue="microphone">

此方法在所有平台上都有效。在桌面平台上,它会提示用户通过文件系统上传文件(忽略microphone的条件)。在iOS上的Safari中,它会打开麦克风应用让您录制音频,然后将其传回网页;在Android上,它允许用户选择合适的应用来录制音频,并将其传回网页。

用户完成录制并返回网站后,您需要以某种方式掌握文件数据。 为 input 元素附加一个onchange事件,然后读取事件对象的files属性,便可快速获得访问权。

1
2
3
4
5
6
7
8
9
10
11
12
<input type="file" accept="audio/*" capture="microphone" id="recorder">
<audio id="player" controls></audio>
<script>
var recorder = document.getElementById('recorder');
var player = document.getElementById('player');
recorder.addEventListener('change', function(e) {
var file = e.target.files[0];
player.src = URL.createObjectURL(file);
});
</script>

在获得文件的访问权之后,便可以对其进行任意操作,比如:

  • 将其直接附加到一个audio元素,这样就能播放文件了
  • 将其下载至用户设备
  • 通过将其附加到一个XMLHttpRequest,上传至服务器
  • 通过Web Audio API传递文件

尽管使用 input 元素方法获得对音频数据访问权的情况普遍存在,却是最没有吸引力的方案。 因为我们真正需要的是获得对麦克风的访问权,直接在页面内提供良好的体验。

以交互方式访问麦克风

现代浏览器可直连麦克风,我们可以借此打造与网页完全集成的体验,让用户永远都不需要离开浏览器。

获得对麦克风的访问权

我们可以利用 WebRTC(Web Real-Time Communication) 规范中名为 getUserMedia() 的API直接访问麦克风。getUserMedia() 将提示用户授予对其相连麦克风(microphone)摄像头(camera)的访问权。
如果授权成功了,该API会返回一个 Stream,其中包含了来自麦克风和摄像头的数据,然后我们将音频数据附加到一个 <audio> 元素、将其附加到一个网络音频 AudioContext或者使用 MediaRecorder API将其进行保存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<audio id="player" controls="controls"></audio>
<script>
var player = document.getElementById('player');
var handleSuccess = function(stream){
if (window.URL) {
player.src = window.URL.createObjectURL(stream);
} else {
player.src = stream;
}
};
navigator.mediaDevices.getUserMedia({audio:true, video:false}).then(handleSuccess);
</script>

在开启网页后,会询问是否允许访问麦克风设备,并且此时,点击 <audio> 控件的播放按钮,会实时的输出麦克风接收到的音频。
不过,这段代码的代码作用并不太大。我们所能做的太少了。

从麦克风获得原始数据

保存来自麦克风的数据

想要保存来自麦克风的数据,最简便的方法就是使用 MediaRecorder API。

MediaRecorder API 将获取 getUserMedia 创建的卡片流信息,然后渐进式的将卡片信息流中的数据保存到首选目的地。

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
<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
let shouldStop = false;
let stopped = false;
const downloadLink = document.getElementById('download');
const stopButton = document.getElementById('stop');
stopButton.addEventListen('click', function(){
shouldStop = true;
});
var handleSuccess = function(stream){
const options = {mimeType:'video/webm;codecs=vp9'};
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.addEventListener('dataavailable', function(e) {
if (e.data.size > 0) {
recordedChunks.push(e.data);
}
if(shouldStop === true && stopped === false) {
mediaRecorder.stop();
stopped = true;
}
});
mediaRecorder.addEventListener('stop', function() {
downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
downloadLink.download = 'acetest.wav';
});
mediaRecorder.start();
};
navigator.mediaDevices.getUserMedia({audio:true, video:false}).then(handleSuccess);
</script>