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

PlayHT与Twilio流式传输仅出白噪音问题排查求助

在Twilio播放PlayHT流式音频仅出现白噪音的排查方案

我们尝试通过Twilio播放从PlayHT获取的mulaw格式流式音频,但仅听到白噪音。两端日志无报错,以下是相关代码,求排查思路。

Twilio函数代码

exports.handler = function(context, event, callback) {
    let twiml = new Twilio.twiml.VoiceResponse();

    try {
        //const connect = twiml.connect();
        console.log("start");

        const connect = twiml.connect();
        connect.stream({
            name: "PlayHT",
            url: 'wss://f074-2a00-23a8-424f-b701-e862-5a32-4e45-397.ngrok-free.app'
        });
        
        console.log(twiml.toString());
        callback(null, twiml);
    } catch (error) {
        console.error("Error connecting to WebSocket:", error);
        callback(error);
    }
};

WebSocket服务器核心代码

wss.on('connection', function connection(ws) {
    ws.on('message', async function incoming(message) {
        // Parse the JSON string into an object
        const messageObj = JSON.parse(message);
        const event = messageObj.event;

        if (event == 'start') {        
            console.log('received: %s', message);

            const streamSid = messageObj.streamSid;

            // Create a test text stream
            const textStream = new Readable({
                read() {                    
                    this.push('This is a test');
                    this.push(null); // End of data
                },
            });

            // Stream audio from text
            const stream = await PlayHTAPI.stream(textStream, {
                voiceEngine: 'PlayHT2.0-turbo',
                voiceId: process.env.PLAYHT_VOICE_ID,
                output_format: "mulaw",
                quality: 'low',
                sample_rate: 8000
            });

            // Pipe the audio stream directly to the WebSocket
            stream.on('data', (chunk) => {
                console.log('sending: %s', chunk);
                
                // Encode chunk in base64
                const base64Chunk = chunk.toString('base64');

                // Prepare the JSON object in Twilio's expected format
                const twilioMessage = {
                    event: "media",
                    media: {
                        track: "outbound", // or "inbound" depending on your use case
                        payload: base64Chunk
                    },
                    streamSid: streamSid
                };

                // Send the JSON object as a string
                ws.send(JSON.stringify(twilioMessage));
            });

            stream.on('end', () => {
                const twilioMessage = {
                    event: "mark",
                    mark: {
                        "name": "PlayHT"
                    },
                    streamSid: streamSid
                };

                // Send the JSON object as a string
                ws.send(JSON.stringify(twilioMessage));

                console.log("--------------End stream--------------");
            });
        }
        else {
            if (event != 'media') {
                console.log('received: %s', message);
            }
        }     
    });
});

排查方向

  • 音频格式参数校验:确认PlayHT输出的mulaw音频是否严格符合Twilio要求——必须是8000Hz采样率、单声道。可以用ffmpeg命令直接验证:ffmpeg -i test.mulaw -hide_banner,查看输出中的采样率和声道数。
  • Base64编码正确性:将PlayHT返回的原始chunk保存为本地mulaw文件,再把该文件转成base64后解码回文件,对比两个文件是否完全一致。若不一致,说明编码环节存在问题。
  • Twilio消息格式检查:确保发送给Twilio的JSON结构无错误,track字段必须为"outbound",streamSid要和Twiliostart事件中返回的完全一致,不能有拼写错误或值不匹配。
  • 流式传输时序与测试:在start事件触发后,先发送一段已知有效的mulaw音频片段(比如提前录制的短语音),验证Twilio是否能正常播放,以此排除PlayHT流本身的问题。
  • 消息大小控制:Twilio对单条媒体消息的payload大小有要求,建议将每个音频chunk拆分到160字节左右(对应20ms的8000Hz mulaw音频),过大的chunk可能导致解析失败。

内容的提问来源于stack exchange,提问作者Ovidiu Gliga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:07:18