VLC的avsync方案是audio video同步到系统时钟,且audio sync的阈值比较敏感,很容易出现丢帧的情况。相对于audio master的同步方案,VLC的同步方案比较“理想”化,经常会出现无声问题。
下面记录下目前遇到过的无声问题及分析记录:
目录
一、seek后无声
1.片源av pts差异较大
2.pts跳变
二、起播无声
1.AudioTrack起播慢
一、seek后无声
1.片源av pts差异较大
首先先介绍下ffmpeg的demux方式,ffmpeg在解封装视频文件时,是按照文件位置从0开始顺序解封装并获取packet的,这也是一种比较省时省力的读取方式。然而对于一些视频文件,它们在封装的时候,临近位置的video packet和audio packet的pts可能会差很多,这就导致demux同一时间解出的video和audio的pts差异会比较大,无法同时送给解码器解码,这也是demux和decoder之间要添加缓存buffer的原因之一。回到问题,对于上面提到的这种问题片源,player seek之后,demux解出的第一帧video和audio的pts会差很多,而VLC的clock会用seek后第一帧的pts做参考时间基准。后续,VLC会根据该时间基准,将音视频帧的pts转换到系统时间上。具体换算关系如下:
/*****************************************************************************
* ClockStreamToSystem: converts a movie clock to system date
*****************************************************************************/
//i_stream :当前帧的原始pts
//cl->ref.i_stream:stream的参考时间基准,即收到第一帧的原始pts
//cl->ref.i_system:系统参考时间基准,即收到第一帧的系统时间
static mtime_t ClockStreamToSystem( input_clock_t *cl, mtime_t i_stream )
{
if( !cl->b_has_reference )
return VLC_TS_INVALID;
return ( i_stream - cl->ref.i_stream ) * cl->i_rate / INPUT_RATE_DEFAULT +
cl->ref.i_system;
}
以下面的日志为例,demux seek后解到的第一帧是video数据,因此以这一帧数据的pts做时间基准。后续demux解出了第一帧audio数据,然而这帧数据的pts要比video的大很多,导致换算成系统时间的pts也大很多,在Audio同步时(参考VLC Audio同步原理分析 ),触发了too early条件,往AudioTrack塞0导致无声。
//seek后demux的第一帧audio和video数据pts差异较大,因为先解出video数据,因此以video pts为时间基准
04-28 15:32:49.791 15181 15495 D VLC : [cebd5930/3c87] libvlc demux: 1st video dts 106873434, pts 106873434
04-28 15:32:49.795 15181 15495 D VLC : [cebd5930/3c87] libvlc demux: 1st audio dts 107456000, pts 107456000
//触发too early,往AudioTrack塞0导致无声
04-28 15:32:49.958 15181 15517 W VLC : [cf15d6b0/3c9d] libvlc audio output: playback way too early (-538238) than (-240000), cache 480585: playing silence
04-28 15:32:49.958 15181 15517 D VLC : [cf15d6b0/3c9d] libvlc audio output: inserting 25835 zeroes
2.pts跳变
pts跳变比较好理解,就是audio的pts因为各种原因发生了跳变,在audio同步时触发too early条件,导致无声。
//fifo demux后的pts发生了跳变
04-28 11:33:11.578 10825 11073 D VLC : [88486a30/2b41] libvlc decoder: FFmpeg vlc_fifo_DequeueUnlocked i_pts 80495001
04-28 11:33:11.579 10825 11073 D VLC : [88486a30/2b41] libvlc decoder: FFmpeg vlc_fifo_DequeueUnlocked i_pts 81796001
//avcodec送往audio软解的pts发生了跳变
04-28 11:12:08.052 6481 6656 W VLC : [8a8ee3b0/1a00] libvlc decoder: avcodec_send_packet i_pts 93441001
04-28 11:12:08.053 6481 6656 W VLC : [8a8ee3b0/1a00] libvlc decoder: avcodec_send_packet i_pts 94799001
//送往AudioTrack的pts发生了跳变
04-28 11:12:08.053 6481 6656 D VLC : [a5a8d430/1a00] libvlc audio output: aout_DecSynchronize drift 15430, cache 1783006 ,cur 57511259532, pts 57513027108
04-28 11:12:09.161 6481 6656 D VLC : [a5a8d430/1a00] libvlc audio output: aout_DecSynchronize drift -1306330, cache 693339 ,cur 57512368064, pts 57514367732
//触发too early条件,往AudioTrack塞0
04-28 11:33:12.554 10825 11073 W VLC : [a5a8d430/2b41] libvlc audio output: playback way too early (-1264388) than (-240000), cache 734153: playing silence
04-28 11:33:12.555 10825 11073 D VLC : [a5a8d430/2b41] libvlc audio output: inserting 27879 zeroes
二、起播无声
1.AudioTrack起播慢
在VLC Audio同步原理分析 中有介绍过,audio在做同步时需要考虑AudioTrack和RingBuffer的缓存值,如果在往AudioTrack喂数据的一段时间内AudioTrack都没有播放,就会导致缓存值过大,进而触发too late条件导致player强制flush缓存,出现无声问题。如果片源含有视频流,因为视频和音频起播速度可能存在差异,刚起播的阶段应该还会有轻微的音画不同步。(在audio master的方案上,video需要向audio对齐,如果audio没有播放,audio clock不会增长,video也就不会播放,因此不会存在这个问题)
//刚起播的200ms内,player往AudioTrack写入了大量数据
04-28 14:34:36.891 8427 9017 E VLC : [ce8cffb0/2339] libvlc audio output: AudioTrack_Play write bytes: 12288, frames: 3072
04-28 14:34:36.894 8427 9006 D VLC : [ce8cffb0/232e] libvlc audio output: i_audiotrack_us 3494, written 64000, circular 0, delay 60506
04-28 14:34:37.119 8427 9017 E VLC : [ce8cffb0/2339] libvlc audio output: AudioTrack_Play write bytes: 6136, frames: 1534
04-28 14:34:37.127 8427 9006 D VLC : [ce8cffb0/232e] libvlc audio output: i_audiotrack_us 117401, written 1114645, circular 0, delay 997244
//这段时间通过AudioTrack_getPlaybackHeadPosition获取的position一直为0
04-28 14:34:36.891 8427 9006 D VLC : [ce8cffb0/232e] libvlc audio output: i_audiotrack_us 0us in AudioTrack_GetSmoothPositionUs
04-28 14:34:37.127 8427 9006 D VLC : [ce8cffb0/232e] libvlc audio output: i_audiotrack_us 0us in AudioTrack_GetSmoothPositionUs
//大量数据积压在AudioTrack和RingBuffer中,导致cache不断变大,进而导致drift不断变大
04-28 14:34:37.127 8427 9006 D VLC : [ce8cffb0/232e] libvlc audio output: aout_DecSynchronize drift 180914, cache 997244 ,cur 2119317784, pts 2120134113
//player判断audio播放太慢,触发too late逻辑,flushing buffers导致无声
04-28 14:34:37.127 8427 9006 W VLC : [ce8cffb0/232e] libvlc audio output: playback way too late (180914) than (180000), cache 997244: flushing buffers