目前主流的安防厂家的摄像头或者NVR都可以支持语音通道,只要配备拾音器和Mic即可实现和平台的语音交互,甚至渠道机已经内置Mic和扬声器,实现即插即用的的语音交互体验,但是目前主流的安防平台,需要安装CS客户端,或者使用厂家提供的浏览器插件来实现语音对讲,应用厂家如果想实现语音互动、大屏指挥等语音场景业务,需要切换到厂家提供的平台或者页面,无法与自己的业务平台实现完美的兼容。
LiveMedia视频中间件提供了完美的语音互动API,在自身的业务平台上全过程使用HTTP API接口通过视频中间件对前端设备进行语音交互,语音界面可由第三方应用厂家自由定制。实现拓扑如下图示:
平台对讲组实现时序图
上图中网页端中获取麦克风声音数据需要服务端开启https
HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。
getUserMedia API最初是navigator.getUserMedia
,目前已被最新Web标准废除,变更为navigator.mediaDevices.getUserMedia()
,但浏览器支持情况不如旧版API普及。 MediaDevices.getUserMedia()
方法提示用户允许使用一个视频和/或一个音频输入设备,例如相机或屏幕共享和/或麦克风。如果用户给予许可,就返回一个Promise
对象,MediaStream
对象作为此Promise
对象的Resolved
[成功]状态的回调函数参数,相应的,如果用户拒绝了许可,或者没有媒体可用的情况下PermissionDeniedError
或者NotFoundError
作为此Promise
的Rejected
[失败]状态的回调函数参数。注意,由于用户不会被要求必须作出允许或者拒绝的选择,所以返回的Promise
对象可能既不会触发resolve
也不会触发 reject
。
navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) { ... })
.catch(function(error) { ... })
test
开启本地音频采集、定时20毫秒通过Websocket发送音频PCM数据至LiveMedia中间件
var begin = document.getElementById('intercomBegin');
var end = document.getElementById('intercomEnd');
navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || navigator.mediaDevices.webkitGetUserMedia;
var ws = null;//实现WebSocket
var record=null;//多媒体对象,用来处理音频
var timeInte = null;//定义一个定时器
begin.onclick = function() {
console.log('开始对讲')
}
end.onclick = function() {
console.log('关闭对讲')
if(ws) {
ws.close();
record.stop();
clearInterval(timeInte);
}
}
function init(rec){
record = rec;
}
if (!navigator.mediaDevices.getUserMedia) {
alert('浏览器不支持音频输入');
}else{
navigator.mediaDevices.getUserMedia( { audio: true })
.then(function (mediaStream){
init(new Recorder(mediaStream));
}
)
.catch(function(err){ console.log(err.name + ": " + err.message); });
}
//录音对象
var Recorder = function(stream) {
var sampleBits = 16;//输出采样数位 8, 16
var sampleRate = 44100;//输出采样率
var bufSize = 8192;
var context = new AudioContext();
var audioInput = context.createMediaStreamSource(stream);
var recorder = context.createScriptProcessor(0, 1, 1);
var resample = new Resampler(context.sampleRate, 44100, 1, bufSize);
var audioData = {
size: 0 //录音文件长度
, buffer: [] //录音缓存
, inputSampleRate: context.sampleRate //输入采样率
, inputSampleBits: 16 //输入采样数位 8, 16
, outputSampleRate: sampleRate
, oututSampleBits: sampleBits
, clear: function() {
this.buffer = [];
this.size = 0;
}
, input: function (data) {
this.buffer.push(new Float32Array(data));
this.size += data.length;
}
, compress: function () { //合并压缩
//合并
var data = new Float32Array(this.size);
var offset = 0;
for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset);
offset += this.buffer[i].length;
}
//压缩
var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
var length = data.length / compression;
var result = new Float32Array(length);
var index = 0, j = 0;
while (index < length) {
result[index] = data[j];
j += compression;
index++;
}
return result;
}, encodePCM: function(){//这里不对采集到的数据进行其他格式处理,如有需要均交给服务器端处理。
var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
var bytes = this.compress();
var dataLength = bytes.length * (sampleBits / 8);
var buffer = new ArrayBuffer(dataLength);
var data = new DataView(buffer);
var offset = 0;
for (var i = 0; i < bytes.length; i++, offset += 2) {
var s = Math.max(-1, Math.min(1, bytes[i]));
data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
return new Blob([data]);
}
};
this.start = function () {
audioInput.connect(recorder);
recorder.connect(context.destination);
}
this.stop = function () {
recorder.disconnect();
}
this.getBlob = function () {
return audioData.encodePCM();
}
this.clear = function() {
audioData.clear();
}
recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0));
}
};
function getDate(extra){
var dat = new Date;//生成日期
var year = dat.getFullYear();//取得年
var month = dat.getMonth()+1; //取得月,js从0开始取,所以+1
var date1 = dat.getDate(); //取得天
var hour = dat.getHours();//取得小时
var minutes = dat.getMinutes();//取得分钟
var second = dat.getSeconds();//取得秒
var haomiao = dat.getMilliseconds();
dat = undefined;
return year+"-"+month+"-"+date1+" "+hour+":"+minutes +":"+second+" "+haomiao + extra ;
}
begin.onclick = function() {
ws = new WebSocket("ws://192.168.3.23:9030/ws_talk/57?token=607b9f0b-293f-4412-ac6f-278804be5982");
ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
ws.onopen = function(event) {
console.log('握手成功');
record.start();
};
timeInte=setInterval(function(){
if(ws.readyState==1){//ws进入连接状态,则每隔500毫秒发送一包数据
console.log(getDate('>>'));
console.log(record.getBlob());
if(record.getBlob().length != 0){
console.log("###########send Blob start ########");
ws.send(record.getBlob()); //发送音频数据
console.log("##############send Blob end ###########");
}
record.clear(); //每次发送完成则清理掉旧数据
}
},20); //每隔20ms发送一次,定时器
}
LiveMedia可支持一对一,一对多的集群指挥对讲
qq交流群:698793654