1. 程式人生 > >rtmp直播拉流客戶端EasyRTMPClient TCP視窗大小設計方法

rtmp直播拉流客戶端EasyRTMPClient TCP視窗大小設計方法

EasyRTMPClient 簡介

EasyRTMPClient是EasyDarwin流媒體團隊開發、提供的一套非常穩定、易用、支援重連線的RTMPClient工具,以SDK形式提供,介面呼叫非常簡單。該EasyRTMPClient庫未使用或參考現有的任何RTMP庫,完全由EasyDarwin團隊自主實現,因此,EasyRTMPClient更易於維護,穩定性、可擴充套件性得以提升,同時,本RTMP庫全平臺支援(包括windows/linux 32&64,ARM各平臺,android, ios)!

相關連線

由EasyDarwin團隊推出的 RTMP協議教程:初級部分 進階部分

高階部分

談談RTMP視窗大小

RTMP視窗大小基本概念

RTMP訊息包一共分成三種類型。一類是命令(通知)訊息,一類是音訊訊息,一類是視訊訊息。而視窗大小則屬於第一種訊息,即命令訊息。視窗大小的本意是讓對端了解與本端的通訊狀況,用以控制媒體傳輸流量的一種方案。通常,我們從RTMP伺服器中進行拉取RTMP流到本地時,在協商的過程當中,會發送0x05,0x06訊息包,即頻寬值通知,通常設為2.5M。在實際的拉流過程中,我們通常隔一段時間就得向伺服器報告,我們已經從服務中收到了多少資料量,此種報告,就是開篇所提到的視窗大小,即ack size確認。我在實際開發的過程當中,通常,當接收的資料量接近於3倍頻寬值(2.5M*3)時,向伺服器報告一下目前已接收了多少資料。經測試,針對於Flash Media Server(FMS)對該訊息是比較敏感的(其它RTMP伺服器,各方實現不同,對該視窗大小的確認或許沒有如此敏感)。如果客戶端未能及時向FMS回饋該資料,即使在正常的拉收資料流的過程當中,FMS也會斷開與客戶的連線。

由RTMP視窗大小引發的問題

上述,基本對RTMP視窗大小做了一個介紹。根據RTMP協議標準來看,RTMP視窗大小是通過0x03型別來標識的,其負載通常是四個位元組,用大端序來表示當前視窗大小,即當前已接收的資料總量。前面,我們說過,FMS對視窗大小這條訊息是十分敏感的,除了按上述方法,及時向FMS反饋0x03訊息包(視窗大小確認)外,仍須注意另一個問題,即4位元組溢位的情況。我們知道,4位元組無符號,大概能表示4.29G左右的資料量,當從RTMP伺服器所接收的資料量即將達到4.29G時,應及時的向RTMP伺服器進行一次視窗大小的置零反饋。該bug是經過大量時間分析而得來,據後來查驗,librtmp庫也同樣存在該bug(該Bug於2013/2014年左右遇到並分析處理),由於我沒用librtmp庫,因此,暫不能確認當前版本是否有修正該bug(應該沒有修正 ^_^)。

EasyRTMPClient 中的處理

在實際的開發過程當中,當我們接收的資料量接近於4.29G時,我們及時將該值提前置0,反饋給FMS伺服器即可。如果過早的置0,或直接等待其溢位,FMS同樣會立即斷開RTMP客戶端。下面貼出EasyRTMPClient對上述Bug的處理程式碼:

int SendWindowAcknowledgementSize( MSRtmpSession * msrs )
{
    int ret = 0;
    MRPKT * pkt = NULL;
    MPARASITICAL_BUFS * bufs = NULL;

    pkt = allocRMPkt();
    if ( !pkt )
    {
        return -1;
    }

    writeMessageHdr( 0x02, 0, 0, 0x03, -1, pkt );

    bufs = msrs->msr->hdr.alloc( msrs->msr->hdr.hdr );
    *( ( unsigned int * )bufs->buf ) = htonl( msrs->msr->info.recvInfo.bytes );
    bufs->len = sizeof( unsigned int );
    msrs->msr->info.ackSize = msrs->msr->info.recvInfo.bytes;                          // reference for next send ack size

    fillPayload( bufs, pkt, msrs->msr ); 
    commitMRPkt( pkt, msrs->msr );

    ret = MRSendChunks( msrs, &pkt, -1 );

    if ( msrs->msr->info.recvInfo.bytes + msrs->msr->info.clientBW * 3 > 4294967295 )  // stream bytes > 4.2G, ack clean.
    {
        msrs->msr->info.recvInfo.bytes = 0;                                            // Improve by Inpilen.
        msrs->msr->info.ackSize = 0;
    }

    return ret;
}

獲取更多資訊

EasyRTMPClient交流群:544917793

Copyright © EasyDarwin.org 2012-2017

EasyDarwin