Live555-RTSP服务器
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