如何加速Google Cloud Speech转写?流式API使用求助
实现Google Cloud Speech流式转写的完整指南
我来帮你一步步搞定这个流式语音转写的提速问题——之前我做过类似的实现,刚好能解决你的几个核心疑问:浏览器端的流式捕获、JS到Java服务器的可靠流传输,以及Java端对接Google Cloud Speech的流式API。
一、浏览器端:捕获并流式发送音频到Java服务器
首先澄清:浏览器端没有直接暴露Google Cloud Speech的流式JS API(主要是为了避免API密钥泄露),但我们可以用原生的MediaRecorder API捕获麦克风流,再通过WebSocket实现低延迟的流式传输,完全不会破坏音频数据:
捕获麦克风流并配置传输
async function startRecording() { // 获取麦克风权限并捕获流,强制指定16kHz单声道(匹配Speech API要求) const stream = await navigator.mediaDevices.getUserMedia({ audio: { sampleRate: 16000, channelCount: 1 } }); // 配置MediaRecorder为Google Speech兼容的格式:16kHz单声道,Opus编码 const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm;codecs=opus', audioBitsPerSecond: 16000 }); // 建立WebSocket连接到Java服务器的流式接口 const socket = new WebSocket('ws://your-java-server-domain:8080/speech-stream'); // 每300ms发送一次音频块(可根据延迟需求调整,200-500ms是平衡延迟和请求量的区间) mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0 && socket.readyState === WebSocket.OPEN) { socket.send(event.data); } }; // 录音结束时发送明确的结束标记 mediaRecorder.onstop = () => { if (socket.readyState === WebSocket.OPEN) { socket.send(JSON.stringify({ type: 'RECORDING_END' })); } socket.close(); }; // 开始录音 mediaRecorder.start(300); return { mediaRecorder, socket }; }用WebSocket的原因是它天生支持双向实时流,比HTTP Chunked更适合这种场景,而且能轻松传递结束信号。
实时接收转写结果
要实现类似官方Demo的实时更新,监听WebSocket的消息即可:// 在startRecording函数里添加 socket.onmessage = (event) => { const transcript = event.data; // 更新页面上的转写区域 document.getElementById('live-transcript').textContent = transcript; };
二、Java服务器:接收流并调用Google Cloud Speech的streamingRecognize
Java端的核心是用Google Cloud Speech的SDK处理流式请求,同时通过WebSocket接收浏览器的音频块:
添加依赖
确保Maven的pom.xml里包含最新版的Speech SDK:<dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-speech</artifactId> <version>2.32.0</version> <!-- 替换为最新版本 --> </dependency>WebSocket端点实现
这里用Spring Boot的WebSocket示例(也可以用Java EE标准的WebSocket API):@Component @ServerEndpoint("/speech-stream") public class SpeechStreamingEndpoint { private SpeechClient speechClient; private StreamObserver<StreamingRecognizeRequest> requestObserver; @OnOpen public void onOpen(Session session) throws IOException { // 初始化Speech客户端 speechClient = SpeechClient.create(); // 配置识别参数:和浏览器端的音频格式完全匹配 RecognitionConfig config = RecognitionConfig.newBuilder() .setEncoding(RecognitionConfig.AudioEncoding.OGG_OPUS) .setSampleRateHertz(16000) .setLanguageCode("zh-CN") // 替换成你需要的语言代码 .build(); StreamingRecognitionConfig streamingConfig = StreamingRecognitionConfig.newBuilder() .setConfig(config) .setInterimResults(true) // 开启实时中间结果(关键!实现Demo的实时转写) .build(); // 构建结果观察者:把识别结果发回浏览器 StreamObserver<StreamingRecognizeResponse> responseObserver = new StreamObserver<>() { @Override public void onNext(StreamingRecognizeResponse response) { // 取第一个结果的最优转录文本 String transcript = response.getResultsList().get(0) .getAlternativesList().get(0).getTranscript(); try { session.getBasicRemote().sendText(transcript); } catch (IOException e) { e.printStackTrace(); } } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onCompleted() { try { session.close(); } catch (IOException e) { e.printStackTrace(); } } }; // 获取流式请求的观察者 requestObserver = speechClient.streamingRecognizeCallable().call(responseObserver); // 发送第一个请求:必须包含流式配置 requestObserver.onNext(StreamingRecognizeRequest.newBuilder() .setStreamingConfig(streamingConfig) .build()); } @OnMessage public void onMessage(Session session, ByteBuffer message) { // 判断是否是录音结束标记 try { String signal = new String(message.array()); if (signal.contains("RECORDING_END")) { // 通知Google API结束识别 requestObserver.onCompleted(); speechClient.close(); return; } } catch (Exception ignored) { // 如果是二进制音频数据,会抛出异常,直接处理音频块 } // 发送音频块到Google Speech API requestObserver.onNext(StreamingRecognizeRequest.newBuilder() .setAudioContent(ByteString.copyFrom(message)) .build()); } @OnClose public void onClose(Session session) { if (requestObserver != null) { requestObserver.onCompleted(); } if (speechClient != null) { try { speechClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }关键细节
- 格式匹配:浏览器的音频编码、采样率、声道数必须和Java端的
RecognitionConfig完全一致,否则API会返回错误或识别失败。 - 初始请求:第一个
StreamingRecognizeRequest必须携带streamingConfig,后续请求只需要发送音频数据。 - 结束标记:收到浏览器的结束信号后,必须调用
requestObserver.onCompleted(),否则Google API会一直等待数据,不会返回最终结果。
- 格式匹配:浏览器的音频编码、采样率、声道数必须和Java端的
三、优化提速的小技巧
- 调整浏览器发送音频块的间隔(比如从300ms改成200ms),减少识别延迟,但注意不要太小导致请求过载。
- 开启
setInterimResults(true)后,Google API会返回实时的中间结果,用户能看到“打字式”的转写效果,体验和官方Demo一致。 - 确保Java服务器和Google Cloud Speech API的区域一致(比如都用
asia-east1),减少网络延迟。
内容的提问来源于stack exchange,提问作者DeepProblems
相关产品推荐
相关产品推荐

