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服务器的完整URL
    this.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的数据转换为字节数据并填充到Uint8Array
    intData.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> */

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构-二叉搜索树(BST)

目录 什么是二叉搜索树 二叉搜索树的特性 (1)顺序性 (2)局限性 二叉搜索树的应用 二叉搜索树的操作 (1)查找节点 (2)插入节点 (3)删除节点 (4)中序遍历 什么是二叉搜索树 如图所示&#xff0c;二叉搜索树&#xff08;binary search tree&#xff09;满足以下条件。…

【Vivado那些事儿】使用 Python 提取 ILA 数据

ILA应该是调试AMD-Xilinx FPGA最常用的IP。 在调试中&#xff0c;我们希望 ILA 中的波形可以提供有关设计问题的所有信息&#xff0c;但情况并非如此。对于复杂的调试&#xff0c;我们还需要将 ILA 捕获的真实数据存储到可以进一步处理的文件中。根据放置 ILA 的位置&#xff0…

C语言阶段的题目解析

前言 我们C语言已经学习的差不多了&#xff0c;但是C语言之中存在的一些问题与难点我们还不一定能够又快又好地解决&#xff0c;为了夯实我们的基础&#xff0c;我们来练习几道稍微有点难度的C语言习题吧 例题一 题目 int main(void) {unsigned char i 7;int j 0;for (; i…

【PyTorch 实战3:YOLOv5检测模型】10min揭秘 YOLOv5 检测网络架构、工作原理以及pytorch代码实现(附代码实现!)

YOLOv5简介 YOLOv5&#xff08;You Only Look Once, Version 5&#xff09;是一种先进的目标检测模型&#xff0c;是YOLO系列的最新版本&#xff0c;由Ultralytics公司开发。该模型利用深度学习技术&#xff0c;能够在图像或视频中实时准确地检测出多个对象的位置及其类别&…

千锤百炼之每日算法(三)

题外话 不会再水了,先把算法任务完成! 正题 第一题 简写单词 规定一种对于复合词的简写方式为只保留每个组成单词的首字母,并将首字母大写后再连接在一起 比如“College English Test"可以简写成“CET",“Computer Science 可以简写为"CS"&#xff0c;I a…

【深度学习】【Lora训练1】StabelDiffusion,Lora训练过程,秋叶包,Linux,SDXL Lora训练

文章目录 一、环境搭建指南二、个性化安装流程三、启动应用四、打开web五、开始训练 19.27服务器 一、环境搭建指南 打造一个高效且友好的开发环境&#xff1a; 项目源码获取&#xff1a; 通过以下命令轻松克隆项目及所有子模块至您的Linux系统&#xff1a; git clone --recu…

《ElementUI 基础知识》el-tabs header 监听鼠标中键滚动时左右滑动(ElementPlus同样适用)

前言 收到需求&#xff0c;可监听 el-tabs 头在鼠标 hover 时。滑动鼠标中键&#xff0c;可左右滑动&#xff01; 效果 鼠标中键上下滑动时&#xff1b;向上滑&#xff0c;向左移动&#xff1b;向下滑&#xff0c;向右移动&#xff1b; 实现 代码56 - 60行&#xff0c;添加…

寝室快修|基于SprinBoot+vue的贵工程寝室快修小程序(源码+数据库+文档)

贵工程寝室快修目录 目录 基于SprinBootvue的贵工程寝室快修小程序 一、前言 二、系统设计 三、系统功能设计 1学生信息管理 2 在线报修管理 3公告信息管理 4论坛信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&a…

操作系统安全:安全审计,Windows系统日志详解,Windows事件ID汇总

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

C++深度解析教程笔记2

C深度解析教程笔记2 第3课 - 进化后的 const 分析实验-C与C的const区别实验-C与C的const区别&const作用域 第4课 - 布尔类型和引用小结 本文学习自狄泰软件学院 唐佐林老师的 C深度解析教程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 第3课 - 进化后…

Unity 递归实现数字不重复的排列组合

实现 private void Permutation(List<int> num, int leftIndex, List<string> strs) {if (leftIndex < num.Count){for (int rightIndex leftIndex; rightIndex < num.Count; rightIndex){Swap(num, leftIndex, rightIndex);Permutation(num, leftIndex 1…

HarmonyOS 鸿蒙下载三方依赖 ohpm环境搭建

前言 ohpm&#xff08;One Hundred Percent Mermaid &#xff09;是一个集成了Mermaid的命令工具&#xff0c;可以用于生成关系图、序列图、等各种图表。我们可以使用ohpm来生成漂亮且可读性强的图表。 本期教大家如何搭建ophm环境&#xff1a; 一、在DevEco Studio中&#…

前端可以掌握的nginx相关操作

一、前言&#xff1a; 在日常开发中&#xff0c;前端工程师可以把打好的前端包直接放到测试服务器上&#xff0c;自己再验证功能是否改好&#xff0c;这样可以提高开发效率&#xff0c;写篇笔记记录一下我个人用到的命令 二、使用的工具 用MobaXterm完成相关操作&#xff0c…

Vue3 + TS 项目实战 - 后台管理系统 - 按钮权限

前期回顾 网站的打赏 —— 新一代的思路-CSDN博客https://blog.csdn.net/m0_57904695/article/details/136704914?spm1001.2014.3001.5501 目录 &#x1f6a9; XX银行_系统管理_按钮权限控制_前端_提测单 项目信息 提测版本信息 功能列表 测试范围 测试环境 ✅ 步…

[paper note]代码生成评估模型-CodeBLEU原理分析

论文信息 论文标题&#xff1a;CodeBLEU: a Method for Automatic Evaluation of Code Synthesis 发表时间&#xff1a;2020年9月 论文原文&#xff1a;CodeBLEU: a Method for Automatic Evaluation of Code Synthesis 论文内容 摘要 评价指标对一个领域的发展起着至关重…

大厂常见算法50题-替换空格

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注, 一起巧‘背’算法! 文章目录 题目解法一 String类replace方法解法二 遍历替换总结 题目 解法一 String类replace方法 String类自带的replace&#xff0c;方法传入两个char类型的参数&#xff0c;分…

分类预测 | Matlab实现CNN-GRU-SAM-Attention卷积门控循环单元融合空间注意力机制的数据分类预测

分类预测 | Matlab实现CNN-GRU-SAM-Attention卷积门控循环单元融合空间注意力机制的数据分类预测 目录 分类预测 | Matlab实现CNN-GRU-SAM-Attention卷积门控循环单元融合空间注意力机制的数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现CNN-GRU…

蓝牙低能耗安全连接 – 数值比较

除了 LE Legacy 配对之外&#xff0c;LE Secure Connections 是另一种配对选项。 LE 安全连接是蓝牙 v4.2 中引入的增强安全功能。它使用符合联邦信息处理标准 (FIPS) 的算法&#xff08;称为椭圆曲线 Diffie Hellman (ECDH)&#xff09;来生成密钥。对于 LE 安全连接&#xff…

【Stream流基础篇】Java中的函数、函数对象、函数接口和方法引用及转换

什么是函数 在数学中&#xff0c;函数是这样定义的&#xff1a;它是给定一个数集A&#xff0c;假设其中的元素为x&#xff0c;对A中的元素x施加对应法则f&#xff0c;记作f&#xff08;x&#xff09;&#xff0c;得到另一数集B&#xff0c;假设B中的元素为y&#xff0c;则y与x…

pytorch中的过拟合和欠拟合

基本概念 我们知道&#xff0c;所谓的神经网络其实就是一个复杂的非线性函数&#xff0c;网络越深&#xff0c;这个函数就越复杂&#xff0c;相应的表达能力也就越强&#xff0c;神经网络的训练则是一个拟合的过程。   当模型的复杂度小于真实数据的复杂度&#xff0c;模型表…
最新文章