llive 555 信令类及消息

main  函数:

int main(int argc, char** argv) {// Begin by setting up our usage environment:TaskScheduler* scheduler = BasicTaskScheduler::createNew();UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);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 <username>, <password> 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;    /*创建rtsp 服务*/rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);if (rtspServer == NULL) {rtspServerPortNum = 8554;rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);}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";*env << "Play streams from this server using the URL\n";if (weHaveAnIPv4Address(*env)) {char* rtspURLPrefix = rtspServer->ipv4rtspURLPrefix();*env << "\t" << rtspURLPrefix << "<filename>\n";delete[] rtspURLPrefix;if (weHaveAnIPv6Address(*env)) *env << "or\n";}if (weHaveAnIPv6Address(*env)) {char* rtspURLPrefix = rtspServer->ipv6rtspURLPrefix();*env << "\t" << rtspURLPrefix << "<filename>\n";delete[] rtspURLPrefix;}*env << "where <filename> 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).)\n";} else {*env << "(RTSP-over-HTTP tunneling is not available.)\n";}env->taskScheduler().doEventLoop(); // does not returnreturn 0; // only to prevent compiler warning
}

main 函数中:1)new 一个DynamicRTSPServer类:DynamicRTSPServer 改类继承RTSPServer

2)setUpTunnelingOverHTTP,监听来自80等端口

Boolean RTSPServer::setUpTunnelingOverHTTP(Port httpPort) {fHTTPServerSocketIPv4 = setUpOurSocket(envir(), httpPort, AF_INET);fHTTPServerSocketIPv6 = setUpOurSocket(envir(), httpPort, AF_INET6);if (fHTTPServerSocketIPv4 >= 0 || fHTTPServerSocketIPv6 >= 0) {fHTTPServerPort = httpPort;envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocketIPv4,incomingConnectionHandlerHTTPIPv4, this);envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocketIPv6,incomingConnectionHandlerHTTPIPv6, this);return True;}return False;
}

setUpTunnelingOverHTTP  函数一个任务,一直调用incomingConnectionHandlerHTTPIPv4或者

incomingConnectionHandlerHTTPIPv6

void RTSPServer::incomingConnectionHandlerHTTPIPv4(void* instance, int /*mask*/) {RTSPServer* server = (RTSPServer*)instance;server->incomingConnectionHandlerHTTPIPv4();
}
void RTSPServer::incomingConnectionHandlerHTTPIPv4() {incomingConnectionHandlerOnSocket(fHTTPServerSocketIPv4);
}
void RTSPServer::incomingConnectionHandlerHTTPIPv6(void* instance, int /*mask*/) {RTSPServer* server = (RTSPServer*)instance;server->incomingConnectionHandlerHTTPIPv6();
}
void RTSPServer::incomingConnectionHandlerHTTPIPv6() {incomingConnectionHandlerOnSocket(fHTTPServerSocketIPv6);
}

incomingConnectionHandlerHTTPIPv4 调用incomingConnectionHandlerOnSocket函数

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {struct sockaddr_storage 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;}ignoreSigPipeOnSocket(clientSocket); // so that clients on the same host that are killed don't also kill usmakeSocketNonBlocking(clientSocket);increaseSendBufferTo(envir(), clientSocket, 50*1024);#ifdef DEBUGenvir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif// Create a new object for handling this connection:(void)createNewClientConnection(clientSocket, clientAddr);
}

调用accept 函数,有网络请求,就调用createNewClientConnection创建RTSPClientConnection

GenericMediaServer::ClientConnection*
RTSPServer::createNewClientConnection(int clientSocket, struct sockaddr_storage const& clientAddr) {return new RTSPClientConnection(*this, clientSocket, clientAddr, fOurConnectionsUseTLS);
}
RTSPServer::RTSPClientConnection
::RTSPClientConnection(RTSPServer& ourServer,int clientSocket, struct sockaddr_storage const& clientAddr,Boolean useTLS): GenericMediaServer::ClientConnection(ourServer, clientSocket, clientAddr, useTLS),fOurRTSPServer(ourServer), fClientInputSocket(fOurSocket), fClientOutputSocket(fOurSocket),fAddressFamily(clientAddr.ss_family),fIsActive(True), fRecursionCount(0), fOurSessionCookie(NULL), fScheduledDelayedTask(0) {resetRequestBuffer();
}

RTSPClientConnection 类构造函数:调用GenericMediaServer::ClientConnection(ourServer, clientSocket, clientAddr, useTLS);


GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer& ourServer,int clientSocket, struct sockaddr_storage const& clientAddr,Boolean useTLS): fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr), fTLS(envir()) {// Add ourself to our 'client connections' table:fOurServer.fClientConnections->Add((char const*)this, this);if (useTLS) {// Perform extra processing to handle a TLS connection:fTLS.setCertificateAndPrivateKeyFileNames(ourServer.fTLSCertificateFileName,ourServer.fTLSPrivateKeyFileName);fTLS.isNeeded = True;fTLS.tlsAcceptIsNeeded = True; // call fTLS.accept() the next time the socket is readable}// Arrange to handle incoming requests:resetRequestBuffer();envir().taskScheduler().setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
}

ClientConnection 构造函数:起一个任务一直调用incomingRequestHandler 函数

void GenericMediaServer::ClientConnection::incomingRequestHandler(void* instance, int /*mask*/) {ClientConnection* connection = (ClientConnection*)instance;connection->incomingRequestHandler();
}void GenericMediaServer::ClientConnection::incomingRequestHandler() {if (fTLS.tlsAcceptIsNeeded) { // we need to successfully call fTLS.accept() first:if (fTLS.accept(fOurSocket) <= 0) return; // either an error, or we need to try again laterfTLS.tlsAcceptIsNeeded = False;// We can now read data, as usual:}int bytesRead;if (fTLS.isNeeded) {bytesRead = fTLS.read(&fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft);} else {struct sockaddr_storage dummy; // 'from' address, meaningless in this casebytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);}handleRequestBytes(bytesRead);
}

incomingRequestHandler  函数调用handleRequestBytes 函数

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {int numBytesRemaining = 0;++fRecursionCount;do {RTSPServer::RTSPClientSession* clientSession = NULL;if (newBytesRead < 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {// Either the client socket has died, or the request was too big for us.// Terminate this connection:
#ifdef DEBUGfprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
#endiffIsActive = False;break;}Boolean endOfMsg = False;unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
#ifdef DEBUGptr[newBytesRead] = '\0';fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() %s %d new bytes:%s\n",this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);
#endifif (fClientOutputSocket != fClientInputSocket && numBytesRemaining == 0) {// 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).// But first, we remove any whitespace that may be in the input data:unsigned toIndex = 0;for (int fromIndex = 0; fromIndex < newBytesRead; ++fromIndex) {char c = ptr[fromIndex];if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) { // not 'whitespace': space,tab,CR,NLptr[toIndex++] = c;}}newBytesRead = toIndex;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), numBytesToDecode, decodedSize);
#ifdef DEBUGfprintf(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 - fBase64RemainderCount + newBase64RemainderCount;// adjust to allow for the size of the new decoded data (+ remainder)delete[] decodedBytes;}fBase64RemainderCount = newBase64RemainderCount;}unsigned char* tmpPtr = fLastCRLF + 2;if (fBase64RemainderCount == 0) { // no more Base-64 bytes remain to be read/decoded// Look for the end of the message: <CR><LF><CR><LF>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// 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];char sessionIdStr[RTSP_PARAM_STRING_MAX];unsigned contentLength = 0;Boolean urlIsRTSPS;Boolean playAfterSetup = False;fLastCRLF[2] = '\0'; // temporarily, for parsingBoolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer,cmdName, sizeof cmdName,urlPreSuffix, sizeof urlPreSuffix,urlSuffix, sizeof urlSuffix,cseq, sizeof cseq,sessionIdStr, sizeof sessionIdStr,contentLength, urlIsRTSPS);fLastCRLF[2] = '\r'; // restore its value// Check first for a bogus "Content-Length" value that would cause a pointer wraparound:if (tmpPtr + 2 + contentLength < tmpPtr + 2) {
#ifdef DEBUGfprintf(stderr, "parseRTSPRequestString() returned a bogus \"Content-Length:\" value: 0x%x (%d)\n", contentLength, (int)contentLength);
#endifcontentLength = 0;parseSucceeded = False;}if (parseSucceeded) {
#ifdef DEBUGfprintf(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 the request included a "Session:" id, and it refers to a client session that's// current ongoing, then use this command to indicate 'liveness' on that client session:Boolean const requestIncludedSessionId = sessionIdStr[0] != '\0';if (requestIncludedSessionId) {clientSession= (RTSPServer::RTSPClientSession*)(fOurRTSPServer.lookupClientSession(sessionIdStr));if (clientSession != NULL) clientSession->noteLiveness();}// We now have a complete RTSP request.// Handle the specified command (beginning with commands that are session-independent):fCurrentCSeq = cseq;// If the request specified the wrong type of URL// (i.e., "rtsps" instead of "rtsp", or vice versa), then send back a 'redirect':if (urlIsRTSPS != fOurRTSPServer.fWeServeSRTP) {
#ifdef DEBUGfprintf(stderr, "Calling handleCmd_redirect()\n");
#endifhandleCmd_redirect(urlSuffix);} else if (strcmp(cmdName, "OPTIONS") == 0) {// If the "OPTIONS" command included a "Session:" id for a session that doesn't exist,// then treat this as an error:if (requestIncludedSessionId && clientSession == NULL) {
#ifdef DEBUGfprintf(stderr, "Calling handleCmd_sessionNotFound() (case 1)\n");
#endifhandleCmd_sessionNotFound();} else {// Normal case:handleCmd_OPTIONS();}} else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {// The special "*" URL means: an operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:if (strcmp(cmdName, "GET_PARAMETER") == 0) {handleCmd_GET_PARAMETER((char const*)fRequestBuffer);} else if (strcmp(cmdName, "SET_PARAMETER") == 0) {handleCmd_SET_PARAMETER((char const*)fRequestBuffer);} else {handleCmd_notSupported();}} else if (strcmp(cmdName, "DESCRIBE") == 0) {handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);} else if (strcmp(cmdName, "SETUP") == 0) {Boolean areAuthenticated = True;if (!requestIncludedSessionId) {// No session id was present in the request.// So create a new "RTSPClientSession" object for this request.// But first, make sure that we're authenticated to perform this command:char urlTotalSuffix[2*RTSP_PARAM_STRING_MAX];// enough space for urlPreSuffix/urlSuffix'\0'urlTotalSuffix[0] = '\0';if (urlPreSuffix[0] != '\0') {strcat(urlTotalSuffix, urlPreSuffix);strcat(urlTotalSuffix, "/");}strcat(urlTotalSuffix, urlSuffix);if (authenticationOK("SETUP", urlTotalSuffix, (char const*)fRequestBuffer)) {clientSession= (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();} else {areAuthenticated = False;}}if (clientSession != NULL) {clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);playAfterSetup = clientSession->fStreamAfterSETUP;} else if (areAuthenticated) {
#ifdef DEBUGfprintf(stderr, "Calling handleCmd_sessionNotFound() (case 2)\n");
#endifhandleCmd_sessionNotFound();}} else if (strcmp(cmdName, "TEARDOWN") == 0|| strcmp(cmdName, "PLAY") == 0|| strcmp(cmdName, "PAUSE") == 0|| strcmp(cmdName, "GET_PARAMETER") == 0|| strcmp(cmdName, "SET_PARAMETER") == 0) {if (clientSession != NULL) {clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);} else {
#ifdef DEBUGfprintf(stderr, "Calling handleCmd_sessionNotFound() (case 3)\n");
#endifhandleCmd_sessionNotFound();}} else if (strcmp(cmdName, "REGISTER") == 0 || strcmp(cmdName, "DEREGISTER") == 0) {// Because - unlike other commands - an implementation of this command needs// the entire URL, we re-parse the command to get it:char* url = strDupSize((char*)fRequestBuffer);if (sscanf((char*)fRequestBuffer, "%*s %s", url) == 1) {// Check for special command-specific parameters in a "Transport:" header:Boolean reuseConnection, deliverViaTCP;char* proxyURLSuffix;parseTransportHeaderForREGISTER((const char*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);handleCmd_REGISTER(cmdName, url, urlSuffix, (char const*)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);delete[] proxyURLSuffix;} else {handleCmd_bad();}delete[] url;} else {// The command is one that we don't handle:handleCmd_notSupported();}} else {
#ifdef DEBUGfprintf(stderr, "parseRTSPRequestString() failed; checking now for HTTP commands (for RTSP-over-HTTP tunneling)...\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 parsingparseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,urlSuffix, sizeof urlPreSuffix,sessionCookie, sizeof sessionCookie,acceptStr, sizeof acceptStr);*fLastCRLF = '\r';if (parseSucceeded) {
#ifdef DEBUGfprintf(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 (strcmp(cmdName, "OPTIONS") == 0) {handleHTTPCmd_OPTIONS();} else 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:fIsActive = False;break;}} else {isValidHTTPCmd = False;}if (!isValidHTTPCmd) {handleHTTPCmd_notSupported();}} else {
#ifdef DEBUGfprintf(stderr, "parseHTTPRequestString() failed!\n");
#endifhandleCmd_bad();}}#ifdef DEBUGfprintf(stderr, "sending response: %s", fResponseBuffer);
#endifunsigned const numBytesToWrite = strlen((char*)fResponseBuffer);if (fTLS.isNeeded) {fTLS.write((char const*)fResponseBuffer, numBytesToWrite);} else {send(fClientOutputSocket, (char const*)fResponseBuffer, numBytesToWrite, 0);}if (playAfterSetup) {// The client has asked for streaming to commence now, rather than after a// subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:clientSession->handleCmd_withinSession(this, "PLAY", urlPreSuffix, urlSuffix, (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 requestif (numBytesRemaining > 0) {memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);newBytesRead = numBytesRemaining;}} while (numBytesRemaining > 0);--fRecursionCount;// If it has a scheduledDelayedTask, don't delete the instance or close the sockets. The sockets can be reused in the task.if (!fIsActive && fScheduledDelayedTask <= 0) {if (fRecursionCount > 0) closeSockets(); else delete this;// Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively// while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).// In such a case we don't want to actually delete ourself until we leave the outermost call.}
}

handleRequestBytes 处理rtsp 信令流程。

信息令抓包数据

OPTIONS rtsp://192.168.31.188:8000/slamtv60.264 RTSP/1.0

CSeq: 2

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

RTSP/1.0 200 OK

CSeq: 2

Date: Sat, Sep 02 2017 07:28:23 GMT

Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER

DESCRIBE rtsp://192.168.31.188:8000/slamtv60.264 RTSP/1.0

CSeq: 3

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Accept: application/sdp

RTSP/1.0 200 OK

CSeq: 3

Date: Sat, Sep 02 2017 07:28:23 GMT

Content-Base: rtsp://192.168.31.188:8554/slamtv60.264/

Content-Type: application/sdp

Content-Length: 526

v=0

o=- 1504337303775687 1 IN IP4 192.168.31.188

s=H.264 Video, streamed by the LIVE555 Media Server

i=slamtv60.264

t=0 0

a=tool:LIVE555 Streaming Media v2017.07.18

a=type:broadcast

a=control:*

a=range:npt=0-

a=x-qt-text-nam:H.264 Video, streamed by the LIVE555 Media Server

a=x-qt-text-inf:slamtv60.264

m=video 0 RTP/AVP 96

c=IN IP4 0.0.0.0

b=AS:500

a=rtpmap:96 H264/90000

a=fmtp:96 packetization-mode=1;profile-level-id=4D4033;sprop-parameter-sets=Z01AM5JUDAS0IAAAAwBAAAAM0eMGVA==,aO48gA==

a=control:track1

SETUP rtsp://192.168.31.188:8554/slamtv60.264/track1 RTSP/1.0

CSeq: 4

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Transport: RTP/AVP;unicast;client_port=49484-49485

RTSP/1.0 200 OK

CSeq: 4

Date: Sat, Sep 02 2017 07:28:23 GMT

Transport: RTP/AVP;unicast;destination=192.168.31.146;source=192.168.31.188;client_port=49484-49485;server_port=6970-6971

Session: EF285CD9;timeout=65

PLAY rtsp://192.168.31.188:8554/slamtv60.264/ RTSP/1.0

CSeq: 5

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Session: EF285CD9

Range: npt=0.000-

RTSP/1.0 200 OK

CSeq: 5

Date: Sat, Sep 02 2017 07:28:23 GMT

Range: npt=0.000-

Session: EF285CD9

RTP-Info: url=rtsp://192.168.31.188:8554/slamtv60.264/track1;seq=7267;rtptime=3188764371

GET_PARAMETER rtsp://192.168.31.188:8554/slamtv60.264/ RTSP/1.0

CSeq: 6

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Session: EF285CD9

RTSP/1.0 200 OK

CSeq: 6

Date: Sat, Sep 02 2017 07:28:23 GMT

Session: EF285CD9

Content-Length: 10

TEARDOWN rtsp://192.168.31.188:8554/slamtv60.264/ RTSP/1.0

CSeq: 7

User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)

Session: EF285CD9

RTSP/1.0 200 OK

CSeq: 7

Date: Sat, Sep 02 2017 07:28:34 GMT

live555 信令分发相关推荐

  1. 微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog

    前言 mars 是微信官方的终端基础组件,是一个使用 C++ 编写的业务性无关,平台性无关的基础组件.目前已接入微信 Android.iOS.Mac.Windows.WP 等客户端.现正在筹备开源中, ...

  2. 【拓展】腾讯十大最受欢迎的开源项目!

    高性能通用 key-value 组件 MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强.从 2015 年中至今在微信 ...

  3. 转战物联网·基础篇13-了解物联网之物连接相关通信技术(2)

    转战物联网·基础篇13-了解物联网之物连接相关通信技术(2) 三.硬件设备间组网的通信技术与通信协议(2) 11.6LoWPAN 12.PROFINET 13.EtherCAT 14.RFID.NFC ...

  4. 腾讯十大最受欢迎的开源项目!

    高性能通用 key-value 组件 MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强.从 2015 年中至今在微信 ...

  5. 腾讯十大开源项目,最后一个太受欢迎了!

    点击上方关注 前端技术江湖,一起学习,天天进步 高性能通用 key-value 组件 MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现 ...

  6. 跨平台基础网络框架Mars初探

    前言 对于新派单通知.订单时效变更通知等需要及时反馈给用户的消息,目前点我达骑手的解决方案是定时轮询并通过http请求的方式主动从服务器获取变更,这种方式存在一定的缺陷,如http方式轮询流量消耗较高 ...

  7. 你还记得几个腾讯的开源项目,这十个你用过吗

    前言 腾讯开源了许多非常有价值的项目,下面我们一起来看看腾讯10大开源项目有哪些 1.Android 热修复框架 Tinker Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码 ...

  8. Windows PC、 Linux、 Android、 iOS多平台支持H5无插件播放RTSP摄像机解决方案

    Windows PC. Linux. Android. iOS多平台支持H5无插件播放RTSP摄像机解决方案 需求分析 视频流媒体监控行业已经进入互联网时代,浏览器承载了绝大多数的互联网访问流量,目前 ...

  9. 实际中的WebRTC:STUN,TURN以及信令(五)

    原标题:WebRTC in the real world: STUN, TURN and signaling 前文链接:实际中的WebRTC:STUN,TURN以及信令(一),实际中的WebRTC:S ...

最新文章

  1. apt-get update,apt-get upgrade,apt-get dist-upgrade的作用
  2. 01H5-fe-html5-005插入音频
  3. 如何用vue-router为每个路由配置各自的title
  4. thinkphp3 php jwt,ThinkPHP5 使用 JWT 进行加密
  5. 强烈推荐:给去美国的新生说几句(转载),超实用
  6. 0.typescript-相关文档
  7. 抢注“哔哩哔哩”商标卖成人用品?A站回应:不符合价值观 已申请注销
  8. linux命令的-和--参数问题
  9. 基于CentOS7上的搭建javaweb环境 - 学习笔记
  10. CentOS6.5菜鸟之旅:中文编辑器忍痛放弃Sublime
  11. 鸿蒙系统是华容网格吗,鸿蒙上手机还在迟疑,国内对手却已悄然来到
  12. 容器技术Docker K8s 18 容器服务ACK基础与进阶-容器网络管理
  13. C#WINFORM控件之ComboBox
  14. element ui的table组件在鼠标滑动时边框线消失的解决
  15. Chrome浏览器如何完美实现截长屏幕
  16. 关于RJ45 网线接线问题
  17. 研究生预备军:论文选题与写作
  18. 第二人生的源码分析(三十九)关闭WinXP的错误报告功能
  19. NASA准备在2021年推出最大望远镜!哈佛用棉花糖机造“肉”?
  20. 苹果应用分身_你喜欢用苹果手机拍照!不学会这4个功能,怪不得拍不出好照片...

热门文章

  1. k8s集群reset恢复重置
  2. Acer 4750 安装黑苹果_黑苹果的安装(干货)
  3. 获取项目服务器ip,java获取服务器ip地址
  4. 科技无障碍盛会举办,人工智能和创新成为高频词
  5. 【渝粤题库】国家开放大学2021春1040公司概论题目
  6. Opencv值core组件(二):感兴趣区域选取与计算数组加权和
  7. python完成文件夹批量word转pdf文件及pdf文件合并+word文件合并
  8. css实现盾牌的动画效果
  9. Python-----(3)数
  10. 阿里云的云安全防护产品有哪些?都有什么作用?