音视频同步机制

        工作闲暇时间,对于之前做音视频项目的一些总结。在音视频项目过程中总会遇到一些问题,比如解码花屏,卡顿等现象,我们可以综合考虑配置一些参数。比如帧率,码率,分辨率等设置均衡。现在说一下重点,就是在项目过程中如何做到音频和视频的同步策略

一、不同步的现象

        由于视频解码和显示,非常消耗性能,所以视频帧无法和音频帧一样,保证每一帧都能严格准时播放。在现有技术和硬件条件下,任何库都无法百分百保证音视频同步,只能尽力保证误差最小

二、同步方案

       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即可

第五步:播放完毕

你可能感兴趣的:(音视频,android)