📄 rtspserver.cpp
字号:
unsigned resultURLSuffixMaxSize,
char* resultCSeq,
unsigned resultCSeqMaxSize) {
// This parser is currently rather dumb; it should be made smarter #####
// Read everything up to the first space as the command name:
Boolean parseSucceeded = False;
unsigned i;
for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) {
char c = reqStr[i];
if (c == ' ' || c == '\t') {
parseSucceeded = True;
break;
}
resultCmdName[i] = c;
}
resultCmdName[i] = '\0';
if (!parseSucceeded) return False;
// Skip over the prefix of any "rtsp://" or "rtsp:/" URL that follows:
unsigned j = i+1;
while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j; // skip over any additional white space
for (j = i+1; j < reqStrSize-8; ++j) {
if ((reqStr[j] == 'r' || reqStr[j] == 'R')
&& (reqStr[j+1] == 't' || reqStr[j+1] == 'T')
&& (reqStr[j+2] == 's' || reqStr[j+2] == 'S')
&& (reqStr[j+3] == 'p' || reqStr[j+3] == 'P')
&& reqStr[j+4] == ':' && reqStr[j+5] == '/') {
j += 6;
if (reqStr[j] == '/') {
// This is a "rtsp://" URL; skip over the host:port part that follows:
++j;
while (j < reqStrSize && reqStr[j] != '/' && reqStr[j] != ' ') ++j;
} else {
// This is a "rtsp:/" URL; back up to the "/":
--j;
}
i = j;
break;
}
}
// Look for the URL suffix (before the following "RTSP/"):
parseSucceeded = False;
for (unsigned k = i+1; k < reqStrSize-5; ++k) {
if (reqStr[k] == 'R' && reqStr[k+1] == 'T' &&
reqStr[k+2] == 'S' && reqStr[k+3] == 'P' && reqStr[k+4] == '/') {
while (--k >= i && reqStr[k] == ' ') {} // go back over all spaces before "RTSP/"
unsigned k1 = k;
while (k1 > i && reqStr[k1] != '/' && reqStr[k1] != ' ') --k1;
// the URL suffix comes from [k1+1,k]
// Copy "resultURLSuffix":
if (k - k1 + 1 > resultURLSuffixMaxSize) return False; // there's no room
unsigned n = 0, k2 = k1+1;
while (k2 <= k) resultURLSuffix[n++] = reqStr[k2++];
resultURLSuffix[n] = '\0';
// Also look for the URL 'pre-suffix' before this:
unsigned k3 = --k1;
while (k3 > i && reqStr[k3] != '/' && reqStr[k3] != ' ') --k3;
// the URL pre-suffix comes from [k3+1,k1]
// Copy "resultURLPreSuffix":
if (k1 - k3 + 1 > resultURLPreSuffixMaxSize) return False; // there's no room
n = 0; k2 = k3+1;
while (k2 <= k1) resultURLPreSuffix[n++] = reqStr[k2++];
resultURLPreSuffix[n] = '\0';
i = k + 7; // to go past " RTSP/"
parseSucceeded = True;
break;
}
}
if (!parseSucceeded) return False;
// Look for "CSeq:", skip whitespace,
// then read everything up to the next \r or \n as 'CSeq':
parseSucceeded = False;
for (j = i; j < reqStrSize-5; ++j) {
if (reqStr[j] == 'C' && reqStr[j+1] == 'S' && reqStr[j+2] == 'e' &&
reqStr[j+3] == 'q' && reqStr[j+4] == ':') {
j += 5;
unsigned n;
while (j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t')) ++j;
for (n = 0; n < resultCSeqMaxSize-1 && j < reqStrSize; ++n,++j) {
char c = reqStr[j];
if (c == '\r' || c == '\n') {
parseSucceeded = True;
break;
}
resultCSeq[n] = c;
}
resultCSeq[n] = '\0';
break;
}
}
if (!parseSucceeded) return False;
return True;
}
// Handler routines for specific RTSP commands:
// Generate a "Date:" header for use in a RTSP response:
static char const* dateHeader() {
static char buf[200];
#if !defined(_WIN32_WCE)
time_t tt = time(NULL);
strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT\r\n", gmtime(&tt));
#else
// WinCE apparently doesn't have "time()", "strftime()", or "gmtime()",
// so generate the "Date:" header a different, WinCE-specific way.
// (Thanks to Pierre l'Hussiez for this code)
SYSTEMTIME SystemTime;
GetSystemTime(&SystemTime);
WCHAR dateFormat[] = L"ddd, MMM dd yyyy";
WCHAR timeFormat[] = L"HH:mm:ss GMT\r\n";
WCHAR inBuf[200];
DWORD locale = LOCALE_NEUTRAL;
int ret = GetDateFormat(locale, 0, &SystemTime,
(LPTSTR)dateFormat, (LPTSTR)inBuf, sizeof inBuf);
inBuf[ret - 1] = ' ';
ret = GetTimeFormat(locale, 0, &SystemTime,
(LPTSTR)timeFormat,
(LPTSTR)inBuf + ret, (sizeof inBuf) - ret);
wcstombs(buf, inBuf, wcslen(inBuf));
#endif
return buf;
}
static char const* allowedCommandNames
= "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE";
void RTSPServer::RTSPClientSession::handleCmd_notSupported(char const* cseq) {
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
cseq, dateHeader(), allowedCommandNames);
fSessionIsActive = False; // triggers deletion of ourself after responding
}
void RTSPServer::RTSPClientSession::handleCmd_notFound(char const* cseq) {
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
cseq, dateHeader());
fSessionIsActive = False; // triggers deletion of ourself after responding
}
void RTSPServer::RTSPClientSession::handleCmd_unsupportedTransport(char const* cseq) {
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 461 Unsupported Transport\r\nCSeq: %s\r\n%s\r\n",
cseq, dateHeader());
fSessionIsActive = False; // triggers deletion of ourself after responding
}
void RTSPServer::RTSPClientSession
:: handleCmd_OPTIONS(char const* cseq)
{
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
cseq, dateHeader(), allowedCommandNames);
}
void RTSPServer::RTSPClientSession
:: handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix,
char const* fullRequestStr)
{
char* sdpDescription = NULL;
char* rtspURL = NULL;
do {
// 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 "urlSuffix":
ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
if (session == NULL) {
handleCmd_notFound(cseq);
break;
}
// Then, assemble a SDP description for this session:
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);
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;
}
char* RTSPServer
::rtspURL(ServerMediaSession const* serverMediaSession) const {
struct in_addr ourAddress;
if(ReceivingInterfaceAddr != 0)
ourAddress.s_addr = ReceivingInterfaceAddr;
else
ourAddress = CUtility::GetLocalAddr();
char const* sessionName = serverMediaSession->streamName();
unsigned sessionNameLength = strlen(sessionName);
char* urlBuffer = new char[100 + sessionNameLength];
char* resultURL;
portNumBits portNumHostOrder = ntohs(fServerPort.num());
if (portNumHostOrder == 554 /* the default port number */) {
sprintf(urlBuffer, "rtsp://%s/%s", our_inet_ntoa(ourAddress),
sessionName);
} else {
sprintf(urlBuffer, "rtsp://%s:%hu/%s",
our_inet_ntoa(ourAddress), portNumHostOrder,
sessionName);
}
resultURL = strDup(urlBuffer);
delete[] urlBuffer;
return resultURL;
}
void RTSPServer::RTSPClientSession
:: handleCmd_SETUP(char const* cseq,
char const* urlPreSuffix, char const* urlSuffix,
char const* fullRequestStr)
{
// "urlPreSuffix" should be the session (stream) name, and
// "urlSuffix" should be the subsession (track) name.
char const* streamName = urlPreSuffix;
char const* trackId = urlSuffix;
// Check whether we have existing session state, and, if so, whether it's
// for the session that's named in "streamName". (Note that we don't
// support more than one concurrent session on the same client connection.) #####
if (fOurServerMediaSession != NULL
&& strcmp(streamName, fOurServerMediaSession->streamName()) != 0)
{
fOurServerMediaSession = NULL;
}
if (fOurServerMediaSession == NULL)
{
// Set up this session's state.
// Look up the "ServerMediaSession" object for the specified stream:
if (streamName[0] != '\0' ||
fOurServer.lookupServerMediaSession("") != NULL)
{ // normal case
} else
{ // weird case: there was no track id in the URL
streamName = urlSuffix;
trackId = NULL;
}
fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);
if (fOurServerMediaSession == NULL)
{
handleCmd_notFound(cseq);
return;
}
fOurServerMediaSession->incrementReferenceCount();
// Set up our array of states for this session's subsessions (tracks):
reclaimStreamStates();
LSTServerMediaSubsession::iterator it;
fNumStreamStates = fOurServerMediaSession->fServerMediaSubSession.size();
fStreamStates = new struct streamState[fNumStreamStates];
ServerMediaSubsession* subsession = NULL;
unsigned i = 0;
for(it = fOurServerMediaSession->fServerMediaSubSession.begin();
it != fOurServerMediaSession->fServerMediaSubSession.end(); it++)
{
subsession = (*it);
fStreamStates[i].subsession = subsession;
fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later
i++;
}
}
// Look up information for the specified subsession (track):
ServerMediaSubsession* subsession = NULL;
unsigned streamNum;
if (trackId != NULL)
{ // normal case
for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum)
{
subsession = fStreamStates[streamNum].subsession;
if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
}
if (streamNum >= fNumStreamStates)
{
// The specified track id doesn't exist, so this request fails:
handleCmd_notFound(cseq);
return;
}
}
else
{
// Weird case: there was no track id in the URL.
// This works only if we have only one subsession:
if (fNumStreamStates != 1)
{
handleCmd_bad(cseq);
return;
}
streamNum = 0;
subsession = fStreamStates[streamNum].subsession;
}
// ASSERT: subsession != NULL
// Look for a "Transport:" header in the request string,
// to extract client parameters:
StreamingMode streamingMode;
char* streamingModeString; // set when RAW_UDP streaming is specified
char* clientsDestinationAddressStr;
u_int8_t clientsDestinationTTL;
portNumBits clientRTPPortNum, clientRTCPPortNum;
unsigned char rtpChannelId, rtcpChannelId;
parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
clientsDestinationAddressStr, clientsDestinationTTL,
clientRTPPortNum, clientRTCPPortNum,
rtpChannelId, rtcpChannelId);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -