UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g) { UsageEnvironment& s1 = s << timestampString() << " Groupsock(" << g.socketNum() << ": " << our_inet_ntoa(g.groupAddress()) << ", " << g.port() << ", "; if (g.isSSM()) { return s1 << "SSM source: " << our_inet_ntoa(g.sourceFilterAddress()) << ")"; } else { return s1 << (unsigned)(g.ttl()) << ")"; } }
int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler); // Create a 'groupsock' for the input multicast group,port: char const* sessionAddressStr = "224.2.127.254"; struct in_addr sessionAddress; sessionAddress.s_addr = our_inet_addr(sessionAddressStr); const Port port(9875); const unsigned char ttl = 0; // we're only reading from this mcast group Groupsock inputGroupsock(*env, sessionAddress, port, ttl); // Start reading and printing incoming packets // (Because this is the only thing we do, we can just do this // synchronously, in a loop, so we don't need to set up an asynchronous // event handler like we do in most of the other test programs.) unsigned packetSize; struct sockaddr_in fromAddress; while (inputGroupsock.handleRead(packet, maxPacketSize, packetSize, fromAddress)) { printf("\n[packet from %s (%d bytes)]\n", our_inet_ntoa(fromAddress.sin_addr), packetSize); // Ignore the first 8 bytes (SAP header). if (packetSize < 8) { *env << "Ignoring short packet from " << our_inet_ntoa(fromAddress.sin_addr) << "%s!\n"; continue; } // convert "application/sdp\0" -> "application/sdp\0x20" // or all other nonprintable characters to blank, except new line unsigned idx = 8; while (idx < packetSize) { if (packet[idx] < 0x20 && packet[idx] != '\n') packet[idx] = 0x20; idx++; } packet[packetSize] = '\0'; // just in case printf((char*)(packet+8)); } return 0; // only to prevent compiler warning }
void RTSPServer::incomingConnectionHandler1() { struct sockaddr_in clientAddr; SOCKLEN_T clientAddrLen = sizeof clientAddr; // printf("incomingConnectionHandler1\n"); //jay int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); if (clientSocket < 0) { int err = envir().getErrno(); if (err != EWOULDBLOCK) { envir().setResultErrMsg("accept() failed: "); } return; } makeSocketNonBlocking(clientSocket); increaseSendBufferTo(envir(), clientSocket, 50*1024); #if defined(DEBUG) || defined(DEBUG_CONNECTIONS) envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << '\n'; #endif // Create a new object for this RTSP session. // (Choose a random 32-bit integer for the session id (it will be encoded as a 8-digit hex number). We don't bother checking for // a collision; the probability of two concurrent sessions getting the same session id is very low.) unsigned sessionId = (unsigned)our_random(); (void)createNewClientSession(sessionId, clientSocket, clientAddr); }
Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize, unsigned& bytesRead, struct sockaddr_in& fromAddress) { // Read data from the socket, and relay it across any attached tunnels //##### later make this code more general - independent of tunnels bytesRead = 0; int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize; int numBytes = readSocket(env(), socketNum(), buffer, maxBytesToRead, fromAddress); if (numBytes < 0) { if (DebugLevel >= 0) { // this is a fatal error env().setResultMsg("Groupsock read failed: ", env().getResultMsg()); } return False; } // If we're a SSM group, make sure the source address matches: if (isSSM() && fromAddress.sin_addr.s_addr != sourceFilterAddress().s_addr) { return True; } // We'll handle this data. // Also write it (with the encapsulation trailer) to each member, // unless the packet was originally sent by us to begin with. bytesRead = numBytes; int numMembers = 0; if (!wasLoopedBackFromUs(env(), fromAddress)) { statsIncoming.countPacket(numBytes); statsGroupIncoming.countPacket(numBytes); numMembers = outputToAllMembersExcept(NULL, ttl(), buffer, bytesRead, fromAddress.sin_addr.s_addr); if (numMembers > 0) { statsRelayedIncoming.countPacket(numBytes); statsGroupRelayedIncoming.countPacket(numBytes); } } if (DebugLevel >= 3) { env() << *this << ": read " << bytesRead << " bytes from "; env() << our_inet_ntoa(fromAddress.sin_addr); if (numMembers > 0) { env() << "; relayed to " << numMembers << " members"; } env() << "\n"; } return True; }
void RTSPServer::RTSPClientSession ::livenessTimeoutTask(RTSPClientSession* clientSession) { // If this gets called, the client session is assumed to have timed out, // so delete it: if (!clientSession->isMulticast()) { #ifdef DEBUG fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", our_inet_ntoa(clientSession->fClientAddr.sin_addr)); #endif delete clientSession; } }
char const* PassiveServerMediaSubsession::sdpLines() { if (fSDPLines == NULL ) { // Construct a set of SDP lines that describe this subsession: // Use the components from "rtpSink": Groupsock const& gs = fRTPSink.groupsockBeingUsed(); struct in_addr const& ipAddress = gs.groupAddress(); unsigned short portNum = ntohs(gs.port().num()); unsigned char ttl = gs.ttl(); unsigned char rtpPayloadType = fRTPSink.rtpPayloadType(); char const* mediaType = fRTPSink.sdpMediaType(); char* rtpmapLine = fRTPSink.rtpmapLine(); char const* rangeLine = rangeSDPLine(); char const* auxSDPLine = fRTPSink.auxSDPLine(); if (auxSDPLine == NULL) auxSDPLine = ""; char* const ipAddressStr = strDup(our_inet_ntoa(ipAddress)); char const* const sdpFmt = "m=%s %d RTP/AVP %d\r\n" "c=IN IP4 %s/%d\r\n" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr) + 3 /* max char len */ + strlen(rtpmapLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char* sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> portNum, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr, // c= <connection address> ttl, // c= TTL rtpmapLine, // a=rtpmap:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control:<track-id> delete[] ipAddressStr; delete[] (char*)rangeLine; delete[] rtpmapLine; fSDPLines = strDup(sdpLines); delete[] sdpLines; } return fSDPLines; }
void OnDemandServerMediaSubsession ::setSDPLinesFromRTPSink(RTPSink *rtpSink, FramedSource *inputSource, unsigned estBitrate) { if (rtpSink == NULL) return; char const *mediaType = rtpSink->sdpMediaType(); unsigned char rtpPayloadType = rtpSink->rtpPayloadType(); struct in_addr serverAddrForSDP; serverAddrForSDP.s_addr = fServerAddressForSDP; char *const ipAddressStr = strDup(our_inet_ntoa(serverAddrForSDP)); char *rtpmapLine = rtpSink->rtpmapLine(); char const *rangeLine = rangeSDPLine(); char const *auxSDPLine = getAuxSDPLine(rtpSink, inputSource); if (auxSDPLine == NULL) auxSDPLine = ""; char const *const sdpFmt = "m=%s %u RTP/AVP %d\r\n" "c=IN IP4 %s\r\n" "b=AS:%u\r\n" "%s" "%s" "%s" "a=control:%s\r\n"; unsigned sdpFmtSize = strlen(sdpFmt) + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */ + strlen(ipAddressStr) + 20 /* max int len */ + strlen(rtpmapLine) + strlen(rangeLine) + strlen(auxSDPLine) + strlen(trackId()); char *sdpLines = new char[sdpFmtSize]; sprintf(sdpLines, sdpFmt, mediaType, // m= <media> fPortNumForSDP, // m= <port> rtpPayloadType, // m= <fmt list> ipAddressStr, // c= address estBitrate, // b=AS:<bandwidth> rtpmapLine, // a=rtpmap:... (if present) rangeLine, // a=range:... (if present) auxSDPLine, // optional extra SDP line trackId()); // a=control:<track-id> delete[] (char *)rangeLine; delete[] rtpmapLine; delete[] ipAddressStr; fSDPLines = strDup(sdpLines); delete[] sdpLines; }
char* RTSPServer::rtspURLPrefix(int clientSocket) const { struct sockaddr_in ourAddress; if (clientSocket < 0) { // Use our default IP address in the URL: ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0 ? ReceivingInterfaceAddr : ourIPAddress(envir()); // hack } else { SOCKLEN_T namelen = sizeof ourAddress; getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen); } char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/" portNumBits portNumHostOrder = ntohs(fServerPort.num()); if (portNumHostOrder == 554 /* the default port number */) { sprintf(urlBuffer, "rtsp://%s/", our_inet_ntoa(ourAddress.sin_addr)); } else { sprintf(urlBuffer, "rtsp://%s:%hu/", our_inet_ntoa(ourAddress.sin_addr), portNumHostOrder); } return strDup(urlBuffer); }
void RTSPServer::RTSPClientSession ::livenessTimeoutTask(RTSPClientSession* clientSession) { // If this gets called, the client session is assumed to have timed out, // so delete it: // However, we don't timeout multicast sessions, because to do so would require // closing all client sessions that have requested the stream - not just this one. // Also, the multicast stream itself would usually not be halted, in any case. if (clientSession->isMulticast()) return; #ifdef DEBUG fprintf(stderr, "RTSP client session from %s has timed out (due to inactivity)\n", our_inet_ntoa(clientSession->fClientAddr.sin_addr)); #endif delete clientSession; }
RTSPServer::RTSPClientSession* RTSPServer::attachClientSession(int clientSocket, IRTSPClientSessionObserver* observer) { if (clientSocket < 0) { return NULL; } struct sockaddr_in clientAddr; SOCKLEN_T clientAddrLen = sizeof clientAddr; if (getpeername(clientSocket, (struct sockaddr*)&clientAddr, &clientAddrLen) < 0) return NULL; #if defined(DEBUG) || defined(DEBUG_CONNECTIONS) fprintf(stderr, "attached client session from %s\n", our_inet_ntoa(clientAddr.sin_addr)); #endif // Create a new object for this RTSP session: RTSPClientSession* clientSession = new RTSPClientSession(*this, ++fSessionIdCounter, clientSocket, clientAddr, true, observer); return clientSession; }
void RTSPServer::incomingConnectionHandler1() { struct sockaddr_in clientAddr; SOCKLEN_T clientAddrLen = sizeof clientAddr; int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); if (clientSocket < 0) { int err = envir().getErrno(); if (err != EWOULDBLOCK) { envir().setResultErrMsg("accept() failed: "); } return; } makeSocketNonBlocking(clientSocket); increaseSendBufferTo(envir(), clientSocket, 50*1024); #if defined(DEBUG) || defined(DEBUG_CONNECTIONS) fprintf(stderr, "accept()ed connection from %s\n", our_inet_ntoa(clientAddr.sin_addr)); #endif // Create a new object for this RTSP session: new RTSPClientSession(*this, ++fSessionIdCounter, clientSocket, clientAddr); }
char *ServerMediaSession::generateSDPDescription() { struct in_addr ipAddress; ipAddress.s_addr = ourIPAddress(envir()); char *const ipAddressStr = strDup(our_inet_ntoa(ipAddress)); unsigned ipAddressStrSize = strlen(ipAddressStr); // For a SSM sessions, we need a "a=source-filter: incl ..." line also: char *sourceFilterLine; if (fIsSSM) { char const *const sourceFilterFmt = "a=source-filter: incl IN IP4 * %s\r\n" "a=rtcp-unicast: reflection\r\n"; unsigned const sourceFilterFmtSize = strlen(sourceFilterFmt) + ipAddressStrSize + 1; sourceFilterLine = new char[sourceFilterFmtSize]; sprintf(sourceFilterLine, sourceFilterFmt, ipAddressStr); } else { sourceFilterLine = strDup(""); } char *rangeLine = NULL; // for now char *sdp = NULL; // for now do { // Count the lengths of each subsession's media-level SDP lines. // (We do this first, because the call to "subsession->sdpLines()" // causes correct subsession 'duration()'s to be calculated later.) unsigned sdpLength = 0; ServerMediaSubsession *subsession; for (subsession = fSubsessionsHead; subsession != NULL; subsession = subsession->fNext) { char const *sdpLines = subsession->sdpLines(); if (sdpLines == NULL) break; // the media's not available sdpLength += strlen(sdpLines); } if (subsession != NULL) break; // an error occurred // Unless subsessions have differing durations, we also have a "a=range:" line: float dur = duration(); if (dur == 0.0) { rangeLine = strDup("a=range:npt=0-\r\n"); } else if (dur > 0.0) { char buf[100]; sprintf(buf, "a=range:npt=0-%.3f\r\n", dur); rangeLine = strDup(buf); } else // subsessions have differing durations, so "a=range:" lines go there { rangeLine = strDup(""); } char const *const sdpPrefixFmt = "v=0\r\n" "o=- %ld%06ld %d IN IP4 %s\r\n" "s=%s\r\n" "i=%s\r\n" "t=0 0\r\n" "a=tool:%s%s\r\n" "a=type:broadcast\r\n" "a=control:*\r\n" "%s" "%s" "a=x-qt-text-nam:%s\r\n" "a=x-qt-text-inf:%s\r\n" "%s"; sdpLength += strlen(sdpPrefixFmt) + 20 + 6 + 20 + ipAddressStrSize + strlen(fDescriptionSDPString) + strlen(fInfoSDPString) + strlen(libNameStr) + strlen(libVersionStr) + strlen(sourceFilterLine) + strlen(rangeLine) + strlen(fDescriptionSDPString) + strlen(fInfoSDPString) + strlen(fMiscSDPLines); sdp = new char[sdpLength]; if (sdp == NULL) break; // Generate the SDP prefix (session-level lines): sprintf(sdp, sdpPrefixFmt, fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id> 1, // o= <version> // (needs to change if params are modified) ipAddressStr, // o= <address> fDescriptionSDPString, // s= <description> fInfoSDPString, // i= <info> libNameStr, libVersionStr, // a=tool: sourceFilterLine, // a=source-filter: incl (if a SSM session) rangeLine, // a=range: line fDescriptionSDPString, // a=x-qt-text-nam: line fInfoSDPString, // a=x-qt-text-inf: line fMiscSDPLines); // miscellaneous session SDP lines (if any) // Then, add the (media-level) lines for each subsession: char *mediaSDP = sdp; for (subsession = fSubsessionsHead; subsession != NULL; subsession = subsession->fNext) { mediaSDP += strlen(mediaSDP); sprintf(mediaSDP, "%s", subsession->sdpLines()); } } while (0); delete[] rangeLine; delete[] sourceFilterLine; delete[] ipAddressStr; return sdp; }
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(); ServerMediaSubsessionIterator iter(*fOurServerMediaSession); for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} fStreamStates = new struct streamState[fNumStreamStates]; iter.reset(); ServerMediaSubsession* subsession; for (unsigned i = 0; i < fNumStreamStates; ++i) { subsession = iter.next(); fStreamStates[i].subsession = subsession; fStreamStates[i].streamToken = NULL; // for now; reset by SETUP later } } // Look up information for the specified subsession (track): ServerMediaSubsession* subsession = NULL; unsigned streamNum; if (trackId != NULL && trackId[0] != '\0') { // 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 = NULL; // set when RAW_UDP streaming is specified char* clientsDestinationAddressStr; u_int8_t clientsDestinationTTL; portNumBits clientRTPPortNum, clientRTCPPortNum; unsigned char rtpChannelId, rtcpChannelId; parseTransportHeader(fullRequestStr, streamingMode, streamingModeString, fIsMulticast, clientsDestinationAddressStr, clientsDestinationTTL, clientRTPPortNum, clientRTCPPortNum, rtpChannelId, rtcpChannelId); if (streamingMode == RTP_TCP && rtpChannelId == 0xFF) { // TCP streaming was requested, but with no "interleaving=" fields. // (QuickTime Player sometimes does this.) Set the RTP and RTCP channel ids to // proper values: rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1; } fTCPStreamIdCount += 2; Port clientRTPPort(clientRTPPortNum); Port clientRTCPPort(clientRTCPPortNum); // Next, check whether a "Range:" header is present in the request. // This isn't legal, but some clients do this to combine "SETUP" and "PLAY": double rangeStart = 0.0, rangeEnd = 0.0; fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) || parsePlayNowHeader(fullRequestStr); // Then, get server parameters from the 'subsession': int tcpSocketNum = streamingMode == RTP_TCP ? fClientSocket : -1; netAddressBits destinationAddress = 0; u_int8_t destinationTTL = 5; #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING if (clientsDestinationAddressStr != NULL) { // Use the client-provided "destination" address. // Note: This potentially allows the server to be used in denial-of-service // attacks, so don't enable this code unless you're sure that clients are // trusted. destinationAddress = our_inet_addr(clientsDestinationAddressStr); } // Also use the client-provided TTL. destinationTTL = clientsDestinationTTL; #endif delete[] clientsDestinationAddressStr; Port serverRTPPort(0); Port serverRTCPPort(0); subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr, clientRTPPort, clientRTCPPort, tcpSocketNum, rtpChannelId, rtcpChannelId, destinationAddress, destinationTTL, fIsMulticast, serverRTPPort, serverRTCPPort, fStreamStates[streamNum].streamToken); struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress; char* destAddrStr = strDup(our_inet_ntoa(destinationAddr)); struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr; getsockname(fClientSocket, (struct sockaddr*)&sourceAddr, &namelen); char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr)); if (fIsMulticast) { switch (streamingMode) { case RTP_UDP: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL, fOurSessionId); break; case RTP_TCP: // multicast streams can't be sent via TCP handleCmd_unsupportedTransport(cseq); break; case RAW_UDP: snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), streamingModeString, destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), destinationTTL, fOurSessionId); break; } } else { switch (streamingMode) { case RTP_UDP: { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), fOurSessionId); break; } case RTP_TCP: { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId, fOurSessionId); break; } case RAW_UDP: { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), streamingModeString, destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()), fOurSessionId); break; } } } delete[] destAddrStr; delete[] sourceAddrStr; delete[] streamingModeString; }
//处理setup命令时使用 void RTSPServer::RTSPClientSession::handleCmd_SETUP() void OnDemandServerMediaSubsession::getStreamParameters( unsigned clientSessionId, netAddressBits clientAddress, Port const& clientRTPPort, Port const& clientRTCPPort, int tcpSocketNum, unsigned char rtpChannelId, unsigned char rtcpChannelId, netAddressBits& destinationAddress, u_int8_t& /*destinationTTL*/, Boolean& isMulticast, Port& serverRTPPort, Port& serverRTCPPort, void*& streamToken) { if (destinationAddress == 0) destinationAddress = clientAddress; struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress; isMulticast = False; if (fLastStreamToken != NULL && fReuseFirstSource) { // Special case: Rather than creating a new 'StreamState', // we reuse the one that we've already created: DEBUG_LOG(INF, "Reuse first source"); serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort(); serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort(); ++((StreamState*)fLastStreamToken)->referenceCount(); streamToken = fLastStreamToken; } else { // Normal case: Create a new media source: DEBUG_LOG(INF, "Create a new media source"); unsigned streamBitrate; FramedSource* mediaSource = createNewStreamSource(clientSessionId, streamBitrate); // Create 'groupsock' and 'sink' objects for the destination, // using previously unused server port numbers: RTPSink* rtpSink; BasicUDPSink* udpSink; Groupsock* rtpGroupsock; Groupsock* rtcpGroupsock; portNumBits serverPortNum; if (clientRTCPPort.num() == 0) { // We're streaming raw UDP (not RTP). Create a single groupsock: NoReuse dummy; // ensures that we skip over ports that are already in use for (serverPortNum = fInitialPortNum; ; ++serverPortNum) { struct in_addr dummyAddr; dummyAddr.s_addr = 0; serverRTPPort = serverPortNum; rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255); if (rtpGroupsock->socketNum() >= 0) break; // success } rtcpGroupsock = NULL; rtpSink = NULL; udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock); } else { // Normal case: We're streaming RTP (over UDP or TCP). Create a pair of // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even): NoReuse dummy; // ensures that we skip over ports that are already in use for (portNumBits serverPortNum = fInitialPortNum; ; serverPortNum += 2) { struct in_addr dummyAddr; dummyAddr.s_addr = 0; serverRTPPort = serverPortNum; rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255); if (rtpGroupsock->socketNum() < 0) { delete rtpGroupsock; continue; // try again } serverRTCPPort = serverPortNum+1; rtcpGroupsock = new Groupsock(envir(), dummyAddr, serverRTCPPort, 255); if (rtcpGroupsock->socketNum() < 0) { delete rtpGroupsock; delete rtcpGroupsock; continue; // try again } break; // success } unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource); udpSink = NULL; } // Turn off the destinations for each groupsock. They'll get set later // (unless TCP is used instead): if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations(); if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations(); if (rtpGroupsock != NULL) { // Try to use a big send buffer for RTP - at least 0.1 second of // specified bandwidth and at least 50 KB unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024; increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize); } // Set up the state of the stream. The stream will get started later: DEBUG_LOG(INF, "Set up the state of the stream. The stream will get started later"); streamToken = fLastStreamToken = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink, streamBitrate, mediaSource, rtpGroupsock, rtcpGroupsock); } // Record these destinations as being for this client session id: Destinations* destinations; if (tcpSocketNum < 0) { // UDP DEBUG_LOG(INF, "Create UDP Destinations: destinationAddr=%s, clientRTPPort=%d, clientRTCPPort=%d", our_inet_ntoa(destinationAddr), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num())); destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort); } else { // TCP DEBUG_LOG(INF, "Create TCP Destinations: tcpSocketNum=%d, rtpChannelId=%d, rtcpChannelId=%d", tcpSocketNum, rtpChannelId, rtcpChannelId); destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId); } fDestinationsHashTable->Add((char const*)clientSessionId, destinations); }
Boolean DarwinInjector ::setDestination(char const* remoteRTSPServerNameOrAddress, char const* remoteFileName, char const* sessionName, char const* sessionInfo, portNumBits remoteRTSPServerPortNumber, char const* remoteUserName, char const* remotePassword, char const* sessionAuthor, char const* sessionCopyright, int timeout) { char* sdp = NULL; char* url = NULL; Boolean success = False; // until we learn otherwise do { // Begin by creating our RTSP client object: fRTSPClient = RTSPClient::createNew(envir(), fVerbosityLevel, fApplicationName); if (fRTSPClient == NULL) break; // Get the remote RTSP server's IP address: struct in_addr addr; { NetAddressList addresses(remoteRTSPServerNameOrAddress); if (addresses.numAddresses() == 0) break; NetAddress const* address = addresses.firstAddress(); addr.s_addr = *(unsigned*)(address->data()); } char const* remoteRTSPServerAddressStr = our_inet_ntoa(addr); // Construct a SDP description for the session that we'll be streaming: char const* const sdpFmt = "v=0\r\n" "o=- %u %u IN IP4 127.0.0.1\r\n" "s=%s\r\n" "i=%s\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "a=x-qt-text-nam:%s\r\n" "a=x-qt-text-inf:%s\r\n" "a=x-qt-text-cmt:source application:%s\r\n" "a=x-qt-text-aut:%s\r\n" "a=x-qt-text-cpy:%s\r\n"; // plus, %s for each substream SDP unsigned sdpLen = strlen(sdpFmt) + 20 /* max int len */ + 20 /* max int len */ + strlen(sessionName) + strlen(sessionInfo) + strlen(remoteRTSPServerAddressStr) + strlen(sessionName) + strlen(sessionInfo) + strlen(fApplicationName) + strlen(sessionAuthor) + strlen(sessionCopyright) + fSubstreamSDPSizes; unsigned const sdpSessionId = our_random(); unsigned const sdpVersion = sdpSessionId; sdp = new char[sdpLen]; sprintf(sdp, sdpFmt, sdpSessionId, sdpVersion, // o= line sessionName, // s= line sessionInfo, // i= line remoteRTSPServerAddressStr, // c= line sessionName, // a=x-qt-text-nam: line sessionInfo, // a=x-qt-text-inf: line fApplicationName, // a=x-qt-text-cmt: line sessionAuthor, // a=x-qt-text-aut: line sessionCopyright // a=x-qt-text-cpy: line ); char* p = &sdp[strlen(sdp)]; SubstreamDescriptor* ss; for (ss = fHeadSubstream; ss != NULL; ss = ss->next()) { sprintf(p, "%s", ss->sdpLines()); p += strlen(p); } // Construct a RTSP URL for the remote stream: char const* const urlFmt = "rtsp://%s:%u/%s"; unsigned urlLen = strlen(urlFmt) + strlen(remoteRTSPServerNameOrAddress) + 5 /* max short len */ + strlen(remoteFileName); url = new char[urlLen]; sprintf(url, urlFmt, remoteRTSPServerNameOrAddress, remoteRTSPServerPortNumber, remoteFileName); // Do a RTSP "ANNOUNCE" with this SDP description: Boolean announceSuccess; if (remoteUserName[0] != '\0' || remotePassword[0] != '\0') { announceSuccess = fRTSPClient->announceWithPassword(url, sdp, remoteUserName, remotePassword, timeout); } else { announceSuccess = fRTSPClient->announceSDPDescription(url, sdp, NULL, timeout); } if (!announceSuccess) break; // Tell the remote server to start receiving the stream from us. // (To do this, we first create a "MediaSession" object from the SDP description.) fSession = MediaSession::createNew(envir(), sdp); if (fSession == NULL) break; ss = fHeadSubstream; MediaSubsessionIterator iter(*fSession); MediaSubsession* subsession; ss = fHeadSubstream; unsigned streamChannelId = 0; while ((subsession = iter.next()) != NULL) { if (!subsession->initiate()) break; if (!fRTSPClient->setupMediaSubsession(*subsession, True /*streamOutgoing*/, True /*streamUsingTCP*/)) { break; } // Tell this subsession's RTPSink and RTCPInstance to use // the RTSP TCP connection: ss->rtpSink()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++); if (ss->rtcpInstance() != NULL) { ss->rtcpInstance()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++); } ss = ss->next(); } if (subsession != NULL) break; // an error occurred above // Tell the RTSP server to start: if (!fRTSPClient->playMediaSession(*fSession)) break; // Finally, make sure that the output TCP buffer is a reasonable size: increaseSendBufferTo(envir(), fRTSPClient->socketNum(), 100*1024); success = True; } while (0); delete[] sdp; delete[] url; return success; }
void RTCPInstance::incomingReportHandler1() { unsigned char* pkt = fInBuf; unsigned packetSize; struct sockaddr_in fromAddress; int typeOfPacket = PACKET_UNKNOWN_TYPE; do { int tcpReadStreamSocketNum = fRTCPInterface.nextTCPReadStreamSocketNum(); unsigned char tcpReadStreamChannelId = fRTCPInterface.nextTCPReadStreamChannelId(); if (!fRTCPInterface.handleRead(pkt, maxPacketSize, packetSize, fromAddress)) { break; } // Ignore the packet if it was looped-back from ourself: if (RTCPgs()->wasLoopedBackFromUs(envir(), fromAddress)) { // However, we still want to handle incoming RTCP packets from // *other processes* on the same machine. To distinguish this // case from a true loop-back, check whether we've just sent a // packet of the same size. (This check isn't perfect, but it seems // to be the best we can do.) if (fHaveJustSentPacket && fLastPacketSentSize == packetSize) { // This is a true loop-back: fHaveJustSentPacket = False; break; // ignore this packet } } if (fIsSSMSource) { // This packet was received via unicast. 'Reflect' it by resending // it to the multicast group. // NOTE: Denial-of-service attacks are possible here. // Users of this software may wish to add their own, // application-specific mechanism for 'authenticating' the // validity of this packet before reflecting it. fRTCPInterface.sendPacket(pkt, packetSize); fHaveJustSentPacket = True; fLastPacketSentSize = packetSize; } #ifdef DEBUG fprintf(stderr, "[%p]saw incoming RTCP packet (from address %s, port %d)\n", this, our_inet_ntoa(fromAddress.sin_addr), ntohs(fromAddress.sin_port)); unsigned char* p = pkt; for (unsigned i = 0; i < packetSize; ++i) { if (i%4 == 0) fprintf(stderr, " "); fprintf(stderr, "%02x", p[i]); } fprintf(stderr, "\n"); #endif int totPacketSize = IP_UDP_HDR_SIZE + packetSize; // Check the RTCP packet for validity: // It must at least contain a header (4 bytes), and this header // must be version=2, with no padding bit, and a payload type of // SR (200) or RR (201): if (packetSize < 4) break; unsigned rtcpHdr = ntohl(*(unsigned*)pkt); if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR<<16))) { #ifdef DEBUG fprintf(stderr, "rejected bad RTCP packet: header 0x%08x\n", rtcpHdr); #endif break; } // Process each of the individual RTCP 'subpackets' in (what may be) // a compound RTCP packet. unsigned reportSenderSSRC = 0; Boolean packetOK = False; while (1) { unsigned rc = (rtcpHdr>>24)&0x1F; unsigned pt = (rtcpHdr>>16)&0xFF; unsigned length = 4*(rtcpHdr&0xFFFF); // doesn't count hdr ADVANCE(4); // skip over the header if (length > packetSize) break; // Assume that each RTCP subpacket begins with a 4-byte SSRC: if (length < 4) break; length -= 4; reportSenderSSRC = ntohl(*(unsigned*)pkt); ADVANCE(4); Boolean subPacketOK = False; switch (pt) { case RTCP_PT_SR: { #ifdef DEBUG fprintf(stderr, "SR\n"); #endif if (length < 20) break; length -= 20; // Extract the NTP timestamp, and note this: unsigned NTPmsw = ntohl(*(unsigned*)pkt); ADVANCE(4); unsigned NTPlsw = ntohl(*(unsigned*)pkt); ADVANCE(4); unsigned rtpTimestamp = ntohl(*(unsigned*)pkt); ADVANCE(4); if (fSource != NULL) { RTPReceptionStatsDB& receptionStats = fSource->receptionStatsDB(); receptionStats.noteIncomingSR(reportSenderSSRC, NTPmsw, NTPlsw, rtpTimestamp); } ADVANCE(8); // skip over packet count, octet count // If a 'SR handler' was set, call it now: if (fSRHandlerTask != NULL) (*fSRHandlerTask)(fSRHandlerClientData); // The rest of the SR is handled like a RR (so, no "break;" here) } case RTCP_PT_RR: { #ifdef DEBUG fprintf(stderr, "RR\n"); #endif unsigned reportBlocksSize = rc*(6*4); if (length < reportBlocksSize) break; length -= reportBlocksSize; if (fSink != NULL) { // Use this information to update stats about our transmissions: RTPTransmissionStatsDB& transmissionStats = fSink->transmissionStatsDB(); for (unsigned i = 0; i < rc; ++i) { unsigned senderSSRC = ntohl(*(unsigned*)pkt); ADVANCE(4); // We care only about reports about our own transmission, not others' if (senderSSRC == fSink->SSRC()) { unsigned lossStats = ntohl(*(unsigned*)pkt); ADVANCE(4); unsigned highestReceived = ntohl(*(unsigned*)pkt); ADVANCE(4); unsigned jitter = ntohl(*(unsigned*)pkt); ADVANCE(4); unsigned timeLastSR = ntohl(*(unsigned*)pkt); ADVANCE(4); unsigned timeSinceLastSR = ntohl(*(unsigned*)pkt); ADVANCE(4); transmissionStats.noteIncomingRR(reportSenderSSRC, fromAddress, lossStats, highestReceived, jitter, timeLastSR, timeSinceLastSR); } else { ADVANCE(4*5); } } } else { ADVANCE(reportBlocksSize); } if (pt == RTCP_PT_RR) { // i.e., we didn't fall through from 'SR' // If a 'RR handler' was set, call it now: // Specific RR handler: if (fSpecificRRHandlerTable != NULL) { netAddressBits fromAddr; portNumBits fromPortNum; if (tcpReadStreamSocketNum < 0) { // Normal case: We read the RTCP packet over UDP fromAddr = fromAddress.sin_addr.s_addr; fromPortNum = ntohs(fromAddress.sin_port); } else { // Special case: We read the RTCP packet over TCP (interleaved) // Hack: Use the TCP socket and channel id to look up the handler fromAddr = tcpReadStreamSocketNum; fromPortNum = tcpReadStreamChannelId; } Port fromPort(fromPortNum); RRHandlerRecord* rrHandler = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort)); if (rrHandler != NULL) { if (rrHandler->rrHandlerTask != NULL) { (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData); } } } // General RR handler: if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData); } subPacketOK = True; typeOfPacket = PACKET_RTCP_REPORT; break; } case RTCP_PT_BYE: { #ifdef DEBUG fprintf(stderr, "BYE\n"); #endif // If a 'BYE handler' was set, call it now: TaskFunc* byeHandler = fByeHandlerTask; if (byeHandler != NULL && (!fByeHandleActiveParticipantsOnly || (fSource != NULL && fSource->receptionStatsDB().lookup(reportSenderSSRC) != NULL) || (fSink != NULL && fSink->transmissionStatsDB().lookup(reportSenderSSRC) != NULL))) { fByeHandlerTask = NULL; // we call this only once by default (*byeHandler)(fByeHandlerClientData); } // We should really check for & handle >1 SSRCs being present ##### subPacketOK = True; typeOfPacket = PACKET_BYE; break; } // Later handle SDES, APP, and compound RTCP packets ##### default: #ifdef DEBUG fprintf(stderr, "UNSUPPORTED TYPE(0x%x)\n", pt); #endif subPacketOK = True; break; } if (!subPacketOK) break; // need to check for (& handle) SSRC collision! ##### #ifdef DEBUG fprintf(stderr, "validated RTCP subpacket (type %d): %d, %d, %d, 0x%08x\n", typeOfPacket, rc, pt, length, reportSenderSSRC); #endif // Skip over any remaining bytes in this subpacket: ADVANCE(length); // Check whether another RTCP 'subpacket' follows: if (packetSize == 0) { packetOK = True; break; } else if (packetSize < 4) { #ifdef DEBUG fprintf(stderr, "extraneous %d bytes at end of RTCP packet!\n", packetSize); #endif break; } rtcpHdr = ntohl(*(unsigned*)pkt); if ((rtcpHdr & 0xC0000000) != 0x80000000) { #ifdef DEBUG fprintf(stderr, "bad RTCP subpacket: header 0x%08x\n", rtcpHdr); #endif break; } } if (!packetOK) { #ifdef DEBUG fprintf(stderr, "rejected bad RTCP subpacket: header 0x%08x\n", rtcpHdr); #endif break; } else { #ifdef DEBUG fprintf(stderr, "validated entire RTCP packet\n"); #endif } onReceive(typeOfPacket, totPacketSize, reportSenderSSRC); } while (0); }
SIPClient::SIPClient(UsageEnvironment& env, unsigned char desiredAudioRTPPayloadFormat, char const* mimeSubtype, int verbosityLevel, char const* applicationName) : Medium(env), fT1(500000 /* 500 ms */), fDesiredAudioRTPPayloadFormat(desiredAudioRTPPayloadFormat), fVerbosityLevel(verbosityLevel), fCSeq(0), fURL(NULL), fURLSize(0), fToTagStr(NULL), fToTagStrSize(0), fUserName(NULL), fUserNameSize(0), fInviteSDPDescription(NULL), fInviteCmd(NULL), fInviteCmdSize(0){ if (mimeSubtype == NULL) mimeSubtype = ""; fMIMESubtype = strDup(mimeSubtype); fMIMESubtypeSize = strlen(fMIMESubtype); if (applicationName == NULL) applicationName = ""; fApplicationName = strDup(applicationName); fApplicationNameSize = strlen(fApplicationName); struct in_addr ourAddress; ourAddress.s_addr = ourIPAddress(env); // hack fOurAddressStr = strDup(our_inet_ntoa(ourAddress)); fOurAddressStrSize = strlen(fOurAddressStr); fOurSocket = new Groupsock(env, ourAddress, 0, 255); if (fOurSocket == NULL) { env << "ERROR: Failed to create socket for addr " << our_inet_ntoa(ourAddress) << ": " << env.getResultMsg() << "\n"; } // Now, find out our source port number. Hack: Do this by first trying to // send a 0-length packet, so that the "getSourcePort()" call will work. fOurSocket->output(envir(), 255, (unsigned char*)"", 0); Port srcPort(0); getSourcePort(env, fOurSocket->socketNum(), srcPort); if (srcPort.num() != 0) { fOurPortNum = ntohs(srcPort.num()); } else { // No luck. Try again using a default port number: fOurPortNum = 5060; delete fOurSocket; fOurSocket = new Groupsock(env, ourAddress, fOurPortNum, 255); if (fOurSocket == NULL) { env << "ERROR: Failed to create socket for addr " << our_inet_ntoa(ourAddress) << ", port " << fOurPortNum << ": " << env.getResultMsg() << "\n"; } } // Set various headers to be used in each request: char const* formatStr; unsigned headerSize; // Set the "User-Agent:" header: char const* const libName = "LIVE555 Streaming Media v"; char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING; char const* libPrefix; char const* libSuffix; if (applicationName == NULL || applicationName[0] == '\0') { applicationName = libPrefix = libSuffix = ""; } else { libPrefix = " ("; libSuffix = ")"; } formatStr = "User-Agent: %s%s%s%s%s\r\n"; headerSize = strlen(formatStr) + fApplicationNameSize + strlen(libPrefix) + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix); fUserAgentHeaderStr = new char[headerSize]; sprintf(fUserAgentHeaderStr, formatStr, applicationName, libPrefix, libName, libVersionStr, libSuffix); fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr); reset(); }