录音实时上传

前言

最近在工作有一个需求,在浏览器实现视频对话的录音,然后需要将录音上传到服务端;

一、问题

一开始想的是当挂断电话的时候,将录音数据直接发送到后端,但是会有几个问题

1.如果用户直接管理浏览器的话,上传肯定是没办法上传的,sendBeacon有不少限制,比 如限制上传大小,

2.如果录音比较大,一次性上传大文件不太好,虽然可以分段上传

虽然每个问题都能处理,但是每一个问题处理起来都挺麻烦的,那有没有比较简单的处理方式呢?有的,兄弟,有的

二、使用websocket

我们可以在录音的时候连接websocket,然后实时将数据发送给后端,让后端将录音文件拼接起来

js 复制代码
let websocket;
const WEBSOCKET_URL = "ws://localhost:8081"; // 替换为你的WebSocket地址
const MAX_RETRY_COUNT = 5;
const RECONNECT_DELAY_BASE = 1000;
let audioBufferQueue = [];
// 编写重连;
let reconnectAttempts = 0;
let index = 0;
// 是否处于重连状态
let isReconnecting = false;
function attemptReconnect() {
  if (reconnectAttempts < MAX_RETRY_COUNT) {
    const delay = RECONNECT_DELAY_BASE * Math.pow(2, reconnectAttempts);
    console.log(`将在${delay}ms后尝试重连...`);

    setTimeout(() => {
      reconnectAttempts++;
      setupWebSocket();
    }, delay);
  } else {
    console.error(`已达到最大重连次数(${MAX_RETRY_COUNT}),停止尝试`);
    ElMessage.error("停止录音");
    audioBufferQueue = [];
    stopRecording();
  }
}
function setupWebSocket() {
  websocket = new WebSocket(WEBSOCKET_URL);

  websocket.onopen = () => {
    reconnectAttempts = 0; // 重置重连计数器
    console.log("WebSocket连接成功");
    isReconnecting = false;
    while (
      audioBufferQueue.length > 0 &&
      websocket.readyState === WebSocket.OPEN
    ) {
      websocket.send(audioBufferQueue.shift());
    }
    // if (mediaRecorder && mediaRecorder.state === "paused") {
    //   mediaRecorder.resume(); // 恢复暂停的录音
    // }
  };

  websocket.onclose = (event) => {
    if (event.wasClean) {
      console.log(`连接正常关闭`);
      isReconnecting = false;
      audioBufferQueue = [];
      index = 0;
    } else {
      console.warn("连接异常断开");
      isReconnecting = true;
      attemptReconnect();
    }
  };

  websocket.onerror = (error) => {
    console.error("WebSocket错误:", error);
  };
}
async function startRecord() {
  try {
    // 1. 检查浏览器支持
    if (!navigator.mediaDevices?.getUserMedia) {
      throw new Error("您的浏览器不支持音频录制功能");
    }

    setupWebSocket();

    // 3. 获取麦克风权限
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        sampleRate: 16000,
        channelCount: 1,
        echoCancellation: true,
      },
    });

    // 4. 选择最佳音频格式
    const format = getSupportedAudioFormat();

    // 5. 初始化录音器
    mediaRecorder = new MediaRecorder(stream, {
      mimeType: format,
      audioBitsPerSecond: 128000,
    });

    // 6. 实时音频数据处理
    mediaRecorder.ondataavailable = async (event) => {
      if (event.data.size > 0 && websocket.readyState === WebSocket.OPEN) {
        // 将Blob转换为ArrayBuffer发送
        const arrayBuffer = await event.data.arrayBuffer();
        if (isReconnecting) {
          // 不发送,将数据保存
          audioBufferQueue.push({ arrayBuffer, timestamp: index++ });
        } else {
          websocket.send({ arrayBuffer, timestamp: index++ });
        }
      }
    };

    // 7. 开始录制,设置较小的timeslice实现低延迟
    mediaRecorder.start(100); // 每100ms触发一次ondataavailable

    console.log("录音开始...");
  } catch (error) {
    console.error("录音启动失败:", error);
    alert(`录音失败: ${error.message}`);
    if (websocket) websocket.close();
  }
}

function stopRecording() {
  if (mediaRecorder) {
    mediaRecorder.stop();
    mediaRecorder.stream.getTracks().forEach((track) => track?.stop?.());
    mediaRecorder = null;
  }

  if (websocket && websocket.readyState === WebSocket.OPEN) {
    // 发送结束标记
    websocket.send(JSON.stringify({ action: "end" }));
    index = 0;
    websocket.close();
  }
}

主要看一下websocket这一块的逻辑

  1. 首先,当录音开始之后就开始连接websocket
  2. 然后再每次录制的ondataavailable事件,就将数据通过websocket发送给后端
  3. 当录音结束之后关闭websocket
    注意:如果再录制的过程中websocket断开怎么办呢
    那我们可以在传数据的时候给每个数据携带一个索引,后面后端拼接的时候通过索引按顺序拼接,再websocket重新连接的时候,将这段时间的数据放入一个数组中,当重连成功后,循环遍历发送,关闭websocket将index重置
    有人可能会想用数字溢出怎么办,Number是2^53 - 1,基本不会溢出,如果实在不放心,可以用BigInt来处理

总结

以上就是录音实时上传的一个实现,如果有更好的方法,欢迎大家提出

相关推荐
sanx1810 分钟前
一站式电竞平台解决方案:数据、直播、源码,助力业务飞速启航
前端·数据库·apache·数据库开发·时序数据库
余防18 分钟前
存储型XSS,反射型xss
前端·安全·web安全·网络安全·xss
ObjectX前端实验室31 分钟前
从零到一:系统化掌握大模型应用开发【目录】
前端·llm·agent
guoyp212638 分钟前
前端实验(二)模板语法
前端·vue.js
葡萄城技术团队44 分钟前
Excel 转在线协作难题破解:SpreadJS 纯前端表格控件的技术方案与实践
前端·excel
我的xiaodoujiao44 分钟前
Windows系统Web UI自动化测试学习系列3--浏览器驱动下载使用
前端·windows·测试工具·ui
一只小风华~1 小时前
学习笔记:Vue Router 中的嵌套路由详解[特殊字符]概述
前端·javascript·vue.js
泻水置平地1 小时前
若依前后端分离版实现前端国际化步骤
前端
Villiam_AY1 小时前
从后端到react框架
前端·react.js·前端框架
CodeCraft Studio1 小时前
全球知名的Java Web开发平台Vaadin上线慧都网
java·开发语言·前端·vaadin·java开发框架·java全栈开发·java ui 框架