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

Node.js迁移Google Speech-To-Text v2音频流无响应问题求助

问题排查与修复方案

核心问题分析

你的代码存在几个关键问题导致Google Speech-To-Text V2无响应:

  • 配置拼写错误:recognitionConfig里的audoDecodingConfig是笔误,正确字段名是audioDecodingConfig,这个错误会导致API无法识别解码配置。
  • 未指定Twilio音频格式:Twilio Stream传输的是PCMU(G.711 μ-law)编码、8kHz采样率的单声道音频,必须在配置中明确声明,否则Google API无法正确解码音频数据。
  • 使用私有API方法:client._streamingRecognize()是内部私有方法,应该使用官方公开的client.streamingRecognize()来创建识别流。
  • 音频数据未正确解码:Twilio传来的msg.media.payload是Base64编码的原始音频,需要先解码为Buffer再传入识别流。

修复后的完整代码

const speech = require('@google-cloud/speech').v2;
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const WebSocket = require('ws');
const https = require('https');

const client = new speech.SpeechClient({
  keyFilename: './googlecreds.json',
  apiEndpoint: 'eu-speech.googleapis.com'
});

const recognizerName = "projects/12345678910/locations/eu/recognizers/name";

// 修正配置:明确Twilio音频格式
const recognitionConfig = {
  audioDecodingConfig: {
    encoding: 'PCMU',
    sampleRateHertz: 8000,
    audioChannelCount: 1
  },
};

const streamingConfig = {
  config: recognitionConfig,
};

const configRequest = {
  recognizer: recognizerName,
  streamingConfig: streamingConfig,
};

const app = express();
app.use(bodyParser.urlencoded({ extended: true }));

// Load your key and certificate
const privateKey = fs.readFileSync('location', 'utf8');
const certificate = fs.readFileSync('location', 'utf8');
const ca = fs.readFileSync('location', 'utf8');

const credentials = {
  key: privateKey,
  cert: certificate,
  ca: ca
};

const server = https.createServer(credentials, app);
const wss = new WebSocket.Server({ 
  server: server, 
  path: '/stream',
});

wss.on("connection", function connection(ws) {
    let recognizeStream = null;

    ws.on("message", function incoming(message) {
        try {
            const msg = JSON.parse(message);
            switch (msg.event) {
                case "start":
                    // 使用公开的streamingRecognize方法
                    const stream = client.streamingRecognize();
                    recognizeStream = stream[0];
                    
                    recognizeStream.on('data', response => {
                      if (response.results && response.results.length > 0) {
                        const transcript = response.results[0].alternatives[0].transcript;
                        console.log('转写结果:', transcript);
                      }
                    })
                    .on('error', err => {
                      console.error('识别流错误:', err);
                    })
                    .on('end', () => {
                      console.log('识别流已结束');
                    });

                    // 发送配置请求
                    stream[1].write(configRequest);
                    break;
                case "media":
                    if (!recognizeStream) break;
                    // 将Base64解码为Buffer后传入
                    const audioBuffer = Buffer.from(msg.media.payload, 'base64');
                    recognizeStream.write({audio: audioBuffer});
                    break;
                case "stop":
                    if (recognizeStream) {
                        recognizeStream.end();
                        recognizeStream = null;
                    }
                    break;
            }
        } catch (err) {
            console.error('消息处理错误:', err);
        }
    });

    ws.on('close', () => {
        if (recognizeStream) {
            recognizeStream.end();
        }
    });
});

app.post('/voice', (req, res) => {
  const twiml = `
<Response>
    <Say>talk now</Say>
    <Connect>
        <Stream url="wss://my.domain.com/stream"/>
    </Connect>
    <Pause length="60"/>
</Response>
`;
  res.type('text/xml');
  res.send(twiml);
});

const port = process.env.PORT || 8080;
server.listen(port, '0.0.0.0', () => {
  console.log(`Server running on port ${port}`);
});

额外排查建议

  • 检查Google Cloud控制台的Speech-To-Text API是否已启用,并且服务账号拥有roles/speech.recognitionUser权限。
  • 在识别流的error事件中打印完整错误对象(而非仅message),便于排查API返回的具体问题。
  • 可以先录制一段Twilio的音频数据,用本地脚本测试转写是否正常,排除WebSocket传输环节的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 04:32:52