live555
Real Time Streaming Protocol或 者RTSP(實時流媒體協議),是由Real network 和Netscape共同提出的如何有效地在IP網絡上傳輸流媒體數據的應用層協議。RTSP提供一種可擴展的框架,使能夠提供能控制的,按需傳輸實時數 據,比如音頻和視頻文件。源數據可以包括現場數據的反饋和存貯的文件。rtsp對流媒體提供了諸如暫停,快進等控制,而它本身并不傳輸數據,rtsp作用 相當于流媒體服務器的遠程控制。傳輸數據可以通過傳輸層的tcp,udp協議,rtsp也提供了基于rtp傳輸機制的一些有效的方法。
?????? RTSP協議是一個非常類似HTTP協議的流控制協議。它們都使用純文本來發送信息,而且rtsp協議的語法 也和HTTP類似。Rtsp一開始這樣設計,也是為了能夠兼容使用以前寫的HTTP協議分析代 碼 。這是個好消息。
它們主要的區別是HTTP協議是沒有狀態的, http協議在發送一個命令后,連接會斷 開,而且命令之間沒有依賴性。不同的是RTSP的命令需要知道現在正處于一個什么狀態,也就是說rtsp的命令總是 按照順序來發送,某個命令總在另外一個命令之前要發送。Rtsp不管處于什么狀態都不會去斷掉連接。
HTTP 協議默 認使用80端口,而RTSP 默認使用554端口。如果一些服務器因為某些安全的原因而封掉了這個端口,那代理和防火墻可能不讓RTSP消息通 過,需要管理員去放開554端口,而使得rtsp協議能通過。
基本類
在Mplayer中如果使用live選項進行編譯,則需要安裝live555庫。live555實現RTP/RTSP功能。
使用環境(usageEnvironment):UsageEnvironment和TaskScheduler類用在調度不同事件。還有 HashTable類定義,這些都是抽象基類。在使用過程中可以利用環境的特定功能。
groupsock:封裝網絡接口和socket。特別是還封裝了multicast應用,這個multicast并不是Mbone意義的 multicast,而是將多個寫而不讀socket組合處理,用來模擬multicast。
liveMedia:定義一個類棧,根類是Medium類-不同的流類型和編解碼器。
BasicUsageEnvironment:定義一個usageEnvironment的實現, 這個里面除了有一個TaskScheduler以外,都是一些說明性的東西。TaskSheduler里面是一些調度相關的函數,其中 doEventLoop是主控函數,定義為純虛函數。
testProgs:目錄下是一個簡單的實現,使用了BasicUsageEnvironment來展示如何使用這些庫。
BasicTaskScheduler0:主要成員變量有fdelayQueue, fReadHandlers, fLastHandledSocketNum;這里主要的處理函數是 scheduleDelayedTask , 通過定時器觸發一個時間,比如RTCP包的發送等。
BasicTaskScheduler:中又添加了 fMaxNumSockets 和fReadSet。 其中freadHandlers類中定義一個鏈表,將所有的句柄,對應的處理函數 和處理函數參數作鏈接成一個鏈表,通過成員函數assignHandler管理。這里面主要的函數是 turnOnBackgroundReadHandling ,這個函數把句柄和處理函數注冊到 select中,這樣可以完成數據包的等待及其處理操作。
MediaSession類中定義了一個mediaSubSession鏈表;MediaSubSession中又SessionId,服務端口 號,rtp/rtcp ChannelId和MediaSink指針,等等一些參數信息。
H.264
1. 基類
Medium:定義媒體名字和環境信息,處理任務名,靜態函數close和lookupByName和一些虛函數來指明該medium類型,是媒體幀的基 類。
MediaSource類:實現基類中medium類型的虛函數,
FramedSoruce:定義了getNextFrame和doGetNextFrame純虛函數是使用到的一些變量。
2. 相關類
H264VideoStreamFramer;H264VideoFileSink:H264VideoRTPSource:H264VideoRTPSinik
H.264利用NAL封裝數據,通過RTP傳輸數據包。相關的處理在RTPSource/Sink中。
Mplayer
從RTSP或者SIP中渠道SDP描述,然后調用Live555中的mediaSession類創建Session。通過成員函數 initializeWithSDP分析SDP描述。
OpenRTSP
1. Client
1. 創建TaskScheduler和UsageEnvironment實例;
2. 調用createClient創建media實例;
在openRTSP.c中,main完成配置以后,開始如下循環:
startPlayingStreams();
env->taskScheduler().doEventLoop(); // does not return
在BasicTaskScheduler0類中,定義為while(1) SingleStep();
SingleStep的處理是通過select監聽一組句柄,這組句柄通過iter組成的鏈表串接起來,對每個句柄有處理函數,如果有句柄上有數據,那么 調用對應的處理函數。
2. liev555mediaserver
創建過程:
1. 創建TaskScheduler:這里僅僅初始化一個fdset并且socket數目初始化為0。
2. 以TaskScheduler為參數創建UsageEnvironment對象。
3. 以前述environment和服務端口號(554/8554)以及用戶認證對象為參數創建 RTSPServer對象,這里是用子類 DynamicRTSPServer 的創建函數創建。在createNew成員函數中建立socket,分配發送緩沖區,和創建RTSPServer對象。這里通過 turnOnBackgroundReadHandling函數將要處理的句柄和處理函數關聯起來。
4. 執行env->taskScheduler().doEventLoop();
從RFC2326中可以看出通常的交互流程是發送describe,然后發送setup,再play。所以以請求MPG多媒體URI為例分析如下:
C->M: DESCRIBE rtsp://foo/twister RTSP/1.0
CSeq: 1
M->C: RTSP/1.0 200 OK
CSeq: 1
Content-Type: application/sdp
Content-Length: 164
v=0
o=- 2890844256 2890842807 IN IP4 172.16.2.93
s=RTSP Session
i=An Example of RTSP Session Usage
a=control:rtsp://foo/twister
t=0 0
m=audio 0 RTP/AVP 0
a=control:rtsp://foo/twister/audio
m=video 0 RTP/AVP 26
a=control:rtsp://foo/twister/video
C->M: SETUP rtsp://foo/twister/audio RTSP/1.0
CSeq: 2
Transport: RTP/AVP;unicast;client_port=8000-8001
M->C: RTSP/1.0 200 OK
CSeq: 2
Transport: RTP/AVP;unicast;client_port=8000-8001;
server_port=9000-9001
Session: 12345678
C->M: SETUP rtsp://foo/twister/video RTSP/1.0
CSeq: 3
Transport: RTP/AVP;unicast;client_port=8002-8003
Session: 12345678
M->C: RTSP/1.0 200 OK
CSeq: 3
Transport: RTP/AVP;unicast;client_port=8002-8003;
server_port=9004-9005
Session: 12345678
C->M: PLAY rtsp://foo/twister RTSP/1.0
CSeq: 4
Range: npt=0-
Session: 12345678
M->C: RTSP/1.0 200 OK
CSeq: 4
Session: 12345678
RTP-Info: url=rtsp://foo/twister/video;
seq=9810092;rtptime=3450012
C->M: PAUSE rtsp://foo/twister/video RTSP/1.0
CSeq: 5
Session: 12345678
M->C: RTSP/1.0 460 Only aggregate operation allowed
CSeq: 5
C->M: PAUSE rtsp://foo/twister RTSP/1.0
CSeq: 6
Session: 12345678
M->C: RTSP/1.0 200 OK
CSeq: 6
Session: 12345678
C->M: SETUP rtsp://foo/twister RTSP/1.0
CSeq: 7
Transport: RTP/AVP;unicast;client_port=10000
M->C: RTSP/1.0 459 Aggregate operation not allowed
CSeq: 7
函數 handleCmd_DESCRIBE 處理describe請求,生成含SDP信息響應消息,lookupServerMediaSession函數是虛函數,在創建RTSPServer對象 時,用的是子類DynamicRTSPServer的創建函數,所以上述函數使用的是類DynamicRTSPServer中的定義。函數打開,并且分析 流媒體文件。createNewSMS根據請求的文件后綴來調用對應的處理函數。如果是MPG,那么創建ServerMediaSession對象并添加 到RTSPserver中,這個對象是可以通過Hash類定位的。然后創建一個Mpeg1or2FileServerDemux對象demux,然后將創 建demux對象中的音頻,視頻子會話對象并且通過函數 addSubsession 將他們添加到ServerMediaSession中鏈表中。響應函數會依次調用會話中注冊的子會話的 sdpLines函 數以取得SDP信息。sdpLines是一個純虛函數,如果URI指定的MPG文件,那么sdpLines 函數在 OnDemandServerMediaSubsession 中定義實現。此時,對每個子會話那么會創建一個sink對象。對應MPG文件,在 MPEG1or2DemuxedServerMediaSubsession類定義的createNewRTPSink 會創建對象:
音頻:MPEG1or2AudioRTPSink::AudioRTPSink::MultiFramedRTPSink::RTPSink::MediaSink::Media
視頻:MPEG1or2VideoRTPSink::VideoRTPSink::MultiFramedRTPSink::RTPSink::MediaSink::Media
而SDP信息的獲取在函數 setSDPLinesFromRTPSink 中處理。(AudioRTPSink指定媒體類型,時間標簽頻率和載荷格式名“MPA”,視頻名字是“MPV")
這里類結構 MPEG1or2DemuxedServerMediaSubsession ::OnDemandServerMediaSubsession ::ServerMediaSubsession ::Medium
函數 handleCmd_SETUP處理setup請求,
MPEG1or2Demux 類是mpeg相關的一個主要類,創建該類時會分析媒體文件。該類定義中有個數組,OutputDescriptor fOutput[256];MPEG1or2Demux的構造函數中初始化。
?????????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????? 在windows使用vs2008編譯live555
1.live555 源代碼簡介
liveMedia項目的源代碼包括四個基本的庫,各種測試代碼以及IVE555 Media Server。
四 個基本的庫分別是 UsageEnvironment&TaskScheduler,groupsock,liveMedia,BasicUsageEnvironment。
UsageEnvironment 和TaskScheduler類用于事件的調度,實現異步讀取事件的句柄的設置以及錯誤信息的輸出。另外,還有一個HashTable類定義了一個通用的 hash表,其它代碼要用到這個表。這些都是抽象類,在應用程序中基于這些類實現自己的子類。
groupsock類是對網絡接口的封裝, 用于收發數據包。正如名字本身,Groupsock主要是面向多播數據的收發的,它也同時支持單播數據的收發。Groupsock定義了兩個構造函數
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
Port port, u_int8_t ttl);
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
Port port);
前 者是用于SIM(source-independent multicast)組,后者用于SSM(source-specific multicast)組。groupsock庫中的Helper例程提供了讀寫socket等函數,并且屏蔽了不同的操作系統之間的區別,這是在 GroupsockHelper.cpp文件中實現的。
liveMedia庫中有一系列類,基類是Medium,這些類針對不同的流媒體 類型和編碼。
各種測試代碼在testProgram目錄下,比如openRTSP等,這些代碼有助于理解liveMedia的應用。
LIVE555 Media Server是一個純粹的RTSP服務器。支持多種格式的媒體文件:
* TS流文件,擴展名ts。
* PS流文件,擴展名mpg。
* MPEG-4視頻基本流文件,擴展名m4e。
* MP3文件,擴展名mp3。
* WAV文件(PCM),擴展名wav。
* AMR音頻文件,擴展名.amr。
* AAC文件,ADTS格式,擴展名aac。
2.在windows下編譯live555
(1).下載 live555,http://www.live555.com/
(2). 編輯 win32config,TOOLS32=C:\Program Files\Microsoft Visual Studio 9.0\VC
即保證這個路徑是你的Vs2008 路徑。
(3). 編輯"LINK_OPTS_0 = $(linkdebug) msvcirt.lib" in win32config to "LINK_OPTS_0 = $(linkdebug) msvcrt.lib",由于編譯器所要LINK的運行庫不一樣了
(4). 修改groupsock/makefile.head,替換"INCLUDES = -Iinclude -I../UsageEnvironment/include" 為"INCLUDES = -Iinclude -I../UsageEnvironment/include -DNO_STRSTREAM".
(5). 運行genWindowsMakefiles.cmd 生成VS能夠編譯的*.mak文件
(6). 將以下內容保存為live/compile.bat
call "C:\Program Files\Microsoft Visual Studio 9\VC\vcvarsall.bat"
cd liveMedia
nmake /B -f liveMedia.mak
cd ../groupsock
nmake /B -f groupsock.mak
cd ../UsageEnvironment
nmake /B -f UsageEnvironment.mak
cd ../BasicUsageEnvironment
nmake /B -f BasicUsageEnvironment.mak
cd ../testProgs
nmake /B -f testProgs.mak
cd ../mediaServer
nmake /B -f mediaServer.mak
有 關這一點來說,建議把vs2008的編譯環境加入到環境變量中,那么以后需用命令行編譯程序都可行了,可以參考 http://blog.chinaunix.net/u3/94873/showart_1907792.html的前部分設置VS2008的環境設 置。
(7). 在命令行下運行compile.bat,就會看到很多編譯過程出現在CMD中了。
以上的編譯并不是 DEBUG模式,要調試時,應先在win32config加入一行"NODEBUG=1"
進行編譯后,可以在要調試的程序路徑下輸入 如:C:\works\VCCode\video\live555-latest\live\testProgs>devenv openRTSP.exe,這樣就會把相關的調試環境搭建起來進行調試了。
liveMedia項目的源代碼包括四個基本的庫,各種測試代碼以及IVE555 Media Server。
四個基本的庫分別是 UsageEnvironment&TaskScheduler,groupsock,liveMedia,BasicUsageEnvironment。
UsageEnvironment 和TaskScheduler類用于事件的調度,實現異步讀取事件的句柄的設置以及錯誤信息的輸出。另外,還有一個HashTable類定義了一個通用的 hash表,其它代碼要用到這個表。這些都是抽象類,在應用程序中基于這些類實現自己的子類。
groupsock類是對網絡接口的封裝, 用于收發數據包。正如名字本身,Groupsock主要是面向多播數據的收發的,它也同時支持單播數據的收發。Groupsock定義了兩個構造函數
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
Port port, u_int8_t ttl);
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
Port port);
前者是用于SIM(source-independent multicast)組,后者用于SSM(source-specific multicast)組。groupsock庫中的Helper例程提供了讀寫socket等函數,并且屏蔽了不同的操作系統之間的區別,這是在 GroupsockHelper.cpp文件中實現的。
liveMedia庫中有一系列類,基類是Medium,這些類針對不同的流媒體 類型和編碼。
各種測試代碼在testProgram目錄下,比如openRTSP等,這些代碼有助于理解liveMedia的應用。
LIVE555 Media Server是一個純粹的RTSP服務器。支持多種格式的媒體文件:
* TS流文件,擴展名ts。
* PS流文件,擴展名mpg。
* MPEG-4視頻基本流文件,擴展名m4e。
* MP3文件,擴展名mp3。
* WAV文件(PCM),擴展名wav。
* AMR音頻文件,擴展名.amr。
* AAC文件,ADTS格式,擴展名aac。
用live555開發應用程序
基于 liveMedia的程序,需要通過繼承UsageEnvironment抽象類和TaskScheduler抽象類,定義相應的類來處理事件調度,數據 讀寫以及錯誤處理。live項目的源代碼里有這些類的一個實現,這就是“BasicUsageEnvironment”庫。 BasicUsageEnvironment主要是針對簡單的控制臺應用程序,利用select實現事件獲取和處理。這個庫利用Unix或者 Windows的控制臺作為輸入輸出,處于應用程序原形或者調試的目的,可以用這個庫用戶可以開發傳統的運行與控制臺的應用。
通過使用 自定義的“UsageEnvironment”和“TaskScheduler”抽象類的子類,這些應用程序就可以在特定的環境中運行,不需要做過多的修 改。需要指出的是在圖形環境(GUI toolkit)下,抽象類 TaskScheduler 的子類在實現 doEventLoop()的時候應該與圖形環境自己的事件處理框架集成。
先來熟悉在liveMedia庫中Source,Sink以及 Filter等概念。Sink就是消費數據的對象,比如把接收到的數據存儲到文件,這個文件就是一個Sink。Source就是生產數據的對象,比如通過 RTP讀取數據。數據流經過多個'source'和'sink's,下面是一個示例:
'source1' -> 'source2' (a filter) -> 'source3' (a filter) -> 'sink'
從其它 Source接收數據的source也叫做"filters"。Module是一個sink或者一個filter。
數據接收的終點是 Sink類,MediaSink是所有Sink類的基類。MediaSink的定義如下:
class MediaSink: public Medium {
public:
static Boolean lookupByName(UsageEnvironment& env, char const* sinkName,
MediaSink*& resultSink);
typedef void (afterPlayingFunc)(void* clientData);
Boolean startPlaying(MediaSource& source,
afterPlayingFunc* afterFunc,
void* afterClientData);
virtual void stopPlaying();
// Test for specific types of sink:
virtual Boolean isRTPSink() const;
FramedSource* source() const {return fSource;}
protected:
MediaSink(UsageEnvironment& env); // abstract base class
virtual ~MediaSink();
virtual Boolean sourceIsCompatibleWithUs(MediaSource& source);
// called by startPlaying()
virtual Boolean continuePlaying() = 0;
// called by startPlaying()
static void onSourceClosure(void* clientData);
// should be called (on ourselves) by continuePlaying() when it
// discovers that the source we're playing from has closed.
FramedSource* fSource;
private:
// redefined virtual functions:
virtual Boolean isSink() const;
private:
// The following fields are used when we're being played:
afterPlayingFunc* fAfterFunc;
void* fAfterClientData;
};
Sink 類實現對數據的處理是通過實現純虛函數continuePlaying(),通常情況下continuePlaying調用 fSource->getNextFrame來為Source設置數據緩沖區,處理數據的回調函數等,fSource是MediaSink的類型為 FramedSource*的類成員;
基于liveMedia的應用程序的控制流程如下:
應用程序是事件驅動的,使用如 下方式的循環
while (1) {
通過查找讀網絡句柄的列表和延遲隊列(delay queue)來發現需要完成的任務
完成這個任務
}
對于每個sink,在進入這個循環之 前,應用程序通常調用下面的方法來啟動需要做的生成任務:
someSinkObject->startPlaying();
任何時候,一個Module需要獲取數據都通過調用剛好在它之前 的那個Module的FramedSource::getNextFrame()方法。這是通過純虛函數FramedSource: oGetNextFrame() 實現的,每一個Source module都有相應的實現。
Each 'source' module's implementation of "doGetNextFrame()" works by arranging for an 'after getting' function to be called (from an event handler) when new data becomes available for the caller.
注意,任何應用程序都要處理從'sources'到'sinks' 的數據流,但是并非每個這樣的數據流都與從網絡接口收發數據相對應。
比如,一個服務器應用程序發送RTP數據包的時候用到一個或多 個"RTPSink" modules。這些"RTPSink" modules以別的方式接收數據,通常是文件 "*Source" modules (e.g., to read data from a file), and, as a side effect, transmit RTP packets.
一個簡單的RTSP客戶端程序
在另一個文章里,給出了這個簡單的客戶端的 程序的代碼,可以通過修改Makefile來裁剪liveMedia,使得這個客戶端最小化。此客戶端已經正常運行。
首先是OPTION
然 后是DESCRIBE
建立Media Session,調用的函數是 MediaSession::createNew,在文件liveMedia/MediaSession.cpp中實現。
為這個Media Session建立RTPSource,這是通過調用 MediaSubsession::initiate來實現的的,這個方法在liveMedia/MediaSession.cpp中實現。
在然 后是SETUP
最后是PLAY
rtp數據的句 柄:MultiFramedRTPSource::networkReadHandler 在liveMedia/MultiFramedRTPSource.cpp中
rtcp數據處理的句 柄:RTCPInstance::incomingReportHandler 在liveMedia/RTCP.cpp中
rtp數據處 理的句柄的設置:MultiFramedRTPSource: oGetNextFrame 在liveMedia/MultiFramedRTPSource.cpp中, 被FileSink::continuePlaying調用在FileSink.cpp中.
rtcp數據處理的句柄設置 fRTCPInstance = RTCPInstance::createNew 在/liveMedia/MediaSession.cpp中調用,
createNew 調用了構造函數RTCPInstance::RTCPInstance,這個構造函數有如下調用
TaskScheduler::BackgroundHandlerProc* handler = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler;
*********************************************************************************************************************
通 過分析live庫提供的例子程序OpenRTSP,可以清晰地了解客戶端接收來自網絡上媒體數據的過程。注意,RTP協議和RTCP協議接收的數據分別是 視音頻數據和發送/接收狀況的相關信息,其中,RTP協議只負責接收數據,而RTCP協議除了接收服務器的消息之外,還要向服務器反饋。
A.??????? main函數流程
main(int argc,char *argv[])
{
1.??????????? 創建BasicTaskScheduler對象
2.??????????? 創建BisicUsageEnvironment對象
3.??????????? 分析argv參數,(最簡單的用法是:openRTSP rtsp://172.16.24.240/mpeg4video.mp4)以便在下面設置一些相關參數
4.??????????? 創建RTSPClient對象
5.??????????? 由RTSPClient對象向服務器發送OPTION消息并接受回應
6.??????????? 產生SDPDescription字符串(由RTSPClient對象向服務器發送DESCRIBE消息并接受回應,根據回應的信息產生 SDPDescription字符串,其中包括視音頻數據的協議和解碼器類型)
7.??????????? 創建MediaSession對象(根據SDPDescription在MediaSession中創建和初始化MediaSubSession子會話對 象)
8.??????????? while循環中配置所有子會話對象(為每個子會話創建RTPSource和RTCPInstance對象,并創建兩個GroupSock對象,分別對應 RTPSource和RTCPInstance對象,把在每個GroupSock對象中創建的socket描述符置入 BasicTaskScheduler::fReadSet中,RTPSource對象的創建的依據是SDPDescription,例如對于MPEG4 文件來說,視音頻RTPSource分別對應MPEG4ESVideoRTPSource和MPEG4GenericRTPSource對象。 RTCPInstance對象在構造函數中完成將Socket描述符、處理接收RTCP數據的函數 (RTCPInstance::incomingReportHandler)以及RTCPInstance本身三者綁定在一個 HandlerDescriptor對象中,并置入BasicTaskScheduler::fReadHandler中。完成綁定后會向服務器發送一條 消息。)
9.??????????? 由RTSPClient對象向服務器發送SETUP消息并接受回應。
10.??????? while循環中為每個子會話創建接收器(FileSink對象),在FileSink對象中根據子會話的codec等屬性缺省產生記錄視音頻數據的文件 名,視音頻文件名分別為:video-MP4V-ES-1和audio-MPEG4-GENERIC-2,無后綴名
11.??????? while循環中為每個子會話的視音頻數據裝配相應的接收函數,將每個子會話中的RTPSource中的GroupSock對象中的SOCKET描述符, 置入BasicTaskScheduler::fReadSet中,并將描述符、處理接收RTP數據的函數 (MultiFramedRTPSource::networkReadHandler)以及RTPSource本身三者綁定在一個 HandlerDescriptor對象中,并置入BasicTaskScheduler::fReadHandler中,并將FileSink的緩沖區 和包含寫入文件操作的一個函數指針配置給RTPSource對象,這個緩沖區將會在networkReadHandler中接收來自網絡的視音頻數據(分 析和去掉RTP包頭的工作由RTPSource完成),而這個函數指針在networkReadHandler中被調用以完成將緩沖區中的數據寫入文件。
12.??????? 由RTSPClient對象向服務器發送PLAY消息并接受回應。
13.??????? 進入while循環,調用BasicTaskScheduler::SingleStep()函數接受數據,直到服務器發送TREADOWN消息給客戶 端,客戶端接收到該消息后釋放資源,程序退出
總結
- 上一篇: 很强大的FFMPEG API Docum
- 下一篇: 联想如何在桌面显示计算机和网络图标,联想