void FileSource::visit(FileSink& v) { #if 0 // only works if target is a pipe. loff_t offset = offset_; result_ = ::splice(handle(), &offset, v.handle(), NULL, count_, SPLICE_F_MOVE | SPLICE_F_NONBLOCK | SPLICE_F_MORE); if (result_ > 0) { offset_ = offset; count_ -= result_; } #endif char buf[8 * 1024]; result_ = pread(handle(), buf, sizeof(buf), offset_); if (result_ <= 0) return; result_ = v.write(buf, result_); if (result_ <= 0) return; offset_ += result_; count_ -= result_; }
void FileSink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned /*durationInMicroseconds*/) { FileSink* sink = (FileSink*)clientData; sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime); }
void setupStreams() { static MediaSubsessionIterator* setupIter = NULL; if (setupIter == NULL) setupIter = new MediaSubsessionIterator(*session); while ((subsession = setupIter->next()) != NULL) { // We have another subsession left to set up: if (subsession->clientPortNum() == 0) continue; // port # was not set setupSubsession(subsession, streamUsingTCP, continueAfterSETUP); return; } // We're done setting up subsessions. delete setupIter; if (!madeProgress) shutdown(); // Create output files: if (createReceivers) { #if 0 /*wayde*/ 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); #endif /*wayde*/ } else { // Create and start "FileSink"s for each subsession: madeProgress = False; MediaSubsessionIterator iter(*session); 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: if (duration == 0) { if (scale > 0) duration = session->playEndTime() - initialSeekTime; // use SDP end time else if (scale < 0) duration = initialSeekTime; } if (duration < 0) duration = 0.0; endTime = initialSeekTime; if (scale > 0) { if (duration <= 0) endTime = -1.0f; else endTime = initialSeekTime + duration; } else { endTime = initialSeekTime - duration; if (endTime < 0) endTime = 0.0f; } startPlayingSession(session, initialSeekTime, endTime, scale, continueAfterPLAY); }
/*------------------------------------------------------------------------------ * Look for the FileCast stream outputs in the config file *----------------------------------------------------------------------------*/ void DarkIce :: configFileCast ( const Config & config ) throw ( Exception ) { // look for FileCast encoder output streams, // sections [file-0], [file-1], ... char stream[] = "file- "; size_t streamLen = Util::strLen( stream); unsigned int u; for ( u = noAudioOuts; u < maxOutput; ++u ) { const ConfigSection * cs; // ugly hack to change the section name to "stream0", "stream1", etc. stream[streamLen-1] = '0' + (u - noAudioOuts); if ( !(cs = config.get( stream)) ) { break; } const char * str; const char * format = 0; AudioEncoder::BitrateMode bitrateMode; unsigned int bitrate = 0; double quality = 0.0; const char * targetFileName = 0; unsigned int sampleRate = 0; int lowpass = 0; int highpass = 0; bool fileAddDate = false; const char * fileDateFormat = 0; format = cs->getForSure( "format", " missing in section ", stream); if ( !Util::strEq( format, "vorbis") && !Util::strEq( format, "mp3") && !Util::strEq( format, "mp2") && !Util::strEq( format, "aac") && !Util::strEq( format, "aacp") ) { throw Exception( __FILE__, __LINE__, "unsupported stream format: ", format); } str = cs->getForSure("bitrate", " missing in section ", stream); bitrate = Util::strToL( str); targetFileName = cs->getForSure( "fileName", " missing in section ", stream); str = cs->get( "fileAddDate"); fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false; fileDateFormat = cs->get( "fileDateFormat"); str = cs->get( "sampleRate"); sampleRate = str ? Util::strToL( str) : dsp->getSampleRate(); str = cs->get( "bitrate"); bitrate = str ? Util::strToL( str) : 0; str = cs->get( "quality"); quality = str ? Util::strToD( str) : 0.0; str = cs->getForSure( "bitrateMode", " not specified in section ", stream); if ( Util::strEq( str, "cbr") ) { bitrateMode = AudioEncoder::cbr; if ( bitrate == 0 ) { throw Exception( __FILE__, __LINE__, "bitrate not specified for CBR encoding"); } } else if ( Util::strEq( str, "abr") ) { bitrateMode = AudioEncoder::abr; if ( bitrate == 0 ) { throw Exception( __FILE__, __LINE__, "bitrate not specified for ABR encoding"); } } else if ( Util::strEq( str, "vbr") ) { bitrateMode = AudioEncoder::vbr; if ( cs->get( "quality" ) == 0 ) { throw Exception( __FILE__, __LINE__, "quality not specified for VBR encoding"); } } else { throw Exception( __FILE__, __LINE__, "invalid bitrate mode: ", str); } if (Util::strEq(format, "aac") && bitrateMode != AudioEncoder::abr) { throw Exception(__FILE__, __LINE__, "currently the AAC format only supports " "average bitrate mode"); } if (Util::strEq(format, "aacp") && bitrateMode != AudioEncoder::cbr) { throw Exception(__FILE__, __LINE__, "currently the AAC+ format only supports " "constant bitrate mode"); } str = cs->get( "lowpass"); lowpass = str ? Util::strToL( str) : 0; str = cs->get( "highpass"); highpass = str ? Util::strToL( str) : 0; // go on and create the things // the underlying file if ( fileAddDate ) { if (fileDateFormat == 0) { targetFileName = Util::fileAddDate( targetFileName); } else { targetFileName = Util::fileAddDate( targetFileName, fileDateFormat ); } } FileSink * targetFile = new FileSink( stream, targetFileName); if ( !targetFile->exists() ) { if ( !targetFile->create() ) { throw Exception( __FILE__, __LINE__, "can't create output file", targetFileName); } } // streaming related stuff audioOuts[u].socket = 0; audioOuts[u].server = new FileCast( targetFile ); if ( Util::strEq( format, "mp3") ) { #ifndef HAVE_LAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with lame support, " "thus can't create mp3 stream: ", stream); #else audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, dsp->getChannel(), lowpass, highpass ); #endif // HAVE_TWOLAME_LIB } else if ( Util::strEq( format, "mp2") ) { #ifndef HAVE_TWOLAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with TwoLAME support, " "thus can't create MPEG Audio Layer 2 stream: ", stream); #else audioOuts[u].encoder = new TwoLameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, sampleRate, dsp->getChannel() ); #endif // HAVE_TWOLAME_LIB } else if ( Util::strEq( format, "vorbis") ) { #ifndef HAVE_VORBIS_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with Ogg Vorbis support, " "thus can't Ogg Vorbis stream: ", stream); #else audioOuts[u].encoder = new VorbisLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, dsp->getSampleRate(), dsp->getChannel() ); #endif // HAVE_VORBIS_LIB } else if ( Util::strEq( format, "aac") ) { #ifndef HAVE_FAAC_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with AAC support, " "thus can't aac stream: ", stream); #else audioOuts[u].encoder = new FaacEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, dsp->getChannel()); #endif // HAVE_FAAC_LIB } else if ( Util::strEq( format, "aacp") ) { #ifndef HAVE_AACPLUS_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with AAC+ support, " "thus can't aacplus stream: ", stream); #else audioOuts[u].encoder = new aacPlusEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, dsp->getChannel()); #endif // HAVE_AACPLUS_LIB } else { throw Exception( __FILE__, __LINE__, "Illegal stream format: ", format); } encConnector->attach( audioOuts[u].encoder.get()); } noAudioOuts += u; }
/*------------------------------------------------------------------------------ * Look for the ShoutCast stream outputs in the config file *----------------------------------------------------------------------------*/ void DarkIce :: configShoutCast ( const Config & config, unsigned int bufferSecs ) throw ( Exception ) { // look for Shoutcast encoder output streams, // sections [shoutcast-0], [shoutcast-1], ... char stream[] = "shoutcast- "; size_t streamLen = Util::strLen( stream); unsigned int u; for ( u = noAudioOuts; u < maxOutput; ++u ) { const ConfigSection * cs; // ugly hack to change the section name to "stream0", "stream1", etc. stream[streamLen-1] = '0' + (u - noAudioOuts); if ( !(cs = config.get( stream)) ) { break; } #ifndef HAVE_LAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with lame support, " "thus can't connect to ShoutCast, stream: ", stream); #else const char * str; unsigned int sampleRate = 0; unsigned int channel = 0; AudioEncoder::BitrateMode bitrateMode; unsigned int bitrate = 0; double quality = 0.0; const char * server = 0; unsigned int port = 0; const char * password = 0; const char * name = 0; const char * url = 0; const char * genre = 0; bool isPublic = false; const char * mountPoint = 0; int lowpass = 0; int highpass = 0; const char * irc = 0; const char * aim = 0; const char * icq = 0; const char * localDumpName = 0; FileSink * localDumpFile = 0; bool fileAddDate = false; const char * fileDateFormat = 0; str = cs->get( "sampleRate"); sampleRate = str ? Util::strToL( str) : dsp->getSampleRate(); str = cs->get( "channel"); channel = str ? Util::strToL( str) : dsp->getChannel(); str = cs->get( "bitrate"); bitrate = str ? Util::strToL( str) : 0; str = cs->get( "quality"); quality = str ? Util::strToD( str) : 0.0; str = cs->getForSure( "bitrateMode", " not specified in section ", stream); if ( Util::strEq( str, "cbr") ) { bitrateMode = AudioEncoder::cbr; if ( bitrate == 0 ) { throw Exception( __FILE__, __LINE__, "bitrate not specified for CBR encoding"); } if ( cs->get( "quality" ) == 0 ) { throw Exception( __FILE__, __LINE__, "quality not specified for CBR encoding"); } } else if ( Util::strEq( str, "abr") ) { bitrateMode = AudioEncoder::abr; if ( bitrate == 0 ) { throw Exception( __FILE__, __LINE__, "bitrate not specified for ABR encoding"); } } else if ( Util::strEq( str, "vbr") ) { bitrateMode = AudioEncoder::vbr; if ( cs->get( "quality" ) == 0 ) { throw Exception( __FILE__, __LINE__, "quality not specified for VBR encoding"); } } else { throw Exception( __FILE__, __LINE__, "invalid bitrate mode: ", str); } server = cs->getForSure( "server", " missing in section ", stream); str = cs->getForSure( "port", " missing in section ", stream); port = Util::strToL( str); password = cs->getForSure("password"," missing in section ",stream); name = cs->get( "name"); mountPoint = cs->get( "mountPoint" ); url = cs->get( "url"); genre = cs->get( "genre"); str = cs->get( "public"); isPublic = str ? (Util::strEq( str, "yes") ? true : false) : false; str = cs->get( "lowpass"); lowpass = str ? Util::strToL( str) : 0; str = cs->get( "highpass"); highpass = str ? Util::strToL( str) : 0; irc = cs->get( "irc"); aim = cs->get( "aim"); icq = cs->get( "icq"); str = cs->get("fileAddDate"); fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false; fileDateFormat = cs->get( "fileDateFormat"); localDumpName = cs->get( "localDumpFile"); // go on and create the things // check for and create the local dump file if needed if ( localDumpName != 0 ) { if ( fileAddDate ) { if (fileDateFormat == 0) { localDumpName = Util::fileAddDate(localDumpName); } else { localDumpName = Util::fileAddDate( localDumpName, fileDateFormat ); } } localDumpFile = new FileSink( stream, localDumpName); if ( !localDumpFile->exists() ) { if ( !localDumpFile->create() ) { reportEvent( 1, "can't create local dump file", localDumpName); localDumpFile = 0; } } if ( fileAddDate ) { delete[] localDumpFile; } } // streaming related stuff audioOuts[u].socket = new TcpSocket( server, port); audioOuts[u].server = new ShoutCast( audioOuts[u].socket.get(), password, mountPoint, bitrate, name, url, genre, isPublic, irc, aim, icq, localDumpFile, bufferSecs ); audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, channel, lowpass, highpass ); encConnector->attach( audioOuts[u].encoder.get()); #endif // HAVE_LAME_LIB } noAudioOuts += u; }
/*------------------------------------------------------------------------------ * Look for the IceCast2 stream outputs in the config file *----------------------------------------------------------------------------*/ void DarkIce :: configIceCast2 ( const Config & config, unsigned int bufferSecs ) throw ( Exception ) { // look for IceCast2 encoder output streams, // sections [icecast2-0], [icecast2-1], ... char stream[] = "icecast2- "; size_t streamLen = Util::strLen( stream); unsigned int u; for ( u = noAudioOuts; u < maxOutput; ++u ) { const ConfigSection * cs; // ugly hack to change the section name to "stream0", "stream1", etc. stream[streamLen-1] = '0' + (u - noAudioOuts); if ( !(cs = config.get( stream)) ) { break; } const char * str; IceCast2::StreamFormat format; unsigned int sampleRate = 0; unsigned int channel = 0; AudioEncoder::BitrateMode bitrateMode; unsigned int bitrate = 0; unsigned int maxBitrate = 0; double quality = 0.0; const char * server = 0; unsigned int port = 0; const char * password = 0; const char * mountPoint = 0; const char * name = 0; const char * description = 0; const char * url = 0; const char * genre = 0; bool isPublic = false; int lowpass = 0; int highpass = 0; const char * localDumpName = 0; FileSink * localDumpFile = 0; bool fileAddDate = false; const char * fileDateFormat = 0; str = cs->getForSure( "format", " missing in section ", stream); if ( Util::strEq( str, "vorbis") ) { format = IceCast2::oggVorbis; } else if ( Util::strEq( str, "mp3") ) { format = IceCast2::mp3; } else if ( Util::strEq( str, "mp2") ) { format = IceCast2::mp2; } else if ( Util::strEq( str, "aac") ) { format = IceCast2::aac; } else if ( Util::strEq( str, "aacp") ) { format = IceCast2::aacp; } else { throw Exception( __FILE__, __LINE__, "unsupported stream format: ", str); } str = cs->get( "sampleRate"); sampleRate = str ? Util::strToL( str) : dsp->getSampleRate(); str = cs->get( "channel"); channel = str ? Util::strToL( str) : dsp->getChannel(); // determine fixed bitrate or variable bitrate quality str = cs->get( "bitrate"); bitrate = str ? Util::strToL( str) : 0; str = cs->get( "maxBitrate"); maxBitrate = str ? Util::strToL( str) : 0; str = cs->get( "quality"); quality = str ? Util::strToD( str) : 0.0; str = cs->getForSure( "bitrateMode", " not specified in section ", stream); if ( Util::strEq( str, "cbr") ) { bitrateMode = AudioEncoder::cbr; if ( bitrate == 0 ) { throw Exception( __FILE__, __LINE__, "bitrate not specified for CBR encoding"); } } else if ( Util::strEq( str, "abr") ) { bitrateMode = AudioEncoder::abr; if ( bitrate == 0 ) { throw Exception( __FILE__, __LINE__, "bitrate not specified for ABR encoding"); } } else if ( Util::strEq( str, "vbr") ) { bitrateMode = AudioEncoder::vbr; if ( cs->get( "quality" ) == 0 ) { throw Exception( __FILE__, __LINE__, "quality not specified for VBR encoding"); } } else { throw Exception( __FILE__, __LINE__, "invalid bitrate mode: ", str); } server = cs->getForSure( "server", " missing in section ", stream); str = cs->getForSure( "port", " missing in section ", stream); port = Util::strToL( str); password = cs->getForSure("password"," missing in section ",stream); mountPoint = cs->getForSure( "mountPoint", " missing in section ", stream); name = cs->get( "name"); description = cs->get( "description"); url = cs->get( "url"); genre = cs->get( "genre"); str = cs->get( "public"); isPublic = str ? (Util::strEq( str, "yes") ? true : false) : false; str = cs->get( "lowpass"); lowpass = str ? Util::strToL( str) : 0; str = cs->get( "highpass"); highpass = str ? Util::strToL( str) : 0; str = cs->get( "fileAddDate"); fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false; fileDateFormat = cs->get( "fileDateFormat"); localDumpName = cs->get( "localDumpFile"); // go on and create the things // check for and create the local dump file if needed if ( localDumpName != 0 ) { if ( fileAddDate ) { if (fileDateFormat == 0) { localDumpName = Util::fileAddDate(localDumpName); } else { localDumpName = Util::fileAddDate( localDumpName, fileDateFormat ); } } localDumpFile = new FileSink( stream, localDumpName); if ( !localDumpFile->exists() ) { if ( !localDumpFile->create() ) { reportEvent( 1, "can't create local dump file", localDumpName); localDumpFile = 0; } } if ( fileAddDate ) { delete[] localDumpName; } } // streaming related stuff audioOuts[u].socket = new TcpSocket( server, port); audioOuts[u].server = new IceCast2( audioOuts[u].socket.get(), password, mountPoint, format, bitrate, name, description, url, genre, isPublic, localDumpFile, bufferSecs ); switch ( format ) { case IceCast2::mp3: #ifndef HAVE_LAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with lame support, " "thus can't create mp3 stream: ", stream); #else audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, channel, lowpass, highpass ); #endif // HAVE_LAME_LIB break; case IceCast2::oggVorbis: #ifndef HAVE_VORBIS_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with Ogg Vorbis support, " "thus can't Ogg Vorbis stream: ", stream); #else audioOuts[u].encoder = new VorbisLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, dsp->getChannel(), maxBitrate); #endif // HAVE_VORBIS_LIB break; case IceCast2::mp2: #ifndef HAVE_TWOLAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with TwoLame support, " "thus can't create mp2 stream: ", stream); #else audioOuts[u].encoder = new TwoLameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, sampleRate, channel ); #endif // HAVE_TWOLAME_LIB break; case IceCast2::aac: #ifndef HAVE_FAAC_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with AAC support, " "thus can't aac stream: ", stream); #else audioOuts[u].encoder = new FaacEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, dsp->getChannel()); #endif // HAVE_FAAC_LIB break; case IceCast2::aacp: #ifndef HAVE_AACPLUS_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with AAC+ support, " "thus can't aacp stream: ", stream); #else audioOuts[u].encoder = new aacPlusEncoder( audioOuts[u].server.get(), dsp.get(), bitrateMode, bitrate, quality, sampleRate, channel ); #endif // HAVE_AACPLUS_LIB break; default: throw Exception( __FILE__, __LINE__, "Illegal stream format: ", format); } encConnector->attach( audioOuts[u].encoder.get()); } noAudioOuts += u; }
/*------------------------------------------------------------------------------ * Look for the ShoutCast stream outputs in the config file *----------------------------------------------------------------------------*/ void DarkIce :: configShoutCast ( const Config & config, unsigned int bufferSecs ) throw ( Exception ) { // look for IceCast encoder output streams, // sections [shoutcast-0], [shoutcast-1], ... char stream[] = "shoutcast- "; size_t streamLen = Util::strLen( stream); unsigned int u; for ( u = 0; u < maxOutput; ++u ) { const ConfigSection * cs; // ugly hack to change the section name to "stream0", "stream1", etc. stream[streamLen-1] = '0' + u; if ( !(cs = config.get( stream)) ) { break; } #ifndef HAVE_LAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with lame support, " "thus can't connect to ShoutCast, stream: ", stream); #else const char * str; unsigned int sampleRate = 0; unsigned int bitrate = 0; const char * server = 0; unsigned int port = 0; const char * password = 0; const char * name = 0; const char * url = 0; const char * genre = 0; bool isPublic = false; int lowpass = 0; int highpass = 0; const char * irc = 0; const char * aim = 0; const char * icq = 0; const char * localDumpName = 0; FileSink * localDumpFile = 0; str = cs->get( "sampleRate"); sampleRate = str ? Util::strToL( str) : dsp->getSampleRate(); str = cs->getForSure("bitrate", " missing in section ", stream); bitrate = Util::strToL( str); server = cs->getForSure( "server", " missing in section ", stream); str = cs->getForSure( "port", " missing in section ", stream); port = Util::strToL( str); password = cs->getForSure("password"," missing in section ",stream); name = cs->get( "name"); url = cs->get( "url"); genre = cs->get( "genre"); str = cs->get( "public"); isPublic = str ? (Util::strEq( str, "yes") ? true : false) : false; str = cs->get( "lowpass"); lowpass = str ? Util::strToL( str) : 0; str = cs->get( "highpass"); highpass = str ? Util::strToL( str) : 0; irc = cs->get( "irc"); aim = cs->get( "aim"); icq = cs->get( "icq"); localDumpName = cs->get( "localDumpFile"); // go on and create the things // check for and create the local dump file if needed if ( localDumpName != 0 ) { localDumpFile = new FileSink( localDumpName); if ( !localDumpFile->exists() ) { if ( !localDumpFile->create() ) { reportEvent( 1, "can't create local dump file", localDumpName); localDumpFile = 0; } } } // encoder related stuff unsigned int bs = bufferSecs * (dsp->getBitsPerSample() / 8) * dsp->getChannel() * dsp->getSampleRate(); reportEvent( 6, "using buffer size", bs); // streaming related stuff audioOuts[u].socket = new TcpSocket( server, port); audioOuts[u].server = new ShoutCast( audioOuts[u].socket.get(), password, bitrate, name, url, genre, isPublic, irc, aim, icq, localDumpFile ); audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrate, sampleRate, dsp->getChannel(), lowpass, highpass ); encConnector->attach( audioOuts[u].encoder.get()); #endif // HAVE_LAME_LIB } noAudioOuts += u; }
/*------------------------------------------------------------------------------ * Look for the IceCast2 stream outputs in the config file *----------------------------------------------------------------------------*/ void DarkIce :: configIceCast2 ( const Config & config, unsigned int bufferSecs ) throw ( Exception ) { // look for IceCast2 encoder output streams, // sections [icecast2-0], [icecast2-1], ... char stream[] = "icecast2- "; size_t streamLen = Util::strLen( stream); unsigned int u; for ( u = 0; u < maxOutput; ++u ) { const ConfigSection * cs; // ugly hack to change the section name to "stream0", "stream1", etc. stream[streamLen-1] = '0' + u; if ( !(cs = config.get( stream)) ) { break; } const char * str; IceCast2::StreamFormat format; unsigned int bitrate = 0; const char * server = 0; unsigned int port = 0; const char * password = 0; const char * mountPoint = 0; const char * name = 0; const char * description = 0; const char * url = 0; const char * genre = 0; bool isPublic = false; const char * localDumpName = 0; FileSink * localDumpFile = 0; str = cs->getForSure( "format", " missing in section ", stream); if ( Util::strEq( str, "vorbis") ) { format = IceCast2::oggVorbis; } else if ( Util::strEq( str, "mp3") ) { format = IceCast2::mp3; // TODO: enable this format in the future, when icecast2 // supports it as well throw Exception( __FILE__, __LINE__, "unsupported stream format: ", str); } else { throw Exception( __FILE__, __LINE__, "unsupported stream format: ", str); } str = cs->getForSure("bitrate", " missing in section ", stream); bitrate = Util::strToL( str); server = cs->getForSure( "server", " missing in section ", stream); str = cs->getForSure( "port", " missing in section ", stream); port = Util::strToL( str); password = cs->getForSure("password"," missing in section ",stream); mountPoint = cs->getForSure( "mountPoint", " missing in section ", stream); name = cs->get( "name"); description = cs->get("description"); url = cs->get( "url"); genre = cs->get( "genre"); str = cs->get( "public"); isPublic = str ? (Util::strEq( str, "yes") ? true : false) : false; localDumpName = cs->get( "localDumpFile"); // go on and create the things // check for and create the local dump file if needed if ( localDumpName != 0 ) { localDumpFile = new FileSink( localDumpName); if ( !localDumpFile->exists() ) { if ( !localDumpFile->create() ) { reportEvent( 1, "can't create local dump file", localDumpName); localDumpFile = 0; } } } // encoder related stuff unsigned int bs = bufferSecs * (dsp->getBitsPerSample() / 8) * dsp->getChannel() * dsp->getSampleRate(); reportEvent( 6, "using buffer size", bs); // streaming related stuff audioOuts[u].socket = new TcpSocket( server, port); audioOuts[u].server = new IceCast2( audioOuts[u].socket.get(), password, mountPoint, format, bitrate, name, description, url, genre, isPublic, localDumpFile ); switch ( format ) { case IceCast2::mp3: #ifndef HAVE_LAME_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with lame support, " "thus can't create mp3 stream: ", stream); #else audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrate, dsp->getSampleRate(), dsp->getChannel() ); #endif // HAVE_LAME_LIB break; case IceCast2::oggVorbis: #ifndef HAVE_VORBIS_LIB throw Exception( __FILE__, __LINE__, "DarkIce not compiled with Ogg Vorbis support, " "thus can't Ogg Vorbis stream: ", stream); #else audioOuts[u].encoder = new VorbisLibEncoder( audioOuts[u].server.get(), dsp.get(), bitrate, dsp->getSampleRate(), dsp->getChannel() ); #endif // HAVE_VORBIS_LIB break; default: throw Exception( __FILE__, __LINE__, "Illegal stream format: ", format); } encConnector->attach( audioOuts[u].encoder.get()); } noAudioOuts += u; }
void createOutputFiles(char const* periodicFilenameSuffix) { char outFileName[1000]; if (outputQuickTimeFile || outputAVIFile) { if (periodicFilenameSuffix[0] == '\0') { // Normally (unless the '-P <interval-in-seconds>' option was given) we output to 'stdout': sprintf(outFileName, "stdout"); } else { // Otherwise output to a type-specific file name, containing "periodicFilenameSuffix": char const* prefix = fileNamePrefix[0] == '\0' ? "output" : fileNamePrefix; snprintf(outFileName, sizeof outFileName, "%s%s.%s", prefix, periodicFilenameSuffix, outputAVIFile ? "avi" : generateMP4Format ? "mp4" : "mov"); } if (outputQuickTimeFile) { qtOut = QuickTimeFileSink::createNew(*env, *session, outFileName, fileSinkBufferSize, movieWidth, movieHeight, movieFPS, packetLossCompensate, syncStreams, generateHintTracks, generateMP4Format); if (qtOut == NULL) { *env << "Failed to create a \"QuickTimeFileSink\" for outputting to \"" << outFileName << "\": " << env->getResultMsg() << "\n"; shutdown(); } else { *env << "Outputting to the file: \"" << outFileName << "\"\n"; } qtOut->startPlaying(sessionAfterPlaying, NULL); } else { // outputAVIFile aviOut = AVIFileSink::createNew(*env, *session, outFileName, fileSinkBufferSize, movieWidth, movieHeight, movieFPS, packetLossCompensate); if (aviOut == NULL) { *env << "Failed to create an \"AVIFileSink\" for outputting to \"" << outFileName << "\": " << env->getResultMsg() << "\n"; shutdown(); } else { *env << "Outputting to the file: \"" << outFileName << "\"\n"; } aviOut->startPlaying(sessionAfterPlaying, NULL); } } else { // Create and start "FileSink"s for each subsession: madeProgress = False; MediaSubsessionIterator iter(*session); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // was not initiated // Create an output file for each desired stream: if (singleMedium == NULL || periodicFilenameSuffix[0] != '\0') { // Output file name is // "<filename-prefix><medium_name>-<codec_name>-<counter><periodicFilenameSuffix>" static unsigned streamCounter = 0; snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d%s", fileNamePrefix, subsession->mediumName(), subsession->codecName(), ++streamCounter, periodicFilenameSuffix); } else { // When outputting a single medium only, we output to 'stdout // (unless the '-P <interval-in-seconds>' option was given): 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 adds 'start codes', // and (at the start) the SPS and PPS NAL units: fileSink = H264VideoFileSink::createNew(*env, outFileName, subsession->fmtp_spropparametersets(), fileSinkBufferSize, oneFilePerFrame); } else if (strcmp(subsession->mediumName(), "video") == 0 && (strcmp(subsession->codecName(), "H265") == 0)) { // For H.265 video stream, we use a special sink that adds 'start codes', // and (at the start) the VPS, SPS, and PPS NAL units: fileSink = H265VideoFileSink::createNew(*env, outFileName, subsession->fmtp_spropvps(), subsession->fmtp_spropsps(), subsession->fmtp_sproppps(), 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 \"" << outFileName << "\"\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(); } }
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 }
int main(int argc, char *argv[]) { Opt opt; opt.registerOpt(P_DeviceName, "-d", false, true, "specify DV4Mini device name", "/dev/ttyACM0"); opt.registerOpt(P_Frequency, "-f", false, true, "set frequency in Hz", "430312500"); opt.registerOpt(P_WriteRxToFile, "-w", false, true, "write received data to file"); opt.registerOpt(P_TxFromFile, "-tx", false, true, "transmit data from file"); opt.registerOpt(P_SimMode, "-s", false, false, "simulate without real hardware"); opt.registerOpt(P_LogLevel, "-ll", false, true, "log level"); opt.registerOpt(P_DebugMode, "-debug", false, false, "set debug mode"); if(!opt.parse(argc, argv)) { opt.printHelp(); return 1; } bool bDebug = opt.get(P_DebugMode); bool bSimMode = opt.get(P_SimMode); if(bDebug) opt.dump(); std::string sDeviceName = opt.getString(P_DeviceName); int iFrequency_Hz = opt.getInt(P_Frequency); DV4Mini mini; if(bSimMode) { sDeviceName = "/dev/null"; mini.setSimulationMode(true); } mini.setLogFile(stdout, opt.getInt(P_LogLevel)); if(!mini.open(sDeviceName.c_str())) { perror(sDeviceName.c_str()); return 1; } mini.setFrequency(iFrequency_Hz); printf("Using device \"%s\" at %d Hz (%.3f kHz).\n", sDeviceName.c_str(), iFrequency_Hz, iFrequency_Hz / 1000.); mini.setLED(true); FileSink fsink; if(opt.get(P_WriteRxToFile)) { const char *pszWriteFileName = opt.getString(P_WriteRxToFile).c_str(); if(!fsink.open(pszWriteFileName)) { perror(pszWriteFileName); return 1; } mini.setRxSink(&fsink); printf("Writing received data to file \"%s\".\n", pszWriteFileName); } if(opt.get(P_TxFromFile)) { const char *pszReadFileName = opt.getString(P_TxFromFile).c_str(); FILE *pFile = fopen(pszReadFileName, "r"); if(!pFile) { perror(pszReadFileName); return 1; } printf("Sending file \"%s\"", pszReadFileName); BYTE buf[100]; while(true) { putchar('.'); int iBytes = fread(buf, 1, sizeof buf, pFile); if(iBytes <= 0) break; mini.transmit(buf, iBytes); usleep(10); } puts("done!\n"); mini.flush(); fclose(pFile); mini.close(); return 0; } #if 1 for(int i = 0; i < 5; ++i) { sleep(1); printf("RSSI=%d\n", mini.getRSSI()); } #else for(int i = 0; i < 50; ++i) { usleep(100); printf("RSSI=%d\n", mini.getRSSI()); } #endif mini.close(); return 0; }