【VLC核心二】clock管理流程

转载地址:https://blog.csdn.net/yagerfgcs/article/details/51124352

一、前言

clock管理是vlc播放音视频的重要部分,从live555收流到decoder解码到render渲染,整个播放过程中均需依赖clock机制。

二、涉及的类文件

src\input\input.c

modules\access\live555.cpp

src\input\es_out.c

src\input\decoder.c

src\input\clock.c

三、clock核心点备注

1、live555::CmdExecuteControl(ES_OUT_SET_PCR,p_sys->i_pcr+1)

注:p_sys->i_pcr+1作为input_clock_Update的i_ck_stream参数传入。p_sys->i_pcr在live555的StreamRead函数中赋值

2、clock中i_cr_average值的来源

clock中i_cr_average=配置文件中读取默认40ms * i_pts_delay / DEFAULT_PTS_DELAY;
DEFAULT_PTS_DELAY = 3*CLOCK_FREQ/10

3、当准备收流或回放拖动时,会触发设置PCR动作,对应ES_OUT_SET_PCR,继而调用clock::input_clock_Update(i_pcr, mdate()),更新clock机制,其核心处理如下

a、判断当前流时戳与上一帧时戳差值是否大于MAX_GAP,如果大于MAX_GAP,说明收到的帧已经跳变过大,则重置clock值ResetClock。(MAX_GAP宏的值为60s,可以调整宏代码,此值过大,应该调整为1s以内,否则在回放拖动时有bug,后续文章详解);

b、每隔20ms调用AvgUpdate计算一次 stream clock 和system clock间的漂移;

[cpp] view plain copy

  1. typedef struct  
  2. {  
  3.     mtime_t i_value;  
  4.     int     i_residue; //残余  
  5.   
  6.     int     i_count;      
  7.     int     i_divider; // 分割;分配  
  8. } average_t;  
  9.   
  10. AvgUpdate详解:  
  11. //将当前mdate时间转换为流时戳  
  12. const mtime_t i_converted = ClockSystemToStream( cl, i_ck_system );  
  13. //用转换的流时戳-真实流时戳作为ivalue  
  14. AvgUpdate( &cl->drift, i_converted - i_ck_stream );  
  15.   
  16. static void AvgUpdate( average_t *p_avg, mtime_t i_value )  
  17. {  
  18.     const int i_f0 = __MIN( p_avg->i_divider - 1, p_avg->i_count );//i_f0记录当前已存值的个数  
  19.     const int i_f1 = p_avg->i_divider - i_f0;//剩余个数  
  20.     //统计总值=已存个数*平均value + 剩余个数*新来i_value + 余数  
  21.     // 好处是:未存满值的时候,剩余个数均使用新的i_value填充  
  22.     const mtime_t i_tmp = i_f0 * p_avg->i_value + i_f1 * i_value + p_avg->i_residue;  
  23.     // 计算新平均值和余数  
  24.     p_avg->i_value   = i_tmp / p_avg->i_divider;  
  25.     p_avg->i_residue = i_tmp % p_avg->i_divider;  
  26.   
  27.     p_avg->i_count++;  
  28. }  

c、计算当前帧是否来晚了

[cpp] view plain copy

  1. 当前系统时戳即mdate的当前时间- (当前系统时戳将流时戳+平均偏移,转换为系统时戳)。如果大于零,说明改帧比期望它到的时间来晚了。来晚了就更新到数组里。VLC缓存了三个late的值,为后续GetJitter获取的时候可以计算平均的late值。  
  2.   
  3. 摘录:如果late,存储到数组中  
  4. if( i_late > 0 )  
  5.     {  
  6.         cl->late.pi_value[cl->late.i_index] = i_late;  
  7.         cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;  
  8.     }  
  9.   
  10. 摘录:input_clock_GetJitter  
  11. 寻找中间的late值,这种方法可以规避掉bad values  
  12. const mtime_t *p = cl->late.pi_value;  
  13.     mtime_t i_late_median = p[0] + p[1] + p[2] - __MIN(__MIN(p[0],p[1]),p[2]) - __MAX(__MAX(p[0],p[1]),p[2]);  
  14.     mtime_t i_pts_delay = cl->i_pts_delay ;  


d、如果晚了,在es_out.c中,调用clock::input_clock_GetJitter统计抖动,并调用clock:: input_clock_Reset和input_clock_SetJitter重置clock,重新调节计算漂移的参数。这样就可以重新缓存待解码的数据。

4、缓存数据的核心流程

a、每次es_out.c中SET_PCR中调用EsOutDecodersStopBuffering 如果是缓冲状态, 判断是否缓冲完?

[cpp] view plain copy

  1. 1)计算流缓存时间:调用input_clock_GetState检查i_stream_duration值得到流缓存的时间  
  2. 2)计算预设缓存时间:  
  3. const mtime_t i_buffering_duration = p_sys->i_pts_delay + i_preroll_duration + p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;  
  4. 实测中i_buffering_duration  = p_sys->i_pts_delay = 1000;其它值为0,待研究什么情况下其它值不为零。  
  5. 3)如果i_stream_duration<i_buffering_duration,则继续缓冲,否则已缓冲满。  

b、input_DecoderWaitBuffering缓冲完通知decoder模块。解码模块队列中循环解码,可参考文章《【VLC核心一】播放流程梳理

5、clock在live555收流拼帧部分相关工作

a、拼帧完成后送SteamRead,SteamRead中的处理

[cpp] view plain copy

  1. int64_t i_pts = (int64_t)pts.tv_sec * INT64_C(1000000) +(int64_t)pts.tv_usec;  
  2. i_pts &= INT64_C(0x00ffffffffffffff);  
  3.   
  4. if( p_sys->i_pcr < i_pts )  
  5. {  
  6.     p_sys->i_pcr = i_pts;  
  7. }  

四、核心流程时序图

猜你喜欢

转载自blog.csdn.net/newarow/article/details/79819479