知道了Live555 的基础知识,但我们还是不清楚RTSP,RTP,RTCP它们之间是如何运作的。我们首先分析RTSP服务运作,它的函数调用关系如上图所示。从上面的函数调用流程图我们知道,RTSP先是先创建一个TCP socket 来监听客户端的连接。如果客户端没有发起连接请求,那么RTSP就一直循环监听客户端的连接。如果客户端发起了连接,那么就创建一个客户会话,并把该会话加入到调度任务,该会话就会一直循环调度来查询客户端发送的RTSP命令(OPTIONS、DESCRIBE、SETUP、PLAY、TEARDOWN)
下面就按照函数的调用关系解析各函数的作用。
服务端的入口函数为mediaserver目录下live555MediaServer.cpp 文件的main函数。
int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); (1.0) UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler); (1.1) UserAuthenticationDatabase* authDB = NULL; #ifdef ACCESS_CONTROL // To implement client access control to the RTSP server, do the following: authDB = new UserAuthenticationDatabase; authDB->addUserRecord("username1", "password1"); // replace these with real strings // Repeat the above with each <username>, <password> that you wish to allow // access to the server. #endif // Create the RTSP server. Try first with the default port number (554), // and then with the alternative port number (8554): RTSPServer* rtspServer; portNumBits rtspServerPortNum = 554; rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB); (1.2) if (rtspServer == NULL) { rtspServerPortNum = 8554; rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB); (1.3) } if (rtspServer == NULL) { *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n"; exit(1); } *env << "LIVE555 Media Server\n"; *env << "\tversion " << MEDIA_SERVER_VERSION_STRING << " (LIVE555 Streaming Media library version " << LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n"; char* urlPrefix = rtspServer->rtspURLPrefix(); *env << "Play streams from this server using the URL\n\t" << urlPrefix << "<filename>\nwhere <filename> is a file present in the current directory.\n"; *env << "Each file's type is inferred from its name suffix:\n"; *env << "\t\".264\" => a H.264 Video Elementary Stream file\n"; *env << "\t\".265\" => a H.265 Video Elementary Stream file\n"; *env << "\t\".aac\" => an AAC Audio (ADTS format) file\n"; *env << "\t\".ac3\" => an AC-3 Audio file\n"; *env << "\t\".amr\" => an AMR Audio file\n"; *env << "\t\".dv\" => a DV Video file\n"; *env << "\t\".m4e\" => a MPEG-4 Video Elementary Stream file\n"; *env << "\t\".mkv\" => a Matroska audio+video+(optional)subtitles file\n"; *env << "\t\".mp3\" => a MPEG-1 or 2 Audio file\n"; *env << "\t\".mpg\" => a MPEG-1 or 2 Program Stream (audio+video) file\n"; *env << "\t\".ogg\" or \".ogv\" or \".opus\" => an Ogg audio and/or video file\n"; *env << "\t\".ts\" => a MPEG Transport Stream file\n"; *env << "\t\t(a \".tsx\" index file - if present - provides server 'trick play' support)\n"; *env << "\t\".vob\" => a VOB (MPEG-2 video with AC-3 audio) file\n"; *env << "\t\".wav\" => a WAV Audio file\n"; *env << "\t\".webm\" => a WebM audio(Vorbis)+video(VP8) file\n"; *env << "See http://www.live555.com/mediaServer/ for additional documentation.\n"; // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling. // Try first with the default HTTP port (80), and then with the alternative HTTP // port numbers (8000 and 8080). if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) { *env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n"; } else { *env << "(RTSP-over-HTTP tunneling is not available.)\n"; } env->taskScheduler().doEventLoop(); // does not return (1.4) return 0; // only to prevent compiler warning }(1.0)创建任务调度器
(1.1)创建交互环境
(1.2)使用554端口创建RTSP服务器
(1.3)如果554端口已经被占用,则使用8554端口创建RTSP服务器
(1.4)进入任务调度
我们进入(1.2)中的创建函数,
DynamicRTSPServer* DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) { int ourSocket = setUpOurSocket(env, ourPort); (2.0) if (ourSocket == -1) return NULL; return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds); (2.1) }(2.0)从它里面调用的setupStreamSocket ->createSocket-> socket(AF_INET, type, 0)我们可以知道这里创建的是一个TCP 类型socket,它是用来帧听客户端的连接。
(2.1)这是对RTSPServerSupportingHTTPStreaming函数封装的一个接口,进入该函数
RTSPServerSupportingHTTPStreaming* RTSPServerSupportingHTTPStreaming::createNew(UsageEnvironment& env, Port rtspPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) { int ourSocket = setUpOurSocket(env, rtspPort); (3.0) if (ourSocket == -1) return NULL; return new RTSPServerSupportingHTTPStreaming(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds);(3.1) }(3.0)这里与(2.0)处相同,不知道它在这里还要再创建一个TCP socket 的作用是什么,求高人指点。这里需要明白一点的就是它们的继承关系是:DynamicRTSPServer -->RTSPServerSupportingHTTPStreaming ---> RTSPServer
(3.1)这里进过几层封装,最后会调用到RTSPServer 函数
RTSPServer::RTSPServer(UsageEnvironment& env, int ourSocket, Port ourPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationSeconds) : GenericMediaServer(env, ourSocket, ourPort, reclamationSeconds), (4.0) fHTTPServerSocket(-1), fHTTPServerPort(0), fClientConnectionsForHTTPTunneling(NULL), // will get created if needed fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)), fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)), fRegisterRequestCounter(0), fAuthDB(authDatabase), fAllowStreamingRTPOverTCP(True) { }(4.0)处初始化函数GenericMediaServer,进入函数里面有
GenericMediaServer ::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort, unsigned reclamationSeconds) : Medium(env), fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds), fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)), fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)), fClientSessions(HashTable::create(STRING_HASH_KEYS)) { ignoreSigPipeOnSocket(fServerSocket); // so that clients on the same host that are killed don't also kill us // Arrange to handle connections from others: env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this); (5.0) }(5.0)这里需要注意两个地方turnOnBackgroundReadHandling 函数和incomingConnectionHandler 函数
turnOnBackgroundReadHandling 函数里面调用的是setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);,它设置socket函数可读,并且使handlerProc 函数周期读取数据到clientData。
incomingConnectionHandler 这个函数就是任务调度中每次调度都会执行的函数。该函数的调用关系是incomingConnectionHandler ---> server->incomingConnectionHandler ---> incomingConnectionHandlerOnSocket,进入该函数
void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) { struct sockaddr_in clientAddr; SOCKLEN_T clientAddrLen = sizeof clientAddr; int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); (6.0) 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 us makeSocketNonBlocking(clientSocket); increaseSendBufferTo(envir(), clientSocket, 50*1024); #ifdef DEBUG envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n"; #endif // Create a new object for handling this connection: (void)createNewClientConnection(clientSocket, clientAddr); (6.1) }(6.0)接收客户端的连接,如果没有设备连接,直接返回。如果有设备连接进入(6.1)创建客户会话createNewClientConnection ---> RTSPServer::RTSPClientConnection
::RTSPClientConnection ---> GenericMediaServer::ClientConnection::ClientConnection 进入该函数
GenericMediaServer::ClientConnection ::ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr) : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) { // Add ourself to our 'client connections' table: fOurServer.fClientConnections->Add((char const*)this, this); // Arrange to handle incoming requests: resetRequestBuffer(); envir().taskScheduler() .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);(7.0) }(7.0)把incomingRequestHandler 添加到任务调度中,也就是Live555 会周期调用该函数查询客户端发送过来的RTSP命令( OPTIONS 、 DESCRIBE 、 SETUP 、 PLAY 、 TEARDOWN)