add RtpCast skeleton
This commit is contained in:
parent
d3eb9525dc
commit
9bca6850a7
|
@ -79,4 +79,14 @@ aim = aim here # AIM info related to the stream
|
||||||
icq = I see you too
|
icq = I see you too
|
||||||
# ICQ info related to the stream
|
# ICQ info related to the stream
|
||||||
|
|
||||||
|
# this section describes a streaming udp to an specific IP
|
||||||
|
# there may be up to 8 of these sections, named [rtp-0] ... [rtp-7]
|
||||||
|
# these can be mixed with [icecast-x] and [shoutcast-x] sections
|
||||||
|
[rtp-0]
|
||||||
|
bitrateMode = cbr # average bit rate
|
||||||
|
format = aac # format of the stream: ogg vorbis
|
||||||
|
bitrate = 96 # bitrate of the stream sent to the server
|
||||||
|
dstip = 239.2.2.2 # destination address
|
||||||
|
port = 5004 # destination port, usually 5004
|
||||||
|
isRaw = no # force raw UDP stream instead RTP?
|
||||||
|
localDumpFile = dump.aac # local dump file
|
||||||
|
|
|
@ -190,6 +190,7 @@ DarkIce :: init ( const Config & config ) throw ( Exception )
|
||||||
configIceCast( config, bufferSecs);
|
configIceCast( config, bufferSecs);
|
||||||
configIceCast2( config, bufferSecs);
|
configIceCast2( config, bufferSecs);
|
||||||
configShoutCast( config, bufferSecs);
|
configShoutCast( config, bufferSecs);
|
||||||
|
configRtpCast( config, bufferSecs);
|
||||||
configFileCast( config);
|
configFileCast( config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,6 +680,268 @@ DarkIce :: configIceCast2 ( const Config & config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Look for the RtpCast stream outputs in the config file
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
DarkIce :: configRtpCast ( const Config & config,
|
||||||
|
unsigned int bufferSecs )
|
||||||
|
throw ( Exception )
|
||||||
|
{
|
||||||
|
// look for RtpCast encoder output streams,
|
||||||
|
// sections [rtpcast-0], [rtpcast-1], ...
|
||||||
|
char stream[] = "rtp- ";
|
||||||
|
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 * dstip = 0;
|
||||||
|
unsigned int port = 0;
|
||||||
|
bool isRaw = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
dstip = cs->getForSure( "dstip", " missing in section ", stream);
|
||||||
|
str = cs->getForSure( "port", " missing in section ", stream);
|
||||||
|
str = cs->get( "isRaw");
|
||||||
|
isRaw = 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 NetSocket( server, port);
|
||||||
|
audioOuts[u].server = new RtpCast( audioOuts[u].socket.get(),
|
||||||
|
format,
|
||||||
|
bitrate,
|
||||||
|
sampleRate,
|
||||||
|
channel,
|
||||||
|
isRaw,
|
||||||
|
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
|
* Look for the ShoutCast stream outputs in the config file
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -42,6 +42,8 @@ darkice_SOURCES = AudioEncoder.h\
|
||||||
ShoutCast.h\
|
ShoutCast.h\
|
||||||
FileCast.h\
|
FileCast.h\
|
||||||
FileCast.cpp\
|
FileCast.cpp\
|
||||||
|
RtpCast.cpp\
|
||||||
|
RtpCast.h\
|
||||||
LameLibEncoder.cpp\
|
LameLibEncoder.cpp\
|
||||||
LameLibEncoder.h\
|
LameLibEncoder.h\
|
||||||
TwoLameLibEncoder.cpp\
|
TwoLameLibEncoder.cpp\
|
||||||
|
|
|
@ -114,10 +114,12 @@ static const char fileid[] = "$Id$";
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
NetSocket :: init ( const char * host,
|
NetSocket :: init ( const char * host,
|
||||||
unsigned short port ) throw ( Exception )
|
unsigned short port,
|
||||||
|
bool isUdp ) throw ( Exception )
|
||||||
{
|
{
|
||||||
this->host = Util::strDup( host);
|
this->host = Util::strDup( host);
|
||||||
this->port = port;
|
this->port = port;
|
||||||
|
this->isUdp = isUdp;
|
||||||
this->sockfd = 0;
|
this->sockfd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +146,7 @@ NetSocket :: NetSocket ( const NetSocket & ss ) throw ( Exception )
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
init( ss.host, ss.port);
|
init( ss.host, ss.port, ss.isUdp );
|
||||||
|
|
||||||
if ( (fd = ss.sockfd ? dup( ss.sockfd) : 0) == -1 ) {
|
if ( (fd = ss.sockfd ? dup( ss.sockfd) : 0) == -1 ) {
|
||||||
strip();
|
strip();
|
||||||
|
@ -172,7 +174,7 @@ NetSocket :: operator= ( const NetSocket & ss ) throw ( Exception )
|
||||||
Sink::operator=( ss );
|
Sink::operator=( ss );
|
||||||
Source::operator=( ss );
|
Source::operator=( ss );
|
||||||
|
|
||||||
init( ss.host, ss.port);
|
init( ss.host, ss.port, ss.isUdp );
|
||||||
|
|
||||||
if ( (fd = ss.sockfd ? dup( ss.sockfd) : 0) == -1 ) {
|
if ( (fd = ss.sockfd ? dup( ss.sockfd) : 0) == -1 ) {
|
||||||
strip();
|
strip();
|
||||||
|
@ -197,11 +199,12 @@ NetSocket :: open ( void ) throw ( Exception )
|
||||||
#ifdef HAVE_ADDRINFO
|
#ifdef HAVE_ADDRINFO
|
||||||
struct addrinfo hints
|
struct addrinfo hints
|
||||||
struct addrinfo * ptr;
|
struct addrinfo * ptr;
|
||||||
struct sockaddr_storage addr;
|
struct sockaddr_storage addr, laddr;
|
||||||
char portstr[6];
|
char portstr[6];
|
||||||
#else
|
#else
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr, laddr;
|
||||||
struct hostent * pHostEntry;
|
struct hostent * pHostEntry;
|
||||||
|
struct ip_mreq mreq;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( isOpen() ) {
|
if ( isOpen() ) {
|
||||||
|
@ -210,11 +213,11 @@ NetSocket :: open ( void ) throw ( Exception )
|
||||||
|
|
||||||
#ifdef HAVE_ADDRINFO
|
#ifdef HAVE_ADDRINFO
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = isUdp ? SOCK_DGRAM : SOCK_STREAM;
|
||||||
hints.ai_family = AF_ANY;
|
hints.ai_family = AF_ANY;
|
||||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||||
|
|
||||||
if (getaddrinfo(host , portstr, &hints, &ptr)) {
|
if (getaddrinfo(isUdp ? "0.0.0.0" : host , portstr, &hints, &ptr)) {
|
||||||
sockfd = 0;
|
sockfd = 0;
|
||||||
throw Exception( __FILE__, __LINE__, "getaddrinfo error", errno);
|
throw Exception( __FILE__, __LINE__, "getaddrinfo error", errno);
|
||||||
}
|
}
|
||||||
|
@ -229,19 +232,63 @@ NetSocket :: open ( void ) throw ( Exception )
|
||||||
memset( &addr, 0, sizeof(addr));
|
memset( &addr, 0, sizeof(addr));
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
addr.sin_addr.s_addr = *((long*) pHostEntry->h_addr_list[0]);
|
addr.sin_addr.s_addr = isUdp ? INADDR_ANY : *((long*) pHostEntry->h_addr_list[0]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( (sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1 ) {
|
if ( (sockfd = socket( AF_INET, isUdp ? SOCK_DGRAM : SOCK_STREAM, isUdp ? IPPROTO_UDP : IPPROTO_TCP)) == -1 ) {
|
||||||
sockfd = 0;
|
sockfd = 0;
|
||||||
throw Exception( __FILE__, __LINE__, "socket error", errno);
|
throw Exception( __FILE__, __LINE__, "socket error", errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set TCP keep-alive
|
// set TCP keep-alive / UDP reuse address
|
||||||
optval = 1;
|
optval = 1;
|
||||||
optlen = sizeof(optval);
|
optlen = sizeof(optval);
|
||||||
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) == -1) {
|
if (isUdp) {
|
||||||
reportEvent(5, "can't set TCP socket keep-alive mode", errno);
|
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen) == -1) {
|
||||||
|
reportEvent(5, "can't set UDP reuse address", errno);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) == -1) {
|
||||||
|
reportEvent(5, "can't set TCP socket keep-alive mode", errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUdp) {
|
||||||
|
#ifdef HAVE_ADDRINFO
|
||||||
|
//todo: implement when ADDRINFO used
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_family = AF_ANY;
|
||||||
|
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||||
|
|
||||||
|
if (getaddrinfo(host , portstr, &hints, &ptr)) {
|
||||||
|
sockfd = 0;
|
||||||
|
throw Exception( __FILE__, __LINE__, "getaddrinfo error", errno);
|
||||||
|
}
|
||||||
|
memcpy ( laddr, ptr->ai_addr, ptr->ai_addrlen);
|
||||||
|
freeaddrinfo(ptr);
|
||||||
|
#else
|
||||||
|
memset(&mreq, 0, sizeof(struct ip_mreq));
|
||||||
|
mreq.imr_multiaddr = *((long*) pHostEntry->h_addr_list[0]);
|
||||||
|
|
||||||
|
if (IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
|
||||||
|
mreq.imr_interface.s_addr=INADDR_ANY;
|
||||||
|
if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
|
||||||
|
::close( sockfd);
|
||||||
|
sockfd = 0;
|
||||||
|
throw Exception( __FILE__, __LINE__, "can't join multicast address", errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memset( &laddr, 0, sizeof(addr));
|
||||||
|
laddr.sin_family = AF_INET;
|
||||||
|
laddr.sin_port = htons(port);
|
||||||
|
laddr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
#endif
|
||||||
|
if (bind(sockfd, (struct sockaddr*)&laddr, sizeof(laddr))) {
|
||||||
|
::close( sockfd);
|
||||||
|
sockfd = 0;
|
||||||
|
throw Exception( __FILE__, __LINE__, "can't bind UDP socket", errno);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect
|
// connect
|
||||||
|
@ -267,7 +314,7 @@ NetSocket :: canRead ( unsigned int sec,
|
||||||
sigset_t sigset;
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() || isUdp ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +349,7 @@ NetSocket :: read ( void * buf,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() || isUdp ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,6 +389,8 @@ NetSocket :: canWrite ( unsigned int sec,
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if ( isUdp ) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
|
@ -382,7 +431,7 @@ NetSocket :: write ( const void * buf,
|
||||||
#ifdef HAVE_MSG_NOSIGNAL
|
#ifdef HAVE_MSG_NOSIGNAL
|
||||||
ret = send( sockfd, buf, len, MSG_NOSIGNAL);
|
ret = send( sockfd, buf, len, MSG_NOSIGNAL);
|
||||||
#else
|
#else
|
||||||
ret = send( sockfd, buf, len, 0);
|
ret = send( sockfd, buf, len, isUdp ? MSG_DONTWAIT : 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
|
|
|
@ -74,6 +74,11 @@ class NetSocket : public Source, public Sink, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
int sockfd;
|
int sockfd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of socket.
|
||||||
|
*/
|
||||||
|
bool isUdp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the object.
|
* Initialize the object.
|
||||||
*
|
*
|
||||||
|
@ -83,7 +88,8 @@ class NetSocket : public Source, public Sink, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init ( const char * host,
|
init ( const char * host,
|
||||||
unsigned short port ) throw ( Exception );
|
unsigned short port,
|
||||||
|
bool isUdp ) throw ( Exception );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* De-initialize the object.
|
* De-initialize the object.
|
||||||
|
@ -119,9 +125,10 @@ class NetSocket : public Source, public Sink, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
NetSocket( const char * host,
|
NetSocket( const char * host,
|
||||||
unsigned short port ) throw ( Exception )
|
unsigned short port,
|
||||||
|
bool isUdp ) throw ( Exception )
|
||||||
{
|
{
|
||||||
init( host, port);
|
init( host, port, isUdp );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2000-2007 Tyrell Corporation. All rights reserved.
|
||||||
|
|
||||||
|
Tyrell DarkIce
|
||||||
|
|
||||||
|
File : RtpCast.cpp
|
||||||
|
Version : $Revision: 462 $
|
||||||
|
Author : $Author: rafael@riseup.net $
|
||||||
|
Location : $HeadURL$
|
||||||
|
|
||||||
|
Copyright notice:
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* ============================================================ include files */
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#else
|
||||||
|
#error need stdio.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#else
|
||||||
|
#error need string.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_MATH_H
|
||||||
|
#include <math.h>
|
||||||
|
#else
|
||||||
|
#error need math.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "Exception.h"
|
||||||
|
#include "Source.h"
|
||||||
|
#include "Sink.h"
|
||||||
|
#include "Util.h"
|
||||||
|
#include "RtpCast.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* =================================================== local data structures */
|
||||||
|
|
||||||
|
|
||||||
|
/* ================================================ local constants & macros */
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* File identity
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
static const char fileid[] = "$Id$";
|
||||||
|
|
||||||
|
/* =============================================== local function prototypes */
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================================= module code */
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Initialize the object
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
RtpCast :: init ( StreamFormat format,
|
||||||
|
const char * mountPoint,
|
||||||
|
const char * description )
|
||||||
|
throw ( Exception )
|
||||||
|
{
|
||||||
|
this->format = format;
|
||||||
|
this->mountPoint = Util::strDup( mountPoint);
|
||||||
|
this->description = description ? Util::strDup( description) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* De-initialize the object
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
RtpCast :: strip ( void ) throw ( Exception )
|
||||||
|
{
|
||||||
|
delete[] mountPoint;
|
||||||
|
if ( description ) {
|
||||||
|
delete[] description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,415 @@
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2000-2007 Tyrell Corporation. All rights reserved.
|
||||||
|
|
||||||
|
Tyrell DarkIce
|
||||||
|
|
||||||
|
File : RtpCast.h
|
||||||
|
Version : $Revision: 466 $
|
||||||
|
Author : $Author: piratfm $
|
||||||
|
Location : $HeadURL$
|
||||||
|
|
||||||
|
Copyright notice:
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
#ifndef ICE_CAST2_H
|
||||||
|
#define ICE_CAST2_H
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#error This is a C++ include file
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================================ include files */
|
||||||
|
|
||||||
|
#include "Ref.h"
|
||||||
|
#include "Reporter.h"
|
||||||
|
#include "Sink.h"
|
||||||
|
#include "NetSocket.h"
|
||||||
|
#include "BufferedSink.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* ================================================================ constants */
|
||||||
|
|
||||||
|
|
||||||
|
/* =================================================================== macros */
|
||||||
|
|
||||||
|
|
||||||
|
/* =============================================================== data types */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing output to an RtpCast server with
|
||||||
|
* ice login
|
||||||
|
*
|
||||||
|
* @author $Author: piratfm $
|
||||||
|
* @version $Revision: 466 $
|
||||||
|
*/
|
||||||
|
class RtpCast : public Sink, public virtual Reporter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for specifying the format of the stream.
|
||||||
|
*/
|
||||||
|
enum StreamFormat { mp3, mp2, oggVorbis, aac, aacp };
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format of the stream.
|
||||||
|
*/
|
||||||
|
StreamFormat format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The socket connection to the server.
|
||||||
|
*/
|
||||||
|
Ref<NetSocket> socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BufferedSink encapsulating the socket connection to the server.
|
||||||
|
*/
|
||||||
|
Ref<BufferedSink> bufferedSink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional Sink to enable stream dumps.
|
||||||
|
*/
|
||||||
|
Ref<Sink> streamDump;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the BufferedSink buffer in seconds.
|
||||||
|
*/
|
||||||
|
unsigned int bufferDuration;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initalize the object.
|
||||||
|
*
|
||||||
|
* @param mountPoint mount point of the stream on the server.
|
||||||
|
* @param remoteDumpFile remote dump file (may be NULL).
|
||||||
|
* @param description description of the stream.
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
init ( StreamFormat format,
|
||||||
|
const char * mountPoint,
|
||||||
|
const char * description )
|
||||||
|
throw ( Exception );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-initalize the object.
|
||||||
|
*
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
strip ( void ) throw ( Exception );
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor. Always throws an Exception.
|
||||||
|
*
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
RtpCast ( void ) throw ( Exception )
|
||||||
|
{
|
||||||
|
throw Exception( __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Sink underneath this CastSink.
|
||||||
|
*
|
||||||
|
* @return pointer to the Sink underneath this CastSink.
|
||||||
|
*/
|
||||||
|
inline Sink *
|
||||||
|
getSink ( void ) const throw ()
|
||||||
|
{
|
||||||
|
return bufferedSink.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the NetSocket underneath this CastSink.
|
||||||
|
*
|
||||||
|
* @return pointer to the NetSocket underneath this CastSink.
|
||||||
|
*/
|
||||||
|
inline NetSocket *
|
||||||
|
getSocket ( void ) const throw ()
|
||||||
|
{
|
||||||
|
return socket.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param socket socket connection to the server.
|
||||||
|
* @param password password to the server.
|
||||||
|
* @param mountPoint mount point of the stream on the server.
|
||||||
|
* @param format the format of the stream.
|
||||||
|
* @param name name of the stream.
|
||||||
|
* @param description description of the stream.
|
||||||
|
* @param url URL associated with the stream.
|
||||||
|
* @param genre genre of the stream.
|
||||||
|
* @param bitRate bitrate of the stream (e.g. mp3 bitrate).
|
||||||
|
* @param isPublic is the stream public?
|
||||||
|
* @param streamDump an optional sink to dump the binary stream
|
||||||
|
* data to.
|
||||||
|
* @param bufferDuration duration of the BufferedSink buffer
|
||||||
|
* in seconds.
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
RtpCast ( NetSocket * socket,
|
||||||
|
const char * password,
|
||||||
|
const char * mountPoint,
|
||||||
|
StreamFormat format,
|
||||||
|
unsigned int bitRate,
|
||||||
|
const char * name = 0,
|
||||||
|
const char * description = 0,
|
||||||
|
const char * url = 0,
|
||||||
|
const char * genre = 0,
|
||||||
|
bool isRtp = false,
|
||||||
|
Sink * streamDump = 0,
|
||||||
|
unsigned int bufferDuration = 10 )
|
||||||
|
throw ( Exception )
|
||||||
|
: CastSink( socket,
|
||||||
|
password,
|
||||||
|
bitRate,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
genre,
|
||||||
|
isPublic,
|
||||||
|
streamDump,
|
||||||
|
bufferDuration )
|
||||||
|
{
|
||||||
|
init( format, mountPoint, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor.
|
||||||
|
*
|
||||||
|
* @param cs the RtpCast to copy.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
RtpCast( const RtpCast & cs ) throw ( Exception )
|
||||||
|
: CastSink( cs )
|
||||||
|
{
|
||||||
|
init( cs.getFormat(),
|
||||||
|
cs.getMountPoint(),
|
||||||
|
cs.getDescription() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline virtual
|
||||||
|
~RtpCast( void ) throw ( Exception )
|
||||||
|
{
|
||||||
|
strip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assignment operator.
|
||||||
|
*
|
||||||
|
* @param cs the RtpCast to assign this to.
|
||||||
|
* @return a reference to this RtpCast.
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline virtual RtpCast &
|
||||||
|
operator= ( const RtpCast & cs ) throw ( Exception )
|
||||||
|
{
|
||||||
|
if ( this != &cs ) {
|
||||||
|
strip();
|
||||||
|
CastSink::operator=( cs );
|
||||||
|
init( cs.getFormat(),
|
||||||
|
cs.getMountPoint(),
|
||||||
|
cs.getDescription() );
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the format of the stream.
|
||||||
|
*
|
||||||
|
* @return the format of the stream.
|
||||||
|
*/
|
||||||
|
inline StreamFormat
|
||||||
|
getFormat ( void ) const throw ()
|
||||||
|
{
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the CastSink.
|
||||||
|
* Logs in to the server.
|
||||||
|
*
|
||||||
|
* @return true if opening was successfull, false otherwise.
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
virtual bool
|
||||||
|
open ( void ) throw ( Exception );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the CastSink is open.
|
||||||
|
*
|
||||||
|
* @return true if the CastSink is open, false otherwise.
|
||||||
|
*/
|
||||||
|
inline virtual bool
|
||||||
|
isOpen ( void ) const throw ()
|
||||||
|
{
|
||||||
|
return bufferedSink != NULL ? bufferedSink->isOpen() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the CastSink is ready to accept data.
|
||||||
|
* Blocks until the specified time for data to be available.
|
||||||
|
*
|
||||||
|
* @param sec the maximum seconds to block.
|
||||||
|
* @param usec micro seconds to block after the full seconds.
|
||||||
|
* @return true if the CastSink is ready to accept data,
|
||||||
|
* false otherwise.
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline virtual bool
|
||||||
|
canWrite ( unsigned int sec,
|
||||||
|
unsigned int usec ) throw ( Exception )
|
||||||
|
{
|
||||||
|
return getSink()->canWrite( sec, usec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the CastSink.
|
||||||
|
*
|
||||||
|
* @param buf the data to write.
|
||||||
|
* @param len number of bytes to write from buf.
|
||||||
|
* @return the number of bytes written (may be less than len).
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline virtual unsigned int
|
||||||
|
write ( const void * buf,
|
||||||
|
unsigned int len ) throw ( Exception )
|
||||||
|
{
|
||||||
|
if ( streamDump != 0 ) {
|
||||||
|
streamDump->write( buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSink()->write( buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush all data that was written to the CastSink to the server.
|
||||||
|
*
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
flush ( void ) throw ( Exception )
|
||||||
|
{
|
||||||
|
if ( streamDump != 0 ) {
|
||||||
|
streamDump->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSink()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
if ( streamDump != 0 ) {
|
||||||
|
streamDump->cut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the CastSink.
|
||||||
|
*
|
||||||
|
* @exception Exception
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
close ( void ) throw ( Exception )
|
||||||
|
{
|
||||||
|
if ( streamDump != 0 ) {
|
||||||
|
streamDump->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSink()->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bitrate of the stream (e.g. mp3 bitrate).
|
||||||
|
*
|
||||||
|
* @return the bitrate of the stream (e.g. mp3 bitrate).
|
||||||
|
*/
|
||||||
|
inline unsigned int
|
||||||
|
getBitRate ( void ) const throw ()
|
||||||
|
{
|
||||||
|
return bitRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the duration of the BufferedSink buffer in seconds.
|
||||||
|
*
|
||||||
|
* @return the the duration of the BufferedSink buffer in seconds.
|
||||||
|
*/
|
||||||
|
inline unsigned int
|
||||||
|
getBufferDuration ( void ) const throw ()
|
||||||
|
{
|
||||||
|
return bufferDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ================================================= external data structures */
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================================== function prototypes */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ICE_CAST2_H */
|
||||||
|
|
Loading…
Reference in New Issue