1 问题描述
最近碰到一个问题,流媒体服务器写出来的TS文件用ffmpeg、potplayer播放时长是正确的,但是使用mediainfo和windows自带的系统播放器播放,就会出现播放时长偏小的问题。而mediainfo中音视频播放时长却是正确值。如下图所示:
如上图所示,该MPEG-TS文件的播放总时长为9.268秒,视频播放时长为10.865秒,音频播放时长为10.755秒。而该视频在ffmpeg中显示播放时长为10.865秒。
2 问题原因
针对以上问题,笔者下载了mediainfo的源码编译调试发现,mediainfo解析MPEG-TS的播放时长来自于TS包中的PCR(即program_clock_reference/节目时钟参考)。而音视频播放时长来自于PES中时间戳。说到这里,我估计有些不熟悉MPEG-TS格式的小伙伴已经开始晕了。
3.MPEG2系统音视频同步方式
MPEG-TS文件结构分三个层面:
3.1.TS层
使用固定包大小对数据进行打包,TS包大小固定为188字节(M2TS/蓝光高清为192字节)。它包括TS头数据、有效载荷数据和填充数据三部分。
3.1.1 TS包头
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则是在这个字段中,如下
...]
3.1.2有效载荷数据
有效载荷数据一般为音视频数据包的PES数据包,或是节目映射表PMT和节目访问表PAT等TS节目信息。
3.1.3 填充部分
当TS包头和有效载荷数据不总188字节时,将会使用0xff填充,直到TS包满足188字节为止。
3.2 PES(Packetize Elementary Stream)
经过打包的基本码流,一般以数据帧为单位对音视频数据进行打包,同时会携带有该数据帧的时间戳和一些其他标志位信息。
3.3 ES(Elementary Stream)
视频基本码流数据。
4 PCR
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之间。
5 解决方案
现在终于明白了,mediainfo和系统播放器获取时间是根据PCR/节目时钟参考来计算的,而ffmpeg和potplayer之类的播放器时使用PTS来计算时间戳的。而PCR的时间间隔最大不能超过0.1s,即100ms,而这个TS文件的PCR时间间隔明显就大于1秒,才会导致这个问题的出现。想要解决这个问题,在每间隔100ms插入一个PCR就OK了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。