存在问题:当前的播放器产品上,播放REAL VIDEO时,虽然文件是25fps的,并且在DEOCDER和RENDER都没有丢帧,但是视频看起来就是一顿一顿的,不流畅,但是并没有延迟。
1、检查后,发现问题出在时间戳上。当前在DECODE之后,frame的timestamp根据demux出来的pFrame->ulTimestamp拿的,DECODER输出时,时间戳如下(单位微秒)
Rv output buffer pts = 323000, dur = 1000, Current = 48534, Skew = 274466
Rv output buffer pts = 480000, dur = 157000, Current = 59978, Skew = 420022
Rv output buffer pts = 481000, dur = 1000, Current = 67589, Skew = 413411
Rv output buffer pts = 482000, dur = 1000, Current = 74178, Skew = 407822
Rv output buffer pts = 483000, dur = 1000, Current = 80045, Skew = 402955
Rv output buffer pts = 640000, dur = 157000, Current = 86134, Skew = 553866
Rv output buffer pts = 641000, dur = 1000, Current = 91322, Skew = 549678
Rv output buffer pts = 642000, dur = 1000, Current = 108634, Skew = 533366
Rv output buffer pts = 643000, dur = 1000, Current = 113267, Skew = 529733
Rv output buffer pts = 800000, dur = 157000, Current = 118922, Skew = 681078
Rv output buffer pts = 801000, dur = 1000, Current = 124000, Skew = 677000
Rv output buffer pts = 802000, dur = 1000, Current = 128767, Skew = 673233
Rv output buffer pts = 803000, dur = 1000, Current = 133456, Skew = 669544
Rv output buffer pts = 960000, dur = 157000, Current = 138845, Skew = 821155
Rv output buffer pts = 961000, dur = 1000, Current = 144111, Skew = 816889
Rv output buffer pts = 962000, dur = 1000, Current = 148734, Skew = 813266
Rv output buffer pts = 963000, dur = 1000, Current = 153478, Skew = 809522
Rv output buffer pts = 1200000, dur = 237000, Current = 159911, Skew = 1040089
Rv output buffer pts = 1201000, dur = 1000, Current = 165689, Skew = 1035311
Rv output buffer pts = 1202000, dur = 1000, Current = 170911, Skew = 1031089
Rv output buffer pts = 1203000, dur = 1000, Current = 176211, Skew = 1026789
Rv output buffer pts = 1360000, dur = 157000, Current = 183034, Skew = 1176966
Rv output buffer pts = 1361000, dur = 1000, Current = 188367, Skew = 1172633
Rv output buffer pts = 1362000, dur = 1000, Current = 193211, Skew = 1168789
Rv output buffer pts = 1363000, dur = 1000, Current = 198289, Skew = 1164711
Rv output buffer pts = 1520000, dur = 157000, Current = 204034, Skew = 1315966
Rv output buffer pts = 1521000, dur = 1000, Current = 216122, Skew = 1304878
Rv output buffer pts = 1522000, dur = 1000, Current = 221100, Skew = 1300900
Rv output buffer pts = 1523000, dur = 1000, Current = 226122, Skew = 1296878
两个FRAME之间间隔可能是150MS以上,也可能是1MS,在经过Video Compositor和OSD合成后,间隔时间一毫秒的FRAME全部被compositor丢弃,到了compositor输出时,帧间隔都是150MS左右的了,每秒帧率的确不到10帧。所以需要修改时间戳的计算方法
2、一段时间以来,我一直认为REAL文件的帧率不是固定的,因为无法在DEMUX中按照协议取得REAL文件的帧率;并且帧率不固定、存在漂移这点也被Doctor.Li赞同。今天静下心来查了一下,发现根本原因并不是帧率漂移造成的,而是部分RV ENCODER并不是按照REAL协议来封装RM文件的。在rv_depacki_unpack_format_info函数中,ufFramesPerSecond应该是32bit的数据,但实际上相当大的一部分REAL文件在这里只用了16bit,所以读出来的帧率是几万几万的。这个地方需要稍微修改一下来容错
if( FramesPerSecond > 0xFFFF)
{
FramesPerSecond = FramesPerSecond >> 16;
}
这样就能得到固定的帧率了。不过很奇怪为什么桌面的REALONE PLAYER它就能读到帧率,而SDK里却存在容错性的问题。
3、得到帧率后,修正时间戳算法
在rv_frame_struct结构体中,我们可以看到
typedef struct rv_frame_struct
{
UINT32 ulDataLen;
BYTE* pData;
UINT32 ulTimestamp;
UINT16 usSequenceNum;
UINT16 usFlags;
HXBOOL bLastPacket;
UINT32 ulNumSegments;
rv_segment* pSegment;
} rv_frame;
我本来打算用 usSequenceNum * FrameDuration 来做时间戳,结果发现即使时间戳和AUDIO同步了,播放时演员的嘴型还是对不上。这说明RV FRAME在时间并就不是均匀的,这也再次证实了前面提到的REAL格式帧率不固定、存在轻微漂移的观点。我只好将计就计,对时间戳进行造假了。下面伪代码中的时间单位都是毫秒(mili seconds), 与时间间隔不是一毫秒的帧我们称为"有效帧", 否则称为"无效帧". 暂时这么叫把, 最后不论有效帧无效帧都应该发送出去的.
/* set timestamp of real video frame */
{
声明 静态变量 前一帧的原始时间戳;
声明 静态变量 最后一个有效帧的时间戳;
声明 静态变量 最后一个有效帧的序号; //上面rv_frame_struct结构体中的usSequenceNum
声明 临时变量 当前帧与前一帧的原始时间间隔; //未伪造过的时间戳之差
当前帧与上一帧的原始时间间隔 = 当前帧德原始时间戳 - 前一帧的原始时间戳;
如果( 当前帧与上一帧的原始时间间隔 > (根据帧率算出的两帧之间平均时长 / 2 ) ) //把当前帧算作有效帧
{
最后一个有效帧的时间戳 = 当前帧的原始时间戳;
最后一格有效帧的序号 = 当前帧的序号;
设置当前帧的伪造时间戳 = 当前帧的原始时间戳;
}
否则 //无效帧
{
伪造当前帧的时间戳 = (当前帧 的序号 - 最后一个有效帧的序号) * 根据帧率算出的两帧之间平均时长 + 最后一个有效帧的时间戳;
}
前一帧原始时间戳 = 当前帧的原始时间戳;
}
伪造完成,效果让人满意,视频输出平滑。但是对于码率太低的RV文件,比如视频只有200KBPS却有VGA的分辨率,由于帧率实在太低,所以看起来是一卡一卡的,没有办法咯,实际上并没有丢帧,把码率提高就OK了。
另一方面,在输出时丢帧判断条件是: 当前帧的时间戳 < 系统当前时间 + 两帧的平均间隔时间。 由于我们伪造了时间戳,比如在25fps下两帧间隔应该是40ms, 但REAL VIDEO在某些时候两帧间隔会达到80ms,而我们伪造出来仍然只有间隔40ms,所以在RENDER位置上的丢帧处理应该更"宽容"些,改为:当前帧的时间戳 < 系统当前时间 + 两帧的平均间隔时间*2 毕竟汇聚劳动人民血汗好不容易解出来的一个帧,终于跑到播放器的终点,却因为伪造的时间戳慢了那么一点点儿丢弃,实在是浪费。
本文转自Walzer博客园博客,原文链接:http://www.cnblogs.com/walzer/archive/2007/05/01/733871.html,如需转载请自行联系原作者