5、RTSP服務運做git
基礎基本搞明白了,那麼RTSP,RTP等這些協議又是如何利用這些基礎機制運做的呢?首先來看RTSP.RTSP首先需創建TCP偵聽socket。可見於此函數:session
[cpp] view plaincopyapp
DynamicRTSPServer* DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort, dom
UserAuthenticationDatabase* authDatabase, socket
unsigned reclamationTestSeconds) { 函數
int ourSocket = setUpOurSocket(env, ourPort); //創建TCP socket this
if (ourSocket == -1) url
return NULL; spa
return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, .net
reclamationTestSeconds);
}
要幀聽客戶端的鏈接,就須要利用任務調度機制了,因此需添加一個socket handler。可見於此函數:
[cpp] view plaincopy
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))
{
#ifdef USE_SIGNALS
// Ignore the SIGPIPE signal, so that clients on the same host that are killed
// don't also kill us:
signal(SIGPIPE, SIG_IGN);
#endif
// Arrange to handle connections from others:
env.taskScheduler().turnOnBackgroundReadHandling(
fRTSPServerSocket,
(TaskScheduler::BackgroundHandlerProc*) &incomingConnectionHandlerRTSP,
this);
}
當收到客戶的鏈接時需保存下表明客戶端的新socket,之後用這個socket與這個客戶通信。每一個客戶未來會對應一個rtp會話,並且各客戶的RTSP請求只控制本身的rtp會話,那麼最好創建一個會話類,表明各客戶的rtsp會話。因而類RTSPServer::RTSPClientSession產生,它保存的表明客戶的socket。下爲RTSPClientSession的建立過程
[cpp] view plaincopy
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 " << our_inet_ntoa(clientAddr.sin_addr) << "\n";
#endif
//產生一個sesson id
// 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_random();
} while (sessionId == 0);
//建立RTSPClientSession,注意傳入的參數
(void) createNewClientSession(sessionId, clientSocket, clientAddr);
}
RTSPClientSession要提供什麼功能呢?能夠想象:須要監聽客戶端的rtsp請求並回應它,須要在DESCRIBE請求中返回所請求的流的信息,須要在SETUP請求中創建起RTP會話,須要在TEARDOWN請求中關閉RTP會話,等等...
RTSPClientSession要偵聽客戶端的請求,就需把本身的socket handler加入計劃任務。證據以下:
[cpp] view plaincopy
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();
envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
(TaskScheduler::BackgroundHandlerProc*) &incomingRequestHandler,
this);
noteLiveness();
}
下面重點講一下下RTSPClientSession響應DESCRIBE請求的過程:
[cpp] view plaincopy
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;
//返回後會被當即發送(沒有把socket write操做放入計劃任務中)。
}
fOurServer.lookupServerMediaSession(urlTotalSuffix)中會在找不到同名ServerMediaSession時新建一個,表明一個RTP流的ServerMediaSession們是被RTSPServer管理的,而不是被RTSPClientSession擁有。爲何呢?由於ServerMediaSession表明的是一個靜態的流,也就是能夠從它裏面獲取一個流的各類信息,但不能獲取傳輸狀態。不一樣客戶可能鏈接到同一個流,因此ServerMediaSession應被RTSPServer所擁有。建立一個ServerMediaSession過程值得一觀: