简介

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 APIgetUserMedia 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)设备语音指挥(对讲、广播)相关推荐

  1. 【技术教程】基于EasyRTSPSever与GB28181协议设备端EasyGBD实现的摄像机模拟器架构

    上一次我们计划做一款摄像机模拟器设备,主要的功能用途是为了方便更多的安防视频开发者能够更加简单.便捷.高效地找一款摄像机设备来做开发. 安防摄像机虽然很简单就能买到,但是配电.配电源.调网络,占空间, ...

  2. GB28181系列笔记-语音对讲功能

    GB28181系列笔记-注册与保活 GB28181系列笔记-设备目录查询 GB28181系列笔记-实时流请求 GB28181系列笔记-历史流查询与请求 GB28181系列笔记-语音对讲功能 GB281 ...

  3. 安卓GB28181跨网段语音对讲

    GB28181语音对讲实际使用中遇到的主要问题是跨网段后rtp udp包不能穿透,针对这个问题有两套解决方案. 方案一,安卓端语音发送走实时视音频点播通道,把编码后的语音数据封装到PS包中,和视频帧一 ...

  4. 【语音去噪】基于matlab小波硬阈值语音降噪【含Matlab源码 532期】

    ⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[语音去噪]基于matlab小波硬阈值语音降噪[含Matlab源码 532期] 点击上面蓝色字体,直接付费下载,即可. 获取代码方式2: ...

  5. 基于耳部脑电的语音想象脑机接口系统

    本次分享一篇发表于JNE期刊的基于耳部脑电的语音想象脑机接口系统. 摘要 本研究调查了以用户耳朵 (ear-EEG) 为中心的脑电图 (EEG) 对基于语音想象的脑机接口 (BCI) 系统的功效.开发 ...

  6. 宽带噪声语音信号增强matlab,基于声卡和Matlab平台的语音信号增强处理系统

    基于声卡和Matlab平台的语音信号增强处理系统 摘要:计算机声卡是多媒体技术中最基本的组成部分,是实现声波/数字信号相互转换的一种硬件.文章对基于计算机声卡的谱相减语音增强系统进行分析.首先分析了基 ...

  7. R语言ggplot2可视化散点图(scatter plot)、并在可视化图像的顶部和右边添加边缘直方图(Marginal Histogram)、使用geom_smooth函数基于lm方法拟合数据点之间

    R语言ggplot2可视化散点图(scatter plot).并在可视化图像的顶部和右边添加边缘直方图(Marginal Histogram).使用geom_smooth函数基于lm方法拟合数据点之间 ...

  8. R语言ggplot2可视化:使用geom_smooth函数基于lm方法为每个分组的部分数据(subset data)拟合趋势关系曲线、对指定范围的数据拟合曲线

    R语言ggplot2可视化:使用geom_smooth函数基于lm方法为每个分组的部分数据(subset data)拟合趋势关系曲线.对指定范围的数据拟合曲线 目录

  9. linux设备驱动程序jd,Linux设备驱动程序学习(基于2440的GPIO字符设备驱动)

    基于2440的GPIO字符设备驱动及应用程序是针对2440型号的底板的驱动及测试应用程序,详细情况请见底板的PCB图. S3C2440提供130 路复用的IO口线,分为如下端口进行管理: - Port ...

最新文章

  1. UA MATH566 统计理论 Fisher信息论的性质下
  2. int指令---汇编学习笔记
  3. 我和阿里巴巴的孽缘(三)
  4. 关于SAP Spartacus在服务器端渲染模式和SAP Commerce Cloud API白名单的问题
  5. Mr.J--C语言头函数的建立(附严薇敏《数据结构》线性表代码)
  6. redis系列之1----redis简介以及linux上的安装
  7. django进阶05中间件
  8. web ftp java代码_java web ftp cli
  9. 【渝粤教育】国家开放大学2018年春季 0508-22T影视特技及后期合成 参考试题
  10. Python的矩阵分块
  11. C#中MessageBox用法大全(附效果图)
  12. SVN 无法提交 svn: Can't open file '/svn/ssq/db/txn-current-lock': Permission denied
  13. Unity实现人物旋转+移动
  14. PS——规定尺寸的证件照的制作
  15. 【大学生辩论赛】如何练习自己的辩论口才
  16. unity游戏开发为什么工作这么难找
  17. windows创建符号链接命令
  18. 百度地图上定位自己所在的位置
  19. 基于ShineBlink物联网开发板和机智云平台开发的“针对短期内宠物无人照顾的智能宠物屋”系统
  20. uipath锚点的使用

热门文章

  1. 云社区 博客 博客详情 如何设计实时数据平台(技术篇)
  2. flutter - built_value 自动生成model
  3. 《计算机网络》中英文对照
  4. 问题解决:AttributeError: ResultSet object has no attribute ‘get‘.
  5. DSP相对于计算机的优点,基于DSP的导航计算机研究
  6. 宝塔面板linux ftp怎么设置权限,宝塔面板创建FTP账号的几种方法(根据需要安装FTP功能)...
  7. Pedestrian Detection
  8. makefile 学习笔记 八:如何运行 make
  9. Turtlebot4入门教程-机械-有效载荷
  10. 计算机控制面板没有笔和触摸,HP EliteBook 840 G1 笔记本电脑 - 虽然电脑不支持触控笔,但控制面板仍显示触控笔和触摸选项卡...