之前在前人基礎上重複造了一個網頁錄音的輪子,順帶把github倉庫使用研究了一下,扔到了github上。javascript
優點在於結構簡單,可插拔式的錄音格式支持,幾乎能夠支持任意格式(前提有相應的編碼器);默認提供實時音量反饋、有一個波形顯示擴展支持。錄音結果很是容易當即播放錄音或者上傳錄音到服務器(提供參考源碼)。css
2018-05-16首發,2019-04-21更新html
GitHub地址:github.com/xiangyuecn/…html5
在線測試demo傳送門:xiangyuecn.github.io/Recorder/java
之前準備作一個網頁版聊天界面,表情啊、圖片啊、上傳文件啊都應該要有,視頻就算了,語音仍是要的。android
當下環境html5的錄音功能支持狀況大爲良好(IOS上偏落後點) ios
如是,就有了這個輪子。git
如下內容copy自READMEgithub
在線測試,支持大部分已實現getUserMedia
的移動端、PC端瀏覽器,包括騰訊Android X5內核(QQ、微信)。點此查看瀏覽器支持狀況。web
錄音默認輸出mp3格式,另外可選wav格式(此格式錄音文件超大);有限支持ogg(beta)、webm(beta)、amr(beta)格式;支持任意格式擴展(前提有相應編碼器)。
mp3默認16kbps的比特率,2kb每秒的錄音大小,音質還能夠(若是使用8kbps可達到1kb每秒,不過音質太渣)。
mp3使用lamejs編碼,壓縮後的recorder.mp3.min.js文件150kb左右(開啓gzip後54kb)。若是對錄音文件大小沒有特別要求,能夠僅僅使用錄音核心+wav編碼器,源碼不足300行,壓縮後的recorder.wav.min.js不足4kb。
瀏覽器Audio Media兼容性mp3最好,wav還行,其餘要麼不支持播放,要麼不支持編碼。
特別注:IOS(11.X、12.X)
上只有Safari
支持getUserMedia
,其餘瀏覽器均不支持,參考下面的已知問題。
在須要錄音功能的頁面引入壓縮好的recorder.***.min.js文件便可 (注意:須要在https等安全環境下才能進行錄音)
<script src="recorder.mp3.min.js"></script>
複製代碼
或者直接使用源碼(src內的爲源碼、dist內的爲壓縮後的),能夠引用src目錄中的recorder-core.js+相應類型的實現文件,好比要mp3錄音:
<script src="src/recorder-core.js"></script> <!--必須引入的錄音核心-->
<script src="src/engine/mp3.js"></script> <!--相應格式支持文件-->
<script src="src/engine/mp3-engine.js"></script> <!--若是此格式有額外的編碼引擎的話,也要加上-->
複製代碼
而後使用,假設當即運行,只錄3秒
var rec=Recorder();//使用默認配置,mp3格式
rec.open(function(){//打開麥克風受權得到相關資源
rec.start();//開始錄音
setTimeout(function(){
rec.stop(function(blob,duration){//到達指定條件中止錄音
console.log(URL.createObjectURL(blob),"時長:"+duration+"ms");
rec.close();//釋放錄音資源
//已經拿到blob文件對象想幹嗎就幹嗎:當即播放、上傳
/*當即播放例子*/
var audio=document.createElement("audio");
audio.controls=true;
document.body.appendChild(audio);
//簡單的一嗶
audio.src=URL.createObjectURL(blob);
audio.play();
},function(msg){
console.log("錄音失敗:"+msg);
});
},3000);
},function(msg,isUserNotAllow){//用戶拒絕未受權或不支持
console.log((isUserNotAllow?"UserNotAllow,":"")+"沒法錄音:"+msg);
});
複製代碼
var TestApi="/test_request";//用來在控制檯network中能看到請求數據,測試的請求結果可有可無
var rec=Recorder();rec.open(function(){rec.start();setTimeout(function(){rec.stop(function(blob,duration){
//-----↓↓↓如下才是主要代碼↓↓↓-------
//本例子假設使用jQuery封裝的請求方式,實際使用中自行調整爲本身的請求方式
//錄音結束時拿到了blob文件對象,能夠用FileReader讀取出內容,或者用FormData上傳
var api=TestApi;
/***方式一:將blob文件轉成base64純文本編碼,使用普通application/x-www-form-urlencoded表單上傳***/
var reader=new FileReader();
reader.onloadend=function(){
$.ajax({
url:api //上傳接口地址
,type:"POST"
,data:{
mime:blob.type //告訴後端,這個錄音是什麼格式的,可能先後端都固定的mp3能夠不用寫
,upfile_b64:(/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result)||[])[1] //錄音文件內容,後端進行base64解碼成二進制
//...其餘表單參數
}
,success:function(v){
console.log("上傳成功",v);
}
,error:function(s){
console.error("上傳失敗",s);
}
});
};
reader.readAsDataURL(blob);
/***方式二:使用FormData用multipart/form-data表單上傳文件***/
var form=new FormData();
form.append("upfile",blob,"recorder.mp3"); //和普通form表單並沒有二致,後端接收到upfile參數的文件,文件名爲recorder.mp3
//...其餘表單參數
$.ajax({
url:api //上傳接口地址
,type:"POST"
,contentType:false //讓xhr自動處理Content-Type header,multipart/form-data須要生成隨機的boundary
,processData:false //不要處理data,讓xhr自動處理
,data:form
,success:function(v){
console.log("上傳成功",v);
}
,error:function(s){
console.error("上傳失敗",s);
}
});
//-----↑↑↑以上纔是主要代碼↑↑↑-------
},function(msg){console.log("錄音失敗:"+msg);});},3000);},function(msg){console.log("沒法錄音:"+msg);});
複製代碼
getUserMedia
的支持狀況。構造函數,拿到Recorder
的實例,而後能夠進行請求獲取麥克風權限和錄音。
set
參數爲配置對象,默認配置值以下:
set={
type:"mp3" //輸出類型:mp3,wav等,使用一個類型前須要先引入對應的編碼引擎
,bitRate:16 //比特率 wav(位):1六、8,MP3(單位kbps):8kbps時文件大小1k/s,16kbps 2k/s,錄音文件很小
,sampleRate:16000 //採樣率,wav格式文件大小=sampleRate*時間;mp3此項對低比特率文件大小有影響,高比特率幾乎無影響。
//wav任意值,mp3取值範圍:48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000
,bufferSize:4096 //AudioContext緩衝大小。會影響onProcess調用速度,相對於AudioContext.sampleRate=48000時,4096接近12幀/s,調節此參數可生成比較流暢的回調動畫。
//取值256, 512, 1024, 2048, 4096, 8192, or 16384
//注意,取值不能太低,2048開始不一樣瀏覽器可能回調速率跟不上形成音質問題(低端瀏覽器→說的就是騰訊X5)
,onProcess:NOOP //接收到錄音數據時的回調函數:fn(this.buffer,powerLevel,bufferDuration,bufferSampleRate)
//buffer=[緩衝PCM數據,...],powerLevel:當前緩衝的音量級別0-100,bufferDuration:已緩衝時長,bufferSampleRate:緩衝使用的採樣率
//若是須要繪製波形之類功能,須要實現此方法便可,使用以計算好的powerLevel能夠實現音量大小的直觀展現,使用buffer能夠達到更高級效果
}
複製代碼
注意:set內是數字的明確傳數字,不要傳字符串之類的致使不可預測的異常,其餘有配置的地方也是同樣(感謝214282049@qq.com
19-01-10發的反饋郵件)。
請求打開錄音資源,若是瀏覽器不支持錄音或用戶拒絕麥克風權限將會調用fail
,打開後須要調用close
。
注意:此方法是異步的;通常使用時打開,用完當即關閉;可重複調用,可用來測試是否能錄音。
另外:由於此方法會調起用戶受權請求,若是僅僅想知道瀏覽器是否支持錄音(好比:若是瀏覽器不支持就走另一套錄音方案),應使用Recorder.Support()
方法。
success
=fn();
fail
=fn(errMsg,isUserNotAllow); 若是是用戶主動拒絕的錄音權限,除了有錯誤消息外,isUserNotAllow=true,方便程序中作不一樣的提示,提高用戶主動受權機率
關閉釋放錄音資源,釋放完成後會調用success()
回調
開始錄音,需先調用open
;若是不支持、錯誤,不會有任何提示,由於stop時天然能獲得錯誤。
結束錄音並返回錄音數據blob對象
,拿到blob對象就能夠隨心所欲了,不限於當即播放、上傳
success(blob,duration)
:blob
:錄音數據audio/mp3|wav...格式,duration
:錄音時長,單位毫秒
fail(errMsg)
:錄音出錯回調
提示:stop時會進行音頻編碼,音頻編碼可能會很慢,10幾秒錄音花費2秒左右算是正常,編碼並未使用Worker方案(文件多),內部採起的是分段編碼+setTimeout來處理,界面卡頓不明顯。
暫停錄音。
恢復繼續錄音。
模擬一段錄音數據,後面能夠調用stop進行編碼。需提供pcm數據int[],和pcm數據的採樣率。
可用於將一個音頻解碼出來的pcm數據方便的轉換成另一個格式:
var amrBlob=...;//amr音頻blob對象
var amrSampleRate=8000;//amr音頻採樣率
//解碼amr獲得pcm數據
var reader=new FileReader();
reader.onload=function(){
Recorder.AMR.decode(new Uint8Array(reader.result),function(pcm){
transformOgg(pcm);
});
};
reader.readAsArrayBuffer(amrBlob);
//將pcm轉成ogg
function transformOgg(pcmData){
Recorder({type:"ogg",bitRate:64,sampleRate:32000})
.mock(pcmData,amrSampleRate)
.stop(function(blob,duration){
//咱們就獲得了新採樣率和比特率的ogg文件
console.log(blob,duration);
});
};
複製代碼
判斷瀏覽器是否支持錄音,隨時能夠調用。注意:僅僅是檢測瀏覽器支持狀況,不會判斷和調起用戶受權(rec.open()會判斷用戶受權),不會判斷是否支持特定格式錄音。
因爲Recorder持有的錄音資源是全局惟一的,可經過此方法檢測是否有Recorder已調用過open打開了錄音功能。
可參考/src/package-build.js中如何合併的一個文件,好比mp3是由recorder-core.js
,engine/mp3.js
,engine/mp3-engine.js
組成的。
除了recorder-core.js
其餘引擎文件都是可選的,能夠把所有編碼格式合到一塊兒也,也能夠只合並幾種,而後就能夠支持相應格式的錄音了。
能夠修改/src/package-build.js後,在src目錄內執行壓縮:
cnpm install
npm start
複製代碼
若是你有其餘格式的編碼器而且想貢獻出來,能夠提交新增格式文件的pull(文件放到/src/engine中),咱們升級它。
wav格式編碼器時參考網上資料寫的,會發現代碼和別人家的差很少。源碼2kb大小。
採用的是lamejs(LGPL License)這個庫的代碼,https://github.com/zhuker/lamejs/blob/bfb7f6c6d7877e0fe1ad9e72697a871676119a0e/lame.all.js
這個版本的文件代碼;已對lamejs源碼進行了部分改動,用於修復發現的問題。LGPL協議涉及到的文件:mp3-engine.js
;這些文件也採用LGPL受權,不適用MIT協議。源碼518kb大小,壓縮後150kb左右,開啓gzip後50來k。
採用的是ogg-vorbis-encoder-js(MIT License),https://github.com/higuma/ogg-vorbis-encoder-js/blob/7a872423f416e330e925f5266d2eb66cff63c1b6/lib/OggVorbisEncoder.js
這個版本的文件代碼。此編碼器源碼2.2M,超級大,壓縮後1.6M,開啓gzip後327K左右。對錄音的壓縮率比lamejs高出一倍, 但Vorbis in Ogg好像Safari不支持(真的假的)。
這個編碼器時經過查閱MDN編寫的一個玩意,沒多大使用價值:錄幾秒就至少要幾秒來編碼。。。緣由是:未找到對已有pcm數據進行快速編碼的方法。數據導入到MediaRecorder,音頻有幾秒就要等幾秒,相似邊播放邊收聽形。(想接原始錄音Stream?我不可能給的!)輸出音頻雖然能夠經過比特率來控制文件大小,但音頻文件中的比特率並不是設定比特率,採樣率因爲是咱們本身採樣的,到這個編碼器隨他怎麼搞。只有比較新的瀏覽器支持(需實現瀏覽器MediaRecorder),壓縮率和mp3差很少。源碼2kb大小。
採用的是benz-amr-recorder(MIT License)優化後的amr.js(Unknown License),https://github.com/BenzLeung/benz-amr-recorder/blob/462c6b91a67f7d9f42d0579fb5906fad9edb2c9d/src/amrnb.js
這個版本的文件代碼,已對此代碼進行過調整更方便使用。支持編碼和解碼操做。因爲最高只有12.8kbps的碼率,音質和同比配置的mp三、ogg差一個檔次。因爲支持解碼操做,理論上全部支持Audio的瀏覽器均可以播放(須要本身寫代碼實現)。源碼1M多,蠻大,壓縮後445K,開啓gzip後136K。優勢:錄音文件小。
已實現的一個把amr轉成wav格式來播放的方法,True=fn(wavBlob,duration)
。要使用此方法須要帶上上面的wav
格式編碼器。仿照此方法可輕鬆轉成別的格式,參考mock
方法介紹那節。
//好比增長aac格式支持 (可參考/src/engine/mp3.js實現)
//新增一個aac.js,編寫如下格式代碼便可實現這個類型
Recorder.prototype.aac=function(pcmData,successCall,failCall){
//經過aac編碼器把pcm數據轉成aac格式數據,經過this.set拿到傳入的配置數據
... pcmData->aacData
//返回數據
successCall(new Blob(aacData,{type:"audio/aac"}));
}
//調用
Recorder({type:"aac"})
複製代碼
在src/extensions
目錄內爲擴展支持庫,這些擴展庫默認都沒有合併到生成代碼中,需單獨引用(dist
或src
中的)才能使用。
WaveView
擴展waveview.js
,4kb大小源碼,錄音時動態顯示波形,具體樣子參考演示地址頁面。此擴展參考MCVoiceWave庫編寫的,具體代碼在https://github.com/HaloMartin/MCVoiceWave/blob/f6dc28975fbe0f7fc6cc4dbc2e61b0aa5574e9bc/MCVoiceWave/MCVoiceWaveView.m
中。
此擴展是在錄音時onProcess
回調中使用;bufferSize
會影響繪製幀率,越小越流暢(但越消耗cpu),默認配置的大概12幀/s。基礎使用方法:
var wave;
var rec=Recorder({
onProcess:function(buffers,powerLevel,bufferDuration,bufferSampleRate){
wave.input(buffers[buffers.length-1],powerLevel,bufferSampleRate);//輸入音頻數據,更新顯示波形
}
});
rec.open(function(){
wave=Recorder.WaveView({elem:".elem"}); //建立wave對象,寫這裏面瀏覽器妥妥的
rec.start();
});
複製代碼
構造函數,set
參數爲配置對象,默認配置值以下:
set={
elem:"css selector" //自動顯示到dom,並以此dom大小爲顯示大小
//或者配置顯示大小,手動把waveviewObj.elem顯示到別的地方
,width:0 //顯示寬度
,height:0 //顯示高度
//以上配置二選一
scale:2 //縮放係數,由於正整數,使用2(3? no!)倍寬高進行繪製,避免移動端繪製模糊
,speed:8 //移動速度係數,越大越快
,lineWidth:2 //線條基礎粗細
//漸變色配置:[位置,css顏色,...] 位置: 取值0.0-1.0之間
,linear1:[0,"rgba(150,97,236,1)",1,"rgba(54,197,252,1)"] //線條漸變色1,從左到右
,linear2:[0,"rgba(209,130,253,0.6)",1,"rgba(54,197,252,0.6)"] //線條漸變色2,從左到右
,linearBg:[0,"rgba(255,255,255,0.2)",1,"rgba(54,197,252,0.2)"] //背景漸變色,從上到下
}
複製代碼
輸入音頻數據,更新波形顯示,這個方法調用的越快,波形越流暢。pcmData爲當前的錄音數據片斷,其餘參數和onProcess
回調相同。
對於支持錄音的瀏覽器可以正常錄音並返回錄音數據;對於不支持的瀏覽器,引入js和執行相關方法都不會產生異常,而且進入相關的fail回調。通常在open的時候就能檢測到是否支持或者被用戶拒絕,可在用戶開始錄音以前提示瀏覽器不支持錄音或受權。
在Android Hybrid App中使用本庫來錄音,須要在App源碼中實現如下兩步分:
AndroidManifest.xml
聲明須要用到的兩個權限<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
複製代碼
WebChromeClient
中實現onPermissionRequest
網頁受權請求@Override
public void onPermissionRequest(PermissionRequest request) {
...此處應包裹一層系統權限請求
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
request.grant(request.getResources());
}
}
複製代碼
注:若是應用的
騰訊X5內核
,除了上面兩個權限外,還必須提供android.permission.CAMERA
權限。另外沒法重寫此onPermissionRequest
方法,他會本身彈框詢問,若是被拒絕了就永遠拒絕了,參考已知問題部分。
若是不出意外,App內顯示的網頁就能正常錄音了。
.assets/android_test
目錄中提供了Android測試源碼(若是不想本身打包能夠用打包好的apk來測試,位於.assets/android_test/app-debug-xxx.apk
)。提供了系統自帶WebView
、和騰訊X5內核
兩個測試界面。
微信內瀏覽器他家的JsSDK也支持錄音,涉及笨重難調的公衆號開發(光sdk初始化就能阻礙不少新奇想法的產生,signature限制太多),只能知足最基本的使用(大部分狀況足夠了)。若是JsSDK錄完音能返回音頻數據,這個SDK將好用10000倍,若是能實時返回音頻數據,將好用100000倍。關鍵是他們家是拒絕給這種簡單好用的功能的,必須繞一個大圈:錄好音了->上傳到微信服務器->自家服務器請求微信服務器多進行媒體下載->保存錄音(微信小程序之前也是二逼路子,如今稍微好點能實時拿到錄音mp3數據),若是能升級:錄好音了拿到音頻數據->上傳保存錄音,目測對最終結果是沒有區別的,還簡單很多,對微信自家也算是很是經濟實用。[2018]因爲微信IOS上不支持原生JS錄音,Android上又支持,爲了兼容而去兼容的事情我是拒絕的(並且是僅僅爲了兼容IOS上面的微信),其實也算不上去兼容,由於微信JsSDK中的接口徹底算是另一種東西,接入的話對整個錄音流程都會產生徹底不同的變化,還不如沒有進入錄音流程以前就進行分支判斷處理。
最後:若是是在微信上用的多,應優先直接接入他家的JsSDK(沒有公衆號開個訂閱號又不要錢),基本上能夠忽略兼容性問題,就是麻煩點。
2018-09-19 caniuse 註明IOS
11.X - 12.X
上 只有Safari
支持調用getUserMedia
,其餘App下WKWebView(UIWebView?)(相關資料)均不支持。經用戶測試驗證IOS 12上chrome、UC都沒法錄音,部分IOS 12 Safari能夠獲取到而且能正常錄音,但部分不行,緣由未知,參考ios 12 支不支持錄音了。在IOS上不支持錄音的環境下應該採用其餘解決方案,參考案例演示
、關於微信JsSDK
部分。
2019-02-28 issues#14 若是getUserMedia
返回的MediaStreamTrack.readyState == "ended"
,"ended" which indicates that the input is not giving any more data and will never provide new data.
,致使沒法錄音。若是產生這種狀況,目前在rec.open
方法調用時會正確檢測到,並執行fail
回調。形成issues#14
ended
緣由是App源碼中AndroidManifest.xml
中沒有聲明android.permission.MODIFY_AUDIO_SETTINGS
權限,致使騰訊X5不能正常錄音。
2019-03-09 在Android上QQ、微信裏,請求受權使用麥克風的提示,通過長時間觀察發現,他們的表現很隨機、很奇特。可能每次在調用getUserMedia
時候都會彈選擇,也可能選擇一次就不會再彈提示,也可能重啓App後又會彈。若是用戶拒絕了,可能次日又會彈,或者永遠都不彈了,要麼重置(裝)App。使用騰訊X5內核的App測試也是同樣奇特表現,拒絕權限後可能必需要重置(裝)。這個問題貌似跟X5內核自動升級的版本有關。
若是這個庫有幫助到您,請 Star 一下。GitHub:github.com/xiangyuecn/…