原因分析
卡顿的原因,是因为解码的fps比发送的fps低导致的,通常来说,主要是传输不稳定或者解码速度慢导致的。本章节主要针对传输不稳定导致接收帧率低引起的卡顿问题的优化策略。
逻辑设计
针对传输不稳定的问题,通常需要在接收端预先缓存一段时间的数据,来进行防抖。缓存的时间长度将直接影响抗抖动的能力,以及画面延迟,通常有以下关系:
缓存时间长,抗抖动能力强,但是延迟也更高。
缓存时间短,抗抖动能力弱,但是延迟低。
平衡抗抖能力和延迟时间,缓存市场一般取1-2s。
实作过程分解
1、发送帧率(SendFps)计算
APP端需要把收到的帧,从FRAMEINFO_t中取出timestamp(单位:毫秒),然后用当前的timestamp减去上一帧的timestamp,即可得到每一帧的发送间隔:SendIntervalMs=CurrentTimeStampMs-PrevTimeStampMs。
一种更优化的策略是,多次采样取平均值,比如取10次SendIntervalMs,来计算平均的SendIntervalMs,计算的时候,去除一些偏离较多的数值。比如:
SendIntervalMs: 40,40,80,40,40,40,40,41,40,39
AvgSendIntervalMs = SUM(40+40+40+40+40+40+41+40+39)/9。
在计算的过程中,应该将去掉80这个数值,这个数值引起的原因,可能是中途有一个帧丢失导致的,如果不去掉,将引起偏差。
预估发送帧率:SendFps=1000/SendIntervalMs。
建议在整个解码过程中一直实时计算SendFps,以免设备做码率自适应导致SendFps未更新出现卡顿。
2、开始缓存
根据上面的SendFps以及要缓存的时间TS,得到要缓存的帧数据量:CacheFrameCount=SendFps*TS。比如发送帧率25fps,缓存时间2s,CacheFrameCount = 50。
缓存的时机,分以下几种:
- 出图前,会导致出图慢。
- 出图后,导致出图后出现loading的奇怪现象。
- 中途如出现缓存帧数过低,应该重新进行缓存。
3、解码帧控
缓存帧数足够后,就可以进行解码了。每解完一帧,都需要加入sleep做帧控。
sleep时长,可以分为两种:
- 固定时长,一般取SendIntervalMs,这种情况可能会导致延迟累积,需要增加清缓存的操作。
- 可变时长,中值取SendIntervalMs,需要细分多个档位进行控制,档位越多,调控越精细,用户感知就会越弱。
4、可变时长的帧控播放
通常来说,可变时长需要遵循以下原则:
- 可变时长调控的时候,调整幅度尽量小,以免画面有较明显的快播和慢播。
- 可变时长调控的时候,CacheFrameCount越接近SendFps*TS,调整幅度应该要越迟钝和幅度越小,反之,则幅度和灵敏度要增加。