Commit ca44fe14 authored by Christophe Massiot's avatar Christophe Massiot

* ALL: Add a way to specify per-output options (239.1.0.1:1234/tsid=42). *...

* ALL: Add a way to specify per-output options (239.1.0.1:1234/tsid=42). * dvblast.c: Fix reinitialization of existing inputs, creating discontinuities. * demux.c: Print NIT table; always print DVB tables. * demux.c: Only check the CRC when needed. * demux.c: Use biTStream built-in print functions. * demux.c: Allow the same PID to be declared twice in PMT; allow the same service to be declared twice in SDT.
parent 527bda5e
......@@ -55,6 +55,9 @@ EITp/f, TOT, TDT). If you also want to pass-through the EIT schedule tables,
use the -e switch. It is considered a good practice to configure the name
of the network (for the NIT) with the -M option.
If you don't want to set these options on a general basis, you can set them
per output - see below.
Other rarely used options are available - run dvblast -h for more
information.
......@@ -83,7 +86,7 @@ Configuring outputs
DVBlast reads a configuration file containing one or several lines in the
format :
<IP>[:<port>][/udp] <always on> <SID> [<PID>,]*
<IP>[:<port>][/<option>]* <always on> <SID> [<PID>,]*
For instance :
239.255.0.1:1234 1 10750 1234,1235,1236
......@@ -108,6 +111,18 @@ regularly reset the CAM module if it fails to descramble the service,
assuming the module is dead. Every time it is reset a few TS packets
will be lost, that is why this feature is optional.
Other options can be set by appending / to the multicast address definition.
Available options include :
/udp (turns on -U for a specific output)
/dvb (turns on -C for a specific output)
/epg (turns on -C -e for a specific output)
/tsid=XXX (sets the transport stream ID)
/retention=XXX (see -E)
/latency=XXX (see -L)
Several options can be appended, for instance:
239.255.0.1:1234/udp/epg/tsid=42
The optional "/udp" parameter can be used to force DVBlast to output
raw UDP stream. This functionality is provided for backwards compatibility
with IPTV set top boxes that don't support RTP and should only be used
......
......@@ -38,6 +38,7 @@
#include <bitstream/mpeg/pes.h>
#include <bitstream/mpeg/psi.h>
#include <bitstream/dvb/si.h>
#include <bitstream/mpeg/psi_print.h>
/*****************************************************************************
* Local declarations
......@@ -73,6 +74,8 @@ static int i_nb_sids = 0;
static PSI_TABLE_DECLARE(pp_current_pat_sections);
static PSI_TABLE_DECLARE(pp_next_pat_sections);
static PSI_TABLE_DECLARE(pp_current_nit_sections);
static PSI_TABLE_DECLARE(pp_next_nit_sections);
static PSI_TABLE_DECLARE(pp_current_sdt_sections);
static PSI_TABLE_DECLARE(pp_next_sdt_sections);
static mtime_t i_last_dts = -1;
......@@ -100,6 +103,7 @@ static bool SIDIsSelected( uint16_t i_sid );
static bool PIDWouldBeSelected( uint8_t *p_es );
static bool PMTNeedsDescrambling( uint8_t *p_pmt );
static void FlushEIT( output_t *p_output, mtime_t i_dts );
static void SendTDT( block_t *p_ts );
static void NewPAT( output_t *p_output );
static void NewPMT( output_t *p_output );
static void NewNIT( output_t *p_output );
......@@ -133,23 +137,20 @@ void demux_Open( void )
SetPID(PAT_PID);
p_pids[PAT_PID].i_psi_refcount++;
if( b_dvb_compliance )
{
SetPID(NIT_PID);
p_pids[NIT_PID].i_psi_refcount++;
SetPID(NIT_PID);
p_pids[NIT_PID].i_psi_refcount++;
psi_table_init( pp_current_sdt_sections );
psi_table_init( pp_next_sdt_sections );
SetPID(SDT_PID);
p_pids[SDT_PID].i_psi_refcount++;
psi_table_init( pp_current_sdt_sections );
psi_table_init( pp_next_sdt_sections );
SetPID(SDT_PID);
p_pids[SDT_PID].i_psi_refcount++;
SetPID(EIT_PID);
p_pids[EIT_PID].i_psi_refcount++;
SetPID(EIT_PID);
p_pids[EIT_PID].i_psi_refcount++;
SetPID(RST_PID);
SetPID(RST_PID);
SetPID(TDT_PID);
}
SetPID(TDT_PID);
}
/*****************************************************************************
......@@ -207,19 +208,11 @@ static void demux_Handle( block_t *p_ts )
}
/* PSI parsing */
if ( b_dvb_compliance && (i_pid == TDT_PID || i_pid == RST_PID) )
{
for ( i = 0; i < i_nb_outputs; i++ )
{
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID )
&& pp_outputs[i]->p_sdt_section )
output_Put( pp_outputs[i], p_ts );
}
}
if ( i_pid == TDT_PID || i_pid == RST_PID )
SendTDT( p_ts );
else if ( p_pids[i_pid].i_psi_refcount )
{
HandlePSIPacket( p_ts->p_ts, p_ts->i_dts );
}
p_pids[i_pid].i_last_cc = i_cc;
/* PCR handling */
......@@ -313,7 +306,7 @@ 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, uint16_t i_sid,
void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
uint16_t *pi_pids, int i_nb_pids )
{
int i;
......@@ -321,12 +314,28 @@ void demux_Change( output_t *p_output, uint16_t i_sid,
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;
int pid_change = 0, tsid_change = 0;
if ( i_tsid != -1 &&
(!p_output->b_fixed_tsid || p_output->i_tsid != i_tsid) )
{
p_output->b_fixed_tsid = true;
p_output->i_tsid = i_tsid;
tsid_change = 1;
}
if ( i_tsid == -1 && p_output->b_fixed_tsid && !b_random_tsid )
{
p_output->b_fixed_tsid = false;
if ( psi_table_validate(pp_current_pat_sections) )
p_output->i_tsid =
psi_table_get_tableidext(pp_current_pat_sections);
tsid_change = 1;
}
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) )) )
return; /* No change */
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,
......@@ -426,6 +435,7 @@ void demux_Change( output_t *p_output, uint16_t i_sid,
memcpy( p_output->pi_pids, pi_pids, sizeof(uint16_t) * i_nb_pids );
p_output->i_nb_pids = i_nb_pids;
out_change:
if ( sid_change )
{
NewSDT( p_output );
......@@ -433,6 +443,12 @@ void demux_Change( output_t *p_output, uint16_t i_sid,
NewPAT( p_output );
NewPMT( p_output );
}
else if ( tsid_change )
{
NewSDT( p_output );
NewNIT( p_output );
NewPAT( p_output );
}
else if ( pid_change )
NewPMT( p_output );
}
......@@ -578,6 +594,7 @@ static void SelectPSI( uint16_t i_sid, uint16_t i_pid )
int i;
p_pids[i_pid].i_psi_refcount++;
p_pids[i_pid].b_pes = false;
for ( i = 0; i < i_nb_outputs; i++ )
if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
......@@ -744,11 +761,7 @@ static void SendPAT( mtime_t i_dts )
p = p_output->p_pat_section = psi_allocate();
pat_init( p );
pat_set_length( p, 0 );
if ( b_unique_tsid )
pat_set_tsid( p, p_output->i_ts_id );
else
pat_set_tsid( p,
psi_table_get_tableidext(pp_current_pat_sections) );
pat_set_tsid( p, p_output->i_tsid );
psi_set_version( p, p_output->i_pat_version );
psi_set_current( p );
psi_set_section( p, 0 );
......@@ -795,6 +808,7 @@ 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)
&& 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 );
......@@ -813,6 +827,7 @@ 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)
&& 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 );
......@@ -824,6 +839,9 @@ static void SendSDT( mtime_t i_dts )
*****************************************************************************/
static void SendEIT( sid_t *p_sid, mtime_t i_dts, uint8_t *p_eit )
{
uint8_t i_table_id = psi_get_tableid( p_eit );
bool b_epg = i_table_id >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST &&
i_table_id <= EIT_TABLE_ID_SCHED_ACTUAL_LAST;
int i;
for ( i = 0; i < i_nb_outputs; i++ )
......@@ -831,11 +849,13 @@ 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 ( b_unique_tsid )
if ( eit_get_tsid( p_eit ) != p_output->i_tsid )
{
eit_set_tsid( p_eit, p_output->i_ts_id );
eit_set_tsid( p_eit, p_output->i_tsid );
psi_set_crc( p_eit );
}
......@@ -861,6 +881,24 @@ static void FlushEIT( output_t *p_output, mtime_t i_dts )
p_output->i_eit_ts_buffer_offset = 0;
}
/*****************************************************************************
* SendTDT
*****************************************************************************/
static void SendTDT( block_t *p_ts )
{
int i;
for ( i = 0; i < i_nb_outputs; i++ )
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID)
&& (p_output->i_config & OUTPUT_DVB)
&& p_output->p_sdt_section != NULL )
output_Put( p_output, p_ts );
}
}
/*****************************************************************************
* NewPAT
*****************************************************************************/
......@@ -883,10 +921,7 @@ static void NewPAT( output_t *p_output )
p = p_output->p_pat_section = psi_allocate();
pat_init( p );
pat_set_length( p, PAT_PROGRAM_SIZE );
if ( b_unique_tsid )
pat_set_tsid( p, p_output->i_ts_id );
else
pat_set_tsid( p, psi_table_get_tableidext(pp_current_pat_sections) );
pat_set_tsid( p, p_output->i_tsid );
psi_set_version( p, p_output->i_pat_version );
psi_set_current( p );
psi_set_section( p, 0 );
......@@ -1039,11 +1074,7 @@ static void NewNIT( output_t *p_output )
p_ts = nit_get_ts( p, 0 );
nitn_init( p_ts );
if ( b_unique_tsid )
nitn_set_tsid( p_ts, p_output->i_ts_id );
else
nitn_set_tsid( p_ts,
psi_table_get_tableidext(pp_current_pat_sections) );
nitn_set_tsid( p_ts, p_output->i_tsid );
nitn_set_onid( p_ts, i_network_id );
nitn_set_desclength( p_ts, 0 );
......@@ -1090,10 +1121,7 @@ static void NewSDT( output_t *p_output )
p = p_output->p_sdt_section = psi_allocate();
sdt_init( p, true );
sdt_set_length( p, PSI_MAX_SIZE );
if ( b_unique_tsid )
sdt_set_tsid( p, p_output->i_ts_id );
else
sdt_set_tsid( p, psi_table_get_tableidext(pp_current_sdt_sections) );
sdt_set_tsid( p, p_output->i_tsid );
psi_set_version( p, p_output->i_sdt_version );
psi_set_current( p );
psi_set_section( p, 0 );
......@@ -1125,7 +1153,7 @@ static void NewSDT( output_t *p_output )
}
/*****************************************************************************
* UpdatePAT/PMT/SDT/NIT
* UpdatePAT/PMT/SDT
*****************************************************************************/
#define DECLARE_UPDATE_FUNC( table ) \
static void Update##table( uint16_t i_sid ) \
......@@ -1142,13 +1170,24 @@ DECLARE_UPDATE_FUNC(PAT)
DECLARE_UPDATE_FUNC(PMT)
DECLARE_UPDATE_FUNC(SDT)
static void UpdateNIT(void)
/*****************************************************************************
* UpdateTSID
*****************************************************************************/
static void UpdateTSID(void)
{
uint16_t i_tsid = psi_table_get_tableidext(pp_current_pat_sections);
int i;
for ( i = 0; i < i_nb_outputs; i++ )
if ( pp_outputs[i]->i_config & OUTPUT_VALID )
NewNIT( pp_outputs[i] );
{
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) && !p_output->b_fixed_tsid )
{
p_output->i_tsid = i_tsid;
NewNIT( p_output );
}
}
}
/*****************************************************************************
......@@ -1354,18 +1393,31 @@ static void DeleteProgram( uint16_t i_sid, uint16_t i_pid )
*****************************************************************************/
static void HandlePAT( mtime_t i_dts )
{
bool b_display;
bool b_display, b_change = false;
PSI_TABLE_DECLARE( pp_old_pat_sections );
uint8_t i_last_section = psi_table_get_lastsection( pp_next_pat_sections );
uint8_t i;
if ( psi_table_validate( pp_current_pat_sections ) &&
psi_table_compare( pp_current_pat_sections, pp_next_pat_sections ) )
{
/* Identical PAT. Shortcut. */
psi_table_free( pp_next_pat_sections );
psi_table_init( pp_next_pat_sections );
goto out_pat;
}
if ( !pat_table_validate( pp_next_pat_sections ) )
{
msg_Warn( NULL, "invalid PAT received" );
psi_table_free( pp_next_pat_sections );
psi_table_init( pp_next_pat_sections );
goto out_pat;
}
b_display = !psi_table_validate( pp_current_pat_sections )
|| psi_table_get_version( pp_current_pat_sections )
!= psi_table_get_version( pp_next_pat_sections );
if ( b_display )
msg_Dbg( NULL, "new PAT ts_id=%hu version=%hhu",
psi_table_get_tableidext( pp_next_pat_sections ),
psi_table_get_version( pp_next_pat_sections ) );
/* Switch tables. */
psi_table_copy( pp_old_pat_sections, pp_current_pat_sections );
......@@ -1375,7 +1427,11 @@ static void HandlePAT( mtime_t i_dts )
if ( !psi_table_validate( pp_old_pat_sections )
|| psi_table_get_tableidext( pp_current_pat_sections )
!= psi_table_get_tableidext( pp_old_pat_sections ) )
UpdateNIT();
{
b_display = b_change = true;
UpdateTSID();
/* This will trigger a universal reset of everything. */
}
for ( i = 0; i <= i_last_section; i++ )
{
......@@ -1386,55 +1442,52 @@ static void HandlePAT( mtime_t i_dts )
while ( (p_program = pat_get_program( p_section, j )) != NULL )
{
const uint8_t *p_old_program = NULL;
uint16_t i_sid = patn_get_program( p_program );
uint16_t i_pid = patn_get_pid( p_program );
int i_pmt;
j++;
if ( b_display )
msg_Dbg( NULL, " * number=%hu pid=%hu", i_sid, i_pid );
if ( i_sid == 0 )
{
if ( b_display && b_dvb_compliance && i_pid != NIT_PID )
if ( i_pid != NIT_PID )
msg_Warn( NULL,
"NIT is carried on PID %u which isn't DVB compliant",
i_pid );
continue; /* NIT */
}
if ( psi_table_validate( pp_old_pat_sections ) )
if ( !psi_table_validate( pp_old_pat_sections )
|| (p_old_program = pat_table_find_program(
pp_old_pat_sections, i_sid )) == NULL
|| patn_get_pid( p_old_program ) != i_pid
|| b_change )
{
const uint8_t *p_old_program = pat_table_find_program(
pp_old_pat_sections, i_sid );
b_display = true;
if ( p_old_program != NULL )
{
uint16_t i_old_pid = patn_get_pid( p_old_program );
if ( i_old_pid == i_pid )
continue; /* No change */
DeleteProgram( i_sid, i_old_pid );
}
}
DeleteProgram( i_sid, patn_get_pid( p_old_program ) );
SelectPSI( i_sid, i_pid );
SelectPSI( i_sid, i_pid );
for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ )
if ( pp_sids[i_pmt]->i_sid == 0 )
break;
for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ )
if ( pp_sids[i_pmt]->i_sid == 0 )
break;
if ( i_pmt == i_nb_sids )
{
sid_t *p_sid = malloc( sizeof(sid_t) );
p_sid->p_current_pmt = NULL;
i_nb_sids++;
pp_sids = realloc( pp_sids, sizeof(sid_t *) * i_nb_sids );
pp_sids[i_pmt] = p_sid;
}
if ( i_pmt == i_nb_sids )
{
sid_t *p_sid = malloc( sizeof(sid_t) );
p_sid->p_current_pmt = NULL;
i_nb_sids++;
pp_sids = realloc( pp_sids, sizeof(sid_t *) * i_nb_sids );
pp_sids[i_pmt] = p_sid;
}
pp_sids[i_pmt]->i_sid = i_sid;
pp_sids[i_pmt]->i_pmt_pid = i_pid;
pp_sids[i_pmt]->i_sid = i_sid;
pp_sids[i_pmt]->i_pmt_pid = i_pid;
UpdatePAT( i_sid );
UpdatePAT( i_sid );
}
}
}
......@@ -1452,21 +1505,15 @@ static void HandlePAT( mtime_t i_dts )
{
uint16_t i_sid = patn_get_program( p_program );
uint16_t i_pid = patn_get_pid( p_program );
const uint8_t *p_new_program;
j++;
if ( i_sid == 0 )
continue; /* NIT */
p_new_program = pat_table_find_program(
pp_current_pat_sections, i_sid );
if ( p_new_program == NULL )
if ( pat_table_find_program( pp_current_pat_sections, i_sid )
== NULL )
{
if ( b_display )
msg_Dbg( NULL, " * removed number=%hu pid=%hu",
i_sid, i_pid );
b_display = true;
DeleteProgram( i_sid, i_pid );
UpdatePAT( i_sid );
}
......@@ -1477,8 +1524,9 @@ static void HandlePAT( mtime_t i_dts )
}
if ( b_display )
msg_Dbg( NULL, "end PAT" );
pat_table_print( pp_current_pat_sections, msg_Dbg, NULL );
out_pat:
SendPAT( i_dts );
}
......@@ -1504,17 +1552,9 @@ static void HandlePATSection( uint16_t i_pid, uint8_t *p_section,
/*****************************************************************************
* HandlePMT
*****************************************************************************/
static bool ESDiffer( const uint8_t *p_es1, const uint8_t *p_es2 )
{
if ( pmtn_get_desclength( p_es1 ) != pmtn_get_desclength( p_es2 ) )
return true;
return memcmp( p_es1, p_es2, PMT_ES_SIZE + pmtn_get_desclength( p_es1 ) );
}
static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
{
bool b_display, b_change = false;
bool b_change, b_new;
uint16_t i_sid = pmt_get_program( p_pmt );
sid_t *p_sid;
bool b_needs_descrambling, b_needed_descrambling, b_is_selected;
......@@ -1536,13 +1576,28 @@ static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
}
p_sid = pp_sids[i];
if ( i_pid != p_sid->i_pmt_pid || !pmt_validate( p_pmt ) )
if ( i_pid != p_sid->i_pmt_pid )
{
msg_Warn( NULL, "invalid PMT section received on PID %u", i_pid );
free( p_pmt );
return;
}
if ( p_sid->p_current_pmt != NULL &&
psi_compare( p_sid->p_current_pmt, p_pmt ) )
{
/* Identical PMT. Shortcut. */
free( p_pmt );
goto out_pmt;
}
if ( !pmt_validate( p_pmt ) )
{
msg_Warn( NULL, "invalid PMT section received on PID %u", i_pid );
free( p_pmt );
goto out_pmt;
}
b_needs_descrambling = PMTNeedsDescrambling( p_pmt );
b_needed_descrambling = p_sid->p_current_pmt != NULL ?
PMTNeedsDescrambling( p_sid->p_current_pmt ) :
......@@ -1554,12 +1609,9 @@ static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
!b_needs_descrambling && b_needed_descrambling )
en50221_DeletePMT( p_sid->p_current_pmt );
b_display = p_sid->p_current_pmt == NULL
|| psi_get_version( p_sid->p_current_pmt )
!= psi_get_version( p_pmt );
if ( b_display )
msg_Dbg( NULL, "new PMT program number=%hu version=%hhu pid_pcr=%hu",
i_sid, psi_get_version( p_pmt ), i_pcr_pid );
b_new = b_change = p_sid->p_current_pmt == NULL
|| psi_get_version( p_sid->p_current_pmt )
!= psi_get_version( p_pmt );
if ( p_sid->p_current_pmt == NULL
|| i_pcr_pid != pmt_get_pcrpid( p_sid->p_current_pmt ) )
......@@ -1575,18 +1627,10 @@ static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
j = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{
uint8_t i_type = pmtn_get_streamtype( p_es );
uint16_t i_pid = pmtn_get_pid( p_es );
const uint8_t *p_current_es;
j++;
if ( b_display )
msg_Dbg( NULL, " * es pid=%hu type=%hu", i_pid, i_type );
if ( p_sid->p_current_pmt == NULL
|| (p_current_es = pmt_find_es( p_sid->p_current_pmt, i_pid ))
== NULL
|| ESDiffer( p_es, p_current_es ) )
if ( b_new || pmt_find_es( p_sid->p_current_pmt, i_pid ) == NULL )
{
b_change = true;
if ( PIDWouldBeSelected( p_es ) )
......@@ -1604,9 +1648,6 @@ static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
if ( pmt_find_es( p_pmt, i_current_pcr_pid ) == NULL )
{
b_change = true;
if ( b_display )
msg_Dbg( NULL, " * removed pid_pcr=%hu",
i_current_pcr_pid );
UnselectPID( i_sid, i_current_pcr_pid );
}
}
......@@ -1618,15 +1659,11 @@ static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
if ( PIDWouldBeSelected( p_es ) )
{
uint8_t i_current_type = pmtn_get_streamtype( p_es );
uint16_t i_current_pid = pmtn_get_pid( p_es );
if ( pmt_find_es( p_pmt, i_current_pid ) == NULL )
{
b_change = true;
if ( b_display )
msg_Dbg( NULL, " * removed es pid=%hu type=%hhu",
i_current_pid, i_current_type );
UnselectPID( i_sid, i_current_pid );
}
}
......@@ -1648,17 +1685,54 @@ static void HandlePMT( uint16_t i_pid, uint8_t *p_pmt, mtime_t i_dts )
}
UpdatePMT( i_sid );
}
if ( b_display )
msg_Dbg( NULL, "end PMT" );
pmt_print( p_pmt, msg_Dbg, NULL );
}
out_pmt:
SendPMT( p_sid, i_dts );
}
/*****************************************************************************
* HandleNITSection
* HandleNIT
*****************************************************************************/
static void HandleNIT( mtime_t i_dts )
{
bool b_display;
if ( psi_table_validate( pp_current_nit_sections ) &&
psi_table_compare( pp_current_nit_sections, pp_next_nit_sections ) )
{
/* Identical NIT. Shortcut. */
psi_table_free( pp_next_nit_sections );
psi_table_init( pp_next_nit_sections );
goto out_nit;
}
if ( !nit_table_validate( pp_next_nit_sections ) )
{
msg_Warn( NULL, "invalid NIT received" );
psi_table_free( pp_next_nit_sections );
psi_table_init( pp_next_nit_sections );
goto out_nit;
}
b_display = !psi_table_validate( pp_current_nit_sections )
|| psi_table_get_version( pp_current_nit_sections )
!= psi_table_get_version( pp_next_nit_sections );
/* Switch tables. */
psi_table_free( pp_current_nit_sections );
psi_table_copy( pp_current_nit_sections, pp_next_nit_sections );
psi_table_init( pp_next_nit_sections );
if ( b_display )
nit_table_print( pp_current_nit_sections, msg_Dbg, NULL );
out_nit:
;
}
static void HandleNITSection( uint16_t i_pid, uint8_t *p_section,
mtime_t i_dts )
{
......@@ -1669,9 +1743,12 @@ static void HandleNITSection( uint16_t i_pid, uint8_t *p_section,
return;
}
/* Actually we don't care about the incoming NIT. Just build our own. */
free( p_section );
if ( psi_table_section( pp_next_nit_sections, p_section ) )
HandleNIT( i_dts );
/* This case is different because DVB specifies a minimum bitrate for
* PID 0x10, even if we don't have any thing to send (for cheap
* transport over network boundaries). */
SendNIT( i_dts );
}
......@@ -1679,58 +1756,34 @@ static void HandleNITSection( uint16_t i_pid, uint8_t *p_section,
/*****************************************************************************
* HandleSDT
*****************************************************************************/
static void CheckServiceDescriptor( uint8_t *p_service )
{
uint16_t j = 0;
const uint8_t *p_desc;
while ( (p_desc = descs_get_desc( sdtn_get_descs(p_service), j )) != NULL )
{
uint8_t i_tag = desc_get_tag( p_desc );
j++;
if( i_tag == 0x48 )
{
uint8_t i_type = desc48_get_type( p_desc );
DESC_DECLARE_STRING1( psz_provider );
DESC_DECLARE_STRING1( psz_service );
desc48_get_provider( p_desc, psz_provider );
desc48_get_service( p_desc, psz_service );
msg_Dbg( NULL, " - type=%hhu provider=%s name=%s",
i_type, psz_provider, psz_service );
}
}
}
static bool ServicesDiffer( const uint8_t *p_service1,
const uint8_t *p_service2 )
{
if ( sdtn_get_desclength( p_service1 )
!= sdtn_get_desclength( p_service2 ) )
return true;
return memcmp( p_service1, p_service2,
SDT_SERVICE_SIZE + sdtn_get_desclength( p_service1 ) );
}
static void HandleSDT( mtime_t i_dts )
{
bool b_display;
bool b_change, b_new;
PSI_TABLE_DECLARE( pp_old_sdt_sections );
uint8_t i_last_section = psi_table_get_lastsection( pp_next_sdt_sections );
uint8_t i;
int j;
b_display = !psi_table_validate( pp_current_sdt_sections )
|| psi_table_get_version( pp_current_sdt_sections )
!= psi_table_get_version( pp_next_sdt_sections );
if ( b_display )
msg_Dbg( NULL, "new SDT ts_id=%hu version=%hhu original_network_id=%hu",
psi_table_get_tableidext( pp_next_sdt_sections ),
psi_table_get_version( pp_next_sdt_sections ),
sdt_get_onid(
psi_table_get_section( pp_next_sdt_sections, 0 ) ) );
if ( psi_table_validate( pp_current_sdt_sections ) &&
psi_table_compare( pp_current_sdt_sections, pp_next_sdt_sections ) )
{
/* Identical sdt. Shortcut. */
psi_table_free( pp_next_sdt_sections );
psi_table_init( pp_next_sdt_sections );
goto out_sdt;
}
if ( !sdt_table_validate( pp_next_sdt_sections ) )
{
msg_Warn( NULL, "invalid SDT received" );
psi_table_free( pp_next_sdt_sections );
psi_table_init( pp_next_sdt_sections );
goto out_sdt;
}
b_change = b_new = !psi_table_validate( pp_current_sdt_sections )
|| psi_table_get_version( pp_current_sdt_sections )
!= psi_table_get_version( pp_next_sdt_sections );
/* Switch tables. */
psi_table_copy( pp_old_sdt_sections, pp_current_sdt_sections );
......@@ -1747,38 +1800,48 @@ static void HandleSDT( mtime_t i_dts )
while ( (p_service = sdt_get_service( p_section, j )) != NULL )
{
uint16_t i_sid = sdtn_get_sid( p_service );
bool b_eit_schedule = sdtn_get_eitschedule( p_service );
bool b_eit_present = sdtn_get_eitpresent( p_service );
uint8_t i_running_status = sdtn_get_running( p_service );
bool b_ca = sdtn_get_ca( p_service );
const uint8_t *p_current_service;
j++;
if ( b_display )
if ( b_new ||
sdt_table_find_service( pp_old_sdt_sections, i_sid ) == NULL )
{
msg_Dbg( NULL, " * service id=%hu eit%s%s running=%hhu%s",
i_sid, b_eit_schedule ? " schedule" : "",
b_eit_present ? " present" : "", i_running_status,
b_ca ? " scrambled" : "" );
CheckServiceDescriptor( p_service );
}
if ( !psi_table_validate( pp_old_sdt_sections )
|| (p_current_service =
sdt_table_find_service( pp_old_sdt_sections, i_sid ))
== NULL
|| ServicesDiffer( p_service, p_current_service ) )
b_change = true;
UpdateSDT( i_sid );
}
}
}
if ( psi_table_validate( pp_old_sdt_sections ) )
{
i_last_section = psi_table_get_lastsection( pp_old_sdt_sections );
for ( i = 0; i <= i_last_section; i++ )
{
uint8_t *p_section =
psi_table_get_section( pp_old_sdt_sections, i );
const uint8_t *p_service;
int j = 0;
while ( (p_service = sdt_get_service( p_section, j )) != NULL )
{
uint16_t i_sid = sdtn_get_sid( p_service );
j++;
if ( sdt_table_find_service( pp_current_sdt_sections, i_sid )
== NULL )
{
b_change = true;
UpdateSDT( i_sid );
}
}
}
psi_table_free( pp_old_sdt_sections );
}
if ( b_display )
msg_Dbg( NULL, "end SDT" );
if ( b_change )
sdt_table_print( pp_current_sdt_sections, msg_Dbg, NULL );
out_sdt:
SendSDT( i_dts );
}
......@@ -1842,7 +1905,7 @@ static void HandleSection( uint16_t i_pid, uint8_t *p_section, mtime_t i_dts )
if ( !psi_validate( p_section ) )
{
msg_Warn( NULL, "invalid section CRC on PID %u", i_pid );
msg_Warn( NULL, "invalid section on PID %u", i_pid );
free( p_section );
return;
}
......@@ -1874,8 +1937,7 @@ static void HandleSection( uint16_t i_pid, uint8_t *p_section, mtime_t i_dts )
default:
if ( i_table_id == EIT_TABLE_ID_PF_ACTUAL ||
(b_enable_epg &&
i_table_id >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST &&
(i_table_id >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST &&
i_table_id <= EIT_TABLE_ID_SCHED_ACTUAL_LAST) )
{
HandleEIT( i_pid, p_section, i_dts );
......
/*****************************************************************************
* dvblast.c
*****************************************************************************
* Copyright (C) 2004, 2008-2009 VideoLAN
* Copyright (C) 2004, 2008-2010 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
......@@ -63,14 +63,9 @@ int b_tone = 0;
int i_bandwidth = 8;
char *psz_modulation = NULL;
int b_budget_mode = 0;
int b_output_udp = 0;
int b_dvb_compliance = 0;
int b_enable_epg = 0;
int b_unique_tsid = 0;
int b_random_tsid = 0;
uint16_t i_network_id = 0xffff;
const char *psz_network_name = "DVBlast - http://www.videolan.org/projects/dvblast.html";
mtime_t i_output_latency = DEFAULT_OUTPUT_LATENCY;
mtime_t i_max_retention = DEFAULT_MAX_RETENTION;
volatile int b_hup_received = 0;
int i_verbose = DEFAULT_VERBOSITY;
int i_syslog = 0;
......@@ -79,6 +74,12 @@ in_addr_t i_src_addr = { 0 };
int b_src_rawudp = 0;
int i_asi_adapter = 0;
static int b_udp_global = 0;
static int b_dvb_global = 0;
static int b_epg_global = 0;
static mtime_t i_latency_global = DEFAULT_OUTPUT_LATENCY;
static mtime_t i_retention_global = DEFAULT_MAX_RETENTION;
void (*pf_Open)( void ) = NULL;
block_t * (*pf_Read)( mtime_t i_poll_timeout ) = NULL;
int (*pf_SetFilter)( uint16_t i_pid ) = NULL;
......@@ -108,7 +109,7 @@ 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, *psz_token3;
char *psz_parser, *psz_token, *psz_token2;
struct addrinfo *p_addr;
struct addrinfo ai_hints;
char sz_port[6];
......@@ -116,7 +117,12 @@ static void ReadConfiguration( char *psz_file )
uint16_t i_sid = 0;
uint16_t *pi_pids = NULL;
int i_nb_pids = 0;
uint8_t i_config = 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;
snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT );
......@@ -127,11 +133,24 @@ static void ReadConfiguration( char *psz_file )
if ( psz_token == NULL )
continue;
if ( (psz_token3 = strrchr( psz_token, '/' )) != NULL )
psz_token2 = psz_token;
while ( (psz_token2 = strchr( psz_token2, '/' )) != NULL )
{
*psz_token3 = '\0';
if( strncasecmp( psz_token3 + 1, "udp", 3 ) == 0 )
*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
msg_Warn( NULL, "unrecognized option %s", psz_token2 );
}
if ( !strncmp( psz_token, "[", 1 ) )
......@@ -227,9 +246,8 @@ static void ReadConfiguration( char *psz_file )
}
}
msg_Dbg( NULL, "conf: %s w=%d sid=%d pids[%d]=%d,%d,%d,%d,%d...",
psz_displayname,
( i_config & OUTPUT_WATCH ) ? 1 : 0, i_sid, i_nb_pids,
msg_Dbg( NULL, "conf: %s config=0x%x sid=%d pids[%d]=%d,%d,%d,%d,%d...",
psz_displayname, i_config, 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],
......@@ -247,7 +265,6 @@ static void ReadConfiguration( char *psz_file )
( p_esad->sin_port == p_nsad->sin_port ) )
{
p_output = pp_outputs[i];
output_Init( p_output, i_config, psz_displayname, (void *)p_addr );
break;
}
}
......@@ -263,19 +280,20 @@ static void ReadConfiguration( char *psz_file )
( p_esad->sin6_port == p_nsad->sin6_port ) )
{
p_output = pp_outputs[i];
output_Init( p_output, i_config, psz_displayname, (void *)p_addr );
break;
}
}
}
if ( i == i_nb_outputs )
p_output = output_Create( i_config, psz_displayname, (void *)p_addr );
p_output = output_Create( psz_displayname, p_addr );
if ( p_output != NULL )
{
demux_Change( p_output, i_sid, pi_pids, i_nb_pids );
p_output->i_config |= OUTPUT_STILL_PRESENT;
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 );
}
free( psz_displayname );
......@@ -287,11 +305,13 @@ static void ReadConfiguration( char *psz_file )
for ( i = 0; i < i_nb_outputs; i++ )
{
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) &&
!( pp_outputs[i]->i_config & OUTPUT_STILL_PRESENT ) )
output_t *p_output = pp_outputs[i];
if ( (p_output->i_config & OUTPUT_VALID) &&
!(p_output->i_config & OUTPUT_STILL_PRESENT) )
{
msg_Dbg( NULL, "closing %s", pp_outputs[i]->psz_displayname );
demux_Change( pp_outputs[i], 0, NULL, 0 );
demux_Change( pp_outputs[i], -1, 0, NULL, 0 );
output_Close( pp_outputs[i] );
}
......@@ -530,15 +550,15 @@ int main( int i_argc, char **pp_argv )
break;
case 'U':
b_output_udp = 1;
b_udp_global = 1;
break;
case 'L':
i_output_latency = strtoll( optarg, NULL, 0 ) * 1000;
i_latency_global = strtoll( optarg, NULL, 0 ) * 1000;
break;
case 'E':
i_max_retention = strtoll( optarg, NULL, 0 ) * 1000;
i_retention_global = strtoll( optarg, NULL, 0 ) * 1000;
break;
case 'd':
......@@ -621,11 +641,16 @@ int main( int i_argc, char **pp_argv )
i_dup_config |= OUTPUT_VALID;
}
if ( i_dup_config &= OUTPUT_VALID ) {
output_Init( &output_dup, i_dup_config, psz_displayname, p_daddr );
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);
msg_Err( NULL, "Invalid configuration for -d switch: %s" ,
optarg);
free( psz_displayname );
freeaddrinfo( p_daddr );
......@@ -681,11 +706,11 @@ int main( int i_argc, char **pp_argv )
break;
case 'C':
b_dvb_compliance = 1;
b_dvb_global = 1;
break;
case 'e':
b_enable_epg = 1;
b_epg_global = 1;
break;
case 'M':
......@@ -701,7 +726,7 @@ int main( int i_argc, char **pp_argv )
break;
case 'T':
b_unique_tsid = 1;
b_random_tsid = 1;
break;
case 'V':
......@@ -721,16 +746,16 @@ int main( int i_argc, char **pp_argv )
msg_Warn( NULL, "restarting" );
if ( b_output_udp )
if ( b_udp_global )
{
msg_Warn( NULL, "raw UDP output is deprecated. Please consider using RTP." );
msg_Warn( NULL, "for DVB-IP compliance you should use RTP." );
}
if ( b_enable_epg && !b_dvb_compliance )
if ( b_epg_global && !b_dvb_global )
{
msg_Dbg( NULL, "turning on DVB compliance, required by EPG information" );
b_dvb_compliance = 1;
b_dvb_global = 1;
}
signal( SIGHUP, SigHandler );
......
......@@ -47,6 +47,8 @@
* Bit 2 : Set if output is valid (replaces m_addr != 0 tests)
* Bit 3 : Set for UDP, otherwise use RTP if a network stream
* Bit 4 : Set for file / FIFO output, unset for network (future use)
* Bit 5 : Set if DVB conformance tables are inserted
* Bit 6 : Set if DVB EIT schedule tables are forwarded
*****************************************************************************/
#define OUTPUT_WATCH 0x01
......@@ -54,6 +56,8 @@
#define OUTPUT_VALID 0x04
#define OUTPUT_UDP 0x08
#define OUTPUT_FILE 0x10
#define OUTPUT_DVB 0x20
#define OUTPUT_EPG 0x40
typedef int64_t mtime_t;
......@@ -96,12 +100,16 @@ 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;
uint16_t i_ts_id;
/* 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;
......@@ -125,14 +133,9 @@ extern int b_tone;
extern int i_bandwidth;
extern char *psz_modulation;
extern int b_budget_mode;
extern int b_output_udp;
extern int b_dvb_compliance;
extern int b_enable_epg;
extern int b_unique_tsid;
extern int b_random_tsid;
extern uint16_t i_network_id;
extern const char *psz_network_name;
extern mtime_t i_output_latency;
extern mtime_t i_max_retention;
extern mtime_t i_wallclock;
extern volatile int b_hup_received;
extern int i_comm_fd;
......@@ -185,15 +188,14 @@ 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, uint16_t i_sid,
void demux_Change( output_t *p_output, int i_tsid, uint16_t i_sid,
uint16_t *pi_pids, int i_nb_pids );
void demux_ResendCAPMTs( void );
bool demux_PIDIsSelected( uint16_t i_pid );
output_t *output_Create( uint8_t i_config, const char *psz_displayname,
void *p_init_data );
int output_Init( output_t *p_output, uint8_t i_config,
const char *psz_displayname, void *p_init_data );
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 );
void output_Close( output_t *p_output );
void output_Put( output_t *p_output, block_t *p_block );
mtime_t output_Send( void );
......
......@@ -77,8 +77,7 @@ static void rtp_SetHdr( output_t *p_output, packet_t *p_packet,
/*****************************************************************************
* output_Create : called from main thread
*****************************************************************************/
output_t *output_Create( uint8_t i_config, const char *psz_displayname,
void *p_init_data )
output_t *output_Create( const char *psz_displayname, struct addrinfo *p_ai )
{
int i;
output_t *p_output = NULL;
......@@ -101,7 +100,7 @@ output_t *output_Create( uint8_t i_config, const char *psz_displayname,
pp_outputs[i] = p_output;
}
if ( output_Init( p_output, i_config, psz_displayname, p_init_data ) < 0 )
if ( output_Init( p_output, psz_displayname, p_ai ) < 0 )
return NULL;
return p_output;
......@@ -110,8 +109,8 @@ output_t *output_Create( uint8_t i_config, const char *psz_displayname,
/*****************************************************************************
* output_Init
*****************************************************************************/
int output_Init( output_t *p_output, uint8_t i_config,
const char *psz_displayname, void *p_init_data )
int output_Init( output_t *p_output, const char *psz_displayname,
struct addrinfo *p_ai )
{
p_output->i_sid = 0;
p_output->p_packets = p_output->p_last_packet = NULL;
......@@ -135,15 +134,22 @@ int output_Init( output_t *p_output, uint8_t i_config,
p_output->p_sdt_section = NULL;
p_output->p_eit_ts_buffer = NULL;
p_output->i_eit_ts_buffer_offset = 0;
if ( b_unique_tsid )
p_output->i_ts_id = rand() & 0xffff;
if ( b_random_tsid )
{
p_output->i_tsid = rand() & 0xffff;
p_output->b_fixed_tsid = true;
}
else
{
p_output->i_tsid = 0;
p_output->b_fixed_tsid = false;
}
p_output->i_ref_timestamp = 0;
p_output->i_ref_wallclock = 0;
p_output->i_config = i_config;
p_output->i_config = 0;
p_output->psz_displayname = strdup( psz_displayname );
struct addrinfo *p_ai = (struct addrinfo *)p_init_data;
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,
......@@ -205,7 +211,7 @@ static void output_Flush( output_t *p_output )
NB_BLOCKS_IPV6 : NB_BLOCKS;
int i_iov, i_block;
if ( !b_output_udp && !(p_output->i_config & OUTPUT_UDP) )
if ( !(p_output->i_config & OUTPUT_UDP) )
{
p_iov[0].iov_base = p_rtp_hdr;
p_iov[0].iov_len = sizeof(p_rtp_hdr);
......@@ -262,7 +268,8 @@ void output_Put( output_t *p_output, block_t *p_block )
if ( p_output->p_last_packet != NULL
&& p_output->p_last_packet->i_depth < i_block_cnt
&& p_output->p_last_packet->i_dts + i_max_retention > p_block->i_dts )
&& p_output->p_last_packet->i_dts + p_output->i_max_retention
> p_block->i_dts )
{
p_packet = p_output->p_last_packet;
if ( ts_has_adaptation( p_block->p_ts )
......@@ -298,7 +305,7 @@ mtime_t output_Send( void )
if ( output_dup.i_config & OUTPUT_VALID )
{
while ( output_dup.p_packets != NULL
&& output_dup.p_packets->i_dts + i_output_latency
&& output_dup.p_packets->i_dts + output_dup.i_output_latency
<= i_wallclock )
output_Flush( &output_dup );
......@@ -313,18 +320,19 @@ mtime_t output_Send( void )
continue;
while ( p_output->p_packets != NULL
&& p_output->p_packets->i_dts + i_output_latency
&& p_output->p_packets->i_dts + p_output->i_output_latency
<= i_wallclock )
output_Flush( p_output );
if ( p_output->p_packets != NULL
&& (p_output->p_packets->i_dts < i_earliest_dts
&& (p_output->p_packets->i_dts + p_output->i_output_latency
< i_earliest_dts
|| 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;
}
return i_earliest_dts == -1 ? -1 :
i_earliest_dts + i_output_latency - i_wallclock;
return i_earliest_dts == -1 ? -1 : i_earliest_dts - i_wallclock;
}
/*****************************************************************************
......
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