基于LM的(GB28181/EHOME)设备语音指挥(对讲、广播)
简介
LM视频中间音频对讲和广播,支持一对一、一对多、多对多、控制前端播放自定wav语音文件及创建语音聊天室等功能,可基于基础安防设备实现实时多方语音通话、语音喊话及报警联动语音播放等场景下语音交互。
LM视频中间件支持的设备如下:
- 全系列海康设备
- 全系列大华设备
- GB28181设备(内网UDP/TCP,外网TCP模式)
- EHOME协议设备,包含ISUP
LM主要对外开放接口有,开启对讲(广播)、关闭对讲(广播)、发送语音和发送wav文件播放等,详细API说明见API说明文档
提示
需设备上安装了麦克风和喇叭
API接口说明
开启对讲(广播)
此接口为开启或加入对讲(组),其中请求参数
- deviceId 可以为空时,为用户发起新建(加入)对讲组,本次请求不添加新的设备到对讲组中
- ssid为0时,代表新建对讲(广播)组,当ssid不为0时为加入已存在的对讲组,ssid对讲组必须要存在,否则会返回对讲组不存在的错误
注:
- 用户创建对讲组后,若在一段时间内(15秒)不调用发送语音或者播放wav文件接口,LM将自动释放当前对讲组,同时关闭和前端设备之间的对讲会话。
- 如对讲的设备已被对讲组占用,LM会在接口返回User Already Opening错误,调用者可以在接口返回的数组详情中得到占用设备得对讲组ssid,从而再发起加入对讲组得请求进行语音通讯。
请求
POST /api/v1/talk/start?token=f384932b-92b9-4947-a567-10c33a82b44f HTTP/1.1
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 63{"ssid":0,"deviceId":["A20221214135512","A123445"]
}
应答
返回的信息需要到detail数组中获取每个设备的开启结果
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 177
Connection: keep-alive
Access-Control-Allow-Origin: *{"result": 200,"message": "OK","ssid": 1,"detail": [{"deviceId": "A20221214135512","result": "OK"},{"deviceId": "A123445","result": "Invalid Device ID"}]
}
关闭对讲(广播)
此接口为关闭对讲组接口,使用时也可以不调用此接口,LM也会自动检测是否还存在用户存在对讲组中,接口参数
- deviceId为空时关闭参数ssid指定的对讲组
- ssId为非0且LM中必须存在此对讲组
请求
POST /api/v1/talk/stop?token=8305918c-69e8-4280-ae22-85aa5d696e0e HTTP/1.1Content-Type: application/jsonAccept: */*Host: 192.168.3.23:9030Accept-Encoding: gzip, deflate, brConnection: keep-aliveContent-Length: 20{"ssid":636}
!!! sample “应答”
HTTP/1.1 200 OKContent-Type: application/jsonContent-Length: 42Connection: keep-aliveAccess-Control-Allow-Origin: *{"result": 200,"message": "OK"}
发送语音
此接口是在打开对讲接口调用成功后返回后,客户端使用websocket发送本地及采集的PCM语音数据,语音采样率:44000, 采样位为16, 单通道。
播放wav文件
本接口仅支持WAV格式的PCM语音文件
网页H5音频采集
网页音频采用使用的是HTML5的getUserMedia API,getUserMedia API为用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。getUserMedia API的浏览器兼容行如图示:
注意
需要网页H5支持采集音频有以下两种方式(以Chrome浏览器为例子):
- 服务器端开启https
- 浏览本地设置例外,进入浏览器本地设置
- 地址栏输入chrome://flags/
- 在设置页面找到选项Insecure origins treated as secure,添加例外服务器地址,重启浏览器即可在开发环境采集音频
采集音频
initTalk(){// 初始化多媒体对象if (!navigator.mediaDevices.getUserMedia) {alert('浏览器不支持音频输入');} else if(this.record == null){navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || navigator.mediaDevices.webkitGetUserMedia;navigator.mediaDevices.getUserMedia({audio: true}).then((mediaStream)=> {this.record = new this.getRecorder(mediaStream)})}}
getRecorder(stream){let sampleBits = 16;//输出采样数位 8, 16let sampleRate = 44100;//输出采样率let bufSize = 8192;let context = new AudioContext();let audioInput = context.createMediaStreamSource(stream);let recorder = context.createScriptProcessor(0, 1, 1);let audio_resample = new Resampler(context.sampleRate, sampleRate, 1, bufSize);console.log(audio_resample);let audioData = {size: 0, //录音文件长度buffer: [], //录音缓存inputSampleRate: sampleRate, //输入采样率inputSampleBits: 16, //输入采样数位 8, 16outputSampleRate: sampleRate,oututSampleBits: sampleBits,clear: function () {audioData.buffer = [];audioData.size = 0;},input: function (data) {audioData.buffer.push(new Float32Array(data));audioData.size += data.length;},compress: function () { //合并压缩//合并let data = new Float32Array(this.size);let offset = 0;for (let i = 0; i < this.buffer.length; i++) {data.set(this.buffer[i], offset);offset += this.buffer[i].length;}//压缩let compression = parseInt(this.inputSampleRate / this.outputSampleRate);let length = data.length / compression;let result = new Float32Array(length);let index = 0, j = 0;while (index < length) {result[index] = data[j];j += compression;index++;}return result;},encodePCM: function () {//这里不对采集到的数据进行其他格式处理,如有需要均交给服务器端处理。let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);let bytes = this.compress();let dataLength = bytes.length * (sampleBits / 8);let buffer = new ArrayBuffer(dataLength);let data = new DataView(buffer);let offset = 0;for (let i = 0; i < bytes.length; i++, offset += 2) {let 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));audioData.input(audio_resample.resample(e.inputBuffer.getChannelData(0)));}}getTalkStart(){this.initTalk()if(this.talkOpen){ // this.talkOpen为true开启对讲let data = {deviceId: [this.orgId],ssid: 0};talkStartServe(data,this.getToken()).then(res => {if(res.result==200 && res.ssid != 0) {this.ssid = res.ssidthis.imgSrc = require('@/assets/img/yj-open.png')this.$message.success('设备对讲已开启')this.talkOpen = falsethis.sendTalk()}else {if(res.detail[0] != undefined){if(res.detail[0].result == "User Opened Talking"){this.ssid = res.detail[0].ssidthis.imgSrc = require('@/assets/img/yj-open.png')this.$message.success('设备对讲已开启')this.talkOpen = falsethis.sendTalk()}else{this.$message.error(res.detail[0].result)this.imgSrc = require('@/assets/img/yunjing.png')this.talkOpen = true}}else{this.$message.error("对讲打开失败");this.imgSrc = require('@/assets/img/yunjing.png')this.talkOpen = true}}})}else { // 关闭对讲if(this.record != null){this.record.stop();}if(this.socket != null){this.socket.close();}if(this.pcmPlayer != null){this.pcmPlayer.destroy();this.pcmPlayer = null;} //不需要关闭,后台会根据连接自动关闭对讲组this.$message.success('设备对讲已关闭')this.imgSrc = require('@/assets/img/yunjing.png')this.talkOpen = true/*talkStopServe({ssid: this.ssid},this.getToken()).then(res => {if(res.result==200) {this.$message.success('设备对讲已关闭')this.imgSrc = require('@/assets/img/yunjing.png')this.talkOpen = true}else {this.$message.error('操作失败')this.imgSrc = require('@/assets/img/yj-open.png')this.talkOpen = false}})*/}}
发送音频
sendTalk() {// WebSocket发送音频let that = this // this指向会有问题that代替let locUrl = window.location.hostlet stemp = '';if(window.location.protocol === 'https:'){stemp ='s';}const socketUrl = 'ws' + stemp +'://'+ locUrl + '/ws_talk/'+this.ssid+'?token='+this.getToken();// + '&sampleRate=' + this.context.sampleRate;that.socket = new WebSocket(socketUrl)that.socket.binaryType = 'arraybuffer'that.socket.onopen = function () {console.log('浏览器WebSocket已打开');if(that.record != null){that.record.start();}else{console.log('声音采集打开失败');} window.timeInte = setInterval(() => {if(that.socket != null && that.socket.readyState == 1) {if (that.record != null && that.record.getBlob().size != 0) {that.socket.send(that.record.getBlob()); //发送音频数据that.record.clear();}}},20)};if(that.pcmPlayer == null){that.pcmPlayer = new PCMPlayer({inputCodec: 'Int16',channels: 1,sampleRate: 16000,flushTime: 200})}that.socket.onmessage = function(msg) {// 接收WebSocket消息that.pcmPlayer.feed(msg.data)}}
常见错误码
result | message | 说明 |
---|---|---|
200 | OK | 成功 |
400 | device not find | 设备不存在 |
400 | audio file format not support | 音频格式不支持 |
400 | talk session not find | 对讲组不存在 |
403 | limit license | 授权限制 |
423 | invalid server | 服务错误或授权到期 |
424 | user not login | 用户未登录 |
User Opened Talking | 设备已打开设备对讲 | |
RTSP Disconnect | 设备对讲打开失败 |
基于LM的(GB28181/EHOME)设备语音指挥(对讲、广播)相关推荐
- 【技术教程】基于EasyRTSPSever与GB28181协议设备端EasyGBD实现的摄像机模拟器架构
上一次我们计划做一款摄像机模拟器设备,主要的功能用途是为了方便更多的安防视频开发者能够更加简单.便捷.高效地找一款摄像机设备来做开发. 安防摄像机虽然很简单就能买到,但是配电.配电源.调网络,占空间, ...
- GB28181系列笔记-语音对讲功能
GB28181系列笔记-注册与保活 GB28181系列笔记-设备目录查询 GB28181系列笔记-实时流请求 GB28181系列笔记-历史流查询与请求 GB28181系列笔记-语音对讲功能 GB281 ...
- 安卓GB28181跨网段语音对讲
GB28181语音对讲实际使用中遇到的主要问题是跨网段后rtp udp包不能穿透,针对这个问题有两套解决方案. 方案一,安卓端语音发送走实时视音频点播通道,把编码后的语音数据封装到PS包中,和视频帧一 ...
- 【语音去噪】基于matlab小波硬阈值语音降噪【含Matlab源码 532期】
⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[语音去噪]基于matlab小波硬阈值语音降噪[含Matlab源码 532期] 点击上面蓝色字体,直接付费下载,即可. 获取代码方式2: ...
- 基于耳部脑电的语音想象脑机接口系统
本次分享一篇发表于JNE期刊的基于耳部脑电的语音想象脑机接口系统. 摘要 本研究调查了以用户耳朵 (ear-EEG) 为中心的脑电图 (EEG) 对基于语音想象的脑机接口 (BCI) 系统的功效.开发 ...
- 宽带噪声语音信号增强matlab,基于声卡和Matlab平台的语音信号增强处理系统
基于声卡和Matlab平台的语音信号增强处理系统 摘要:计算机声卡是多媒体技术中最基本的组成部分,是实现声波/数字信号相互转换的一种硬件.文章对基于计算机声卡的谱相减语音增强系统进行分析.首先分析了基 ...
- R语言ggplot2可视化散点图(scatter plot)、并在可视化图像的顶部和右边添加边缘直方图(Marginal Histogram)、使用geom_smooth函数基于lm方法拟合数据点之间
R语言ggplot2可视化散点图(scatter plot).并在可视化图像的顶部和右边添加边缘直方图(Marginal Histogram).使用geom_smooth函数基于lm方法拟合数据点之间 ...
- R语言ggplot2可视化:使用geom_smooth函数基于lm方法为每个分组的部分数据(subset data)拟合趋势关系曲线、对指定范围的数据拟合曲线
R语言ggplot2可视化:使用geom_smooth函数基于lm方法为每个分组的部分数据(subset data)拟合趋势关系曲线.对指定范围的数据拟合曲线 目录
- linux设备驱动程序jd,Linux设备驱动程序学习(基于2440的GPIO字符设备驱动)
基于2440的GPIO字符设备驱动及应用程序是针对2440型号的底板的驱动及测试应用程序,详细情况请见底板的PCB图. S3C2440提供130 路复用的IO口线,分为如下端口进行管理: - Port ...
最新文章
- UA MATH566 统计理论 Fisher信息论的性质下
- int指令---汇编学习笔记
- 我和阿里巴巴的孽缘(三)
- 关于SAP Spartacus在服务器端渲染模式和SAP Commerce Cloud API白名单的问题
- Mr.J--C语言头函数的建立(附严薇敏《数据结构》线性表代码)
- redis系列之1----redis简介以及linux上的安装
- django进阶05中间件
- web ftp java代码_java web ftp cli
- 【渝粤教育】国家开放大学2018年春季 0508-22T影视特技及后期合成 参考试题
- Python的矩阵分块
- C#中MessageBox用法大全(附效果图)
- SVN 无法提交 svn: Can't open file '/svn/ssq/db/txn-current-lock': Permission denied
- Unity实现人物旋转+移动
- PS——规定尺寸的证件照的制作
- 【大学生辩论赛】如何练习自己的辩论口才
- unity游戏开发为什么工作这么难找
- windows创建符号链接命令
- 百度地图上定位自己所在的位置
- 基于ShineBlink物联网开发板和机智云平台开发的“针对短期内宠物无人照顾的智能宠物屋”系统
- uipath锚点的使用
热门文章
- 云社区 博客 博客详情 如何设计实时数据平台(技术篇)
- flutter - built_value 自动生成model
- 《计算机网络》中英文对照
- 问题解决:AttributeError: ResultSet object has no attribute ‘get‘.
- DSP相对于计算机的优点,基于DSP的导航计算机研究
- 宝塔面板linux ftp怎么设置权限,宝塔面板创建FTP账号的几种方法(根据需要安装FTP功能)...
- Pedestrian Detection
- makefile 学习笔记 八:如何运行 make
- Turtlebot4入门教程-机械-有效载荷
- 计算机控制面板没有笔和触摸,HP EliteBook 840 G1 笔记本电脑 - 虽然电脑不支持触控笔,但控制面板仍显示触控笔和触摸选项卡...