基于FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字

简介: 基于FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字


终于折腾好了(使用FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字,我的是Vue.js项目),对一个前端仔来说,听到要兼容IE,都是不情愿的,但是需求来了,那就折腾起来吧。(主要这需求砍不了,菜刀没带)。但是经过这几天折腾,记录一下,不足之处,还望指教~

   首先呢,明确下我们的目标,支持IE11浏览器录音后转文字。我们用逆向思维思考下,音频转文字。我们目前用的是科大讯飞的SDK。前端的话,把我们拿到的音频数据,包装好,调用一下后端给的接口,然后后端调讯飞SDK,转化为文字,发回来给我们。所以我们最主要是拿到音频数据,即录音。那我们难点是IE11上录音,因为IE 不支持 navigator.mediaDevices.getUserMedia这样的API。这里用JS调用flash去实现IE上录音。(应该还可以调用ActiveX,没尝试)。

   这里在github找到两个可以运行起来的demo。传送门:recorder.js,FlashWavRecorder 

   这两个提供的example 都可以正常跑起来。recorder.js音质比较好,第二个杂音大点。但recorder.js不知道怎么取到录音后的音频数据,照理说,上传都可以实现了,拿到音频数据,理应可以。或许是我的能力问题,还请指点。FlashWavRecorder 有getBase64的方法,所以就选用FlashWavRecorder 了。



  调用别人的SDK,就要符合别人的游戏规则。如下摘自科大讯飞接入规则
  1. 接口说明
    授权认证,调用接口需要将 Appid,CurTime, Param 和 CheckSum 信息放在 HTTP 请求头中;
    接口统一为 UTF-8 编码;
    接口支持 http 和 https;
    请求方式为POST。
  2. 授权认证
    在调用所有业务接口时,都需要在 Http Request Header 中配置以下参数用于授权认证:

参数 格式 说明 必须
X-Appid string 讯飞开放平台注册申请应用的应用ID(appid) 是
X-CurTime string 当前UTC时间戳,从1970年1月1日0点0 分0 秒开始到现在的秒数 是
X-Param string 相关参数JSON串经Base64编码后的字符串,见各接口详细说明 是
X-CheckSum string 令牌,计算方法:MD5(apiKey + curTime + param),三个值拼接的字符串,进行MD5哈希计算(32位小写),其中apiKey由讯飞提供,调用方管理。 是
讯飞demo中有这个字段 文档没写

body string dataUrl
注:

apiKey:接口密钥,由讯飞开放平台提供,调用方注意保管,如泄露,可联系讯飞技术人员重置;
checkSum 有效期:出于安全性考虑,每个 checkSum 的有效期为 5 分钟(用 curTime 计算),同时 curTime 要与标准时间同步,否则,时间相差太大,服务端会直接认为 curTime 无效;
BASE64 编码采用 MIME 格式,字符包括大小写字母各26个,加上10个数字,和加号 + ,斜杠 / ,一共64个字符。
checkSum 生成示例:

String apiKey="abcd1234";
String curTime="1502607694";
String param="eyAiYXVmIjogImF1ZGlvL0wxNjtyYXR...";
String checkSum=MD5(apiKey+curTime+param);
Copy

  1. 白名单
    在调用所有业务接口时,授权认证通过后,服务端会检查调用方ip是否在讯飞开放平台配置的ip白名单中,对于没有配置到白名单中的IP发来的请求,服务端会拒绝服务。 注:

IP白名单可在控制台应用管理卡片上编辑,五分钟左右生效;
IP白名单最多可设置5个,更多的需求可通过工单联系技术人员;
如果服务器返回结果如下所示,则表示由于未配置IP白名单,服务端拒绝服务:
{
"code":"10105",
"desc":"illegal access|illegal client_ip",
"data":"",
"sid":"xxxxxx"
}

我们请求后端接口类似这样:

that.$http.postObj('restful/voice/recognition', {
'body': sendurl,
'X-Appid': that.appid,
'X-CurTime': time,
'X-Param': xParam,
'X-CheckSum': xChecksum
}).then((res) => {
//成功识别语音后,在这里拿到文字 eg:res.data.data
console.log(res)
}
}).catch((err) => {
console.log(err)
});

//basic.js

$(function () {
//这里的$(function () {}) 原生写法是function DOMContentLoaded(){};
//document.addEventListener('DOMContentLoaded', DOMContentLoaded, false);

var RECORDER_APP_ID = "recorderApp";
var $level = $('.level .progress');
var appWidth = 24;
var appHeight = 24;
var flashvars = {'upload_image': '../images/upload.png'};
var params = {};
var attributes = {'id': RECORDER_APP_ID, 'name': RECORDER_APP_ID};
swfobject.embedSWF("../recorder.swf", "flashcontent", appWidth, appHeight, "11.0.0", "", flashvars, params, attributes);
//注意这句,要确保swfobject.js 已经加载好了 要准备个容器不然报错


//这里使用 document.getElementById(RECORDER_APP_ID).style.top='-3333px'
// 因为把swf对象设置为display:none 或者visibility:hidden 都是使用不了
window.fwr_event_handler = function fwr_event_handler() {//flash插件相关状态反馈
var name, $controls;
switch (arguments[0]) {
case "ready":
FWRecorder.uploadFormId = "#uploadForm";
FWRecorder.uploadFieldName = "upload_file[filename]";
FWRecorder.connect(RECORDER_APP_ID, 0);//如果老是报 recoder is null 这里没有调进来
FWRecorder.recorderOriginalWidth = appWidth;
FWRecorder.recorderOriginalHeight = appHeight;
break;
  case "permission_panel_closed":
            FWRecorder.recorderOriginalWidth = 0;
            FWRecorder.recorderOriginalHeight = 0;
            document.getElementById(RECORDER_APP_ID).style.top='-3333px'//隐藏白白一块
                FWRecorder.defaultSize();
                break;
  case "microphone_user_request":
    FWRecorder.showPermissionWindow();//这里请求后 会弹出 是否 允许 使用麦克风的请求
    break;

    ....这里省略局部代码
  case "microphone_level"://这里用于显示声波。如果成功接入麦克风,录音的时候会一直调用
    $level.css({height: arguments[1] * 100 + '%'});
    break;

    ....这里省略局部代码

};
.......这里省略局部代码
});

      以上主要是理解swfobject.embedSWF的用法。这里有详细的传送门: swfobject.js使用

//recoder.js

(function(global) {
var Recorder;

var RECORDED_AUDIO_TYPE = "audio/wav";

Recorder = {
recorder: null,
recorderOriginalWidth: 0,
recorderOriginalHeight: 0,
uploadFormId: null,
uploadFieldName: null,
isReady: false,

connect: function(name, attempts) {//连接麦克风 主要是init recorder
  if(navigator.appName.indexOf("Microsoft") != -1) {
    Recorder.recorder = window[name];
  } else {
    Recorder.recorder = document[name];//swfObject对象 ,这里的 name='recorderApp'
  }

  if(attempts >= 40) {
    return;
  }

  // flash app needs time to load and initialize
  if(Recorder.recorder && Recorder.recorder.init) {
    Recorder.recorderOriginalWidth = Recorder.recorder.width;
    Recorder.recorderOriginalHeight = Recorder.recorder.height;
    if(Recorder.uploadFormId && $) {
      var frm = $(Recorder.uploadFormId); 
     Recorder.recorder.init(frm.attr('action').toString(), Recorder.uploadFieldName, frm.serializeArray());
    }
    return;
  }

  setTimeout(function() {Recorder.connect(name, attempts+1);}, 100);
},

playBack: function(name) {//播放
  // TODO: Rename to `playback`
  Recorder.recorder.playBack(name);
},

stopRecording: function() {//停止录音
  Recorder.recorder.stopRecording();
},

resize: function(width, height) {//设置 麦克风权限请求框大小
  Recorder.recorder.width = width + "px";
  Recorder.recorder.height = height + "px";
},

defaultSize: function() {
  Recorder.resize(Recorder.recorderOriginalWidth, Recorder.recorderOriginalHeight);
},

show: function() {
  Recorder.recorder.show();
},

hide: function() {
  Recorder.recorder.hide();
},

getBase64: function(name) {//来获取base64数据 如果需要转回来 window.atob("base64")
  var data = Recorder.recorder.getBase64(name);
  return 'data:' + RECORDED_AUDIO_TYPE + ';base64,' + data;
},

getBlob: function(name) {//这里转化为blod 后面讯飞转文字的时候要用到。当然可以自己转
  var base64Data = Recorder.getBase64(name).split(',')[1];
  return base64toBlob(base64Data, RECORDED_AUDIO_TYPE);
},
showPermissionWindow: function(options) {
  Recorder.resize(240, 160);
  // need to wait until app is resized before displaying permissions screen
  var permissionCommand = function() {
    if (options && options.permanent) {
      Recorder.recorder.permitPermanently();
    } else {
      Recorder.recorder.permit();
    }
  };
  setTimeout(permissionCommand, 1);
},

};

function base64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;

var byteCharacters = atob(b64Data);
var byteArrays = [];

for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
  var slice = byteCharacters.slice(offset, offset + sliceSize);

  var byteNumbers = new Array(slice.length);
  for (var i = 0; i < slice.length; i++) {
    byteNumbers[i] = slice.charCodeAt(i);
  }

  var byteArray = new Uint8Array(byteNumbers);
  byteArrays.push(byteArray);
}

return new Blob(byteArrays, {type: contentType});

}

global.FWRecorder = Recorder;

})(this);

然后写几个button 接入就好了。 大家可以动手试一试。

注意:如果是在vue.js中当成模块的时候,ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

不能使用arguments.callee;不能使用arguments.caller;禁止this指向全局对象;不能使用fn.caller和fn.arguments获取函数调用的堆栈 等等 所以引入swfobject.js 的时候 要把 arguments.callee 替换掉。 传送门:严格模式下arguments.callee代替思路

例如有用arguments.callee的函数里,

function (){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);

把匿名函数 改为具名函数 ? swfobject.js 里面好像有7处需要改

function fn(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(fn,0);

相关文章
|
6月前
|
Web App开发 前端开发 JavaScript
|
1天前
解决win10无法打开自带的IE浏览器的问题
解决win10无法打开自带的IE浏览器的问题
7 0
|
1月前
|
存储 机器人
在阿里云RPA中,你可以通过以下步骤来更改默认唤醒IE浏览器的地址
【2月更文挑战第28天】在阿里云RPA中,你可以通过以下步骤来更改默认唤醒IE浏览器的地址
20 1
|
7月前
|
Web App开发 XML 编解码
IE浏览器下载文件中文文件名乱码问题解决
IE浏览器下载文件中文文件名乱码问题解决
85 0
|
4月前
|
Web App开发 JavaScript 前端开发
JavaScript在IE和标准浏览器下的兼容性处理
JavaScript在IE和标准浏览器下的兼容性处理
41 0
|
5月前
|
前端开发
border-radius 兼容 IE8浏览器
border-radius 兼容 IE8浏览器
40 1
|
5月前
|
Web App开发 移动开发 安全
IE浏览器,和Edge浏览器
IE浏览器,和Edge浏览器
|
5月前
|
JavaScript
兼容IE浏览器
兼容IE浏览器
32 0
|
7月前
|
编解码 网络协议 开发工具
IE浏览器下如何低延迟播放RTSP或RTMP流
首先,虽然本文是介绍IE浏览器下OCX控件播放RTSP或RTMP,但这种方式并不推荐,毕竟它只能用于IE浏览器环境下,局限太大,而且随着微软IE浏览器的更新,不确定后续支持情况。当然,话说回来,如果是在特定的使用场景下,只需要某些版本IE浏览器支持,但对延迟和稳定性要求非常高,OCX控件方式也不失为一个好的选择。
|
7月前
|
编解码 网络协议 开发工具
如何在IE浏览器播放RTSP或RTMP流
好多开发者一直苦恼于如何在IE浏览器环境下,构建低延迟的RTSP或RTMP播放,对于RTSP流来说,好多公司通常的做法是把RTSP转RTMP,然后分发到RTMP服务器,然后服务器转http-flv出来,浏览器直接播放http-flv流,亦或通过flash控件直接播放RTMP流,还有就是,转hls流出来,缺点是hls流延迟更大。
288 0