滿足直播/轉發/分析等需求的RTMPClient客戶端EasyRTMPClient是如何處理RTMP時間戳的?

視頻流媒體中視頻數據的傳輸佔據了絕大部分的帶寬,如何提升編碼效率、減小帶寬使用、提升畫面質量,成爲音視頻開發者努力的重點。隨着互聯網、流媒體技術的發展,兼容支持H.264、H.265編碼器(可減少計算的複雜性、提高壓縮率,並降低編碼時間)已經成爲迫在眉睫的事。

EasyRTMPClient是一套穩定、易用、支持重連的RTMPClient工具,以SDK形式提供,接口調用非常簡單。本篇文章我們就來談談在研發EasyRTMPClient過程中,我們對RTMP時間戳的處理方式。

EasyRTMPClient

 

RTMP時間戳

基本概念

RTMP消息包一共分成三種類型。一類是命令(通知)消息,一類是音頻消息,一類是視頻消息。在實際開發過程中,我們主要關心的是音頻和視頻消息包的時間戳。RTMP消息分爲塊頭和消息頭,而RTMP消息包的時間戳主要存儲於消息頭域中的,用三個字節以大端序來存儲,如果時間戳超過0xFFFFFF時,則將消息包域的時間戳域設成0xFFFFFF,然後在消息包域和負載之間會插入四節來表示時間戳,該四字節時間戳域通常稱爲擴展時間戳。

RTMP時間戳形式

按adobe公司已公開的rtmp資料來看,rtmp消息包的時間戳主要有兩種形式,一種稱爲遞增模式,一種稱爲差值模式。

遞增模式

此時間戳格式與flv文件tag的時間戳保持一致。即音頻、視頻時間戳基於同一個時鐘來進行遞增。
如:A0 -> V0 ->A1 -> A2 -> V1 ….
基於此種模塊,它們的時間戳值一定滿足:V1 >= A2 >= A1 >= V0 >= A0。通常,在打音頻或視頻時間戳時,可直接取系統時間戳即可。

差值模式

此時間戳格式,則較前一種有很大不同,主要區別是,音頻和視頻時間戳不在基於同一時鐘來處理。無論音頻還是視頻消息包,每次打的時間戳都是相較於同一類消息包的前一條消息的時間戳的差值,或者可以將該值理解成爲某一幀的duration值。例如,在某個流中,視頻是25幀,那麼此時,視頻幀消息包的時間戳每次打的值基本都是在40左右(40=1000ms / 25f, 實際情況可能距該值上浮或下降幾毫秒),音頻每包時間戳跟視頻包同理。

EasyRTMPClient中的處理

在實際的開發過程當中,我們通常無法預知對端的流是採用以上何種時間戳格式來進行處理的(當然,可以通過RTMP協商的時候進行約束)。爲了減少上層開發的工作量,EasyRTMPClient在實際開發過程中,對上述兩種時間戳都進行了兼容。輸出統一的格式,與flv文件tag時間戳保持一致,即使用遞增模式。

在RTMP中,音頻包時間戳和視頻包時間戳處理方法基本是一致的,因此,下面只貼出EasyRTMPClient對上述兩種時間戳的視頻處理代碼爲:

        if ( 0xFFFFFF != pkt->timestamp )
        {
            pkt->extendTimestamp = pkt->timestamp;
        }

        s = pkt->extendTimestamp / 1000;
        us = ( pkt->extendTimestamp % 1000 ) * 1000;
        if ( 0 == easyRtmp->preTimestamp.s && 0 == easyRtmp->preTimestamp.us )
        {
            easyRtmp->preTimestamp.s = s;
            easyRtmp->preTimestamp.us = us;
        }
        else
        {
            if ( s >= easyRtmp->preTimestamp.s && us >= easyRtmp->preTimestamp.us )
            {
                goto _WRITE_TIMESTAMP;
            }
            ms = easyRtmp->videoTimestamp.s * 1000 + easyRtmp->videoTimestamp.us / 1000;
            ms += pkt->extendTimestamp;
            s = ms / 1000;
            us = ( ms % 1000 ) * 1000;
            easyRtmp->videoTimestamp.s = s;
            easyRtmp->videoTimestamp.us = us;
        }

_WRITE_TIMESTAMP:
        easyRtmp->preTimestamp.s = s;
        easyRtmp->preTimestamp.us = us;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章