当前位置: 首页 > news >正文

webscoket+webrtc实现语音通话

1.项目方案

前端采用webrtc创建音频上下文,后创建音频源输入音频处理器,连接音频输入与处理器,处理器再连接到音频输出(扬声器),再通过事件获取音频数据,把音频数据转换成字节数据通过webscoket发送给后端。

注意

1.前端使用的创建音频源api createScriptProcessor  onaudioprocess  已经开始废弃使用,但是浏览器依然适配。

2.因为前端使用websocket实时传输录音数据,后端开发需要多线程接受处理数据,给每个数据包提供index坐标,然后处理后保存,再通过单线程发送,可以降低延迟

2.前端代码

// AudioManager.js
export default class AudioManager {/*** 构造函数* @param {string} url - WebSocket服务器的地址* @param {function} onMessageCallback - 当WebSocket接收到消息时的回调函数*/constructor(url, onMessageCallback) {this.url = url; // WebSocket服务器的完整URLthis.websocket = null; // WebSocket连接实例this.audioContext = null; // 音频上下文this.audioStream = null; // 流媒体对象this.audioProcessor = null; // 音频处理器this.onMessageCallback = onMessageCallback; // WebSocket消息的回调函数}/*** 初始化WebSocket连接并设置消息监听器*/initWs() {console.log(this.url,';this.url');// 创建WebSocket实例this.websocket = new WebSocket(this.url);// 设置WebSocket接收消息时的回调函数this.websocket.onmessage = (e) => {if (this.onMessageCallback) {// 调用通过构造函数传入的回调函数处理接收到的消息this.onMessageCallback(e.data);}};// 请求用户的麦克风权限并开始处理音频流this.queryHttp();}/*** 停止录音并关闭所有资源*/stopRecording() {// 关闭所有音频轨道if (this.audioStream) {this.audioStream.getTracks().forEach((track) => track.stop());}// 断开音频处理器的连接if (this.audioProcessor) {this.audioProcessor.disconnect();}// 关闭音频上下文if (this.audioContext) {this.audioContext.close();}// 关闭WebSocket连接if (this.websocket) {this.websocket.close();}}/*** 请求麦克风资源,并在成功后处理音频流*/queryHttp() {navigator.mediaDevices.getUserMedia({audio: {echoCancellation: true, // 开启回声消除noiseSuppression: true, // 开启噪声抑制autoGainControl: true, // 开启自动增益控制},}).then((stream) => {// 处理成功获取的音频流this.handleStream(stream);}).catch((error) => {// 处理获取音频流失败的情况console.error("Error accessing microphone:", error);});}/*** 处理音频流,连接音频输入和处理器,并设置音频处理事件* @param {MediaStream} stream - 从麦克风获取的音频流*/handleStream(stream) {this.audioContext = new AudioContext({sampleRate: 16000, // 设置采样率latencyHint: "interactive", // 延迟模式为交互式channels: 1, // 单声道frameRate: 60, // 帧率sampleType: "int16", // 采样类型numberOfOutputs: 1, // 输出数量});// 创建音频源输入let audioInput = this.audioContext.createMediaStreamSource(stream);// 创建音频处理器this.audioProcessor = this.audioContext.createScriptProcessor(4096, 1, 1);// 设置音频处理事件this.audioProcessor.onaudioprocess = (event) => {// 获取音频数据const inputData = event.inputBuffer.getChannelData(0);// 转换音频数据为字节数据const byteData = this.convertToByteData(inputData);// 通过WebSocket发送字节数据this.websocket.send(byteData);};// 连接音频输入与处理器,处理器再连接到音频输出(扬声器)audioInput.connect(this.audioProcessor);this.audioProcessor.connect(this.audioContext.destination);// 保存音频流引用this.audioStream = stream;}/*** 将浮点数组的音频数据转换为字节数据* @param {Float32Array} inputData - 浮点数组格式的原始音频数据* @return {Uint8Array} 字节数据*/convertToByteData(inputData) {// 创建Int16Array,由于原始的音频数据是Float32Array类型,需要转换const intData = new Int16Array(inputData.map((item) => item * 32767));// 创建Uint8Array来保存字节数据const byteData = new Uint8Array(intData.length * 2);// 将Int16Array的数据转换为字节数据并填充到Uint8ArrayintData.forEach((value, index) => {byteData[index * 2] = value & 0xff; // 存储低位字节byteData[index * 2 + 1] = (value >> 8) & 0xff; // 存储高位字节});return byteData;}
}/*使用方法
<template><audio style="display: none" ref="audio" controls="controls" autoplay><source :src="audioUrl" type="audio/wav" /></audio>
</template><script>
import AudioManager from './AudioManager.js';export default {data() {return {audioManager: null,};},methods: {handleWsMessage(data) {let message = JSON.parse(data);this.$refs.audio.src = message.url; },startRecording() {this.audioManager = new AudioManager('your-server-url',this.handleWsMessage // 回调函数用于接受通话后获取的信息这里我用于播放接受的音频);this.audioManager.initWs();},},
};
</script> */

相关文章:

webscoket+webrtc实现语音通话

1.项目方案 前端采用webrtc创建音频上下文&#xff0c;后创建音频源输入和音频处理器&#xff0c;连接音频输入与处理器&#xff0c;处理器再连接到音频输出&#xff08;扬声器&#xff09;&#xff0c;再通过事件获取音频数据&#xff0c;把音频数据转换成字节数据通过webscok…...

PHP源码_众筹商城

众筹商城源码 众筹商品平台 商城加共识元富之路 网上商城众筹 前端是编译后的&#xff0c;后端PHP&#xff0c;带商城 运行截图 源码贡献 https://githubs.xyz/boot?app39 部分数据库表 CREATE TABLE ti_shopro_store (id int(11) NOT NULL AUTO_INCREMENT COMMENT ID,nam…...

智能小程序 Ray 开发——表单组件 Button 和 Checkbox 实操讲解

Button 多端按钮基础组件&#xff0c;可用于进行强交互的操作。 导入 import { Button } from ray-js/ray; 属性说明 属性类型默认值必填说明支持平台sizekeyof Sizedefault否按钮的大小涂鸦、微信typekeyof Typedefault否按钮的样式类型涂鸦、微信plainbooleanfalse否按钮…...

渗透之sql注入联合查询的注入

sql注入产生的原因&#xff1a; 由于程序过滤不严谨&#xff0c;导致用户有一些异常输入&#xff0c;最终触发数据库的查询。所以会出现sql注入这个问题。有些恶意的人就会利用这些信息导致数据库泄露。 注意&#xff1a;一般我们存在注入点我们会查询管理员的账号和密码&#…...

NLP transformers - 文本分类

Text classification 文章目录 Text classification加载 IMDb 数据集Preprocess 预处理EvaluateTrainInference 本文翻译自&#xff1a;Text classification https://huggingface.co/docs/transformers/tasks/sequence_classification notebook : https://colab.research.googl…...

QT 开发COM(ActiveX)组件基础介绍和方案验证

一、COM简介 1.1 COM是什么&#xff1f; COM&#xff0c;Component Object Model&#xff0c;即组件对象模型&#xff0c;是一种以组件为发布单元的对象模型&#xff0c;这种模型使各软件组件可以用一种统一的方式进行交互。COM 既提供了组件之间进行交互的规范&#xff0c;也…...

[1673]jsp在线考试管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 在线考试管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…...

每日一算法

问题 等待登机的你看着眼前有老有小长长的队伍十分无聊&#xff0c;你突然 想要知道&#xff0c;是否存在两个年龄相仿的乘客。每个乘客的年龄用 1个0 到 36500 的整数表示&#xff0c;两个乘客的年龄相差 365 以内 就认为是相仿的。 具体来说&#xff0c;你有一个长度为 n 的…...

Spring Cloud Gateway直接管理Vue.js的静态资源

1. 构建Vue.js应用 像之前一样&#xff0c;构建你的Vue.js应用&#xff0c;并将生成的静态资源&#xff08;位于dist目录&#xff09;复制到Spring Boot项目的某个目录&#xff0c;比如src/main/resources/static。 2. 配置Spring Boot静态资源处理 Spring Boot默认会处理sr…...

14.集合、常见的数据结构

集合 概念 Java中的集合就是一个容器&#xff0c;用来存放Java对象。 集合在存放对象的时候&#xff0c;不同的容器&#xff0c;存放的方法实现是不一样的&#xff0c; Java中将这些不同实现的容器&#xff0c;往上抽取就形成了Java的集合体系。 Java集合中的根接口&#x…...

NLP从入门到实战——命名实体识别

1. 命名实体识别 1.1 概念 中文命名实体识别&#xff08;Named Entity Recognition&#xff0c;NER是指识别中文文本中实体的边界和类别。命名实体识别是文本处理中的基础技术&#xff0c;广泛应用在自然语言处理、推荐系统、知识图谱等领域&#xff0c;比如推荐系统中的基于…...

接口测试工具-postman介绍

一、介绍 Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。 作用:常用于进行接口测试。 它可以模拟浏览器发起任何形式的HTTP请求...

日志中看到来自User Agent go-http-client / 1.1的大量请求(go-http-client 1.1)服务器爆了

在日志中看到来自User Agent go-http-client / 1.1的大量请求 The go-http-client/1.1 User Agent 是Google的Bot / Crawler 这不是真的。这可能是指Go库&#xff08;根据您提到的来源&#xff0c;由Google开发&#xff0c;但是我找不到可靠的信息&#xff09;。 之前写过“G…...

yolov8 区域声光报警+计数

yolov8 区域报警计数 1. 基础2. 报警功能2. 1声音报警代码2. 2画面显示报警代码 3. 完整代码4. 源码 1. 基础 本项目是在 yolov8 区域多类别计数 的基础上实现的&#xff0c;具体区域计数原理可见上边文章 2. 报警功能 设置一个区域region_points&#xff0c;当行人这一类别…...

《QT实用小工具·五十五》带有标签、下划线的Material Design风格输入框

1、概述 源码放在文章末尾 该项目实现了一个带有标签动画、焦点动画、正确提示、错误警告的单行输入框控件。下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef LABELEDEDIT_H #define LABELEDEDIT_H#include <QObject> #include <QWidget>…...

用Go实现一个无界资源池

写在文章开头 我们希望通过go语言实现一个简单的资源池&#xff0c;而这个资源池的资源包括但不限于: 数据库连接池线程池协程池网络连接池 只要这些资源实现我们指定的关闭方法&#xff0c;则都可以通过我们封装的资源池进行统一管理&#xff0c;需要简单说明一下这个资源池…...

Apache Seata基于改良版雪花算法的分布式UUID生成器分析2

title: 关于新版雪花算法的答疑 author: selfishlover keywords: [Seata, snowflake, UUID, page split] date: 2021/06/21 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 关于新版雪花算法的答疑 在上一篇关于新版雪花算法的解析中…...

13、揭秘JVM垃圾回收器:面试必备知识,你掌握了吗?

13.1、前文回顾 在上一篇文章中,我们详细分析了触发Minor GC的时机,以及对象何时会从新生代迁移到老年代。我们还讨论了为了确保新生代向老年代的内存迁移安全,需要在Minor GC之前如何检查老年代的内存空间,以及在什么情况下会触发老年代的Full GC,以及老年代的垃圾回收算…...

治疗耳鸣患者案例分享第二期

“患者耳鸣20年了&#xff0c;目前耳朵没有堵或者胀的感觉&#xff0c;但是偶尔有点痒&#xff0c;平时会有头晕头胀这种情况&#xff0c;然后头晕是稍微晕炫一下。然后头疼是经常有的&#xff0c;头胀不经常。” 患者耳鸣持续20年&#xff0c;虽然耳朵没有堵或胀的感觉&#x…...

数据加密的方法

这些方法可以单独或结合使用&#xff0c;以提高数据的安全性和保护隐私。 对称加密&#xff1a;使用相同的密钥对数据进行加密和解密。常见的对称加密算法包括DES、AES和RC4。 非对称加密&#xff1a;使用一对密钥&#xff08;公钥和私钥&#xff09;对数据进行加密和解密。发…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...