html5录音支持pc和Android、ios部分浏览器,微信也是支持的,JavaScript getUserMedia

之前在前人基础上重复造了一个网页录音的轮子,顺带把github仓库使用研究了一下,扔到了github上。javascript

优点在于结构简单,可插拔式的录音格式支持,几乎能够支持任意格式(前提有相应的编码器);默认提供实时音量反馈、有一个波形显示扩展支持。录音结果很是容易当即播放录音或者上传录音到服务器(提供参考源码)。css

2018-05-16首发,2019-04-21更新html

GitHub地址:https://github.com/xiangyuecn/Recorderhtml5

在线测试demo传送门:https://xiangyuecn.github.io/Recorder/java

效果图

自述

之前准备作一个网页版聊天界面,表情啊、图片啊、上传文件啊都应该要有,视频就算了,语音仍是要的。android

当下环境html5的录音功能支持状况大为良好(IOS上偏落后点)
2019-04浏览器支持状况,棒棒哒ios

如是,就有了这个轮子。git

如下内容copy自READMEgithub

Recorder用于html5录音

在线测试,支持大部分已实现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,其余浏览器均不支持,参考下面的已知问题。

快速使用

【1】加载框架

在须要录音功能的页面引入压缩好的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> <!--若是此格式有额外的编码引擎的话,也要加上-->

【2】调用录音

而后使用,假设当即运行,只录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);});

【附】问题排查

  • 打开Demo页面试试看,是否是也有一样的问题。
  • 检查是否是在https之类的安全环境下调用的。
  • 检查是否是IOS系统,确认caniuseIOS对getUserMedia的支持状况。
  • 检查上面第1步是否把框架加载到位,在Demo页面有应该加载哪些js的提示。
  • 提交Issue,热心网友帮你解答。

方法文档

【构造】rec=Recorder(set)

构造函数,拿到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.com19-01-10发的反馈邮件)。

【方法】rec.open(success,fail)

请求打开录音资源,若是浏览器不支持录音或用户拒绝麦克风权限将会调用fail,打开后须要调用close

注意:此方法是异步的;通常使用时打开,用完当即关闭;可重复调用,可用来测试是否能录音。

另外:由于此方法会调起用户受权请求,若是仅仅想知道浏览器是否支持录音(好比:若是浏览器不支持就走另一套录音方案),应使用Recorder.Support()方法。

success=fn();

fail=fn(errMsg,isUserNotAllow); 若是是用户主动拒绝的录音权限,除了有错误消息外,isUserNotAllow=true,方便程序中作不一样的提示,提高用户主动受权几率

【方法】rec.close(success)

关闭释放录音资源,释放完成后会调用success()回调

【方法】rec.start()

开始录音,需先调用open;若是不支持、错误,不会有任何提示,由于stop时天然能获得错误。

【方法】rec.stop(success,fail)

结束录音并返回录音数据blob对象,拿到blob对象就能够随心所欲了,不限于当即播放、上传

success(blob,duration)blob:录音数据audio/mp3|wav...格式,duration:录音时长,单位毫秒

fail(errMsg):录音出错回调

提示:stop时会进行音频编码,音频编码可能会很慢,10几秒录音花费2秒左右算是正常,编码并未使用Worker方案(文件多),内部采起的是分段编码+setTimeout来处理,界面卡顿不明显。

【方法】rec.pause()

暂停录音。

【方法】rec.resume()

恢复继续录音。

【方法】rec.mock(pcmData,pcmSampleRate)

模拟一段录音数据,后面能够调用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);
        });
};

【静态方法】Recorder.Support()

判断浏览器是否支持录音,随时能够调用。注意:仅仅是检测浏览器支持状况,不会判断和调起用户受权(rec.open()会判断用户受权),不会判断是否支持特定格式录音。

【静态方法】Recorder.IsOpen()

因为Recorder持有的录音资源是全局惟一的,可经过此方法检测是否有Recorder已调用过open打开了录音功能。

压缩合并一个本身须要的js文件

可参考/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

wav格式编码器时参考网上资料写的,会发现代码和别人家的差很少。源码2kb大小。

mp3

采用的是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。

beta-ogg

采用的是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不支持(真的假的)。

beta-webm

这个编码器时经过查阅MDN编写的一个玩意,没多大使用价值:录几秒就至少要几秒来编码。。。缘由是:未找到对已有pcm数据进行快速编码的方法。数据导入到MediaRecorder,音频有几秒就要等几秒,相似边播放边收听形。(想接原始录音Stream?我不可能给的!)输出音频虽然能够经过比特率来控制文件大小,但音频文件中的比特率并不是设定比特率,采样率因为是咱们本身采样的,到这个编码器随他怎么搞。只有比较新的浏览器支持(需实现浏览器MediaRecorder),压缩率和mp3差很少。源码2kb大小。

beta-amr

采用的是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。优势:录音文件小。

Recorder.amr2wav(amrBlob,True,False)

已实现的一个把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目录内为扩展支持库,这些扩展库默认都没有合并到生成代码中,需单独引用(distsrc中的)才能使用。

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();
});

【构造】wave=Recorder.WaveView(set)

构造函数,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)"] //背景渐变色,从上到下
}

【方法】wave.input(pcmData,powerLevel,sampleRate)

输入音频数据,更新波形显示,这个方法调用的越快,波形越流畅。pcmData为当前的录音数据片断,其余参数和onProcess回调相同。

兼容性

对于支持录音的浏览器可以正常录音并返回录音数据;对于不支持的浏览器,引入js和执行相关方法都不会产生异常,而且进入相关的fail回调。通常在open的时候就能检测到是否支持或者被用户拒绝,可在用户开始录音以前提示浏览器不支持录音或受权。

Android Hybrid App中录音示例

在Android Hybrid App中使用本库来录音,须要在App源码中实现如下两步分:

  1. AndroidManifest.xml声明须要用到的两个权限
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
  1. 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

微信内浏览器他家的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:https://github.com/xiangyuecn/Recorder

相关文章
相关标签/搜索