📄 rtspclient.cpp
字号:
// Also, tell the RDT source to use the RTSP TCP socket: RealRDTSource* rdtSource = (RealRDTSource*)(subsession.readSource()); rdtSource->setInputSocket(fInputSocketNum); } }#endif char const *prefix, *separator, *suffix; constructSubsessionURL(subsession, prefix, separator, suffix); char* transportFmt; if (strcmp(subsession.protocolName(), "UDP") == 0) { char const* setupFmt = "SETUP %s%s RTSP/1.0\r\n"; unsigned setupSize = strlen(setupFmt) + strlen(prefix) + strlen (separator); setupStr = new char[setupSize]; sprintf(setupStr, setupFmt, prefix, separator); transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n"; } else { char const* setupFmt = "SETUP %s%s%s RTSP/1.0\r\n"; unsigned setupSize = strlen(setupFmt) + strlen(prefix) + strlen (separator) + strlen(suffix); setupStr = new char[setupSize]; sprintf(setupStr, setupFmt, prefix, separator, suffix); transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n"; } if (transportStr == NULL) { // 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; unsigned short 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"); break; } rtcpNumber = rtpNumber + 1; } unsigned transportSize = strlen(transportFmt) + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */; transportStr = new char[transportSize]; sprintf(transportStr, transportFmt, transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber); } // (Later implement more, as specified in the RTSP spec, sec D.1 #####) char* const cmdFmt = "%s" "CSeq: %d\r\n" "%s" "%s" "%s" "%s" "\r\n"; unsigned cmdSize = strlen(cmdFmt) + strlen(setupStr) + 20 /* max int len */ + strlen(transportStr) + strlen(sessionStr) + strlen(authenticatorStr) + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, setupStr, ++fCSeq, transportStr, sessionStr, authenticatorStr, fUserAgentHeaderStr); delete[] authenticatorStr; if (sessionStr[0] != '\0') delete[] sessionStr; delete[] setupStr; delete[] transportStr; // And then send it: if (!sendRequest(cmd, "SETUP")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("SETUP", bytesRead, responseCode, firstLine, nextLineStart)) break; // Look for a "Session:" header (to set our session id), and // a "Transport: " header (to set the server address/port) // For now, ignore other headers. char* lineStart; char* sessionId = new char[fResponseBufferSize]; // ensures we have enough space unsigned cLength = 0; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (sscanf(lineStart, "Session: %[^;]", sessionId) == 1) { subsession.sessionId = strDup(sessionId); delete[] fLastSessionId; fLastSessionId = strDup(sessionId); // Also look for an optional "; timeout = " parameter following this: char* afterSessionId = lineStart + strlen(sessionId) + strlen ("Session: ");; int timeoutVal; if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) { fSessionTimeoutParameter = timeoutVal; } continue; } char* serverAddressStr; portNumBits serverPortNum; unsigned char rtpChannelId, rtcpChannelId; if (parseTransportResponse(lineStart, serverAddressStr, serverPortNum, rtpChannelId, rtcpChannelId)) { delete[] subsession.connectionEndpointName(); subsession.connectionEndpointName() = serverAddressStr; subsession.serverPortNum = serverPortNum; subsession.rtpChannelId = rtpChannelId; subsession.rtcpChannelId = rtcpChannelId; continue; } // Also check for a "Content-Length:" header. Some weird servers include this // in the RTSP "SETUP" response. if (sscanf(lineStart, "Content-Length: %d", &cLength) == 1) continue; } delete[] sessionId; if (subsession.sessionId == NULL) { envir().setResultMsg("\"Session:\" header is missing in the response"); break; } // If we saw a "Content-Length:" header in the response, then discard whatever // included data it refers to: if (cLength > 0) { char* dummyBuf = new char[cLength]; getResponse1(dummyBuf, cLength); delete[] dummyBuf; } if (streamUsingTCP) { // Tell the subsession to receive RTP (and send/receive RTCP) // over the RTSP stream: if (subsession.rtpSource() != NULL) subsession.rtpSource()->setStreamSocket(fInputSocketNum, subsession.rtpChannelId); if (subsession.rtcpInstance() != NULL) subsession.rtcpInstance()->setStreamSocket(fInputSocketNum, subsession.rtcpChannelId); } else { // Normal case. // Set the RTP and RTCP sockets' destination address and port // from the information in the SETUP response (if present): netAddressBits destAddress = subsession.connectionEndpointAddress(); if (destAddress == 0) destAddress = fServerAddress; subsession.setDestinations(destAddress); } delete[] cmd; return True; } while (0); delete[] cmd; return False;}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("C", LC_NUMERIC); sprintf(buf, "Scale: %f\r\n", scale); } return strDup(buf);} static char* createRangeString(float start, float 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("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("C", LC_NUMERIC); sprintf(buf, "Range: npt=%.3f-%.3f\r\n", start, end); } return strDup(buf);} static char const* NoSessionErr = "No RTSP session is currently in progress\n";Boolean RTSPClient::playMediaSession(MediaSession& session, float start, float end, float scale) {#ifdef SUPPORT_REAL_RTSP if (session.isRealNetworksRDT) { // This is a RealNetworks stream; set the "Subscribe" parameter before proceeding: char* streamRuleString = RealGetSubscribeRuleString(&session); setMediaSessionParameter(session, "Subscribe", streamRuleString); delete[] streamRuleString; }#endif char* cmd = NULL; do { // First, make sure that we have a RTSP session in progress if (fLastSessionId == NULL) { envir().setResultMsg(NoSessionErr); break; } // Send the PLAY command: // First, construct an authenticator string: char* authenticatorStr = createAuthenticatorString(&fCurrentAuthenticator, "PLAY", fBaseURL); // And then a "Scale:" string: char* scaleStr = createScaleString(scale, session.scale()); // And then a "Range:" string: char* rangeStr = createRangeString(start, end); char* const cmdFmt = "PLAY %s RTSP/1.0\r\n" "CSeq: %d\r\n" "Session: %s\r\n" "%s" "%s" "%s" "%s" "\r\n"; char const* sessURL = sessionURL(session); unsigned cmdSize = strlen(cmdFmt) + strlen(sessURL) + 20 /* max int len */ + strlen(fLastSessionId) + strlen(scaleStr) + strlen(rangeStr) + strlen(authenticatorStr) + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, sessURL, ++fCSeq, fLastSessionId, scaleStr, rangeStr, authenticatorStr, fUserAgentHeaderStr); delete[] scaleStr; delete[] rangeStr; delete[] authenticatorStr; if (!sendRequest(cmd, "PLAY")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("PLAY", bytesRead, responseCode, firstLine, nextLineStart)) break; // Look for various headers that we understand: char* lineStart; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (parseScaleHeader(lineStart, session.scale())) continue; if (parseRangeHeader(lineStart, session.playStartTime(), session.playEndTime())) continue; u_int16_t seqNum; u_int32_t timestamp; if (parseRTPInfoHeader(lineStart, seqNum, timestamp)) { // This is data for our first subsession. Fill it in, and do the same for our other subsessions: MediaSubsessionIterator iter(session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { subsession->rtpInfo.seqNum = seqNum; subsession->rtpInfo.timestamp = timestamp; subsession->rtpInfo.infoIsNew = True; if (!parseRTPInfoHeader(lineStart, seqNum, timestamp)) break; } continue; } } if (fTCPStreamIdCount == 0) { // we're not receiving RTP-over-TCP // Arrange to handle incoming requests sent by the server envir().taskScheduler().turnOnBackgroundReadHandling(fInputSocketNum, (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this); } delete[] cmd; return True; } while (0); delete[] cmd; return False;}Boolean RTSPClient::playMediaSubsession(MediaSubsession& subsession, float start, float end, float scale, Boolean hackForDSS) { char* cmd = NULL; do { // First, make sure that we have a RTSP session in progress if (subsession.sessionId == NULL) { envir().setResultMsg(NoSessionErr); break; } // Send the PLAY command: // First, construct an authenticator string: char* authenticatorStr = createAuthenticatorString(&fCurrentAuthenticator, "PLAY", fBaseURL); // And then a "Scale:" string: char* scaleStr = createScaleString(scale, subsession.scale()); // And then a "Range:" string: char* rangeStr = createRangeString(start, end); char* const cmdFmt = "PLAY %s%s%s RTSP/1.0\r\n" "CSeq: %d\r\n" "Session: %s\r\n" "%s" "%s" "%s" "%s" "\r\n"; char const *prefix, *separator, *suffix; constructSubsessionURL(subsession, prefix, separator, suffix); if (hackForDSS || fServerIsKasenna) { // When "PLAY" is used to inject RTP packets into a DSS // (violating the RTSP spec, btw; "RECORD" should have been used) // the DSS can crash (or hang) if the '/trackid=...' portion of // the URL is present. separator = suffix = ""; } unsigned cmdSize = strlen(cmdFmt) + strlen(prefix) + strlen(separator) + strlen(suffix) + 20 /* max int len */ + strlen(subsession.sessionId) + strlen(scaleStr) + strlen(rangeStr) + strlen(authenticatorStr) + fUserAgentHeaderStrSize; cmd = new char[cmdSize]; sprintf(cmd, cmdFmt, prefix, separator, suffix, ++fCSeq, subsession.sessionId, scaleStr, rangeStr, authenticatorStr, fUserAgentHeaderStr); delete[] scaleStr; delete[] rangeStr; delete[] authenticatorStr; if (!sendRequest(cmd, "PLAY")) break; // Get the response from the server: unsigned bytesRead; unsigned responseCode; char* firstLine; char* nextLineStart; if (!getResponse("PLAY", bytesRead, responseCode, firstLine, nextLineStart)) break; // Look for various headers that we understand: char* lineStart; while (1) { lineStart = nextLineStart; if (lineStart == NULL) break; nextLineStart = getLine(lineStart); if (parseScaleHeader(lineStart, subsession.scale())) continue; if (parseRangeHeader(lineStart, subsession._playStartTime(), subsession._playEndTime())) continue; u_int16_t seqNum; u_int32_t timestamp; if (parseRTPInfoHeader(lineStart, seqNum, timestamp)) { subsession.rtpInfo.seqNum = seqNum; subsession.rtpInfo.timestamp = timestamp; subsession.rtpInfo.infoIsNew = True; continue; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -