畢業課題打算從最原始的地方做起。好吧,那就從live555採集轉發本地攝像頭視頻開始吧。首先從源碼開始吧,今天看了點liveMedia中的session,這裏做個總結。
整個源碼中的繼承順序爲
H264VideoFileServerMediaSubsession::FileServerMediaSubsession:: OnDemandServerMediaSubsession::ServerMediaSubsession ::Medium
ServerMediaSession ::Medium
Medium:抽象了基本的接口,包括環境,task和媒體名和媒體查找函數以及一些輔助函數,幾乎所有得處理單元都繼承自Medium類。
ServerMediaSession:添加了子會話鏈表,SDP(Session Description Protocol 會話協議)描述以及一些媒體相關處理函數。其中靜態成員函數lookupByName可以從session鏈表中找到對應的媒體
ServerMediaSubsession:定義了指向ServerMediaSession的父指針,指向下個一個對象的指針。該媒體的SDP信息,該媒體的讀取定位函數等。
ServerMediaSubsessionIterator:一個迭代器類,用來遍歷subsession鏈表。
程序爲容器中的每一個流建立一個subseesion,然後通過 ServerMediaSession::addSubsession 函數,將subsession 加入到ServerMediaSession。具體代碼如下
ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {
if (subsession->fParentSession != NULL) return False; // it's already used
if (fSubsessionsTail == NULL) {
fSubsessionsHead = subsession;
} else {
fSubsessionsTail->fNext = subsession;
}
fSubsessionsTail = subsession;
subsession->fParentSession = this;
subsession->fTrackNumber = ++fSubsessionCounter;
return True;
}
ServerMediaSession與流名字所指定與文件是沒有關係的,也就是說它不會操作文件,而文件的操作是放在 ServerMediaSubsession中的。具體應該是在ServerMediaSubsession的sdpLines()函數中打開。sdpLines()是在派生類OnDemandServerMediaSubsession中實現,代碼如下
char const*
OnDemandServerMediaSubsession::sdpLines() {
if (fSDPLines == NULL) {
// We need to construct a set of SDP lines that describe this
// subsession (as a unicast stream). To do so, we first create
// dummy (unused) source and "RTPSink" objects,
// whose parameters we use for the SDP lines:
//構建一套SDP行來描述這個Subsession。創建未使用的source和RTPSink對象
unsigned estBitrate;
FramedSource* inputSource = createNewStreamSource(0, estBitrate);
if (inputSource == NULL) return NULL; // file not found
struct in_addr dummyAddr;
dummyAddr.s_addr = 0;
Groupsock dummyGroupsock(envir(), dummyAddr, 0, 0);
unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
RTPSink* dummyRTPSink
= createNewRTPSink(&dummyGroupsock, rtpPayloadType, inputSource);
//Sink中的到SDP
setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);
Medium::close(dummyRTPSink);
closeStreamSource(inputSource);
}
return fSDPLines;
}
OnDemandServerMediaSubsession類繼承 ServerMediaSubsession類,OnDemand實際上用來描述需求,Subsession表示只是一種類型媒體的會話。所以可以通過繼承OnDemandServerMediaSubsession來實現不同類型的媒體轉發,比如OnDemandServerMediaSubsession的兩個子類
1.ProxyServerMediaSubsession:用於實現一個單播RTSP的服務器代理另一個RTSP流
2.FileServerMediaSubsession:虛基類,不同類型媒體通過繼承它來實現對於需求,比如音頻,視頻,比如h263,h264編解碼。FileServerMediaSubsession類只是添加了兩個成員變量 fFileName,fFileSize用來描述轉發的媒體。構造函數如下:
FileServerMediaSubsession
::FileServerMediaSubsession(UsageEnvironment& env, char const* fileName,
Boolean reuseFirstSource)
: OnDemandServerMediaSubsession(env, reuseFirstSource),
fFileSize(0) {
fFileName = strDup(fileName);
}
而H264VideoFileServerMediaSubsession類繼承了FileServerMediaSubsession,它將構造函數放入protected中,並用靜態的成員方法createNew來實例對象,這是設計模式的工廠模式。createNew(env, fileName, reuseSource)是每一種類型的流媒體自己的subsession實現。如果 reuseSource == True,則多個客戶端共享 StreamState 對象,也即意味着多個客戶端播放同一流文件時,服務器端使用同一FileSource 對象對該流文件進行數據讀取操作,使用同一RTPSink 對象對流數據進行封裝發送操作,這時,多個客戶端播放的內容就是同步的,這適合於實時數據播放;如果reuseSource==False,則對每個客戶端,服務器端單獨維護一個StreamState 對象進行播放操作,此時,多個客戶端播放的內容就是獨立的,這適合於數據回放。
當然,單看session看不出什麼門道來,那看下面
SDP消息組裝過程:
ServerMediaSession負責產生會話公共描述信息,子會話描述由H264VideoFileServerMediaSubsession產生。 H264VideoFileServerMediaSubsession在其父類成員函數OnDemandServerMediaSubsession::sdpLines()中生成會話描述信息。在sdpLines()實現裏面,創建一個FramedSource(具體實現類爲H264VideoStreamFramer)和RTPSink(具體實現類爲H264VideoRTPSink),最後調用OnDemandServerMediaSubsession::setSDPLinesFromRTPSink(...)成員函數生成子會話描述。
好吧,這樣思路基本清楚了點,以後要寫自己需要的編解碼類,那就繼承OnDemandServerMediaSubsession吧。對於souce-sink 機制下次講了....