解決REAL VIDEO的視頻平滑問題 (轉)

存在問題:當前的播放器產品上,播放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  畢竟匯聚勞動人民血汗好不容易解出來的一個幀,終於跑到播放器的終點,卻因爲僞造的時間戳慢了那麼一點點兒丟棄,實在是浪費。

 

 

 

轉自

www.Walzer.cn - 技術與管理博客

原文地址 http://www.cnblogs.com/walzer/archive/2007/05/01/733871.html

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章