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> */

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
实现web android语音,您可以按照以下步骤进行操作: 1. 在Android端,您可以使用AudioRecord类来录制音频数据,并使用WebSocket将其实时传输到服务器。您需要设置采样率、声道数等参数,并将录制的音频数据转换为字节数组。然后,您可以使用OkHttp或Java-WebSocket等库来实现WebSocket连接,并将字节数组发送到服务器。 2. 在服务器端,您需要解码接收到的字节数组,并将其转换为音频数据。您可以使用Java Sound API或其他音频处理库来实现。然后,您可以使用WebSocket将处理后的音频数据实时发送回客户端。 3. 在web端,您可以使用WebRTC或其他音视频通信库来实现语音通话。您需要创建一个音频流,并将其连接到服务器端的WebSocket,以接收实时音频数据。然后,您可以使用AudioContext和MediaStream等API来播放接收到的音频流。 4. 在Android端和web端,您可以使用AudioTrack类来播放音频数据。Android端的AudioTrack可以直接播放从服务器接收到的音频数据,而web端的AudioTrack可以播放通过WebRTC接收到的音频数据。 5. 对于后端,您可以使用Spring Boot框架来搭建WebSocket服务器,并使用Spring WebSocket API来实现WebSocket连接和消息处理。您需要定义一个WebSocketHandler类来处理接收到的音频数据,并将其发送到连接的客户端。 需要注意的是,实现语音通话需要考虑网络延迟和带宽等问题。您可以设置合适的传输速率和压缩算法来优化传输效率,同时使用QoS技术来保证语音通话的质量。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农六六

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值