Twilio Media Streams+ElevenLabs+OpenAI(Python):呼叫可接通、转录正常,但主叫方听不到声音
兄弟我之前也踩过一模一样的坑!你说的这个场景我太熟悉了——用Twilio Media Streams搭实时AI语音助手,Quart+Hypercorn跑Python服务,转录正常、GPT回复也能生成,但主叫端就是听不到ElevenLabs输出的音频,完全静音对吧?我当时折腾了快一下午,总结了几个最可能的问题点,你挨个排查试试:
1. 最常见的坑:ElevenLabs音频格式和Twilio要求不匹配
Twilio Media Streams对音频格式有严格要求:必须是G.711 μ-law(PCMU)、8kHz采样率、单声道、8位量化的原始音频流。但ElevenLabs默认返回的要么是MP3,要么是16位线性PCM,这俩Twilio都不认!
解决办法:调用ElevenLabs API时,一定要指定output_format为pcm_8000(注意这个是16位线性PCM,还得转成PCMU),然后把返回的音频数据转换成Twilio需要的PCMU格式。比如我当时用的代码片段:
# 调用ElevenLabs获取流式音频 import requests # 用requests直接调API更灵活,方便处理流式数据 resp = requests.post( f"https://api.elevenlabs.io/v1/text-to-speech/{你的语音ID}", headers={"xi-api-key": 你的ElevenLabs密钥}, json={ "text": gpt生成的回复内容, "model_id": "eleven_monolingual_v1", "voice_settings": {"stability": 0.6, "similarity_boost": 0.8} }, params={"output_format": "pcm_8000"}, # 关键:指定8kHz 16位线性PCM stream=True ) # 把16位线性PCM转成Twilio需要的PCMU格式 import struct def linear16_to_pcmu(sample): if sample == 0: return 0xff sign = (sample >> 8) & 0x80 magnitude = abs(sample) if magnitude > 32635: magnitude = 32635 exponent = 0 temp = magnitude >> 7 while temp > 0: exponent += 1 temp >>= 1 exponent = min(exponent, 7) mantissa = (magnitude >> (exponent + 3)) & 0x0f return ~(sign | (exponent << 4) | mantissa) & 0xff # 逐块处理音频流 for chunk in resp.iter_content(chunk_size=320): # 320字节对应40ms音频(16位PCM,8kHz) if chunk: # 把字节转成16位整数数组 samples = struct.unpack(f">{len(chunk)//2}h", chunk) # 转成PCMU字节 pcmu_chunk = bytes([linear16_to_pcmu(s) for s in samples]) # 接下来就是把这个pcmu_chunk发给Twilio
2. WebSocket消息格式不对,Twilio不认
你是不是直接把音频字节发过去了?Twilio要求必须把音频包装成指定的JSON结构,还要用base64编码payload!正确的WebSocket消息应该是这样的:
import json import base64 # 包装成Twilio要求的Media事件 media_msg = json.dumps({ "event": "media", "media": { "payload": base64.b64encode(pcmu_chunk).decode("utf-8") }, "streamSid": 你的Twilio流ID # 这个必须和Twilio连接时给的streamSid一致 }) # 通过Quart的WebSocket发送 await websocket.send(media_msg)
我当时就是忘了base64编码,直接发原始字节,Twilio根本不处理,自然没声音。
3. 音频发送太滞后或未流式处理
如果你等ElevenLabs把整个音频生成完再一次性发给Twilio,可能Twilio的流已经超时或者主叫端已经听不到了。一定要用流式处理:一边接收ElevenLabs的音频块,一边立刻转码、包装、发送给Twilio,这样声音才会实时出来。
4. 最后可以用Twilio日志排查
去Twilio控制台的「调试日志」里看Media Stream相关的事件,如果Twilio收到了你发的media事件,说明连接和消息格式没问题,那肯定是音频格式的问题;如果没收到media事件,那就是你的WebSocket发送逻辑有问题,比如没拿到正确的streamSid,或者Quart的WebSocket有阻塞。
我当时就是卡在前两个问题上,转对格式+包装正确的JSON后,声音立刻就出来了!你挨个试一遍,应该能解决。
内容来源于stack exchange

