bool ElcRTSPServer::setSource(const string& url, const string& iname) { LOG_INFO("ElcRTSPServer::setSource -url="<<url<<" -name="<<iname); mUrl = url; mName = iname; Boolean reuseFirstSource = true; if(!rtspServer) { LOG_ERROR("ElcRTSPServer::setSource failed! Rtspserver does not created propertly"); return false; } mediaSession = ServerMediaSession::createNew(*mEnv, mName.c_str(), mName.c_str(), "ELC"); if(!mediaSession) return false; // if multicast streaming if(Config::isMulticast) { struct in_addr destinationAddress; if(Config::streamingDestinationIp == "default" || Config::streamingDestinationIp =="") destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*mEnv); else destinationAddress.s_addr = our_inet_addr(Config::streamingDestinationIp.c_str()); Port rtpPort(Config::rtpPortNum); Port rtcpPort(Config::rtcpPortNum); rtpGroupsock = new Groupsock(*mEnv, destinationAddress, rtpPort, 255/*Config::ttl*/); rtpGroupsock->multicastSendOnly(); // we're a SSM source rtcpGroupsock = new Groupsock(*mEnv, destinationAddress, rtcpPort, 255/*Config::ttl*/); rtcpGroupsock->multicastSendOnly(); // we're a SSM source videoSink = MPEG4ESVideoRTPSink::createNew(*mEnv, rtpGroupsock, 96); const unsigned estimatedSessionBandwidth = 500; // in kbps; for RTCP b/w share const unsigned maxCNAMElen = 100; unsigned char CNAME[maxCNAMElen+1]; gethostname((char*)CNAME, maxCNAMElen); CNAME[maxCNAMElen] = '\0'; // just in case rtcp = RTCPInstance::createNew(*mEnv, rtcpGroupsock, estimatedSessionBandwidth, CNAME, videoSink, NULL /* we're a server*/ , True /* we're a SSM source */); mediaSession->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp)); rtspServer->addServerMediaSession(mediaSession); LOG_INFO("ElcRTSPServer::setSource OK"); } else // unicast streaming { mediaSubSession = Mpeg4LiveServerMediaSubSession::createNew(*mEnv, reuseFirstSource, mUrl); mediaSession->addSubsession(mediaSubSession); rtspServer->addServerMediaSession(mediaSession); LOG_INFO("ElcRTSPServer::setSource OK"); } return true; }
NetAddressList::NetAddressList(char const* hostname) : fNumAddresses(0), fAddressArray(NULL) { struct hostent* host; // Check first whether "hostname" is an IP address string: netAddressBits addr = our_inet_addr((char*)hostname); if (addr != INADDR_NONE) { // yes it was an IP address string //##### host = gethostbyaddr((char*)&addr, sizeof (netAddressBits), AF_INET); host = NULL; // don't bother calling gethostbyaddr(); we only want 1 addr if (host == NULL) { // For some unknown reason, gethostbyaddr() failed, so just // return a 1-element list with the address we were given: fNumAddresses = 1; fAddressArray = new NetAddress*[fNumAddresses]; if (fAddressArray == NULL) return; fAddressArray[0] = new NetAddress((u_int8_t*)&addr, sizeof (netAddressBits)); return; } } else { // Try resolving "hostname" as a real host name #if defined(VXWORKS) char hostentBuf[512]; host = (struct hostent*)resolvGetHostByName((char*)hostname,(char*)&hostentBuf,sizeof hostentBuf); #else host = our_gethostbyname((char*)hostname); #endif if (host == NULL) { // It was a host name, and we couldn't resolve it. We're SOL. return; } } u_int8_t const** const hAddrPtr = (u_int8_t const**)host->h_addr_list; if (hAddrPtr != NULL) { // First, count the number of addresses: u_int8_t const** hAddrPtr1 = hAddrPtr; while (*hAddrPtr1 != NULL) { ++fNumAddresses; ++hAddrPtr1; } // Next, set up the list: fAddressArray = new NetAddress*[fNumAddresses]; if (fAddressArray == NULL) return; for (unsigned i = 0; i < fNumAddresses; ++i) { fAddressArray[i] = new NetAddress(hAddrPtr[i], host->h_length); } } }
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 }
FramedSource* MPEG2TransportUDPServerMediaSubsession ::createNewStreamSource(unsigned/* clientSessionId*/, unsigned& estBitrate) { estBitrate = 5000; // kbps, estimate if (fInputGroupsock == NULL) { // Create a 'groupsock' object for receiving the input stream: struct in_addr inputAddress; inputAddress.s_addr = fInputAddressStr == NULL ? 0 : our_inet_addr(fInputAddressStr); fInputGroupsock = new Groupsock(envir(), inputAddress, fInputPort, 255); } FramedSource* transportStreamSource; if (fInputStreamIsRawUDP) { transportStreamSource = BasicUDPSource::createNew(envir(), fInputGroupsock); } else { transportStreamSource = SimpleRTPSource::createNew(envir(), fInputGroupsock, 33, 90000, "video/MP2T", 0, False /*no 'M' bit*/); } return MPEG2TransportStreamFramer::createNew(envir(), transportStreamSource); }
void CCreateRTPServer::createGroupsock() { m_prtcpGroupsockAudio = NULL; m_prtpGroupsockAudio = NULL; m_prtcpGroupsockVideo = NULL; m_prtpGroupsockVideo = NULL; struct in_addr destAddress; //destAddress.s_addr = our_inet_addr(m_destAddressStr.c_str()); if(!m_rtpDestAddressStr.empty()) destAddress.s_addr = our_inet_addr(m_rtpDestAddressStr.c_str()); else destAddress.s_addr = chooseRandomIPv4SSMAddress(*env); if(m_pAudioSourceQueue != NULL) { const unsigned short rtpPortNumAudio = m_rtpPortNum; const unsigned short rtcpPortNumAudio = rtpPortNumAudio + 1; const Port rtpPortAudio(rtpPortNumAudio); const Port rtcpPortAudio(rtcpPortNumAudio); m_prtpGroupsockAudio = new Groupsock(*env, destAddress, rtpPortAudio, m_ttl); m_prtcpGroupsockAudio = new Groupsock(*env, destAddress, rtcpPortAudio, m_ttl); if(m_prtpGroupsockAudio && m_prtcpGroupsockAudio) { m_rtpPortNum += 2; } } if(m_pVideoSourceQueue != NULL) { const unsigned short rtpPortNumVideo = m_rtpPortNum; const unsigned short rtcpPortNumVideo = rtpPortNumVideo + 1; const Port rtpPortVideo(rtpPortNumVideo); const Port rtcpPortVideo(rtcpPortNumVideo); m_prtpGroupsockVideo = new Groupsock(*env, destAddress, rtpPortVideo, m_ttl); m_prtcpGroupsockVideo = new Groupsock(*env, destAddress, rtcpPortVideo, m_ttl); } }
char const* destinationAddressStr #ifdef USE_SSM = "232.255.42.42"; #else = "239.255.42.42"; // Note: This is a multicast address. If you wish to stream using // unicast instead, then replace this string with the unicast address // of the (single) destination. (You may also need to make a similar // change to the receiver program.) #endif const unsigned short rtpPortNum = 8888; const unsigned short rtcpPortNum = rtpPortNum+1; const unsigned char ttl = 7; // low, in case routers don't admin scope struct in_addr destinationAddress; destinationAddress.s_addr = our_inet_addr(destinationAddressStr); const Port rtpPort(rtpPortNum); const Port rtcpPort(rtcpPortNum); Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl); Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl); #ifdef USE_SSM rtpGroupsock.multicastSendOnly(); rtcpGroupsock.multicastSendOnly(); #endif // Create a 'MPEG Video RTP' sink from the RTP 'groupsock': videoSink = MPEG1or2VideoRTPSink::createNew(*env, &rtpGroupsock); // Create (and start) a 'RTCP instance' for this RTP sink: const unsigned estimatedSessionBandwidth = 4500; // in kbps; for RTCP b/w share
NetAddressList::NetAddressList(char const* hostname) : fNumAddresses(0), fAddressArray(NULL) { // First, check whether "hostname" is an IP address string: netAddressBits addr = our_inet_addr((char*)hostname); if (addr != INADDR_NONE) { // Yes, it was an IP address string. Return a 1-element list with this address: fNumAddresses = 1; fAddressArray = new NetAddress*[fNumAddresses]; if (fAddressArray == NULL) return; fAddressArray[0] = new NetAddress((u_int8_t*)&addr, sizeof (netAddressBits)); return; } // "hostname" is not an IP address string; try resolving it as a real host name instead: #if defined(USE_GETHOSTBYNAME) || defined(VXWORKS) struct hostent* host; #if defined(VXWORKS) char hostentBuf[512]; host = (struct hostent*)resolvGetHostByName((char*)hostname, (char*)&hostentBuf, sizeof hostentBuf); #else host = gethostbyname((char*)hostname); #endif if (host == NULL || host->h_length != 4 || host->h_addr_list == NULL) return; // no luck u_int8_t const** const hAddrPtr = (u_int8_t const**)host->h_addr_list; // First, count the number of addresses: u_int8_t const** hAddrPtr1 = hAddrPtr; while (*hAddrPtr1 != NULL) { ++fNumAddresses; ++hAddrPtr1; } // Next, set up the list: fAddressArray = new NetAddress*[fNumAddresses]; if (fAddressArray == NULL) return; for (unsigned i = 0; i < fNumAddresses; ++i) { fAddressArray[i] = new NetAddress(hAddrPtr[i], host->h_length); } #else // Use "getaddrinfo()" (rather than the older, deprecated "gethostbyname()"): struct addrinfo addrinfoHints; memset(&addrinfoHints, 0, sizeof addrinfoHints); addrinfoHints.ai_family = AF_INET; // For now, we're interested in IPv4 addresses only struct addrinfo* addrinfoResultPtr = NULL; int result = getaddrinfo(hostname, NULL, &addrinfoHints, &addrinfoResultPtr); if (result != 0 || addrinfoResultPtr == NULL) return; // no luck // First, count the number of addresses: const struct addrinfo* p = addrinfoResultPtr; while (p != NULL) { if (p->ai_addrlen < 4) continue; // sanity check: skip over addresses that are too small ++fNumAddresses; p = p->ai_next; } // Next, set up the list: fAddressArray = new NetAddress*[fNumAddresses]; if (fAddressArray == NULL) return; unsigned i = 0; p = addrinfoResultPtr; while (p != NULL) { if (p->ai_addrlen < 4) continue; fAddressArray[i++] = new NetAddress((u_int8_t const*)&(((struct sockaddr_in*)p->ai_addr)->sin_addr.s_addr), 4); p = p->ai_next; } // Finally, free the data that we had allocated by calling "getaddrinfo()": freeaddrinfo(addrinfoResultPtr); #endif }
JNIEXPORT void JNICALL Java_com_parizene_streamer_Streamer_loop(JNIEnv *env, jobject obj, jstring addr) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); uEnv = BasicUsageEnvironment::createNew(*scheduler); // Create 'groupsocks' for RTP and RTCP: struct in_addr destinationAddress; const char *_addr = env->GetStringUTFChars(addr, NULL); destinationAddress.s_addr = our_inet_addr(_addr); /*chooseRandomIPv4SSMAddress(*uEnv);*/ env->ReleaseStringUTFChars(addr, _addr); // Note: This is a multicast address. If you wish instead to stream // using unicast, then you should use the "testOnDemandRTSPServer" // test program - not this test program - as a model. const unsigned short rtpPortNum = 18888; const unsigned short rtcpPortNum = rtpPortNum + 1; const unsigned char ttl = 255; const Port rtpPort(rtpPortNum); const Port rtcpPort(rtcpPortNum); Groupsock rtpGroupsock(*uEnv, destinationAddress, rtpPort, ttl); Groupsock rtcpGroupsock(*uEnv, destinationAddress, rtcpPort, ttl); // Create a 'H264 Video RTP' sink from the RTP 'groupsock': OutPacketBuffer::maxSize = 100000; videoSink = H264VideoRTPSink::createNew(*uEnv, &rtpGroupsock, 96); // Create (and start) a 'RTCP instance' for this RTP sink: const unsigned estimatedSessionBandwidth = 500; // in kbps; for RTCP b/w share const unsigned maxCNAMElen = 100; unsigned char CNAME[maxCNAMElen + 1]; gethostname((char*) CNAME, maxCNAMElen); CNAME[maxCNAMElen] = '\0'; // just in case RTCPInstance* rtcp = RTCPInstance::createNew(*uEnv, &rtcpGroupsock, estimatedSessionBandwidth, CNAME, videoSink, NULL /* we're a server */, True /* we're a SSM source */); // Note: This starts RTCP running automatically RTSPServer* rtspServer = RTSPServer::createNew(*uEnv, 8554); if (rtspServer == NULL) { LOGE("Failed to create RTSP server: %s", uEnv->getResultMsg()); exit(1); } ServerMediaSession* sms = ServerMediaSession::createNew(*uEnv, "streamer", inputFilename, "Session streamed by \"testH264VideoStreamer\"", True /*SSM*/); sms->addSubsession( PassiveServerMediaSubsession::createNew(*videoSink, rtcp)); rtspServer->addServerMediaSession(sms); char* url = rtspServer->rtspURL(sms); LOGI("Play this stream using the URL \"%s\"", url); delete[] url; // Start the streaming: LOGI("Beginning streaming...\n"); play(); uEnv->taskScheduler().doEventLoop(); // does not return }
netAddressBits ourIPAddress(UsageEnvironment& env) { static netAddressBits ourAddress = 0; int sock = -1; struct in_addr testAddr; if (ourAddress == 0) { // We need to find our source address struct sockaddr_in fromAddr; fromAddr.sin_addr.s_addr = 0; // Get our address by sending a (0-TTL) multicast packet, // receiving it, and looking at the source address used. // (This is kinda bogus, but it provides the best guarantee // that other nodes will think our address is the same as we do.) do { loopbackWorks = 0; // until we learn otherwise testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary Port testPort(15947); // ditto sock = setupDatagramSocket(env, testPort); if (sock < 0) break; if (!socketJoinGroup(env, sock, testAddr.s_addr)) break; unsigned char testString[] = "hostIdTest"; unsigned testStringLength = sizeof testString; if (!writeSocket(env, sock, testAddr, testPort, 0, testString, testStringLength)) break; unsigned char readBuffer[20]; struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; int bytesRead = readSocket(env, sock, readBuffer, sizeof readBuffer, fromAddr, &timeout); if (bytesRead == 0 // timeout occurred || bytesRead != (int)testStringLength || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) { break; } loopbackWorks = 1; } while (0); if (!loopbackWorks) do { // We couldn't find our address using multicast loopback // so try instead to look it up directly. char hostname[100]; hostname[0] = '\0'; #ifndef CRIS gethostname(hostname, sizeof hostname); #endif if (hostname[0] == '\0') { env.setResultErrMsg("initial gethostname() failed"); break; } #if defined(VXWORKS) #include <hostLib.h> if (ERROR == (ourAddress = hostGetByName( hostname ))) break; #else struct hostent* hstent = (struct hostent*)gethostbyname(hostname); if (hstent == NULL || hstent->h_length != 4) { env.setResultErrMsg("initial gethostbyname() failed"); break; } // Take the first address that's not bad // (This code, like many others, won't handle IPv6) netAddressBits addr = 0; for (unsigned i = 0; ; ++i) { char* addrPtr = hstent->h_addr_list[i]; if (addrPtr == NULL) break; netAddressBits a = *(netAddressBits*)addrPtr; if (!badAddress(a)) { addr = a; break; } } if (addr != 0) { fromAddr.sin_addr.s_addr = addr; } else { env.setResultMsg("no address"); break; } } while (0); // Make sure we have a good address: netAddressBits from = fromAddr.sin_addr.s_addr; if (badAddress(from)) { char tmp[100]; sprintf(tmp, "This computer has an invalid IP address: 0x%x", (netAddressBits)(ntohl(from))); env.setResultMsg(tmp); from = 0; } ourAddress = from; #endif if (sock >= 0) { socketLeaveGroup(env, sock, testAddr.s_addr); closeSocket(sock); } // Use our newly-discovered IP address, and the current time, // to initialize the random number generator's seed: struct timeval timeNow; gettimeofday(&timeNow, NULL); unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec; our_srandom(seed); } return ourAddress; }
bool IPTVFeederRTP::Open(const QString &url) { LOG(VB_RECORD, LOG_INFO, LOC + QString("Open(%1) -- begin").arg(url)); QMutexLocker locker(&_lock); if (_source) { LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- end 1"); return true; } QUrl parse(url); if (!parse.isValid() || parse.host().isEmpty() || (-1 == parse.port())) { LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- end 2"); return false; } struct in_addr addr; QByteArray host = parse.host().toLatin1(); addr.s_addr = our_inet_addr(host.constData()); // Begin by setting up our usage environment: if (!InitEnv()) return false; ReceivingSocketAddr = our_inet_addr(parse.host().toLatin1()); Groupsock *socket = new Groupsock(*_live_env, addr, parse.port(), 0); if (!socket) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create Live RTP Socket."); FreeEnv(); return false; } _source = SimpleRTPSource::createNew(*_live_env, socket, 33, 90000, "video/MP2T", 0, False); if (!_source) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create Live RTP Source."); if (socket) delete socket; FreeEnv(); return false; } _sink = IPTVMediaSink::CreateNew(*_live_env, TSPacket::kSize * 128*1024); if (!_sink) { LOG(VB_GENERAL, LOG_ERR, QString("IPTV # Failed to create sink: %1") .arg(_live_env->getResultMsg())); Medium::close(_source); _source = NULL; if (socket) delete socket; FreeEnv(); return false; } _sink->startPlaying(*_source, NULL, NULL); vector<TSDataListener*>::iterator it = _listeners.begin(); for (; it != _listeners.end(); ++it) _sink->AddListener(*it); LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- end"); return true; }
int CMPIPTV_RTSP::OpenConnection(void) { this->logger.Log(LOGGER_INFO, METHOD_START_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME); int result = STATUS_OK; this->rtspClient = RTSPClient::createNew(*this->rtspEnvironment); result |= (this->rtspClient == NULL); if (result == STATUS_OK) { // RTSPClient works with char, not with TCHAR char *tempRtspUrl = ConvertToMultiByte(this->rtspUrl); result |= (tempRtspUrl == NULL); if (result == STATUS_OK) { char* optionsResult = this->rtspClient->sendOptionsCmd(tempRtspUrl, NULL, NULL, NULL, this->receiveDataTimeout / 2000); result |= (optionsResult == NULL); if (result != STATUS_OK) { TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while sending OPTIONS command")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("OPTIONS result")); this->LogFullRtspMessage(LOGGER_VERBOSE, message, optionsResult); FREE_MEM(message); char *describeResult = this->rtspClient->describeURL(tempRtspUrl, NULL, FALSE, this->receiveDataTimeout / 2000); result |= (describeResult == NULL); if (result != STATUS_OK) { TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while sending DESCRIBE command")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("DESCRIBE result")); this->LogFullRtspMessage(LOGGER_VERBOSE, message, describeResult); FREE_MEM(message); this->rtspSession = MediaSession::createNew(*this->rtspEnvironment, describeResult); result |= (this->rtspSession == NULL); if (result != STATUS_OK) { TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while creating new session")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { result |= (!this->rtspSession->hasSubsessions()); if (result != STATUS_OK) { this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("session doesn't have subsessions")); } else { // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*this->rtspSession); MediaSubsession *subsession = NULL; while ((result == STATUS_OK) && ((subsession = iter.next()) != NULL)) { char *tempSubSessionName = (char *)subsession->mediumName(); char *tempSubSessionCodecName = (char *)subsession->codecName(); #ifdef _MBCS TCHAR *subSessionName = ConvertToMultiByteA(tempSubSessionName); TCHAR *subSessionCodecName = ConvertToMultiByteA(tempSubSessionCodecName); #else TCHAR *subSessionName = ConvertToUnicodeA(tempSubSessionName); TCHAR *subSessionCodecName = ConvertToUnicodeA(tempSubSessionCodecName); #endif if (!subsession->initiate()) { result = STATUS_ERROR; TCHAR *message = FormatString(_T("%s: %s: unable to create receiver for subsession '%s', codec '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, subSessionCodecName); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: created receiver for subsession '%s', codec '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, subSessionCodecName); // set session ID, doesn't matter what subsession->sessionId = tempSubSessionName; if (subsession->rtpSource() != NULL) { // because we're saving the incoming data, rather than playing // it in real time, allow an especially large time threshold // (1 second) for reordering misordered incoming packets: unsigned const thresh = 1000000; // 1 second subsession->rtpSource()->setPacketReorderingThresholdTime(thresh); // set the RTP source's OS socket buffer size as appropriate int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned int currentBufferSize = getReceiveBufferSize(*this->rtspEnvironment, socketNum); if (this->defaultBufferSize > currentBufferSize) { setReceiveBufferTo(*this->rtspEnvironment, socketNum, this->defaultBufferSize); unsigned setBufferSize = getReceiveBufferSize(*this->rtspEnvironment, socketNum); if (setBufferSize == this->defaultBufferSize) { this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: set buffer size for subsession '%s' successful, previous size: %i, requested size: %i, current size: %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize); } else { result = STATUS_ERROR; this->logger.Log(LOGGER_ERROR, _T("%s: %s: set buffer size for subsession '%s' failed, previous size: %i, requested size: %i, current size: %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize); } } if (_tcsncicmp(subSessionName, _T("audio"), 5) == 0) { // audio this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: audio subsession '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName); result |= (!rtspClient->setupMediaSubsession(*subsession)); if (result != STATUS_OK) { // error occured TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error while setup subsession")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { this->logger.Log(LOGGER_WARNING, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("subsession audio codec not supported")); } } else if (_tcsncicmp(subSessionName, _T("video"), 5) == 0) { // video this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: video subsession '%s'"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName); result |= (!rtspClient->setupMediaSubsession(*subsession)); if (result != STATUS_OK) { // error occured TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error while setup subsession")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { if (_tcsncicmp(subSessionCodecName, _T("MP2T"), 4) == 0) { // MPEG2 Transport Stream // set new RTSP source this->rtspSource = subsession->rtpSource(); if (subsession->rtcpInstance() != NULL) { this->logger.Log(LOGGER_VERBOSE, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("set subsession 'Bye' handler")); subsession->rtcpInstance()->setByeHandler(SubsessionByeHandler, this); } } else if (_tcsncicmp(subSessionCodecName, _T("H264"), 4) == 0) { // H264 codec, HD TV this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("H264 not supported")); result = STATUS_ERROR; } else { // SD TV this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("other subsession video codec than MP2T not supported")); result = STATUS_ERROR; } } } else { this->logger.Log(LOGGER_WARNING, _T("%s: %s: unknown subsession '%s', ignored"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName); } } else { this->logger.Log(LOGGER_WARNING, _T("%s: %s: subsession '%s' doesn't have RTP source"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName); } } // free subsession name and codec name FREE_MEM(subSessionName); FREE_MEM(subSessionCodecName); } // we should have some RTSP source result |= (this->rtspSource == NULL); if (result == STATUS_OK) { result |= (!this->rtspClient->playMediaSession(*this->rtspSession)); if (result != STATUS_OK) { // error occured TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error while playing session")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { // create UDP socket and start playing struct in_addr destinationAddress; destinationAddress.s_addr = our_inet_addr("127.0.0.1"); unsigned int port = this->rtspUdpPortRangeStart; do { this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: UDP port %u"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port); // special construction force not reuse same UDP port { NoReuse noReuse; this->rtspUdpGroupsock = new Groupsock(*this->rtspEnvironment, destinationAddress, port, 1); } if (this->rtspUdpGroupsock->socketNum() == (-1)) { this->logger.Log(LOGGER_ERROR, _T("%s: %s: UDP port %u occupied, trying another port"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port); port++; delete this->rtspUdpGroupsock; this->rtspUdpGroupsock = NULL; } } while ((this->rtspUdpGroupsock == NULL) && (port <= this->rtspUdpPortRangeEnd)); result |= (this->rtspUdpGroupsock == NULL); if (result != STATUS_OK) { this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("cannot create UDP sink, no free port")); } else { this->rtspUdpSink = BasicUDPSink::createNew(*this->rtspEnvironment, this->rtspUdpGroupsock, this->rtspUdpSinkMaxPayloadSize); result |= (this->rtspUdpSink == NULL); if (result != STATUS_OK) { TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("cannot create UDP sink")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } else { if (this->rtspUdpSink->startPlaying(*this->rtspSource, NULL, NULL)) { this->logger.Log(LOGGER_INFO, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("playing started")); // now create UDP connection TCHAR *url = FormatString(_T("udp://@127.0.0.1:%u"), port); result |= (url == NULL); if (result == STATUS_OK) { // parse UDP url // ParseURL calls ClearSession and IsConnected must return FALSE // in another case will be current RTSP connection closed result = this->CMPIPTV_UDP::ParseUrl(url, NULL); if (result == STATUS_OK) { // connect to UDP url result = this->CMPIPTV_UDP::OpenConnection(); } } FREE_MEM(url); } else { result = STATUS_ERROR; TCHAR *message = FormatString(METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("error occured while starting playing")); this->LogRtspMessage(LOGGER_ERROR, message); FREE_MEM(message); } } } } } } } } } if (optionsResult != NULL) { delete[] optionsResult; optionsResult = NULL; } } FREE_MEM(tempRtspUrl); } if (result == STATUS_OK) { // start winsock worker thread this->rtspSchedulerThreadHandle = CreateThread( NULL, // default security attributes 0, // use default stack size &CMPIPTV_RTSP::RtspSchedulerWorker, // thread function name this, // argument to thread function 0, // use default creation flags &this->rtspSchedulerThreadId); // returns the thread identifier if (this->rtspSchedulerThreadHandle == NULL) { // thread not created result = STATUS_ERROR; this->logger.Log(LOGGER_ERROR, _T("%s: %s: cannot create RTSP scheduler thread, error: %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, GetLastError()); } } if (result != STATUS_OK) { // if failed opening connection, than close connection this->CloseConnection(); } this->logger.Log(LOGGER_INFO, (result == STATUS_OK) ? METHOD_END_FORMAT : METHOD_END_FAIL_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME); return (result == STATUS_OK) ? STATUS_OK : STATUS_ERROR; }
netAddressBits ourIPAddress(UsageEnvironment& env) { static netAddressBits ourAddress = 0; int sock = -1; struct in_addr testAddr; if (ReceivingInterfaceAddr != INADDR_ANY) { // Hack: If we were told to receive on a specific interface address, then // define this to be our ip address: ourAddress = ReceivingInterfaceAddr; } if (ourAddress == 0) { // We need to find our source address struct sockaddr_in fromAddr; fromAddr.sin_addr.s_addr = 0; // Get our address by sending a (0-TTL) multicast packet, // receiving it, and looking at the source address used. // (This is kinda bogus, but it provides the best guarantee // that other nodes will think our address is the same as we do.) do { loopbackWorks = 0; // until we learn otherwise testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary Port testPort(15947); // ditto sock = setupDatagramSocket(env, testPort); if (sock < 0) break; if (!socketJoinGroup(env, sock, testAddr.s_addr)) break; unsigned char testString[] = "hostIdTest"; unsigned testStringLength = sizeof testString; if (!writeSocket(env, sock, testAddr, testPort.num(), 0, testString, testStringLength)) break; // Block until the socket is readable (with a 5-second timeout): fd_set rd_set; FD_ZERO(&rd_set); FD_SET((unsigned)sock, &rd_set); const unsigned numFds = sock+1; struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; int result = select(numFds, &rd_set, NULL, NULL, &timeout); if (result <= 0) break; unsigned char readBuffer[20]; int bytesRead = readSocket(env, sock, readBuffer, sizeof readBuffer, fromAddr); if (bytesRead != (int)testStringLength || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) { break; } // We use this packet's source address, if it's good: loopbackWorks = !badAddressForUs(fromAddr.sin_addr.s_addr); } while (0); if (sock >= 0) { socketLeaveGroup(env, sock, testAddr.s_addr); closeSocket(sock); } if (!loopbackWorks) do { // We couldn't find our address using multicast loopback, // so try instead to look it up directly - by first getting our host name, and then resolving this host name char hostname[100]; hostname[0] = '\0'; int result = gethostname(hostname, sizeof hostname); if (result != 0 || hostname[0] == '\0') { env.setResultErrMsg("initial gethostname() failed"); break; } // Try to resolve "hostname" to an IP address: NetAddressList addresses(hostname); NetAddressList::Iterator iter(addresses); NetAddress const* address; // Take the first address that's not bad: netAddressBits addr = 0; while ((address = iter.nextAddress()) != NULL) { netAddressBits a = *(netAddressBits*)(address->data()); if (!badAddressForUs(a)) { addr = a; break; } } // Assign the address that we found to "fromAddr" (as if the 'loopback' method had worked), to simplify the code below: fromAddr.sin_addr.s_addr = addr; } while (0); // Make sure we have a good address: netAddressBits from = fromAddr.sin_addr.s_addr; if (badAddressForUs(from)) { char tmp[100]; sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val()); env.setResultMsg(tmp); from = 0; } ourAddress = from; // Use our newly-discovered IP address, and the current time, // to initialize the random number generator's seed: struct timeval timeNow; gettimeofday(&timeNow, NULL); unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec; our_srandom(seed); } return ourAddress; }
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; }
int CMPIPTV_RTSP::OpenConnection(void) { this->logger.Log(LOGGER_INFO, METHOD_START_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME); this->isRtspSessionSetup = false; // LIVE555 works with char, not with TCHAR char *tempRtspUrl = ConvertToMultiByte(this->rtspUrl); if (tempRtspUrl == NULL) { return STATUS_ERROR; } // start LIVE555 worker thread this->rtspSchedulerThreadHandle = CreateThread( NULL, // default security attributes 0, // use default stack size &CMPIPTV_RTSP::RtspSchedulerWorker, // thread function name this, // argument to thread function 0, // use default creation flags &this->rtspSchedulerThreadId); // returns the thread identifier if (this->rtspSchedulerThreadHandle == NULL) { this->logger.Log(LOGGER_ERROR, _T("%s: %s: failed to create RTSP scheduler thread, error = %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, GetLastError()); return STATUS_ERROR; } this->rtspClient = MPRTSPClient::createNew(this, *this->rtspEnvironment, tempRtspUrl); FREE_MEM(tempRtspUrl); if ( this->rtspClient == NULL || SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("OPTIONS")) != STATUS_OK || SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("DESCRIBE")) != STATUS_OK ) { CloseConnection(); return STATUS_ERROR; } this->rtspSession = MediaSession::createNew(*this->rtspEnvironment, this->rtspResponseResultString); if (this->rtspSession == NULL || !this->rtspSession->hasSubsessions()) { this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, this->rtspSession == NULL ? _T("failed to create session") : _T("session doesn't have sub-sessions")); CloseConnection(); return STATUS_ERROR; } // Setup the RTP source for the session. Only one sub-session expected/supported. MediaSubsessionIterator iter(*this->rtspSession); MediaSubsession *subsession = NULL; FramedSource *rtspSource = NULL; while ((subsession = iter.next()) != NULL) { #ifdef _MBCS TCHAR *subSessionName = ConvertToMultiByteA(subsession->mediumName()); TCHAR *subSessionCodecName = ConvertToMultiByteA(subsession->codecName()); #else TCHAR *subSessionName = ConvertToUnicodeA(subsession->mediumName()); TCHAR *subSessionCodecName = ConvertToUnicodeA(subsession->codecName()); #endif if (_tcsncicmp(subSessionName, _T("video"), 5) != 0 || _tcsncicmp(subSessionCodecName, _T("MP2T"), 4) != 0) { TCHAR *message = FormatString(_T("sub-session medium or codec not supported, medium = %s, codec = %s"), subSessionName, subSessionCodecName); this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, message); FREE_MEM(message); FREE_MEM(subSessionName); FREE_MEM(subSessionCodecName); continue; } // If a client port is configured, find a free pair of ports in the range. // The first port is used for RTP; the second port is used for RTCP. Once // we find one free port, we assume the next one is also free. if (this->rtspRtpClientPortRangeStart > 0) { struct in_addr destinationAddress; destinationAddress.s_addr = our_inet_addr("127.0.0.1"); unsigned int port = this->rtspRtpClientPortRangeStart; Groupsock *groupsock = NULL; do { this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: RTP client port %u"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port); // special construction force not reuse same UDP port { NoReuse noReuse(*this->rtspEnvironment); groupsock = new Groupsock(*this->rtspEnvironment, destinationAddress, port, 1); } if (groupsock == NULL || groupsock->socketNum() == -1) { this->logger.Log(LOGGER_WARNING, _T("%s: %s: RTP client port %u occupied, trying next even port"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port); port += 2; if (groupsock != NULL) { delete groupsock; groupsock = NULL; } } } while ((groupsock == NULL) && (port <= this->rtspRtpClientPortRangeEnd)); // Did we find a free port? If not, we fall back to a random port chosen // by LIVE555. if (groupsock != NULL) { delete groupsock; groupsock = NULL; subsession->setClientPortNum(port); } } if (!subsession->initiate() || subsession->rtpSource() == NULL) { TCHAR *message = FormatString(_T("failed to create receiver for sub-session, medium = %s, codec = %s"), subSessionName, subSessionCodecName); this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, message); FREE_MEM(message); FREE_MEM(subSessionName); FREE_MEM(subSessionCodecName); continue; } this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: created receiver for sub-session, medium = %s, codec = %s"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, subSessionCodecName); FREE_MEM(subSessionName); FREE_MEM(subSessionCodecName); // set session ID, doesn't matter what subsession->setSessionId(subsession->mediumName()); // because we're saving the incoming data, rather than playing // it in real time, allow an especially large time threshold // for reordering misordered incoming packets: subsession->rtpSource()->setPacketReorderingThresholdTime(1000000); // 1 second // set the RTP source's OS socket buffer size as appropriate int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned int currentBufferSize = getReceiveBufferSize(*this->rtspEnvironment, socketNum); if (this->defaultBufferSize > currentBufferSize) { setReceiveBufferTo(*this->rtspEnvironment, socketNum, this->defaultBufferSize); unsigned setBufferSize = getReceiveBufferSize(*this->rtspEnvironment, socketNum); if (setBufferSize == this->defaultBufferSize) { this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: set buffer size for sub-session, previous size = %i, requested size = %i, current size = %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize); } else { this->logger.Log(LOGGER_WARNING, _T("%s: %s: failed to set buffer size for sub-session, previous size = %i, requested size = %i, current size = %i"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, subSessionName, currentBufferSize, this->defaultBufferSize, setBufferSize); } } if (SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("SETUP"), subsession) != STATUS_OK) { CloseConnection(); return STATUS_ERROR; } rtspSource = subsession->rtpSource(); break; } // If we don't have an RTSP source then we can't continue. if (rtspSource == NULL) { CloseConnection(); return STATUS_ERROR; } this->isRtspSessionSetup = true; if (SendRtspCommand(METHOD_OPEN_CONNECTION_NAME, _T("PLAY")) != STATUS_OK) { CloseConnection(); return STATUS_ERROR; } // create UDP socket and start playing struct in_addr destinationAddress; destinationAddress.s_addr = our_inet_addr("127.0.0.1"); unsigned int port = this->rtspUdpPortRangeStart; do { this->logger.Log(LOGGER_VERBOSE, _T("%s: %s: UDP port %u"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port); // special construction force not reuse same UDP port { NoReuse noReuse(*this->rtspEnvironment); this->rtspUdpGroupsock = new Groupsock(*this->rtspEnvironment, destinationAddress, port, 1); } if (this->rtspUdpGroupsock == NULL || this->rtspUdpGroupsock->socketNum() == -1) { this->logger.Log(LOGGER_WARNING, _T("%s: %s: UDP port %u occupied, trying another port"), PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, port); port++; if (this->rtspUdpGroupsock != NULL) { delete this->rtspUdpGroupsock; this->rtspUdpGroupsock = NULL; } } } while ((this->rtspUdpGroupsock == NULL) && (port <= this->rtspUdpPortRangeEnd)); if (this->rtspUdpGroupsock == NULL) { this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("failed to create UDP socket, no free port")); CloseConnection(); return STATUS_ERROR; } this->rtspUdpSink = BasicUDPSink::createNew(*this->rtspEnvironment, this->rtspUdpGroupsock, this->rtspUdpSinkMaxPayloadSize); if (this->rtspUdpSink == NULL) { this->logger.Log(LOGGER_ERROR, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("failed to create UDP sink")); CloseConnection(); return STATUS_ERROR; } if (!this->rtspUdpSink->startPlaying(*rtspSource, NULL, NULL)) { this->LogRtspMessage(LOGGER_ERROR, METHOD_OPEN_CONNECTION_NAME, _T("failed to start UDP sink")); CloseConnection(); return STATUS_ERROR; } this->logger.Log(LOGGER_INFO, METHOD_MESSAGE_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME, _T("streaming started")); // create a UDP connection to the local stream TCHAR *url = FormatString(_T("udp://@127.0.0.1:%u"), port); if ( url == NULL || this->CMPIPTV_UDP::ParseUrl(url, NULL) != STATUS_OK || this->CMPIPTV_UDP::OpenConnection() != STATUS_OK ) { FREE_MEM(url); this->logger.Log(LOGGER_INFO, METHOD_END_FAIL_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME); CloseConnection(); return STATUS_ERROR; } FREE_MEM(url); this->logger.Log(LOGGER_INFO, METHOD_END_FORMAT, PROTOCOL_IMPLEMENTATION_NAME, METHOD_OPEN_CONNECTION_NAME); return STATUS_OK; }
void startReplicaFileSink(StreamReplicator* replicator, char const* outputFileName); // forward int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); // Create a 'groupsock' for the input multicast group,port: char const* inputAddressStr #ifdef USE_SSM = "232.255.42.42"; #else = "239.255.42.42"; #endif struct in_addr inputAddress; inputAddress.s_addr = our_inet_addr(inputAddressStr); Port const inputPort(8888); unsigned char const inputTTL = 0; // we're only reading from this mcast group #ifdef USE_SSM char* sourceAddressStr = "aaa.bbb.ccc.ddd"; // replace this with the real source address struct in_addr sourceFilterAddress; sourceFilterAddress.s_addr = our_inet_addr(sourceAddressStr); Groupsock inputGroupsock(*env, inputAddress, sourceFilterAddress, inputPort); #else Groupsock inputGroupsock(*env, inputAddress, inputPort, inputTTL); #endif