连接成功却听不到歌声:实时音频兜底实践

实时通信界面显示“已连接”,只证明信令或传输协商走完了;真正的声音还要经过数据到达、解码、浏览器播放和用户设备输出。

“在线”与“有声”之间隔着多层状态

CVoice Stage 中,歌手麦克风优先以 Opus 通过 WebRTC 发布,房间服务只向同房间听众转发歌声;视频由每个浏览器自行加载,不经过歌房服务器。即使连接协商完成,公网媒体端口、入站数据、解码器、音频上下文和扬声器权限仍可能在任一层失败。

早期状态只显示“OPUS”,容易把协商成功误认为播放成功。后来将它拆成“OPUS 发布”“RTC 检测中”“OPUS 有声”和“PCM 备用”,让状态描述对应可以观察到的事实。

怎样证明听众真的收到了声音

只看到入站数据包还不够:包可能没有被正常解码,也可能解码后全是静音。听众端会周期性读取 WebRTC 入站统计,并进一步观察实际解码波形。只有最近确实出现有效声音时,才停止播放并行的 PCM 备用通道。

这项检查还必须理解正常停顿。歌手换气时波形短暂安静,不应该立刻判断断流;只要入站数据持续且该轨已经验证过有声,系统会保留主通道状态。无包、持续解码静音或轨道中断时,备用通道继续承担声音。

移动浏览器还有“用户手势”这一关

手机浏览器常常禁止网页在没有点击的情况下自动播放声音或恢复音频上下文。房间连接不应该等待音频授权,否则刷新后可能永远卡在重连阶段;但真正播放时,需要提供明确的“启用视频与歌声”按钮,在同一次用户操作中启动媒体与音频处理。

iOS 对媒体元素音量控制也存在特殊限制。Stage 将可控制的媒体音轨接入 Web Audio 增益节点,并在首次接入失败后允许用户手势触发重试,而不是让一次失败决定整个房间都无法混音。

兜底不能以明显不同步为代价

实时歌声与每个听众本地加载的视频来自不同链路,网络延迟和设备缓冲不可能完全相同。主通道切换到备用通道时,声音要继续跟随歌曲时间轴,视频不应跳帧。系统记录歌手设置的提前量和当前链路估计,为远端声音增加有限延迟,使两条路径尽量靠近同一时间参考。

判断信号它能证明什么不能证明什么
信令连接成功双方可以交换协商信息不能证明媒体包已到达
入站包持续增加网络正在传输媒体数据不能证明浏览器已解码出声音
解码波形出现有效信号主通道最近真实有声不能保证用户扬声器音量合适
用户确认听见完整链路在当前设备可用不能代表其他听众设备
这次实践的核心可靠系统不应把内部状态名称当成成功证据。每一层都要寻找与用户结果尽量接近的可观察信号。