1. 程式人生 > >流媒體學習筆記2(live555中的Session)

流媒體學習筆記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 機制下次講了....