|
|
@@ -9,8 +9,6 @@ import gzip
|
|
|
import json
|
|
|
import uuid
|
|
|
import subprocess
|
|
|
-import tempfile
|
|
|
-import os
|
|
|
|
|
|
import websockets
|
|
|
|
|
|
@@ -135,32 +133,35 @@ def _parse_response(res: bytes) -> dict:
|
|
|
def _convert_webm_to_pcm(webm_data: bytes) -> bytes:
|
|
|
"""
|
|
|
使用 ffmpeg 将 WebM/Opus 音频转为 PCM 16kHz 16-bit 单声道。
|
|
|
- 返回原始 PCM 字节。
|
|
|
+ 通过 stdin/stdout 管道传输,避免临时文件问题。
|
|
|
"""
|
|
|
- inp_path = None
|
|
|
- out_path = None
|
|
|
- try:
|
|
|
- with tempfile.NamedTemporaryFile(suffix='.webm', delete=False) as inp:
|
|
|
- inp.write(webm_data)
|
|
|
- inp_path = inp.name
|
|
|
- out_path = inp_path.replace('.webm', '.pcm')
|
|
|
-
|
|
|
- subprocess.run(
|
|
|
- [
|
|
|
- 'ffmpeg', '-y', '-i', inp_path,
|
|
|
- '-f', 's16le', '-ar', '16000', '-ac', '1',
|
|
|
- out_path
|
|
|
- ],
|
|
|
- check=True,
|
|
|
- capture_output=True,
|
|
|
- )
|
|
|
- with open(out_path, 'rb') as f:
|
|
|
- return f.read()
|
|
|
- finally:
|
|
|
- if inp_path and os.path.exists(inp_path):
|
|
|
- os.unlink(inp_path)
|
|
|
- if out_path and os.path.exists(out_path):
|
|
|
- os.unlink(out_path)
|
|
|
+ cmd = [
|
|
|
+ 'ffmpeg',
|
|
|
+ '-y',
|
|
|
+ '-f', 'webm', # 明确指定输入格式
|
|
|
+ '-i', 'pipe:0', # 从 stdin 读取
|
|
|
+ '-vn', # 跳过视频流
|
|
|
+ '-acodec', 'pcm_s16le',
|
|
|
+ '-ar', '16000',
|
|
|
+ '-ac', '1',
|
|
|
+ '-f', 's16le', # 输出原始 PCM
|
|
|
+ 'pipe:1', # 输出到 stdout
|
|
|
+ ]
|
|
|
+
|
|
|
+ result = subprocess.run(
|
|
|
+ cmd,
|
|
|
+ input=webm_data,
|
|
|
+ capture_output=True,
|
|
|
+ )
|
|
|
+
|
|
|
+ if result.returncode != 0:
|
|
|
+ stderr_msg = result.stderr.decode('utf-8', errors='replace')[:500]
|
|
|
+ raise RuntimeError(f"ffmpeg 转码失败 (exit={result.returncode}): {stderr_msg}")
|
|
|
+
|
|
|
+ if not result.stdout:
|
|
|
+ raise ValueError("ffmpeg 转码输出为空")
|
|
|
+
|
|
|
+ return result.stdout
|
|
|
|
|
|
|
|
|
async def transcribe_audio(audio_data: bytes) -> str:
|