DTS和PTS的分析

原由:

  • 近来在研究HLS(HTTP Live Streaming),以实现android上播放m3u8文件。由于TS段的切分不统一,每个视频网站给出的m3u8 playlists总有差别,在时间戳显示上有差异,所以对DTS和PTS进行了研究。
  • DTS和PTS是音视频同步的关键技术,同时也是丢帧策略密切相关。

dts/pts定义 dts: decoding time stamp pts: present time stamp 在ISO/IEC13818-1中制定90k Hz 的时钟,如果编码帧频是30,那么时间戳间隔就该是90000 / 30 = 3000。 在FFMPEG中有三种时间单位:秒、微秒和dts/pts。从dts/pts转化为微秒公式:

dts* AV_TIME_BASE/ denominator

其中AV_TIME_BASE为1,000,000,denominator为90,000。 拿到m3u8播放列表后,首先进行解析。HTTP Live Streaming标准草案可以从这里http://tools.ietf.org/html/draft-pantos-http-live-streaming-08查看。 解析代码在ffmpeg/libavformat/hls.c中

解析播放列表的问题:

  • 当解析到#EXT-X-TARGETDURATION标签时,后面紧跟着的是TS段的最大时长,当前没有什么用。#EXTINF标签后紧跟的是当前TS段的时长,当EXT-X-VERSION标签大于等于3时,TS段的时长可以为小数,当前(2012-07-26)的FFMPEG代码还不支持EXT-X-VERSION标签的判断,TS段的时长也为整数。seg->duration保存了当前段的时长,单位为秒。
  • 当前草案中还有EXT-X-DISCONTINUITY标签,它表征其后面的视频段文件和之前的不连续,这意味着文件格式、时间戳顺序、编码参数等的变化。但是很遗憾,当前FFMPEG仍然不支持,这意味着该标签出现后,后续的PES中携带的dts和pts将重新从零开始计数。
HLS上下文结构体:
?
01
02
03
04
05
06
07
08
09
10
11
typedef  struct  HLSContext {
     int  n_variants;
     struct  variant **variants;
     int  cur_seq_no;
     int  end_of_segment;
     int  first_packet;
     int64_t first_timestamp;
     int64_t seek_timestamp;
     int  seek_flags;
     AVIOInterruptCB *interrupt_callback;
} HLSContext;

HLS上下文中存在当前的段序号,在HLS.c文件中,hls_read()函数根据判断得到当前段读取完毕后,将cur_seq_no加一,从而读取下一个TS段。在hls_read_packet()函数读取一个packet,该packet包含一帧可被解码的图像,或者一帧或多帧音频。

这里c->seek_timestamp为标志位,它表征当前视频发生了SEEK事件,当发生SEEK事件后首先调用hls_read_seek()函数定位到应该读取的TS段,更新HLS上下文中的段序号。当读取到该段的packet,有两种判断。 在ffplay中,当外界发起seek请求后,将执行以下操作。

  1. 调用avformat_seek_file(),完成文件的seek定位
  2. 清空解码前packet队列(音频、视频、字幕) 
  3. 调用avcodec_flush_buffers(),清空解码buffer和相关状态 

在第一个步骤中,将在HLS层进行seek操作,seek流程图如下图所示:
DTS和PTS的分析_第1张图片
首先读取packet,判断是否有seek操作,没有则直接将该packet返回,送人后续的解码操作。如果是seek情况,则读取dts时间戳,如果dts没有值,则直接清除seek标志并返回packet(问题一)。如果dts时间戳有值,则将该值转化为微秒并与seek传入的时间进行比较,看是否大于seek时间,如果大于则表明读取的packet达到了seek要求(问题二),否则继续读packet。如果seek时间已经满足,则看该packet的flags是否是关键帧,如果是则返回该packet(问题三),否则继续读packet。
该流程很简单,但是带来了三个问题。分别解释

  • 问题一,如果dts没有值,返回回去后,解码状态全部进行了reset,则送入的第一帧信息应该为关键帧,否则该帧需要参考其他帧,产生花屏。
  • 问题二,如果dts时间戳有误,将出现dts转化为微秒后永远小于seek传入时间问题,则永远无法返回packet,导致seek僵死。
  • 问题三,判断packet是否为关键帧,忽略了该packet是否为视频,如果该packet为音频并且flag & AV_PKT_FLAG_KEY的结果为真,则将返回该packet并清空seek标准。后续读到的视频也有非关键帧的可能,从而导致花屏。
 

你可能感兴趣的:(DTS和PTS的分析)