📄 rtspclient.cpp
字号:
char* userAgentName = new char[userAgentNameSize]; sprintf(userAgentName, "%s%s%s%s%s", applicationName, libPrefix, libName, libVersionStr, libSuffix); setUserAgentString(userAgentName); delete[] userAgentName;}RTSPClient::~RTSPClient() { reset(); delete[] fResponseBuffer; delete[] fUserAgentHeaderStr;}Boolean RTSPClient::isRTSPClient() const { return True;}void RTSPClient::reset() { resetTCPSockets(); resetResponseBuffer(); fServerAddress = 0; setBaseURL(NULL); fCurrentAuthenticator.reset(); delete[] fLastSessionId; fLastSessionId = NULL;}void RTSPClient::resetTCPSockets() { if (fInputSocketNum >= 0) { envir().taskScheduler().disableBackgroundHandling(fInputSocketNum); ::closeSocket(fInputSocketNum); if (fOutputSocketNum != fInputSocketNum) { envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum); ::closeSocket(fOutputSocketNum); } } fInputSocketNum = fOutputSocketNum = -1;}void RTSPClient::resetResponseBuffer() { fResponseBytesAlreadySeen = 0; fResponseBufferBytesLeft = responseBufferSize;}void RTSPClient::setBaseURL(char const* url) { delete[] fBaseURL; fBaseURL = strDup(url);}int RTSPClient::openConnection() { do { // Set up a connection to the server. Begin by parsing the URL: NetAddress destAddress; portNumBits urlPortNum; char const* urlSuffix; if (!parseRTSPURL(envir(), fBaseURL, destAddress, urlPortNum, &urlSuffix)) break; portNumBits destPortNum = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum; // We don't yet have a TCP socket (or we used to have one, but it got closed). Set it up now. fInputSocketNum = fOutputSocketNum = setupStreamSocket(envir(), 0); if (fInputSocketNum < 0) break; // Connect to the remote endpoint: fServerAddress = *(unsigned*)(destAddress.data()); int connectResult = connectToServer(fInputSocketNum, destPortNum); if (connectResult < 0) break; else if (connectResult > 0) { // The connection succeeded. Arrange to handle responses to requests sent on it: envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE, (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this); } return connectResult; } while (0); resetTCPSockets(); return -1;}int RTSPClient::connectToServer(int socketNum, portNumBits remotePortNum) { MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(remotePortNum)); if (fVerbosityLevel >= 1) { envir() << "Opening connection to " << our_inet_ntoa(remoteName.sin_addr) << ", port " << remotePortNum << "...\n"; } if (connect(socketNum, (struct sockaddr*) &remoteName, sizeof remoteName) != 0) { if (envir().getErrno() == EINPROGRESS) { // The connection is pending; we'll need to handle it later. Wait for our socket to be 'writable', or have an exception. envir().taskScheduler().setBackgroundHandling(socketNum, SOCKET_WRITABLE|SOCKET_EXCEPTION, (TaskScheduler::BackgroundHandlerProc*)&connectionHandler, this); return 0; } envir().setResultErrMsg("connect() failed: "); if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n"; return -1; } if (fVerbosityLevel >= 1) envir() << "...local connection opened\n"; return 1;}char*RTSPClient::createAuthenticatorString(char const* cmd, char const* url) { Authenticator& auth = fCurrentAuthenticator; // alias, for brevity if (auth.realm() != NULL && auth.username() != NULL && auth.password() != NULL) { // We have a filled-in authenticator, so use it: char* authenticatorStr; if (auth.nonce() != NULL) { // Digest authentication char const* const authFmt = "Authorization: Digest username=\"%s\", realm=\"%s\", " "nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n"; char const* response = auth.computeDigestResponse(cmd, url); unsigned authBufSize = strlen(authFmt) + strlen(auth.username()) + strlen(auth.realm()) + strlen(auth.nonce()) + strlen(url) + strlen(response); authenticatorStr = new char[authBufSize]; sprintf(authenticatorStr, authFmt, auth.username(), auth.realm(), auth.nonce(), url, response); auth.reclaimDigestResponse(response); } else { // Basic authentication char const* const authFmt = "Authorization: Basic %s\r\n"; unsigned usernamePasswordLength = strlen(auth.username()) + 1 + strlen(auth.password()); char* usernamePassword = new char[usernamePasswordLength+1]; sprintf(usernamePassword, "%s:%s", auth.username(), auth.password()); char* response = base64Encode(usernamePassword, usernamePasswordLength); unsigned const authBufSize = strlen(authFmt) + strlen(response) + 1; authenticatorStr = new char[authBufSize]; sprintf(authenticatorStr, authFmt, response); delete[] response; delete[] usernamePassword; } return authenticatorStr; } // We don't have a (filled-in) authenticator. return strDup("");}static char* createSessionString(char const* sessionId) { char* sessionStr; if (sessionId != NULL) { sessionStr = new char[20+strlen(sessionId)]; sprintf(sessionStr, "Session: %s\r\n", sessionId); } else { sessionStr = strDup(""); } return sessionStr;}static char* createScaleString(float scale, float currentScale) { char buf[100]; if (scale == 1.0f && currentScale == 1.0f) { // This is the default value; we don't need a "Scale:" header: buf[0] = '\0'; } else { Locale l("C", LC_NUMERIC); sprintf(buf, "Scale: %f\r\n", scale); } return strDup(buf);}static char* createRangeString(double start, double end) { char buf[100]; if (start < 0) { // We're resuming from a PAUSE; there's no "Range:" header at all buf[0] = '\0'; } else if (end < 0) { // There's no end time: Locale l("C", LC_NUMERIC); sprintf(buf, "Range: npt=%.3f-\r\n", start); } else { // There's both a start and an end time; include them both in the "Range:" hdr Locale l("C", LC_NUMERIC); sprintf(buf, "Range: npt=%.3f-%.3f\r\n", start, end); } return strDup(buf);}unsigned RTSPClient::sendRequest(RequestRecord* request) { char* cmd = NULL; do { Boolean connectionIsPending = False; if (!fRequestsAwaitingConnection.isEmpty()) { // A connection is currently pending (with at least one enqueued request). Enqueue this request also: connectionIsPending = True; } else if (fInputSocketNum < 0) { // we need to open a connection int connectResult = openConnection(); if (connectResult < 0) break; // an error occurred else if (connectResult == 0) { // A connection is pending connectionIsPending = True; } // else the connection succeeded. Continue sending the command.u } if (connectionIsPending) { fRequestsAwaitingConnection.enqueue(request); return request->cseq(); } // If requested (and we're not already doing it, or have done it), set up the special protocol for tunneling RTSP-over-HTTP: if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && fOutputSocketNum == fInputSocketNum) { if (!setupHTTPTunneling1()) break; fRequestsAwaitingHTTPTunneling.enqueue(request); return request->cseq(); } // Construct and send the command: // First, construct command-specific headers that we need: char* cmdURL = fBaseURL; // by default Boolean cmdURLWasAllocated = False; char const* protocolStr = "RTSP/1.0"; // by default char* extraHeaders = (char*)""; // by default Boolean extraHeadersWereAllocated = False; char* contentLengthHeader = (char*)""; // by default Boolean contentLengthHeaderWasAllocated = False; char const* contentStr = request->contentStr(); // by default if (contentStr == NULL) contentStr = ""; unsigned contentStrLen = strlen(contentStr); if (contentStrLen > 0) { char const* contentLengthHeaderFmt = "Content-length: %d\r\n"; unsigned contentLengthHeaderSize = strlen(contentLengthHeaderFmt) + 20 /* max int len */; contentLengthHeader = new char[contentLengthHeaderSize]; sprintf(contentLengthHeader, contentLengthHeaderFmt, contentStrLen); contentLengthHeaderWasAllocated = True; } if (strcmp(request->commandName(), "DESCRIBE") == 0) { extraHeaders = (char*)"Accept: application/sdp\r\n"; } else if (strcmp(request->commandName(), "OPTIONS") == 0) { } else if (strcmp(request->commandName(), "ANNOUNCE") == 0) { extraHeaders = (char*)"Content-Type: application/sdp\r\n"; } else if (strcmp(request->commandName(), "SETUP") == 0) { MediaSubsession& subsession = *request->subsession(); Boolean streamUsingTCP = (request->booleanFlags()&0x1) != 0; Boolean streamOutgoing = (request->booleanFlags()&0x2) != 0; Boolean forceMulticastOnUnspecified = (request->booleanFlags()&0x4) != 0; char const *prefix, *separator, *suffix; constructSubsessionURL(subsession, prefix, separator, suffix); char const* transportFmt; if (strcmp(subsession.protocolName(), "UDP") == 0) { suffix = ""; transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n"; } else { transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n"; } cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1]; cmdURLWasAllocated = True; sprintf(cmdURL, "%s%s%s", prefix, separator, suffix); // Construct a "Transport:" header. char const* transportTypeStr; char const* modeStr = streamOutgoing ? ";mode=receive" : ""; // Note: I think the above is nonstandard, but DSS wants it this way char const* portTypeStr; portNumBits rtpNumber, rtcpNumber; if (streamUsingTCP) { // streaming over the RTSP connection transportTypeStr = "/TCP;unicast"; portTypeStr = ";interleaved"; rtpNumber = fTCPStreamIdCount++; rtcpNumber = fTCPStreamIdCount++; } else { // normal RTP streaming unsigned connectionAddress = subsession.connectionEndpointAddress(); Boolean requestMulticastStreaming = IsMulticastAddress(connectionAddress) || (connectionAddress == 0 && forceMulticastOnUnspecified); transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast"; portTypeStr = ";client_port"; rtpNumber = subsession.clientPortNum(); if (rtpNumber == 0) { envir().setResultMsg("Client port number unknown\n"); delete[] cmdURL; break; } rtcpNumber = rtpNumber + 1; } unsigned transportSize = strlen(transportFmt) + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */; char* transportStr = new char[transportSize]; sprintf(transportStr, transportFmt, transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber); // When sending more than one "SETUP" request, include a "Session:" header in the 2nd and later commands: char* sessionStr = createSessionString(fLastSessionId); // The "Transport:" and "Session:" (if present) headers make up the 'extra headers': extraHeaders = new char[transportSize + strlen(sessionStr)]; extraHeadersWereAllocated = True; sprintf(extraHeaders, "%s%s", transportStr, sessionStr); delete[] transportStr; delete[] sessionStr; } else if (strcmp(request->commandName(), "GET") == 0 || strcmp(request->commandName(), "POST") == 0) { NetAddress destAddress; portNumBits urlPortNum; if (!parseRTSPURL(envir(), fBaseURL, destAddress, urlPortNum, (char const**)&cmdURL)) break; if (cmdURL[0] == '\0') cmdURL = (char*)"/"; protocolStr = "HTTP/1.0"; if (strcmp(request->commandName(), "GET") == 0) { // Create a 'session cookie' string, using MD5: struct { struct timeval timestamp; unsigned counter; } seedData; gettimeofday(&seedData.timestamp, NULL); seedData.counter = ++fSessionCookieCounter; our_MD5Data((unsigned char*)(&seedData), sizeof seedData, fSessionCookie); // DSS seems to require that the 'session cookie' string be 22 bytes long: fSessionCookie[23] = '\0'; char const* const extraHeadersFmt = "x-sessioncookie: %s\r\n" "Accept: application/x-rtsp-tunnelled\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -