1. 程式人生 > >Live555學習之(五)------live555ProxyServer.cpp的學習

Live555學習之(五)------live555ProxyServer.cpp的學習

live555ProxyServer.cpp在live/proxyServer目錄下,這個程式展示瞭如何利用live555來做一個代理伺服器轉發rtsp視訊(例如,IPCamera的視訊)。

  首先來看一下main函式

複製程式碼
 1 int main(int argc, char** argv) 
 2 {
 3   // Increase the maximum size of video frames that we can 'proxy' without truncation.
 4   // (Such frames are unreasonably large; the back-end servers should really not be sending frames this large!)
5 OutPacketBuffer::maxSize = 300000; // bytes 6 7 // Begin by setting up our usage environment: 8 TaskScheduler* scheduler = BasicTaskScheduler::createNew(); 9 env = BasicUsageEnvironment::createNew(*scheduler); 10 11 /* 12 .... 對各種輸入引數的處理,在此略去 13 */ 14 15 // Create the RTSP server. Try first with the default port number (554),
16 // and then with the alternative port number (8554): 17 RTSPServer* rtspServer; 18 portNumBits rtspServerPortNum = 554; 19 rtspServer = createRTSPServer(rtspServerPortNum); 20 if (rtspServer == NULL) { 21 rtspServerPortNum = 8554; 22 rtspServer = createRTSPServer(rtspServerPortNum);
23 } 24 if (rtspServer == NULL) { 25 *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n"; 26 exit(1); 27 } 28 29 // Create a proxy for each "rtsp://" URL specified on the command line: 30 for (i = 1; i < argc; ++i) { 31 char const* proxiedStreamURL = argv[i]; 32 char streamName[30]; 33 if (argc == 2) { 34 sprintf(streamName, "%s", "proxyStream"); // there's just one stream; give it this name 35 } else { 36 sprintf(streamName, "proxyStream-%d", i); // there's more than one stream; distinguish them by name 37 } 38 ServerMediaSession* sms 39 = ProxyServerMediaSession::createNew(*env, rtspServer, 40 proxiedStreamURL, streamName, 41 username, password, tunnelOverHTTPPortNum, verbosityLevel); 42 rtspServer->addServerMediaSession(sms); 43 // proxiedStreamURL是代理的源rtsp地址字串,streamName表示代理後的ServerMediaSession的名字 44 char* proxyStreamURL = rtspServer->rtspURL(sms); 45 *env << "RTSP stream, proxying the stream \"" << proxiedStreamURL << "\"\n"; 46 *env << "\tPlay this stream using the URL: " << proxyStreamURL << "\n"; 47 delete[] proxyStreamURL; 48 } 49 50 if (proxyREGISTERRequests) { 51 *env << "(We handle incoming \"REGISTER\" requests on port " << rtspServerPortNum << ")\n"; 52 } 53 54 // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling. 55 // Try first with the default HTTP port (80), and then with the alternative HTTP 56 // port numbers (8000 and 8080). 57 58 if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) { 59 *env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n"; 60 } else { 61 *env << "\n(RTSP-over-HTTP tunneling is not available.)\n"; 62 } 63 64 // Now, enter the event loop: 65 env->taskScheduler().doEventLoop(); // does not return 66 67 return 0; // only to prevent compiler warning 68 }
複製程式碼

  main函式還是很簡單,第一行是設定OutPacketBuffer::maxSize的值,經過測試,我設定成300000個位元組時就可以傳送1080p的視訊了。

  然後還是建立TaskShcheduler和UsageEnvironment物件,中間是對各種輸入引數的處理,在此我就省略不作分析了。

  然後建立RTSPServer,根據輸入的rtsp地址串建立ProxyServerMediaSession並新增到RTSPServer,然後開始程式的無限迴圈。

  看一下ProxyServerMediaSession這個類

複製程式碼
 1 class ProxyServerMediaSession: public ServerMediaSession {
 2 public:
 3   static ProxyServerMediaSession* createNew(UsageEnvironment& env,
 4                         RTSPServer* ourRTSPServer, // Note: We can be used by just one "RTSPServer"
 5                         char const* inputStreamURL, // the "rtsp://" URL of the stream we'll be proxying
 6                         char const* streamName = NULL,
 7                         char const* username = NULL, char const* password = NULL,
 8                         portNumBits tunnelOverHTTPPortNum = 0,
 9                             // for streaming the *proxied* (i.e., back-end) stream
10                         int verbosityLevel = 0,
11                         int socketNumToServer = -1);
12  // Hack: "tunnelOverHTTPPortNum" == 0xFFFF (i.e., all-ones) means: Stream RTP/RTCP-over-TCP, but *not* using HTTP
13    // "verbosityLevel" == 1 means display basic proxy setup info; "verbosityLevel" == 2 means display RTSP client protocol also.
14 // If "socketNumToServer" >= 0,then it is the socket number of an already-existing TCP connection to the server.
15 //(In this case, "inputStreamURL" must point to the socket's endpoint, so that it can be accessed via the socket.)
16 
17   virtual ~ProxyServerMediaSession();
18 
19   char const* url() const;
20 
21   char describeCompletedFlag;
22     // initialized to 0; set to 1 when the back-end "DESCRIBE" completes.
23     // (This can be used as a 'watch variable' in "doEventLoop()".)
24   Boolean describeCompletedSuccessfully() const { return fClientMediaSession != NULL; }
25     // This can be used - along with "describeCompletdFlag" - to check whether the back-end "DESCRIBE" completed *successfully*.
26 
27 protected:
28   ProxyServerMediaSession(UsageEnvironment& env, RTSPServer* ourRTSPServer,
29               char const* inputStreamURL, char const* streamName,
30               char const* username, char const* password,
31               portNumBits tunnelOverHTTPPortNum, int verbosityLevel,
32               int socketNumToServer,
33               createNewProxyRTSPClientFunc* ourCreateNewProxyRTSPClientFunc
34               = defaultCreateNewProxyRTSPClientFunc);
35 
36   // If you subclass "ProxyRTSPClient", then you will also need to define your own function
37   // - with signature "createNewProxyRTSPClientFunc" (see above) - that creates a new object
38   // of this subclass.  You should also subclass "ProxyServerMediaSession" and, in your
39   // subclass's constructor, initialize the parent class (i.e., "ProxyServerMediaSession")
40   // constructor by passing your new function as the "ourCreateNewProxyRTSPClientFunc"
41   // parameter.
42 
43 protected:
44   RTSPServer* fOurRTSPServer;                  // 新增該ProxyServerMediaSession的RTSPServer物件
45   ProxyRTSPClient* fProxyRTSPClient;        // 通過一個ProxyRTSPClient物件與給定rtsp伺服器進行溝通
46   MediaSession* fClientMediaSession;           // 通過一個MediaSession物件去請求給定rtsp地址表示的媒體資源
47 
48 private:
49   friend class ProxyRTSPClient;
50   friend class ProxyServerMediaSubsession;
51   void continueAfterDESCRIBE(char const* sdpDescription);
52   void resetDESCRIBEState(); // undoes what was done by "contineAfterDESCRIBE()"
53 
54 private:
55   int fVerbosityLevel;
56   class PresentationTimeSessionNormalizer* fPresentationTimeSessionNormalizer;
57   createNewProxyRTSPClientFunc* fCreateNewProxyRTSPClientFunc;
58 };
複製程式碼

  ProxyServerMediaSession是ServerMediaSession的子類,它與普通的ServerMediaSession相比多了三個重要的成員變數:RTSPServer* fOurRTSPServer,ProxyRTSPClient* fProxyRTSPClient,MediaSession* fClientMediaSession。fOurRTSPServer儲存新增該ProxyServerMediaSession的RTSPServer物件,fProxyRTSPClient儲存該ProxyServerMediaSession對應的ProxyRTSPClient物件,fClientMediaSession儲存該ProxyServerMediaSession對應的MediaSession物件。每個ProxyServerMediaSession對應一個ProxyRTSPClient物件和MediaSession物件,從這個地方可以看出,live555代理伺服器同時作為RTSP伺服器端和RTSP客戶端,作為RTSP客戶端去獲取給定rtsp地址(比如IPCamera的rtsp地址)的媒體資源,然後作為RTSP伺服器端轉發給其他的RTSP客戶端(比如VLC)。

  ProxyRTSPClient是RTSPClient的子類,我們來看一下它的定義

複製程式碼
 1 // A subclass of "RTSPClient", used to refer to the particular "ProxyServerMediaSession" object being used.
 2 // It is used only within the implementation of "ProxyServerMediaSession", but is defined here, in case developers wish to
 3 // subclass it.
 4 
 5 class ProxyRTSPClient: public RTSPClient {
 6 public:
 7   ProxyRTSPClient(class ProxyServerMediaSession& ourServerMediaSession, char const* rtspURL,
 8                   char const* username, char const* password,
 9                   portNumBits tunnelOverHTTPPortNum, int verbosityLevel, int socketNumToServer);
10   virtual ~ProxyRTSPClient();
11 
12   void continueAfterDESCRIBE(char const* sdpDescription);   //包含了continueAfterDESCRIBE回撥函式
13   void continueAfterLivenessCommand(int resultCode, Boolean serverSupportsGetParameter); //傳送心跳命令後的回撥函式 
14   void continueAfterSETUP();                                //包含了continueAfterSETUP回撥函式
15 
16 private:
17   void reset();
18 
19   Authenticator* auth() { return fOurAuthenticator; }
20 
21   void scheduleLivenessCommand();                      // 設定何時執行傳送心跳命令的任務
22   static void sendLivenessCommand(void* clientData);   // 傳送心跳命令
23 
24   void scheduleDESCRIBECommand();             // 設定何時執行傳送DESCRIBE命令的任務
25   static void sendDESCRIBE(void* clientData);      // 傳送DESCRIBE命令
26 
27   static void subsessionTimeout(void* clientData);
28   void handleSubsessionTimeout();
29 
30 private:
31   friend class ProxyServerMediaSession;
32   friend class ProxyServerMediaSubsession;
33   ProxyServerMediaSession& fOurServerMediaSession;
34   char* fOurURL;
35   Authenticator* fOurAuthenticator;
36   Boolean fStreamRTPOverTCP;
37   class ProxyServerMediaSubsession *fSetupQueueHead, *fSetupQueueTail;
38   unsigned fNumSetupsDone;
39   unsigned fNextDESCRIBEDelay; // in seconds
40   Boolean fServerSupportsGetParameter, fLastCommandWasPLAY;
41   TaskToken fLivenessCommandTask, fDESCRIBECommandTask, fSubsessionTimerTask;
42 };
複製程式碼

  我們接下來看一下建立ProxyServerMediaSession物件的過程

複製程式碼
 1 ProxyServerMediaSession* ProxyServerMediaSession
 2 ::createNew(UsageEnvironment& env, RTSPServer* ourRTSPServer,
 3         char const* inputStreamURL, char const* streamName,
 4         char const* username, char const* password,
 5         portNumBits tunnelOverHTTPPortNum, int verbosityLevel, int socketNumToServer) {
 6   return new ProxyServerMediaSession(env, ourRTSPServer, inputStreamURL, streamName, username, password,
 7                      tunnelOverHTTPPortNum, verbosityLevel, socketNumToServer);
 8 }
 9 
10 
11 ProxyServerMediaSession

            
           

相關推薦

Live555學習()------live555ProxyServer.cpp學習

live555ProxyServer.cpp在live/proxyServer目錄下,這個程式展示瞭如何利用live555來做一個代理伺服器轉發rtsp視訊(例如,IPCamera的視訊)。   首先來看一下main函式 1 int main(int

六天搞懂“深度學習:深度學習

簡單地說,深度學習就是一種採用深度神經網路的機器學習技術,深度神經網路就是一種包含2個或者2個以上隱藏層的多層神經網路。 這裡再次簡單回顧一下“深度學習”的發展歷史: l 第一代神經網路——單層神經網路,在解決機器學習面臨的實際問題時,很快就暴露出它的基本侷限性,單層神經網路只

大數據學習——HDFS常用命令

dfs 放置 shell. 下載 文件 參數 linux系統 文件的 com HDFS文件操作常用命令: (1)列出HDFS下的文件 hadoop dfs -ls <目錄> (2)上傳文件 將Linux系統本地文件上傳到HDFS中 hadoop d

tensorflow源碼學習 -- 同步訓練和異步訓練

stack location warning 可能 oss implicit mov -i ner 同步和異步訓練是由optimizer來決定的。 1. 同步訓練 同步訓練需要使用SyncReplicasOptimizer,參考http

機器學習numpy和matplotlib學習(十

今天來學習矩陣的建立和一些基本運算 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : SundayCoder-俊勇 # @File : numpy7.py import numpy as np # numpy基

python學習(map,filter,reduce函式的使用)

map函式 我們首先使用原始的方法定義幾個功能函式來實現數字的加,減,平方運算。程式碼如下所示: def add(x): return x+1 def reduce(x): return x-1 def pf(x): return x**2 num_1=[1,2

tensorflow原始碼學習 -- 同步訓練和非同步訓練

 同步和非同步訓練是由optimizer來決定的。         1. 同步訓練         同步訓練需要使用SyncReplicasOptimizer,參考https://www.tensorflow.org

oracle 18c 18.3 學習 unplug drop plug pdb

os: centos 7.4 db: oracle 18c(18.3) pdb 的優勢之一就是 unplug、plug,本篇blog介紹下 pdb 的 unplug。 以 pdbdongg 為例,進行 unplug 操作。 unplug pdb 必須先 close,然後再

分散式學習:redis分步式鎖

前言 分散式鎖一般有三種實現方式:1. 資料庫樂觀鎖;2. 基於Redis的分散式鎖;3. 基於ZooKeeper的分散式鎖。本篇部落格將介紹第二種方式,基於Redis實現分散式鎖。雖然網上已經有各種介紹Redis分散式鎖實現的部落格,然而他們的實現卻有著各種各樣的問題,本

Netty框架學習():細說資料容器-ByteBuf

1. 簡介 位元組是網路資料的基本單位。 Java NIO 提供了 ByteBuffer 作為位元組容器,但是這個類使用起來過於複雜,而且也有些繁瑣。Netty使用了即易於使用又具備良好效能的ByteBuf來替代ByteBuffer。 本文將對ByteBuffer做一個簡單的總結。

【redis學習】基於redis的分散式鎖實現

    在單個JVM中,我們可以很方便的用sychronized或者reentrantLock在資源競爭時進行加鎖,保證高併發下資料執行緒安全。但是若是分散式環境下,多個JVM同時對一個資源進行競爭時,我們該如何保證執行緒安全呢?分散式鎖便能實現我們的要求。   &n

演算法學習:佇列

佇列的概念: 佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。佇列中沒有元素時,稱為空佇列。 要著重強調的地方: (1)所有刪除的操

AOP學習種通知

建立AOP建立package命名為com.learn.aop(根據實際情況修改)配置AOP,新建ExecutionAOP,內容如下@Aspect @Component public class ExecutionAop { // 切點範圍 @Pointcut("execution(* com

vue的原始碼學習——7.資料驅動(update)

1. 介紹         版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。  &nb

vue的原始碼學習——6.資料驅動(createElement)

1. 介紹       版本:2.5.17。         我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。   &nbs

vue的原始碼學習——5.資料驅動(Virtual DOM)

1. 介紹       版本:2.5.17。        我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。    &nbs

vue的原始碼學習——4.資料驅動(render)

介紹         版本:2.5.17。        我們使用vue-vli建立基於Runtime+Compiler的v

vue的原始碼學習——3.資料驅動(Vue 例項掛載的實現)

介紹         版本:2.5.17。        我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。        

vue的原始碼學習——2.資料驅動(new Vue發生了什麼)

介紹         版本:2.5.17。        我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。       小小的de

vue的原始碼學習——1.資料驅動的簡介

介紹         版本:2.5.17 資料驅動       Vue.js 一個核心思想是資料驅動。所謂資料驅動,是指檢視是由資料驅動生成的,我們對檢視的修改,不會直接操作 DOM,而