实时音视频生成中的同步与批处理延迟 ​

实时音视频生成中,单个模型运行得快,不代表用户能及时看到与声音对应的画面。一次适配中,短文本可以正常播放,文本变长后却逐渐出现卡顿。模型初始化和单次推理已经通过检查,问题仍然会出现在整段播放过程中。

这种现象需要同时看计算时间、数据产生的节奏和播放时间。模型可以一次生成一批画面,播放器却必须按媒体时间逐帧消费;音频可以很快合成完,画面也不能因此提前播放。平均 FPS 会把这些等待与积压藏在一起。

一、区分三种时间 ​

计算时间描述某一步执行了多久,适合用单调时钟测量。媒体时间描述音频采样点或视频帧属于哪一个时刻。播放时间则是这些媒体实际被消费的时刻。

例如,一批画面用 100 毫秒生成,覆盖了 640 毫秒媒体时间。它说明生产速度可以高于播放速度,却不能说明首帧只需等待 100 毫秒:前面可能还在等待语音、收集音频窗口、凑 batch,或排队执行。

音视频同步应建立在媒体时间上。设音频采样率为 fs,累计有效采样点为 n,视频帧率为 fv,帧编号为 k,两者对应:

ta=t0+nfs,tv=t0+kfv

这里假定两路从同一媒体起点 t0 开始。跳过、补齐或替换某一段时,都需要继续维护这个对应关系。某个视频帧因为计算较慢而晚生成,不应把它的生成完成时刻重新当成媒体时间,否则计算抖动会被写进时间轴。

PTS 与 time base 只是时间的不同表示方式。换算关系为 time = pts × time_base,而不同流、编码器和封装器可能使用不同 time base。RTP 音视频也可以使用不同的时钟频率,需要通过对应的时钟映射同步,不能直接比较两路 RTP 时间戳的整数大小。

已有媒体的裁剪、转码和时间偏移问题,见 视频转码、抽帧与时间定位的一致性;这里讨论的是媒体正在生成时怎样维持同一条时间轴。

二、先建立音频块与视频帧的对应关系 ​

以 16 kHz 单声道 PCM、20 毫秒音频块和 25 FPS 视频为例:一个音频块有 320 个采样点,一帧视频覆盖 40 毫秒,因此对应两个连续音频块。这是时间换算示例,不代表所有模型和传输链路都采用同样设置。

单元覆盖的媒体时长需要保留的信息
一个音频块20 毫秒起始采样点、有效样本数
一帧视频40 毫秒帧编号及对应音频区间
一批 16 帧画面640 毫秒16 帧各自的时间与音频对应

模型提取的音频特征还可能具有另一种时间分辨率。构造某一视频帧的特征窗口时,需要明确中心点怎样从媒体时间换算到特征索引,窗口是否读取未来音频,以及序列两端如何补齐。

如果窗口包含右侧上下文,在线生成就必须等待相应音频,或者采用明确的替代策略。离线推理能直接读取整段音频,容易掩盖这部分前视等待。窗口宽度相同,也不代表在线与离线具有相同的首帧延迟。

对于分别维护音频、特征和画面队列的实现,仅保证各队列先进先出还不够。超时、取消和补帧可能让某一路少一项;随后按数量继续取数据,会把后续所有项错配。更可靠的交接单元应带上时间区间、序号和所属生成任务,再在组合时核对。

静音也占用时间。旁路口型模型可以节约计算,但仍应推进相应的画面与音频时间;如果只跳过计算和输出,下一段非静音内容就可能接到错误位置。

三、batch 同时影响吞吐与等待 ​

3.1 批量计算时间不是凑批时间 ​

设 batch 为 B,一批的计算时间为 C(B),则计算吞吐近似为:

R(B)=BC(B)

若输入帧以 fv 的速度逐个到达,并且必须凑满 B 帧才提交,第一帧等待后续帧的时间约为 (B−1)/fv。在理想的均匀到达情况下,批内各帧的平均凑批等待约为:

Wbatch≈B−12fv

25 FPS、batch=16 时,一批覆盖 640 毫秒媒体时间,第一帧的凑批等待约为 600 毫秒,平均约 300 毫秒,之后才进入计算。这里的前提是输入按实时速度到达;如果一段语音已经完整合成并可立即提取特征,就不应再次套用同样的等待估计。

这也是为什么增大 batch 可能改善平均吞吐,却使第一段画面更晚出现。调参时需要同时记录首帧时间、稳态产出速度和队列余量,不能只选择 FPS 最高的配置。

3.2 按剩余媒体时长调度 ​

首批采用较小 batch,可以减少启动等待;进入稳定播放后,再根据缓冲余量选择更高效的批次。另一种方式是设置凑批截止时间,输入不足时也允许提交。

这种调度必须落到目标模型实际支持的 batch 上。只有固定档位时,可以选较小档位,或补齐输入后只消费有效输出。补齐的画面和音频不能继续推进真实媒体时间,也不能作为真实结果进入播放队列。

同时要避免“buffer 越多越好”的判断。少量预缓冲有助于吸收抖动,但积压越多,交互响应越慢,用户中断时需要丢弃的旧结果也越多。合适的 batch 是满足计算供给和延迟预算的选择,而不是一个始终最大的固定值。

四、长文本会放大持续供给不足与积压 ​

一次旧实现采用预分配输出缓冲、减少重复类型转换后,局部推理时间下降,长文本仍会卡顿。后续又调整了图、精度和集成代码,较长文本的播放表现才改善。这类组合变化能证明整条路径有所改善,但不能将全部收益归因到其中一个开关。

长文本为什么更容易暴露问题,可以从未播放媒体时长 Q 看。若生产速率为 r(t) 秒媒体/秒墙钟时间,播放连续进行,缓冲不为空时有:

dQdt=r(t)−1

当 r(t)<1 持续足够久,已有缓冲会被耗尽。短句可能在耗尽之前就结束,所以看起来正常;长句则逐渐暴露供给不足。当 r(t)>1 且没有容量约束时,缓冲又会持续增长,占用更多内存,并拉长待播放内容的年龄。

即使平均生产速率超过 1,也可能因单批耗时长尾而断流。需要同时看各阶段的时间分布、空队列等待和最老数据的等待时长,而不只看整段平均值。

一个 100 帧队列,在 25 FPS 下约有 4 秒媒体;一个 100 块的 20 毫秒音频队列,却只有 2 秒。用相同队列长度限制两路,不能得到相同的时间预算。建议把容量换成毫秒、采样点或字节,并同时记录最老媒体时间。

有界队列是控制积压的一种机制,但需要让等待传回生产者。只限制视频结果队列,而让合成音频和特征队列无限增长,仍会把内存压力转移到上游。反过来,生产者阻塞在满队列、消费者又已退出,也会使停止流程卡住;写入等待需要能够超时或响应取消。

五、网络分块不等于连续生成与播放 ​

音频接口持续返回网络块,只能说明传输分块。若调用方先把所有块收齐,再解码整段音频并切成 PCM,首音时间仍包含整段合成和接收时间。留存的一个语音实现就采用了这种处理方式:它解决了音频格式识别问题,但不能因此视为已经实现低首音延迟的连续解码。

同样,网络块不必恰好对应一个可独立解码的音频文件,也不必对齐 PCM 采样边界。压缩音频需要正确的解码状态,原始 PCM 则需要明确采样率、通道数、数值类型与字节序。随意把每个块当作完整 WAV 或 MP3,可能失败,也可能漏掉边界数据。

重采样也需要考虑跨块状态。将许多短块分别当作互不相关的音频重采样,可能在边界引入不连续。分块后应保留未消费数据、滤波状态和剩余采样点;在最终播放前检查累计时长与样本数是否一致。

尾段不足一个音频块时,应明确是按真实样本数输出、在协议允许的情况下补零,还是由播放器处理。直接舍弃会损失尾音;补零后若把填充长度也计入原始语音,会改变后续时间映射。对齐不能只检查中间稳定部分,还要覆盖第一块和最后一块。

流式语音内部的缓存、分句和播放完成状态,见 端侧多模态会话与流式语音的状态管理。

六、跨阶段优化要沿着实际数据流计时 ​

模型之间使用不同的推理框架时,接口兼容不代表数据始终留在同一设备。Tensor 转 NumPy、转回 Tensor,可能隐含同步、设备到主机拷贝、重新分配和主机到设备拷贝。Python 包装层保持同一个方法名,并不能消除这些操作。

因此应沿实际交接点计时,把等待、前处理、传输、模型执行和后处理分别记录。设备执行是异步的情况下,单纯测调用返回时间可能漏掉执行耗时;需要使用后端支持的事件或在对照测试中明确同步位置。同步本身也会改变并行度,局部测量与整条链路测试应分开进行。

主机缓冲复用可以减少分配和复制开销,却不等于零拷贝。声明使用 IO Binding 也不能替代对内存位置和设备绑定方式的检查。对于类型或数据范围不正确的输出,应先完成数值对齐,再讨论移除转换,参见 ONNX 模型转换中的中间特征与数值对齐。

更改多个编译参数后,应保留同输入、同 batch、同精度和同播放配置的对照。如果单模型测试几乎不变、整体播放却变顺畅,仍要继续检查排队、类型转换和缓冲行为,不能仅凭主观效果认定某个算子已经加速。

七、验证首帧、持续播放和停止 ​

场景重点观察
短句首次请求合成等待、特征前视、首批凑批、首次播放
长句持续播放生产与消费速率、空队列、最老数据年龄及内存增长
静音与非静音切换时间是否持续推进,两路是否使用同一区间
尾段与短音频最后样本是否丢失,填充是否进入了真实时间
中断后立即新建请求旧任务、旧队列与迟到结果是否仍能进入播放

除首音和首帧延迟外,还应统计首个有效内容之后的连续性。输出流稳定标记为 25 FPS,可能包含重复画面,不能单独证明每帧都是及时生成的新结果。音视频偏差则要在同一媒体时刻上比较,并区分固定偏移、持续漂移和偶发抖动。

固定偏移可以检查起点和预缓冲差异;持续漂移要查实际样本数、帧推进和时钟换算;偶发抖动则继续看队列等待和耗时长尾。三者需要不同的处理方法,统一增加 sleep 很容易把一种问题遮住,同时制造另一种延迟。

停止时还要区分“模型不再接收新任务”和“播放器不再消费旧结果”。为生成任务维护标识,先让旧结果失效,再清理队列和退出执行;新任务的时间轴与旧任务的迟到输出应分开。仅清空一次队列,而允许尚未完成的推理继续向里写入,仍可能出现旧画面或旧声音。

一条可靠的实时链路,需要同时做到按时生产、按媒体时间配对,以及受控地缓冲和结束。模型吞吐决定能供给多少内容,时钟和队列决定这些内容最终以什么节奏到达用户。

八、参考资料 ​