Windows 11 上部署 Jitsi Meet,并实现局域网实时语音翻译字幕
本文记录一个可复现的测试方案:在 Windows 11 上通过 WSL2 和 Docker Desktop 运行 Jitsi Meet,再接入完全位于 Docker 内网的 Whisper/NLLB 翻译适配器,实现"中文讲话、英文字幕;中文讲话、阿拉伯语字幕"等多语言实时字幕效果。
本案例重点是测试、内网和小规模使用。它不是面向公网生产环境的最终架构。
一、Jitsi Meet 是什么
Jitsi Meet 是一套开源的视频会议解决方案,支持浏览器和移动端访问,也可以通过 Docker 自行部署。它的核心组件包括:
- Jitsi Meet Web:浏览器会议页面。
- Prosody:XMPP 信令服务器,负责会议状态和参与者协商。
- Jicofo:会议控制组件,负责会议编排和桥接服务调度。
- Jitsi Videobridge(JVB):音视频 SFU,负责转发参会者的音视频流。
- 可选扩展:录制、直播、电话接入、Etherpad、转写和语音翻译等。
Jitsi 的基本会议功能包括摄像头、麦克风、屏幕共享、聊天、邀请链接、多人会议和移动端浏览器访问。Jitsi 官方文档将它定位为可以自行托管的开源视频会议平台;官方 Docker 方案适合快速部署和测试。
参考:
二、Jitsi 适合做什么
适合的场景
| 场景 | 适合原因 |
|---|---|
| 公司内网会议 | 音视频和业务数据可以留在企业网络内,部署成本较低 |
| 跨语言内部沟通 | 可以为不同参会者显示不同目标语言字幕 |
| 远程培训和教学 | 屏幕共享、多人会议和字幕可以组合使用 |
| 客服、技术支持和远程协作 | 可以通过会议链接快速加入,不强制安装客户端 |
| 功能验证和原型开发 | 开源、可修改,适合验证 WebRTC、转写和 AI 翻译方案 |
不适合直接照搬到生产环境的场景
- 面向公网的大规模会议平台。
- 对延迟、准确率和高并发有严格 SLA 的实时翻译服务。
- 尚未完成身份认证、审计、备份和模型授权确认的正式业务系统。
- 涉及高度敏感内容、但还没有完成数据流向和日志策略评审的会议。
Jitsi 本身可以作为可靠的视频会议基础,但"实时翻译"还取决于语音识别模型、翻译模型、硬件性能、网络质量和领域词汇。会议功能稳定,不代表 AI 字幕一定准确。
三、本案例的最终效果
会议中的每个人可以独立选择目标语言:
- A 说中文。
- B 选择
English,看到英文字幕。 - C 选择
العربية,看到阿拉伯语字幕。 - 识别结果中带有说话人信息,字幕可以区分是谁发言。
- 原始会议声音保持不变,本方案主要实现文字字幕翻译,不合成翻译语音。
图中是实际测试截图。发布到公网博客前,建议根据需要将局域网 IP、会议名称和参会者名称打码或替换。
四、整体架构

4.1 数据流
#mermaid-svg-IoZ5XKr0PlFHbs8k{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IoZ5XKr0PlFHbs8k .error-icon{fill:#552222;}#mermaid-svg-IoZ5XKr0PlFHbs8k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IoZ5XKr0PlFHbs8k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .marker.cross{stroke:#333333;}#mermaid-svg-IoZ5XKr0PlFHbs8k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IoZ5XKr0PlFHbs8k p{margin:0;}#mermaid-svg-IoZ5XKr0PlFHbs8k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .cluster-label text{fill:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .cluster-label span{color:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .cluster-label span p{background-color:transparent;}#mermaid-svg-IoZ5XKr0PlFHbs8k .label text,#mermaid-svg-IoZ5XKr0PlFHbs8k span{fill:#333;color:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .node rect,#mermaid-svg-IoZ5XKr0PlFHbs8k .node circle,#mermaid-svg-IoZ5XKr0PlFHbs8k .node ellipse,#mermaid-svg-IoZ5XKr0PlFHbs8k .node polygon,#mermaid-svg-IoZ5XKr0PlFHbs8k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .rough-node .label text,#mermaid-svg-IoZ5XKr0PlFHbs8k .node .label text,#mermaid-svg-IoZ5XKr0PlFHbs8k .image-shape .label,#mermaid-svg-IoZ5XKr0PlFHbs8k .icon-shape .label{text-anchor:middle;}#mermaid-svg-IoZ5XKr0PlFHbs8k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .rough-node .label,#mermaid-svg-IoZ5XKr0PlFHbs8k .node .label,#mermaid-svg-IoZ5XKr0PlFHbs8k .image-shape .label,#mermaid-svg-IoZ5XKr0PlFHbs8k .icon-shape .label{text-align:center;}#mermaid-svg-IoZ5XKr0PlFHbs8k .node.clickable{cursor:pointer;}#mermaid-svg-IoZ5XKr0PlFHbs8k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .arrowheadPath{fill:#333333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IoZ5XKr0PlFHbs8k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IoZ5XKr0PlFHbs8k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IoZ5XKr0PlFHbs8k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IoZ5XKr0PlFHbs8k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .cluster text{fill:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k .cluster span{color:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IoZ5XKr0PlFHbs8k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IoZ5XKr0PlFHbs8k rect.text{fill:none;stroke-width:0;}#mermaid-svg-IoZ5XKr0PlFHbs8k .icon-shape,#mermaid-svg-IoZ5XKr0PlFHbs8k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IoZ5XKr0PlFHbs8k .icon-shape p,#mermaid-svg-IoZ5XKr0PlFHbs8k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IoZ5XKr0PlFHbs8k .icon-shape .label rect,#mermaid-svg-IoZ5XKr0PlFHbs8k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IoZ5XKr0PlFHbs8k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IoZ5XKr0PlFHbs8k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IoZ5XKr0PlFHbs8k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTPS :8443
WebRTC 音视频
配置转写/翻译连接
Opus WebSocket
transcription-result
参会者浏览器
Jitsi Web / Nginx
Jitsi Videobridge
Prosody XMPP
Jicofo 会议控制
local-translation
Opus 解码 + VAD
Whisper small
语音识别
共享识别结果
NLLB-600M INT8
多目标语言翻译
models 持久化缓存
4.2 每个部分的实现点
| 部分 | 实现方式 | 关键配置或代码 |
|---|---|---|
| Windows 宿主机 | Windows 11 + WSL2 + Docker Desktop | Docker Desktop 使用 WSL2 后端 |
| Web 入口 | Jitsi Web/Nginx | PUBLIC_URL=https://<局域网IP>:8443 |
| 音视频媒体 | JVB 转发 WebRTC | 防火墙放行 10000/udp |
| 会议控制 | Prosody + Jicofo | Jicofo 负责建立转写/翻译 WebSocket |
| 语音输入 | JVB 输出参会者 Opus 音频 | 不开放翻译服务的局域网端口 |
| 语音识别 | Faster-Whisper small,CPU,INT8 | 支持 LOCAL_SOURCE_LANGUAGE=zh 等源语言提示 |
| 文本翻译 | NLLB-200 distilled 600M | 本地运行,输出多个目标语言字幕 |
| 参会者语言 | 前端翻译菜单 | 每个参会者可以独立选择目标语言 |
| 说话人显示 | 端点 ID 映射到 Jitsi participant name | 字幕显示说话人名称 |
| 隐私日志 | 后端不打印原文和译文 | 只记录语言、标签、字符数、耗时和错误 |
| 数据持久化 | Docker volume | 模型缓存存放在 local-translation/models |
五、为什么 Windows 需要 WSL2 和 Docker Desktop
Jitsi 的服务端组件主要运行在 Linux 容器中,Windows 不适合直接以原生 Windows 服务的方式部署完整 Jitsi 服务。Windows 11 上更容易复现的路线是:
text
Windows 11
└─ WSL2 / Virtual Machine Platform
└─ Docker Desktop(WSL2 backend)
└─ Jitsi Docker Compose
这套方式适合测试和内网小规模使用。正式公网环境通常更建议使用独立的 Linux 服务器或云主机,以获得更稳定的网络、端口、资源隔离和运维能力。
六、部署前准备
6.1 硬件和系统
- Windows 11 64 位。
- BIOS/UEFI 已开启硬件虚拟化。
- 已启用 WSL2 和 Virtual Machine Platform。
- 已安装 Ubuntu WSL2 发行版。
- 已安装 Docker Desktop,并启用 WSL2 后端。
- 建议至少 8 GB 内存;本案例的 Whisper 和 NLLB 模型会占用较多内存。
- 如果使用 CPU 推理,建议有较新的多核处理器。
6.2 网络和端口
内网测试至少需要:
| 端口 | 协议 | 用途 |
|---|---|---|
| 8443 | TCP | HTTPS 网页访问 |
| 10000 | UDP | Jitsi WebRTC 音视频媒体 |
local-translation 的 38030 只在 Docker 内部网络使用,不需要对局域网开放。
本案例局域网地址为:
text
https://10.27.0.253:8443
实际部署时必须替换成 Windows 主机当前的局域网 IPv4 地址。若路由器通过 DHCP 更换了地址,需要同步修改 PUBLIC_URL 和 JVB_ADVERTISE_IPS。
七、获取 Jitsi Docker 配置
官方建议使用 Docker 项目的稳定版发布包,而不是直接把开发分支当作生产部署代码。本文案例使用:
text
stable-11146-2
后续复现时可以使用官方最新稳定版,但要注意配置目录和环境变量可能随版本变化。
官方参考:
下载并解压后进入项目目录,例如:
powershell
cd C:\JitsiMeet\docker-jitsi-meet
Copy-Item env.example .env
生成 Jitsi 内部密码,并创建配置目录。不同版本的目录列表可能略有差异,应以对应版本的官方说明为准。
八、Jitsi 基础配置
在 .env 中至少配置以下内容。示例中的密码只表示配置项名称,不要把真实密码发布到博客或代码仓库。
dotenv
PUBLIC_URL=https://<Windows局域网IP>:8443
JVB_ADVERTISE_IPS=<Windows局域网IP>
HTTPS_PORT=8443
ENABLE_AUDIO_TRANSLATION=1
ENABLE_TRANSCRIPTIONS=1
JICOFO_TRANSLATION_URL_TEMPLATE=ws://local-translation:38030/translate?sessionId={{MEETING_ID}}
# 目标语言菜单
TRANSLATION_LANGUAGES=['zh-CN','zh-TW','en','ar','ja','ko','fr','de','es','ru','it','pt','hi','th','vi','id','tr','nl']
TRANSLATION_LANGUAGES_HEAD=['zh-CN','en','ar']
# 源语言:zh、en、ja、ar 或 auto
LOCAL_SOURCE_LANGUAGE=zh
源语言是会议级设置:
- 主要使用中文的会议设置为
zh,可以显著减少中文短句被误判成韩语、乌克兰语等语言。 - 英语会议设置为
en。 - 语言混杂的会议设置为
auto,但短句误判概率会增加。
目标语言则是参会者级设置:B 选择英文、C 选择阿拉伯语,不会互相覆盖。
九、实现本地 Whisper/NLLB 翻译适配器
9.1 适配器的职责
local-translation 服务实现 Jitsi bridge-based transcription/translation 所需的 WebSocket 接口:
text
/transcribe
/translate
它接收 JVB 转发的 Opus 音频,完成以下处理:
text
Opus 音频
→ Opus 解码
→ 48 kHz 转 16 kHz
→ 简单 VAD 和静音分段
→ Faster-Whisper 识别
→ NLLB 翻译
→ transcription-result
→ JVB 转发给参会者
官方 bridge-based 转写链路也是由 JVB 通过 WebSocket 连接转写服务,再把结果注入会议。本文没有使用已经过时的 Jigasi 转写路线。
参考:Jitsi Bridge-based Transcriptions
9.2 Docker Compose 覆盖文件
本文使用单独的 translation.yml,不修改 Jitsi 官方主 Compose 文件的主体结构。关键内容如下:
yaml
services:
local-translation:
build:
context: ./local-translation
image: jitsi/local-translation:latest
restart: unless-stopped
environment:
PORT: 38030
SOURCE_LANGUAGE: ${LOCAL_SOURCE_LANGUAGE:-auto}
WHISPER_MODEL: small
WHISPER_COMPUTE_TYPE: int8
WHISPER_THREADS: 8
TRANSLATION_MODEL_REPO: facebook/nllb-200-distilled-600M
TRANSLATION_NUM_BEAMS: 1
TRANSLATION_MAX_NEW_TOKENS: 96
TRANSLATOR_DYNAMIC_INT8: 1
SILENCE_SEC: 0.55
MAX_UTTERANCE_SEC: 6
MIN_SPEECH_SEC: 0.35
volumes:
- ./local-translation/models:/models
networks:
meet.jitsi:
networks:
meet.jitsi:
服务不映射新的宿主机端口,只加入 meet.jitsi Docker 内部网络。
9.3 识别和翻译模型
本案例使用:
- Faster-Whisper small:负责语音转文字。
facebook/nllb-200-distilled-600M:负责多语言文本翻译。- CPU 动态 INT8:减少 NLLB 内存占用和翻译耗时。
模型首次启动会下载到 local-translation/models。之后重启容器会复用缓存,但模型加载仍可能需要几十秒到几分钟。
需要注意:NLLB-200 distilled 600M 的 Hugging Face 模型卡标注为 CC-BY-NC-4.0,并且定位为研究模型。公司正式商用前必须审查模型许可证、数据合规和业务场景适用性,不能因为代码是开源的就默认模型可以直接商用。
参考:NLLB-200 distilled 600M 模型卡
十、三个关键优化
10.1 不在日志中记录字幕原文
服务端日志只保留类似下面的信息:
text
source=zh chars=8
translation tag=<speaker-tag> target=en chars=19
不记录识别原文和译文内容;Docker 日志限制为:
yaml
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
注意:这只代表后端日志不保存原文。会议中的字幕仍然会在参会者浏览器中显示,浏览器端的字幕面板属于会议使用体验,不能与服务端日志混为一谈。
10.2 手动指定源语言
Whisper 在很短、音量较低或带噪声的语音上可能误判语言。已增加:
dotenv
LOCAL_SOURCE_LANGUAGE=zh
服务也兼容 WebSocket URL 中的标准 lang 参数,例如:
text
/translate?sessionId=<MEETING_ID>&lang=zh
对于固定语言的会议,手动指定源语言通常比自动检测更稳定。对于中英日韩等语言频繁切换的会议,再使用 auto。
10.3 一次识别,多目标语言翻译
处理逻辑从:
text
每种目标语言都重新做一次语音识别
调整为:
text
一次 Whisper 识别
→ 共享源文本
→ 英文翻译
→ 中文翻译
→ 阿拉伯语翻译
服务端增加了识别任务和翻译任务缓存。相同音频块或相同文本/目标语言请求可以复用任务,减少重复 CPU 计算。
这并不意味着多种目标语言完全没有额外开销:每个目标语言仍需要一次文本翻译。真正进一步降低延迟,可以将 NLLB 转换为 CTranslate2/ONNX INT8,或者使用 GPU 推理。
十一、构建和启动
在部署目录执行:
powershell
cd C:\JitsiMeet\docker-jitsi-meet
# 首次构建本地翻译镜像
docker compose -f docker-compose.yml -f translation.yml build local-translation
# 启动 Jitsi 和本地翻译服务
docker compose -f docker-compose.yml -f translation.yml up -d
# 查看服务状态
docker compose -f docker-compose.yml -f translation.yml ps
当前案例已经准备了两个 PowerShell 脚本:
powershell
# 启动
powershell -ExecutionPolicy Bypass -File "C:\JitsiMeet\docker-jitsi-meet\start-jitsi.ps1"
# 停止,不删除配置和模型
powershell -ExecutionPolicy Bypass -File "C:\JitsiMeet\docker-jitsi-meet\stop-jitsi.ps1"
# 修改翻译代码后重新构建并启动
powershell -ExecutionPolicy Bypass -File "C:\JitsiMeet\docker-jitsi-meet\start-jitsi.ps1" -Rebuild
本案例脚本实际位于:
- start-jitsi.ps1
- stop-jitsi.ps1
启动脚本使用两个 Compose 文件,是因为 local-translation 和自定义 Web 镜像定义在 translation.yml 中。只执行没有 -f translation.yml 的命令,可能看不到本地翻译服务。
十二、访问和使用
在本机或同一局域网设备访问:
text
https://<Windows局域网IP>:8443
测试阶段使用自签名证书时,浏览器会显示证书警告,需要在测试设备上手动继续访问。正式环境应使用受信任的 HTTPS 证书。
进入会议后:
- 允许浏览器使用麦克风和摄像头。
- 确认 Windows 的输入设备电平会随着说话变化。
- 打开字幕面板。
- 打开翻译语言菜单。
- B 选择
English,C 选择العربية。 - A 说中文,观察不同参会者是否看到不同语言字幕。
十三、验收清单
基础会议
- 本机可以打开 Jitsi 页面。
- 同一局域网内的电脑和手机可以加入。
- 摄像头、麦克风和扬声器正常。
- UDP
10000可用于媒体传输。 - 两人通话 10 分钟没有明显断流。
实时字幕翻译
- 中文源语言设置为
zh时,中文短句不会频繁被识别成其他语言。 - B 选择英文后能看到英文字幕。
- C 选择阿拉伯语后能看到阿拉伯语字幕。
- 不同参会者的目标语言互不影响。
- 字幕能够显示正确的说话人名称。
- 翻译服务停止时,普通 Jitsi 音视频会议仍可以继续。
- Docker Desktop 重启后,容器可以恢复运行。
日志和隐私
- 服务日志中不出现识别原文和译文。
- Docker 日志轮转已经启用。
- 会议音频没有发送到 OpenAI 或其他云端 API。
- 模型、配置和日志目录的访问权限已限制。
十四、常见问题
1. 页面提示 ERR_CONNECTION_REFUSED
通常检查:
powershell
docker compose -f docker-compose.yml -f translation.yml ps
docker ps
确认 web 容器正在运行,并确认访问端口是 8443。如果 Windows 防火墙阻止了 TCP 8443,局域网设备也会无法访问。
2. 能听到对方声音,但对方听不到我
先不要从 Jitsi 服务端排查,优先检查客户端:
- Windows 设置中的输入设备是否选择正确。
- 麦克风音量条是否随着说话变化。
- 浏览器网站权限是否允许麦克风。
- Windows 隐私设置中的"允许桌面应用访问麦克风"是否打开。
- 微信、Teams 等其他软件是否独占了麦克风。
- Jitsi 底部麦克风按钮是否处于静音状态。
如果其他软件也采集不到声音,问题通常在 Windows 设备、驱动或麦克风本身,而不是 Jitsi。
3. 字幕延迟很高
CPU 模式下,延迟通常来自三个地方:
text
语音分段等待 + Whisper 识别 + NLLB 翻译
可以依次尝试:
- 固定源语言,例如
LOCAL_SOURCE_LANGUAGE=zh。 - 缩短
MAX_UTTERANCE_SEC,但过短会让句子被切碎。 - 使用更快的 Whisper 模型,但要接受识别准确率可能下降。
- 使用 CTranslate2/ONNX INT8 翻译。
- 使用 GPU 或单独的 Linux 推理服务器。
4. 字幕不准确
- 先确认源语言设置正确。
- 让讲话者靠近麦克风,减少回声和背景噪声。
- 避免多人同时讲话。
- 给专有名词、产品名和部门名增加领域词汇校正。
- 对正式业务不要只依赖自动字幕,重要内容需要人工确认。
Whisper 官方模型卡也提醒,不同语言和不同语音条件下的识别质量并不均匀,部署前应使用自己的真实音频进行评估。
参考:Whisper 模型卡
十五、内网使用和外部使用的区别
公司内部使用
建议至少完成:
- 使用企业 DNS,不长期依赖变化的局域网 IP。
- 使用企业内部 CA 或受信任的 HTTPS 证书。
- 将 Docker Desktop 测试机逐步迁移到独立 Linux 主机。
- 配置会议密码、房间权限和主持人权限。
- 限制管理文件、模型目录和 Docker Socket 的访问。
- 对字幕和会议数据制定保留、清理和访问规则。
- 评估 Whisper/NLLB 模型的许可证是否适合公司业务。
- 对 CPU、内存、网络带宽和并发人数做压力测试。
对外部用户开放
不建议直接把当前局域网方案映射到公网。至少需要新增:
- 公网域名。
- 受信任的 TLS 证书。
- 公网反向代理或正确配置的 80/443 端口。
- JVB 的 UDP
10000公网媒体通道。 - 必要时配置 TURN 服务,解决复杂 NAT 和企业网络限制。
- 身份认证、房间权限、等候室或主持人控制。
- 防暴力尝试、限流、监控、告警和备份。
- 独立的翻译推理节点或 GPU 节点。
- 对外部音频、字幕、日志和模型数据进行合规评估。
外部访问时,翻译服务仍应只放在服务器内部网络,不应把 38030 直接开放给互联网。
十六、后续可以继续优化的方向
性能
- 使用 CTranslate2 或 ONNX Runtime 的 INT8 模型。
- 使用 GPU 推理,或者将翻译服务拆到独立 Linux 主机。
- 进一步实现按会议共享的识别队列,减少多个 JVB 连接之间的重复识别。
- 统计分段等待、ASR、翻译和网络发送的耗时。
- 根据参会人数动态限制目标语言数量。
准确率
- 使用更大的 Whisper 模型。
- 为公司名称、产品名称、技术术语增加词汇表。
- 使用领域翻译模型或经过评估的商用模型。
- 增加语言置信度和低置信度提示。
- 改善回声消除、噪声抑制和说话人分离。
产品能力
- 在会议页面加入源语言选择,而不是只通过
.env设置。 - 提供字幕开关、字号、颜色和历史清理控制。
- 增加字幕导出前的权限审批。
- 增加翻译服务健康状态和延迟监控。
- 在合规允许的前提下增加会议纪要和检索功能。
- 如果需要"听到翻译后的声音",再增加 TTS 语音翻译链路;这会明显增加延迟和资源消耗。
十七、项目文件结构
最终实现中与翻译相关的文件大致如下:
text
docker-jitsi-meet/
├─ .env
├─ docker-compose.yml
├─ translation.yml
├─ config/
│ └─ jicofo/
│ └─ custom-jicofo.conf
├─ local-translation/
│ ├─ Dockerfile
│ ├─ README.md
│ ├─ server.py
│ └─ models/
├─ web-custom/
│ └─ Dockerfile
├─ start-jitsi.ps1
└─ stop-jitsi.ps1
其中:
server.py:Opus 解码、VAD、Whisper、NLLB、WebSocket 协议和字幕结果。translation.yml:本地翻译容器、内部网络、模型目录和日志轮转。custom-jicofo.conf:将 Jicofo 的转写连接指向本地服务。web-custom/Dockerfile:目标语言菜单、字幕筛选和说话人名称显示补丁。.env:局域网 IP、端口、翻译开关、语言和密码配置。start-jitsi.ps1/stop-jitsi.ps1:Windows 下的一键启动和停止。
结语
这套方案证明了:在 Windows 11 上不需要原生安装 Jitsi,也可以通过 WSL2 + Docker Desktop 搭建一个可用于内网测试的 Jitsi Meet,并接入本地 Whisper/NLLB 实现实时多语言字幕。
它的核心价值是把会议、识别和翻译链路拆开:Jitsi 负责稳定的实时音视频,翻译适配器负责本地语音识别和文本翻译,参会者在前端独立选择目标语言。
但在正式投入公司或公网使用前,仍然需要重点完成三件事:
- 对模型许可证和数据合规进行确认。
- 用真实业务音频评估延迟和准确率。
- 将 Windows 测试机迁移到更适合长期运行的 Linux 或云服务器,并补齐认证、证书、TURN、监控和备份。