Commit 1c16632e authored by massiot's avatar massiot

* ALL: Move the config-specific stuff to a substructure, with dedicated

   functions.
 * dvblast.c: Factorize the IP-parsing stuff.
 * output.c: Add support for a configurable MTU and a variable number of TS
   per packet.
 * dvblast.c: Add new options /ifindex= /mtu= /tos= /ssrc=.
 * output.c: Use biTStream instead of custom RTP functions.
 * udp.c: Implement IPv6 support and add options /mtu= /ifaddr= /ifindex=.


git-svn-id: svn://svn.videolan.org/dvblast/trunk@160 55d3f8b6-4a41-4d2d-a900-313d1436a5b8
parent ad33737f
# DVBlast Makefile
# Customise the path of your kernel
VERSION = 1.2.0
VERSION = 2.0.0
TOPDIR = `basename ${PWD}`
CFLAGS += -Wall -O3 -fomit-frame-pointer
......
......@@ -7,6 +7,8 @@ Changes between 1.2 and 2.0:
* Smooth packet output with an extra buffer
* libdvbpsi runtime is no longer needed; biTStream development library
is used instead
* Add the ability to bind to a specific network interface
* Add a configurable MTU
* Add support for NIT in DVB compliance mode
* Syslog support with -l option
* Override FEC Inner with -F option
......
......@@ -80,13 +80,26 @@ instance a multi-program transport stream. The address is specified with
such a stream for instance to cross network boundaries between the
receivers and the target network.
The syntax of the -D option is:
[<src host>[:<src port>]@]<src mcast>[:<port>][/<opts>]*
where <src mcast> is the multicast address carrying the stream, and <src
host> is optionally the address of the source, for source-specific multicast.
Options include:
/udp (for streams without an RTP header)
/mtu=XXXX (sets the maximum UDP packet size)
/ifindex=X (binds to a specific network interface, by link number)
/ifaddr=XXX.XXX.XXX.XXX (binds to a specific network interface, by address)
For example:
-D 239.255.0.2:1234/udp/ifindex=1
Configuring outputs
===================
DVBlast reads a configuration file containing one or several lines in the
format :
<IP>[:<port>][/<option>]* <always on> <SID> [<PID>,]*
<IP>[:<port>][@<IP>[:<port>]][/<option>]* <always on> <SID> [<PID>,]*
For instance :
239.255.0.1:1234 1 10750 1234,1235,1236
......@@ -105,6 +118,15 @@ For example :
[ff12::1%eth0]:1234 1 10750
[ff15::abcd]:1234 1 10750
An optional bind address can be specified after an '@' sign. The typical
usage of this is to allow binding to a specific interface in a system which
has several. For instance:
239.255.0.1:1234@192.168.42.1 1 10750
For IPv6 the bind address is not relevant; binding to an interface is achieved
via the option "/ifindex=X" where X is the number of the link, as given by
the command `ip link`.
The "always on" flag tells DVBlast whether the channel is expected to
be on at all times or if it may break. If set to "1", then DVBlast will
regularly reset the CAM module if it fails to descramble the service,
......@@ -117,12 +139,16 @@ Available options include :
/dvb (turns on -C for a specific output)
/epg (turns on -C -e for a specific output)
/tsid=XXX (sets the transport stream ID)
/ssrc=XXX.XXX.XXX.XXX (sets the RTP synchronization source IPv4)
/retention=XXX (see -E)
/latency=XXX (see -L)
/ttl=XX (see -t)
/tos=XX (sets the IPv4 Type Of Service option)
/mtu=XXXX (sets the maximum UDP packet size)
Several options can be appended, for instance:
239.255.0.1:1234/udp/epg/tsid=42
239.255.0.1:1234/udp/epg/tsid=42/ssrc=192.168.0.1
The optional "/udp" parameter can be used to force DVBlast to output
raw UDP stream. This functionality is provided for backwards compatibility
......
......@@ -238,7 +238,7 @@ static void demux_Handle( block_t *p_ts )
for ( i = 0; i < i_nb_outputs; i++ )
{
output_t *p_output = pp_outputs[i];
if ( p_output->i_sid == p_sid->i_sid )
if ( p_output->config.i_sid == p_sid->i_sid )
{
p_output->i_ref_timestamp = i_timestamp;
p_output->i_ref_wallclock = p_ts->i_dts;
......@@ -257,7 +257,7 @@ static void demux_Handle( block_t *p_ts )
output_t *p_output = p_pids[i_pid].pp_outputs[i];
if ( p_output != NULL )
{
if ( i_ca_handle && (p_output->i_config & OUTPUT_WATCH) &&
if ( i_ca_handle && (p_output->config.i_config & OUTPUT_WATCH) &&
ts_get_unitstart( p_ts->p_ts ) )
{
uint8_t *p_payload;
......@@ -282,7 +282,7 @@ static void demux_Handle( block_t *p_ts )
msg_Warn( NULL,
"too many errors for stream %s, resetting",
p_output->psz_displayname );
p_output->config.psz_displayname );
en50221_Reset();
}
}
......@@ -296,7 +296,7 @@ static void demux_Handle( block_t *p_ts )
}
}
if ( output_dup.i_config & OUTPUT_VALID )
if ( output_dup.config.i_config & OUTPUT_VALID )
output_Put( &output_dup, p_ts );
p_ts->i_refcount--;
......@@ -315,44 +315,51 @@ static int IsIn( uint16_t *pi_pids, int i_nb_pids, uint16_t i_pid )
return ( i != i_nb_pids );
}
void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
uint16_t *pi_pids, int i_nb_pids )
void demux_Change( output_t *p_output, const output_config_t *p_config )
{
int i;
uint16_t *pi_wanted_pids, *pi_current_pids;
int i_nb_wanted_pids, i_nb_current_pids;
uint16_t i_old_sid = p_output->i_sid;
int sid_change = ( i_sid != i_old_sid );
int pid_change = 0, tsid_change = 0;
if ( i_tsid != -1 &&
(!p_output->b_fixed_tsid || p_output->i_tsid != i_tsid) )
uint16_t i_old_sid = p_output->config.i_sid;
uint16_t i_sid = p_config->i_sid;
uint16_t *pi_old_pids = p_output->config.pi_pids;
uint16_t *pi_pids = p_config->pi_pids;
int i_old_nb_pids = p_output->config.i_nb_pids;
int i_nb_pids = p_config->i_nb_pids;
bool b_sid_change = i_sid != i_old_sid;
bool b_pid_change = false, b_tsid_change = false;
int i;
if ( p_config->i_tsid != -1 && p_output->config.i_tsid != p_config->i_tsid )
{
p_output->b_fixed_tsid = true;
p_output->i_tsid = i_tsid;
tsid_change = 1;
p_output->i_tsid = p_config->i_tsid;
b_tsid_change = true;
}
if ( i_tsid == -1 && p_output->b_fixed_tsid && !b_random_tsid )
if ( p_config->i_tsid == -1 && p_output->config.i_tsid != -1 )
{
p_output->b_fixed_tsid = false;
if ( psi_table_validate(pp_current_pat_sections) )
if ( psi_table_validate(pp_current_pat_sections) && !b_random_tsid )
p_output->i_tsid =
psi_table_get_tableidext(pp_current_pat_sections);
tsid_change = 1;
else
p_output->i_tsid = rand() & 0xffff;
b_tsid_change = true;
}
p_output->config.i_tsid = p_config->i_tsid;
if ( i_sid == p_output->i_sid && i_nb_pids == p_output->i_nb_pids &&
(!i_nb_pids ||
!memcmp( p_output->pi_pids, pi_pids, i_nb_pids * sizeof(uint16_t) )) )
if ( !b_sid_change && p_config->i_nb_pids == p_output->config.i_nb_pids &&
(!p_config->i_nb_pids ||
!memcmp( p_output->config.pi_pids, p_config->pi_pids,
p_config->i_nb_pids * sizeof(uint16_t) )) )
goto out_change;
GetPIDS( &pi_wanted_pids, &i_nb_wanted_pids, i_sid, pi_pids, i_nb_pids );
GetPIDS( &pi_current_pids, &i_nb_current_pids, p_output->i_sid,
p_output->pi_pids, p_output->i_nb_pids );
GetPIDS( &pi_current_pids, &i_nb_current_pids, i_old_sid, pi_old_pids,
i_old_nb_pids );
if ( sid_change && i_old_sid )
if ( b_sid_change && i_old_sid )
{
p_output->i_sid = i_sid;
p_output->config.i_sid = p_config->i_sid;
for ( i = 0; i < i_nb_sids; i++ )
{
if ( pp_sids[i]->i_sid == i_old_sid )
......@@ -370,16 +377,15 @@ void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
for ( i = 0; i < i_nb_current_pids; i++ )
{
if ( pi_current_pids[i] != EMPTY_PID &&
!IsIn( pi_wanted_pids, i_nb_wanted_pids, pi_current_pids[i] ) )
if ( !IsIn( pi_wanted_pids, i_nb_wanted_pids, pi_current_pids[i] ) )
{
StopPID( p_output, pi_current_pids[i] );
pid_change = 1;
b_pid_change = true;
}
}
if ( sid_change &&
i_ca_handle && i_old_sid && SIDIsSelected( i_old_sid ) )
if ( b_sid_change && i_ca_handle && i_old_sid &&
SIDIsSelected( i_old_sid ) )
{
for ( i = 0; i < i_nb_sids; i++ )
{
......@@ -395,20 +401,19 @@ void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
for ( i = 0; i < i_nb_wanted_pids; i++ )
{
if ( pi_wanted_pids[i] != EMPTY_PID &&
!IsIn( pi_current_pids, i_nb_current_pids, pi_wanted_pids[i] ) )
if ( !IsIn( pi_current_pids, i_nb_current_pids, pi_wanted_pids[i] ) )
{
StartPID( p_output, pi_wanted_pids[i] );
pid_change = 1;
b_pid_change = true;
}
}
free( pi_wanted_pids );
free( pi_current_pids );
if ( sid_change && i_sid )
if ( b_sid_change && i_sid )
{
p_output->i_sid = i_old_sid;
p_output->config.i_sid = i_old_sid;
for ( i = 0; i < i_nb_sids; i++ )
{
if ( pp_sids[i]->i_sid == i_sid )
......@@ -438,27 +443,27 @@ void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
}
}
p_output->i_sid = i_sid;
free( p_output->pi_pids );
p_output->pi_pids = malloc( sizeof(uint16_t) * i_nb_pids );
memcpy( p_output->pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids );
p_output->i_nb_pids = i_nb_pids;
p_output->config.i_sid = i_sid;
free( p_output->config.pi_pids );
p_output->config.pi_pids = malloc( sizeof(uint16_t) * i_nb_pids );
memcpy( p_output->config.pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids );
p_output->config.i_nb_pids = i_nb_pids;
out_change:
if ( sid_change )
if ( b_sid_change )
{
NewSDT( p_output );
NewNIT( p_output );
NewPAT( p_output );
NewPMT( p_output );
}
else if ( tsid_change )
else if ( b_tsid_change )
{
NewSDT( p_output );
NewNIT( p_output );
NewPAT( p_output );
}
else if ( pid_change )
else if ( b_pid_change )
NewPMT( p_output );
}
......@@ -578,9 +583,9 @@ static void SelectPID( uint16_t i_sid, uint16_t i_pid )
int i;
for ( i = 0; i < i_nb_outputs; i++ )
if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid
&& !pp_outputs[i]->i_nb_pids )
if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID)
&& pp_outputs[i]->config.i_sid == i_sid
&& !pp_outputs[i]->config.i_nb_pids )
StartPID( pp_outputs[i], i_pid );
}
......@@ -589,9 +594,9 @@ static void UnselectPID( uint16_t i_sid, uint16_t i_pid )
int i;
for ( i = 0; i < i_nb_outputs; i++ )
if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid
&& !pp_outputs[i]->i_nb_pids )
if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID)
&& pp_outputs[i]->config.i_sid == i_sid
&& !pp_outputs[i]->config.i_nb_pids )
StopPID( pp_outputs[i], i_pid );
}
......@@ -606,8 +611,8 @@ static void SelectPSI( uint16_t i_sid, uint16_t i_pid )
p_pids[i_pid].b_pes = false;
for ( i = 0; i < i_nb_outputs; i++ )
if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid )
if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID)
&& pp_outputs[i]->config.i_sid == i_sid )
SetPID( i_pid );
}
......@@ -621,8 +626,8 @@ static void UnselectPSI( uint16_t i_sid, uint16_t i_pid )
&p_pids[i_pid].i_psi_buffer_used );
for ( i = 0; i < i_nb_outputs; i++ )
if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid )
if ( (pp_outputs[i]->config.i_config & OUTPUT_VALID)
&& pp_outputs[i]->config.i_sid == i_sid )
UnsetPID( i_pid );
}
......@@ -757,7 +762,7 @@ static void SendPAT( mtime_t i_dts )
{
output_t *p_output = pp_outputs[i];
if ( !(p_output->i_config & OUTPUT_VALID) )
if ( !(p_output->config.i_config & OUTPUT_VALID) )
continue;
if ( p_output->p_pat_section == NULL &&
......@@ -796,8 +801,8 @@ static void SendPMT( sid_t *p_sid, mtime_t i_dts )
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID)
&& p_output->i_sid == p_sid->i_sid
if ( (p_output->config.i_config & OUTPUT_VALID)
&& p_output->config.i_sid == p_sid->i_sid
&& p_output->p_pmt_section != NULL )
OutputPSISection( p_output, p_output->p_pmt_section,
p_sid->i_pmt_pid, &p_output->i_pmt_cc, i_dts,
......@@ -816,8 +821,8 @@ static void SendNIT( mtime_t i_dts )
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB)
if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->config.i_config & OUTPUT_DVB)
&& p_output->p_nit_section != NULL )
OutputPSISection( p_output, p_output->p_nit_section, NIT_PID,
&p_output->i_nit_cc, i_dts, NULL, NULL );
......@@ -835,8 +840,8 @@ static void SendSDT( mtime_t i_dts )
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB)
if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->config.i_config & OUTPUT_DVB)
&& p_output->p_sdt_section != NULL )
OutputPSISection( p_output, p_output->p_sdt_section, SDT_PID,
&p_output->i_sdt_cc, i_dts, NULL, NULL );
......@@ -857,10 +862,10 @@ static void SendEIT( sid_t *p_sid, mtime_t i_dts, uint8_t *p_eit )
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB)
&& (!b_epg || (p_output->i_config & OUTPUT_EPG))
&& p_output->i_sid == p_sid->i_sid )
if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->config.i_config & OUTPUT_DVB)
&& (!b_epg || (p_output->config.i_config & OUTPUT_EPG))
&& p_output->config.i_sid == p_sid->i_sid )
{
if ( eit_get_tsid( p_eit ) != p_output->i_tsid )
{
......@@ -901,8 +906,8 @@ static void SendTDT( block_t *p_ts )
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB)
if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->config.i_config & OUTPUT_DVB)
&& p_output->p_sdt_section != NULL )
output_Put( p_output, p_ts );
}
......@@ -920,11 +925,11 @@ static void NewPAT( output_t *p_output )
p_output->p_pat_section = NULL;
p_output->i_pat_version++;
if ( !p_output->i_sid ) return;
if ( !p_output->config.i_sid ) return;
if ( !psi_table_validate(pp_current_pat_sections) ) return;
p_program = pat_table_find_program( pp_current_pat_sections,
p_output->i_sid );
p_output->config.i_sid );
if ( p_program == NULL ) return;
p = p_output->p_pat_section = psi_allocate();
......@@ -938,7 +943,7 @@ static void NewPAT( output_t *p_output )
p = pat_get_program( p, 0 );
patn_init( p );
patn_set_program( p, p_output->i_sid );
patn_set_program( p, p_output->config.i_sid );
patn_set_pid( p, patn_get_pid( p_program ) );
psi_set_crc( p_output->p_pat_section );
}
......@@ -989,10 +994,10 @@ static void NewPMT( output_t *p_output )
p_output->p_pmt_section = NULL;
p_output->i_pmt_version++;
if ( !p_output->i_sid ) return;
if ( !p_output->config.i_sid ) return;
for ( i = 0; i < i_nb_sids; i++ )
if ( pp_sids[i]->i_sid == p_output->i_sid )
if ( pp_sids[i]->i_sid == p_output->config.i_sid )
break;
if ( i == i_nb_sids ) return;
......@@ -1003,7 +1008,7 @@ static void NewPMT( output_t *p_output )
p = p_output->p_pmt_section = psi_allocate();
pmt_init( p );
psi_set_length( p, PSI_MAX_SIZE );
pmt_set_program( p, p_output->i_sid );
pmt_set_program( p, p_output->config.i_sid );
psi_set_version( p, p_output->i_pmt_version );
psi_set_current( p );
pmt_set_pcrpid( p, pmt_get_pcrpid( p_current_pmt ) );
......@@ -1017,8 +1022,9 @@ static void NewPMT( output_t *p_output )
uint16_t i_pid = pmtn_get_pid( p_current_es );
j++;
if ( (p_output->i_nb_pids || !PIDWouldBeSelected( p_current_es ))
&& !IsIn( p_output->pi_pids, p_output->i_nb_pids, i_pid ) )
if ( (p_output->config.i_nb_pids || !PIDWouldBeSelected( p_current_es ))
&& !IsIn( p_output->config.pi_pids, p_output->config.i_nb_pids,
i_pid ) )
continue;
p_es = pmt_get_es( p, k );
......@@ -1055,7 +1061,7 @@ static void NewNIT( output_t *p_output )
p_output->p_nit_section = NULL;
p_output->i_nit_version++;
if ( !p_output->i_sid ) return;
if ( !p_output->config.i_sid ) return;
if ( !psi_table_validate(pp_current_pat_sections) ) return;
p = p_output->p_nit_section = psi_allocate();
......@@ -1113,11 +1119,11 @@ static void NewSDT( output_t *p_output )
p_output->p_sdt_section = NULL;
p_output->i_sdt_version++;
if ( !p_output->i_sid ) return;
if ( !p_output->config.i_sid ) return;
if ( !psi_table_validate(pp_current_sdt_sections) ) return;
p_current_service = sdt_table_find_service( pp_current_sdt_sections,
p_output->i_sid );
p_output->config.i_sid );
if ( p_current_service == NULL )
{
......@@ -1145,7 +1151,7 @@ static void NewSDT( output_t *p_output )
p_service = sdt_get_service( p, 0 );
sdtn_init( p_service );
sdtn_set_sid( p_service, p_output->i_sid );
sdtn_set_sid( p_service, p_output->config.i_sid );
if ( sdtn_get_eitschedule(p_current_service) )
sdtn_set_eitschedule(p_service);
if ( sdtn_get_eitpresent(p_current_service) )
......@@ -1175,8 +1181,8 @@ static void Update##table( uint16_t i_sid ) \
int i; \
\
for ( i = 0; i < i_nb_outputs; i++ ) \
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) \
&& pp_outputs[i]->i_sid == i_sid ) \
if ( ( pp_outputs[i]->config.i_config & OUTPUT_VALID ) \
&& pp_outputs[i]->config.i_sid == i_sid ) \
New##table( pp_outputs[i] ); \
}
......@@ -1196,7 +1202,8 @@ static void UpdateTSID(void)
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) && !p_output->b_fixed_tsid )
if ( (p_output->config.i_config & OUTPUT_VALID)
&& p_output->config.i_tsid == -1 && !b_random_tsid )
{
p_output->i_tsid = i_tsid;
NewNIT( p_output );
......@@ -1212,8 +1219,8 @@ static bool SIDIsSelected( uint16_t i_sid )
int i;
for ( i = 0; i < i_nb_outputs; i++ )
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID )
&& pp_outputs[i]->i_sid == i_sid )
if ( ( pp_outputs[i]->config.i_config & OUTPUT_VALID )
&& pp_outputs[i]->config.i_sid == i_sid )
return true;
return false;
......
......@@ -45,6 +45,7 @@
#endif
#include <bitstream/dvb/si.h>
#include <bitstream/ietf/rtp.h>
/*****************************************************************************
* Local declarations
......@@ -52,10 +53,9 @@
mtime_t i_wallclock = 0;
output_t **pp_outputs = NULL;
int i_nb_outputs = 0;
output_t output_dup = { 0 };
output_t output_dup;
static char *psz_conf_file = NULL;
char *psz_srv_socket = NULL;
in_addr_t i_ssrc = 0;
static int i_priority = -1;
int i_adapter = 0;
int i_fenum = 0;
......@@ -73,16 +73,16 @@ int b_random_tsid = 0;
uint16_t i_network_id = 0xffff;
uint8_t *p_network_name;
size_t i_network_name_size;
volatile int b_hup_received = 0;
int i_verbose = DEFAULT_VERBOSITY;
int i_syslog = 0;
uint16_t i_src_port = DEFAULT_PORT;
in_addr_t i_src_addr = { 0 };
int b_src_rawudp = 0;
char *psz_udp_src = NULL;
int i_asi_adapter = 0;
const char *psz_native_charset = "UTF-8";
const char *psz_dvb_charset = "ISO_8859-1";
volatile sig_atomic_t b_hup_received = 0;
int i_verbose = DEFAULT_VERBOSITY;
int i_syslog = 0;
uint8_t pi_ssrc_global[4] = { 0, 0, 0, 0 };
static int b_udp_global = 0;
static int b_dvb_global = 0;
static int b_epg_global = 0;
......@@ -98,7 +98,145 @@ void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL;
/*****************************************************************************
* Configuration files
*****************************************************************************/
static void ReadConfiguration( char *psz_file )
void config_Init( output_config_t *p_config )
{
memset( p_config, 0, sizeof(output_config_t) );
p_config->psz_displayname = NULL;
p_config->i_family = AF_UNSPEC;
p_config->connect_addr.ss_family = AF_UNSPEC;
p_config->bind_addr.ss_family = AF_UNSPEC;
p_config->i_if_index_v6 = -1;
p_config->pi_pids = NULL;
}
void config_Free( output_config_t *p_config )
{
free( p_config->psz_displayname );
free( p_config->pi_pids );
}
static void config_Defaults( output_config_t *p_config )
{
config_Init( p_config );
p_config->i_config = (b_udp_global ? OUTPUT_UDP : 0) |
(b_dvb_global ? OUTPUT_DVB : 0) |
(b_epg_global ? OUTPUT_EPG : 0);
p_config->i_max_retention = i_retention_global;
p_config->i_output_latency = i_latency_global;
p_config->i_tsid = -1;
p_config->i_ttl = i_ttl_global;
memcpy( p_config->pi_ssrc, pi_ssrc_global, 4 * sizeof(uint8_t) );
}
bool config_ParseHost( output_config_t *p_config, char *psz_string )
{
struct addrinfo *p_ai;
int i_mtu;
p_config->psz_displayname = strdup( psz_string );
p_ai = ParseNodeService( psz_string, &psz_string, DEFAULT_PORT );
if ( p_ai == NULL ) goto err;
memcpy( &p_config->connect_addr, p_ai->ai_addr, p_ai->ai_addrlen );
freeaddrinfo( p_ai );
p_config->i_family = p_config->connect_addr.ss_family;
if ( p_config->i_family == AF_UNSPEC ) goto err;
if ( psz_string == NULL || !*psz_string ) goto end;
if ( *psz_string == '@' )
{
psz_string++;
p_ai = ParseNodeService( psz_string, &psz_string, 0 );
if ( p_ai == NULL || p_ai->ai_family != p_config->i_family )
msg_Warn( NULL, "invalid bind address" );
else
memcpy( &p_config->bind_addr, p_ai->ai_addr, p_ai->ai_addrlen );
freeaddrinfo( p_ai );
}
while ( (psz_string = strchr( psz_string, '/' )) != NULL )
{
*psz_string++ = '\0';
#define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) ))
#define ARG_OPTION( option ) (psz_string + strlen(option))
if ( IS_OPTION("udp") )
p_config->i_config |= OUTPUT_UDP;
else if ( IS_OPTION("dvb") )
p_config->i_config |= OUTPUT_DVB;
else if ( IS_OPTION("epg") )
p_config->i_config |= OUTPUT_EPG;
else if ( IS_OPTION("tsid=") )
p_config->i_tsid = strtol( ARG_OPTION("tsid="), NULL, 0 );
else if ( IS_OPTION("retention=") )
p_config->i_max_retention = strtoll( ARG_OPTION("retention="),
NULL, 0 ) * 1000;
else if ( IS_OPTION("latency=") )
p_config->i_output_latency = strtoll( ARG_OPTION("latency="),
NULL, 0 ) * 1000;
else if ( IS_OPTION("ttl=") )
p_config->i_ttl = strtol( ARG_OPTION("ttl="), NULL, 0 );
else if ( IS_OPTION("tos=") )
p_config->i_tos = strtol( ARG_OPTION("tos="), NULL, 0 );
else if ( IS_OPTION("mtu=") )
p_config->i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 );
else if ( IS_OPTION("ifindex=") )
p_config->i_if_index_v6 = strtol( ARG_OPTION("ifindex="), NULL, 0 );
else if ( IS_OPTION("ssrc=") )
{
in_addr_t i_addr = inet_addr( ARG_OPTION("ssrc=") );
memcpy( p_config->pi_ssrc, &i_addr, 4 * sizeof(uint8_t) );
}
else
msg_Warn( NULL, "unrecognized option %s", psz_string );
#undef IS_OPTION
#undef ARG_OPTION
}
end:
i_mtu = p_config->i_family == AF_INET6 ? DEFAULT_IPV6_MTU :
DEFAULT_IPV4_MTU;
if ( !p_config->i_mtu )
p_config->i_mtu = i_mtu;
else if ( p_config->i_mtu < TS_SIZE + RTP_HEADER_SIZE )
{
msg_Warn( NULL, "invalid MTU %d, setting %d", p_config->i_mtu, i_mtu );
p_config->i_mtu = i_mtu;
}
return true;
err:
free( p_config->psz_displayname );
return false;
}
static void config_Print( output_config_t *p_config )
{
const char *psz_base = "conf: %s config=0x%x sid=%d pids[%d]=";
size_t i_len = strlen(psz_base) + 6 * p_config->i_nb_pids + 1;
char psz_format[i_len];
int i, j = strlen(psz_base);
strcpy( psz_format, psz_base );
for ( i = 0; i < p_config->i_nb_pids; i++ )
j += sprintf( psz_format + j, "%u,", p_config->pi_pids[i] );
psz_format[j - 1] = '\0';
msg_Dbg( NULL, psz_format, p_config->psz_displayname, p_config->i_config,
p_config->i_sid, p_config->i_nb_pids );
}
static void config_ReadFile( char *psz_file )
{
FILE *p_file;
char psz_line[2048];
......@@ -118,130 +256,40 @@ static void ReadConfiguration( char *psz_file )
while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL )
{
output_t *p_output = NULL;
char *psz_parser, *psz_token, *psz_token2;
struct addrinfo *p_addr;
struct addrinfo ai_hints;
char sz_port[6];
char *psz_displayname;
uint16_t i_sid = 0;
uint16_t *pi_pids = NULL;
int i_nb_pids = 0;
uint8_t i_config = (b_udp_global ? OUTPUT_UDP : 0) |
(b_dvb_global ? OUTPUT_DVB : 0) |
(b_epg_global ? OUTPUT_EPG : 0);
mtime_t i_retention = i_retention_global;
mtime_t i_latency = i_latency_global;
int i_tsid = -1;
int i_ttl = i_ttl_global;
snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT );
output_config_t config;
output_t *p_output;
char *psz_token, *psz_parser;
if ( !strncmp( psz_line, "#", 1 ) )
continue;
psz_token = strtok_r( psz_line, "\t\n ", &psz_parser );
if ( psz_token == NULL )
continue;
psz_token2 = psz_token;
while ( (psz_token2 = strchr( psz_token2, '/' )) != NULL )
{
*psz_token2++ = '\0';
if ( !strncasecmp( psz_token2, "udp", 3 ) )
i_config |= OUTPUT_UDP;
else if ( !strncasecmp( psz_token2, "dvb", 3 ) )
i_config |= OUTPUT_DVB;
else if ( !strncasecmp( psz_token2, "epg", 3 ) )
i_config |= OUTPUT_EPG;
else if ( !strncasecmp( psz_token2, "tsid=", 5 ) )
i_tsid = strtol( psz_token2 + 5, NULL, 0 );
else if ( !strncasecmp( psz_token2, "retention=", 10 ) )
i_retention = strtoll( psz_token2 + 10, NULL, 0 ) * 1000;
else if ( !strncasecmp( psz_token2, "latency=", 8 ) )
i_latency = strtoll( psz_token2 + 8, NULL, 0 ) * 1000;
else if ( !strncasecmp( psz_token2, "ttl=", 4 ) )
i_ttl = strtol( psz_token2 + 4, NULL, 0 );
else
msg_Warn( NULL, "unrecognized option %s", psz_token2 );
}
if ( !strncmp( psz_token, "[", 1 ) )
{
if ( (psz_token2 = strchr( psz_token, ']' ) ) == NULL )
continue;
char *psz_maddr = malloc( psz_token2 - psz_token );
memset( psz_maddr, '\0', ( psz_token2 - psz_token ) );
strncpy( psz_maddr, psz_token + 1, ( psz_token2 - psz_token - 1 ));
if ( (psz_token2 = strchr( psz_token2, ':' )) != NULL )
{
*psz_token2 = '\0';
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) );
}
p_addr = malloc( sizeof( p_addr ) );
config_Defaults( &config );
memset( &ai_hints, 0, sizeof( ai_hints ) );
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
ai_hints.ai_family = AF_INET6;
int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_addr );
if ( i_ai != 0 )
{
msg_Err( NULL, "Cannot configure output [%s]:%s: %s", psz_maddr,
sz_port, gai_strerror( i_ai ) );
continue;
}
psz_displayname = malloc( INET6_ADDRSTRLEN + 8 );
snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 8 ), "[%s]:%s",
psz_maddr, sz_port );
free( psz_maddr );
}
else
psz_token = strtok_r( psz_line, "\t\n ", &psz_parser );
if ( psz_token == NULL || !config_ParseHost( &config, psz_token ))
{
if ( (psz_token2 = strrchr( psz_token, ':' )) != NULL )
{
*psz_token2 = '\0';
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) );
}
p_addr = malloc( sizeof( p_addr ) );
memset( &ai_hints, 0, sizeof( ai_hints ) );
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
ai_hints.ai_family = AF_INET;
int i_ai = getaddrinfo( psz_token, sz_port, &ai_hints, &p_addr );
if ( i_ai != 0 )
{
msg_Err( NULL, "Cannot configure output %s:%s: %s", psz_token,
sz_port, gai_strerror( i_ai ) );
continue;
}
psz_displayname = malloc( INET_ADDRSTRLEN + 6 );
snprintf( psz_displayname, ( INET_ADDRSTRLEN + 6 ), "%s:%s",
psz_token, sz_port );
config_Free( &config );
continue;
}
psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token == NULL )
{
config_Free( &config );
continue;
}
if( atoi( psz_token ) == 1 )
i_config |= OUTPUT_WATCH;
config.i_config |= OUTPUT_WATCH;
else
i_config &= ~OUTPUT_WATCH;
config.i_config &= ~OUTPUT_WATCH;
psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token == NULL )
{
config_Free( &config );
continue;
i_sid = strtol(psz_token, NULL, 0);
}
config.i_sid = strtol(psz_token, NULL, 0);
psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token != NULL )
......@@ -252,69 +300,33 @@ static void ReadConfiguration( char *psz_file )
psz_token = strtok_r( psz_token, ",", &psz_parser );
if ( psz_token == NULL )
break;
pi_pids = realloc( pi_pids,
(i_nb_pids + 1) * sizeof(uint16_t) );
pi_pids[i_nb_pids++] = strtol(psz_token, NULL, 0);
config.pi_pids = realloc( config.pi_pids,
(config.i_nb_pids + 1) * sizeof(uint16_t) );
config.pi_pids[config.i_nb_pids++] = strtol(psz_token, NULL, 0);
psz_token = NULL;
}
}
msg_Dbg( NULL,
"conf: %s config=0x%x ttl=%d sid=%d pids[%d]=%d,%d,%d,%d,%d...",
psz_displayname, i_config, i_ttl, i_sid, i_nb_pids,
i_nb_pids < 1 ? -1 : pi_pids[0],
i_nb_pids < 2 ? -1 : pi_pids[1],
i_nb_pids < 3 ? -1 : pi_pids[2],
i_nb_pids < 4 ? -1 : pi_pids[3],
i_nb_pids < 5 ? -1 : pi_pids[4] );
config_Print( &config );
for ( i = 0; i < i_nb_outputs; i++ )
{
if ( pp_outputs[i]->p_addr->ss_family == AF_INET )
{
struct sockaddr_in *p_esad = (struct sockaddr_in *)pp_outputs[i]->p_addr;
struct sockaddr_in *p_nsad = (struct sockaddr_in *)p_addr->ai_addr;
p_output = output_Find( &config );
if ( ( p_esad->sin_addr.s_addr == p_nsad->sin_addr.s_addr ) &&
( p_esad->sin_port == p_nsad->sin_port ) )
{
p_output = pp_outputs[i];
break;
}
}
else if ( pp_outputs[i]->p_addr->ss_family == AF_INET6 )
{
struct sockaddr_in6 *p_esad = (struct sockaddr_in6 *)pp_outputs[i]->p_addr;
struct sockaddr_in6 *p_nsad = (struct sockaddr_in6 *)p_addr->ai_addr;
if ( ( p_esad->sin6_addr.s6_addr32[0] == p_nsad->sin6_addr.s6_addr32[0] ) &&
( p_esad->sin6_addr.s6_addr32[1] == p_nsad->sin6_addr.s6_addr32[1] ) &&
( p_esad->sin6_addr.s6_addr32[2] == p_nsad->sin6_addr.s6_addr32[2] ) &&
( p_esad->sin6_addr.s6_addr32[3] == p_nsad->sin6_addr.s6_addr32[3] ) &&
( p_esad->sin6_port == p_nsad->sin6_port ) )
{
p_output = pp_outputs[i];
break;
}
}
}
if ( i == i_nb_outputs )
p_output = output_Create( psz_displayname, p_addr );
if ( p_output == NULL )
p_output = output_Create( &config );
if ( p_output != NULL )
{
p_output->i_config = OUTPUT_VALID | OUTPUT_STILL_PRESENT | i_config;
p_output->i_output_latency = i_latency;
p_output->i_max_retention = i_retention;
demux_Change( p_output, i_tsid, i_sid, pi_pids, i_nb_pids );
if ( p_output->i_ttl != i_ttl )
output_SetTTL( p_output, i_ttl );
free( p_output->config.psz_displayname );
p_output->config.psz_displayname = strdup( config.psz_displayname );
output_Change( p_output, &config );
demux_Change( p_output, &config );
p_output->config.i_config = OUTPUT_VALID | OUTPUT_STILL_PRESENT |
config.i_config;
}
free( psz_displayname );
free( pi_pids );
freeaddrinfo( p_addr );
config_Free( &config );
}
fclose( p_file );
......@@ -322,16 +334,20 @@ static void ReadConfiguration( char *psz_file )
for ( i = 0; i < i_nb_outputs; i++ )
{
output_t *p_output = pp_outputs[i];
output_config_t config;
if ( (p_output->i_config & OUTPUT_VALID) &&
!(p_output->i_config & OUTPUT_STILL_PRESENT) )
config_Init( &config );
if ( (p_output->config.i_config & OUTPUT_VALID) &&
!(p_output->config.i_config & OUTPUT_STILL_PRESENT) )
{
msg_Dbg( NULL, "closing %s", pp_outputs[i]->psz_displayname );
demux_Change( pp_outputs[i], -1, 0, NULL, 0 );
output_Close( pp_outputs[i] );
msg_Dbg( NULL, "closing %s", p_output->config.psz_displayname );
demux_Change( p_output, &config );
output_Close( p_output );
}
pp_outputs[i]->i_config &= ~OUTPUT_STILL_PRESENT;
p_output->config.i_config &= ~OUTPUT_STILL_PRESENT;
config_Free( &config );
}
}
......@@ -357,7 +373,7 @@ static void DisplayVersion()
*****************************************************************************/
void usage()
{
msg_Raw( NULL, "Usage: dvblast [-q] [-c <config file>] [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] [-i <RT priority>] [-a <adapter>] [-n <frontend number>] [-S <diseqc>] [-f <frequency>|-D <src mcast>:<port>|-A <ASI adapter>] [-F <fec inner>] [-R <rolloff>] [-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-m <modulation] [-u] [-U] [-L <latency>] [-E <retention>] [-d <dest IP:port>] [-C [-e] [-M <network name] [-N <network ID>]] [-T] [-j <system charset>] [-J <DVB charset>]" );
msg_Raw( NULL, "Usage: dvblast [-q] [-c <config file>] [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] [-i <RT priority>] [-a <adapter>] [-n <frontend number>] [-S <diseqc>] [-f <frequency>|-D [<src host>[:<src port>]@]<src mcast>[:<port>][/<opts>]*|-A <ASI adapter>] [-F <fec inner>] [-R <rolloff>] [-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-m <modulation] [-u] [-U] [-L <latency>] [-E <retention>] [-d <dest IP>[<:port>][/<opts>]*] [-C [-e] [-M <network name] [-N <network ID>]] [-T] [-j <system charset>] [-J <DVB charset>]" );
msg_Raw( NULL, "Input:" );
msg_Raw( NULL, " -a --adapter <adapter>" );
......@@ -411,6 +427,7 @@ int main( int i_argc, char **pp_argv )
const char *psz_network_name = "DVBlast - http://www.videolan.org/projects/dvblast.html";
char *p_network_name_tmp = NULL;
size_t i_network_name_tmp_size;
char *psz_dup_config = NULL;
mtime_t i_poll_timeout = MAX_POLL_TIMEOUT;
struct sched_param param;
int i_error;
......@@ -510,7 +527,7 @@ int main( int i_argc, char **pp_argv )
struct in_addr maddr;
if ( !inet_aton( optarg, &maddr ) )
usage();
i_ssrc = maddr.s_addr;
memcpy( pi_ssrc_global, &maddr.s_addr, 4 * sizeof(uint8_t) );
break;
}
......@@ -585,105 +602,11 @@ int main( int i_argc, char **pp_argv )
break;
case 'd':
{
char *psz_token, *psz_displayname;
char sz_port[6];
struct addrinfo *p_daddr;
struct addrinfo ai_hints;
int i_dup_config = 0;
snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT );
p_daddr = malloc( sizeof( p_daddr ) );
memset( p_daddr, '\0', sizeof( p_daddr ) );
if ( !strncmp( optarg, "[", 1 ) )
{
if ( (psz_token = strchr( optarg, ']' ) ) == NULL )
{
msg_Err(NULL, "Invalid target address for -d switch");
break;
}
char *psz_maddr = malloc( psz_token - optarg );
memset( psz_maddr, '\0', ( psz_token - optarg ) );
strncpy( psz_maddr, optarg + 1, ( psz_token - optarg - 1 ));
if ( (psz_token = strchr( psz_token, ':' )) != NULL )
{
*psz_token = '\0';
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token + 1 ) );
}
memset( &ai_hints, 0, sizeof( ai_hints ) );
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
ai_hints.ai_family = AF_INET6;
int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_daddr );
if ( i_ai != 0 )
{
msg_Err( NULL, "Cannot duplicate to [%s]:%s: %s", psz_maddr,
sz_port, gai_strerror( i_ai ) );
break;
}
psz_displayname = malloc( INET6_ADDRSTRLEN + 20 );
snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 20 ),
"duplicate ([%s]:%s)", psz_maddr, sz_port );
i_dup_config |= OUTPUT_VALID;
free( psz_maddr );
}
else
{
if ( (psz_token = strrchr( optarg, ':' )) != NULL )
{
*psz_token = '\0';
snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token + 1 ) );
}
memset( &ai_hints, 0, sizeof( ai_hints ) );
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
ai_hints.ai_family = AF_INET;
int i_ai = getaddrinfo( optarg, sz_port, &ai_hints, &p_daddr );
if ( i_ai != 0 )
{
msg_Err( NULL, "Cannot duplicate to %s:%s: %s", optarg,
sz_port, gai_strerror( i_ai ) );
break;
}
psz_displayname = malloc( INET_ADDRSTRLEN + 18 );
snprintf( psz_displayname, ( INET_ADDRSTRLEN + 18 ),
"duplicate (%s:%s)", optarg, sz_port );
i_dup_config |= OUTPUT_VALID;
}
if ( i_dup_config & OUTPUT_VALID )
{
output_dup.i_config = i_dup_config;
output_dup.i_output_latency = i_latency_global;
output_dup.i_max_retention = i_retention_global;
output_Init( &output_dup, psz_displayname, p_daddr );
}
else
msg_Err( NULL, "Invalid configuration for -d switch: %s" ,
optarg);
free( psz_displayname );
freeaddrinfo( p_daddr );
psz_dup_config = optarg;
break;
}
case 'D':
{
char *psz_token;
struct in_addr maddr;
psz_udp_src = optarg;
if ( pf_Open != NULL )
usage();
if ( psz_srv_socket != NULL )
......@@ -691,27 +614,11 @@ int main( int i_argc, char **pp_argv )
msg_Err( NULL, "-r is only available for linux-dvb input" );
usage();
}
pf_Open = udp_Open;
pf_Read = udp_Read;
pf_SetFilter = udp_SetFilter;
pf_UnsetFilter = udp_UnsetFilter;
if ( (psz_token = strrchr( optarg, '/' )) != NULL )
{
*psz_token = '\0';
b_src_rawudp = ( strncasecmp( psz_token + 1, "udp", 3 ) == 0 );
}
if ( (psz_token = strrchr( optarg, ':' )) != NULL )
{
*psz_token = '\0';
i_src_port = atoi( psz_token + 1 );
}
if ( !inet_aton( optarg, &maddr ) )
usage();
i_src_addr = maddr.s_addr;
break;
}
case 'A':
i_asi_adapter = strtol( optarg, NULL, 0 );
......@@ -789,6 +696,23 @@ int main( int i_argc, char **pp_argv )
b_dvb_global = 1;
}
memset( &output_dup, 0, sizeof(output_dup) );
if ( psz_dup_config != NULL )
{
output_config_t config;
config_Defaults( &config );
if ( !config_ParseHost( &config, psz_dup_config ) )
msg_Err( NULL, "Invalid target address for -d switch" );
else
{
output_Init( &output_dup, &config );
output_Change( &output_dup, &config );
}
config_Free( &config );
}
if ( strcasecmp( psz_native_charset, psz_dvb_charset ) )
{
#ifdef HAVE_ICONV
......@@ -843,7 +767,7 @@ int main( int i_argc, char **pp_argv )
}
}
ReadConfiguration( psz_conf_file );
config_ReadFile( psz_conf_file );
if ( psz_srv_socket != NULL )
comm_Open();
......@@ -856,7 +780,7 @@ int main( int i_argc, char **pp_argv )
{
b_hup_received = 0;
msg_Warn( NULL, "HUP received, reloading" );
ReadConfiguration( psz_conf_file );
config_ReadFile( psz_conf_file );
}
p_ts = pf_Read( i_poll_timeout );
......
......@@ -30,10 +30,8 @@
#define DEFAULT_PORT 3001
#define TS_SIZE 188
#define NB_BLOCKS 7
#define NB_BLOCKS_IPV6 6 // assume MTU of 1280 bytes for IPv6
#define RTP_SIZE 12
#define EMPTY_PID 8192
#define DEFAULT_IPV4_MTU 1500
#define DEFAULT_IPV6_MTU 1280
#define PADDING_PID 8191
#define WATCHDOG_WAIT 10000000LL
#define MAX_ERRORS 1000
......@@ -74,14 +72,35 @@ typedef struct block_t
typedef struct packet_t packet_t;
typedef struct output_t
typedef struct output_config_t
{
/* address information, protocol agnostic */
struct sockaddr_storage *p_addr;
socklen_t i_addrlen;
/* identity */
int i_family;
struct sockaddr_storage connect_addr;
struct sockaddr_storage bind_addr;
int i_if_index_v6;
/* display string */
/* common config */
char *psz_displayname;
uint64_t i_config;
/* output config */
uint8_t pi_ssrc[4];
mtime_t i_output_latency, i_max_retention;
int i_ttl;
uint8_t i_tos;
int i_mtu;
/* demux config */
int i_tsid;
uint16_t i_sid; /* 0 if raw mode */
uint16_t *pi_pids;
int i_nb_pids;
} output_config_t;
typedef struct output_t
{
output_config_t config;
/* output */
int i_handle;
......@@ -103,17 +122,7 @@ typedef struct output_t
uint8_t i_sdt_version, i_sdt_cc;
block_t *p_eit_ts_buffer;
uint8_t i_eit_ts_buffer_offset, i_eit_cc;
/* configuration */
uint16_t i_sid; /* 0 if raw mode */
uint16_t *pi_pids;
int i_nb_pids;
int i_ttl;
in_addr_t i_ssrc;
uint16_t i_tsid;
bool b_fixed_tsid;
mtime_t i_output_latency, i_max_retention;
uint8_t i_config;
} output_t;
extern int i_syslog;
......@@ -122,7 +131,6 @@ extern output_t **pp_outputs;
extern int i_nb_outputs;
extern output_t output_dup;
extern char *psz_srv_socket;
extern in_addr_t i_ssrc;
extern int i_adapter;
extern int i_fenum;
extern int i_frequency;
......@@ -142,9 +150,7 @@ extern size_t i_network_name_size;
extern mtime_t i_wallclock;
extern volatile int b_hup_received;
extern int i_comm_fd;
extern uint16_t i_src_port;
extern in_addr_t i_src_addr;
extern int b_src_rawudp;
extern char *psz_udp_src;
extern int i_asi_adapter;
extern const char *psz_native_charset;
extern const char *psz_dvb_charset;
......@@ -158,6 +164,10 @@ extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid );
* Prototypes
*****************************************************************************/
void config_Init( output_config_t *p_config );
void config_Free( output_config_t *p_config );
bool config_ParseHost( output_config_t *p_config, char *psz_string );
/* Connect/Disconnect from syslogd */
void msg_Connect( const char *ident );
void msg_Disconnect( void );
......@@ -173,6 +183,8 @@ void msg_Raw( void *_unused, const char *psz_format, ... );
mtime_t mdate( void );
void msleep( mtime_t delay );
void hexDump( uint8_t *p_data, uint32_t i_len );
struct addrinfo *ParseNodeService( char *_psz_string, char **ppsz_end,
uint16_t i_default_port );
void dvb_Open( void );
void dvb_Reset( void );
......@@ -193,21 +205,20 @@ void asi_UnsetFilter( int i_fd, uint16_t i_pid );
void demux_Open( void );
void demux_Run( block_t *p_ts );
void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
uint16_t *pi_pids, int i_nb_pids );
void demux_Change( output_t *p_output, const output_config_t *p_config );
void demux_ResendCAPMTs( void );
bool demux_PIDIsSelected( uint16_t i_pid );
char *demux_Iconv(void *_unused, const char *psz_encoding,
char *p_string, size_t i_length);
output_t *output_Create( const char *psz_displayname, struct addrinfo *p_ai );
int output_Init( output_t *p_output, const char *psz_displayname,
struct addrinfo *p_ai );
output_t *output_Create( const output_config_t *p_config );
int output_Init( output_t *p_output, const output_config_t *p_config );
void output_Close( output_t *p_output );
void output_Put( output_t *p_output, block_t *p_block );
mtime_t output_Send( void );
void output_SetTTL( output_t *p_output, int i_ttl );
output_t *output_Find( const output_config_t *p_config );
void output_Change( output_t *p_output, const output_config_t *p_config );
void comm_Open( void );
void comm_Read( void );
......
......@@ -39,16 +39,19 @@
#include "dvblast.h"
#include <bitstream/mpeg/ts.h>
#include <bitstream/ietf/rtp.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
struct packet_t
{
block_t *pp_blocks[NB_BLOCKS];
int i_depth;
mtime_t i_dts;
struct packet_t *p_next;
mtime_t i_dts;
int i_depth;
block_t **pp_blocks;
/* PRIVATE - this MUST be at the end of the structure: */
block_t *p_blocks; /* actually an array of pointers */
};
static uint8_t p_pad_ts[TS_SIZE] = {
......@@ -70,21 +73,17 @@ static uint8_t p_pad_ts[TS_SIZE] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static int net_Open( output_t *p_output );
static void rtp_SetHdr( output_t *p_output, packet_t *p_packet,
uint8_t *p_hdr );
/*****************************************************************************
* output_Create : called from main thread
* output_Create : create and insert the output_t structure
*****************************************************************************/
output_t *output_Create( const char *psz_displayname, struct addrinfo *p_ai )
output_t *output_Create( const output_config_t *p_config )
{
int i;
output_t *p_output = NULL;
for ( i = 0; i < i_nb_outputs; i++ )
{
if ( !( pp_outputs[i]->i_config & OUTPUT_VALID ) )
if ( !( pp_outputs[i]->config.i_config & OUTPUT_VALID ) )
{
p_output = pp_outputs[i];
break;
......@@ -94,30 +93,31 @@ output_t *output_Create( const char *psz_displayname, struct addrinfo *p_ai )
if ( p_output == NULL )
{
p_output = malloc( sizeof(output_t) );
memset( p_output, 0, sizeof(output_t) );
i_nb_outputs++;
pp_outputs = realloc( pp_outputs, i_nb_outputs * sizeof(output_t *) );
pp_outputs[i] = p_output;
}
if ( output_Init( p_output, psz_displayname, p_ai ) < 0 )
if ( output_Init( p_output, p_config ) < 0 )
return NULL;
return p_output;
}
/*****************************************************************************
* output_Init
* output_Init : set up the output initial config
*****************************************************************************/
int output_Init( output_t *p_output, const char *psz_displayname,
struct addrinfo *p_ai )
int output_Init( output_t *p_output, const output_config_t *p_config )
{
p_output->i_sid = 0;
p_output->p_packets = p_output->p_last_packet = NULL;
p_output->pi_pids = NULL;
p_output->i_nb_pids = 0;
socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
p_output->i_nb_errors = 0;
memset( p_output, 0, sizeof(output_t) );
config_Init( &p_output->config );
/* Init run-time values */
p_output->p_packets = p_output->p_last_packet = NULL;
p_output->i_cc = rand() & 0xffff;
p_output->i_pat_cc = rand() & 0xf;
p_output->i_pmt_cc = rand() & 0xf;
......@@ -133,36 +133,66 @@ int output_Init( output_t *p_output, const char *psz_displayname,
p_output->p_nit_section = NULL;
p_output->p_sdt_section = NULL;
p_output->p_eit_ts_buffer = NULL;
p_output->i_eit_ts_buffer_offset = 0;
if ( b_random_tsid )
{
p_output->i_tsid = rand() & 0xffff;
p_output->b_fixed_tsid = true;
}
else
/* Init socket-related fields */
p_output->config.i_family = p_config->i_family;
memcpy( &p_output->config.connect_addr, &p_config->connect_addr,
sizeof(struct sockaddr_storage) );
memcpy( &p_output->config.bind_addr, &p_config->bind_addr,
sizeof(struct sockaddr_storage) );
p_output->config.i_if_index_v6 = p_config->i_if_index_v6;
p_output->i_handle = socket( p_config->i_family, SOCK_DGRAM, IPPROTO_UDP );
if ( p_output->i_handle < 0 )
{
p_output->i_tsid = 0;
p_output->b_fixed_tsid = false;
msg_Err( NULL, "couldn't create socket (%s)", strerror(errno) );
p_output->config.i_config &= ~OUTPUT_VALID;
return -errno;
}
p_output->i_ref_timestamp = 0;
p_output->i_ref_wallclock = 0;
p_output->i_config = 0;
p_output->i_ttl = 0;
p_output->psz_displayname = strdup( psz_displayname );
if ( p_config->bind_addr.ss_family != AF_UNSPEC )
{
if ( bind( p_output->i_handle, (struct sockaddr *)&p_config->bind_addr,
i_sockaddr_len ) < 0 )
msg_Warn( NULL, "couldn't bind socket (%s)", strerror(errno) );
p_output->i_addrlen = p_ai->ai_addrlen;
p_output->p_addr = malloc( p_output->i_addrlen );
memcpy( p_output->p_addr, p_ai->ai_addr,
p_output->i_addrlen );
if ( p_config->i_family == AF_INET )
{
struct sockaddr_in *p_connect_addr =
(struct sockaddr_in *)&p_output->config.connect_addr;
struct sockaddr_in *p_bind_addr =
(struct sockaddr_in *)&p_output->config.bind_addr;
if ( IN_MULTICAST( ntohl( p_connect_addr->sin_addr.s_addr ) ) )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_MULTICAST_IF,
(void *)&p_bind_addr->sin_addr.s_addr,
sizeof(p_bind_addr->sin_addr.s_addr) );
}
}
if ( p_config->i_family == AF_INET6 && p_config->i_if_index_v6 != -1 )
{
struct sockaddr_in6 *p_addr =
(struct sockaddr_in6 *)&p_output->config.connect_addr;
if ( IN6_IS_ADDR_MULTICAST( p_addr->sin6_addr.s6_addr ) )
setsockopt( p_output->i_handle, IPPROTO_IPV6,
IPV6_MULTICAST_IF, (void *)&p_config->i_if_index_v6,
sizeof(p_config->i_if_index_v6) );
}
if ( ( p_output->i_handle = net_Open( p_output ) ) < 0 )
if ( connect( p_output->i_handle,
(struct sockaddr *)&p_output->config.connect_addr,
i_sockaddr_len ) < 0 )
{
p_output->i_config &= ~OUTPUT_VALID;
return -1;
msg_Err( NULL, "couldn't connect socket (%s)", strerror(errno) );
close( p_output->i_handle );
p_output->config.i_config &= ~OUTPUT_VALID;
return -errno;
}
p_output->i_config |= OUTPUT_VALID;
p_output->config.i_config |= OUTPUT_VALID;
return 0;
}
......@@ -189,15 +219,27 @@ void output_Close( output_t *p_output )
}
p_output->p_packets = p_output->p_last_packet = NULL;
free( p_output->psz_displayname );
free( p_output->p_pat_section );
free( p_output->p_pmt_section );
free( p_output->p_nit_section );
free( p_output->p_sdt_section );
free( p_output->p_eit_ts_buffer );
free( p_output->p_addr );
p_output->i_config &= ~OUTPUT_VALID;
p_output->config.i_config &= ~OUTPUT_VALID;
close( p_output->i_handle );
config_Free( &p_output->config );
}
/*****************************************************************************
* output_BlockCount
*****************************************************************************/
static int output_BlockCount( output_t *p_output )
{
int i_mtu = p_output->config.i_mtu;
if ( !(p_output->config.i_config & OUTPUT_UDP) )
i_mtu -= RTP_HEADER_SIZE;
return i_mtu / TS_SIZE;
}
/*****************************************************************************
......@@ -206,17 +248,25 @@ void output_Close( output_t *p_output )
static void output_Flush( output_t *p_output )
{
packet_t *p_packet = p_output->p_packets;
struct iovec p_iov[NB_BLOCKS + 1];
uint8_t p_rtp_hdr[RTP_SIZE];
int i_block_cnt = ( p_output->p_addr->ss_family == AF_INET6 ) ?
NB_BLOCKS_IPV6 : NB_BLOCKS;
int i_block_cnt = output_BlockCount( p_output );
struct iovec p_iov[i_block_cnt + 1];
uint8_t p_rtp_hdr[RTP_HEADER_SIZE];
int i_iov, i_block;
if ( !(p_output->i_config & OUTPUT_UDP) )
if ( !(p_output->config.i_config & OUTPUT_UDP) )
{
p_iov[0].iov_base = p_rtp_hdr;
p_iov[0].iov_len = sizeof(p_rtp_hdr);
rtp_SetHdr( p_output, p_packet, p_rtp_hdr );
rtp_set_hdr( p_rtp_hdr );
rtp_set_type( p_rtp_hdr, RTP_TYPE_TS );
rtp_set_cc( p_rtp_hdr, p_output->i_cc++ );
rtp_set_timestamp( p_rtp_hdr,
p_output->i_ref_timestamp
+ (p_packet->i_dts - p_output->i_ref_wallclock)
* 9 / 100 );
rtp_set_ssrc( p_rtp_hdr, p_output->config.pi_ssrc );
i_iov = 1;
}
else
......@@ -239,7 +289,7 @@ static void output_Flush( output_t *p_output )
if ( writev( p_output->i_handle, p_iov, i_iov ) < 0 )
{
msg_Err( NULL, "couldn't writev to %s (%s)",
p_output->psz_displayname, strerror(errno) );
p_output->config.psz_displayname, strerror(errno) );
}
/* Update the wallclock because writev() can take some time. */
i_wallclock = mdate();
......@@ -261,15 +311,14 @@ static void output_Flush( output_t *p_output )
*****************************************************************************/
void output_Put( output_t *p_output, block_t *p_block )
{
int i_block_cnt = ( p_output->p_addr->ss_family == AF_INET6 ) ?
NB_BLOCKS_IPV6 : NB_BLOCKS;
int i_block_cnt = output_BlockCount( p_output );
packet_t *p_packet;
p_block->i_refcount++;
if ( p_output->p_last_packet != NULL
&& p_output->p_last_packet->i_depth < i_block_cnt
&& p_output->p_last_packet->i_dts + p_output->i_max_retention
&& p_output->p_last_packet->i_dts + p_output->config.i_max_retention
> p_block->i_dts )
{
p_packet = p_output->p_last_packet;
......@@ -280,7 +329,10 @@ void output_Put( output_t *p_output, block_t *p_block )
}
else
{
p_packet = malloc( sizeof(packet_t) );
/* This isn't the cleanest allocation of the world. */
p_packet = malloc( sizeof(packet_t)
+ (i_block_cnt - 1) * sizeof(block_t *) );
p_packet->pp_blocks = &p_packet->p_blocks;
p_packet->i_depth = 0;
p_packet->p_next = NULL;
p_packet->i_dts = p_block->i_dts;
......@@ -303,11 +355,11 @@ mtime_t output_Send( void )
mtime_t i_earliest_dts = -1;
int i;
if ( output_dup.i_config & OUTPUT_VALID )
if ( output_dup.config.i_config & OUTPUT_VALID )
{
while ( output_dup.p_packets != NULL
&& output_dup.p_packets->i_dts + output_dup.i_output_latency
<= i_wallclock )
&& output_dup.p_packets->i_dts
+ output_dup.config.i_output_latency <= i_wallclock )
output_Flush( &output_dup );
if ( output_dup.p_packets != NULL )
......@@ -317,111 +369,112 @@ mtime_t output_Send( void )
for ( i = 0; i < i_nb_outputs; i++ )
{
output_t *p_output = pp_outputs[i];
if ( !( p_output->i_config & OUTPUT_VALID ) )
if ( !( p_output->config.i_config & OUTPUT_VALID ) )
continue;
while ( p_output->p_packets != NULL
&& p_output->p_packets->i_dts + p_output->i_output_latency
<= i_wallclock )
&& p_output->p_packets->i_dts
+ p_output->config.i_output_latency <= i_wallclock )
output_Flush( p_output );
if ( p_output->p_packets != NULL
&& (p_output->p_packets->i_dts + p_output->i_output_latency
< i_earliest_dts
&& (p_output->p_packets->i_dts
+ p_output->config.i_output_latency < i_earliest_dts
|| i_earliest_dts == -1) )
i_earliest_dts = p_output->p_packets->i_dts
+ p_output->i_output_latency;
+ p_output->config.i_output_latency;
}
return i_earliest_dts == -1 ? -1 : i_earliest_dts - i_wallclock;
}
/*****************************************************************************
* output_SetTTL : set the TTL of an output socket
* output_Find : find an existing output from a given output_config_t
*****************************************************************************/
void output_SetTTL( output_t *p_output, int i_ttl )
output_t *output_Find( const output_config_t *p_config )
{
if ( p_output->p_addr->ss_family == AF_INET6 )
{
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p_output->p_addr;
if ( IN6_IS_ADDR_MULTICAST( addr->sin6_addr.s6_addr ) )
setsockopt( p_output->i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
(void *)&i_ttl, sizeof(i_ttl) );
}
else if ( p_output->p_addr->ss_family == AF_INET )
socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
int i;
for ( i = 0; i < i_nb_outputs; i++ )
{
struct sockaddr_in *addr = (struct sockaddr_in *)p_output->p_addr;
if ( IN_MULTICAST( ntohl( addr->sin_addr.s_addr ) ) )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
(void *)&i_ttl, sizeof(i_ttl) );
output_t *p_output = pp_outputs[i];
if ( !(p_output->config.i_config & OUTPUT_VALID) ) continue;
if ( p_config->i_family != p_output->config.i_family ||
memcmp( &p_config->connect_addr, &p_output->config.connect_addr,
i_sockaddr_len ) ||
memcmp( &p_config->bind_addr, &p_output->config.bind_addr,
i_sockaddr_len ) )
continue;
if ( p_config->i_family == AF_INET6 &&
p_config->i_if_index_v6 != p_output->config.i_if_index_v6 )
continue;
return p_output;
}
p_output->i_ttl = i_ttl;
return NULL;
}
/*****************************************************************************
* net_Open
* output_Change : get changes from a new output_config_t
*****************************************************************************/
static int net_Open( output_t *p_output )
void output_Change( output_t *p_output, const output_config_t *p_config )
{
int i_handle = socket( p_output->p_addr->ss_family, SOCK_DGRAM, IPPROTO_UDP );
memcpy( p_output->config.pi_ssrc, p_config->pi_ssrc, 4 * sizeof(uint8_t) );
p_output->config.i_output_latency = p_config->i_output_latency;
p_output->config.i_max_retention = p_config->i_max_retention;
if ( i_handle < 0 )
if ( p_output->config.i_ttl != p_config->i_ttl )
{
msg_Err( NULL, "couldn't create socket for %s (%s)",
p_output->psz_displayname, strerror(errno) );
return -errno;
if ( p_output->config.i_family == AF_INET6 )
{
struct sockaddr_in6 *p_addr =
(struct sockaddr_in6 *)&p_output->config.connect_addr;
if ( IN6_IS_ADDR_MULTICAST( p_addr->sin6_addr.s6_addr ) )
setsockopt( p_output->i_handle, IPPROTO_IPV6,
IPV6_MULTICAST_HOPS, (void *)&p_config->i_ttl,
sizeof(p_config->i_ttl) );
}
else
{
struct sockaddr_in *p_addr =
(struct sockaddr_in *)&p_output->config.connect_addr;
if ( IN_MULTICAST( ntohl( p_addr->sin_addr.s_addr ) ) )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_MULTICAST_TTL,
(void *)&p_config->i_ttl, sizeof(p_config->i_ttl) );
}
p_output->config.i_ttl = p_config->i_ttl;
}
if ( connect( i_handle, (struct sockaddr *)p_output->p_addr,
p_output->i_addrlen ) < 0 )
if ( p_output->config.i_tos != p_config->i_tos )
{
msg_Err( NULL, "couldn't connect socket to %s (%s)",
p_output->psz_displayname, strerror(errno) );
close( i_handle );
return -errno;
if ( p_output->config.i_family == AF_INET )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_TOS,
(void *)&p_config->i_tos, sizeof(p_config->i_tos) );
p_output->config.i_tos = p_config->i_tos;
}
return i_handle;
}
/*****************************************************************************
* rtp_SetHdr
*****************************************************************************/
/*
* Reminder : RTP header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static void rtp_SetHdr( output_t *p_output, packet_t *p_packet, uint8_t *p_hdr )
{
mtime_t i_timestamp;
i_timestamp = p_output->i_ref_timestamp
+ (p_packet->i_dts - p_output->i_ref_wallclock) * 9 / 100;
p_hdr[0] = 0x80;
p_hdr[1] = 33;
p_hdr[2] = p_output->i_cc >> 8;
p_hdr[3] = p_output->i_cc & 0xff;
p_hdr[4] = (i_timestamp >> 24) & 0xff;
p_hdr[5] = (i_timestamp >> 16) & 0xff;
p_hdr[6] = (i_timestamp >> 8) & 0xff;
p_hdr[7] = i_timestamp & 0xff;
p_hdr[8] = ((uint8_t *)&i_ssrc)[0];
p_hdr[9] = ((uint8_t *)&i_ssrc)[1];
p_hdr[10] = ((uint8_t *)&i_ssrc)[2];
p_hdr[11] = ((uint8_t *)&i_ssrc)[3];
p_output->i_cc++;
if ( p_output->config.i_mtu != p_config->i_mtu
|| ((p_output->config.i_config ^ p_config->i_config) & OUTPUT_UDP) )
{
int i_block_cnt;
packet_t *p_packet = p_output->p_last_packet;
p_output->config.i_config &= ~OUTPUT_UDP;
p_output->config.i_config |= p_config->i_config & OUTPUT_UDP;
p_output->config.i_mtu = p_config->i_mtu;
i_block_cnt = output_BlockCount( p_output );
if ( p_packet != NULL && p_packet->i_depth < i_block_cnt )
{
p_packet = realloc( p_packet, sizeof(packet_t *)
+ (i_block_cnt - 1) * sizeof(block_t *) );
p_packet->pp_blocks = &p_packet->p_blocks;
}
}
}
......@@ -38,60 +38,198 @@
#include <arpa/inet.h>
#include <errno.h>
#include <bitstream/ietf/rtp.h>
#include "dvblast.h"
/*****************************************************************************
* Local declarations
*****************************************************************************/
static int i_handle;
static bool b_udp = false;
static int i_block_cnt;
static uint8_t pi_ssrc[4] = { 0, 0, 0, 0 };
static uint16_t i_cc = 0;
/*****************************************************************************
* udp_Open
*****************************************************************************/
void udp_Open( void )
{
i_handle = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in sin;
int i_family;
struct addrinfo *p_connect_ai = NULL, *p_bind_ai;
int i_if_index = 0;
in_addr_t i_if_addr = INADDR_ANY;
int i_mtu = 0;
char *psz_bind, *psz_string = strdup( psz_udp_src );
char *psz_save = psz_string;
int i = 1;
/* Parse configuration. */
if ( (psz_bind = strchr( psz_string, '@' )) != NULL )
{
*psz_bind++ = '\0';
p_connect_ai = ParseNodeService( psz_string, NULL, 0 );
}
else
psz_bind = psz_string;
p_bind_ai = ParseNodeService( psz_bind, &psz_string, DEFAULT_PORT );
if ( p_bind_ai == NULL )
{
msg_Err( NULL, "couldn't parse %s", psz_bind );
exit(EXIT_FAILURE);
}
i_family = p_bind_ai->ai_family;
if ( p_connect_ai != NULL && p_connect_ai->ai_family != i_family )
{
msg_Warn( NULL, "invalid connect address" );
freeaddrinfo( p_connect_ai );
p_connect_ai = NULL;
}
while ( (psz_string = strchr( psz_string, '/' )) != NULL )
{
*psz_string++ = '\0';
#define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) ))
#define ARG_OPTION( option ) (psz_string + strlen(option))
if ( IS_OPTION("udp") )
b_udp = true;
else if ( IS_OPTION("mtu=") )
i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 );
else if ( IS_OPTION("ifindex=") )
i_if_index = strtol( ARG_OPTION("ifindex="), NULL, 0 );
else if ( IS_OPTION("ifaddr=") )
i_if_addr = inet_addr( ARG_OPTION("ifaddr=") );
else
msg_Warn( NULL, "unrecognized option %s", psz_string );
#undef IS_OPTION
#undef ARG_OPTION
}
if ( !i_mtu )
i_mtu = i_family == AF_INET6 ? DEFAULT_IPV6_MTU : DEFAULT_IPV4_MTU;
i_block_cnt = (i_mtu - (b_udp ? 0 : RTP_HEADER_SIZE)) / TS_SIZE;
/* Do stuff. */
if ( (i_handle = socket( i_family, SOCK_DGRAM, IPPROTO_UDP )) < 0 )
{
msg_Err( NULL, "couldn't create socket (%s)", strerror(errno) );
exit(EXIT_FAILURE);
}
setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof( i ) );
/* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
* packet loss caused by scheduling problems */
i = 0x80000;
setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) );
sin.sin_family = AF_INET;
sin.sin_port = htons(i_src_port);
sin.sin_addr.s_addr = i_src_addr;
setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) );
if ( bind( i_handle, (struct sockaddr *)&sin, sizeof( sin ) ) < 0 )
if ( bind( i_handle, p_bind_ai->ai_addr, p_bind_ai->ai_addrlen ) < 0 )
{
msg_Err( NULL, "couldn't bind\n" );
msg_Err( NULL, "couldn't bind (%s)", strerror(errno) );
close( i_handle );
exit(EXIT_FAILURE);
}
/* Join the multicast group if the socket is a multicast address */
if ( IN_MULTICAST( ntohl(sin.sin_addr.s_addr)) )
if ( p_connect_ai != NULL )
{
struct ip_mreq imr;
uint16_t i_port;
if ( i_family == AF_INET6 )
i_port = ((struct sockaddr_in6 *)p_connect_ai->ai_addr)->sin6_port;
else
i_port = ((struct sockaddr_in *)p_connect_ai->ai_addr)->sin_port;
imr.imr_multiaddr.s_addr = sin.sin_addr.s_addr;
imr.imr_interface.s_addr = INADDR_ANY; /* FIXME could be an option */
if ( i_port != 0 && connect( i_handle, p_connect_ai->ai_addr,
p_connect_ai->ai_addrlen ) < 0 )
msg_Warn( NULL, "couldn't connect socket (%s)", strerror(errno) );
}
/* Join Multicast group without source filter */
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ip_mreq) ) == -1 )
/* Join the multicast group if the socket is a multicast address */
if ( i_family == AF_INET6 )
{
struct sockaddr_in6 *p_addr =
(struct sockaddr_in6 *)p_bind_ai->ai_addr;
if ( IN6_IS_ADDR_MULTICAST( p_addr->sin6_addr.s6_addr ) )
{
msg_Err( NULL, "couldn't join multicast group" );
exit(EXIT_FAILURE);
struct ipv6_mreq imr;
imr.ipv6mr_multiaddr = p_addr->sin6_addr;
imr.ipv6mr_interface = i_if_index;
if ( i_if_addr != INADDR_ANY )
msg_Warn( NULL, "ignoring ifaddr option in IPv6" );
if ( setsockopt( i_handle, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ipv6_mreq) ) < 0 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
}
else
{
struct sockaddr_in *p_addr =
(struct sockaddr_in *)p_bind_ai->ai_addr;
if ( IN_MULTICAST( ntohl(p_addr->sin_addr.s_addr)) )
{
if ( p_connect_ai != NULL )
{
/* Source-specific multicast */
struct sockaddr_in *p_src =
(struct sockaddr_in *)&p_connect_ai->ai_addr;
struct ip_mreq_source imr;
imr.imr_multiaddr = p_addr->sin_addr;
imr.imr_interface.s_addr = i_if_addr;
imr.imr_sourceaddr = p_src->sin_addr;
if ( i_if_index )
msg_Warn( NULL, "ignoring ifindex option in SSM" );
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
(char *)&imr, sizeof(struct ip_mreq_source) ) < 0 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
else if ( i_if_index )
{
/* Linux-specific interface-bound multicast */
struct ip_mreqn imr;
imr.imr_multiaddr = p_addr->sin_addr;
imr.imr_address.s_addr = i_if_addr;
imr.imr_ifindex = i_if_index;
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ip_mreqn) ) < 0 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
else
{
/* Regular multicast */
struct ip_mreq imr;
imr.imr_multiaddr = p_addr->sin_addr;
imr.imr_interface.s_addr = i_if_addr;
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&imr, sizeof(struct ip_mreq) ) == -1 )
msg_Warn( NULL, "couldn't join multicast group (%s)",
strerror(errno) );
}
}
}
msg_Dbg( NULL, "binding socket to %s:%u in %s mode",
inet_ntoa( sin.sin_addr ), i_src_port,
b_src_rawudp ? "UDP" : "RTP" );
freeaddrinfo( p_bind_ai );
if ( p_connect_ai != NULL )
freeaddrinfo( p_connect_ai );
free( psz_save );
msg_Dbg( NULL, "binding socket to %s", psz_udp_src );
}
/*****************************************************************************
......@@ -119,38 +257,68 @@ block_t *udp_Read( mtime_t i_poll_timeout )
if ( pfd.revents )
{
struct iovec p_iov[NB_BLOCKS + !b_src_rawudp];
struct iovec p_iov[i_block_cnt + 1];
block_t *p_ts, **pp_current = &p_ts;
int i = 0, i_len;
uint8_t p_rtp_hdr[RTP_SIZE];
int i_iov, i_block;
ssize_t i_len;
uint8_t p_rtp_hdr[RTP_HEADER_SIZE];
if ( !b_src_rawudp )
if ( !b_udp )
{
/* FIXME : this is wrong if RTP header > 12 bytes */
p_iov[i].iov_base = p_rtp_hdr;
p_iov[i].iov_len = RTP_SIZE;
i++;
p_iov[0].iov_base = p_rtp_hdr;
p_iov[0].iov_len = RTP_HEADER_SIZE;
i_iov = 1;
}
else
i_iov = 0;
for ( ; i < NB_BLOCKS + !b_src_rawudp; i++ )
for ( i_block = 0; i_block < i_block_cnt; i_block++ )
{
*pp_current = block_New();
p_iov[i].iov_base = (*pp_current)->p_ts;
p_iov[i].iov_len = TS_SIZE;
p_iov[i_iov].iov_base = (*pp_current)->p_ts;
p_iov[i_iov].iov_len = TS_SIZE;
pp_current = &(*pp_current)->p_next;
i_iov++;
}
pp_current = &p_ts;
if ( (i_len = readv( i_handle, p_iov, NB_BLOCKS + !b_src_rawudp )) < 0 )
if ( (i_len = readv( i_handle, p_iov, i_iov )) < 0 )
{
msg_Err( NULL, "couldn't read from network (%s)",
strerror(errno) );
i_len = 0;
msg_Err( NULL, "couldn't read from network (%s)", strerror(errno) );
goto err;
}
if ( !b_src_rawudp )
i_len -= RTP_SIZE;
int meuh = i_len;
if ( !b_udp )
{
uint8_t pi_new_ssrc[4];
if ( !rtp_check_hdr(p_rtp_hdr) )
msg_Warn( NULL, "invalid RTP packet received" );
if ( rtp_get_type(p_rtp_hdr) != RTP_TYPE_TS )
msg_Warn( NULL, "non-TS RTP packet received" );
rtp_get_ssrc(p_rtp_hdr, pi_new_ssrc);
if ( !memcmp( pi_ssrc, pi_new_ssrc, 4 * sizeof(uint8_t) ) )
{
if ( rtp_get_cc(p_rtp_hdr) != i_cc )
msg_Warn( NULL, "RTP discontinuity" );
}
else
{
struct in_addr addr;
memcpy( &addr.s_addr, pi_new_ssrc, 4 * sizeof(uint8_t) );
msg_Dbg( NULL, "new RTP source: %s", inet_ntoa( addr ) );
memcpy( pi_ssrc, pi_new_ssrc, 4 * sizeof(uint8_t) );
}
i_cc = rtp_get_cc(p_rtp_hdr) + 1;
i_len -= RTP_HEADER_SIZE;
}
i_len /= TS_SIZE;
pp_current = &p_ts;
while ( i_len && *pp_current )
{
pp_current = &(*pp_current)->p_next;
......@@ -160,12 +328,14 @@ block_t *udp_Read( mtime_t i_poll_timeout )
i_wallclock = mdate();
if ( *pp_current )
msg_Dbg( NULL, "partial buffer received" );
msg_Dbg( NULL, "partial buffer received %d", meuh );
err:
block_DeleteChain( *pp_current );
*pp_current = NULL;
return p_ts;
}
return NULL;
}
......
......@@ -29,7 +29,9 @@
#include <stdarg.h>
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
......@@ -250,3 +252,80 @@ void hexDump( uint8_t *p_data, uint32_t i_len )
free( p_outline );
}
/*****************************************************************************
* ParseNodeService: parse a host:port string
*****************************************************************************/
struct addrinfo *ParseNodeService( char *_psz_string, char **ppsz_end,
uint16_t i_default_port )
{
int i_family = AF_INET;
char psz_port_buffer[6];
char *psz_string = strdup( _psz_string );
char *psz_node, *psz_port = NULL, *psz_end;
struct addrinfo *p_res;
struct addrinfo hint;
int i_ret;
if ( psz_string[0] == '[' )
{
i_family = AF_INET6;
psz_node = psz_string + 1;
psz_end = strchr( psz_node, ']' );
if ( psz_end == NULL )
{
msg_Warn( NULL, "invalid IPv6 address %s", _psz_string );
free( psz_string );
return NULL;
}
*psz_end++ = '\0';
}
else
{
psz_node = psz_string;
psz_end = strpbrk( psz_string, "@:,/" );
}
if ( psz_end != NULL && psz_end[0] == ':' )
{
*psz_end++ = '\0';
psz_port = psz_end;
psz_end = strpbrk( psz_port, "@:,/" );
}
if ( psz_end != NULL )
{
*psz_end = '\0';
if ( ppsz_end != NULL )
*ppsz_end = _psz_string + (psz_end - psz_string);
}
else if ( ppsz_end != NULL )
*ppsz_end = _psz_string + strlen(_psz_string);
if ( i_default_port != 0 && (psz_port == NULL || !*psz_port) )
{
sprintf( psz_port_buffer, "%u", i_default_port );
psz_port = psz_port_buffer;
}
if ( psz_node[0] == '\0' )
{
free( psz_string );
return NULL;
}
memset( &hint, 0, sizeof(hint) );
hint.ai_family = i_family;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = 0;
hint.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG;
if ( (i_ret = getaddrinfo( psz_node, psz_port, NULL, &p_res )) != 0 )
{
msg_Warn( NULL, "getaddrinfo error: %s", gai_strerror(i_ret) );
free( psz_string );
return NULL;
}
free( psz_string );
return p_res;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment