📄 rtspclient.cpp
字号:
envir().setResultMsg("Missing or bad \"Transport:\" header"); break; } delete[] subsession.connectionEndpointName(); subsession.connectionEndpointName() = serverAddressStr; subsession.serverPortNum = serverPortNum; subsession.rtpChannelId = rtpChannelId; subsession.rtcpChannelId = rtcpChannelId; 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); subsession.rtpSource()->setServerRequestAlternativeByteHandler(handleAlternativeRequestByte, this); } 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); } success = True; } while (0); delete[] sessionId; return success;}Boolean RTSPClient::handlePLAYResponse(MediaSession& session, MediaSubsession& subsession, char const* scaleParamsStr, char const* rangeParamsStr, char const* rtpInfoParamsStr) { Boolean scaleOK = False, rangeOK = False; do { if (&session != NULL) { // The command was on the whole session if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, session.scale())) break; scaleOK = True; if (rangeParamsStr != NULL && !parseRangeParam(rangeParamsStr, session.playStartTime(), session.playEndTime())) break; rangeOK = True; u_int16_t seqNum; u_int32_t timestamp; if (rtpInfoParamsStr != NULL) { if (!parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) break; // 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 (!parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) break; } } } else { // The command was on a subsession if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, subsession.scale())) break; scaleOK = True; if (rangeParamsStr != NULL && !parseRangeParam(rangeParamsStr, subsession._playStartTime(), subsession._playEndTime())) break; rangeOK = True; u_int16_t seqNum; u_int32_t timestamp; if (rtpInfoParamsStr != NULL) { if (!parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) break; subsession.rtpInfo.seqNum = seqNum; subsession.rtpInfo.timestamp = timestamp; subsession.rtpInfo.infoIsNew = True; } } return True; } while (0); // An error occurred: if (!scaleOK) { envir().setResultMsg("Bad \"Scale:\" header"); } else if (!rangeOK) { envir().setResultMsg("Bad \"Range:\" header"); } else { envir().setResultMsg("Bad \"RTP-Info:\" header"); } return False;}Boolean RTSPClient::handleTEARDOWNResponse(MediaSession& session, MediaSubsession& subsession) { if (&session != NULL) { // The command was on the whole session // Run through each subsession, deleting its "sessionId": MediaSubsessionIterator iter(session); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { delete[] (char*)subsession->sessionId; subsession->sessionId = NULL; } } else { // The command was on a subsession delete[] (char*)subsession.sessionId; subsession.sessionId = NULL; } return True;}Boolean RTSPClient::handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString) { do { // If "parameterName" is non-empty, it should be (possibly followed by ':' and whitespace) at the start of the result string: if (parameterName != NULL && parameterName[0] != '\0') { if (parameterName[1] == '\0') break; // sanity check; there should have been \r\n at the end of "parameterName" unsigned parameterNameLen = strlen(parameterName); // ASSERT: parameterNameLen >= 2; parameterNameLen -= 2; // because of the trailing \r\n if (_strncasecmp(resultValueString, parameterName, parameterNameLen) != 0) break; // parameter name wasn't in the output resultValueString += parameterNameLen; if (resultValueString[0] == ':') ++resultValueString; while (resultValueString[0] == ' ' || resultValueString[0] == '\t') ++resultValueString; } // The rest of "resultValueStr" should be our desired result, but first trim off any \r and/or \n characters at the end: unsigned resultLen = strlen(resultValueString); while (resultLen > 0 && (resultValueString[resultLen-1] == '\r' || resultValueString[resultLen-1] == '\n')) --resultLen; resultValueString[resultLen] = '\0'; return True; } while (0); // An error occurred: envir().setResultMsg("Bad \"GET_PARAMETER\" response"); return False;}Boolean RTSPClient::handleAuthenticationFailure(char const* paramsStr) { // Fill in "fCurrentAuthenticator" with the information from the "WWW-Authenticate:" header: Boolean alreadyHadRealm = fCurrentAuthenticator.realm() != NULL; char* realm = strDupSize(paramsStr); char* nonce = strDupSize(paramsStr); Boolean success = True; if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) { fCurrentAuthenticator.setRealmAndNonce(realm, nonce); } else if (sscanf(paramsStr, "Basic realm=\"%[^\"]\"", realm) == 1) { fCurrentAuthenticator.setRealmAndNonce(realm, NULL); // Basic authentication } else { success = False; // bad "WWW-Authenticate:" header } delete[] realm; delete[] nonce; if (alreadyHadRealm || fCurrentAuthenticator.username() == NULL || fCurrentAuthenticator.password() == NULL) { // We already had a 'realm', or don't have a username and/or password, // so the new "WWW-Authenticate:" header information won't help us. We remain unauthenticated. success = False; } return success;}Boolean RTSPClient::resendCommand(RequestRecord* request) { if (fVerbosityLevel >= 1) envir() << "Resending...\n"; if (request != NULL) request->cseq() = ++fCSeq; return sendRequest(request) != 0;}char const* RTSPClient::sessionURL(MediaSession const& session) const { char const* url = session.controlPath(); if (url == NULL || strcmp(url, "*") == 0) url = fBaseURL; return url;}void RTSPClient::handleAlternativeRequestByte(void* rtspClient, u_int8_t requestByte) { ((RTSPClient*)rtspClient)->handleAlternativeRequestByte1(requestByte);}void RTSPClient::handleAlternativeRequestByte1(u_int8_t requestByte) { fResponseBuffer[fResponseBytesAlreadySeen] = requestByte; handleResponseBytes(1);}static Boolean isAbsoluteURL(char const* url) { // Assumption: "url" is absolute if it contains a ':', before any // occurrence of '/' while (*url != '\0' && *url != '/') { if (*url == ':') return True; ++url; } return False;}void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession, char const*& prefix, char const*& separator, char const*& suffix) { // Figure out what the URL describing "subsession" will look like. // The URL is returned in three parts: prefix; separator; suffix //##### NOTE: This code doesn't really do the right thing if "sessionURL()" // doesn't end with a "/", and "subsession.controlPath()" is relative. // The right thing would have been to truncate "sessionURL()" back to the // rightmost "/", and then add "subsession.controlPath()". // In practice, though, each "DESCRIBE" response typically contains // a "Content-Base:" header that consists of "sessionURL()" followed by // a "/", in which case this code ends up giving the correct result. // However, we should really fix this code to do the right thing, and // also check for and use the "Content-Base:" header appropriately. ##### prefix = sessionURL(subsession.parentSession()); if (prefix == NULL) prefix = ""; suffix = subsession.controlPath(); if (suffix == NULL) suffix = ""; if (isAbsoluteURL(suffix)) { prefix = separator = ""; } else { unsigned prefixLen = strlen(prefix); separator = (prefixLen == 0 || prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/"; }}Boolean RTSPClient::setupHTTPTunneling1() { // Set up RTSP-over-HTTP tunneling, as described in // http://developer.apple.com/documentation/QuickTime/QTSS/Concepts/chapter_2_section_14.html if (fVerbosityLevel >= 1) { envir() << "Requesting RTSP-over-HTTP tunneling (on port " << fTunnelOverHTTPPortNum << ")\n\n"; } // Begin by sending a HTTP "GET", to set up the server->client link. Continue when we handle the response: return sendRequest(new RequestRecord(1, "GET", responseHandlerForHTTP_GET)) != 0;}void RTSPClient::responseHandlerForHTTP_GET(RTSPClient* rtspClient, int responseCode, char* responseString) { if (rtspClient != NULL) rtspClient->responseHandlerForHTTP_GET1(responseCode, responseString);}void RTSPClient::responseHandlerForHTTP_GET1(int responseCode, char* responseString) { RequestRecord* request; do { // Having successfully set up (using the HTTP "GET" command) the server->client link, set up a second TCP connection // (to the same server & port as before) for the client->server link. All future output will be to this new socket. fOutputSocketNum = setupStreamSocket(envir(), 0); if (fOutputSocketNum < 0) break; fHTTPTunnelingConnectionIsPending = True; int connectResult = connectToServer(fOutputSocketNum, fTunnelOverHTTPPortNum); if (connectResult < 0) break; // an error occurred else if (connectResult == 0) { // A connection is pending. Continue setting up RTSP-over-HTTP when the connection completes. // First, move the pending requests to the 'awaiting connection' queue: while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) { fRequestsAwaitingConnection.enqueue(request); } return; } // The connection succeeded. Continue setting up RTSP-over-HTTP: if (!setupHTTPTunneling2()) break; // RTSP-over-HTTP tunneling succeeded. Resume the pending request(s): while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) { sendRequest(request); } return; } while (0); // An error occurred. Dequeue the pending request(s), and tell them about the error: fHTTPTunnelingConnectionIsPending = False; while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) { handleRequestError(request); delete request; } resetTCPSockets();}Boolean RTSPClient::setupHTTPTunneling2() { fHTTPTunnelingConnectionIsPending = False; // Send a HTTP "POST", to set up the client->server link. (Note that we won't see a reply to the "POST".) return sendRequest(new RequestRecord(1, "POST", NULL)) != 0;}void RTSPClient::connectionHandler(void* instance, int /*mask*/) { RTSPClient* client = (RTSPClient*)instance; client->connectionHandler1();}void RTSPClient::connectionHandler1() { // Restore normal handling on our sockets: envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum); envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE, (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this); // Move all requests awaiting connection into a new, temporary queue, to clear "fRequestsAwaitingConnection" // (so that "sendRequest()" doesn't get confused by "fRequestsAwaitingConnection" being nonempty, and enqueue them all over again). RequestQueue tmpRequestQueue; RequestRecord* request; while ((request = fRequestsAwaitingConnection.dequeue()) != NULL) { tmpRequestQueue.enqueue(request); } // Find out whether the connection succeeded or failed: do { int err = 0; SOCKLEN_T len = sizeof err; if (getsockopt(fInputSocketNum, SOL_SOCKET, SO_ERROR, (char*)&err, &len) < 0 || err != 0) { envir().setResultErrMsg("Connection to server failed: ", err); if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n"; break; } // The connection succeeded. If the connection came about from an attempt to set up RTSP-over-HTTP, finish this now: if (fVerbosityLevel >= 1) envir() << "...remote connection opened\n"; if (fHTTPTunnelingConnectionIsPending && !setupHTTPTunneling2()) break; // Resume sending all pending requests: while ((request = tmpRequestQueue.dequeue()) != NULL) { sendRequest(request); } return; } while (0); // An error occurred. Tell all pending requests about the error: while ((request = tmpRequestQueue.dequeue()) != NULL) { handleRequestError(request); delete request; } resetTCPSockets();}void RTSPClient::incomingDataHandler(void* instance, int /*mask*/) { RTSPClient* client = (RTSPClient*)instance;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -