许多浏览器如今都能访问用户的视频和音频输入。 不过,根据浏览器的不一样,这一功能可能体现为一种全动态的内置体验,也可能经过受权给用户设备上的其余应用来实现。web
最简易的作法是直接要求用户提供预先录制的文件。 其实现步骤是:建立一个简单的文件输入元素,而后添加一个表示咱们只能接受音频文件的 accept
过滤器,在理想的状况下,咱们能够直接从麦克风获取这些文件。数组
<input type="file" accept="audio/*" capture="microphone">复制代码
此方法在全部平台上都有效。在桌面平台上,它会提示用户经过文件系统上传文件(忽略 capture="microphone"
)。 在 iOS 上的 Safari 中,它会打开麦克风应用以便您录制音频,而后将其传回网页;在 Android 上,它容许用户选择使用哪个应用来录制音频,录制完毕后将其传回网页。浏览器
用户完成录制并返回网站后,您须要以某种方式掌握文件数据。 为 input 元素附加一个 onchange
事件,而后读取事件对象的 files
属性,即可快速得到文件访问权。bash
<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]; // Do something with the audio file. player.src = URL.createObjectURL(file); });</script>复制代码
得到对文件的访问权后,即可随意对其执行任何操做。例如,能够执行如下操做:服务器
<audio>
元素,这样便能播放文件XMLHttpRequest
,上传至服务器尽管使用 input 元素方法得到对音频数据访问权的状况广泛存在,倒是最没有吸引力的方案。 由于咱们真正须要的是得到对麦克风的访问权,直接在页面内提供良好的体验。网络
现代浏览器可直连麦克风,咱们能够借此打造与网页彻底集成的体验,让用户永远都不须要离开浏览器。ide
咱们能够利用 WebRTC 规范中名为 getUserMedia()
的 API 直接访问麦克风。getUserMedia()
将提示用户授予对其相连麦克风和摄像头的访问权。网站
若是受权成功,该 API 将返回一个 Stream
,其中包含来自摄像头或麦克风的数据,而后咱们能够将数据附加到一个 <audio>
元素、将其附加到一个网络音频 AudioContext
或使用 MediaRecorder
API 对其进行保存。ui
要从麦克风获取数据,咱们只需在传递给 getUserMedia()
API 的约束对象中设置 audio: true
this
<audio id="player" 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>复制代码
这段代码自己的用处并不大。咱们所能作的就是获取音频数据并进行播放。
要从麦克风获取原始数据,咱们须要获取 getUserMedia()
建立的卡片信息流,而后利用 Web Audio API 处理数据。 Web Audio API 是一个简单的 API,用于获取输入源并将这些输入源链接到能够处理音频数据(调节增益等)的节点,最终目的是链接到扬声器以便用户可以听到声音。
能够链接的其中一个节点是 ScriptProcessorNode
。每次音频缓冲区已满,须要您进行处理时,该节点都会发出一个 onaudioprocess
事件。此时,您能够将数据保存到本身的缓冲区内,留供之后使用。
<script> var handleSuccess = function(stream) { var context = new AudioContext(); var input = context.createMediaStreamSource(stream) var processor = context.createScriptProcessor(1024,1,1); source.connect(processor); processor.connect(context.destination); processor.onaudioprocess = function(e){ // Do something with the data, i.e Convert this to WAV console.log(e.inputBuffer); }; }; navigator.mediaDevices.getUserMedia({ audio: true, video: false }) .then(handleSuccess)</script>复制代码
保留在缓冲区内的数据是来自麦克风的原始数据,在这些数据的处理上有如下这几种选择:
要想保存来自麦克风的数据,最简便的方法是使用 MediaRecorder
API。
MediaRecorder
API 将获取 getUserMedia
建立的卡片信息流,而后渐进式地将卡片信息流中的数据保存到首选目的地。
<a id="download">Download<button id="stop">Stop<script> let shouldStop = false; let stopped = false; const downloadLink = document.getElementById('download'); const stopButton = document.getElementById('stop'); stopButton.addEventListener('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>复制代码
在咱们这种状况下,咱们要将数据直接保存到一个数组中,而后在稍后转换成 Blob
后再将其保存到网络服务器,或直接保存在用户设备的存储内。
若是用户以前未授予网站对麦克风的访问权,则调用 getUserMedia
时浏览器会当即提示用户授予网站对麦克风的访问权。
用户讨厌在其机器上收到索要功能强大设备访问权的提示,他们经常会屏蔽权限请求,而若是他们不了解提示的产生环境,也会将其忽略。最好的作法是在首次须要权限时只请求访问麦克风。 一旦用户授予了访问权,就不会再次收到提示,但若是他们拒绝受权,您就没法再次得到访问权以向用户请求权限。
getUserMedia
API 并不能让您了解本身是否已得到对麦克风的访问权。 这就带来了一个问题:为了提供友善的 UI,让用户愿意授予对麦克风的访问权,您就必须请求得到对麦克风的访问权。
在某些浏览器中,能够利用 Permission API 来解决这个问题。navigator.permission
API 让您没必要再次提示用户即可查询到访问特定 API 能力的状态。
要想查询是否有权访问用户的麦克风,能够将 {name: 'microphone'}
传入 query 方法,后者将返回:
granted
— 用户以前已授予对麦克风的访问权;prompt
— 用户还没有授予访问权,调用 getUserMedia
时将会收到提示;denied
— 系统或用户已显式屏蔽对麦克风的访问权,您将没法得到对其的访问权。如今您就能够进行快速检查,以确认是否须要改动用户界面来适应用户须要执行的操做。
navigator.permissions.query({name:'microphone'}).then(function(result) { if (result.state == 'granted') { } else if (result.state == 'prompt') { } else if (result.state == 'denied') { } result.onchange = function() { };});复制代码