外观
FunASR Online Streaming ASR Server (WebSocket)
基于 FunASR C++ ONNX Runtime 构建的高性能实时语音识别服务,内置流式 FSMN-VAD 语音活动检测,支持 SenseVoiceSmall 极速伪流式(多语种 + 标点/ITN + 情绪识别)、Paraformer 2-pass(Two-Pass 双流模式) 实时增量上屏与句尾高精度纠错、以及 Paraformer 1-pass 单流实时模式,完全契合 FreeSWITCH mod_audio_fork 与 callflow-esl。
1. 核心架构与运行模式
服务支持通过配置文件中的 asr.activeProfile 自由切换不同的识别模式与模型组合:
模式 1:SenseVoiceSmall 极速伪流式 (mode: "pseudo_streaming",默认激活)
客户端 (WebSocket 16kHz PCM16 流)
│
├── 1. 独立轻量 FSMN-VAD 切句 (~0.1ms) ──> 判定语音起止与静音段
│
├── 2. 首字极速通道 (累积 150ms PCM) ────> 即刻触发第 1 次 partial 上屏
│
├── 3. 增量自适应步长 (200ms ~ 500ms) ───> SenseVoice 多实例池非阻塞推理 ──> 持续推送 partial 实时字幕
│
└── 4. 句尾静音 / 流结束 ───────────────> SenseVoice 终态推理 + 富文本解析 ──> 推送 final 终态文本 + 语种/情绪/事件- 多语种与富文本原生支持:支持中、英、粤、日、韩等多语种混合识别(
auto自动检测),原生输出高质量标点、逆文本归一化(ITN)、情绪标签(开心、悲伤、愤怒等)与声音事件检测(笑声、掌声、BGM、哭声等); - 首字极速通道:发音达到 150ms 即刻触发首字上屏,自适应步长确保长时间语音推流性能平稳;
- 多实例池并发:基于非阻塞推理实例池(
instancePoolSize),避免 I/O Worker 线程阻塞,保证高并发 WebSocket 网络吞吐。
模式 2:Paraformer 2-Pass 双流模式 (mode: "2pass")
客户端 (WebSocket 16kHz PCM16 流)
│
├── 1. 每 600ms Chunk 到达 ──> Pass 1: Paraformer-Online (流式模型)
│ └──> 毫秒级推送 partial 实时字幕
│
└── 2. 句尾静音 / 流结束 ────> Pass 2: Paraformer-Offline (离线大模型) + CT-Transformer (标点模型)
└──> 纠错并推送 final 高精度文本 + 标点恢复 + 热词偏置- Pass 1(在线流式):音频 chunk 到达即刻送入
Paraformer-Online解码,毫秒级推送type: "partial"实时字幕; - Pass 2(离线重识别与标点):VAD 判定静音或推流结束时,自动调度
Paraformer离线大模型与CT-Transformer标点模型重算整句,推送type: "final"终态文本与热词纠错。
模式 3:Paraformer 单流实时模式 (mode: "online")
- 纯流式 CIF 毫秒级上屏,无句尾离线大模型二次重算,适合资源受限或超高并发单机场景。
2. 默认端口与协议
- 默认监听:
0.0.0.0:10099 GET /health:HTTP 健康检查;ws://<host>:10099/audio:WebSocket 实时音频推流协议。
3. WebSocket 协议交互规范
3.1 握手与连接建立
- 请求路径:
GET /audio HTTP/1.1 - 握手协议:标准 RFC 6455 WebSocket 协议(101 Switching Protocols);
- 子协议(可选):
Sec-WebSocket-Protocol: audio.drachtio.org(若配置了wsSubprotocol)。
3.2 客户端发送数据帧
1. 会话元数据帧(Text Frame, Opcode 0x1)(可选,建连后第一帧发送):
json
{
"uuid": "call-session-uuid-12345",
"direction": "inbound",
"source": "mod_audio_fork",
"hotwordProfile": "default",
"hotwords": "阿里巴巴 通义千问 验证码",
"language": "auto",
"useItn": true,
"enablePartial": true
}uuid/channel_uuid:呼叫通道唯一 ID,后续下发的所有 transcription 事件都会携带该 ID;hotwordProfile:指定激活的热词场景 ID(对应hotword.json中的 Profile);hotwords:动态注入自定义热词字符串(空格分隔);language:指定语种(auto、zh、en、yue、ja、ko);useItn:是否启用逆文本归一化(数字、日期转换,默认true);enablePartial:是否推送中间 partial 字幕(默认true)。
2. 音频数据帧(Binary Frame, Opcode 0x2):
- 格式:16kHz, 16-bit, 单声道 PCM 原始音频;
- 推荐分片:每包 20ms(640 字节,对接 FreeSWITCH RTP 默认切片)或 100ms~600ms 增量推流;
- 结束标识:可发送一个长度为 0 的 Binary Frame 告知当前音频流输入完毕并触发句尾结算。
3. 断开连接帧(Close Frame, Opcode 0x8):
- 服务端会在断开前确保
final终态帧与 Close 握手全部下发给客户端后再关闭 Socket。
3.3 服务端下发事件 (type: "transcription")
A. 实时增量字幕 (Partial)
json
{
"type": "transcription",
"data": {
"text": "重点呢想谈三个问题首先呢就是这一",
"isFinal": false,
"utteranceIndex": 1,
"speechStarted": true,
"speechActive": true,
"speechSegmentDetected": true,
"speechSegmentCompleted": false,
"language": "zh",
"emotion": "NEUTRAL",
"event": "Speech",
"serverSessionId": "ws-1",
"channelUuid": "call-session-uuid-12345"
}
}B. 句尾纠错与标点终态 (Final)
json
{
"type": "transcription",
"data": {
"text": "重点呢想谈三个问题。首先呢就是这一轮全球金融动荡的表现。",
"isFinal": true,
"utteranceIndex": 1,
"speechStarted": true,
"speechActive": false,
"speechSegmentDetected": true,
"speechSegmentCompleted": true,
"finalReason": "segment-completed",
"language": "zh",
"emotion": "HAPPY",
"event": "Speech",
"serverSessionId": "ws-1",
"channelUuid": "call-session-uuid-12345"
}
}4. HTTP 健康检查 (GET /health)
bash
curl http://127.0.0.1:10099/health返回示例:
json
{
"ok": true,
"service": "fanasr-asr-online-server",
"activeProfile": "sensevoice-pseudo-streaming",
"activeModel": "sensevoice-pseudo-streaming",
"description": "SenseVoiceSmall 极速伪流式(多语种检测 + 原生富文本标点/ITN + 情绪识别)",
"mode": "pseudo_streaming",
"activeSessions": 0,
"maxSessions": 256,
"wsPath": "/audio",
"ioWorkers": 8
}5. 配置文件说明 (config.json)
完整配置项定义、字段类型约束、默认值与高级场景调优请查阅专题文档: 📖 FunASR Online Streaming ASR Server 配置参考
json
{
"server": {
"host": "0.0.0.0",
"port": 10099,
"healthPath": "/health",
"wsPath": "/audio",
"wsSubprotocol": "audio.drachtio.org"
},
"concurrency": {
"maxSessions": 256,
"sessionPoolSize": 128,
"ioWorkers": 8,
"instancePoolSize": 6,
"inferenceThreads": 2,
"acceptBacklog": 256,
"workerPollTimeoutMs": 20
},
"socket": {
"tcpNoDelay": true,
"maxFrameBytes": 1048576,
"readBufferBytes": 65536,
"writeBufferBytes": 65536,
"recvBufferBytes": 65536,
"sendBufferBytes": 65536
},
"logging": {
"dir": "logs",
"debugTextFrames": false,
"debugAudioFrames": false,
"debugRecognitionState": false
},
"recording": {
"enabled": false,
"dir": "recordings"
},
"asr": {
"activeProfile": "paraformer-2pass",
"sampleRate": 16000,
"quantize": true,
"idleFinalizeMs": 1000,
"partialMinIntervalMs": 150,
"puncDir": "funasr-ct-transformer-zh-onnx",
"profiles": {
"sensevoice-pseudo-streaming": {
"description": "SenseVoiceSmall 极速伪流式(多语种检测 + 原生富文本标点/ITN + 情绪识别)",
"mode": "pseudo_streaming",
"modelDir": "funasr-sensevoicesmall-zh-cantonese-en-ja-ko-onnx",
"language": "auto",
"useItn": true,
"firstPartialMinMs": 150
},
"paraformer-2pass": {
"description": "Paraformer 2-Pass 双流模式(第1流实时 partial + 第2流离线大模型 final + CT-Transformer 标点纠错)",
"mode": "2pass",
"onlineModelDir": "funasr-paraformer-large-online-zh-onnx",
"offlineModelDir": "funasr-paraformer-large-zh-onnx",
"chunkSize": [5, 10, 5]
},
"paraformer-online": {
"description": "Paraformer 单流实时模式(纯流式 CIF 毫秒级上屏,无句尾二次重算)",
"mode": "online",
"onlineModelDir": "funasr-paraformer-large-online-zh-onnx",
"offlineModelDir": "funasr-paraformer-large-zh-onnx",
"chunkSize": [5, 10, 5]
}
},
"hotwordsFile": "hotword.json",
"defaultHotwordProfile": "default"
},
"vad": {
"modelDir": "funasr-fsmn-vad-zh-onnx",
"quantize": true,
"maxSpeechDuration": 20.0
}
}核心参数速查:
concurrency.maxSessions:系统最大流式并发会话数上限(超限返回 503 快速熔断,默认256);concurrency.ioWorkers:非阻塞 I/O 事件循环 Worker 线程数(默认8);concurrency.instancePoolSize:推理实例池容量 / 模型句柄池(默认6),多路并发时由实例池轮转处理,避免互斥锁阻塞 I/O Worker;concurrency.inferenceThreads:单个 ONNX Runtime 推理实例内部分配的计算线程数(默认2);asr.activeProfile:激活的场景 Profile 名称(如"sensevoice-pseudo-streaming"、"paraformer-2pass"、"paraformer-online");asr.idleFinalizeMs:静音自动终态结算超时(默认1000ms);asr.partialMinIntervalMs:流式中间字幕 (partial) 最小下发间隔毫秒数(默认150ms);vad.modelDir:全局 FSMN-VAD 模型目录路径(默认"funasr-fsmn-vad-zh-onnx");vad.quantize:是否使用 int8 量化 VAD 模型(默认true);vad.maxSpeechDuration:单句最大截断时长(秒,默认20.0s,防止无静音超长说话占用内存)。profiles.*.firstPartialMinMs:SenseVoice 伪流式专用的首字极速上屏门限(默认150ms);profiles.*.maxUtteranceMs:SenseVoice 伪流式专用的单句最长持续时长截断保护(默认15000ms);asr.hotwordsFile:独立热词配置文件路径(默认hotword.json)。
6. 编译与运行
6.1 Windows (MSVC)
powershell
.\build.ps1编译完成后运行:
powershell
cd target\win_x64
.\fanasr_asr_online_server.exe6.2 Linux (Ninja / GCC)
bash
./build.sh
cd target/linux_x64
./fanasr_asr_online_server7. 测试与批量识别
7.1 单音频实时推流联调
bash
bun run request-asr.js
# 或指定测试音频与热词
bun run request-asr.js path/to/sample.wav "阿里巴巴 通义千问"7.2 目录批量音频极速识别
支持对指定目录下的 WAV 音频进行并发批量识别,实时追加写入 CSV 结果文件,并支持断点续传:
bash
# 默认扫描测试音频目录,4 路并发
bun run batch-recognize.js
# 自定义目录、输出路径与并发数
bun run batch-recognize.js --dir "path/to/recordings" --output "results.csv" --concurrency 8