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,而