最近碰到一个问题,流媒体服务器写出来的TS文件用ffmpeg、potplayer播放时长是正确的,可是使用mediainfo和windows自带的系统播放器播放,就会出现播放时长偏小的问题。而mediainfo中音视频播放时长倒是正确值。以下图所示:windows
如上图所示,该MPEG-TS文件的播放总时长为9.268秒,视频播放时长为10.865秒,音频播放时长为10.755秒。而该视频在ffmpeg中显示播放时长为10.865秒。服务器
针对以上问题,笔者下载了mediainfo的源码编译调试发现,mediainfo解析MPEG-TS的播放时长来自于TS包中的PCR(即program_clock_reference/节目时钟参考)。而音视频播放时长来自于PES中时间戳。说到这里,我估计有些不熟悉MPEG-TS格式的小伙伴已经开始晕了。ui
MPEG-TS文件结构分三个层面:编码
使用固定包大小对数据进行打包,TS包大小固定为188字节(M2TS/蓝光高清为192字节)。它包括TS头数据、有效载荷数据和填充数据三部分。spa
TS包头为4字节固定大小包头标识,定义以下
typedef struct TS_packet_header
{调试
unsigned sync_byte : 8; //同步字节, 固定为0x47,表示后面的是一个TS分组 unsigned transport_error_indicator : 1; //传输误码指示符 unsigned payload_unit_start_indicator : 1; //有效荷载单元起始指示符 unsigned transport_priority : 1; //传输优先, 1表示高优先级,传输机制可能用到,解码用不着 unsigned PID : 13; //PID unsigned transport_scrambling_control : 2; //传输加扰控制 unsigned adaption_field_control : 2; //自适应控制 01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00解码器不进行处理 unsigned continuity_counter : 4; //连续计数器 一个4bit的计数器,范围0-15
} TS_packet_header; //总共32位,4个字节
当adaption_field_control=10或11时:
包头后面紧跟者调整字段,PCR则是在这个字段中,以下
...]code
有效载荷数据通常为音视频数据包的PES数据包,或是节目映射表PMT和节目访问表PAT等TS节目信息。视频
当TS包头和有效载荷数据不总188字节时,将会使用0xff填充,直到TS包知足188字节为止。blog
通过打包的基本码流,通常以数据帧为单位对音视频数据进行打包,同时会携带有该数据帧的时间戳和一些其余标志位信息。ip
视频基本码流数据。
PCR(Program Clock Reference/节目时钟参考)用于恢复出与编码端一致的系统时序时钟(STC/System Time Clock)。MPEG2系统层标准规定,PTS的时间间隔不能超过0.7S,出如今TS包头的PCR间隔不能超过0.1S,PCR最大值位26:30:43。在TS传输过程当中,通常DTS和PCR差值会在一个合适的范围,这个差值就是要设置的音视频buffer的大小。通常状况下视频DTS和PCR的差值在700ms-1200ms之间,音频差值在200ms-700ms之间。
如今终于明白了,mediainfo和系统播放器获取时间是根据PCR/节目时钟参考来计算的,而ffmpeg和potplayer之类的播放器时使用PTS来计算时间戳的。而PCR的时间间隔最大不能超过0.1s,即100ms,而这个TS文件的PCR时间间隔明显就大于1秒,才会致使这个问题的出现。想要解决这个问题,在每间隔100ms插入一个PCR就OK了。