发送实时音频数据到udp服务

由于浏览器不能直接连接udp服务,所以需要搭建一个websocket服务做中转,让websocket服务连接udp服务
1、vue开发获取实时音频数据并按4096分包后添加rtp协议头发送到websocket服务(连接websocket自行编写连接到127.0.0.1:8889)

data(){return {audioContext:null,rc:null,}
},
methods:{startRecorder(){let that = this;//可以用下面的代码来边讲话边听const audio = new Audio()audio.autoplay = truenavigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {that.rc = streamaudio.srcObject = streamthat.audioContext = new AudioContext();const mediaStreamSource = that.audioContext.createMediaStreamSource(stream);const bufferSize = 4096; // 根据需要选择缓冲区大小const scriptNode = that.audioContext.createScriptProcessor(bufferSize, 1, 1);// 当音频处理事件发生时scriptNode.onaudioprocess = (event) => {const inputBuffer = event.inputBuffer;const inputData = inputBuffer.getChannelData(0);// 将音频数据编码为 RTP 协议头的 16 位二进制数据const rtpData = new Int16Array(inputData.length);for (let i = 0; i < inputData.length; i++) {rtpData[i] = inputData[i] * 32767; // 缩放到 16 位范围}//原数据的二进制数据const rtpBinary = rtpData.buffer;//发送原数据到websocket服务// websocketsend(rtpBinary);//下面是创建rtp协议头的,可选// 创建 RTP 协议头const version = 2; // RTP 版本const padding = 0; // 填充位const extension = 0; // 扩展位const csrcCount = 0; // CSRC 计数const marker = 0; // 标记位const payloadType = 8; // 负载类型 8:PCMAlet sequenceNumber = 0;//序列号(根据需要修改)let timestamp = 0;//开始截取时间戳(根据需要修改)const sampleRate = 44100; // 音频采样率(根据需要修改)const ssrc = Math.floor(Math.random() * 0xFFFFFFFF);//同步源标识符, 32位的无符号整数(根据需要修改)// 根据需要的时间位置和采样率计算时间戳timestamp += (bufferSize / sampleRate) * 90000; // bufferSize 是 RTP 包的大小const rtpHeader = new Uint8Array(12); // RTP 协议头为12字节const view = new DataView(rtpHeader.buffer);view.setUint8(0, (version << 6) | (padding << 5) | (extension << 4) | csrcCount);//<<左移操作符view.setUint8(1, (marker << 7) | payloadType & 0x7F);//view.setUint8(1, (marker << 7) | payloadType);view.setUint16(2, sequenceNumber);view.setUint32(4, timestamp);view.setUint32(8, ssrc);// 将 RTP 协议头和 rtpBinary 合并为一个数据包const rtpPacket = new Uint8Array(rtpHeader.length + rtpBinary.byteLength);rtpPacket.set(rtpHeader);rtpPacket.set(new Uint8Array(rtpBinary), rtpHeader.length);sequenceNumber = (sequenceNumber + 1) & 0xFFFF;// 发送 RTP 数据到 WebSocket 服务器websocketsend(rtpPacket);};mediaStreamSource.connect(scriptNode);scriptNode.connect(that.audioContext.destination);}).catch(error => alert(error));},//停止采集音频stopRecorder(){const audioTrack = this.rc?.getAudioTracks()[0];if(audioTrack){audioTrack.stop();}if(this.rc){this.rc = nullthis.audioContext.close()this.audioContext = null//停止websocket连接websocketclose()}}
}

2、使用nodejs搭建websocket服务
创建一个app.js,并安装dgram依赖 npm install dgram --save

const WebSocket = require('ws');
const dgram = require('dgram');// 创建WebSocket服务器
const wss = new WebSocket.Server({ port: 8889 });// 创建UDP套接字
const udpSocket = dgram.createSocket('udp4');// 监听WebSocket连接
wss.on('connection', (ws) => {console.log('客户端已连接');// 监听WebSocket消息ws.on('message', (message) => {console.log('收到消息:', message);// 发送消息到UDP服务器udpSocket.send(message, 0, message.length, 8888, '127.0.0.1', (err) => {if (err) {console.error('发送UDP消息失败:', err);}});});// 监听UDP消息udpSocket.on('message', (message, rinfo) => {console.log('收到UDP消息:', message.toString());// 将UDP消息发送给WebSocket客户端ws.send(message.toString());});// 监听WebSocket关闭ws.on('close', () => {console.log('客户端已断开连接');});
});// 开始监听UDP端口
udpSocket.bind(8888, '127.0.0.1', () => {console.log('UDP套接字已绑定');
});

运行app.js: node app.js

3、udp服务上可以看到发送过来的二进制数据(调试工具在这百度网盘地址)
在这里插入图片描述
注:如果提示[Deprecation] The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead. (https://bit.ly/audio-worklet)是因为ScriptProcessorNode已经弃用了,可以不用更改,多了个警告而已,不影响。如需要更换为AudioWorkletNode,以下就是更换后的代码
1、新建一个audioworklet.js

class MyAudioWorkletProcessor extends AudioWorkletProcessor {constructor() {super();// 在这里初始化任何需要的变量,如序列号、时间戳、SSRC 等this.sequenceNumber = 0;this.timestamp = 0;this.ssrc = Math.floor(Math.random() * 0xFFFFFFFF);}process(inputs, outputs, parameters) {const input = inputs[0];const inputData = input[0]; // 假设只有一个输入通道// 创建 RTP 协议头const rtpHeader = new Uint8Array(12); // RTP 协议头为12字节const view = new DataView(rtpHeader.buffer);const version = 2; // RTP 版本const padding = 0; // 填充位const extension = 0; // 扩展位const csrcCount = 0; // CSRC 计数const marker = 0; // 标记位const payloadType = 8; // 负载类型,根据需要更改view.setUint8(0, (version << 6) | (padding << 5) | (extension << 4) | csrcCount);view.setUint8(1, (marker << 7) | payloadType);view.setUint16(2, this.sequenceNumber);view.setUint32(4, this.timestamp);view.setUint32(8, this.ssrc);// 将 RTP 协议头和音频数据合并为一个数据包const rtpPacket = new Uint8Array(rtpHeader.length + inputData.length);rtpPacket.set(rtpHeader);rtpPacket.set(new Uint8Array(inputData.buffer), rtpHeader.length);console.log(rtpPacket);// 发送 RTP 数据到 WebSocket 服务器// 你需要将这个数据包发送到 WebSocket 服务器,可能需要一个 WebSocket 连接来发送数据// WebSocket 发送代码应该放在这里// 更新序列号和时间戳this.sequenceNumber = (this.sequenceNumber + 1) & 0xFFFF;this.timestamp += inputData.length;return true;}}registerProcessor('my-audio-worklet-processor', MyAudioWorkletProcessor);

然后把上面的采集函数改一下,提示一下,这个可能需要在https的网站使用

startRecorder(){let that = this;//可以用下面的代码来边讲话边听const audio = new Audio()audio.autoplay = truenavigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {that.rc = streamaudio.srcObject = streamthat.audioContext = new AudioContext();const mediaStreamSource = that.audioContext.createMediaStreamSource(stream);that.audioContext.audioWorklet.addModule('audioworklet.js').then(() => {// 创建 AudioWorkletNodeconst workletNode = new AudioWorkletNode(that.audioContext, 'my-audio-worklet-processor');// 连接音频输入和输出mediaStreamSource.connect(workletNode);workletNode.connect(that.audioContext.destination);// 音频处理逻辑已在 audioworklet.js 中定义}).catch((error) => {console.error('加载音频工作线程失败:', error);});mediaStreamSource.connect(scriptNode);scriptNode.connect(that.audioContext.destination);}).catch(error => alert(error));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/115583.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据增强】

【数据增强】 1 数据增强的情形2 数据增强的方法 1 数据增强的情形 当数据比较小&#xff0c;难以获取新的训练数据时&#xff0c;可以考虑数据增强&#xff0c;如随机裁剪部分&#xff0c;随机左右上下翻转、随机旋转一个角度、随机亮度变化等微小变化&#xff0c;数据的多样…

手写实现简易Spring框架

该文章记录的是自己实现简易Spring框架的记录&#xff0c;简化了源码中的大量操作&#xff0c;没有实现三级缓存的机制。 其中将会对Component&#xff0c;ComponentScan&#xff0c;Scope&#xff0c;Autowired注解。BeanDefinition类以及ApplicationContext、BeanPostProcess…

如何在RK3568开发板上实现USBNET?——飞凌嵌入式/USB Gadget/USB-NET/网络

本文将借助飞凌嵌入式OK3568-C开发板为大家介绍实现USBNET模式的方法&#xff0c;在这之前需要先知道什么是USB Gadget——USB Gadget是指所开发的电子设备以USB从设备的模式通过USB连接到主机。举个例子&#xff1a;将手机通过USB线插入PC后&#xff0c;手机就是USB Gadget。同…

BOM与DOM--记录

BOM基础&#xff08;BOM简介、常见事件、定时器、this指向&#xff09; BOM和DOM的区别和联系 JavaScript的DOM与BOM的区别与用法详解 DOM和BOM是什么&#xff1f;有什么作用&#xff1f; 图解BOM与DOM的区别与联系 BOM和DOM详解 JavaScript 中的 BOM&#xff08;浏览器对…

医疗虚拟仿真和虚拟现实有什么区别?哪个更好?

随着我们在仿真教育中越来越多地使用新技术&#xff0c;区分虚拟模式的类型很重要。虚拟仿真是一个统称&#xff0c;用来概括术语来描述各种基于仿真的体验&#xff0c;从基于屏幕的平台到沉浸式虚拟现实。然而&#xff0c;各虚拟平台在保真度、沉浸感和临场感的水平上有很大差…

流媒体及直播相关知识

文章目录 前言一、流媒体1、基本概念2、流式传输3、流媒体技术原理4、流媒体传输模式5、H.264 流媒体传输系统框架 二、直播1、直播中使用的流媒体协议2、直播的模块划分3、视频直播流程①、推流到服务器②、服务器流分发 前言 本文主要讲解流媒体及其直播相关知识&#xff0c…

【期望+状压DP】 2021 CCPC G

Problem - G - Codeforces 题意&#xff1a; 思路&#xff1a; 注意到 k 的范围是18&#xff0c;可以考虑状压 要求最小的期望长度&#xff0c;我们可以遍历所有可能的路径&#xff0c;统计这些路径的期望长度的最小值即可 那么怎么遍历呢&#xff1f;这里很经典的处理方式…

初识软件工程

软件工程是一门涵盖软件开发、维护和管理的学科&#xff0c;它通过应用工程化的原则和方法来提高软件系统的质量和可靠性。在当今数字化和信息化的时代&#xff0c;软件工程对于现代社会的各个领域都具有至关重要的作用。 基本概念&#xff1f; 计算机系统中与硬件相互依存的一…

复习Day01:数组part01:701. 二分查找、35. 搜索插入位置、367. 有效的完全平方数、69. x的平方根、74. 搜索二维矩阵

之前的blog链接&#xff1a;https://blog.csdn.net/weixin_43303286/article/details/131690654?spm1001.2014.3001.5501 我用的方法是在leetcode再过一遍例题&#xff0c;明显会的就复制粘贴&#xff0c;之前没写出来就重写&#xff0c;然后从拓展题目中找题目来写。辅以Lab…

ruoyi框架修改左侧菜单样式

菜单效果 ruoyi前端框架左侧的菜单很丑&#xff0c;我们需要修改一下样式&#xff0c;下面直接看效果。 修改代码 1、sidebar.scss .el-menu-item, .el-submenu__title {overflow: hidden !important;text-overflow: ellipsis !important;white-space: nowrap !important;//…

API文档搜索引擎

导航小助手 一、认识搜索引擎 二、项目目标 三、模块划分 四、创建项目 五、关于分词 六、实现索引模块 6.1 实现 Parser类 6.2 实现 Index类 6.2.1 创建 Index类 6.2.2 创建DocInfo类 6.2.3 创建 Weight类 6.2.4 实现 getDocInfo 和 getInverted方法 6.2.5 实现 …

【word格式】mathtype公式插入 | 段落嵌入后格式对齐 | 字体大小调整 |空心字体

1. 公式嵌入 推荐在线latex编辑器&#xff0c;可以截图转 latex 识别率很高 https://www.latexlive.com/home 美中不足&#xff0c;不开会员每天只能用3次识别。 通过公式识别后&#xff0c;输出选择align环境&#xff0c;然后在mathtype中直接粘贴latex就可以转好。 2.公式…