live555 源码分析:ServerMediaSession
在 live555 中,用一個(gè) ServerMediaSession 表示流媒體會(huì)話(huà),它連接了 RTSPServer 和下層流媒體傳輸邏輯。ServerMediaSession 和 ServerMediaSubsession 共同用于執(zhí)行底層流媒體傳輸和狀態(tài)維護(hù)。而 ServerMediaSession 則是在 GenericMediaServer 中,通過(guò) HashTable 來(lái)維護(hù)的。
在分析 live555 中處理 DESCRIBE 請(qǐng)求的代碼( live555 源碼分析:DESCRIBE 的處理)時(shí),我們?cè)吹?RTSPServer::RTSPClientConnection 通過(guò)它來(lái)產(chǎn)生 SDP 消息。本文更詳細(xì)地分析這個(gè)類(lèi)的定義和實(shí)現(xiàn)。
ServerMediaSession
首先看一下 ServerMediaSession 的定義:
class ServerMediaSubsession; // forwardclass ServerMediaSession: public Medium { public:static ServerMediaSession* createNew(UsageEnvironment& env,char const* streamName = NULL,char const* info = NULL,char const* description = NULL,Boolean isSSM = False,char const* miscSDPLines = NULL);static Boolean lookupByName(UsageEnvironment& env,char const* mediumName,ServerMediaSession*& resultSession);char* generateSDPDescription(); // based on the entire session// Note: The caller is responsible for freeing the returned stringchar const* streamName() const { return fStreamName; }Boolean addSubsession(ServerMediaSubsession* subsession);unsigned numSubsessions() const { return fSubsessionCounter; }void testScaleFactor(float& scale); // sets "scale" to the actual supported scalefloat duration() const;// a result == 0 means an unbounded session (the default)// a result < 0 means: subsession durations differ; the result is -(the largest).// a result > 0 means: this is the duration of a bounded sessionvirtual void noteLiveness();// called whenever a client - accessing this media - notes liveness.// The default implementation does nothing, but subclasses can redefine this - e.g., if you// want to remove long-unused "ServerMediaSession"s from the server.unsigned referenceCount() const { return fReferenceCount; }void incrementReferenceCount() { ++fReferenceCount; }void decrementReferenceCount() { if (fReferenceCount > 0) --fReferenceCount; }Boolean& deleteWhenUnreferenced() { return fDeleteWhenUnreferenced; }void deleteAllSubsessions();// Removes and deletes all subsessions added by "addSubsession()", returning us to an 'empty' state// Note: If you have already added this "ServerMediaSession" to a "RTSPServer" then, before calling this function,// you must first close any client connections that use it,// by calling "RTSPServer::closeAllClientSessionsForServerMediaSession()".protected:ServerMediaSession(UsageEnvironment& env, char const* streamName,char const* info, char const* description,Boolean isSSM, char const* miscSDPLines);// called only by "createNew()"virtual ~ServerMediaSession();private: // redefined virtual functionsvirtual Boolean isServerMediaSession() const;private:Boolean fIsSSM;// Linkage fields:friend class ServerMediaSubsessionIterator;ServerMediaSubsession* fSubsessionsHead;ServerMediaSubsession* fSubsessionsTail;unsigned fSubsessionCounter;char* fStreamName;char* fInfoSDPString;char* fDescriptionSDPString;char* fMiscSDPLines;struct timeval fCreationTime;unsigned fReferenceCount;Boolean fDeleteWhenUnreferenced; };由這個(gè)定義,不難理解,它主要是 ServerMediaSubsession 的容器,并通過(guò)一個(gè)單向鏈表來(lái)維護(hù)它們。并提供了需要作用于整個(gè)流媒體會(huì)話(huà)所有子會(huì)話(huà)的操作,如產(chǎn)生 SDP 消息的 generateSDPDescription() 和設(shè)置播放快慢的 testScaleFactor()。
ServerMediaSession 對(duì)象的創(chuàng)建,就像 live555 中許多類(lèi)的創(chuàng)建那樣,通過(guò)一個(gè)靜態(tài)的創(chuàng)建函數(shù) createNew() 實(shí)現(xiàn),該函數(shù)定義如下:
ServerMediaSession* ServerMediaSession ::createNew(UsageEnvironment& env,char const* streamName, char const* info,char const* description, Boolean isSSM, char const* miscSDPLines) {return new ServerMediaSession(env, streamName, info, description,isSSM, miscSDPLines); } . . . . . . static char const* const libNameStr = "LIVE555 Streaming Media v"; char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;ServerMediaSession::ServerMediaSession(UsageEnvironment& env,char const* streamName,char const* info,char const* description,Boolean isSSM, char const* miscSDPLines): Medium(env), fIsSSM(isSSM), fSubsessionsHead(NULL),fSubsessionsTail(NULL), fSubsessionCounter(0),fReferenceCount(0), fDeleteWhenUnreferenced(False) {fStreamName = strDup(streamName == NULL ? "" : streamName);char* libNamePlusVersionStr = NULL; // by defaultif (info == NULL || description == NULL) {libNamePlusVersionStr = new char[strlen(libNameStr) + strlen(libVersionStr) + 1];sprintf(libNamePlusVersionStr, "%s%s", libNameStr, libVersionStr);}fInfoSDPString = strDup(info == NULL ? libNamePlusVersionStr : info);fDescriptionSDPString = strDup(description == NULL ? libNamePlusVersionStr : description);delete[] libNamePlusVersionStr;fMiscSDPLines = strDup(miscSDPLines == NULL ? "" : miscSDPLines);gettimeofday(&fCreationTime, NULL); }ServerMediaSession::~ServerMediaSession() {deleteAllSubsessions();delete[] fStreamName;delete[] fInfoSDPString;delete[] fDescriptionSDPString;delete[] fMiscSDPLines; }每個(gè) ServerMediaSession 都由一個(gè)字符串形式的 streamName 標(biāo)識(shí),這個(gè)標(biāo)識(shí)也是在 GenericMediaServer 中,通過(guò) HashTable 來(lái)維護(hù)時(shí),所用的 key。streamName 由調(diào)用者在創(chuàng)建時(shí)傳入。對(duì)于 “LIVE555 Media Server” 而言,這個(gè)值為資源的路徑。
調(diào)用者還可以在創(chuàng)建時(shí)傳入 info 和 description 提供更多關(guān)于這個(gè)會(huì)話(huà)的描述信息。對(duì)于 “LIVE555 Media Server” 而言,info 同樣為資源的路徑,但 description 為含有流媒體類(lèi)型的一個(gè)字符串,格式為 "$MediaType, streamed by the LIVE555 Media Server",比如對(duì)于 H.264 視頻為 "H.264 Video, streamed by the LIVE555 Media Server"。
創(chuàng)建對(duì)象時(shí),主要是用調(diào)用者傳入的值來(lái)初始化狀態(tài)。
像許多其它 Medium 的子類(lèi)一樣,ServerMediaSession 也提供了一個(gè)對(duì)象查找函數(shù):
Boolean ServerMediaSession ::lookupByName(UsageEnvironment& env, char const* mediumName,ServerMediaSession*& resultSession) {resultSession = NULL; // unless we succeedMedium* medium;if (!Medium::lookupByName(env, mediumName, medium)) return False;if (!medium->isServerMediaSession()) {env.setResultMsg(mediumName, " is not a 'ServerMediaSession' object");return False;}resultSession = (ServerMediaSession*)medium;return True; }作為 ServerMediaSubsession 的容器,ServerMediaSession 還提供了對(duì) ServerMediaSubsession 的添加、管理等操作:
Boolean ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {if (subsession->fParentSession != NULL) return False; // it's already usedif (fSubsessionsTail == NULL) {fSubsessionsHead = subsession;} else {fSubsessionsTail->fNext = subsession;}fSubsessionsTail = subsession;subsession->fParentSession = this;subsession->fTrackNumber = ++fSubsessionCounter;return True; } . . . . . . void ServerMediaSession::noteLiveness() {// default implementation: do nothing }void ServerMediaSession::deleteAllSubsessions() {Medium::close(fSubsessionsHead);fSubsessionsHead = fSubsessionsTail = NULL;fSubsessionCounter = 0; }Boolean ServerMediaSession::isServerMediaSession() const {return True; } . . . . . .向 ServerMediaSession 中添加子會(huì)話(huà)時(shí),新加入的子會(huì)話(huà)總是會(huì)被放在單向鏈表的表頭。添加子會(huì)話(huà)的時(shí)候,會(huì)根據(jù)子會(huì)話(huà)計(jì)數(shù)器 fSubsessionCounter 為子會(huì)話(huà)分配 track number。
用來(lái)產(chǎn)生 SDP 消息的 generateSDPDescription() 和 duration() 函數(shù),及用來(lái)設(shè)置播放快慢的 testScaleFactor(float& scale) 函數(shù),在 live555 源碼分析:DESCRIBE 的處理 和 live555 源碼分析:PLAY 的處理 中已經(jīng)有較為詳細(xì)地說(shuō)明了,這里不再贅述。
為了便于訪問(wèn) ServerMediaSubsession,live555 還提供了一個(gè)迭代器,該迭代器定義如下:
class ServerMediaSubsessionIterator { public:ServerMediaSubsessionIterator(ServerMediaSession& session);virtual ~ServerMediaSubsessionIterator();ServerMediaSubsession* next(); // NULL if nonevoid reset();private:ServerMediaSession& fOurSession;ServerMediaSubsession* fNextPtr; };該迭代器實(shí)現(xiàn)如下:
ServerMediaSubsessionIterator ::ServerMediaSubsessionIterator(ServerMediaSession& session): fOurSession(session) {reset(); }ServerMediaSubsessionIterator::~ServerMediaSubsessionIterator() { }ServerMediaSubsession* ServerMediaSubsessionIterator::next() {ServerMediaSubsession* result = fNextPtr;if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;return result; }void ServerMediaSubsessionIterator::reset() {fNextPtr = fOurSession.fSubsessionsHead; }可以看到 ServerMediaSession 中并沒(méi)有太多的內(nèi)容。大多數(shù)時(shí)候,它僅作為獲得 ServerMediaSubsession 的中介而存在。
ServerMediaSubsession
在 RTSPServer::RTSPClientSession 中處理 SETUP 請(qǐng)求的代碼中,我們看到,它從 ServerMediaSession 獲得 ServerMediaSubsession 并接管了對(duì)它們的管理,并繞過(guò) ServerMediaSession 直接對(duì) ServerMediaSubsession 進(jìn)行操作。執(zhí)行流媒體單個(gè)會(huì)話(huà)的數(shù)據(jù)操作的入口也都在 ServerMediaSubsession。
對(duì)于向 “LIVE555 Media Server” 服務(wù)器請(qǐng)求 H.264 視頻文件的情況,ServerMediaSubsession 的實(shí)際類(lèi)型為 H264VideoFileServerMediaSubsession,我們以此為例來(lái)分析 ServerMediaSubsession。
H264VideoFileServerMediaSubsession 在 DynamicRTSPServer 類(lèi)的 lookupServerMediaSession() 中隨 ServerMediaSession 的創(chuàng)建一起創(chuàng)建,并直接被添加到 ServerMediaSession 中。
#define NEW_SMS(description) do {\ char const* descStr = description\", streamed by the LIVE555 Media Server";\ sms = ServerMediaSession::createNew(env, fileName, fileName, descStr);\ } while(0) . . . . . .} else if (strcmp(extension, ".264") == 0) {// Assumed to be a H.264 Video Elementary Stream file:NEW_SMS("H.264 Video");OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 framessms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));}H264VideoFileServerMediaSubsession 有著如下圖所示的繼承體系:
在這個(gè)繼承體系中,ServerMediaSubsession 定義了可以對(duì)單個(gè)流執(zhí)行的操作,類(lèi)的定義如下:
class ServerMediaSubsession: public Medium { public:unsigned trackNumber() const { return fTrackNumber; }char const* trackId();virtual char const* sdpLines() = 0;virtual void getStreamParameters(unsigned clientSessionId, // innetAddressBits clientAddress, // inPort const& clientRTPPort, // inPort const& clientRTCPPort, // inint tcpSocketNum, // in (-1 means use UDP, not TCP)unsigned char rtpChannelId, // in (used if TCP)unsigned char rtcpChannelId, // in (used if TCP)netAddressBits& destinationAddress, // in outu_int8_t& destinationTTL, // in outBoolean& isMulticast, // outPort& serverRTPPort, // outPort& serverRTCPPort, // outvoid*& streamToken // out) = 0;virtual void startStream(unsigned clientSessionId, void* streamToken,TaskFunc* rtcpRRHandler,void* rtcpRRHandlerClientData,unsigned short& rtpSeqNum,unsigned& rtpTimestamp,ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,void* serverRequestAlternativeByteHandlerClientData) = 0;virtual void pauseStream(unsigned clientSessionId, void* streamToken);virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT,double streamDuration, u_int64_t& numBytes);// This routine is used to seek by relative (i.e., NPT) time.// "streamDuration", if >0.0, specifies how much data to stream, past "seekNPT". (If <=0.0, all remaining data is streamed.)// "numBytes" returns the size (in bytes) of the data to be streamed, or 0 if unknown or unlimited.virtual void seekStream(unsigned clientSessionId, void* streamToken, char*& absStart, char*& absEnd);// This routine is used to seek by 'absolute' time.// "absStart" should be a string of the form "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.<frac>Z".// "absEnd" should be either NULL (for no end time), or a string of the same form as "absStart".// These strings may be modified in-place, or can be reassigned to a newly-allocated value (after delete[]ing the original).virtual void nullSeekStream(unsigned clientSessionId, void* streamToken,double streamEndTime, u_int64_t& numBytes);// Called whenever we're handling a "PLAY" command without a specified start time.virtual void setStreamScale(unsigned clientSessionId, void* streamToken, float scale);virtual float getCurrentNPT(void* streamToken);virtual FramedSource* getStreamSource(void* streamToken);virtual void getRTPSinkandRTCP(void* streamToken,RTPSink const*& rtpSink, RTCPInstance const*& rtcp) = 0;// Returns pointers to the "RTPSink" and "RTCPInstance" objects for "streamToken".// (This can be useful if you want to get the associated 'Groupsock' objects, for example.)// You must not delete these objects, or start/stop playing them; instead, that is done// using the "startStream()" and "deleteStream()" functions.virtual void deleteStream(unsigned clientSessionId, void*& streamToken);virtual void testScaleFactor(float& scale); // sets "scale" to the actual supported scalevirtual float duration() const;// returns 0 for an unbounded session (the default)// returns > 0 for a bounded sessionvirtual void getAbsoluteTimeRange(char*& absStartTime, char*& absEndTime) const;// Subclasses can reimplement this iff they support seeking by 'absolute' time.// The following may be called by (e.g.) SIP servers, for which the// address and port number fields in SDP descriptions need to be non-zero:void setServerAddressAndPortForSDP(netAddressBits addressBits,portNumBits portBits);protected: // we're a virtual base classServerMediaSubsession(UsageEnvironment& env);virtual ~ServerMediaSubsession();char const* rangeSDPLine() const;// returns a string to be delete[]dServerMediaSession* fParentSession;netAddressBits fServerAddressForSDP;portNumBits fPortNumForSDP;private:friend class ServerMediaSession;friend class ServerMediaSubsessionIterator;ServerMediaSubsession* fNext;unsigned fTrackNumber; // within an enclosing ServerMediaSessionchar const* fTrackId; };這些操作可以分為幾類(lèi),一類(lèi)是對(duì)播放進(jìn)行控制的操作,包括 startStream()、pauseStream()、seekStream()、nullSeekStream()、setStreamScale()、deleteStream() 和 testScaleFactor() 等;另一類(lèi)是獲得用于執(zhí)行 I/O 操作的 FramedSource 和 RTPSink 的 getStreamSource() 和 getRTPSinkandRTCP()。
ServerMediaSubsession 類(lèi)提供了幾個(gè)子會(huì)話(huà)的通用操作的實(shí)現(xiàn),這主要包括用于產(chǎn)生字符串形式的 track id 的 trackId(),用于設(shè)置 SDP 服務(wù)器地址和端口號(hào)的 setServerAddressAndPortForSDP(),以及用于生成子會(huì)話(huà)的 SDP 行的 rangeSDPLine():
ServerMediaSubsession::ServerMediaSubsession(UsageEnvironment& env): Medium(env),fParentSession(NULL), fServerAddressForSDP(0), fPortNumForSDP(0),fNext(NULL), fTrackNumber(0), fTrackId(NULL) { }ServerMediaSubsession::~ServerMediaSubsession() {delete[] (char*)fTrackId;Medium::close(fNext); }char const* ServerMediaSubsession::trackId() {if (fTrackNumber == 0) return NULL; // not yet in a ServerMediaSessionif (fTrackId == NULL) {char buf[100];sprintf(buf, "track%d", fTrackNumber);fTrackId = strDup(buf);}return fTrackId; } . . . . . . void ServerMediaSubsession::setServerAddressAndPortForSDP(netAddressBits addressBits,portNumBits portBits) {fServerAddressForSDP = addressBits;fPortNumForSDP = portBits; }char const* ServerMediaSubsession::rangeSDPLine() const {// First, check for the special case where we support seeking by 'absolute' time:char* absStart = NULL; char* absEnd = NULL;getAbsoluteTimeRange(absStart, absEnd);if (absStart != NULL) {char buf[100];if (absEnd != NULL) {sprintf(buf, "a=range:clock=%s-%s\r\n", absStart, absEnd);} else {sprintf(buf, "a=range:clock=%s-\r\n", absStart);}return strDup(buf);}if (fParentSession == NULL) return NULL;// If all of our parent's subsessions have the same duration// (as indicated by "fParentSession->duration() >= 0"), there's no "a=range:" line:if (fParentSession->duration() >= 0.0) return strDup("");// Use our own duration for a "a=range:" line:float ourDuration = duration();if (ourDuration == 0.0) {return strDup("a=range:npt=0-\r\n");} else {char buf[100];sprintf(buf, "a=range:npt=0-%.3f\r\n", ourDuration);return strDup(buf);} }這些操作都比較簡(jiǎn)明,這里不再贅述。
對(duì)于其它眾多操作單個(gè)流的接口,ServerMediaSubsession 類(lèi)都只是提供了一個(gè)空的默認(rèn)實(shí)現(xiàn):
void ServerMediaSubsession::pauseStream(unsigned /*clientSessionId*/,void* /*streamToken*/) {// default implementation: do nothing } void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,void* /*streamToken*/, double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) {// default implementation: do nothingnumBytes = 0; } void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,void* /*streamToken*/, char*& absStart, char*& absEnd) {// default implementation: do nothing (but delete[] and assign "absStart" and "absEnd" to NULL, to show that we don't handle this)delete[] absStart; absStart = NULL;delete[] absEnd; absEnd = NULL; } void ServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/,void* /*streamToken*/, double streamEndTime, u_int64_t& numBytes) {// default implementation: do nothingnumBytes = 0; } void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,void* /*streamToken*/, float /*scale*/) {// default implementation: do nothing } float ServerMediaSubsession::getCurrentNPT(void* /*streamToken*/) {// default implementation: return 0.0return 0.0; } FramedSource* ServerMediaSubsession::getStreamSource(void* /*streamToken*/) {// default implementation: return NULLreturn NULL; } void ServerMediaSubsession::deleteStream(unsigned /*clientSessionId*/,void*& /*streamToken*/) {// default implementation: do nothing }void ServerMediaSubsession::testScaleFactor(float& scale) {// default implementation: Support scale = 1 onlyscale = 1; }float ServerMediaSubsession::duration() const {// default implementation: assume an unbounded session:return 0.0; }void ServerMediaSubsession::getAbsoluteTimeRange(char*& absStartTime, char*& absEndTime) const {// default implementation: We don't support seeking by 'absolute' time, so indicate this by setting both parameters to NULL:absStartTime = absEndTime = NULL; }OnDemandServerMediaSubsession 實(shí)現(xiàn)由 ServerMediaSubsession 定義的流操作接口。為了實(shí)現(xiàn)這些操作,需要一些 I/O 操作,如解析流媒體文件,收發(fā) RTP/RTCP 包等。這些 I/O 操作將由于具體的流媒體源類(lèi)型的不同而不同,因而不會(huì)直接在 OnDemandServerMediaSubsession 中實(shí)現(xiàn)。OnDemandServerMediaSubsession 定義了新的虛函數(shù),以便從子類(lèi)中獲得 FramedSource 和 RTPSink 對(duì)象,來(lái)執(zhí)行 I/O 操作。
OnDemandServerMediaSubsession 的具體實(shí)現(xiàn),暫時(shí)先不詳細(xì)說(shuō)明。
在 H264VideoFileServerMediaSubsession 的類(lèi)繼承層次結(jié)構(gòu)中, FileServerMediaSubsession 用于維護(hù)資源的文件名,其定義如下:
class FileServerMediaSubsession: public OnDemandServerMediaSubsession { protected: // we're a virtual base classFileServerMediaSubsession(UsageEnvironment& env, char const* fileName,Boolean reuseFirstSource);virtual ~FileServerMediaSubsession();protected:char const* fFileName;u_int64_t fFileSize; // if known };這個(gè)定義非常簡(jiǎn)單,其實(shí)現(xiàn)也很簡(jiǎn)單:
FileServerMediaSubsession ::FileServerMediaSubsession(UsageEnvironment& env, char const* fileName,Boolean reuseFirstSource): OnDemandServerMediaSubsession(env, reuseFirstSource),fFileSize(0) {fFileName = strDup(fileName); }FileServerMediaSubsession::~FileServerMediaSubsession() {delete[] (char*)fFileName; }H264VideoFileServerMediaSubsession 最主要的功能則是提供用于執(zhí)行 I/O 操作的 FramedSource 和 RTPSink,其定義如下:
class H264VideoFileServerMediaSubsession: public FileServerMediaSubsession { public:static H264VideoFileServerMediaSubsession*createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource);// Used to implement "getAuxSDPLine()":void checkForAuxSDPLine1();void afterPlayingDummy1();protected:H264VideoFileServerMediaSubsession(UsageEnvironment& env,char const* fileName, Boolean reuseFirstSource);// called only by createNew();virtual ~H264VideoFileServerMediaSubsession();void setDoneFlag() { fDoneFlag = ~0; }protected: // redefined virtual functionsvirtual char const* getAuxSDPLine(RTPSink* rtpSink,FramedSource* inputSource);virtual FramedSource* createNewStreamSource(unsigned clientSessionId,unsigned& estBitrate);virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,unsigned char rtpPayloadTypeIfDynamic,FramedSource* inputSource);private:char* fAuxSDPLine;char fDoneFlag; // used when setting up "fAuxSDPLine"RTPSink* fDummyRTPSink; // ditto };H264VideoFileServerMediaSubsession 通過(guò)實(shí)現(xiàn) createNewStreamSource() 和 createNewRTPSink() 創(chuàng)建并返回 FramedSource 和 RTPSink:
FramedSource* H264VideoFileServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {estBitrate = 500; // kbps, estimate// Create the video source:ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(envir(), fFileName);if (fileSource == NULL) return NULL;fFileSize = fileSource->fileSize();// Create a framer for the Video Elementary Stream:return H264VideoStreamFramer::createNew(envir(), fileSource); }RTPSink* H264VideoFileServerMediaSubsession ::createNewRTPSink(Groupsock* rtpGroupsock,unsigned char rtpPayloadTypeIfDynamic,FramedSource* /*inputSource*/) {return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic); }這里返回的 FramedSource 為 H264VideoStreamFramer,返回的 RTPSink 為 H264VideoRTPSink。
除此之外,H264VideoFileServerMediaSubsession 還提供了 AUX SDP line 有關(guān)的接口實(shí)現(xiàn):
H264VideoFileServerMediaSubsession::H264VideoFileServerMediaSubsession(UsageEnvironment& env,char const* fileName, Boolean reuseFirstSource) :FileServerMediaSubsession(env, fileName, reuseFirstSource),fAuxSDPLine(NULL), fDoneFlag(0), fDummyRTPSink(NULL) { }H264VideoFileServerMediaSubsession::~H264VideoFileServerMediaSubsession() {delete[] fAuxSDPLine; }static void afterPlayingDummy(void* clientData) {H264VideoFileServerMediaSubsession* subsess = (H264VideoFileServerMediaSubsession*)clientData;subsess->afterPlayingDummy1(); }void H264VideoFileServerMediaSubsession::afterPlayingDummy1() {// Unschedule any pending 'checking' task:envir().taskScheduler().unscheduleDelayedTask(nextTask());// Signal the event loop that we're done:setDoneFlag(); }static void checkForAuxSDPLine(void* clientData) {H264VideoFileServerMediaSubsession* subsess = (H264VideoFileServerMediaSubsession*)clientData;subsess->checkForAuxSDPLine1(); }void H264VideoFileServerMediaSubsession::checkForAuxSDPLine1() {nextTask() = NULL;char const* dasl;if (fAuxSDPLine != NULL) {// Signal the event loop that we're done:setDoneFlag();} else if (fDummyRTPSink != NULL && (dasl = fDummyRTPSink->auxSDPLine()) != NULL) {fAuxSDPLine = strDup(dasl);fDummyRTPSink = NULL;// Signal the event loop that we're done:setDoneFlag();} else if (!fDoneFlag) {// try again after a brief delay:int uSecsToDelay = 100000; // 100 msnextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,(TaskFunc*) checkForAuxSDPLine, this);} }char const* H264VideoFileServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource) {if (fAuxSDPLine != NULL) return fAuxSDPLine; // it's already been set up (for a previous client)if (fDummyRTPSink == NULL) { // we're not already setting it up for another, concurrent stream// Note: For H264 video files, the 'config' information ("profile-level-id" and "sprop-parameter-sets") isn't known// until we start reading the file. This means that "rtpSink"s "auxSDPLine()" will be NULL initially,// and we need to start reading data from our file until this changes.fDummyRTPSink = rtpSink;// Start reading the file:fDummyRTPSink->startPlaying(*inputSource, afterPlayingDummy, this);// Check whether the sink's 'auxSDPLine()' is ready:checkForAuxSDPLine(this);}envir().taskScheduler().doEventLoop(&fDoneFlag);return fAuxSDPLine; }Done.
live555 源碼分析系列文章
live555 源碼分析:簡(jiǎn)介
live555 源碼分析:基礎(chǔ)設(shè)施
live555 源碼分析:MediaSever
Wireshark 抓包分析 RTSP/RTP/RTCP 基本工作過(guò)程
live555 源碼分析:RTSPServer
live555 源碼分析:DESCRIBE 的處理
live555 源碼分析:SETUP 的處理
live555 源碼分析:PLAY 的處理
live555 源碼分析:RTSPServer 組件結(jié)構(gòu)
live555 源碼分析:ServerMediaSession
總結(jié)
以上是生活随笔為你收集整理的live555 源码分析:ServerMediaSession的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: live555 源码分析:RTSPSer
- 下一篇: live555 源码分析:子会话 SDP