許多瀏覽器如今都能訪問用戶的視頻和音頻輸入。 不過,根據瀏覽器的不一樣,這一功能可能體現爲一種全動態的內置體驗,也可能經過受權給用戶設備上的其餘應用來實現。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() { };});複製代碼