AudioTrack#getCurrentPositionUs(boolean sourceEnded)
//因为 getPlayheadPositionUs() 的粒度只有约20ms, 如果直接拿来用的话精度不够
//要进行采样和平滑演算得到playback position
positionUs = systemClockUs + smoothedPlayheadOffsetUs
= systemClockUs + avg[playbackPositionUs(i) – systemClock(i)]
positionUs -= latencyUs ;
上式中i最大取10,因为getPlayheadPositionUs的精度不足以用来做音视频同步,所以这里通过计算每次getPlayheadPositionUs拿到的值与系统时钟的offset,并且取平均值,来解决精度不足的问题,平滑后的值即为smoothedPlayheadOffsetUs,再加上系统时钟即为“Audio当前播放的时间”。当然,最后要减去通过AudioTrack.getLatency方法获取到的底层delay值,才是最终的结果。
总体来说,音视频同步机制中的同步基准有两种选择:利用系统时间或audio playback position. 如果是video only的流,则利用系统时间,这方面比较简单,不再赘述
a. 如果是用audio position的话, 首先明确是通过下式来计算
startMediaTimeUs + positionUs
式中startMediaTimeUs为码流中拿到的初始audio pts值, positionUs是一个以0为起点的时间值,代表audio 播放了多长时间的数据
b.计算positionUs值则有两个方法, 根据设备支持情况来选择:
b.1.用AudioTimeStamp值来计算,需要注意的是,因为getTimeStamp方法不建议频繁调用,在ExoPlayer中是以500ms为间隔调用的,所以对应的逻辑可以化简为:
positionUs = framePosition/sampleRate + systemClock – nanoTime/1000
b.2. 用audioTrack.getPlaybackHeadPosition方法来计算, 但是因为这个值的粒度只有20ms, 可能存在一些抖动, 所以做了一些平滑处理, 对应的逻辑可以化简为:
positionUs = systemClockUs + smoothedPlayheadOffsetUs - latencyUs
= systemClockUs + avg[playbackPositionUs(i) - systemClock(i)] - latencyUs
= systemClockUs + avg[(audioTrack.getPlaybackHeadPosition/sampleRate)(i) -systemClock(i)] - latencyUs