工作闲暇时间,对于之前做音视频项目的一些总结。在音视频项目过程中总会遇到一些问题,比如解码花屏,卡顿等现象,我们可以综合考虑配置一些参数。比如帧率,码率,分辨率等设置均衡。现在说一下重点,就是在项目过程中如何做到音频和视频的同步策略
一、不同步的现象
由于视频解码和显示,非常消耗性能,所以视频帧无法和音频帧一样,保证每一帧都能严格准时播放。在现有技术和硬件条件下,任何库都无法百分百保证音视频同步,只能尽力保证误差最小
二、同步方案
1、以音频时间为基准
因为音频的编码方式和视频的编码方式不一样,我们经常会以音频时间戳为标准,让视频画面对齐音频。还有一部分原因就是:因为人对视频数据快慢不是很敏感,但是音频数据快慢变化,明显会让人感觉声音变质。视频帧过快时,就延时播放,视频帧过慢时,就不等待帧间隔,加速播放,视频超级慢时,就丢帧。
- 如果视频帧的时间戳小于音频的时间戳,则进行跳帧操作
- 如果视频帧的时间戳大于音频的时间戳,则进行等待操作
2、以系统时间为基准
即利用pts和系统时间计算预计送显时间
(1)计算时间差 MediaCodecVideoRenderer#processOutputBuffer
//计算 “当前帧的pts(bufferPresentationTimeUs )” 与“Audio当前播放时间(positionUs )”之间的时间间隔,
//最后还减去了一个elapsedSinceStartOfLoopUs的值,代表的是程序运行到此处的耗时,
//减去这个值可以看做一种使计算值更精准的做法
long elapsedSinceStartOfLoopUs = (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs;
earlyUs = bufferPresentationTimeUs - positionUs - elapsedSinceStartOfLoopUs;
// Compute the buffer's desired release time in nanoseconds.
// 用当前系统时间加上前面计算出来的时间间隔,即为“预计送显时间”
long systemTimeNs = System.nanoTime();
long unadjustedFrameReleaseTimeNs = systemTimeNs + (earlyUs * 1000);
(2)丢帧和送显 MediaCodecVideoRenderer#processOutputBuffer
//计算 “当前帧的pts(bufferPresentationTimeUs )” 与“Audio当前播放时间(positionUs )”之间的时间间隔,
//最后还减去了一个elapsedSinceStartOfLoopUs的值,代表的是程序运行到此处的耗时,
//减去这个值可以看做一种使计算值更精准的做法
long elapsedSinceStartOfLoopUs = (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs;
earlyUs = bufferPresentationTimeUs - positionUs - elapsedSinceStartOfLoopUs;
// Compute the buffer's desired release time in nanoseconds.
// 用当前系统时间加上前面计算出来的时间间隔,即为“预计送显时间”
long systemTimeNs = System.nanoTime();
long unadjustedFrameReleaseTimeNs = systemTimeNs + (earlyUs * 1000);
注意 丢帧门限值固定为40ms
3、MediaSync的使用
MedaiSync是android M新加入的API,可以帮助应用视音频的同步播放
第一步:初始化MediaSync, 初始化mediaCodec和AudioTrack, 将AudioTrack和surface传给MeidaSync
第二步: MediaSync只会对audiobuffer做操作,一个是代表写入的queueAudio方法,一个是代表写完了的回调方法
第三步:设置播放速度
第四步:开始流转音视频buffer,这里就和MediaCodec的基本调用流程一样了,当拿到audioBuffer后,通过queueAudio将buffer给MediaSync,在对应的回调方法中release播放出去,至于video部分,直接releaseOutputBuffer即可
第五步:播放完毕