RTSP Server創建
RTSP服務器初始化:
RTSPServer::createNew->new RTSPServer::RTSPServer->GenericMediaServer::GenericMediaServer->turnOnBackgroundReadHandling(IPV4sock/IPV6sock,incomingConnectionHandlerIPv4)
如上流程,創建RTSP服務器對象時,初始化了IPV4和IPV6的監聽套接字;同時注冊了套接字可讀事件,設置回調incomingConnectionHandlerIPv4,
void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {struct sockaddr_storage clientAddr;SOCKLEN_T clientAddrLen = sizeof clientAddr;int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);if (clientSocket < 0) {int err = envir().getErrno();if (err != EWOULDBLOCK) {envir().setResultErrMsg("accept() failed: ");}return;}ignoreSigPipeOnSocket(clientSocket); // so that clients on the same host that are killed don't also kill usmakeSocketNonBlocking(clientSocket);increaseSendBufferTo(envir(), clientSocket, 50*1024);#ifdef DEBUGenvir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif// Create a new object for handling this connection:(void)createNewClientConnection(clientSocket, clientAddr);
}
RTSPClientConnection又構造基類GenericMediaServer::ClientConnection對象;在基類的構造函數中調用setBackgroundHandling函數注冊連接套接字的可讀和異常事件,并設置回調函數ClientConnection::incomingRequestHandler;
當服務器接收到新連接時函數調用路徑:
incomingConnectionHandlerOnSocket->RTSPServer::createNewClientConnection->new RTSPClientConnection->GenericMediaServer::ClientConnection->setBackgroundHandling(incomingRequestHandler)
產生新連接時回調incomingConnectionHandlerOnSocket,處理連接套接字的初始化,并調用RTSPServer::createNewClientConnection創建一個RTSPClientConnection連接對象管理這個連接;并設置了回調函數incomingRequestHandler;
當連接接收到消息時:
incomingRequestHandler->handleRequestBytes
連接收到消息時會回調incomingRequestHandler函數,incomingRequestHandler函數讀取數據之后會調用handleRequestBytes函數對數據進行解析,然后調用相應的setup/opation命令進行處理恢復;
void GenericMediaServer::ClientConnection::incomingRequestHandler() {if (fInputTLS->tlsAcceptIsNeeded) { // we need to successfully call fInputTLS->accept() first:if (fInputTLS->accept(fOurSocket) <= 0) return; // either an error, or we need to try again laterfInputTLS->tlsAcceptIsNeeded = False;// We can now read data, as usual:}int bytesRead;if (fInputTLS->isNeeded) {bytesRead = fInputTLS->read(&fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft);} else {struct sockaddr_storage dummy; // 'from' address, meaningless in this casebytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);}handleRequestBytes(bytesRead);
}void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {int numBytesRemaining = 0;++fRecursionCount; // 防止在處理中刪除自身do {// 1. 檢查輸入數據有效性// 2. 處理Base64解碼(如果使用HTTP隧道)// 3. 查找消息結束標記// 4. 解析請求(RTSP或HTTP)// 5. 根據命令類型處理// 6. 發送響應// 7. 處理剩余數據(管道化請求)} while (numBytesRemaining > 0);--fRecursionCount;// 檢查是否需要關閉連接或刪除自身if (!fIsActive && fScheduledDelayedTask <= 0) {if (fRecursionCount > 0) closeSockets();else delete this;}
}
ServerMediaSession類
class 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);//通過名稱在全局介質注冊表中查找ServerMediaSession對象static Boolean lookupByName(UsageEnvironment& env,char const* mediumName,ServerMediaSession*& resultSession);//動態構建SDP描述文件:char* generateSDPDescription(int addressFamily); // based on the entire session// Note: The caller is responsible for freeing the returned string//獲取流名稱char const* streamName() const { return fStreamName; }//添加子會話,使用??鏈表結構??管理子會話,為每個子會話分配自動遞增的fTrackNumberBoolean 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 server then, before calling this function,// you must first close any client connections that use it,// by calling "GenericMediaServer::closeAllClientSessionsForServerMediaSession()".Boolean streamingUsesSRTP; // by default, FalseBoolean streamingIsEncrypted; // by default, Falseprotected://初始化成員變量,包括會話名稱、信息、描述等。如果沒有提供info或description,則使用庫名稱和版本號。記錄創建時間(用于SDP的o=行)。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;// 是否SSM(源特定組播)// Linkage fields:friend class ServerMediaSubsessionIterator;ServerMediaSubsession* fSubsessionsHead;// 媒體子會話鏈表頭ServerMediaSubsession* fSubsessionsTail;// 鏈表尾unsigned fSubsessionCounter;char* fStreamName;// 會話名稱(如"liveVideo")char* fInfoSDPString;// SDP中的會話信息char* fDescriptionSDPString;// SDP中的描述信息char* fMiscSDPLines;// 自定義SDP參數struct timeval fCreationTime;unsigned fReferenceCount;Boolean fDeleteWhenUnreferenced;
};class ServerMediaSubsessionIterator {
public:ServerMediaSubsessionIterator(ServerMediaSession& session);virtual ~ServerMediaSubsessionIterator();ServerMediaSubsession* next(); // NULL if nonevoid reset();private:ServerMediaSession& fOurSession;ServerMediaSubsession* fNextPtr;
};//表示??單條媒體軌道??(如音頻流/視頻流),繼承關系:
class ServerMediaSubsession: public Medium {
public:unsigned trackNumber() const { return fTrackNumber; }char const* trackId();virtual char const* sdpLines(int addressFamily) = 0;virtual void getStreamParameters(unsigned clientSessionId, // instruct sockaddr_storage const& 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)TLSState* tlsState, // in (used if TCP)struct sockaddr_storage& 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*& rtpSink, RTCPInstance*& 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.protected: // we're a virtual base classServerMediaSubsession(UsageEnvironment& env);virtual ~ServerMediaSubsession();char const* rangeSDPLine() const;// returns a string to be delete[]dServerMediaSession* fParentSession;u_int32_t fSRTP_ROC; // horrible hack for SRTP; when the ROC changes, regenerate the SDPprivate:friend class ServerMediaSession;friend class ServerMediaSubsessionIterator;ServerMediaSubsession* fNext;unsigned fTrackNumber; // within an enclosing ServerMediaSessionchar const* fTrackId;
};#endif