Live555 組播及RTSPClient IPv6改造

背景

之前的文章我們講過如何使用Live555框架進行組播 RTP/UDP播放,以及使用rtspclient完成rtsp播放。
隨着網絡時代的發展,IPv6的實施也是越來越深入,而Live555框架目前暫未實現IPv6支持,我們對其進行IPv6改造。因爲代碼改造較爲碎片化,本文僅提供思路。

Live555 框架:

Live555框架如下:
在這裏插入圖片描述
其中,我們只需要對以下兩部分進行擴展,

  • groupSock:用於網絡交互及數據分發,其封裝socket接口,提供加入/退出組播、數據收發等功能。主要需要擴展IPv6/IPv4 Socket兼容。
  • liveMedia:流媒體業務核心,主要需要擴URL兼容及RTSP建連的IPv6支持。
    .

groupSock修改思路:

第一步:

改造NetAddress,前後變化:
在這裏插入圖片描述
NetAddress是一個用於保存網絡地址的類,其內部定義了兩個數據成員,分別是用於保存地址數據的u_int8_t* fData(網絡字節序)和用於指示地址長度的unsigned fLength,這兩個成員描述了網絡地址。它同時也定義了typedef u_int32_t netAddressBits; 4字節來表示網絡地址。
也即是說,整個Live555是以netAddressBits或者NetAddress對象來描述網絡地址的。

修改方法:
摒棄fData,採用socket來描述網絡地址。
socket結構轉換如下(詳細說明):在這裏插入圖片描述
那麼我們採用struct sockaddr_storage來描述一個網絡地址,同時爲了方便轉換爲IPv6/IPv4,定義聯合體:

typedef union NetSockaddr {
		struct sockaddr_storage fGeneric;
		struct sockaddr_in fIn;
		struct sockaddr_in6 fIn6;
} NetSockaddr;

以成員NetSockaddr fSockAddr;來描述網絡地址,就能兼容IPv4和IPv6。
因爲以socket來描述地址,所以把Port類和NetAddress類關聯起來,這樣可以方便通過NetAddress查找其對應的端口號。

因此,接下來我們需要修改整個NetAddress的操作符重載及其函數。爲了方便,可以根據需要增加一些函數供外部使用。

第二步:
將groupSock中使用struct sockaddr_innetAddressBits描述的參數及成員,全部替換爲NetAddress。
爲什麼不直接用struct sockaddr_storage及字節類型呢?因爲NetAddress本身就具備了描述socket及IP地址的能力,這樣的好處是可以更方便的使用NetAddress提供的操作。
因此,需要改造原本groupSock中涉及到這這個兩個類型的所有函數的實現。

第三步:
GroupsockHelper提供groupSock中socket的創建、讀寫、加入/離開組播操作 ,區分IPv4/IPv6流程,修改該類實現setupStreamSocket、setupDatagramSocket、readSocket、writeSocket修改較爲簡單,兼容修改爲IPv6/v4混編即可。加入/離開組播,要注意setsockopt設置的組播操作略有不同。

總結:
基本上就是將NetAddress的核心成員從fData改爲fSockAddr,其他的均是配合其修改,還有是socket IPv6編程的修改,也沒有太可以說道的。

貼上NetAddress及Port的定義

class NetAddress {
    public:
	typedef union NetSockaddr {
		struct sockaddr_storage fGeneric;
		struct sockaddr_in fIn;
		struct sockaddr_in6 fIn6;
        } NetSockaddr;

    public:
        NetAddress();
        NetAddress(const char * ip, int family = AF_INET);
        NetAddress(struct sockaddr_storage * addr, socklen_t len);
		NetAddress(u_int8_t const* data/*should be host order bytes*/, unsigned length=4 /* if ipv4: 32 bits */);
		NetAddress(NetAddress const& orig);
		virtual ~NetAddress();
		
		NetAddress& operator=(NetAddress const& rightSide);
        Boolean operator ==( const NetAddress & a ) const;
        Boolean operator !=( const NetAddress & a ) const;
		
		Boolean isMulticast() const;
		Boolean isLocal() const;
		Boolean isUnSpec() const; 
		int getFamily() const;
		const char* getAddress(char * buf, size_t bufSize) const;
		unsigned length() const;
		u_int8_t const* data() const; // always in network byte order
		struct sockaddr_storage makeSockAddr( Port port, socklen_t * len ) const;
		const struct sockaddr_storage* getSockAddr() const { return &fSockAddr.fGeneric; }
		u_int16_t getPort() const; 	// in host byte order
		void setPort( u_int16_t port ); // in host byte order

    private:
        void setAddress(const char * ip, int flags, int family);
		void clean();
        NetSockaddr fSockAddr;
};
typedef u_int16_t portNumBits;

class Port {
    public:
	Port(portNumBits num = 0/* in host byte order */);
        Boolean operator ==( const Port & a ) const {
          return a.num() == this->num();
        }
        Boolean operator !=( const Port & a ) const {
          return a.num() != this->num();
        }

	portNumBits num() const // in network byte order
		{ return fPortNum; }
        portNumBits port() const // in host byte order 
 		{ return ntohs(fPortNum); }

    private:
	portNumBits fPortNum; // stored in network byte order
#ifdef IRIX
	portNumBits filler; // hack to overcome a bug in IRIX C++ compiler
#endif
};

liveMedia修改思路:

對於liveMedia的修改比較簡單。
首先也一樣,對於各個文件(source、sink、mediasession,rtspClient等),將struct sockaddr_innetAddressBits描述的參數及成員,全部替換爲NetAddress,然後對應修改函數實現即可。

然後對於rtsp,要注意的是SDP信息的解析函數,在c=字段的鏈接描述中,需要支持IPv6。修改如下:

static char* parseCLine(char const* sdpLine) {
  char* resultStr = NULL;
  char* buffer = strDupSize(sdpLine); // ensures we have enough space
  if (sscanf(sdpLine, "c=IN IP4 %[^/\r\n]", buffer) == 1) {
    // Later, handle the optional /<ttl> and /<numAddresses> #####
    resultStr = strDup(buffer);
  } 
  else if( sscanf(sdpLine, "c=IN IP6 %[^/\r\n]", buffer) == 1) { //mark by wusc get  IP6 ip
    resultStr = strDup(buffer);
  }
  delete[] buffer;

  return resultStr;
}

最後就是在rtspClient或者是組播拉流程序,需要修改對URL的解析,區分IPv6,IPv4的URL處理。
可以考慮的幾點是:

  1. 組播地址中,IPv6形式肯定是[ipv6地址]:端口形式,根據有無[]來區分IPv4/IPv6
  2. Rtsp地址中,可以判斷主機host,如果URL攜帶’.’,那麼可以判定是IPv4,否則爲IPv6。

測試效果:

以RTSP爲例,將改造後的Live555模塊替換之前的rtsp代理app中(文章鏈接

用VLC搭建IPv6流服務器,配置爲基於UDP載流的,封裝爲RTP的TS流。
在這裏插入圖片描述

抓包看到rtsp交互流程正常,如下:
在這裏插入圖片描述
其SDP如下:

v=0
o=- 16290660091181941766 16290660091181941766 IN IP6 POM20BFBNSWQMND
s=Unnamed
i=N/A
c=IN IP6 ::
t=0 0
a=tool:vlc 3.0.5
a=recvonly
a=type:broadcast
a=charset:UTF-8
a=control:rtsp://[2001:db8::bc83:14f5:ca89:b694]:8554/wait.ts
m=video 0 RTP/AVP 33
b=RR:0
a=rtpmap:33 MP2T/90000
a=control:rtsp://[2001:db8::bc83:14f5:ca89:b694]:8554/wait.ts/trackID=0

繼續看RTP流是否正常,可以看到PLAY命令後,也收到了IPv6 UDP傳輸的TS流:
在這裏插入圖片描述
至此,就可以確認流程正確,看畫面,正常播放,OK。

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