RTSPServer*
RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds) {
//建立TCP Socket
int ourSocket = setUpOurSocket(env, ourPort);
if (ourSocket == -1) return NULL;
//创建RTSPServer 侦听客户端的socket连接
return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
}
RTSPServer::RTSPServer(UsageEnvironment& env,
int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds)
: Medium(env),
fRTSPServerSocket(ourSocket), fRTSPServerPort(ourPort),
fHTTPServerSocket(-1), fHTTPServerPort(0), fClientSessionsForHTTPTunneling(NULL),
fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
fServerMediaSessions(HashTable::create(STRING_HASH_KEYS))
{
//忽略信号,这样本机上的客户端被中断的时候,服务端不会被中断
ignoreSigPipeOnSocket(ourSocket); // so that clients on the same host that are killed don't also kill us
// Arrange to handle connections from others:
//将incomingConnectionHandlerRTSP加入任务调度
env.taskScheduler().turnOnBackgroundReadHandling(
fRTSPServerSocket,
(TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP,
this);
}
void RTSPServer::incomingConnectionHandlerRTSP(void* instance, int /*mask*/) {
RTSPServer* server = (RTSPServer*)instance;
server->incomingConnectionHandlerRTSP1();
}
void RTSPServer::incomingConnectionHandlerRTSP1() {
incomingConnectionHandler(fRTSPServerSocket);
}
产生,它保存的代表客户的socket 。下为RTSPClientSession 的创建过程 。
void RTSPServer::incomingConnectionHandler(int serverSocket)
{
struct sockaddr_in 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;
}
//设置 socket 的参数
makeSocketNonBlocking(clientSocket);
increaseSendBufferTo(envir(), clientSocket, 50*1024);
#ifdef DEBUG
envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif
// Create a new object for this RTSP session.
// (Choose a random 32-bit integer for the session id (it will be encoded as a 8-digit hex number). We don't bother checking for
// a collision; the probability of two concurrent sessions getting the same session id is very low.)
// (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
unsigned sessionId;
do
{
sessionId = (unsigned)our_random32();
} while (sessionId == 0);
//创建 RTSPClientSession ,注意传入的参数
(void)createNewClientSession(sessionId, clientSocket, clientAddr);
}
RTSPServer::createNewClientSession(unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr) {
return new RTSPClientSession(*this, sessionId, clientSocket, clientAddr);
}
RTSPServer::RTSPClientSession
::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr)
: fOurServer(ourServer), fOurSessionId(sessionId),
fOurServerMediaSession(NULL),
fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
fSessionCookie(NULL), fLivenessCheckTask(NULL),
fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL), fRecursionCount(0)
{
// Arrange to handle incoming requests:
resetRequestBuffer();
//将RTSP会话侦听功能加入任务计划中
envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
(TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
noteLiveness();
}
void RTSPServer::RTSPClientSession::incomingRequestHandler(void* instance, int /*mask*/) {
RTSPClientSession* session = (RTSPClientSession*)instance;
session->incomingRequestHandler1();
}
void RTSPServer::RTSPClientSession::incomingRequestHandler1() {
struct sockaddr_in dummy; // 'from' address, meaningless in this case
int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
handleRequestBytes(bytesRead);
}
void RTSPServer::RTSPClientSession::handleRequestBytes(int newBytesRead)
{
int numBytesRemaining = 0;
++fRecursionCount;
do
{
noteLiveness();
//socket读取失败的处理
if (newBytesRead <= 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft)
{
// Either the client socket has died, or the request was too big for us.
// Terminate this connection:
#ifdef DEBUG
fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
#endif
fSessionIsActive = False;
break;
}
Boolean endOfMsg = False;
unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
#ifdef DEBUG
ptr[newBytesRead] = '\0';
fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() %s %d new bytes:%s\n",
this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);
#endif
//在端口不相等的时候,我们认为是HTTP端口传过来的数据
//并认为数据经过了BASE64加密,所有我们解码。
if (fClientOutputSocket != fClientInputSocket) {
// We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
// We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes):
unsigned numBytesToDecode = fBase64RemainderCount + newBytesRead;
unsigned newBase64RemainderCount = numBytesToDecode%4;
numBytesToDecode -= newBase64RemainderCount;
if (numBytesToDecode > 0) {
ptr[newBytesRead] = '\0';
unsigned decodedSize;
unsigned char* decodedBytes = base64Decode((char const*)(ptr-fBase64RemainderCount), decodedSize);
#ifdef DEBUG
fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
fprintf(stderr, "\n");
#endif
// Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
unsigned char* to = ptr-fBase64RemainderCount;
for (unsigned i = 0; i < decodedSize; ++i) *to++ = decodedBytes[i];
// Then copy any remaining (undecoded) bytes to the end:
for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
delete[] decodedBytes;
}
fBase64RemainderCount = newBase64RemainderCount;
if (fBase64RemainderCount > 0) break; // because we know that we have more input bytes still to receive
}
// Look for the end of the message:
unsigned char *tmpPtr = fLastCRLF + 2;
if (tmpPtr < fRequestBuffer) tmpPtr = fRequestBuffer;
while (tmpPtr < &ptr[newBytesRead-1]) {
if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
if (tmpPtr - fLastCRLF == 2) { // This is it:
endOfMsg = True;
break;
}
fLastCRLF = tmpPtr;
}
++tmpPtr;
}
fRequestBufferBytesLeft -= newBytesRead;
fRequestBytesAlreadySeen += newBytesRead;
if (!endOfMsg) break; // subsequent reads will be needed to complete the request
//解析客户端的RTSP会话命令,DESCRIBE 等等。。。
// Parse the request string into command name and 'CSeq', then handle the command:
fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
char cmdName[RTSP_PARAM_STRING_MAX];
char urlPreSuffix[RTSP_PARAM_STRING_MAX];
char urlSuffix[RTSP_PARAM_STRING_MAX];
char cseq[RTSP_PARAM_STRING_MAX];
unsigned contentLength;
*fLastCRLF = '\0'; // temporarily, for parsing
Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen,
cmdName, sizeof cmdName,
urlPreSuffix, sizeof urlPreSuffix,
urlSuffix, sizeof urlSuffix,
cseq, sizeof cseq,
contentLength);
*fLastCRLF = '\r';
//解析成功则根据对应的命令,进入相应的处理模块
if (parseSucceeded) {
#ifdef DEBUG
fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
#endif
// If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us
if (strcmp(cmdName, "OPTIONS") == 0) {
handleCmd_OPTIONS(cseq);
} else if (strcmp(cmdName, "DESCRIBE") == 0) {
handleCmd_DESCRIBE(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
} else if (strcmp(cmdName, "SETUP") == 0) {
handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
} else if (strcmp(cmdName, "TEARDOWN") == 0
|| strcmp(cmdName, "PLAY") == 0
|| strcmp(cmdName, "PAUSE") == 0
|| strcmp(cmdName, "GET_PARAMETER") == 0
|| strcmp(cmdName, "SET_PARAMETER") == 0) {
handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq, (char const*)fRequestBuffer);
} else {
handleCmd_notSupported(cseq);
}
} else {
#ifdef DEBUG
fprintf(stderr, "parseRTSPRequestString() failed\n");
#endif
// The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
char sessionCookie[RTSP_PARAM_STRING_MAX];
char acceptStr[RTSP_PARAM_STRING_MAX];
*fLastCRLF = '\0'; // temporarily, for parsing
parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,
urlSuffix, sizeof urlPreSuffix,
sessionCookie, sizeof sessionCookie,
acceptStr, sizeof acceptStr);
*fLastCRLF = '\r';
if (parseSucceeded) {
#ifdef DEBUG
fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
#endif
// Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
Boolean isValidHTTPCmd = True;
if (sessionCookie[0] == '\0') {
// There was no "x-sessionCookie:" header. If there was an "Accept: application/x-rtsp-tunnelled" header,
// then this is a bad tunneling request. Otherwise, assume that it's an attempt to access the stream via HTTP.
if (strcmp(acceptStr, "application/x-rtsp-tunnelled") == 0) {
isValidHTTPCmd = False;
} else {
handleHTTPCmd_StreamingGET(urlSuffix, (char const*)fRequestBuffer);
}
} else if (strcmp(cmdName, "GET") == 0) {
handleHTTPCmd_TunnelingGET(sessionCookie);
} else if (strcmp(cmdName, "POST") == 0) {
// We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
// Check for this, and handle it if it exists:
unsigned char const* extraData = fLastCRLF+4;
unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] - extraData;
if (handleHTTPCmd_TunnelingPOST(sessionCookie, extraData, extraDataSize)) {
// We don't respond to the "POST" command, and we go away:
fSessionIsActive = False;
break;
}
} else {
isValidHTTPCmd = False;
}
if (!isValidHTTPCmd) {
handleHTTPCmd_notSupported();
}
} else {
#ifdef DEBUG
fprintf(stderr, "parseHTTPRequestString() failed!\n");
#endif
handleCmd_bad(cseq);
}
}
#ifdef DEBUG
fprintf(stderr, "sending response: %s", fResponseBuffer);
#endif
//向客户端发送响应的数据
send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
//特殊情况,客户端在SETUP后马上要求视频开始
if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) {
// The client has asked for streaming to commence now, rather than after a
// subsequent "PLAY" command. So, simulate the effect of a "PLAY" command:
handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq,
(char const*)fRequestBuffer);
}
// Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
// If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
unsigned requestSize = (fLastCRLF+4-fRequestBuffer) + contentLength;
numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
resetRequestBuffer(); // to prepare for any subsequent request
if (numBytesRemaining > 0) {
memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);
newBytesRead = numBytesRemaining;
}
} while (numBytesRemaining > 0);
--fRecursionCount;
if (!fSessionIsActive) {
if (fRecursionCount > 0) closeSockets(); else delete this;
// Note: The "fRecursionCount" test is for a pathological situation where we got called recursively while handling a command.
// In such a case we don't want to actually delete ourself until we leave the outermost call.
}
}
void RTSPServer::RTSPClientSession
::handleCmd_DESCRIBE(char const* cseq,
char const* urlPreSuffix, char const* urlSuffix,
char const* fullRequestStr)
{
char* sdpDescription = NULL;
char* rtspURL = NULL;
do
{
//整理RTSP地址
char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2 > sizeof urlTotalSuffix)
{
handleCmd_bad(cseq);
break;
}
urlTotalSuffix[0] = '\0';
if (urlPreSuffix[0] != '\0') {
strcat(urlTotalSuffix, urlPreSuffix);
strcat(urlTotalSuffix, "/");
}
strcat(urlTotalSuffix, urlSuffix);
//验证用户名 密码
if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr)) break;
// We should really check that the request contains an "Accept:" #####
// for "application/sdp", because that's what we're sending back #####
// Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
/*跟据流的名字查找 ServerMediaSession,如果找不到,会创建一个。每
//一个serverMediaSession中至少要包含一个 ServerMediaSubsession。一个ServerMediaSession对应一个媒体,可
//以认为是Server 上的一个文件,或一个实时获取设备。其包含的每个
//ServerMediaSubSession代表媒体中的一个Track。所以一个
//ServerMediaSession对应一个媒体,如果客户请求的媒体名相同,就使用已存
//在的ServerMediaSession,如果不同,就创建一个新的。一个流对应一个
//StreamState,StreamState 与ServerMediaSubsession 相关,但代表的是动态
//的,而ServerMediaSubsession 代表静态的。*/
ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
if (session == NULL) {
handleCmd_notFound(cseq);
break;
}
// Then, assemble a SDP description for this session:
//获取SDP字符串,在函数内会依次获取每个 ServerMediaSubSession的
//字符串然连接起来
sdpDescription = session->generateSDPDescription();
if (sdpDescription == NULL) {
// This usually means that a file name that was specified for a
// "ServerMediaSubsession" does not exist.
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
"CSeq: %s\r\n"
"%s\r\n",
cseq,
dateHeader());
break;
}
unsigned sdpDescriptionSize = strlen(sdpDescription);
// Also, generate our RTSP URL, for the "Content-Base:" header
// (which is necessary to ensure that the correct URL gets used in
// subsequent "SETUP" requests).
rtspURL = fOurServer.rtspURL(session, fClientInputSocket);
//形成响应 DESCRIBE请求的RTSP 字符串。
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
"%s"
"Content-Base: %s/\r\n"
"Content-Type: application/sdp\r\n"
"Content-Length: %d\r\n\r\n"
"%s",
cseq,
dateHeader(),
rtspURL,
sdpDescriptionSize,
sdpDescription);
} while (0);
delete[] sdpDescription;
delete[] rtspURL;
}
以上为RTSP建立的流程,其基本流程如下图所示:
简单总结下:
RTSP服务端(RTSPServer),在侦听到客户端的socket请求时,会创建一个RTSPClientSession类与客户端进行RTSP会话,包括"OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER"。而且会在DESCRIBE中创建serverMediaSession提供媒体服务。