You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Twilio的μ-law 8kHz音频格式转换为Discord.js可实时播放的格式?

如何将Twilio的μ-law 8kHz音频格式转换为Discord.js可实时播放的格式?

我明白你现在的痛点——要把Twilio传来的μ-law格式音频实时转到Discord里播放,之前转码还遇到了Buffer过大的错误。别着急,咱们一步步来解决这个问题,核心是要处理好实时流的分块转码,以及格式的正确转换。

首先理清楚双方的格式要求:

  • Twilio推送的是单声道、8kHz采样率、μ-law编码的音频片段
  • Discord.js语音模块需要的是Opus编码、单声道、支持8kHz/48kHz采样率的音频(8kHz转Opus后Discord会自动适配)

第一步:提前初始化Discord语音连接与播放器

先确保你的Bot已经成功登录Discord,并且完成语音频道的连接和播放器初始化,这部分要放在WebSocket服务启动之前:

const { WebSocketServer } = require("ws");
const base64 = require("base64-js");
const Discord = require("discord.js");
const { VoiceConnectionStatus, joinVoiceChannel, createAudioPlayer, NoSubscriberBehavior, createAudioResource, StreamType } = require("@discordjs/voice");
const { Readable } = require("stream");
const WaveFile = require("wavefile").WaveFile;
const { OpusEncoder } = require("@discordjs/opus");

const client = new Discord.Client({ intents: [Discord.GatewayIntentBits.Guilds, Discord.GatewayIntentBits.GuildVoiceStates] });

let connection;
let player;

client.on('ready', async () => {
  console.log('Discord Bot 已就绪');
  // 替换为你的目标语音频道ID
  const targetChannel = client.channels.cache.get('YOUR_VOICE_CHANNEL_ID');
  if (targetChannel) {
    connection = joinVoiceChannel({
      channelId: targetChannel.id,
      guildId: targetChannel.guild.id,
      adapterCreator: targetChannel.guild.voiceAdapterCreator,
    });

    player = createAudioPlayer({
      behaviors: {
        noSubscriber: NoSubscriberBehavior.Pause,
      },
    });
    connection.subscribe(player);

    // 监听播放器错误,避免崩溃
    player.on('error', err => {
      console.error('音频播放器出错:', err);
    });
  }
});

// 这里记得填写你的Discord Bot Token
client.login('YOUR_DISCORD_BOT_TOKEN');

// 启动WebSocket服务接收Twilio音频
const wss = new WebSocketServer({ port: 5000 });

第二步:正确处理Twilio的媒体Payload

在WebSocket的media事件中,我们需要分步骤完成格式转换,重点解决Buffer过大的问题:

wss.on("connection", (ws) => {
  console.log("Twilio连接已建立");

  ws.on("message", (message) => {
    const msg = JSON.parse(message);
    switch (msg.event) {
      case "connected":
        console.log("新的Twilio通话已连接");
        break;
      case "start":
        console.log(`开始媒体流传输...`, msg.start);
        break;
      case "media":
        console.log("收到音频片段");
        const payload = Buffer.from(msg.media.payload, "base64");

        // 1. 将μ-law编码转换为16位线性PCM
        const wav = new WaveFile();
        // 初始化WaveFile为Twilio的音频格式:单声道、8kHz、μ-law
        wav.fromScratch(1, 8000, 'ulaw', payload);
        // 转换为16位PCM(去掉μ-law编码)
        wav.toSampleRate(8000);
        // 移除WAV文件头(前44字节),只保留纯PCM数据
        const pcmBuffer = wav.toBuffer().slice(44);

        // 2. 调整PCM长度为Opus帧大小的倍数(8kHz单声道每帧对应20ms音频=320字节)
        const opusFrameSize = 320;
        let adjustedPcm = pcmBuffer;
        if (adjustedPcm.length % opusFrameSize !== 0) {
          const paddingLength = opusFrameSize - (adjustedPcm.length % opusFrameSize);
          adjustedPcm = Buffer.concat([adjustedPcm, Buffer.alloc(paddingLength, 0)]);
        }

        // 3. 分帧编码为Opus(避免一次性处理过大Buffer)
        const encoder = new OpusEncoder(8000, 1);
        const opusChunks = [];
        for (let i = 0; i < adjustedPcm.length; i += opusFrameSize) {
          const frameChunk = adjustedPcm.slice(i, i + opusFrameSize);
          opusChunks.push(encoder.encode(frameChunk));
        }

        // 4. 创建可读流并推送给Discord播放器
        const opusStream = new Readable({
          read() {
            opusChunks.forEach(chunk => this.push(chunk));
            this.push(null); // 标记当前音频片段流结束
          }
        });

        const audioResource = createAudioResource(opusStream, {
          inputType: StreamType.Opus, // 直接传入Opus编码流
        });

        // 实时播放当前片段
        player.play(audioResource);
        break;
      case "stop":
        console.log("Twilio通话已结束");
        break;
      default:
        break;
    }
  });

  ws.on("close", () => {
    console.log(`Twilio连接已关闭`);
  });
});

关键问题解析

  1. Buffer过大错误:之前的问题是一次性将大量PCM数据传入Opus编码器,现在我们分帧处理(每帧对应20ms音频),完全规避了Buffer大小限制。
  2. WAV头处理:WaveFile转换后会生成标准WAV头,我们需要移除这44字节,只保留纯PCM数据,否则编码器会解析出错。
  3. 实时性保障:每次收到Twilio的音频片段就立即处理播放,不累积数据,确保通话的实时性。

额外注意事项

  • 确保所有依赖安装完整:npm install discord.js @discordjs/voice @discordjs/opus wavefile ws base64-js
  • Twilio的WebSocket媒体推送默认是每20ms一段,我们的帧大小设置刚好匹配这个节奏,转码效率最高
  • 务必监听播放器错误事件,避免单段音频出错导致整个Bot崩溃

备注:内容来源于stack exchange,提问作者Axio Axio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 15:50:25