本文图由StarUML生成。
本文分析Live555中RTSPServer代码。涉及流程从服务启动到接收到rtsp协议后的交互。
RTSPServer的创建由RTSPServer::createNew()生成
// Create the RTSP server: RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB); if (rtspServer == NULL) { *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n"; exit(1); }
createNew调用了RTSPServer构造函数,并在其中设置了该端口号消息对应的处理器的handler
1: // Arrange to handle connections from others:
2: env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
3: (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
其中turnOnBackgroundReadHandling进一步通过void BasicTaskScheduler::setBackgroundHandling将收到消息的处理器设置为(TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP。
后续任务调度器主循环doEventLoop在检测到有消息到达时,通过socket检索到对应处理器并调用之。
1: // A H.264 video elementary stream:
2: {
3: char const* streamName = "h264ESVideoTest";
4: char const* inputFileName = "test.264";
5: ServerMediaSession* sms
6: = ServerMediaSession::createNew(*env, streamName, streamName,
7: descriptionString);
8: sms->addSubsession(H264VideoFileServerMediaSubsession
9: ::createNew(*env, inputFileName, reuseFirstSource));
10: rtspServer->addServerMediaSession(sms);
11:
12: announceStream(rtspServer, sms, streamName, inputFileName);
13: }
其中ServerMediaSession负责会话公共描述信息,具体文件格式相关的则由H264VideoFileServerMediaSubsession等负责。H264VideoFileServerMediaSubsession继承自ServerMediaSubsession,但ServerMediaSubsession与ServerMediaSession类层次上是兄弟关系,均继承自Medium类。一个ServerMediaSession可同时关联多个ServerMediaSubsession,但目前没找到这样的例子。
任务调度器主循环一直在调用BasicTaskScheduler::SingleStep,其中依次检测每个handler是否有消息到达需要处理,如果达到出发条件则调用注册到该handler的处理器,如上面介绍是RTSPServer::incomingConnectionHandlerRTSP。在其中又调用了供RTSP和HTTP等公用的RTSPServer::incomingConnectionHandler。后者accept连接,如果成功则创建新的RTSPServer::RTSPClientConnection为此连接服务。
RTSPServer::incomingConnectionHandler的实现
1: void RTSPServer::incomingConnectionHandler(int serverSocket)
2: {
3: struct sockaddr_in clientAddr;
4: SOCKLEN_T clientAddrLen = sizeof clientAddr;
5: int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
6: if (clientSocket < 0)
7: {
8: int err = envir().getErrno();
9: if (err != EWOULDBLOCK)
10: {
11: envir().setResultErrMsg("accept() failed: ");
12: }
13: return;
14: }
15: makeSocketNonBlocking(clientSocket);
16: increaseSendBufferTo(envir(), clientSocket, 50*1024);
17:
18: #ifdef DEBUG
19: envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
20: #endif
21:
22: // Create a new object for handling this RTSP connection:
23: (void)createNewClientConnection(clientSocket, clientAddr);
24: }
RTSPServer::RTSPClientConnection构造函数的实现
1: RTSPServer::RTSPClientConnection
2: ::RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
3: : fOurServer(ourServer), fIsActive(True),
4: fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
5: fRecursionCount(0), fOurSessionCookie(NULL)
6: {
7: // Add ourself to our 'client connections' table:
8: fOurServer.fClientConnections->Add((char const*)this, this);
9:
10: // Arrange to handle incoming requests:
11: resetRequestBuffer();
12: envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
13: (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
14: }
在其中,进一步将其回调处理函数设置为了RTSPServer::RTSPClientConnection::incomingRequestHandler。正常情况下在紧随其后的下一个事件循环中此新handler将被检测并被调用。
1: void RTSPServer::RTSPClientConnection::incomingRequestHandler1()
2: {
3: struct sockaddr_in dummy; // 'from' address, meaningless in this case
4:
5: int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
6: handleRequestBytes(bytesRead);//读取接收到的数据,并处理
7: }
8:
9: void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead)
10: {
11: int numBytesRemaining = 0;
12: ++fRecursionCount;
13:
14: do {
15: RTSPServer::RTSPClientSession* clientSession = NULL;
16:
17: if (newBytesRead < 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft)
18: {
19: // Either the client socket has died, or the request was too big for us.
20: // Terminate this connection:
21: #ifdef DEBUG
22: fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
23: #endif
24: fIsActive = False;
25: break;
26: }
27:
28: Boolean endOfMsg = False;
29: unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
30:
31: //略,消息组装等代码 。
32:
33: // Parse the request string into command name and 'CSeq', then handle the command:
34: fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
35: char cmdName[RTSP_PARAM_STRING_MAX];
36: char urlPreSuffix[RTSP_PARAM_STRING_MAX];
37: char urlSuffix[RTSP_PARAM_STRING_MAX];
38: char cseq[RTSP_PARAM_STRING_MAX];
39: char sessionIdStr[RTSP_PARAM_STRING_MAX];
40: unsigned contentLength = 0;
41: fLastCRLF[2] = '\0'; // temporarily, for parsing
42: Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer, //RTSP 命令解析
43: cmdName, sizeof cmdName, //请求中的cmdName
44: urlPreSuffix, sizeof urlPreSuffix,
45: urlSuffix, sizeof urlSuffix,
46: cseq, sizeof cseq,
47: sessionIdStr, sizeof sessionIdStr,
48: contentLength);
49: fLastCRLF[2] = '\r'; // restore its value
50: Boolean playAfterSetup = False;
51: if (parseSucceeded)
52: {
53: #ifdef DEBUG
54: fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %ld bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
55: #endif
56: // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
57: if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us
58:
59: // If the request included a "Session:" id, and it refers to a client session that's
60: // current ongoing, then use this command to indicate 'liveness' on that client session:
61: Boolean const requestIncludedSessionId = sessionIdStr[0] != '\0';
62: if (requestIncludedSessionId)
63: {
64: clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
65: if (clientSession != NULL) clientSession->noteLiveness();
66: }
67:
68: // We now have a complete RTSP request.
69: // Handle the specified command (beginning with commands that are session-independent):
70: fCurrentCSeq = cseq;
71: if (strcmp(cmdName, "OPTIONS") == 0) //根据当前的cmdName进行相应处理。 正常流程一般都是options describe setup play
72: {
73: handleCmd_OPTIONS(); //简单拼接一个支持命令的字符串响应
74: }
75: else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0')
76: {
77: // The special "*" URL means: an operation on the entire server. This works only for GET_PARAMETER and SET_PARAMETER:
78: if (strcmp(cmdName, "GET_PARAMETER") == 0)
79: {
80: handleCmd_GET_PARAMETER((char const*)fRequestBuffer);
81: }
82: else if (strcmp(cmdName, "SET_PARAMETER") == 0)
83: {
84: handleCmd_SET_PARAMETER((char const*)fRequestBuffer);
85: }
86: else
87: {
88: handleCmd_notSupported();
89: }
90: }
91: else if (strcmp(cmdName, "DESCRIBE") == 0)
92: {
93: handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer); //这个涉及到鉴权,会话查找,如果存在的话生成对应sdp,并构造响应字符串
94: }
95: else if (strcmp(cmdName, "SETUP") == 0)
96: {
97: if (!requestIncludedSessionId)
98: {
99: // No session id was present in the request. So create a new "RTSPClientSession" object
100: // for this request. Choose a random (unused) 32-bit integer for the session id
101: // (it will be encoded as a 8-digit hex number). (We avoid choosing session id 0,
102: // because that has a special use (by "OnDemandServerMediaSubsession").)
103: u_int32_t sessionId;
104: do {
105: sessionId = (u_int32_t)our_random32();
106: sprintf(sessionIdStr, "%08X", sessionId);
107: } while (sessionId == 0 || fOurServer.fClientSessions->Lookup(sessionIdStr) != NULL);
108: clientSession = fOurServer.createNewClientSession(sessionId);
109: fOurServer.fClientSessions->Add(sessionIdStr, clientSession);
110: }
111: if (clientSession != NULL)
112: {
113: clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
114: playAfterSetup = clientSession->fStreamAfterSETUP;
115: }
116: else
117: {
118: handleCmd_sessionNotFound();
119: }
120: }
121: else if (strcmp(cmdName, "TEARDOWN") == 0
122: || strcmp(cmdName, "PLAY") == 0
123: || strcmp(cmdName, "PAUSE") == 0
124: || strcmp(cmdName, "GET_PARAMETER") == 0
125: || strcmp(cmdName, "SET_PARAMETER") == 0)
126: {
127: if (clientSession != NULL)
128: {
129: clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
130: }
131: else
132: {
133: handleCmd_sessionNotFound();
134: }
135: }
136: else if (strcmp(cmdName, "REGISTER") == 0)
137: {
138: // Because - unlike other commands - an implementation of this command needs
139: // the entire URL, we re-parse the command to get it:
140: char* url = strDupSize((char*)fRequestBuffer);
141: if (sscanf((char*)fRequestBuffer, "%*s %s", url) == 1)
142: {
143: // Check for special command-specific parameters in a "Transport:" header:
144: Boolean reuseConnection, deliverViaTCP;
145: char* proxyURLSuffix;
146: parseTransportHeaderForREGISTER((const char*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
147:
148: handleCmd_REGISTER(url, urlSuffix, (char const*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
149: delete[] proxyURLSuffix;
150: }
151: else
152: {
153: handleCmd_bad();
154: }
155: delete[] url;
156: }
157: else
158: {
159: // The command is one that we don't handle:
160: handleCmd_notSupported();
161: }
162: }
163: else
164: {
165: // zhu.jinhua delete HTTP code
166: }
167:
168: #ifdef DEBUG
169: fprintf(stderr, "sending response: %s", fResponseBuffer);
170: #endif
171: send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
172:
173: if (playAfterSetup)
174: {
175: // The client has asked for streaming to commence now, rather than after a
176: // subsequent "PLAY" command. So, simulate the effect of a "PLAY" command:
177: clientSession->handleCmd_withinSession(this, "PLAY", urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
178: }
179:
180: // Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
181: // If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
182: unsigned requestSize = (fLastCRLF+4-fRequestBuffer) + contentLength;
183: numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
184: resetRequestBuffer(); // to prepare for any subsequent request
185:
186: if (numBytesRemaining > 0)
187: {
188: memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);
189: newBytesRead = numBytesRemaining;
190: }
191: } while (numBytesRemaining > 0);
192:
193: --fRecursionCount;
194: if (!fIsActive)
195: {
196: if (fRecursionCount > 0) closeSockets(); else delete this;
197: // Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively
198: // while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).
199: // In such a case we don't want to actually delete ourself until we leave the outermost call.
200: }
201: }
其中option命令的处理如下
1: void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS()
2: {
3: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
4: "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
5: fCurrentCSeq, dateHeader(), fOurServer.allowedCommandNames());
6: }
可见其只是简单根据支持情况拼接了个字符串而已。同样describe的响应结构如下
1: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
2: "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
3: "%s"
4: "Content-Base: %s/\r\n"
5: "Content-Type: application/sdp\r\n"
6: "Content-Length: %d\r\n\r\n"
7: "%s",
8: fCurrentCSeq,
9: dateHeader(),
10: rtspURL,
11: sdpDescriptionSize,
12: sdpDescription);
最后上一个RTPServer的类图