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; }
int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); progName = APP_NAME; username = SIP_USERNAME; password = SIP_PASSWORD; 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 = SIP_URL; // 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); //#ifdef SUPPORT_REAL_RTSP // } else if (session->isRealNetworksRDT) { // // For RealNetworks' sessions, we create a single output file, // // named "output.rm". // char outFileName[1000]; // if (singleMedium == NULL) { // snprintf(outFileName, sizeof(outFileName), // "%soutput.rm", fileNamePrefix); // } else { // // output to 'stdout' as normal, even though we actually output all media // sprintf(outFileName, "stdout"); // } // FileSink* fileSink = FileSink::createNew(*env, outFileName, // fileSinkBufferSize, // oneFilePerFrame); // // // The output file needs to begin with a special 'RMFF' header, // // in order for it to be usable. Write this header first: // unsigned headerSize; // unsigned char* headerData = RealGenerateRMFFHeader(session, headerSize); // struct timeval timeNow; // gettimeofday(&timeNow, NULL); // fileSink->addData(headerData, headerSize, timeNow); // delete[] headerData; // // // Start playing the output file from the first subsession. // // (Hack: Because all subsessions' data is actually multiplexed on the // // single RTSP TCP connection, playing from one subsession is sufficient.) // iter.reset(); // madeProgress = False; // while ((subsession = iter.next()) != NULL) { // if (subsession->readSource() == NULL) continue; // was not initiated // // fileSink->startPlaying(*(subsession->readSource()), // subsessionAfterPlaying, subsession); // madeProgress = True; // break; // play from one subsession only // } // if (!madeProgress) shutdown(); //#endif // } 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 }