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

简介: 一、前言 VLC播放音视频的核心流程梳理,从live555收流到ffmpeg解码的整套流程 涉及到MultiFramedRTPSource、RTPSource、FramedSource、live555、es_out、decoder、video、clock、video_output、araw、mtime、dec、input、output、filters、directx等核心类。

一、前言

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为40ms
4、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;最终将期望时间戳送渲染;

三、核心流程时序图


目录
相关文章
|
22天前
|
算法 数据处理 开发者
FFmpeg库的使用与深度解析:解码音频流流程
FFmpeg库的使用与深度解析:解码音频流流程
34 0
|
28天前
|
Linux iOS开发 开发者
探索FFmpeg:实现自定义播放速度的全方位指南(一)
探索FFmpeg:实现自定义播放速度的全方位指南
87 0
|
24天前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(三)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
26 0
|
24天前
|
存储 编解码 数据处理
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码(二)
【FFmpeg 视频基本格式】深入理解FFmpeg:从YUV到PCM,解码到编码
33 0
|
24天前
|
存储 缓存 编解码
【FFmpeg 视频播放】深入理解多媒体播放:同步策略、缓冲技术与性能优化(一)
【FFmpeg 视频播放】深入理解多媒体播放:同步策略、缓冲技术与性能优化
47 0
|
28天前
|
存储 编解码 调度
剖析ffmpeg视频解码播放:时间戳的处理
剖析ffmpeg视频解码播放:时间戳的处理
42 0
|
27天前
|
编解码 算法 vr&ar
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(二)
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换
24 1
|
27天前
|
存储 编解码 算法
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换(一)
深度剖析FFmpeg视频解码后的帧处理到Qt显示 从AVFrame到QImage的转换
49 1
|
27天前
ffmpeg `AVCodecContext`的`frame_number`字段查看解码器是否正在产生输出帧
ffmpeg `AVCodecContext`的`frame_number`字段查看解码器是否正在产生输出帧
11 0
|
28天前
|
安全 数据处理 数据格式
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(三)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
32 0