Boolean MediaSession ::initiateByMediaType(char const* mimeType, MediaSubsession*& resultSubsession, int useSpecialRTPoffset) { // Look through this session's subsessions for media that match "mimeType" resultSubsession = NULL; MediaSubsessionIterator iter(*this); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { Boolean wasAlreadyInitiated = subsession->readSource() != NULL; if (!wasAlreadyInitiated) { // Try to create a source for this subsession: if (!subsession->initiate(useSpecialRTPoffset)) return False; } // Make sure the source's MIME type is one that we handle: if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) { if (!wasAlreadyInitiated) subsession->deInitiate(); continue; } resultSubsession = subsession; break; // use this } if (resultSubsession == NULL) { envir().setResultMsg("Session has no usable media subsession"); return False; } return True; }
void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) { if (resultCode != 0) { *env << "Failed to get a SDP description from URL \"" << streamURL << "\": " << resultString << "\n"; shutdown(); } char* sdpDescription = resultString; *env << "Opened URL \"" << streamURL << "\", returning a SDP description:\n" << sdpDescription << "\n"; // Create a media session object from this SDP description: session = MediaSession::createNew(*env, sdpDescription); delete[] sdpDescription; if (session == NULL) { *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n"; shutdown(); } else if (!session->hasSubsessions()) { *env << "This session has no media subsessions (i.e., \"m=\" lines)\n"; shutdown(); } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*session); MediaSubsession *subsession; Boolean madeProgress = False; char const* singleMediumToTest = singleMedium; while ((subsession = iter.next()) != NULL) { // If we've asked to receive only a single medium, then check this now: if (singleMediumToTest != NULL) { if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) { *env << "Ignoring \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession, because we've asked to receive a single " << singleMedium << " session only\n"; continue; } else { // Receive this subsession only singleMediumToTest = "xxxxx"; // this hack ensures that we get only 1 subsession of this type } } if (desiredPortNum != 0) { subsession->setClientPortNum(desiredPortNum); desiredPortNum += 2; } if (createReceivers) { if (!subsession->initiate(simpleRTPoffsetArg)) { *env << "Unable to create receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession: " << env->getResultMsg() << "\n"; } else { *env << "Created receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession (client ports " << subsession->clientPortNum() << "-" << subsession->clientPortNum()+1 << ")\n"; madeProgress = True; 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 - either if we were explicitly asked (using -B), // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size. // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size, // then the input data rate may be large enough to justify increasing the OS socket buffer size also.) int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize = getReceiveBufferSize(*env, socketNum); if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) { unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize; newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize); if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it: *env << "Changed socket receive buffer size for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession from " << curBufferSize << " to " << newBufferSize << " bytes\n"; } } } } } else { if (subsession->clientPortNum() == 0) { *env << "No client port was specified for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession. (Try adding the \"-p <portNum>\" option.)\n"; } else { madeProgress = True; } } } if (!madeProgress) shutdown(); // Perform additional 'setup' on each subsession, before playing them: setupStreams(); }
int CMediaNet::MediaNet_Thread( void * pThisVoid ) { CMediaNet *pThis = ( CMediaNet* )pThisVoid; do { // 开始初始化. pThis->SetRtspStatus( RTSPStatus_Init ); // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); progName = "M_CU"; string strUrl = pThis->m_strRTSPUrlA; gettimeofday(&startTime, NULL); unsigned short desiredPortNum = 0; // unfortunately we can't use getopt() here, as Windoze doesn't have it // Create our client object: ourClient = createClient(*env, verbosityLevel, progName); if (ourClient == NULL) { *env << "Failed to create " << clientProtocolName << " client: " << env->getResultMsg() << "\n"; pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv ); break; } // 开始获取Opition. pThis->SetRtspStatus( RTSPStatus_Opitiion ); // Begin by sending an "OPTIONS" command: char* optionsResponse = getOptionsResponse(ourClient, pThis->m_strRTSPUrlA.c_str(), username, password); if (optionsResponse == NULL) { *env << clientProtocolName << " \"OPTIONS\" request failed: " << env->getResultMsg() << "\n"; pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv ); break; } else { *env << clientProtocolName << " \"OPTIONS\" request returned: " << optionsResponse << "\n"; } if( optionsResponse ) { delete[] optionsResponse; } // 开始获取Description. // Open the URL, to get a SDP description: pThis->SetRtspStatus( RTSPStatus_Description ); char* sdpDescription = getSDPDescriptionFromURL(ourClient, pThis->m_strRTSPUrlA.c_str(), username, password, proxyServerName, proxyServerPortNum, desiredPortNum); if (sdpDescription == NULL) { *env << "Failed to get a SDP description from URL \"" << pThis->m_strRTSPUrlA.c_str() << "\": " << env->getResultMsg() << "\n"; pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv ); break; } *env << "Opened URL \"" << pThis->m_strRTSPUrlA.c_str() << "\", returning a SDP description:\n" << sdpDescription << "\n"; // Create a media session object from this SDP description: session = MediaSession::createNew(*env, sdpDescription); delete[] sdpDescription; if (session == NULL) { *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n"; pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv ); break; } else if (!session->hasSubsessions()) { *env << "This session has no media subsessions (i.e., \"m=\" lines)\n"; pThis->SetRtspStatus( RTSPStatus_Error_Connect_Srv ); break; } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*session); MediaSubsession *subsession; Boolean madeProgress = False; char const* singleMediumToTest = singleMedium; while ((subsession = iter.next()) != NULL) { // If we've asked to receive only a single medium, then check this now: if (singleMediumToTest != NULL) { if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) { *env << "Ignoring \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession, because we've asked to receive a single " << singleMedium << " session only\n"; continue; } else { // Receive this subsession only singleMediumToTest = "xxxxx"; // this hack ensures that we get only 1 subsession of this type } } desiredPortNum = 0; if (desiredPortNum != 0) { subsession->setClientPortNum(desiredPortNum); desiredPortNum += 2; } if (true) { if (!subsession->initiate(simpleRTPoffsetArg)) { *env << "Unable to create receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession: " << env->getResultMsg() << "\n"; } else { *env << "Created receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession (client ports " << subsession->clientPortNum() << "-" << subsession->clientPortNum()+1 << ")\n"; madeProgress = True; 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); if (socketInputBufferSize > 0) { // Set the RTP source's input buffer size as specified: int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize = getReceiveBufferSize(*env, socketNum); unsigned newBufferSize = setReceiveBufferTo(*env, socketNum, socketInputBufferSize); *env << "Changed socket receive buffer size for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession from " << curBufferSize << " to " << newBufferSize << " bytes\n"; } } } } else { mcu::tlog << _T( "Use port: " ) << (int)subsession->clientPortNum() << endl; if (subsession->clientPortNum() == 0) { *env << "No client port was specified for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession. (Try adding the \"-p <portNum>\" option.)\n"; } else { madeProgress = True; } } } if (!madeProgress) break; // Perform additional 'setup' on each subsession, before playing them: pThis->SetRtspStatus( RTSPStatus_Setup ); unsigned nResponseCode = NULL; BOOL bSetupSuccess = setupStreams( &nResponseCode ); if ( !bSetupSuccess ) { // setup失败! if ( RTSPResp_Error_Server_Full == nResponseCode ) { pThis->SetRtspStatus( RTSPStatus_Error_Server_Full ); } else { pThis->SetRtspStatus( RTSPStatus_Idle ); } break; } // Create output files: if ( true ) { // Create and start "FileSink"s for each subsession: madeProgress = False; iter.reset(); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // was not initiated MediaSink *pDecodeSink = 0; if (strcmp(subsession->mediumName(), "video") == 0 ) { int nBandWidth = subsession->GetBandWidth(); if ( strcmp(subsession->codecName(), "MP4V-ES") == 0 ) { CMpeg4StreamDecodeSink *pMsds = CMpeg4StreamDecodeSink::CreateNew( *env, 20000, nBandWidth ); pDecodeSink = pMsds; } else if ( strcmp( subsession->codecName(), "H264" ) == 0 ) { CH264StreamDecodeSink *pHsds = CH264StreamDecodeSink::CreateNew( *env, 20000, nBandWidth ); pDecodeSink = pHsds; } else { continue; } } subsession->sink = pDecodeSink; if (subsession->sink == NULL) { *env << "Failed to create CH264StreamDecodeSink \"" << "\n"; } subsession->sink->startPlaying(*(subsession->readSource()), subsessionAfterPlaying, subsession); // Also set a handler to be called if a RTCP "BYE" arrives // for this subsession: if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, subsession); } // 发送NAT探测包。 unsigned char temp[112] = {0}; temp[0] = 0x80; subsession->rtpSource()->RTPgs()->output( *env, 0,temp, 112 ); madeProgress = True; } } // Finally, start playing each subsession, to start the data flow: pThis->SetRtspStatus( RTSPStatus_Play ); startPlayingStreams(); pThis->SetRtspStatus( RTSPStatus_Running ); // 传入结束标志指针。 env->taskScheduler().doEventLoop( &pThis->m_runFlag ); pThis->SetRtspStatus( RTSPStatus_Idle ); } while(0); return 0; }
extern "C" demuxer_t* demux_open_rtp(demuxer_t* demuxer) { struct MPOpts *opts = demuxer->opts; Boolean success = False; do { TaskScheduler* scheduler = BasicTaskScheduler::createNew(); if (scheduler == NULL) break; UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler); if (env == NULL) break; RTSPClient* rtspClient = NULL; SIPClient* sipClient = NULL; if (demuxer == NULL || demuxer->stream == NULL) break; // shouldn't happen demuxer->stream->eof = 0; // just in case // Look at the stream's 'priv' field to see if we were initiated // via a SDP description: char* sdpDescription = (char*)(demuxer->stream->priv); if (sdpDescription == NULL) { // We weren't given a SDP description directly, so assume that // we were given a RTSP or SIP URL: char const* protocol = demuxer->stream->streaming_ctrl->url->protocol; char const* url = demuxer->stream->streaming_ctrl->url->url; extern int verbose; if (strcmp(protocol, "rtsp") == 0) { rtspClient = RTSPClient::createNew(*env, verbose, "MPlayer"); if (rtspClient == NULL) { fprintf(stderr, "Failed to create RTSP client: %s\n", env->getResultMsg()); break; } sdpDescription = openURL_rtsp(rtspClient, url); } else { // SIP unsigned char desiredAudioType = 0; // PCMU (use 3 for GSM) sipClient = SIPClient::createNew(*env, desiredAudioType, NULL, verbose, "MPlayer"); if (sipClient == NULL) { fprintf(stderr, "Failed to create SIP client: %s\n", env->getResultMsg()); break; } sipClient->setClientStartPortNum(8000); sdpDescription = openURL_sip(sipClient, url); } if (sdpDescription == NULL) { fprintf(stderr, "Failed to get a SDP description from URL \"%s\": %s\n", url, env->getResultMsg()); break; } } // Now that we have a SDP description, create a MediaSession from it: MediaSession* mediaSession = MediaSession::createNew(*env, sdpDescription); if (mediaSession == NULL) break; // Create a 'RTPState' structure containing the state that we just created, // and store it in the demuxer's 'priv' field, for future reference: RTPState* rtpState = new RTPState; rtpState->sdpDescription = sdpDescription; rtpState->rtspClient = rtspClient; rtpState->sipClient = sipClient; rtpState->mediaSession = mediaSession; rtpState->audioBufferQueue = rtpState->videoBufferQueue = NULL; rtpState->flags = 0; rtpState->firstSyncTime.tv_sec = rtpState->firstSyncTime.tv_usec = 0; demuxer->priv = rtpState; int audiofound = 0, videofound = 0; // Create RTP receivers (sources) for each subsession: MediaSubsessionIterator iter(*mediaSession); MediaSubsession* subsession; unsigned desiredReceiveBufferSize; while ((subsession = iter.next()) != NULL) { // Ignore any subsession that's not audio or video: if (strcmp(subsession->mediumName(), "audio") == 0) { if (audiofound) { fprintf(stderr, "Additional subsession \"audio/%s\" skipped\n", subsession->codecName()); continue; } desiredReceiveBufferSize = 100000; } else if (strcmp(subsession->mediumName(), "video") == 0) { if (videofound) { fprintf(stderr, "Additional subsession \"video/%s\" skipped\n", subsession->codecName()); continue; } desiredReceiveBufferSize = 2000000; } else { continue; } if (rtsp_port) subsession->setClientPortNum (rtsp_port); if (!subsession->initiate()) { fprintf(stderr, "Failed to initiate \"%s/%s\" RTP subsession: %s\n", subsession->mediumName(), subsession->codecName(), env->getResultMsg()); } else { fprintf(stderr, "Initiated \"%s/%s\" RTP subsession on port %d\n", subsession->mediumName(), subsession->codecName(), subsession->clientPortNum()); // Set the OS's socket receive buffer sufficiently large to avoid // incoming packets getting dropped between successive reads from this // subsession's demuxer. Depending on the bitrate(s) that you expect, // you may wish to tweak the "desiredReceiveBufferSize" values above. int rtpSocketNum = subsession->rtpSource()->RTPgs()->socketNum(); int receiveBufferSize = increaseReceiveBufferTo(*env, rtpSocketNum, desiredReceiveBufferSize); if (verbose > 0) { fprintf(stderr, "Increased %s socket receive buffer to %d bytes \n", subsession->mediumName(), receiveBufferSize); } if (rtspClient != NULL) { // Issue a RTSP "SETUP" command on the chosen subsession: if (!rtspClient->setupMediaSubsession(*subsession, False, rtsp_transport_tcp)) break; if (!strcmp(subsession->mediumName(), "audio")) audiofound = 1; if (!strcmp(subsession->mediumName(), "video")) videofound = 1; } } } if (rtspClient != NULL) { // Issue a RTSP aggregate "PLAY" command on the whole session: if (!rtspClient->playMediaSession(*mediaSession)) break; } else if (sipClient != NULL) { sipClient->sendACK(); // to start the stream flowing } // Now that the session is ready to be read, do additional // MPlayer codec-specific initialization on each subsession: iter.reset(); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // not reading this unsigned flags = 0; if (strcmp(subsession->mediumName(), "audio") == 0) { rtpState->audioBufferQueue = new ReadBufferQueue(subsession, demuxer, "audio"); rtpState->audioBufferQueue->otherQueue = &(rtpState->videoBufferQueue); rtpCodecInitialize_audio(demuxer, subsession, flags); } else if (strcmp(subsession->mediumName(), "video") == 0) { rtpState->videoBufferQueue = new ReadBufferQueue(subsession, demuxer, "video"); rtpState->videoBufferQueue->otherQueue = &(rtpState->audioBufferQueue); rtpCodecInitialize_video(demuxer, subsession, flags); } rtpState->flags |= flags; } success = True; } while (0); if (!success) return NULL; // an error occurred // Hack: If audio and video are demuxed together on a single RTP stream, // then create a new "demuxer_t" structure to allow the higher-level // code to recognize this: if (demux_is_multiplexed_rtp_stream(demuxer)) { stream_t* s = new_ds_stream(demuxer->video); demuxer_t* od = demux_open(opts, s, DEMUXER_TYPE_UNKNOWN, opts->audio_id, opts->video_id, opts->sub_id, NULL); demuxer = new_demuxers_demuxer(od, od, od); } return demuxer; }
bool MtkRTSPClient::handDescription(char* resultString) { CHECK_NULL_COND(resultString, false); char* sdpDescription = resultString; //LOG_DEBUG("SDP description:%s", sdpDescription); // Create a media session object from this SDP description: session = MediaSession::createNew(*env, sdpDescription); if (session == NULL) { LOG_ERR("Failed to create a MediaSession object from the SDP description: %s", env->getResultMsg()); return false; } if (!session->hasSubsessions()) { LOG_ERR("This session has no media subsessions (i.e., \"m=\" lines)"); Medium::close(session); session = NULL; return false; } /* *TO DO:GET THE TIME RANGE */ fStartTime = session->playStartTime(); if (fStartTime < 0) { fStartTime = 0.0f; } fEndTime= session->playEndTime(); if (fEndTime <= 0) { fEndTime = -1.0f; } { /*send setup requesst count*/ iSetupCount = 0; } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*(session)); MediaSubsession *subsession = NULL; RtspReqSender *senderSave = pRtspReqSender->getNext(); if (senderSave == NULL) { LOG_ERR("error"); return false; } CmdSenderDecorator *senderMove = pRtspReqSender; while ((subsession = iter.next()) != NULL) { if (!subsession->initiate(-1)) { LOG_ERR("warning"); continue; } if (subsession->rtpSource() != NULL) { #if 0 // 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); #endif #if 0 // Set the RTP source's OS socket buffer size as appropriate - either if we were explicitly asked (using -B), // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size. // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size, // then the input data rate may be large enough to justify increasing the OS socket buffer size also.) int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize = getReceiveBufferSize(*env, socketNum); LOG_DEBUG("old receive buffer size:%d", curBufferSize); if (fileSinkBufferSize > curBufferSize) { unsigned newBufferSize = setReceiveBufferTo(*env, socketNum, fileSinkBufferSize); LOG_DEBUG("new receive buffer size:%d", newBufferSize); } #else int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned newBufferSize = setReceiveBufferTo(*env, socketNum, maxBufSize); LOG_DEBUG("new receive buffer size:%d", newBufferSize); #endif } if (subsession->readSource() == NULL) { LOG_ERR("warning"); continue; // was not initiated } /* *TO DO:SET UP SUBSESSION */ SetupSender *setupSender = new SetupSender(*senderSave); if (setupSender == NULL) { LOG_ERR("warning"); continue; } sender->RecordSender(setupSender); senderMove->setNext(setupSender); senderMove = setupSender; setupSender->setRspHandler(respHandler); setupSender->setSubsession(subsession); if (bUseTcp == true) { if (subsession->clientPortNum() != 0) { LOG_DEBUG("sub session %p using tcp port :%d!", subsession, subsession->clientPortNum()); setupSender->setParam(false, true, false); } } iSetupCount++; LOG_DEBUG("subsession, name:%s, codec:%s", subsession->mediumName(), subsession->codecName()); } 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; }
bool CRTSPClient::OpenStream(char* url) { XBMC->Log(LOG_DEBUG, "CRTSPClient::OpenStream()"); m_session=NULL; strcpy(m_url,url); // Open the URL, to get a SDP description: char* sdpDescription= getSDPDescriptionFromURL(m_ourClient, url, ""/*username*/, ""/*password*/,""/*proxyServerName*/, 0/*proxyServerPortNum*/,1234/*desiredPortNum*/); if (sdpDescription == NULL) { XBMC->Log(LOG_DEBUG, "Failed to get a SDP description from URL %s %s",url ,m_env->getResultMsg() ); shutdown(); return false; } XBMC->Log(LOG_DEBUG, "Opened URL %s %s",url,sdpDescription); char* range=strstr(sdpDescription,"a=range:npt="); if (range!=NULL) { char *pStart = range+strlen("a=range:npt="); char *pEnd = strstr(range,"-") ; if (pEnd!=NULL) { pEnd++ ; double Start=atof(pStart) ; double End=atof(pEnd) ; XBMC->Log(LOG_DEBUG, "rangestart:%f rangeend:%f", Start,End); m_duration=(long) ((End-Start)*1000.0); } } // Create a media session object from this SDP description: m_session = MediaSession::createNew(*m_env, sdpDescription); delete[] sdpDescription; if (m_session == NULL) { XBMC->Log(LOG_DEBUG, "Failed to create a MediaSession object from the SDP description:%s ",m_env->getResultMsg()); shutdown(); return false; } else if (!m_session->hasSubsessions()) { XBMC->Log(LOG_DEBUG, "This session has no media subsessions"); shutdown(); return false; } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*m_session); MediaSubsession *subsession; Boolean madeProgress = False; char const* singleMediumToTest = singleMedium; while ((subsession = iter.next()) != NULL) { // If we've asked to receive only a single medium, then check this now: if (singleMediumToTest != NULL) { if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) { XBMC->Log(LOG_DEBUG, "Ignoring %s %s %s" , subsession->mediumName(),subsession->codecName(),singleMedium); continue; } else { // Receive this subsession only singleMediumToTest = "xxxxx"; // this hack ensures that we get only 1 subsession of this type } } if (desiredPortNum != 0) { subsession->setClientPortNum(desiredPortNum); desiredPortNum += 2; } if (createReceivers) { if (!subsession->initiate(simpleRTPoffsetArg)) { XBMC->Log(LOG_DEBUG, "Unable to create receiver for %s %s %s" ,subsession->mediumName(),subsession->codecName(),m_env->getResultMsg()); } else { XBMC->Log(LOG_DEBUG, "Created receiver for type=%s codec=%s ports: %d %d " ,subsession->mediumName(),subsession->codecName(),subsession->clientPortNum(),subsession->clientPortNum()+1 ); madeProgress = True; 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: int socketNum= subsession->rtpSource()->RTPgs()->socketNum(); XBMC->Log(LOG_DEBUG, "rtsp:increaseReceiveBufferTo to 2000000 for s:%d",socketNum); increaseReceiveBufferTo( *m_env, socketNum, 2000000 ); unsigned const thresh = 1000000; // 1 second subsession->rtpSource()->setPacketReorderingThresholdTime(thresh); if (socketInputBufferSize > 0) { // Set the RTP source's input buffer size as specified: int socketNum= subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize= getReceiveBufferSize(*m_env, socketNum); unsigned newBufferSize= setReceiveBufferTo(*m_env, socketNum, socketInputBufferSize); XBMC->Log(LOG_DEBUG, "Changed socket receive buffer size for the %s %s %d %d", subsession->mediumName(),subsession->codecName(),curBufferSize,newBufferSize); } } } } else { if (subsession->clientPortNum() == 0) { XBMC->Log(LOG_DEBUG, "No client port was specified for the %s %s",subsession->mediumName(),subsession->codecName()); } else { madeProgress = True; } } } if (!madeProgress) { shutdown(); return false; } // Perform additional 'setup' on each subsession, before playing them: if (!setupStreams()) { return false; } // Create output files: // Create and start "FileSink"s for each subsession: madeProgress = False; iter.reset(); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // was not initiated // Mediaportal: CMemorySink* fileSink= CMemorySink::createNew(*m_env, *m_buffer, fileSinkBufferSize); // XBMC test via file: //FileSink* fileSink = FileSink::createNew(*m_env, m_outFileName, fileSinkBufferSize, false); //oneFilePerFrame subsession->sink = fileSink; if (subsession->sink == NULL) { XBMC->Log(LOG_DEBUG, "Failed to create FileSink %s",m_env->getResultMsg()); shutdown(); return false; } XBMC->Log(LOG_DEBUG, "Created output sink: %s", m_outFileName); subsession->sink->startPlaying(*(subsession->readSource()),my_subsessionAfterPlaying,subsession); // Also set a handler to be called if a RTCP "BYE" arrives // for this subsession: if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(my_subsessionByeHandler,subsession); } madeProgress = True; } return true; }
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 { // 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); // Begin by creating our RTSP client object: fRTSPClient = new RTSPClientForDarwinInjector(envir(), url, fVerbosityLevel, fApplicationName, this); 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()); } AddressString remoteRTSPServerAddressStr(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.val()) + strlen(sessionName) + strlen(sessionInfo) + strlen(fApplicationName) + strlen(sessionAuthor) + strlen(sessionCopyright) + fSubstreamSDPSizes; unsigned const sdpSessionId = our_random32(); unsigned const sdpVersion = sdpSessionId; sdp = new char[sdpLen]; sprintf(sdp, sdpFmt, sdpSessionId, sdpVersion, // o= line sessionName, // s= line sessionInfo, // i= line remoteRTSPServerAddressStr.val(), // 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); } // Do a RTSP "ANNOUNCE" with this SDP description: Authenticator auth; Authenticator* authToUse = NULL; if (remoteUserName[0] != '\0' || remotePassword[0] != '\0') { auth.setUsernameAndPassword(remoteUserName, remotePassword); authToUse = &auth; } fWatchVariable = 0; (void)fRTSPClient->sendAnnounceCommand(sdp, genericResponseHandler, authToUse); // Now block (but handling events) until we get a response: envir().taskScheduler().doEventLoop(&fWatchVariable); delete[] fResultString; if (fResultCode != 0) break; // an error occurred with the RTSP "ANNOUNCE" command // Next, 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; fWatchVariable = 0; (void)fRTSPClient->sendSetupCommand(*subsession, genericResponseHandler, True /*streamOutgoing*/, True /*streamUsingTCP*/); // Now block (but handling events) until we get a response: envir().taskScheduler().doEventLoop(&fWatchVariable); delete[] fResultString; if (fResultCode != 0) break; // an error occurred with the RTSP "SETUP" command // 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: fWatchVariable = 0; (void)fRTSPClient->sendPlayCommand(*fSession, genericResponseHandler); // Now block (but handling events) until we get a response: envir().taskScheduler().doEventLoop(&fWatchVariable); delete[] fResultString; if (fResultCode != 0) break; // an error occurred with the RTSP "PLAY" command // 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; }
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; }
Boolean MediaSession ::initiateByMediaType(char const* mimeType, MediaSubsession*& resultSubsession, PrioritizedRTPStreamSelector*& resultMultiSource, int& resultMultiSourceSessionId, int useSpecialRTPoffset) { // Look through this session's subsessions for media that match "mimeType" resultSubsession = NULL; resultMultiSource = NULL; resultMultiSourceSessionId = 0; unsigned maxStaggerSeconds = 0; MediaSubsessionIterator iter(*this); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { if (resultMultiSourceSessionId != 0 && subsession->mctSLAPSessionId() != resultMultiSourceSessionId) { // We're using a multi-source SLAP session, but this subsession // isn't part of it continue; } Boolean wasAlreadyInitiated = subsession->readSource() != NULL; if (!wasAlreadyInitiated) { // Try to create a source for this subsession: if (!subsession->initiate(useSpecialRTPoffset)) return False; } // Make sure the source's MIME type is one that we handle: if (strcmp(subsession->readSource()->MIMEtype(), mimeType) != 0) { if (!wasAlreadyInitiated) subsession->deInitiate(); continue; } if (subsession->mctSLAPSessionId() == 0) { // Normal case: a single session resultSubsession = subsession; break; // use this } else { // Special case: a multi-source SLAP session resultMultiSourceSessionId = subsession->mctSLAPSessionId(); unsigned subsessionStaggerSeconds = subsession->mctSLAPStagger(); if (subsessionStaggerSeconds > maxStaggerSeconds) { maxStaggerSeconds = subsessionStaggerSeconds; } } } if (resultSubsession == NULL && resultMultiSourceSessionId == 0) { envir().setResultMsg("Session has no usable media subsession"); return False; } if (resultMultiSourceSessionId != 0) { // We have a multi-source MCT SLAP session; create a selector for it: unsigned seqNumStagger = computeSeqNumStagger(maxStaggerSeconds); resultMultiSource = PrioritizedRTPStreamSelector::createNew(envir(), seqNumStagger); if (resultMultiSource == NULL) return False; // Note: each subsession has its own RTCP instance; we don't return them // Then run through the subsessions again, adding each of the sources: iter.reset(); while ((subsession = iter.next()) != NULL) { if (subsession->mctSLAPSessionId() == resultMultiSourceSessionId) { resultMultiSource->addInputRTPStream(subsession->rtpSource(), subsession->rtcpInstance()); } } } return True; }
int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); progName = argv[0]; gettimeofday(&startTime, NULL); #ifdef USE_SIGNALS // Allow ourselves to be shut down gracefully by a SIGHUP or a SIGUSR1: signal(SIGHUP, signalHandlerShutdown); signal(SIGUSR1, signalHandlerShutdown); #endif unsigned short desiredPortNum = 0; // unfortunately we can't use getopt() here, as Windoze doesn't have it while (argc > 2) { char* const opt = argv[1]; if (opt[0] != '-') usage(); switch (opt[1]) { case 'p': { // specify start port number int portArg; if (sscanf(argv[2], "%d", &portArg) != 1) { usage(); } if (portArg <= 0 || portArg >= 65536 || portArg&1) { *env << "bad port number: " << portArg << " (must be even, and in the range (0,65536))\n"; usage(); } desiredPortNum = (unsigned short)portArg; ++argv; --argc; break; } case 'r': { // do not receive data (instead, just 'play' the stream(s)) createReceivers = False; break; } case 'q': { // output a QuickTime file (to stdout) outputQuickTimeFile = True; break; } case '4': { // output a 'mp4'-format file (to stdout) outputQuickTimeFile = True; generateMP4Format = True; break; } case 'i': { // output an AVI file (to stdout) outputAVIFile = True; break; } case 'I': { // specify input interface... NetAddressList addresses(argv[2]); if (addresses.numAddresses() == 0) { *env << "Failed to find network address for \"" << argv[2] << "\""; break; } ReceivingInterfaceAddr = *(unsigned*)(addresses.firstAddress()->data()); ++argv; --argc; break; } case 'a': { // receive/record an audio stream only audioOnly = True; singleMedium = "audio"; break; } case 'v': { // receive/record a video stream only videoOnly = True; singleMedium = "video"; break; } case 'V': { // disable verbose output verbosityLevel = 0; break; } case 'd': { // specify duration, or how much to delay after end time float arg; if (sscanf(argv[2], "%g", &arg) != 1) { usage(); } if (argv[2][0] == '-') { // not "arg<0", in case argv[2] was "-0" // a 'negative' argument was specified; use this for "durationSlop": duration = 0; // use whatever's in the SDP durationSlop = -arg; } else { duration = arg; durationSlop = 0; } ++argv; --argc; break; } case 'D': { // specify maximum number of seconds to wait for packets: if (sscanf(argv[2], "%u", &interPacketGapMaxTime) != 1) { usage(); } ++argv; --argc; break; } case 'c': { // play continuously playContinuously = True; break; } case 'S': { // specify an offset to use with "SimpleRTPSource"s if (sscanf(argv[2], "%d", &simpleRTPoffsetArg) != 1) { usage(); } if (simpleRTPoffsetArg < 0) { *env << "offset argument to \"-S\" must be >= 0\n"; usage(); } ++argv; --argc; break; } case 'O': { // Don't send an "OPTIONS" request before "DESCRIBE" sendOptionsRequest = False; break; } case 'o': { // Send only the "OPTIONS" request to the server sendOptionsRequestOnly = True; break; } case 'm': { // output multiple files - one for each frame oneFilePerFrame = True; break; } case 'n': { // notify the user when the first data packet arrives notifyOnPacketArrival = True; break; } case 't': { // stream RTP and RTCP over the TCP 'control' connection if (controlConnectionUsesTCP) { streamUsingTCP = True; } else { usage(); } break; } case 'T': { // stream RTP and RTCP over a HTTP connection if (controlConnectionUsesTCP) { if (argc > 3 && argv[2][0] != '-') { // The next argument is the HTTP server port number: if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1 && tunnelOverHTTPPortNum > 0) { ++argv; --argc; break; } } } // If we get here, the option was specified incorrectly: usage(); break; } case 'u': { // specify a username and password username = argv[2]; password = argv[3]; argv+=2; argc-=2; if (allowProxyServers && argc > 3 && argv[2][0] != '-') { // The next argument is the name of a proxy server: proxyServerName = argv[2]; ++argv; --argc; if (argc > 3 && argv[2][0] != '-') { // The next argument is the proxy server port number: if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) { usage(); } ++argv; --argc; } } break; } case 'A': { // specify a desired audio RTP payload format unsigned formatArg; if (sscanf(argv[2], "%u", &formatArg) != 1 || formatArg >= 96) { usage(); } desiredAudioRTPPayloadFormat = (unsigned char)formatArg; ++argv; --argc; break; } case 'M': { // specify a MIME subtype for a dynamic RTP payload type mimeSubtype = argv[2]; if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96; ++argv; --argc; break; } case 'w': { // specify a width (pixels) for an output QuickTime or AVI movie if (sscanf(argv[2], "%hu", &movieWidth) != 1) { usage(); } movieWidthOptionSet = True; ++argv; --argc; break; } case 'h': { // specify a height (pixels) for an output QuickTime or AVI movie if (sscanf(argv[2], "%hu", &movieHeight) != 1) { usage(); } movieHeightOptionSet = True; ++argv; --argc; break; } case 'f': { // specify a frame rate (per second) for an output QT or AVI movie if (sscanf(argv[2], "%u", &movieFPS) != 1) { usage(); } movieFPSOptionSet = True; ++argv; --argc; break; } case 'F': { // specify a prefix for the audio and video output files fileNamePrefix = argv[2]; ++argv; --argc; break; } case 'b': { // specify the size of buffers for "FileSink"s if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) { usage(); } ++argv; --argc; break; } case 'B': { // specify the size of input socket buffers if (sscanf(argv[2], "%u", &socketInputBufferSize) != 1) { usage(); } ++argv; --argc; break; } // Note: The following option is deprecated, and may someday be removed: case 'l': { // try to compensate for packet loss by repeating frames packetLossCompensate = True; break; } case 'y': { // synchronize audio and video streams syncStreams = True; break; } case 'H': { // generate hint tracks (as well as the regular data tracks) generateHintTracks = True; break; } case 'Q': { // output QOS measurements qosMeasurementIntervalMS = 1000; // default: 1 second if (argc > 3 && argv[2][0] != '-') { // The next argument is the measurement interval, // in multiples of 100 ms if (sscanf(argv[2], "%u", &qosMeasurementIntervalMS) != 1) { usage(); } qosMeasurementIntervalMS *= 100; ++argv; --argc; } break; } case 's': { // specify initial seek time (trick play) double arg; if (sscanf(argv[2], "%lg", &arg) != 1 || arg < 0) { usage(); } initialSeekTime = arg; ++argv; --argc; break; } case 'z': { // scale (trick play) float arg; if (sscanf(argv[2], "%g", &arg) != 1 || arg == 0.0f) { usage(); } scale = arg; ++argv; --argc; break; } default: { usage(); break; } } ++argv; --argc; } if (argc != 2) usage(); if (outputQuickTimeFile && outputAVIFile) { *env << "The -i and -q (or -4) flags cannot both be used!\n"; usage(); } Boolean outputCompositeFile = outputQuickTimeFile || outputAVIFile; if (!createReceivers && outputCompositeFile) { *env << "The -r and -q (or -4 or -i) flags cannot both be used!\n"; usage(); } if (outputCompositeFile && !movieWidthOptionSet) { *env << "Warning: The -q, -4 or -i option was used, but not -w. Assuming a video width of " << movieWidth << " pixels\n"; } if (outputCompositeFile && !movieHeightOptionSet) { *env << "Warning: The -q, -4 or -i option was used, but not -h. Assuming a video height of " << movieHeight << " pixels\n"; } if (outputCompositeFile && !movieFPSOptionSet) { *env << "Warning: The -q, -4 or -i option was used, but not -f. Assuming a video frame rate of " << movieFPS << " frames-per-second\n"; } if (audioOnly && videoOnly) { *env << "The -a and -v flags cannot both be used!\n"; usage(); } if (sendOptionsRequestOnly && !sendOptionsRequest) { *env << "The -o and -O flags cannot both be used!\n"; usage(); } if (tunnelOverHTTPPortNum > 0) { if (streamUsingTCP) { *env << "The -t and -T flags cannot both be used!\n"; usage(); } else { streamUsingTCP = True; } } if (!createReceivers && notifyOnPacketArrival) { *env << "Warning: Because we're not receiving stream data, the -n flag has no effect\n"; } if (durationSlop < 0) { // This parameter wasn't set, so use a default value. // If we're measuring QOS stats, then don't add any slop, to avoid // having 'empty' measurement intervals at the end. durationSlop = qosMeasurementIntervalMS > 0 ? 0.0 : 5.0; } char* url = argv[1]; // Create our client object: ourClient = createClient(*env, verbosityLevel, progName); if (ourClient == NULL) { *env << "Failed to create " << clientProtocolName << " client: " << env->getResultMsg() << "\n"; shutdown(); } if (sendOptionsRequest) { // Begin by sending an "OPTIONS" command: char* optionsResponse = getOptionsResponse(ourClient, url, username, password); if (sendOptionsRequestOnly) { if (optionsResponse == NULL) { *env << clientProtocolName << " \"OPTIONS\" request failed: " << env->getResultMsg() << "\n"; } else { *env << clientProtocolName << " \"OPTIONS\" request returned: " << optionsResponse << "\n"; } shutdown(); } delete[] optionsResponse; } // Open the URL, to get a SDP description: char* sdpDescription = getSDPDescriptionFromURL(ourClient, url, username, password, proxyServerName, proxyServerPortNum, desiredPortNum); if (sdpDescription == NULL) { *env << "Failed to get a SDP description from URL \"" << url << "\": " << env->getResultMsg() << "\n"; shutdown(); } *env << "Opened URL \"" << url << "\", returning a SDP description:\n" << sdpDescription << "\n"; // Create a media session object from this SDP description: session = MediaSession::createNew(*env, sdpDescription); delete[] sdpDescription; if (session == NULL) { *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n"; shutdown(); } else if (!session->hasSubsessions()) { *env << "This session has no media subsessions (i.e., \"m=\" lines)\n"; shutdown(); } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*session); MediaSubsession *subsession; Boolean madeProgress = False; char const* singleMediumToTest = singleMedium; while ((subsession = iter.next()) != NULL) { // If we've asked to receive only a single medium, then check this now: if (singleMediumToTest != NULL) { if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) { *env << "Ignoring \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession, because we've asked to receive a single " << singleMedium << " session only\n"; continue; } else { // Receive this subsession only singleMediumToTest = "xxxxx"; // this hack ensures that we get only 1 subsession of this type } } if (desiredPortNum != 0) { subsession->setClientPortNum(desiredPortNum); desiredPortNum += 2; } if (createReceivers) { if (!subsession->initiate(simpleRTPoffsetArg)) { *env << "Unable to create receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession: " << env->getResultMsg() << "\n"; } else { *env << "Created receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession (client ports " << subsession->clientPortNum() << "-" << subsession->clientPortNum()+1 << ")\n"; madeProgress = True; 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 - either if we were explicitly asked (using -B), // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size. // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size, // then the input data rate may be large enough to justify increasing the OS socket buffer size also.) int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize = getReceiveBufferSize(*env, socketNum); if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) { unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize; newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize); if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it: *env << "Changed socket receive buffer size for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession from " << curBufferSize << " to " << newBufferSize << " bytes\n"; } } } } } else { if (subsession->clientPortNum() == 0) { *env << "No client port was specified for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession. (Try adding the \"-p <portNum>\" option.)\n"; } else { madeProgress = True; } } } if (!madeProgress) shutdown(); // Perform additional 'setup' on each subsession, before playing them: setupStreams(); // Create output files: if (createReceivers) { if (outputQuickTimeFile) { // Create a "QuickTimeFileSink", to write to 'stdout': qtOut = QuickTimeFileSink::createNew(*env, *session, "stdout", fileSinkBufferSize, movieWidth, movieHeight, movieFPS, packetLossCompensate, syncStreams, generateHintTracks, generateMP4Format); if (qtOut == NULL) { *env << "Failed to create QuickTime file sink for stdout: " << env->getResultMsg(); shutdown(); } qtOut->startPlaying(sessionAfterPlaying, NULL); } else if (outputAVIFile) { // Create an "AVIFileSink", to write to 'stdout': aviOut = AVIFileSink::createNew(*env, *session, "stdout", fileSinkBufferSize, movieWidth, movieHeight, movieFPS, packetLossCompensate); if (aviOut == NULL) { *env << "Failed to create AVI file sink for stdout: " << env->getResultMsg(); shutdown(); } aviOut->startPlaying(sessionAfterPlaying, NULL); } else { // Create and start "FileSink"s for each subsession: madeProgress = False; iter.reset(); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // was not initiated // Create an output file for each desired stream: char outFileName[1000]; if (singleMedium == NULL) { // Output file name is // "<filename-prefix><medium_name>-<codec_name>-<counter>" static unsigned streamCounter = 0; snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d", fileNamePrefix, subsession->mediumName(), subsession->codecName(), ++streamCounter); } else { sprintf(outFileName, "stdout"); } FileSink* fileSink; if (strcmp(subsession->mediumName(), "audio") == 0 && (strcmp(subsession->codecName(), "AMR") == 0 || strcmp(subsession->codecName(), "AMR-WB") == 0)) { // For AMR audio streams, we use a special sink that inserts AMR frame hdrs: fileSink = AMRAudioFileSink::createNew(*env, outFileName, fileSinkBufferSize, oneFilePerFrame); } else if (strcmp(subsession->mediumName(), "video") == 0 && (strcmp(subsession->codecName(), "H264") == 0)) { // For H.264 video stream, we use a special sink that insert start_codes: fileSink = H264VideoFileSink::createNew(*env, outFileName, fileSinkBufferSize, oneFilePerFrame); } else { // Normal case: fileSink = FileSink::createNew(*env, outFileName, fileSinkBufferSize, oneFilePerFrame); } subsession->sink = fileSink; if (subsession->sink == NULL) { *env << "Failed to create FileSink for \"" << outFileName << "\": " << env->getResultMsg() << "\n"; } else { if (singleMedium == NULL) { *env << "Created output file: \"" << outFileName << "\"\n"; } else { *env << "Outputting data from the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession to 'stdout'\n"; } if (strcmp(subsession->mediumName(), "video") == 0 && strcmp(subsession->codecName(), "MP4V-ES") == 0 && subsession->fmtp_config() != NULL) { // For MPEG-4 video RTP streams, the 'config' information // from the SDP description contains useful VOL etc. headers. // Insert this data at the front of the output file: unsigned configLen; unsigned char* configData = parseGeneralConfigStr(subsession->fmtp_config(), configLen); struct timeval timeNow; gettimeofday(&timeNow, NULL); fileSink->addData(configData, configLen, timeNow); delete[] configData; } subsession->sink->startPlaying(*(subsession->readSource()), subsessionAfterPlaying, subsession); // Also set a handler to be called if a RTCP "BYE" arrives // for this subsession: if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, subsession); } madeProgress = True; } } if (!madeProgress) shutdown(); } } // Finally, start playing each subsession, to start the data flow: startPlayingStreams(); env->taskScheduler().doEventLoop(); // does not return return 0; // only to prevent compiler warning }
void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) { ALOG(TX_LOG_INFO, TAG,"continueAfterDESCRIBE\n"); if (resultCode != 0) { ALOG(TX_LOG_INFO, TAG,"Failed to get a SDP description for the URL %s, %s\n", streamURL, resultString); *env << "Failed to get a SDP description for the URL \"" << streamURL << "\": " << resultString << "\n"; delete[] resultString; shutdown(); } char* sdpDescription = resultString; *env << "Opened URL \"" << streamURL << "\", returning a SDP description:\n" << sdpDescription << "\n"; // Create a media session object from this SDP description: session = MediaSession::createNew(*env, sdpDescription); delete[] sdpDescription; if (session == NULL) { ALOG(TX_LOG_INFO, TAG, "Failed to create a MediaSession object from the SDP description:"); shutdown(); } else if (!session->hasSubsessions()) { ALOG(TX_LOG_INFO, TAG, "This session has no media subsessions (i.e., no \"m=\" lines)\n"); shutdown(); } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*session); MediaSubsession *subsession; Boolean madeProgress = False; char const* singleMediumToTest = singleMedium; while ((subsession = iter.next()) != NULL) { // If we've asked to receive only a single medium, then check this now: if (singleMediumToTest != NULL) { if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) { ALOG(TX_LOG_INFO, TAG, "Ignoring codecName = %s\n", subsession->codecName()); *env << "Ignoring \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession, because we've asked to receive a single " << singleMedium << " session only\n"; continue; } else { // Receive this subsession only singleMediumToTest = "xxxxx"; // this hack ensures that we get only 1 subsession of this type } } if (desiredPortNum != 0) { subsession->setClientPortNum(desiredPortNum); desiredPortNum += 2; } if (createReceivers) { if (!subsession->initiate(simpleRTPoffsetArg)) { ALOG(TX_LOG_INFO, TAG, "Unable to create receiver for\n"); *env << "Unable to create receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession: " << env->getResultMsg() << "\n"; } else { ALOG(TX_LOG_INFO, TAG, "Created receiver\n"); *env << "Created receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession ("; if (subsession->rtcpIsMuxed()) { ALOG(TX_LOG_INFO, TAG, "subsession->rtcpIsMuxed() client port = %d\n", subsession->clientPortNum()); *env << "client port " << subsession->clientPortNum(); } else { ALOG(TX_LOG_INFO, TAG, "subsession->rtcpIsMuxed(),,,,else"); *env << "client ports " << subsession->clientPortNum() << "-" << subsession->clientPortNum()+1; } *env << ")\n"; madeProgress = True; if (subsession->rtpSource() != NULL) { unsigned const thresh = 1000000; // 1 second subsession->rtpSource()->setPacketReorderingThresholdTime(thresh); int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize = getReceiveBufferSize(*env, socketNum); if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) { unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize; newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize); if (socketInputBufferSize > 0) { ALOG(TX_LOG_INFO, TAG, "socketInputBufferSize > 0 Changed socket receive buffer size for the\n"); *env << "Changed socket receive buffer size for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession from " << curBufferSize << " to " << newBufferSize << " bytes\n"; } } } } } else { ALOG(TX_LOG_INFO, TAG, "socketInputBufferSize > 0=====else\n"); if (subsession->clientPortNum() == 0) { *env << "No client port was specified for the \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession. (Try adding the \"-p <portNum>\" option.)\n"; } else { madeProgress = True; } } } if (!madeProgress) { ALOG(TX_LOG_INFO, TAG,"(!madeProgress)"); shutdown(); } // Perform additional 'setup' on each subsession, before playing them: setupStreams(); }