Commit 75da755c authored by Christophe Massiot's avatar Christophe Massiot

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

* 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=.
parent 16c7b832
# DVBlast Makefile # DVBlast Makefile
# Customise the path of your kernel # Customise the path of your kernel
VERSION = 1.2.0 VERSION = 2.0.0
TOPDIR = `basename ${PWD}` TOPDIR = `basename ${PWD}`
CFLAGS += -Wall -O3 -fomit-frame-pointer CFLAGS += -Wall -O3 -fomit-frame-pointer
......
...@@ -7,6 +7,8 @@ Changes between 1.2 and 2.0: ...@@ -7,6 +7,8 @@ Changes between 1.2 and 2.0:
* Smooth packet output with an extra buffer * Smooth packet output with an extra buffer
* libdvbpsi runtime is no longer needed; biTStream development library * libdvbpsi runtime is no longer needed; biTStream development library
is used instead 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 * Add support for NIT in DVB compliance mode
* Syslog support with -l option * Syslog support with -l option
* Override FEC Inner with -F option * Override FEC Inner with -F option
......
...@@ -80,13 +80,26 @@ instance a multi-program transport stream. The address is specified with ...@@ -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 such a stream for instance to cross network boundaries between the
receivers and the target network. 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 Configuring outputs
=================== ===================
DVBlast reads a configuration file containing one or several lines in the DVBlast reads a configuration file containing one or several lines in the
format : format :
<IP>[:<port>][/<option>]* <always on> <SID> [<PID>,]* <IP>[:<port>][@<IP>[:<port>]][/<option>]* <always on> <SID> [<PID>,]*
For instance : For instance :
239.255.0.1:1234 1 10750 1234,1235,1236 239.255.0.1:1234 1 10750 1234,1235,1236
...@@ -105,6 +118,15 @@ For example : ...@@ -105,6 +118,15 @@ For example :
[ff12::1%eth0]:1234 1 10750 [ff12::1%eth0]:1234 1 10750
[ff15::abcd]: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 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 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, regularly reset the CAM module if it fails to descramble the service,
...@@ -117,12 +139,16 @@ Available options include : ...@@ -117,12 +139,16 @@ Available options include :
/dvb (turns on -C for a specific output) /dvb (turns on -C for a specific output)
/epg (turns on -C -e for a specific output) /epg (turns on -C -e for a specific output)
/tsid=XXX (sets the transport stream ID) /tsid=XXX (sets the transport stream ID)
/ssrc=XXX.XXX.XXX.XXX (sets the RTP synchronization source IPv4)
/retention=XXX (see -E) /retention=XXX (see -E)
/latency=XXX (see -L) /latency=XXX (see -L)
/ttl=XX (see -t) /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: 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 The optional "/udp" parameter can be used to force DVBlast to output
raw UDP stream. This functionality is provided for backwards compatibility raw UDP stream. This functionality is provided for backwards compatibility
......
...@@ -238,7 +238,7 @@ static void demux_Handle( block_t *p_ts ) ...@@ -238,7 +238,7 @@ static void demux_Handle( block_t *p_ts )
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
{ {
output_t *p_output = pp_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_timestamp = i_timestamp;
p_output->i_ref_wallclock = p_ts->i_dts; p_output->i_ref_wallclock = p_ts->i_dts;
...@@ -257,7 +257,7 @@ static void demux_Handle( block_t *p_ts ) ...@@ -257,7 +257,7 @@ static void demux_Handle( block_t *p_ts )
output_t *p_output = p_pids[i_pid].pp_outputs[i]; output_t *p_output = p_pids[i_pid].pp_outputs[i];
if ( p_output != NULL ) 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 ) ) ts_get_unitstart( p_ts->p_ts ) )
{ {
uint8_t *p_payload; uint8_t *p_payload;
...@@ -282,7 +282,7 @@ static void demux_Handle( block_t *p_ts ) ...@@ -282,7 +282,7 @@ static void demux_Handle( block_t *p_ts )
msg_Warn( NULL, msg_Warn( NULL,
"too many errors for stream %s, resetting", "too many errors for stream %s, resetting",
p_output->psz_displayname ); p_output->config.psz_displayname );
en50221_Reset(); en50221_Reset();
} }
} }
...@@ -296,7 +296,7 @@ static void demux_Handle( block_t *p_ts ) ...@@ -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 ); output_Put( &output_dup, p_ts );
p_ts->i_refcount--; p_ts->i_refcount--;
...@@ -315,44 +315,51 @@ static int IsIn( uint16_t *pi_pids, int i_nb_pids, uint16_t i_pid ) ...@@ -315,44 +315,51 @@ static int IsIn( uint16_t *pi_pids, int i_nb_pids, uint16_t i_pid )
return ( i != i_nb_pids ); return ( i != i_nb_pids );
} }
void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid, void demux_Change( output_t *p_output, const output_config_t *p_config )
uint16_t *pi_pids, int i_nb_pids )
{ {
int i;
uint16_t *pi_wanted_pids, *pi_current_pids; uint16_t *pi_wanted_pids, *pi_current_pids;
int i_nb_wanted_pids, i_nb_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 && uint16_t i_old_sid = p_output->config.i_sid;
(!p_output->b_fixed_tsid || p_output->i_tsid != i_tsid) ) 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 = p_config->i_tsid;
p_output->i_tsid = i_tsid; b_tsid_change = true;
tsid_change = 1;
} }
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) && !b_random_tsid )
if ( psi_table_validate(pp_current_pat_sections) )
p_output->i_tsid = p_output->i_tsid =
psi_table_get_tableidext(pp_current_pat_sections); 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 && if ( !b_sid_change && p_config->i_nb_pids == p_output->config.i_nb_pids &&
(!i_nb_pids || (!p_config->i_nb_pids ||
!memcmp( p_output->pi_pids, pi_pids, i_nb_pids * sizeof(uint16_t) )) ) !memcmp( p_output->config.pi_pids, p_config->pi_pids,
p_config->i_nb_pids * sizeof(uint16_t) )) )
goto out_change; goto out_change;
GetPIDS( &pi_wanted_pids, &i_nb_wanted_pids, i_sid, pi_pids, i_nb_pids ); 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, GetPIDS( &pi_current_pids, &i_nb_current_pids, i_old_sid, pi_old_pids,
p_output->pi_pids, p_output->i_nb_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++ ) for ( i = 0; i < i_nb_sids; i++ )
{ {
if ( pp_sids[i]->i_sid == i_old_sid ) 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, ...@@ -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++ ) for ( i = 0; i < i_nb_current_pids; i++ )
{ {
if ( pi_current_pids[i] != EMPTY_PID && if ( !IsIn( pi_wanted_pids, i_nb_wanted_pids, pi_current_pids[i] ) )
!IsIn( pi_wanted_pids, i_nb_wanted_pids, pi_current_pids[i] ) )
{ {
StopPID( p_output, pi_current_pids[i] ); StopPID( p_output, pi_current_pids[i] );
pid_change = 1; b_pid_change = true;
} }
} }
if ( sid_change && if ( b_sid_change && i_ca_handle && i_old_sid &&
i_ca_handle && i_old_sid && SIDIsSelected( i_old_sid ) ) SIDIsSelected( i_old_sid ) )
{ {
for ( i = 0; i < i_nb_sids; i++ ) 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, ...@@ -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++ ) for ( i = 0; i < i_nb_wanted_pids; i++ )
{ {
if ( pi_wanted_pids[i] != EMPTY_PID && if ( !IsIn( pi_current_pids, i_nb_current_pids, pi_wanted_pids[i] ) )
!IsIn( pi_current_pids, i_nb_current_pids, pi_wanted_pids[i] ) )
{ {
StartPID( p_output, pi_wanted_pids[i] ); StartPID( p_output, pi_wanted_pids[i] );
pid_change = 1; b_pid_change = true;
} }
} }
free( pi_wanted_pids ); free( pi_wanted_pids );
free( pi_current_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++ ) for ( i = 0; i < i_nb_sids; i++ )
{ {
if ( pp_sids[i]->i_sid == i_sid ) 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, ...@@ -438,27 +443,27 @@ void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
} }
} }
p_output->i_sid = i_sid; p_output->config.i_sid = i_sid;
free( p_output->pi_pids ); free( p_output->config.pi_pids );
p_output->pi_pids = malloc( sizeof(uint16_t) * i_nb_pids ); p_output->config.pi_pids = malloc( sizeof(uint16_t) * i_nb_pids );
memcpy( p_output->pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids ); memcpy( p_output->config.pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids );
p_output->i_nb_pids = i_nb_pids; p_output->config.i_nb_pids = i_nb_pids;
out_change: out_change:
if ( sid_change ) if ( b_sid_change )
{ {
NewSDT( p_output ); NewSDT( p_output );
NewNIT( p_output ); NewNIT( p_output );
NewPAT( p_output ); NewPAT( p_output );
NewPMT( p_output ); NewPMT( p_output );
} }
else if ( tsid_change ) else if ( b_tsid_change )
{ {
NewSDT( p_output ); NewSDT( p_output );
NewNIT( p_output ); NewNIT( p_output );
NewPAT( p_output ); NewPAT( p_output );
} }
else if ( pid_change ) else if ( b_pid_change )
NewPMT( p_output ); NewPMT( p_output );
} }
...@@ -578,9 +583,9 @@ static void SelectPID( uint16_t i_sid, uint16_t i_pid ) ...@@ -578,9 +583,9 @@ static void SelectPID( uint16_t i_sid, uint16_t i_pid )
int i; int i;
for ( i = 0; i < i_nb_outputs; i++ ) 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)
&& pp_outputs[i]->i_sid == i_sid && pp_outputs[i]->config.i_sid == i_sid
&& !pp_outputs[i]->i_nb_pids ) && !pp_outputs[i]->config.i_nb_pids )
StartPID( pp_outputs[i], i_pid ); StartPID( pp_outputs[i], i_pid );
} }
...@@ -589,9 +594,9 @@ static void UnselectPID( uint16_t i_sid, uint16_t i_pid ) ...@@ -589,9 +594,9 @@ static void UnselectPID( uint16_t i_sid, uint16_t i_pid )
int i; int i;
for ( i = 0; i < i_nb_outputs; i++ ) 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)
&& pp_outputs[i]->i_sid == i_sid && pp_outputs[i]->config.i_sid == i_sid
&& !pp_outputs[i]->i_nb_pids ) && !pp_outputs[i]->config.i_nb_pids )
StopPID( pp_outputs[i], i_pid ); StopPID( pp_outputs[i], i_pid );
} }
...@@ -606,8 +611,8 @@ static void SelectPSI( uint16_t i_sid, uint16_t i_pid ) ...@@ -606,8 +611,8 @@ static void SelectPSI( uint16_t i_sid, uint16_t i_pid )
p_pids[i_pid].b_pes = false; p_pids[i_pid].b_pes = false;
for ( i = 0; i < i_nb_outputs; i++ ) 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)
&& pp_outputs[i]->i_sid == i_sid ) && pp_outputs[i]->config.i_sid == i_sid )
SetPID( i_pid ); SetPID( i_pid );
} }
...@@ -621,8 +626,8 @@ static void UnselectPSI( uint16_t i_sid, uint16_t 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 ); &p_pids[i_pid].i_psi_buffer_used );
for ( i = 0; i < i_nb_outputs; i++ ) 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)
&& pp_outputs[i]->i_sid == i_sid ) && pp_outputs[i]->config.i_sid == i_sid )
UnsetPID( i_pid ); UnsetPID( i_pid );
} }
...@@ -757,7 +762,7 @@ static void SendPAT( mtime_t i_dts ) ...@@ -757,7 +762,7 @@ static void SendPAT( mtime_t i_dts )
{ {
output_t *p_output = pp_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; continue;
if ( p_output->p_pat_section == NULL && if ( p_output->p_pat_section == NULL &&
...@@ -796,8 +801,8 @@ static void SendPMT( sid_t *p_sid, mtime_t i_dts ) ...@@ -796,8 +801,8 @@ static void SendPMT( sid_t *p_sid, mtime_t i_dts )
{ {
output_t *p_output = pp_outputs[i]; output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) if ( (p_output->config.i_config & OUTPUT_VALID)
&& p_output->i_sid == p_sid->i_sid && p_output->config.i_sid == p_sid->i_sid
&& p_output->p_pmt_section != NULL ) && p_output->p_pmt_section != NULL )
OutputPSISection( p_output, p_output->p_pmt_section, OutputPSISection( p_output, p_output->p_pmt_section,
p_sid->i_pmt_pid, &p_output->i_pmt_cc, i_dts, p_sid->i_pmt_pid, &p_output->i_pmt_cc, i_dts,
...@@ -816,8 +821,8 @@ static void SendNIT( mtime_t i_dts ) ...@@ -816,8 +821,8 @@ static void SendNIT( mtime_t i_dts )
{ {
output_t *p_output = pp_outputs[i]; output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB) && (p_output->config.i_config & OUTPUT_DVB)
&& p_output->p_nit_section != NULL ) && p_output->p_nit_section != NULL )
OutputPSISection( p_output, p_output->p_nit_section, NIT_PID, OutputPSISection( p_output, p_output->p_nit_section, NIT_PID,
&p_output->i_nit_cc, i_dts, NULL, NULL ); &p_output->i_nit_cc, i_dts, NULL, NULL );
...@@ -835,8 +840,8 @@ static void SendSDT( mtime_t i_dts ) ...@@ -835,8 +840,8 @@ static void SendSDT( mtime_t i_dts )
{ {
output_t *p_output = pp_outputs[i]; output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB) && (p_output->config.i_config & OUTPUT_DVB)
&& p_output->p_sdt_section != NULL ) && p_output->p_sdt_section != NULL )
OutputPSISection( p_output, p_output->p_sdt_section, SDT_PID, OutputPSISection( p_output, p_output->p_sdt_section, SDT_PID,
&p_output->i_sdt_cc, i_dts, NULL, NULL ); &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 ) ...@@ -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]; output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB) && (p_output->config.i_config & OUTPUT_DVB)
&& (!b_epg || (p_output->i_config & OUTPUT_EPG)) && (!b_epg || (p_output->config.i_config & OUTPUT_EPG))
&& p_output->i_sid == p_sid->i_sid ) && p_output->config.i_sid == p_sid->i_sid )
{ {
if ( eit_get_tsid( p_eit ) != p_output->i_tsid ) if ( eit_get_tsid( p_eit ) != p_output->i_tsid )
{ {
...@@ -901,8 +906,8 @@ static void SendTDT( block_t *p_ts ) ...@@ -901,8 +906,8 @@ static void SendTDT( block_t *p_ts )
{ {
output_t *p_output = pp_outputs[i]; output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) if ( (p_output->config.i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB) && (p_output->config.i_config & OUTPUT_DVB)
&& p_output->p_sdt_section != NULL ) && p_output->p_sdt_section != NULL )
output_Put( p_output, p_ts ); output_Put( p_output, p_ts );
} }
...@@ -920,11 +925,11 @@ static void NewPAT( output_t *p_output ) ...@@ -920,11 +925,11 @@ static void NewPAT( output_t *p_output )
p_output->p_pat_section = NULL; p_output->p_pat_section = NULL;
p_output->i_pat_version++; 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; if ( !psi_table_validate(pp_current_pat_sections) ) return;
p_program = pat_table_find_program( pp_current_pat_sections, p_program = pat_table_find_program( pp_current_pat_sections,
p_output->i_sid ); p_output->config.i_sid );
if ( p_program == NULL ) return; if ( p_program == NULL ) return;
p = p_output->p_pat_section = psi_allocate(); p = p_output->p_pat_section = psi_allocate();
...@@ -938,7 +943,7 @@ static void NewPAT( output_t *p_output ) ...@@ -938,7 +943,7 @@ static void NewPAT( output_t *p_output )
p = pat_get_program( p, 0 ); p = pat_get_program( p, 0 );
patn_init( p ); 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 ) ); patn_set_pid( p, patn_get_pid( p_program ) );
psi_set_crc( p_output->p_pat_section ); psi_set_crc( p_output->p_pat_section );
} }
...@@ -989,10 +994,10 @@ static void NewPMT( output_t *p_output ) ...@@ -989,10 +994,10 @@ static void NewPMT( output_t *p_output )
p_output->p_pmt_section = NULL; p_output->p_pmt_section = NULL;
p_output->i_pmt_version++; 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++ ) 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; break;
if ( i == i_nb_sids ) return; if ( i == i_nb_sids ) return;
...@@ -1003,7 +1008,7 @@ static void NewPMT( output_t *p_output ) ...@@ -1003,7 +1008,7 @@ static void NewPMT( output_t *p_output )
p = p_output->p_pmt_section = psi_allocate(); p = p_output->p_pmt_section = psi_allocate();
pmt_init( p ); pmt_init( p );
psi_set_length( p, PSI_MAX_SIZE ); 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_version( p, p_output->i_pmt_version );
psi_set_current( p ); psi_set_current( p );
pmt_set_pcrpid( p, pmt_get_pcrpid( p_current_pmt ) ); pmt_set_pcrpid( p, pmt_get_pcrpid( p_current_pmt ) );
...@@ -1017,8 +1022,9 @@ static void NewPMT( output_t *p_output ) ...@@ -1017,8 +1022,9 @@ static void NewPMT( output_t *p_output )
uint16_t i_pid = pmtn_get_pid( p_current_es ); uint16_t i_pid = pmtn_get_pid( p_current_es );
j++; j++;
if ( (p_output->i_nb_pids || !PIDWouldBeSelected( p_current_es )) if ( (p_output->config.i_nb_pids || !PIDWouldBeSelected( p_current_es ))
&& !IsIn( p_output->pi_pids, p_output->i_nb_pids, i_pid ) ) && !IsIn( p_output->config.pi_pids, p_output->config.i_nb_pids,
i_pid ) )
continue; continue;
p_es = pmt_get_es( p, k ); p_es = pmt_get_es( p, k );
...@@ -1055,7 +1061,7 @@ static void NewNIT( output_t *p_output ) ...@@ -1055,7 +1061,7 @@ static void NewNIT( output_t *p_output )
p_output->p_nit_section = NULL; p_output->p_nit_section = NULL;
p_output->i_nit_version++; 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; if ( !psi_table_validate(pp_current_pat_sections) ) return;
p = p_output->p_nit_section = psi_allocate(); p = p_output->p_nit_section = psi_allocate();
...@@ -1113,11 +1119,11 @@ static void NewSDT( output_t *p_output ) ...@@ -1113,11 +1119,11 @@ static void NewSDT( output_t *p_output )
p_output->p_sdt_section = NULL; p_output->p_sdt_section = NULL;
p_output->i_sdt_version++; 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; if ( !psi_table_validate(pp_current_sdt_sections) ) return;
p_current_service = sdt_table_find_service( pp_current_sdt_sections, 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 ) if ( p_current_service == NULL )
{ {
...@@ -1145,7 +1151,7 @@ static void NewSDT( output_t *p_output ) ...@@ -1145,7 +1151,7 @@ static void NewSDT( output_t *p_output )
p_service = sdt_get_service( p, 0 ); p_service = sdt_get_service( p, 0 );
sdtn_init( p_service ); 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) ) if ( sdtn_get_eitschedule(p_current_service) )
sdtn_set_eitschedule(p_service); sdtn_set_eitschedule(p_service);
if ( sdtn_get_eitpresent(p_current_service) ) if ( sdtn_get_eitpresent(p_current_service) )
...@@ -1175,8 +1181,8 @@ static void Update##table( uint16_t i_sid ) \ ...@@ -1175,8 +1181,8 @@ static void Update##table( uint16_t i_sid ) \
int i; \ int i; \
\ \
for ( i = 0; i < i_nb_outputs; i++ ) \ 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 ) \
&& pp_outputs[i]->i_sid == i_sid ) \ && pp_outputs[i]->config.i_sid == i_sid ) \
New##table( pp_outputs[i] ); \ New##table( pp_outputs[i] ); \
} }
...@@ -1196,7 +1202,8 @@ static void UpdateTSID(void) ...@@ -1196,7 +1202,8 @@ static void UpdateTSID(void)
{ {
output_t *p_output = pp_outputs[i]; 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; p_output->i_tsid = i_tsid;
NewNIT( p_output ); NewNIT( p_output );
...@@ -1212,8 +1219,8 @@ static bool SIDIsSelected( uint16_t i_sid ) ...@@ -1212,8 +1219,8 @@ static bool SIDIsSelected( uint16_t i_sid )
int i; int i;
for ( i = 0; i < i_nb_outputs; i++ ) 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 )
&& pp_outputs[i]->i_sid == i_sid ) && pp_outputs[i]->config.i_sid == i_sid )
return true; return true;
return false; return false;
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#endif #endif
#include <bitstream/dvb/si.h> #include <bitstream/dvb/si.h>
#include <bitstream/ietf/rtp.h>
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
...@@ -52,10 +53,9 @@ ...@@ -52,10 +53,9 @@
mtime_t i_wallclock = 0; mtime_t i_wallclock = 0;
output_t **pp_outputs = NULL; output_t **pp_outputs = NULL;
int i_nb_outputs = 0; int i_nb_outputs = 0;
output_t output_dup = { 0 }; output_t output_dup;
static char *psz_conf_file = NULL; static char *psz_conf_file = NULL;
char *psz_srv_socket = NULL; char *psz_srv_socket = NULL;
in_addr_t i_ssrc = 0;
static int i_priority = -1; static int i_priority = -1;
int i_adapter = 0; int i_adapter = 0;
int i_fenum = 0; int i_fenum = 0;
...@@ -73,16 +73,16 @@ int b_random_tsid = 0; ...@@ -73,16 +73,16 @@ int b_random_tsid = 0;
uint16_t i_network_id = 0xffff; uint16_t i_network_id = 0xffff;
uint8_t *p_network_name; uint8_t *p_network_name;
size_t i_network_name_size; size_t i_network_name_size;
volatile int b_hup_received = 0; char *psz_udp_src = NULL;
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;
int i_asi_adapter = 0; int i_asi_adapter = 0;
const char *psz_native_charset = "UTF-8"; const char *psz_native_charset = "UTF-8";
const char *psz_dvb_charset = "ISO_8859-1"; 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_udp_global = 0;
static int b_dvb_global = 0; static int b_dvb_global = 0;
static int b_epg_global = 0; static int b_epg_global = 0;
...@@ -98,7 +98,145 @@ void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL; ...@@ -98,7 +98,145 @@ void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL;
/***************************************************************************** /*****************************************************************************
* Configuration files * 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; FILE *p_file;
char psz_line[2048]; char psz_line[2048];
...@@ -118,130 +256,40 @@ static void ReadConfiguration( char *psz_file ) ...@@ -118,130 +256,40 @@ static void ReadConfiguration( char *psz_file )
while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL ) while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL )
{ {
output_t *p_output = NULL; output_config_t config;
char *psz_parser, *psz_token, *psz_token2; output_t *p_output;
struct addrinfo *p_addr; char *psz_token, *psz_parser;
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 );
if ( !strncmp( psz_line, "#", 1 ) ) if ( !strncmp( psz_line, "#", 1 ) )
continue; continue;
psz_token = strtok_r( psz_line, "\t\n ", &psz_parser ); config_Defaults( &config );
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 ) );
memset( &ai_hints, 0, sizeof( ai_hints ) ); psz_token = strtok_r( psz_line, "\t\n ", &psz_parser );
ai_hints.ai_socktype = SOCK_DGRAM; if ( psz_token == NULL || !config_ParseHost( &config, psz_token ))
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
{ {
if ( (psz_token2 = strrchr( psz_token, ':' )) != NULL ) config_Free( &config );
{ continue;
*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 );
} }
psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token == NULL ) if ( psz_token == NULL )
{
config_Free( &config );
continue; continue;
}
if( atoi( psz_token ) == 1 ) if( atoi( psz_token ) == 1 )
i_config |= OUTPUT_WATCH; config.i_config |= OUTPUT_WATCH;
else else
i_config &= ~OUTPUT_WATCH; config.i_config &= ~OUTPUT_WATCH;
psz_token = strtok_r( NULL, "\t\n ", &psz_parser ); psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token == NULL ) if ( psz_token == NULL )
{
config_Free( &config );
continue; 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 ); psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
if ( psz_token != NULL ) if ( psz_token != NULL )
...@@ -252,69 +300,33 @@ static void ReadConfiguration( char *psz_file ) ...@@ -252,69 +300,33 @@ static void ReadConfiguration( char *psz_file )
psz_token = strtok_r( psz_token, ",", &psz_parser ); psz_token = strtok_r( psz_token, ",", &psz_parser );
if ( psz_token == NULL ) if ( psz_token == NULL )
break; break;
pi_pids = realloc( pi_pids, config.pi_pids = realloc( config.pi_pids,
(i_nb_pids + 1) * sizeof(uint16_t) ); (config.i_nb_pids + 1) * sizeof(uint16_t) );
pi_pids[i_nb_pids++] = strtol(psz_token, NULL, 0); config.pi_pids[config.i_nb_pids++] = strtol(psz_token, NULL, 0);
psz_token = NULL; psz_token = NULL;
} }
} }
msg_Dbg( NULL, config_Print( &config );
"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] );
for ( i = 0; i < i_nb_outputs; i++ ) p_output = output_Find( &config );
{
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;
if ( ( p_esad->sin_addr.s_addr == p_nsad->sin_addr.s_addr ) && if ( p_output == NULL )
( p_esad->sin_port == p_nsad->sin_port ) ) p_output = output_Create( &config );
{
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 ) if ( p_output != NULL )
{ {
p_output->i_config = OUTPUT_VALID | OUTPUT_STILL_PRESENT | i_config; free( p_output->config.psz_displayname );
p_output->i_output_latency = i_latency; p_output->config.psz_displayname = strdup( config.psz_displayname );
p_output->i_max_retention = i_retention;
demux_Change( p_output, i_tsid, i_sid, pi_pids, i_nb_pids ); output_Change( p_output, &config );
if ( p_output->i_ttl != i_ttl ) demux_Change( p_output, &config );
output_SetTTL( p_output, i_ttl );
p_output->config.i_config = OUTPUT_VALID | OUTPUT_STILL_PRESENT |
config.i_config;
} }
free( psz_displayname ); config_Free( &config );
free( pi_pids );
freeaddrinfo( p_addr );
} }
fclose( p_file ); fclose( p_file );
...@@ -322,16 +334,20 @@ static void ReadConfiguration( char *psz_file ) ...@@ -322,16 +334,20 @@ static void ReadConfiguration( char *psz_file )
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
{ {
output_t *p_output = pp_outputs[i]; output_t *p_output = pp_outputs[i];
output_config_t config;
if ( (p_output->i_config & OUTPUT_VALID) && config_Init( &config );
!(p_output->i_config & OUTPUT_STILL_PRESENT) )
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 ); msg_Dbg( NULL, "closing %s", p_output->config.psz_displayname );
demux_Change( pp_outputs[i], -1, 0, NULL, 0 ); demux_Change( p_output, &config );
output_Close( pp_outputs[i] ); 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() ...@@ -357,7 +373,7 @@ static void DisplayVersion()
*****************************************************************************/ *****************************************************************************/
void usage() 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, "Input:" );
msg_Raw( NULL, " -a --adapter <adapter>" ); msg_Raw( NULL, " -a --adapter <adapter>" );
...@@ -411,6 +427,7 @@ int main( int i_argc, char **pp_argv ) ...@@ -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"; const char *psz_network_name = "DVBlast - http://www.videolan.org/projects/dvblast.html";
char *p_network_name_tmp = NULL; char *p_network_name_tmp = NULL;
size_t i_network_name_tmp_size; size_t i_network_name_tmp_size;
char *psz_dup_config = NULL;
mtime_t i_poll_timeout = MAX_POLL_TIMEOUT; mtime_t i_poll_timeout = MAX_POLL_TIMEOUT;
struct sched_param param; struct sched_param param;
int i_error; int i_error;
...@@ -510,7 +527,7 @@ int main( int i_argc, char **pp_argv ) ...@@ -510,7 +527,7 @@ int main( int i_argc, char **pp_argv )
struct in_addr maddr; struct in_addr maddr;
if ( !inet_aton( optarg, &maddr ) ) if ( !inet_aton( optarg, &maddr ) )
usage(); usage();
i_ssrc = maddr.s_addr; memcpy( pi_ssrc_global, &maddr.s_addr, 4 * sizeof(uint8_t) );
break; break;
} }
...@@ -585,105 +602,11 @@ int main( int i_argc, char **pp_argv ) ...@@ -585,105 +602,11 @@ int main( int i_argc, char **pp_argv )
break; break;
case 'd': case 'd':
{ psz_dup_config = optarg;
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 );
break; break;
}
case 'D': case 'D':
{ psz_udp_src = optarg;
char *psz_token;
struct in_addr maddr;
if ( pf_Open != NULL ) if ( pf_Open != NULL )
usage(); usage();
if ( psz_srv_socket != NULL ) if ( psz_srv_socket != NULL )
...@@ -691,27 +614,11 @@ int main( int i_argc, char **pp_argv ) ...@@ -691,27 +614,11 @@ int main( int i_argc, char **pp_argv )
msg_Err( NULL, "-r is only available for linux-dvb input" ); msg_Err( NULL, "-r is only available for linux-dvb input" );
usage(); usage();
} }
pf_Open = udp_Open; pf_Open = udp_Open;
pf_Read = udp_Read; pf_Read = udp_Read;
pf_SetFilter = udp_SetFilter; pf_SetFilter = udp_SetFilter;
pf_UnsetFilter = udp_UnsetFilter; 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; break;
}
case 'A': case 'A':
i_asi_adapter = strtol( optarg, NULL, 0 ); i_asi_adapter = strtol( optarg, NULL, 0 );
...@@ -789,6 +696,23 @@ int main( int i_argc, char **pp_argv ) ...@@ -789,6 +696,23 @@ int main( int i_argc, char **pp_argv )
b_dvb_global = 1; 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 ) ) if ( strcasecmp( psz_native_charset, psz_dvb_charset ) )
{ {
#ifdef HAVE_ICONV #ifdef HAVE_ICONV
...@@ -843,7 +767,7 @@ int main( int i_argc, char **pp_argv ) ...@@ -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 ) if ( psz_srv_socket != NULL )
comm_Open(); comm_Open();
...@@ -856,7 +780,7 @@ int main( int i_argc, char **pp_argv ) ...@@ -856,7 +780,7 @@ int main( int i_argc, char **pp_argv )
{ {
b_hup_received = 0; b_hup_received = 0;
msg_Warn( NULL, "HUP received, reloading" ); msg_Warn( NULL, "HUP received, reloading" );
ReadConfiguration( psz_conf_file ); config_ReadFile( psz_conf_file );
} }
p_ts = pf_Read( i_poll_timeout ); p_ts = pf_Read( i_poll_timeout );
......
...@@ -30,10 +30,8 @@ ...@@ -30,10 +30,8 @@
#define DEFAULT_PORT 3001 #define DEFAULT_PORT 3001
#define TS_SIZE 188 #define TS_SIZE 188
#define NB_BLOCKS 7 #define DEFAULT_IPV4_MTU 1500
#define NB_BLOCKS_IPV6 6 // assume MTU of 1280 bytes for IPv6 #define DEFAULT_IPV6_MTU 1280
#define RTP_SIZE 12
#define EMPTY_PID 8192
#define PADDING_PID 8191 #define PADDING_PID 8191
#define WATCHDOG_WAIT 10000000LL #define WATCHDOG_WAIT 10000000LL
#define MAX_ERRORS 1000 #define MAX_ERRORS 1000
...@@ -74,14 +72,35 @@ typedef struct block_t ...@@ -74,14 +72,35 @@ typedef struct block_t
typedef struct packet_t packet_t; typedef struct packet_t packet_t;
typedef struct output_t typedef struct output_config_t
{ {
/* address information, protocol agnostic */ /* identity */
struct sockaddr_storage *p_addr; int i_family;
socklen_t i_addrlen; struct sockaddr_storage connect_addr;
struct sockaddr_storage bind_addr;
int i_if_index_v6;
/* display string */ /* common config */
char *psz_displayname; 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 */ /* output */
int i_handle; int i_handle;
...@@ -103,17 +122,7 @@ typedef struct output_t ...@@ -103,17 +122,7 @@ typedef struct output_t
uint8_t i_sdt_version, i_sdt_cc; uint8_t i_sdt_version, i_sdt_cc;
block_t *p_eit_ts_buffer; block_t *p_eit_ts_buffer;
uint8_t i_eit_ts_buffer_offset, i_eit_cc; 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; uint16_t i_tsid;
bool b_fixed_tsid;
mtime_t i_output_latency, i_max_retention;
uint8_t i_config;
} output_t; } output_t;
extern int i_syslog; extern int i_syslog;
...@@ -122,7 +131,6 @@ extern output_t **pp_outputs; ...@@ -122,7 +131,6 @@ extern output_t **pp_outputs;
extern int i_nb_outputs; extern int i_nb_outputs;
extern output_t output_dup; extern output_t output_dup;
extern char *psz_srv_socket; extern char *psz_srv_socket;
extern in_addr_t i_ssrc;
extern int i_adapter; extern int i_adapter;
extern int i_fenum; extern int i_fenum;
extern int i_frequency; extern int i_frequency;
...@@ -142,9 +150,7 @@ extern size_t i_network_name_size; ...@@ -142,9 +150,7 @@ extern size_t i_network_name_size;
extern mtime_t i_wallclock; extern mtime_t i_wallclock;
extern volatile int b_hup_received; extern volatile int b_hup_received;
extern int i_comm_fd; extern int i_comm_fd;
extern uint16_t i_src_port; extern char *psz_udp_src;
extern in_addr_t i_src_addr;
extern int b_src_rawudp;
extern int i_asi_adapter; extern int i_asi_adapter;
extern const char *psz_native_charset; extern const char *psz_native_charset;
extern const char *psz_dvb_charset; extern const char *psz_dvb_charset;
...@@ -158,6 +164,10 @@ extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ); ...@@ -158,6 +164,10 @@ extern void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid );
* Prototypes * 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 */ /* Connect/Disconnect from syslogd */
void msg_Connect( const char *ident ); void msg_Connect( const char *ident );
void msg_Disconnect( void ); void msg_Disconnect( void );
...@@ -173,6 +183,8 @@ void msg_Raw( void *_unused, const char *psz_format, ... ); ...@@ -173,6 +183,8 @@ void msg_Raw( void *_unused, const char *psz_format, ... );
mtime_t mdate( void ); mtime_t mdate( void );
void msleep( mtime_t delay ); void msleep( mtime_t delay );
void hexDump( uint8_t *p_data, uint32_t i_len ); 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_Open( void );
void dvb_Reset( void ); void dvb_Reset( void );
...@@ -193,21 +205,20 @@ void asi_UnsetFilter( int i_fd, uint16_t i_pid ); ...@@ -193,21 +205,20 @@ void asi_UnsetFilter( int i_fd, uint16_t i_pid );
void demux_Open( void ); void demux_Open( void );
void demux_Run( block_t *p_ts ); void demux_Run( block_t *p_ts );
void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid, void demux_Change( output_t *p_output, const output_config_t *p_config );
uint16_t *pi_pids, int i_nb_pids );
void demux_ResendCAPMTs( void ); void demux_ResendCAPMTs( void );
bool demux_PIDIsSelected( uint16_t i_pid ); bool demux_PIDIsSelected( uint16_t i_pid );
char *demux_Iconv(void *_unused, const char *psz_encoding, char *demux_Iconv(void *_unused, const char *psz_encoding,
char *p_string, size_t i_length); char *p_string, size_t i_length);
output_t *output_Create( 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 char *psz_displayname, int output_Init( output_t *p_output, const output_config_t *p_config );
struct addrinfo *p_ai );
void output_Close( output_t *p_output ); void output_Close( output_t *p_output );
void output_Put( output_t *p_output, block_t *p_block ); void output_Put( output_t *p_output, block_t *p_block );
mtime_t output_Send( void ); 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_Open( void );
void comm_Read( void ); void comm_Read( void );
......
...@@ -39,16 +39,19 @@ ...@@ -39,16 +39,19 @@
#include "dvblast.h" #include "dvblast.h"
#include <bitstream/mpeg/ts.h> #include <bitstream/mpeg/ts.h>
#include <bitstream/ietf/rtp.h>
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
struct packet_t struct packet_t
{ {
block_t *pp_blocks[NB_BLOCKS];
int i_depth;
mtime_t i_dts;
struct packet_t *p_next; 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] = { static uint8_t p_pad_ts[TS_SIZE] = {
...@@ -70,21 +73,17 @@ 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 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; int i;
output_t *p_output = NULL; output_t *p_output = NULL;
for ( i = 0; i < i_nb_outputs; i++ ) 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]; p_output = pp_outputs[i];
break; break;
...@@ -94,30 +93,31 @@ output_t *output_Create( const char *psz_displayname, struct addrinfo *p_ai ) ...@@ -94,30 +93,31 @@ output_t *output_Create( const char *psz_displayname, struct addrinfo *p_ai )
if ( p_output == NULL ) if ( p_output == NULL )
{ {
p_output = malloc( sizeof(output_t) ); p_output = malloc( sizeof(output_t) );
memset( p_output, 0, sizeof(output_t) );
i_nb_outputs++; i_nb_outputs++;
pp_outputs = realloc( pp_outputs, i_nb_outputs * sizeof(output_t *) ); pp_outputs = realloc( pp_outputs, i_nb_outputs * sizeof(output_t *) );
pp_outputs[i] = p_output; 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 NULL;
return p_output; return p_output;
} }
/***************************************************************************** /*****************************************************************************
* output_Init * output_Init : set up the output initial config
*****************************************************************************/ *****************************************************************************/
int output_Init( output_t *p_output, const char *psz_displayname, int output_Init( output_t *p_output, const output_config_t *p_config )
struct addrinfo *p_ai )
{ {
p_output->i_sid = 0; socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ?
p_output->p_packets = p_output->p_last_packet = NULL; sizeof(struct sockaddr_in) :
p_output->pi_pids = NULL; sizeof(struct sockaddr_in6);
p_output->i_nb_pids = 0;
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_cc = rand() & 0xffff;
p_output->i_pat_cc = rand() & 0xf; p_output->i_pat_cc = rand() & 0xf;
p_output->i_pmt_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, ...@@ -133,36 +133,66 @@ int output_Init( output_t *p_output, const char *psz_displayname,
p_output->p_nit_section = NULL; p_output->p_nit_section = NULL;
p_output->p_sdt_section = NULL; p_output->p_sdt_section = NULL;
p_output->p_eit_ts_buffer = NULL; p_output->p_eit_ts_buffer = NULL;
p_output->i_eit_ts_buffer_offset = 0;
if ( b_random_tsid ) if ( b_random_tsid )
{
p_output->i_tsid = rand() & 0xffff; p_output->i_tsid = rand() & 0xffff;
p_output->b_fixed_tsid = true;
} /* Init socket-related fields */
else 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; msg_Err( NULL, "couldn't create socket (%s)", strerror(errno) );
p_output->b_fixed_tsid = false; 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; if ( p_config->bind_addr.ss_family != AF_UNSPEC )
p_output->i_ttl = 0; {
p_output->psz_displayname = strdup( psz_displayname ); 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; if ( p_config->i_family == AF_INET )
p_output->p_addr = malloc( p_output->i_addrlen ); {
memcpy( p_output->p_addr, p_ai->ai_addr, struct sockaddr_in *p_connect_addr =
p_output->i_addrlen ); (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; msg_Err( NULL, "couldn't connect socket (%s)", strerror(errno) );
return -1; 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; return 0;
} }
...@@ -189,15 +219,27 @@ void output_Close( output_t *p_output ) ...@@ -189,15 +219,27 @@ void output_Close( output_t *p_output )
} }
p_output->p_packets = p_output->p_last_packet = NULL; 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_pat_section );
free( p_output->p_pmt_section ); free( p_output->p_pmt_section );
free( p_output->p_nit_section ); free( p_output->p_nit_section );
free( p_output->p_sdt_section ); free( p_output->p_sdt_section );
free( p_output->p_eit_ts_buffer ); free( p_output->p_eit_ts_buffer );
free( p_output->p_addr ); p_output->config.i_config &= ~OUTPUT_VALID;
p_output->i_config &= ~OUTPUT_VALID;
close( p_output->i_handle ); 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 ) ...@@ -206,17 +248,25 @@ void output_Close( output_t *p_output )
static void output_Flush( output_t *p_output ) static void output_Flush( output_t *p_output )
{ {
packet_t *p_packet = p_output->p_packets; packet_t *p_packet = p_output->p_packets;
struct iovec p_iov[NB_BLOCKS + 1]; int i_block_cnt = output_BlockCount( p_output );
uint8_t p_rtp_hdr[RTP_SIZE]; struct iovec p_iov[i_block_cnt + 1];
int i_block_cnt = ( p_output->p_addr->ss_family == AF_INET6 ) ? uint8_t p_rtp_hdr[RTP_HEADER_SIZE];
NB_BLOCKS_IPV6 : NB_BLOCKS;
int i_iov, i_block; 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_base = p_rtp_hdr;
p_iov[0].iov_len = sizeof(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; i_iov = 1;
} }
else else
...@@ -239,7 +289,7 @@ static void output_Flush( output_t *p_output ) ...@@ -239,7 +289,7 @@ static void output_Flush( output_t *p_output )
if ( writev( p_output->i_handle, p_iov, i_iov ) < 0 ) if ( writev( p_output->i_handle, p_iov, i_iov ) < 0 )
{ {
msg_Err( NULL, "couldn't writev to %s (%s)", 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. */ /* Update the wallclock because writev() can take some time. */
i_wallclock = mdate(); i_wallclock = mdate();
...@@ -261,15 +311,14 @@ static void output_Flush( output_t *p_output ) ...@@ -261,15 +311,14 @@ static void output_Flush( output_t *p_output )
*****************************************************************************/ *****************************************************************************/
void output_Put( output_t *p_output, block_t *p_block ) void output_Put( output_t *p_output, block_t *p_block )
{ {
int i_block_cnt = ( p_output->p_addr->ss_family == AF_INET6 ) ? int i_block_cnt = output_BlockCount( p_output );
NB_BLOCKS_IPV6 : NB_BLOCKS;
packet_t *p_packet; packet_t *p_packet;
p_block->i_refcount++; p_block->i_refcount++;
if ( p_output->p_last_packet != NULL if ( p_output->p_last_packet != NULL
&& p_output->p_last_packet->i_depth < i_block_cnt && 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_block->i_dts )
{ {
p_packet = p_output->p_last_packet; p_packet = p_output->p_last_packet;
...@@ -280,7 +329,10 @@ void output_Put( output_t *p_output, block_t *p_block ) ...@@ -280,7 +329,10 @@ void output_Put( output_t *p_output, block_t *p_block )
} }
else 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->i_depth = 0;
p_packet->p_next = NULL; p_packet->p_next = NULL;
p_packet->i_dts = p_block->i_dts; p_packet->i_dts = p_block->i_dts;
...@@ -303,11 +355,11 @@ mtime_t output_Send( void ) ...@@ -303,11 +355,11 @@ mtime_t output_Send( void )
mtime_t i_earliest_dts = -1; mtime_t i_earliest_dts = -1;
int i; int i;
if ( output_dup.i_config & OUTPUT_VALID ) if ( output_dup.config.i_config & OUTPUT_VALID )
{ {
while ( output_dup.p_packets != NULL while ( output_dup.p_packets != NULL
&& output_dup.p_packets->i_dts + output_dup.i_output_latency && output_dup.p_packets->i_dts
<= i_wallclock ) + output_dup.config.i_output_latency <= i_wallclock )
output_Flush( &output_dup ); output_Flush( &output_dup );
if ( output_dup.p_packets != NULL ) if ( output_dup.p_packets != NULL )
...@@ -317,111 +369,112 @@ mtime_t output_Send( void ) ...@@ -317,111 +369,112 @@ mtime_t output_Send( void )
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
{ {
output_t *p_output = pp_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; continue;
while ( p_output->p_packets != NULL while ( p_output->p_packets != NULL
&& p_output->p_packets->i_dts + p_output->i_output_latency && p_output->p_packets->i_dts
<= i_wallclock ) + p_output->config.i_output_latency <= i_wallclock )
output_Flush( p_output ); output_Flush( p_output );
if ( p_output->p_packets != NULL if ( p_output->p_packets != NULL
&& (p_output->p_packets->i_dts + p_output->i_output_latency && (p_output->p_packets->i_dts
< i_earliest_dts + p_output->config.i_output_latency < i_earliest_dts
|| i_earliest_dts == -1) ) || i_earliest_dts == -1) )
i_earliest_dts = p_output->p_packets->i_dts 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; 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 ) socklen_t i_sockaddr_len = (p_config->i_family == AF_INET) ?
{ sizeof(struct sockaddr_in) :
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p_output->p_addr; sizeof(struct sockaddr_in6);
if ( IN6_IS_ADDR_MULTICAST( addr->sin6_addr.s6_addr ) ) int i;
setsockopt( p_output->i_handle, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
(void *)&i_ttl, sizeof(i_ttl) ); for ( i = 0; i < i_nb_outputs; i++ )
}
else if ( p_output->p_addr->ss_family == AF_INET )
{ {
struct sockaddr_in *addr = (struct sockaddr_in *)p_output->p_addr; output_t *p_output = pp_outputs[i];
if ( IN_MULTICAST( ntohl( addr->sin_addr.s_addr ) ) )
setsockopt( p_output->i_handle, IPPROTO_IP, IP_MULTICAST_TTL, if ( !(p_output->config.i_config & OUTPUT_VALID) ) continue;
(void *)&i_ttl, sizeof(i_ttl) );
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)", if ( p_output->config.i_family == AF_INET6 )
p_output->psz_displayname, strerror(errno) ); {
return -errno; 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, if ( p_output->config.i_tos != p_config->i_tos )
p_output->i_addrlen ) < 0 )
{ {
msg_Err( NULL, "couldn't connect socket to %s (%s)", if ( p_output->config.i_family == AF_INET )
p_output->psz_displayname, strerror(errno) ); setsockopt( p_output->i_handle, IPPROTO_IP, IP_TOS,
close( i_handle ); (void *)&p_config->i_tos, sizeof(p_config->i_tos) );
return -errno; p_output->config.i_tos = p_config->i_tos;
} }
return i_handle; 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;
* rtp_SetHdr 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;
* Reminder : RTP header p_output->config.i_mtu = p_config->i_mtu;
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 i_block_cnt = output_BlockCount( p_output );
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ if ( p_packet != NULL && p_packet->i_depth < i_block_cnt )
|V=2|P|X| CC |M| PT | sequence number | {
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ p_packet = realloc( p_packet, sizeof(packet_t *)
| timestamp | + (i_block_cnt - 1) * sizeof(block_t *) );
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ p_packet->pp_blocks = &p_packet->p_blocks;
| 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++;
} }
...@@ -38,60 +38,198 @@ ...@@ -38,60 +38,198 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <bitstream/ietf/rtp.h>
#include "dvblast.h" #include "dvblast.h"
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
static int i_handle; 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 * udp_Open
*****************************************************************************/ *****************************************************************************/
void udp_Open( void ) void udp_Open( void )
{ {
i_handle = socket(AF_INET, SOCK_DGRAM, 0); int i_family;
struct sockaddr_in sin; 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; 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 ) ); 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 /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
* packet loss caused by scheduling problems */ * packet loss caused by scheduling problems */
i = 0x80000; i = 0x80000;
setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) );
sin.sin_family = AF_INET; setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) );
sin.sin_port = htons(i_src_port);
sin.sin_addr.s_addr = i_src_addr;
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 ); close( i_handle );
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Join the multicast group if the socket is a multicast address */ if ( p_connect_ai != NULL )
if ( IN_MULTICAST( ntohl(sin.sin_addr.s_addr)) )
{ {
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; if ( i_port != 0 && connect( i_handle, p_connect_ai->ai_addr,
imr.imr_interface.s_addr = INADDR_ANY; /* FIXME could be an option */ p_connect_ai->ai_addrlen ) < 0 )
msg_Warn( NULL, "couldn't connect socket (%s)", strerror(errno) );
}
/* Join Multicast group without source filter */ /* Join the multicast group if the socket is a multicast address */
if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, if ( i_family == AF_INET6 )
(char *)&imr, sizeof(struct ip_mreq) ) == -1 ) {
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" ); struct ipv6_mreq imr;
exit(EXIT_FAILURE); 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", freeaddrinfo( p_bind_ai );
inet_ntoa( sin.sin_addr ), i_src_port, if ( p_connect_ai != NULL )
b_src_rawudp ? "UDP" : "RTP" ); 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 ) ...@@ -119,38 +257,68 @@ block_t *udp_Read( mtime_t i_poll_timeout )
if ( pfd.revents ) 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; block_t *p_ts, **pp_current = &p_ts;
int i = 0, i_len; int i_iov, i_block;
uint8_t p_rtp_hdr[RTP_SIZE]; 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 */ /* FIXME : this is wrong if RTP header > 12 bytes */
p_iov[i].iov_base = p_rtp_hdr; p_iov[0].iov_base = p_rtp_hdr;
p_iov[i].iov_len = RTP_SIZE; p_iov[0].iov_len = RTP_HEADER_SIZE;
i++; 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(); *pp_current = block_New();
p_iov[i].iov_base = (*pp_current)->p_ts; p_iov[i_iov].iov_base = (*pp_current)->p_ts;
p_iov[i].iov_len = TS_SIZE; p_iov[i_iov].iov_len = TS_SIZE;
pp_current = &(*pp_current)->p_next; 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)", msg_Err( NULL, "couldn't read from network (%s)", strerror(errno) );
strerror(errno) ); goto err;
i_len = 0;
} }
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; i_len /= TS_SIZE;
pp_current = &p_ts;
while ( i_len && *pp_current ) while ( i_len && *pp_current )
{ {
pp_current = &(*pp_current)->p_next; pp_current = &(*pp_current)->p_next;
...@@ -160,12 +328,14 @@ block_t *udp_Read( mtime_t i_poll_timeout ) ...@@ -160,12 +328,14 @@ block_t *udp_Read( mtime_t i_poll_timeout )
i_wallclock = mdate(); i_wallclock = mdate();
if ( *pp_current ) if ( *pp_current )
msg_Dbg( NULL, "partial buffer received" ); msg_Dbg( NULL, "partial buffer received %d", meuh );
err:
block_DeleteChain( *pp_current ); block_DeleteChain( *pp_current );
*pp_current = NULL; *pp_current = NULL;
return p_ts; return p_ts;
} }
return NULL; return NULL;
} }
......
...@@ -29,7 +29,9 @@ ...@@ -29,7 +29,9 @@
#include <stdarg.h> #include <stdarg.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
...@@ -250,3 +252,80 @@ void hexDump( uint8_t *p_data, uint32_t i_len ) ...@@ -250,3 +252,80 @@ void hexDump( uint8_t *p_data, uint32_t i_len )
free( p_outline ); 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