流媒體學習筆記2(live555中的Session)
畢業課題打算從最原始的地方做起。好吧,那就從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 機制下次講了....