예제 #1
0
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_;
}
예제 #2
0
void FileSink::afterGettingFrame(void* clientData, unsigned frameSize,
				 unsigned numTruncatedBytes,
				 struct timeval presentationTime,
				 unsigned /*durationInMicroseconds*/) {
  FileSink* sink = (FileSink*)clientData;
  sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
}
예제 #3
0
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);
}
예제 #4
0
/*------------------------------------------------------------------------------
 *  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;
}
예제 #5
0
/*------------------------------------------------------------------------------
 *  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;
}
예제 #6
0
/*------------------------------------------------------------------------------
 *  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;
}
예제 #7
0
/*------------------------------------------------------------------------------
 *  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;
}
예제 #8
0
/*------------------------------------------------------------------------------
 *  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;
}
예제 #9
0
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();
  }
}
예제 #10
0
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
}
예제 #11
0
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;
}