【VLC核心一】播放流程梳理->live555收流+ffmpeg:AVCodec解码

来源:互联网 发布:c语言 void 编辑:程序博客网 时间:2024/06/05 20:04

一、前言

VLC播放音视频的核心流程梳理,从live555收流到ffmpeg解码的整套流程

涉及到MultiFramedRTPSource、RTPSource、FramedSource、live555、es_out、decoder、video、clock、video_output、araw、mtime、dec、input、output、filters、directx等核心类。

二、核心点备注
1、RTPSource中使用报文时间戳与当前时间计算抖动的核心代码

unsigned arrival = (timestampFrequency*timeNow.tv_sec);//音频8000,视频90000    arrival += (unsigned)((2.0*timestampFrequency*timeNow.tv_usec + 1000000.0)/2000000);            // note: rounding    int transit = arrival - rtpTimestamp;    if (fLastTransit == (~0)) fLastTransit = transit; // hack for first time    int d = transit - fLastTransit;    fLastTransit = transit;    if (d < 0) d = -d;    fJitter += (1.0/16.0) * ((double)d - fJitter);

2、RTPSource中使用报文时间戳差值计算出timeval 类型的presentationTime的核心代码

// Return the 'presentation time' that corresponds to "rtpTimestamp":  if (fSyncTime.tv_sec == 0 && fSyncTime.tv_usec == 0) {    // This is the first timestamp that we've seen, so use the current    // 'wall clock' time as the synchronization time. (This will be    // corrected later when we receive RTCP SRs.)    fSyncTimestamp = rtpTimestamp;    fSyncTime = timeNow;  }  int timestampDiff = rtpTimestamp - fSyncTimestamp;      // Note: This works even if the timestamp wraps around      // (as long as "int" is 32 bits)  // Divide this by the timestamp frequency to get real time://视频timeDiff =0.040000000000000001  double timeDiff = timestampDiff/(double)timestampFrequency;   // Add this to the 'sync time' to get our result:  unsigned const million = 1000000;  unsigned seconds, uSeconds;  if (timeDiff >= 0.0) {    seconds = fSyncTime.tv_sec + (unsigned)(timeDiff);    uSeconds = fSyncTime.tv_usec      + (unsigned)((timeDiff - (unsigned)timeDiff)*million);    if (uSeconds >= million) {      uSeconds -= million;      ++seconds;    }  } else {    timeDiff = -timeDiff;    seconds = fSyncTime.tv_sec - (unsigned)(timeDiff);    uSeconds = fSyncTime.tv_usec      - (unsigned)((timeDiff - (unsigned)timeDiff)*million);    if ((int)uSeconds < 0) {      uSeconds += million;      --seconds;    }  }

3、live555.cpp中将timeval转换为微秒级时间戳i_pts的要点

int64_t i_pts = (int64_t)pts.tv_sec * INT64_C(1000000) +        (int64_t)pts.tv_usec;/* XXX Beurk beurk beurk Avoid having negative value XXX */    i_pts &= INT64_C(0x00ffffffffffffff);<span style="color:#3333FF;">1)注意避免翻转为负数 2、block里i_pts+1,i_dts根据sdp中的packetization-mode=1字段判断</span>
4、处理H.264视频的要点

1)丢帧策略可以设置丢B帧、非参考帧、除关键帧以外所有帧等,可以在处理延时的情况下加速解码,尝试赶上发流速度,是明智之举;

2)如果存在过期的帧,且当前时间减第一个过期帧的时间>5S,则认为该帧过期时间太长,直接释放;如果统计的过期帧数>4且<12,则设置ffmpeg的解码器丢帧策略;

3)解码后得到pts,如果frame_rate和frame_rate_base>0,或者p_context->time_base.den > 0计算获得下一帧的pts,更新p_sys->i_pts,用于下一帧解码时校验数据

4)每次ffmpeg解码后,使用clock机制将pts流时间戳转换为系统时间,然后检查转换后的时间是否过期,过期算法为:判断显示时间<=mdate() 说明已过期,累加过期帧数;备注:*pi_ts0 >= mdate() + i_ts_delay + i_ts_buffering + i_ts_bound(  i_ts_bound默认值为9s)

5)最后在送渲染队列前使用clock::input_clock_ConvertTS将时戳拉伸到播放rate对应区间。

5、处理音频的要点

1)计算比特率和样本数 unsigned samples = (8 * p_block->i_buffer) / framebits;

2)通过采样数计算时间增量 mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples )

3)a、发现正确的播放频率  b、重新制作音频帧。核心流程如下:

1、仅正常播放速率支持音频播放,否则丢弃2、if ( start_date != VLC_TS_INVALID && start_date < now )期望时间戳帧已严重过期,重刷音频缓存数据,停止重采样(主要发生在用户暂停或解码器错误)3、if ( p_buffer->i_pts < now + AOUT_MIN_PREPARE_TIME )帧已过期,直接丢弃;AOUT_MIN_PREPARE_TIME为40ms4、mtime_t drift = start_date - p_buffer->i_pts;偏移量drift = 期望播放时间戳-当前帧的实际时间戳,drift<0说明帧滞后来了,drift>0说明帧提前来了;5、if( drift < -i_input_rate * 3 * AOUT_MAX_PTS_ADVANCE / INPUT_RATE_DEFAULT )如果偏移量<-3*40ms,则停止缓冲,停止重采样。预设值。6、if( drift > +i_input_rate * 3 * AOUT_MAX_PTS_DELAY / INPUT_RATE_DEFAULT )如果偏移量>3*60ms,缓冲太晚,直接丢弃7、if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) && ( drift < -AOUT_MAX_PTS_ADVANCE || drift > +AOUT_MAX_PTS_DELAY ) && p_input->i_nb_resamplers > 0 )drift漂移量<-40ms或>60ms,则重新发现播放频率.即滞后40ms或提前60ms(主要原因是:1、输入时钟偏移有误2、用户短暂暂停 3、输出端有点延时,丢失了一点同步--费解,4、现网有部分摄像头的采样率和码流里的时间戳不匹配,也会导致该问题)8、根据偏移结果微调采样频率9、p_buffer->i_pts = start_date;最终将期望时间戳送渲染;

三、核心流程时序图


3 0