OpenRTMFP/Cumulus Primer(21)經由伺服器的釋出/訂閱流程的關鍵點

鍾超發表於2012-07-23

OpenRTMFP/Cumulus Primer(21)經由伺服器的釋出/訂閱流程的關鍵點

  • 作者:柳大·Poechant(鍾超 Michael)
  • 部落格:Blog.csdn.net/poechant
  • 郵箱:zhongchao.ustc@gmail.com
  • 日期:July 23th, 2012

整個流程概括如下:

Flash 客戶端通過 NetConnection 與 Cumulus 建立連線,然後通過 NetStream 使用 RTMFP 釋出 Audio/Video/Data(下面簡稱為 A/V/D) 給伺服器,這個 Flash Player 就作為一個釋出者(Publisher)。RTMFP 伺服器接收到後給所有的訂閱者(Subscribers)傳送 Audio/Video/Data。

1 客戶端釋出(Publishing on client side)

通過 NetConnection 連線 RTMFP 伺服器 Cumulus,可以參考《OpenRTMFP/Cumulus Primer(2)用Lua編寫HelloWorld應用擴充套件CumulusServer》一文。關鍵的一個語句如下,其中 nc 是一個 NetConnection 物件(不是“腦殘”的意思 - -|||):

nc.connect("rtmfp://localhost:1935");

在連線成功後通過 NetStream 釋出 Audio/Video,如下所示,其中 ns1 是一個 NetStream 物件。

ns1.publish("poechant_media_flow", "live");

根據音視訊不同的需求,播放相應內容。如果是釋出 Data,則使用NetStream.send()來實現。這樣就完成了客戶端的 A/V/D 釋出

2 伺服器端(Server-side)

Cumulus 通過 RTMFPReceiving 這個 RTMFP 協議資料接收引擎完成一些連線建立的相關動作,以及接收資料包:

void RTMFPServer::receive(RTMFPReceiving& rtmfpReceiving);

該函式會在收到客戶端發來請求時響應,如果是仍未建立連線的請求,則由此建立 Session(RTMFP 的核心概念之一),並取出其中的資料包。這其中有多個過程,我這裡就不詳述,以後會發布文章來解釋。

繼續我們的話題,在RTMFPServer::receive 函式中如果是建立連線階段,則會呼叫 Handshake 類的 receive 來做接下來的處理,這個我就不去詳細分析了,因為與本文主題無關。與本文有關的是,如果是已經建立了 Session 的,則會呼叫:

void ServerSession::packetHandler(PacketReader& packet);

這是一個相對複雜的函式,會從 packet 中取出很多有用的資訊。此外,比較重要的是,在我們上述情況下,會呼叫 Flow 類的:

void Flow::fragmentSortedHandler(UInt64 stage,PacketReader& fragment,UInt8 flags);

該函式中會對 Audio/Video/Data 分別響應不同的處理機制:

switch(type) {
    case Message::AMF_WITH_HANDLER:
    case Message::AMF:
        messageHandler(name,amf);
        break;
    case Message::AUDIO:
        audioHandler(*pMessage);
        break;
    case Message::VIDEO:
        videoHandler(*pMessage);
        break;
    default:
        rawHandler(type,*pMessage);
}

接下來在 Publication 中完成對所有訂閱了該釋出者的 Flash Players 傳送資訊,核心的程式碼為:

for(it=_listeners.begin();it!=_listeners.end();++it) {
    it->second->pushAudioPacket(time,packet);
    packet.reset(pos);
}

for(it=_listeners.begin();it!=_listeners.end();++it) {
    it->second->pushVideoPacket(time,packet);
    packet.reset(pos);
}

for(it=_listeners.begin();it!=_listeners.end();++it) {
    it->second->pushDataPacket(name,packet);
    packet.reset(pos);
}

其中的 _listeners 就是該 Publication 中的所有訂閱者。訂閱者的新增/刪除是通過:

Listener& addListener(
    Peer& peer,
    Poco::UInt32 id,
    FlowWriter& writer,
    bool unbuffered);

void removeListener(
    Peer& peer,
    Poco::UInt32 id);

這兩個函式來實現的。

要注意的是,在 Publication 中已經完成了向訂閱者釋出資訊,之後雖然會響應到 Peer 及 RTMFPServer 的onAudioPacketonVideoPacketonDataPacket,但此時都與訂閱者接收資訊無關了。Cumulus 正是在RTMFPServer::onAudioPacketRTMFPServer::onVideoPacketRTMFPServer::onDataPacket中呼叫使用者定製的服務(Lua 指令碼實現),完成一些自定義的需求。我是在此通過直接的 C++ 功能擴充套件,來新增業務需求的,沒有使用 Lua 指令碼及 Cumulus 中的 Lua 指令碼引擎,主要原因是為了提高效率。

3 客戶端訂閱(Subscribing on client side)

訂閱很簡單,在play的時候傳入正確的釋出者名稱即可。

ns2.play("poechant_media_flow");

測試程式碼可以參考 Reference-1,其中的例子是關於NetStream::send(…)的,用於傳送 Data,Audio 和 Video 的程式可以參考該例修改。

客戶端訂閱後,這些資訊並不會直接從釋出者那裡通過 P2P 的方式接收。如果想使用釋出者與接受者直接連線的方式,則需要在 NetStream 初始化的時候,傳入NetStream.DIRECT_CONNECTIONS引數,預設的NetStream.CONNECT_TO_FMS是將資料上行到伺服器再下行給所有訂閱者(Subscribers)的。根據不同的應用場景,可以使用不同的方式。

4 Reference

  1. http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()

-

轉載請註明來自柳大的CSDN部落格:Blog.csdn.net/poechant

-

相關文章