知道了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 , 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 << "\nwhere 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
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)