- TypeScript 74.5%
- Go 25.1%
- JavaScript 0.2%
- Shell 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| cmd/e2e | ||
| deploy | ||
| frontend | ||
| .gitignore | ||
| codec_compatibility.go | ||
| control_flow_test.go | ||
| database.go | ||
| device_scans.go | ||
| device_scans_test.go | ||
| go.mod | ||
| go.sum | ||
| HANDOFF.md | ||
| http.go | ||
| ice_diagnostics.go | ||
| logging.go | ||
| main.go | ||
| maintenance.go | ||
| media.go | ||
| media_actions.go | ||
| media_actions_test.go | ||
| meeting_policy.go | ||
| meeting_policy_test.go | ||
| permissions.go | ||
| permissions_test.go | ||
| README.md | ||
| reception.go | ||
| reception_test.go | ||
| rooms.go | ||
| server_diagnostics.go | ||
| server_diagnostics_test.go | ||
| server_test.go | ||
| start.sh | ||
| websocket.go | ||
Hola 私人会议
Hola 是面向个人使用的 Web 视频会议项目,支持多设备入会、会议权限管理、摄像头通话与屏幕共享。当前以 Web 为主,采用 Go、React/TypeScript 和 LiveKit,已在杭州服务器上线。
- 在线入口:meet.cloverdo.cn
- 源码仓库:Cloverdo/Hola,开发分支为
dev - 进展更新时间:2026-10-05
- 发布、备份、回滚及排障记录:HANDOFF.md
当前进展
核心 Web 功能和“先确认权限、再连接媒体”的链路已部署,H.264、AV1 的真实 SFU 收发已有回归验证。Windows–Android 的 H.265 摄像头互通仍需真机复测,不能宣布所有设备上的视频功能已完全稳定。
| 项目 | 状态 |
|---|---|
| 业务后端 | Go,运行版本 49c2f95;已替换旧 Python 服务 |
| Web 前端 | React/TypeScript,运行版本 00c7b85 |
| 媒体服务 | LiveKit 1.13.7,SFU 转发,服务器不转码 |
| 数据存储 | SQLite,持久保存账号、会话、会议、设备报告及待同步媒体操作 |
| 部署 | Linux 宿主机进程,hola.service 与 start.sh 管理,暂不使用 Docker |
| 最近完成 | 严格编码协商、黑屏提示、共享统计、两人隐藏加号 |
| APP | 暂未开发,当前继续集中处理 Web |
项目按个人使用、目标不超过 20 人设计。已有 20 设备信令测试,但不等于完成 20 路高画质视频容量验收。
已完成功能
账号、会议与历史
- 登录与管理员账号管理,公开注册关闭。
- 随机 6 位房间码,支持公开会议与带密码的私密会议。
- 每个账号可设置唯一的 8 位个人码,作为固定私密入口;点击数字即可复制。
- 主持人可将公开会议转为私密会议、设置或重设密码;在线成员保持连接,新成员按新规则验证。
- 主持人离开后会议继续;最后一个设备离开 30 分钟后自动结束。
- 历史记录展示时间、时长及累计参会人数,支持分页;切换页面时会议可保持连接并缩为悬浮窗。
大厅与观看界面
- 大厅包含快速加入、会议与在线设备统计、正在进行的会议、设备预设和最近会议。
- 创建、加入前显示确认弹窗,可主动预览本机摄像头;取消或入会前释放预览采集。
- 正在进行的会议每 5 秒静默刷新,手动刷新才显示按钮忙碌状态;历史页每次加载 30 条。
- PC 支持网格与主视角;手机采用主画面和可横向滚动的预览列表,当前选择高亮。
- 竖屏视频完整显示在横向预览框内,全屏时适配屏幕。
- 网格仅在 1 台参会设备时显示灰色加号,主持人也计入人数,达到 2 台即隐藏;共享画面不增加人数。
- 主持人、联席可向当前成员发出一次主画面切换,成员随后仍可自行切走。
- 全屏支持旋转、视频信息、切换摄像头及自己的音视频、分辨率和目标帧率控制。
多设备与权限
同一账号可从多台设备加入,每场会议可选择自己的主设备。普通成员选择主设备不会增加权限;主持人、联席的管理与高画质资格随主设备转移。
| 能力 | 主持人主设备 | 联席主设备 | 普通成员 |
|---|---|---|---|
| 选择自己的主设备 | 支持 | 支持 | 支持 |
| 修改会议编码 | 支持 | 支持 | 不支持 |
| 查看完整设备能力、兼容汇总及执行反馈 | 支持 | 支持 | 不支持 |
| 禁用、恢复普通成员的视频 | 支持 | 支持 | 不支持 |
| 向全员切换一次主画面 | 支持 | 支持 | 不支持 |
| 选择 2K、4K 摄像头档位 | 按设备能力 | 按设备能力 | 不支持 |
| 踢人、管理联席、移交主持、管理会议隐私及结束会议 | 支持 | 不支持 | 不支持 |
平台管理员负责账号管理、运行状态查看与强制结束会议。管理员身份不会自动获得会议内的完整设备汇总或主持权限。后端规则集中于 permissions.go,前端依服务端授权展示操作;角色、主设备迁移或撤销后,详情权限同步变化。
设备检测与云端复用
- 先获取摄像头、麦克风信息,再对每个摄像头检查 24 个组合:3 种编解码 × 4 档分辨率 × 2 个目标帧率。
- 结合实际采集约束、浏览器能力和本机 WebRTC 编解码短测。格式预测失败继续实际测试;短测低帧率记录性能提示,不直接等同于不支持。
- 默认优先选择实测可用的 1080p60 H.265,缺少该组合时按可用组合选择,兼容格式优先 H.264。
- 首次检测前预设禁用,有有效报告后只提供实际通过的组合。
- 格式预测最多 8 路并发;实际采集、编解码逐路运行,避免检测任务相互争抢。同分辨率 60/30 帧复用回环连接。
- 报告按账号与浏览器标识在本机、云端隔离保存,有效 7 天,读取不延期。登录后优先复用本机报告,否则尝试云端恢复。
- 设备标识为本站保存的随机 UUID,表示浏览器实例,并非硬件序列号。换浏览器、无痕访问或清除完整站点数据可能需要重新检测。
- 复用前核对检测版本、时间、浏览器及设备信息;支持手动重新扫描。自动恢复不请求采集权限,不改变正在发送的视频方案。
- 完整扫描不是入会门槛,零可用组合也不直接禁止摄像头。 开启时仍按会议选择的编码实际尝试发布。
控制与媒体链路
权限和规则通过 Go 业务接口与 WebSocket 确认,视频由浏览器编码后经 LiveKit 转发。
flowchart LR
W[Web 客户端] <-->|登录、入会、能力、规则与反馈| G[Go 业务服务]
G <-->|持久化| D[(SQLite)]
G -->|发布源权限、踢出、结束会议| L[LiveKit SFU]
W <-->|规则确认后连接 WebRTC 音视频| L
- 浏览器登录并加入会议,建立业务 WebSocket,报告设备编解码能力。
- Go 根据角色、主设备、禁视频状态与参会设备数,下发带版本的媒体规则。
- 前端确认规则后申请 SFU 凭证,服务端在发放时再次核验权限。
- 浏览器连接媒体服务,按需开启采集;每台设备反馈规则应用中、成功或失败。
- 权限变化重新下发规则,忽略过期反馈;完整设备详情只发送给有权限的管理设备。
会议规则与待执行的 SFU 操作在同一 SQLite 事务中保存。媒体调用不持有会议状态锁,失败时明确显示待同步并每 5 秒重试,重启后继续处理。
音频播放独立于视频布局。可见视频和主画面保持订阅,屏幕外视频可暂停订阅并在重新可见时恢复,音频继续播放。摄像头与共享分别发布、展示和统计。
视频规则
| 项目 | 当前方案 |
|---|---|
| 摄像头格式 | AV1、H.265、H.264,已移除 VP9 |
| 分辨率 | 720p、1080p、2K(2560×1440)、4K(3840×2160) |
| 目标帧率 | 30、60 FPS,实际性能取决于采集、编码与网络 |
| 普通成员 | 5 台及以下最高 1080p60;超过 5 台限制为 720p30,人数回落后恢复原选择 |
| 主持人、联席主设备 | 按设备能力最高 4K60,不随人数改变档位 |
| 屏幕共享 | 独立 H.264,请求 1920×1080、30 FPS,码率上限 5 Mbps |
| 转码与备用流 | 服务器不转码,摄像头只发布所选编码的单路流,无备用编码流 |
创建会议采用个人编码预设,加入已有会议服从会议方案。只有主持人、联席的主设备可以手动修改会议编码。设备不兼容、能力未知或档位未通过时提醒管理设备,不自动切编码。
发布前严格限制协商格式,运行中再次核对实际发送编码;与会议方案不一致时停止该路摄像头并报告失败,避免界面显示已开启却发送另一格式。
网络受限时只降低发送帧率上限,按 60→30→15 调整;采集目标、保存的 30/60 帧目标和分辨率保持不变,网络恢复后逐档尝试回到目标。单纯采集或编码低帧率不会直接作为网络拥塞处理。人数导致的普通成员画质限制属于会议规则,与网络调帧分开执行。
浏览器仍可能自行降低实际发送尺寸;应用检测到持续低于所选分辨率时停止视频并提示手动调整。SFU 凭证约束发布源权限,像素和帧率规则由官方网页执行,并非针对自制客户端的服务器像素硬限制。
诊断与待解决问题
现有诊断能力
- 全屏信息分别显示摄像头发送、共享发送及各路接收的实际编码、分辨率、FPS、间隔码率与延迟。共享每 3 秒采样,码率需要两次样本,换轨后重新测量。
- 收到数据却未能解码或解码停止时,窗口显示原因;管理设备收到实际接收故障反馈,恢复后清除。
- 自动采集浏览器、系统、可用设备信息、前后台状态、采集参数、编解码实现、帧计数、耗时、丢包、冻结与 ICE 路径。
- 服务端采集机器、相关进程负载和 TCP/UDP 诊断;匿名 STUN 包头辅助分析 UDP。
- 支持本机 Debug 导出与诊断录制;正常自动日志不要求开启录制,录制会额外消耗性能。
日志文件(/opt/Hola/logs/) |
内容 |
|---|---|
meeting-diagnostics.jsonl |
入会、摄像头收发、调帧与故障事件 |
browser-diagnostics.jsonl |
客户端采集、连接、协商与规则执行事件 |
server-diagnostics.jsonl |
服务端负载、进程与网络诊断 |
上述日志为 0600,每类保留当前文件及 3 份轮转文件,每份约 20 MB。应用诊断过滤密码、令牌和原始 SDP;浏览器提供的信息无法直接确定精确 CPU 型号、温度或持续硬件性能。
尚未关闭的问题
- 安卓 H.265 摄像头黑屏。 2026-10-05 真机日志出现“请求 H.265、实际发送 VP8”,及“接收 H.265 已有 2835 个包,解码帧数为 0”。严格协商约束、故障提示、帧组装与关键帧日志已上线,最终跨设备互通仍需双方刷新后复测。主持人可手动选择 H.264 对照,系统不自动换码。
- 1080p60/30 的持续性能。 当前重点是 H.265/H.264 常用档位。能力声明、短测和芯片型号不能保证长期通话帧率,4K60 AV1 等高档位也需要独立实测。
- 安卓 Chrome 网页发起屏幕共享。 项目依赖
getDisplayMedia(),该浏览器没有提供手机屏幕采集能力;观看他人共享可以继续使用,原生安卓采集方案尚未开发。 - 移动端后台、锁屏和设备占用。 已加入前台恢复与有限重试,所有浏览器后台持续采集仍无法保证。
- 多人媒体容量。 当前杭州 SFU 出口约 200 Mbps,转发流量随观看人数增加。20 路信令测试不代表 20 路高画质视频稳定运行,其他服务器尚未接入媒体链路。
当前服务器的 UDP 网卡映射问题已修复,H.264、AV1 公网 UDP 收发已验证;移动网络仍可能走 TCP 后备。nps 暂未调整,其负载影响未经过干预对照,不能直接认定为低帧率根因。
已完成验证
| 验证项 | 已记录结果 |
|---|---|
| 最新前端单元测试 | 68 项通过 |
| TypeScript、UI 漂移、生产构建 | 通过 |
| 最新隔离真实 SFU 回归 | 4 项通过,约 1.1 分钟 |
| 最新正式 HTTPS 回归 | 4 项通过,约 2.2 分钟 |
| 后端检查 | 当前 Go 代码已通过 test、vet、race;最近前端修复未修改 Go |
最新回归覆盖两人隐藏加号、H.264 双向解码、手动 AV1 换码与重开摄像头、解码故障提示与恢复、编码不一致时停止、视频退订与恢复、音频持续、摄像头和共享并行及主持禁视频。
正常媒体经过真实浏览器编码、SFU 转发与远端解码;故障场景注入统计,共享选择器使用测试画布和音轨。测试 Chrome 未提供 H.265 编解码,这些结果不能替代安卓 H.265 真机验收,也不能证明物理摄像头持续达到 60 FPS。历次失败、复测及跳过项保留在 HANDOFF.md。
代码结构与开发
Hola/
├── main.go、http.go、websocket.go # 服务入口、HTTP 与业务信令
├── rooms.go、permissions.go # 会议状态与集中权限
├── meeting_policy.go、media_actions.go # 版本化规则与持久媒体操作
├── media.go # LiveKit 凭证、代理和管理接口
├── database.go、device_scans.go # SQLite 与设备报告
├── logging.go、*diagnostics.go # 应用与服务器诊断
├── frontend/src/ # React 页面及会议功能
├── frontend/src/test/ # Vitest 单元测试
├── frontend/tests/ # Playwright 场景
├── cmd/e2e/ # 独立数据库与进程的测试工具
├── deploy/ # systemd 与 RTC 示例
└── HANDOFF.md # 发布与排障交接
后端使用 Go 1.26(以 go.mod 为准),前端使用 Node.js/npm,已使用 Node.js 24 构建。LiveKit 与浏览器媒体访问环境需另行准备。
在仓库根目录检查、构建后端:
go test ./...
go vet ./...
go build -o bin/hola .
在 frontend/ 安装依赖、检查和构建:
npm ci
npm run check
npm test
npm run build
完成后端、前端构建,并提供可访问的 LiveKit 和测试凭据后,在 frontend/ 执行 npm run test:e2e。该工具创建独立数据库与 Go 进程,不使用生产数据库。
| 环境变量 | 用途 |
|---|---|
HOLA_ROOT |
数据与前端目录,默认 /opt/Hola |
HOLA_ADDR |
Go 监听地址,默认 127.0.0.1:9009 |
HOLA_PUBLIC_URL |
对外 HTTPS 地址,用于来源校验 |
LIVEKIT_URL |
LiveKit 管理接口,默认 http://127.0.0.1:7880 |
LIVEKIT_KEY、LIVEKIT_SECRET |
LiveKit 凭据,与媒体服务配置一致 |
HOLA_ADMIN_USER、HOLA_ADMIN_PASSWORD |
空数据库首次初始化管理员,密码至少 8 位 |
HOLA_ALLOW_HTTP=1 |
本机 HTTP 测试模式,正式入口使用 HTTPS |
部署与维护
正式目录 /opt/Hola,Go 监听 127.0.0.1:9009,现有 HTTPS 反向代理提供公网入口。LiveKit 信令经 Go 代理,媒体使用 UDP 50000,TCP 50010 为后备。
start.sh 加载私密 .env,启动 Go 与 LiveKit,Go 退出后可单独重启;deploy/hola.service 管理整套进程。RTC 示例 对应当前杭州服务器,迁移时需核对实际公网地址和网卡。
systemctl status hola
curl http://127.0.0.1:9009/health
/health 返回 Go 版本,不表示前端版本;运行清单分别为 runtime/release.json 与 runtime/frontend-release.json。前端采用原子目录交换并保留旧哈希资源,已有页面需刷新。Go 发布保留持久会话和会议状态,但可能短暂中断连接;整套服务重启影响媒体,发布前检查活动会议并备份。
最近前端备份为 /opt/Hola/backups/camera-codec-20261005-122221,发布与公网临时测试清理后原业务记录逐表 SHA256 一致,Go/LiveKit 均未重启。完整备份和回滚说明见 HANDOFF.md。
.env、数据库、私密配置、凭据、日志、运行目录、依赖与构建产物不提交。每次完成代码修改均提交、推送 Forgejo dev。
后续重点
后续开发以用户确认的范围为准,当前优先事项:
- 用实际 Windows/安卓设备对照 H.265、H.264 的 1080p60/30,结合编码、帧组装、关键帧和解码日志关闭黑屏问题。
- 对采集、硬件编码、网络和服务端负载进行对照,验证降帧及回升效果。
- 根据使用反馈继续优化 PC、手机操作,逐步验证高分辨率与多人容量。
安卓 APK、多服务器客户端配置和 APP 专属能力仍在方案阶段。继续坚持 Web 优先,稳定后再经确认进入 APP 开发。