UNPKG

recorder-core-rlian

Version:

Recorder库:html5 js 录音 mp3 wav ogg webm amr 格式,支持pc和Android、ios部分浏览器、和Hybrid App(提供Android IOS App源码),微信也是支持的,提供H5版语音通话聊天示例 和DTMF编解码

134 lines (112 loc) 5.1 kB
/* 录音 RecordApp: Hybrid App Native支持文件。 特别注明:本文件涉及的功能需要IOS、Android App端的支持,如果你不能修改App的源码,并且坚决要使用本文件,那将会很困难。 本文件诞生的原因是因为IOS端WKWebView(UIWebView)中未开放getUserMedia功能来录音,只能寻求其他解决方案。Android端没有此问题,但Android里面使用网页的录音权限问题可能比原生的权限机制要复杂,为了简化js端的复杂性(出问题了好甩锅),不管是Android还是IOS都实现一下可能会简单很多。当以后IOS任何地方的网页都能录音时,本文件就可以删除了。 录音功能由原生App(Native)代码实现,通过JsBridge和h5进行交互。Native层需要提供:请求权限、开始录音、结束录音、定时回调PCM[Int16]片段 这4个功能、接口。因为js层已自动加载Recorder和相应的js编码引擎,所以,Native层无需进行编码(Android和IOS的音频编码并非易事,且不易更新),大大简化App的逻辑和弹性。 录音必须是单声道的,因为这个库从头到尾就没有打算支持双声道。 JsBridge可以是自己实现的交互方式 或 别人提供的框架。因为不知道具体使用的桥接方式,对应的请求已抽象成了3个方法在Native.Config中,需自行实现。 本文件源码可以不用改动,因为需要改动的地方已放到了app.js的Native.Config中了;最终实际实现可参考app-support-sample目录内的配置文件,另外此目录内还有Android和IOS的demo项目,copy源码改改就能用。 支持在iframe中使用,但如果是跨域要特殊处理。 https://github.com/xiangyuecn/Recorder */ (function(){ "use strict"; var App=RecordApp; var CLog=App.CLog; var platform=App.Platforms.Native; var config=platform.Config; platform.IsInit=true; /*******Hybrid App Native层在录音时定时回调本js方法*******/ /* pcmDataBase64: base64<Int16[]>字符串 当前单声道录音缓冲PCM片段,正常情况下为上次回调本接口开始到现在的录音数据,Int16[]二进制数组需要编码成base64字符串 sampleRate:123456 录制音频实际的采样率 */ var onRecFn=window.NativeRecordReceivePCM=function(pcmDataBase64,sampleRate){//无视iframe var rec=onRecFn.rec; if(!rec){ CLog("未开始录音,但收到Native PCM数据",3); return; }; if(!rec._appStart){ rec.envStart({ envName:platform.Key,canProcess:platform.CanProcess() },sampleRate); }; rec._appStart=1; var bstr=atob(pcmDataBase64),n=bstr.length; var pcm=new Int16Array(n/2); var sum=0; for(var idx=0,s,i=0;i+2<=n;idx++,i+=2){ s=((bstr.charCodeAt(i)|(bstr.charCodeAt(i+1)<<8))<<16)>>16; pcm[idx]=s; sum+=Math.abs(s); }; rec.envIn(pcm,sum); }; //尝试注入顶层window,用于接收Native回调数据,此处特殊处理一下,省得跨域的iframe无权限 try{ window.top.NativeRecordReceivePCM=onRecFn; }catch(e){ var tipsFn=function(){ CLog("检测到跨域iframe,NativeRecordReceivePCM无法注入到顶层,已监听postMessage转发兼容传输数据,请自行实现将top层接收到数据转发到本iframe(不限层),不然无法接收到录音数据",3); }; setTimeout(tipsFn,8000); tipsFn(); addEventListener("message",function(e){//纯天然,无需考虑origin var data=e.data;//{type:"",data:{pcmDataBase64:"",sampleRate:16000}} if(data&&data.type=="NativeRecordReceivePCM"){ data=data.data; onRecFn(data.pcmDataBase64, data.sampleRate); }; }); }; /*******实现统一接口,以下方法参数请参考app.js RecordApp中的同名方法*******/ platform.RequestPermission=function(success,fail){ config.JsBridgeRequestPermission(success,fail); }; platform.Start=function(set,success,fail){ onRecFn.param=set; var rec=Recorder(set); rec.set.disableEnvInFix=true; //不要音频输入丢失补偿 onRecFn.rec=rec;//等待第一个数据到来再调用rec.start App.__Rec=rec;//rec在stop时需要即时清理,因此暴露的内部变量需另外赋值 config.JsBridgeStart(set,success,fail); }; platform.Stop=function(success,fail){ var failCall=function(msg){ fail("录音失败[Native]:"+msg); onRecFn.rec=null; App.__Rec=null; }; config.JsBridgeStop(function(){ var rec=onRecFn.rec; onRecFn.rec=null; if(!rec){ failCall("未开始录音"); return; }; CLog("rec encode start: pcm:"+rec.recSize+" src:"+rec.srcSampleRate+" set:"+JSON.stringify(onRecFn.param)); var end=function(){ //把可能变更的配置写回去 for(var k in rec.set){ onRecFn.param[k]=rec.set[k]; }; }; if(!success){ end(); failCall("仅清理资源"); return; }; rec.stop(function(blob,duration){ CLog("rec encode end") end(); App._SRec=rec; success(blob,duration); App.__Rec=null; },function(msg){ end(); failCall(msg); }); },failCall); }; })();