Commit 527bda5e authored by Christophe Massiot's avatar Christophe Massiot

* demux.c, en50221.c: Get rid of the libdvbpsi dependancy. * demux.c: New -C...

* demux.c, en50221.c: Get rid of the libdvbpsi dependancy. * demux.c: New -C "DVB compliance" option; -e only activates EIT schedule. * demux.c: In DVB compliance mode, generate a NIT (see -M and -N). * demux.c: In DVB compliance mode, pass through RST. * demux.c: Introduce a buffering scheme allowing several EIT sections per TS packet. * dvblast.h: Get rid of the inline function to access TS structures. * en50221.c: Fix a potential memory corruption with application info. * version.h: Bump up version to 2.0.0.
parent 6ece6b2d
...@@ -6,5 +6,9 @@ Installing DVBlast ...@@ -6,5 +6,9 @@ Installing DVBlast
No autotools yet... You have to tweak the Makefile by hand, especially No autotools yet... You have to tweak the Makefile by hand, especially
if your kernel is S2API-enabled but not your distribution (indicate the if your kernel is S2API-enabled but not your distribution (indicate the
path of your kernel where appropriate). Compile the program with `make` path of your kernel where appropriate). Compile the program with `make`
and install with `make install`. It requires the installation of libdvbpsi and install with `make install`.
0.1.6 or later and libdvbpsi-devel (depending on your distribution).
DVBlast no longer requires libdvbpsi runtime and libdvbpsi-devel for
compilation. Instead you must install biTStream on your build machine
(no runtime library needed). biTStream is currently available via SVN at:
svn://svn.videolan.org/bitstream/trunk
...@@ -8,7 +8,7 @@ CFLAGS += -Wall -O3 -fomit-frame-pointer ...@@ -8,7 +8,7 @@ CFLAGS += -Wall -O3 -fomit-frame-pointer
CFLAGS += -g CFLAGS += -g
CFLAGS += -I/usr/src/kernel/linux-2.6.29.1/include CFLAGS += -I/usr/src/kernel/linux-2.6.29.1/include
LDLIBS += -lrt LDLIBS += -lrt
LDLIBS_DVBLAST += -ldvbpsi -lpthread LDLIBS_DVBLAST += -lpthread
OBJ_DVBLAST = dvblast.o util.o dvb.o udp.o asi.o demux.o output.o en50221.o comm.o OBJ_DVBLAST = dvblast.o util.o dvb.o udp.o asi.o demux.o output.o en50221.o comm.o
OBJ_DVBLASTCTL = util.o dvblastctl.o OBJ_DVBLASTCTL = util.o dvblastctl.o
......
$Id$ $Id$
Changes between 1.2 and 1.3: Changes between 1.2 and 2.0:
---------------------------- ----------------------------
* Fix latency and potential packet loss during CAM communication * Fix latency and potential packet loss during CAM communication
* Add optional kernel patches for lower latency * Add optional kernel patches for lower latency
* Smooth packet output with an extra buffer * Smooth packet output with an extra buffer
* libdvbpsi runtime is no longer needed; biTStream development library
is used instead
* 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
* Override Rolloff with -R option * Override Rolloff with -R option
......
...@@ -13,7 +13,7 @@ It outputs one or several RTP streams carrying transport streams with: ...@@ -13,7 +13,7 @@ It outputs one or several RTP streams carrying transport streams with:
- hardware or software PID filtering - hardware or software PID filtering
- PID-based or service-based demultiplexing - PID-based or service-based demultiplexing
- optional descrambling via CAM device - optional descrambling via CAM device
- EIT, SDT and TDT pass-through for EPG information< - optional DVB tables
DVBlast is written to be the core of a custom IRD, CID,or ASI gateway, DVBlast is written to be the core of a custom IRD, CID,or ASI gateway,
based on a PC with a Linux-supported card. It is very lightweight and based on a PC with a Linux-supported card. It is very lightweight and
...@@ -24,7 +24,7 @@ Current features ...@@ -24,7 +24,7 @@ Current features
================ ================
- Lightweight program designed for extreme memory and CPU conditions - Lightweight program designed for extreme memory and CPU conditions
- Only one dependancy: libdvbpsi - No runtime dependancy; one build dependancy (biTStream)
- CAM menus (MMI) support via an external application - CAM menus (MMI) support via an external application
- The configuration file describing outputs can be reloaded without losing - The configuration file describing outputs can be reloaded without losing
a single packet a single packet
...@@ -49,6 +49,12 @@ Please note that frequencies are in kHz for DVB-S/S2/C, but Hz for DVB-T. ...@@ -49,6 +49,12 @@ Please note that frequencies are in kHz for DVB-S/S2/C, but Hz for DVB-T.
Symbol rates are in symbols/s, and bandwidths in MHz. If you have several Symbol rates are in symbols/s, and bandwidths in MHz. If you have several
linux-dvb cards in the machine, specify which one to use with -a. linux-dvb cards in the machine, specify which one to use with -a.
You generally want to run DVBlast in DVB compliance mode with option -C.
This option will pass through or generate mandatory DVB tables (NIT, SDT,
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.
Other rarely used options are available - run dvblast -h for more Other rarely used options are available - run dvblast -h for more
information. information.
...@@ -226,8 +232,7 @@ Note that IPv6 addresses specified on command line may need to have the square ...@@ -226,8 +232,7 @@ Note that IPv6 addresses specified on command line may need to have the square
brackets escaped (\[ and \]), depending on your shell. brackets escaped (\[ and \]), depending on your shell.
The -u switch disables the PID filters, so that all PIDs, even the The -u switch disables the PID filters, so that all PIDs, even the
unused ones, can be output. With -e, dvblast also streams EIT and SDT packets unused ones, can be output.
for the related services.
Other options are self-understandable, and are listed in dvblast -h. Other options are self-understandable, and are listed in dvblast -h.
- Test and enhance the API for DVB-C, H, and ATSC support - Test and enhance the API for DVB-C, H, and ATSC support
- Win32 support - Win32 support
- Improve build system (autostuff) - Improve build system (autostuff)
- bitstream support
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
...@@ -33,29 +34,29 @@ ...@@ -33,29 +34,29 @@
#include "dvblast.h" #include "dvblast.h"
#include "en50221.h" #include "en50221.h"
#include <dvbpsi/demux.h> #include <bitstream/mpeg/ts.h>
#include <dvbpsi/pat.h> #include <bitstream/mpeg/pes.h>
#include <dvbpsi/eit.h> #include <bitstream/mpeg/psi.h>
#include <dvbpsi/sdt.h> #include <bitstream/dvb/si.h>
#include <dvbpsi/dr.h>
#include <dvbpsi/psi.h>
/***************************************************************************** /*****************************************************************************
* Local declarations * Local declarations
*****************************************************************************/ *****************************************************************************/
#define MAX_PIDS 8192
#define PAT_PID 0x00 #define MIN_SECTION_FRAGMENT PSI_HEADER_SIZE_SYNTAX1
#define SDT_PID 0x11
#define EIT_PID 0x12
#define TDT_PID 0x14
typedef struct ts_pid_t typedef struct ts_pid_t
{ {
int i_refcount; int i_refcount;
int b_pes; int i_psi_refcount;
int i_last_cc; bool b_pes;
int8_t i_last_cc;
int i_demux_fd; int i_demux_fd;
/* biTStream PSI section gathering */
uint8_t *p_psi_buffer;
uint16_t i_psi_buffer_used;
output_t **pp_outputs; output_t **pp_outputs;
int i_nb_outputs; int i_nb_outputs;
} ts_pid_t; } ts_pid_t;
...@@ -63,19 +64,17 @@ typedef struct ts_pid_t ...@@ -63,19 +64,17 @@ typedef struct ts_pid_t
typedef struct sid_t typedef struct sid_t
{ {
uint16_t i_sid, i_pmt_pid; uint16_t i_sid, i_pmt_pid;
dvbpsi_handle p_dvbpsi_handle; uint8_t *p_current_pmt;
dvbpsi_pmt_t *p_current_pmt;
} sid_t; } sid_t;
ts_pid_t p_pids[8192]; ts_pid_t p_pids[MAX_PIDS];
static sid_t **pp_sids = NULL; static sid_t **pp_sids = NULL;
static int i_nb_sids = 0; static int i_nb_sids = 0;
static dvbpsi_handle p_pat_dvbpsi_handle; static PSI_TABLE_DECLARE(pp_current_pat_sections);
static dvbpsi_handle p_sdt_dvbpsi_handle; static PSI_TABLE_DECLARE(pp_next_pat_sections);
static dvbpsi_handle p_eit_dvbpsi_handle; static PSI_TABLE_DECLARE(pp_current_sdt_sections);
static dvbpsi_pat_t *p_current_pat = NULL; static PSI_TABLE_DECLARE(pp_next_sdt_sections);
static dvbpsi_sdt_t *p_current_sdt = NULL;
static mtime_t i_last_dts = -1; static mtime_t i_last_dts = -1;
static int i_demux_fd; static int i_demux_fd;
static int i_nb_errors = 0; static int i_nb_errors = 0;
...@@ -97,20 +96,15 @@ static void UnselectPSI( uint16_t i_sid, uint16_t i_pid ); ...@@ -97,20 +96,15 @@ static void UnselectPSI( uint16_t i_sid, uint16_t i_pid );
static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids, static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids,
uint16_t i_sid, uint16_t i_sid,
const uint16_t *pi_pids, int i_nb_pids ); const uint16_t *pi_pids, int i_nb_pids );
static int SIDIsSelected( uint16_t i_sid ); static bool SIDIsSelected( uint16_t i_sid );
int PIDWouldBeSelected( dvbpsi_pmt_es_t *p_es ); static bool PIDWouldBeSelected( uint8_t *p_es );
static int PMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt ); static bool PMTNeedsDescrambling( uint8_t *p_pmt );
static void SendPAT( mtime_t i_dts ); static void FlushEIT( output_t *p_output, mtime_t i_dts );
static void SendSDT( mtime_t i_dts );
static void SendPMT( sid_t *p_sid, mtime_t i_dts );
static void NewPAT( output_t *p_output ); static void NewPAT( output_t *p_output );
static void NewSDT( output_t *p_output );
static void NewPMT( output_t *p_output ); static void NewPMT( output_t *p_output );
static void PATCallback( void *_unused, dvbpsi_pat_t *p_pat ); static void NewNIT( output_t *p_output );
static void SDTCallback( void *_unused, dvbpsi_sdt_t *p_sdt ); static void NewSDT( output_t *p_output );
static void PMTCallback( void *_unused, dvbpsi_pmt_t *p_pmt ); static void HandlePSIPacket( uint8_t *p_ts, mtime_t i_dts );
static void PSITableCallback( void *_unused, dvbpsi_handle h_dvbpsi,
uint8_t i_table_id, uint16_t i_extension );
/***************************************************************************** /*****************************************************************************
* demux_Open * demux_Open
...@@ -127,23 +121,34 @@ void demux_Open( void ) ...@@ -127,23 +121,34 @@ void demux_Open( void )
{ {
p_pids[i].i_last_cc = -1; p_pids[i].i_last_cc = -1;
p_pids[i].i_demux_fd = -1; p_pids[i].i_demux_fd = -1;
psi_assemble_init( &p_pids[i].p_psi_buffer,
&p_pids[i].i_psi_buffer_used );
} }
if ( b_budget_mode ) if ( b_budget_mode )
i_demux_fd = pf_SetFilter(8192); i_demux_fd = pf_SetFilter(8192);
SetPID(PAT_PID); /* PAT */ psi_table_init( pp_current_pat_sections );
p_pat_dvbpsi_handle = dvbpsi_AttachPAT( PATCallback, NULL ); psi_table_init( pp_next_pat_sections );
SetPID(PAT_PID);
p_pids[PAT_PID].i_psi_refcount++;
if( b_enable_epg ) if( b_dvb_compliance )
{ {
SetPID(SDT_PID); /* SDT */ SetPID(NIT_PID);
p_sdt_dvbpsi_handle = dvbpsi_AttachDemux( PSITableCallback, NULL ); 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++;
SetPID(EIT_PID);
p_pids[EIT_PID].i_psi_refcount++;
SetPID(EIT_PID); /* EIT */ SetPID(RST_PID);
p_eit_dvbpsi_handle = dvbpsi_AttachDemux( PSITableCallback, NULL );
SetPID(TDT_PID); /* TDT */ SetPID(TDT_PID);
} }
} }
...@@ -168,25 +173,23 @@ void demux_Run( block_t *p_ts ) ...@@ -168,25 +173,23 @@ void demux_Run( block_t *p_ts )
*****************************************************************************/ *****************************************************************************/
static void demux_Handle( block_t *p_ts ) static void demux_Handle( block_t *p_ts )
{ {
mtime_t i_wallclock = mdate(); uint16_t i_pid = ts_get_pid( p_ts->p_ts );
uint16_t i_pid = block_GetPID( p_ts ); uint8_t i_cc = ts_get_cc( p_ts->p_ts );
uint8_t i_cc = block_GetCC( p_ts );
int i; int i;
if ( block_GetSync( p_ts ) != 0x47 ) if ( !ts_validate( p_ts->p_ts ) )
{ {
msg_Warn( NULL, "invalid sync (0x%x)", p_ts->p_ts[0] ); msg_Warn( NULL, "lost TS sync" );
block_Delete( p_ts ); block_Delete( p_ts );
return; return;
} }
if ( i_pid != PADDING_PID && p_pids[i_pid].i_last_cc != -1 if ( i_pid != PADDING_PID && p_pids[i_pid].i_last_cc != -1
&& p_pids[i_pid].i_last_cc != i_cc /* dup */ && !ts_check_duplicate( i_cc, p_pids[i_pid].i_last_cc )
&& (p_pids[i_pid].i_last_cc + 17 - i_cc) % 16 ) && ts_check_discontinuity( i_cc, p_pids[i_pid].i_last_cc ) )
msg_Warn( NULL, "discontinuity for PID %d", i_pid ); msg_Warn( NULL, "TS discontinuity" );
p_pids[i_pid].i_last_cc = i_cc;
if ( block_HasTransportError( p_ts ) ) if ( ts_get_transporterror( p_ts->p_ts ) )
{ {
msg_Warn( NULL, "transport_error_indicator" ); msg_Warn( NULL, "transport_error_indicator" );
i_nb_errors++; i_nb_errors++;
...@@ -203,93 +206,65 @@ static void demux_Handle( block_t *p_ts ) ...@@ -203,93 +206,65 @@ static void demux_Handle( block_t *p_ts )
dvb_Reset(); dvb_Reset();
} }
if ( p_pids[i_pid].i_refcount ) /* PSI parsing */
if ( b_dvb_compliance && (i_pid == TDT_PID || i_pid == RST_PID) )
{ {
if ( i_pid == PAT_PID ) for ( i = 0; i < i_nb_outputs; i++ )
{
dvbpsi_PushPacket( p_pat_dvbpsi_handle, p_ts->p_ts );
if ( block_UnitStart( p_ts ) )
SendPAT( p_ts->i_dts );
}
else if ( b_enable_epg && i_pid == EIT_PID )
{ {
dvbpsi_PushPacket( p_eit_dvbpsi_handle, p_ts->p_ts ); if ( ( pp_outputs[i]->i_config & OUTPUT_VALID )
} && pp_outputs[i]->p_sdt_section )
else if ( b_enable_epg && i_pid == SDT_PID ) output_Put( pp_outputs[i], p_ts );
{
dvbpsi_PushPacket( p_sdt_dvbpsi_handle, p_ts->p_ts );
if ( block_UnitStart( p_ts ) )
{
uint8_t *p_payload = block_GetPayload( p_ts );
if ( p_payload && *(p_payload + *p_payload + 1) == 0x42 )
SendSDT( p_ts->i_dts );
}
}
else if ( b_enable_epg && i_pid == TDT_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 );
}
}
else
{
for ( i = 0; i < i_nb_sids; i++ )
{
if ( pp_sids[i]->i_sid && pp_sids[i]->i_pmt_pid == i_pid )
{
dvbpsi_PushPacket( pp_sids[i]->p_dvbpsi_handle,
p_ts->p_ts );
if ( block_UnitStart( p_ts ) )
SendPMT( pp_sids[i], p_ts->i_dts );
}
}
} }
} }
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;
if ( block_HasPCR( p_ts ) ) /* PCR handling */
if ( ts_has_adaptation( p_ts->p_ts )
&& ts_get_adaptation( p_ts->p_ts )
&& tsaf_has_pcr( p_ts->p_ts ) )
{ {
mtime_t i_timestamp = block_GetPCR( p_ts ); mtime_t i_timestamp = tsaf_get_pcr( p_ts->p_ts );
int j;
for ( i = 0; i < i_nb_sids; i++ ) for ( j = 0; j < i_nb_sids; j++ )
{ {
if ( pp_sids[i]->i_sid && pp_sids[i]->p_current_pmt != NULL sid_t *p_sid = pp_sids[j];
&& pp_sids[i]->p_current_pmt->i_pcr_pid == i_pid ) if ( p_sid->i_sid && p_sid->p_current_pmt != NULL
&& pmt_get_pcrpid( p_sid->p_current_pmt ) == i_pid )
{ {
uint16_t i_sid = pp_sids[i]->i_sid; for ( i = 0; i < i_nb_outputs; i++ )
int j;
for ( j = 0; j < i_nb_outputs; j++ )
{ {
if ( pp_outputs[j]->i_sid == i_sid ) output_t *p_output = pp_outputs[i];
if ( p_output->i_sid == p_sid->i_sid )
{ {
pp_outputs[j]->i_ref_timestamp = i_timestamp; p_output->i_ref_timestamp = i_timestamp;
pp_outputs[j]->i_ref_wallclock = p_ts->i_dts; p_output->i_ref_wallclock = p_ts->i_dts;
} }
} }
} }
} }
} }
/* Output */
for ( i = 0; i < p_pids[i_pid].i_nb_outputs; i++ ) for ( i = 0; i < p_pids[i_pid].i_nb_outputs; i++ )
{ {
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->i_config & OUTPUT_WATCH) &&
block_UnitStart( p_ts ) ) ts_get_unitstart( p_ts->p_ts ) )
{ {
uint8_t *p_payload; uint8_t *p_payload;
if ( block_GetScrambling( p_ts ) || if ( ts_get_scrambling( p_ts->p_ts ) ||
( p_pids[i_pid].b_pes ( p_pids[i_pid].b_pes
&& (p_payload = block_GetPayload( p_ts )) != NULL && (p_payload = ts_payload( p_ts->p_ts )) + 3
&& p_payload + 3 < p_ts->p_ts + TS_SIZE < p_ts->p_ts + TS_SIZE
&& (p_payload[0] != 0 || p_payload[1] != 0 && !pes_validate(p_payload) ) )
|| p_payload[2] != 1) ) )
{ {
p_output->i_nb_errors++; p_output->i_nb_errors++;
p_output->i_last_error = i_wallclock; p_output->i_last_error = i_wallclock;
...@@ -311,6 +286,11 @@ static void demux_Handle( block_t *p_ts ) ...@@ -311,6 +286,11 @@ static void demux_Handle( block_t *p_ts )
} }
output_Put( p_output, p_ts ); output_Put( p_output, p_ts );
if ( p_output->p_eit_ts_buffer != NULL
&& p_ts->i_dts > p_output->p_eit_ts_buffer->i_dts
+ MAX_EIT_RETENTION )
FlushEIT( p_output, p_ts->i_dts );
} }
} }
...@@ -387,7 +367,7 @@ void demux_Change( output_t *p_output, uint16_t i_sid, ...@@ -387,7 +367,7 @@ void demux_Change( output_t *p_output, uint16_t i_sid,
{ {
if ( pp_sids[i]->i_sid == i_old_sid ) if ( pp_sids[i]->i_sid == i_old_sid )
{ {
if ( pp_sids[i]->p_current_pmt != NULL if ( pp_sids[i]->p_current_pmt != NULL
&& PMTNeedsDescrambling( pp_sids[i]->p_current_pmt ) ) && PMTNeedsDescrambling( pp_sids[i]->p_current_pmt ) )
en50221_UpdatePMT( pp_sids[i]->p_current_pmt ); en50221_UpdatePMT( pp_sids[i]->p_current_pmt );
break; break;
...@@ -449,6 +429,7 @@ void demux_Change( output_t *p_output, uint16_t i_sid, ...@@ -449,6 +429,7 @@ void demux_Change( output_t *p_output, uint16_t i_sid,
if ( sid_change ) if ( sid_change )
{ {
NewSDT( p_output ); NewSDT( p_output );
NewNIT( p_output );
NewPAT( p_output ); NewPAT( p_output );
NewPMT( p_output ); NewPMT( p_output );
} }
...@@ -572,7 +553,8 @@ static void SelectPID( uint16_t i_sid, uint16_t i_pid ) ...@@ -572,7 +553,8 @@ 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 ) && pp_outputs[i]->i_sid == i_sid if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid
&& !pp_outputs[i]->i_nb_pids ) && !pp_outputs[i]->i_nb_pids )
StartPID( pp_outputs[i], i_pid ); StartPID( pp_outputs[i], i_pid );
} }
...@@ -582,7 +564,8 @@ static void UnselectPID( uint16_t i_sid, uint16_t i_pid ) ...@@ -582,7 +564,8 @@ 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 ) && pp_outputs[i]->i_sid == i_sid if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid
&& !pp_outputs[i]->i_nb_pids ) && !pp_outputs[i]->i_nb_pids )
StopPID( pp_outputs[i], i_pid ); StopPID( pp_outputs[i], i_pid );
} }
...@@ -594,8 +577,11 @@ static void SelectPSI( uint16_t i_sid, uint16_t i_pid ) ...@@ -594,8 +577,11 @@ static void SelectPSI( uint16_t i_sid, uint16_t i_pid )
{ {
int i; int i;
p_pids[i_pid].i_psi_refcount++;
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid )
SetPID( i_pid ); SetPID( i_pid );
} }
...@@ -603,8 +589,14 @@ static void UnselectPSI( uint16_t i_sid, uint16_t i_pid ) ...@@ -603,8 +589,14 @@ static void UnselectPSI( uint16_t i_sid, uint16_t i_pid )
{ {
int i; int i;
p_pids[i_pid].i_psi_refcount--;
if ( !p_pids[i_pid].i_psi_refcount )
psi_assemble_reset( &p_pids[i_pid].p_psi_buffer,
&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 ) && pp_outputs[i]->i_sid == i_sid ) if ( (pp_outputs[i]->i_config & OUTPUT_VALID)
&& pp_outputs[i]->i_sid == i_sid )
UnsetPID( i_pid ); UnsetPID( i_pid );
} }
...@@ -615,10 +607,11 @@ static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids, ...@@ -615,10 +607,11 @@ static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids,
uint16_t i_sid, uint16_t i_sid,
const uint16_t *pi_pids, int i_nb_pids ) const uint16_t *pi_pids, int i_nb_pids )
{ {
dvbpsi_pmt_t *p_pmt = NULL; uint8_t *p_pmt = NULL;
uint16_t i_pmt_pid; uint16_t i_pmt_pid, i_pcr_pid;
dvbpsi_pmt_es_t *p_es; uint8_t *p_es;
int i; int i;
uint8_t j;
if ( i_nb_pids || i_sid == 0 ) if ( i_nb_pids || i_sid == 0 )
{ {
...@@ -644,86 +637,87 @@ static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids, ...@@ -644,86 +637,87 @@ static void GetPIDS( uint16_t **ppi_wanted_pids, int *pi_nb_wanted_pids,
if ( p_pmt == NULL ) if ( p_pmt == NULL )
return; return;
i = 0; i_pcr_pid = pmt_get_pcrpid( p_pmt );
for ( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) j = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{
j++;
if ( PIDWouldBeSelected( p_es ) ) if ( PIDWouldBeSelected( p_es ) )
{ {
*ppi_wanted_pids = realloc( *ppi_wanted_pids, *ppi_wanted_pids = realloc( *ppi_wanted_pids,
(*pi_nb_wanted_pids + 1) * sizeof(uint16_t) ); (*pi_nb_wanted_pids + 1) * sizeof(uint16_t) );
(*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = p_es->i_pid; (*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = pmtn_get_pid( p_es );
} }
}
if ( p_pmt->i_pcr_pid != PADDING_PID && p_pmt->i_pcr_pid != i_pmt_pid if ( i_pcr_pid != PADDING_PID && i_pcr_pid != i_pmt_pid
&& !IsIn( *ppi_wanted_pids, *pi_nb_wanted_pids, p_pmt->i_pcr_pid ) ) && !IsIn( *ppi_wanted_pids, *pi_nb_wanted_pids, i_pcr_pid ) )
{ {
*ppi_wanted_pids = realloc( *ppi_wanted_pids, *ppi_wanted_pids = realloc( *ppi_wanted_pids,
(*pi_nb_wanted_pids + 1) * sizeof(uint16_t) ); (*pi_nb_wanted_pids + 1) * sizeof(uint16_t) );
(*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = p_pmt->i_pcr_pid; (*ppi_wanted_pids)[(*pi_nb_wanted_pids)++] = i_pcr_pid;
} }
} }
/***************************************************************************** /*****************************************************************************
* WritePSISection * OutputPSISection
*****************************************************************************/ *****************************************************************************/
static block_t *WritePSISection( dvbpsi_psi_section_t *p_section, static void OutputPSISection( output_t *p_output, uint8_t *p_section,
uint16_t i_pid, uint8_t *pi_cc, uint16_t i_pid, uint8_t *pi_cc, mtime_t i_dts,
mtime_t i_dts ) block_t **pp_ts_buffer,
uint8_t *pi_ts_buffer_offset )
{ {
block_t *p_block, **pp_last = &p_block; uint16_t i_section_length = psi_get_length(p_section) + PSI_HEADER_SIZE;
uint32_t i_length; uint16_t i_section_offset = 0;
uint8_t *p_data = p_section->p_data;
int b_first = 1;
i_length = (uint32_t)( p_section->p_payload_end - p_section->p_data ) +
( p_section->b_syntax_indicator ? 4 : 0 );
do do
{ {
uint32_t i_copy = i_length > (184 - b_first) ? (184 - b_first) : block_t *p_block;
i_length; uint8_t *p;
int i; uint8_t i_ts_offset;
block_t *p_ts;
p_ts = *pp_last = block_New();
pp_last = &p_ts->p_next;
/* write header
* 8b 0x47 sync byte
* 1b transport_error_indicator
* 1b payload_unit_start
* 1b transport_priority
* 13b pid
* 2b transport_scrambling_control
* 2b if adaptation_field 0x03 else 0x01
* 4b continuity_counter
*/
p_ts->p_ts[0] = 0x47;
p_ts->p_ts[1] = ( b_first ? 0x40 : 0x00 ) | ( ( i_pid >> 8 ) & 0x1f );
p_ts->p_ts[2] = i_pid & 0xff;
p_ts->p_ts[3] = 0x10 | *pi_cc;
(*pi_cc)++;
*pi_cc &= 0xf;
if ( b_first ) if ( pp_ts_buffer != NULL && *pp_ts_buffer != NULL )
p_ts->p_ts[4] = 0; /* pointer */ {
p_block = *pp_ts_buffer;
i_ts_offset = *pi_ts_buffer_offset;
}
else
{
p_block = block_New();
p_block->i_dts = i_dts;
i_ts_offset = 0;
}
p = p_block->p_ts;
/* copy payload */ psi_split_section( p, &i_ts_offset, p_section, &i_section_offset );
memcpy( &p_ts->p_ts[4 + b_first], p_data, i_copy );
/* stuffing */ ts_set_pid( p, i_pid );
for( i = 4 + b_first + i_copy; i < 188; i++ ) ts_set_cc( p, *pi_cc );
p_ts->p_ts[i] = 0xff; (*pi_cc)++;
*pi_cc &= 0xf;
p_ts->i_dts = i_dts; if ( i_section_offset == i_section_length )
{
if ( i_ts_offset > MIN_SECTION_FRAGMENT && pp_ts_buffer != NULL )
{
*pp_ts_buffer = p_block;
*pi_ts_buffer_offset = i_ts_offset;
break;
}
else
psi_split_end( p, &i_ts_offset );
}
b_first = 0; p_block->i_dts = i_dts;
i_length -= i_copy; p_block->i_refcount--;
p_data += i_copy; output_Put( p_output, p_block );
if ( pp_ts_buffer != NULL )
{
*pp_ts_buffer = NULL;
*pi_ts_buffer_offset = 0;
}
} }
while ( i_length > 0 ); while ( i_section_offset < i_section_length );
return p_block;
} }
/***************************************************************************** /*****************************************************************************
...@@ -735,238 +729,219 @@ static void SendPAT( mtime_t i_dts ) ...@@ -735,238 +729,219 @@ static void SendPAT( mtime_t i_dts )
for ( i = 0; i < i_nb_outputs; i++ ) for ( i = 0; i < i_nb_outputs; i++ )
{ {
if ( !( pp_outputs[i]->i_config & OUTPUT_VALID ) ) output_t *p_output = pp_outputs[i];
if ( !(p_output->i_config & OUTPUT_VALID) )
continue; continue;
if ( pp_outputs[i]->p_pat_section == NULL && if ( p_output->p_pat_section == NULL &&
pp_outputs[i]->p_sdt_section != NULL && p_current_pat != NULL ) psi_table_validate(pp_current_pat_sections) )
{ {
dvbpsi_pat_t pat; /* SID doesn't exist - build an empty PAT. */
uint8_t *p;
p_output->i_pat_version++;
p = p_output->p_pat_section = psi_allocate();
pat_init( p );
pat_set_length( p, 0 );
if ( b_unique_tsid ) if ( b_unique_tsid )
dvbpsi_InitPAT( &pat, pp_outputs[i]->i_ts_id, pat_set_tsid( p, p_output->i_ts_id );
pp_outputs[i]->i_pat_version, 1 );
else else
dvbpsi_InitPAT( &pat, p_current_pat->i_ts_id, pat_set_tsid( p,
pp_outputs[i]->i_pat_version, 1 ); psi_table_get_tableidext(pp_current_pat_sections) );
psi_set_version( p, p_output->i_pat_version );
pp_outputs[i]->p_pat_section = dvbpsi_GenPATSections( &pat, 0 ); psi_set_current( p );
psi_set_section( p, 0 );
psi_set_lastsection( p, 0 );
psi_set_crc( p_output->p_pat_section );
} }
if ( pp_outputs[i]->p_pat_section != NULL ) if ( p_output->p_pat_section != NULL )
{ OutputPSISection( p_output, p_output->p_pat_section, PAT_PID,
block_t *p_block; &p_output->i_pat_cc, i_dts, NULL, NULL );
}
}
p_block = WritePSISection( pp_outputs[i]->p_pat_section, PAT_PID, /*****************************************************************************
&pp_outputs[i]->i_pat_cc, i_dts ); * SendPMT
while ( p_block != NULL ) *****************************************************************************/
{ static void SendPMT( sid_t *p_sid, mtime_t i_dts )
block_t *p_next = p_block->p_next; {
p_block->i_refcount--; int i;
output_Put( pp_outputs[i], p_block );
p_block = p_next; 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_sid == p_sid->i_sid
&& p_output->p_pmt_section != NULL )
OutputPSISection( p_output, p_output->p_pmt_section,
p_sid->i_pmt_pid, &p_output->i_pmt_cc, i_dts,
NULL, NULL );
} }
} }
/***************************************************************************** /*****************************************************************************
* SendSDT * SendNIT
*****************************************************************************/ *****************************************************************************/
static void SendSDT( mtime_t i_dts ) static void SendNIT( mtime_t i_dts )
{ {
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 ) && pp_outputs[i]->p_sdt_section != NULL ) output_t *p_output = pp_outputs[i];
{
block_t *p_block;
p_block = WritePSISection( pp_outputs[i]->p_sdt_section, SDT_PID, if ( (p_output->i_config & OUTPUT_VALID)
&pp_outputs[i]->i_sdt_cc, i_dts ); && p_output->p_nit_section != NULL )
while ( p_block != NULL ) OutputPSISection( p_output, p_output->p_nit_section, NIT_PID,
{ &p_output->i_nit_cc, i_dts, NULL, NULL );
block_t *p_next = p_block->p_next;
p_block->i_refcount--;
output_Put( pp_outputs[i], p_block );
p_block = p_next;
}
}
} }
} }
/***************************************************************************** /*****************************************************************************
* SendPMT * SendSDT
*****************************************************************************/ *****************************************************************************/
static void SendPMT( sid_t *p_sid, mtime_t i_dts ) static void SendSDT( mtime_t i_dts )
{ {
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 ) && pp_outputs[i]->i_sid == p_sid->i_sid ) output_t *p_output = pp_outputs[i];
{
if ( pp_outputs[i]->p_pmt_section != NULL )
{
block_t *p_block;
p_block = WritePSISection( pp_outputs[i]->p_pmt_section, if ( (p_output->i_config & OUTPUT_VALID)
p_sid->i_pmt_pid, && p_output->p_sdt_section != NULL )
&pp_outputs[i]->i_pmt_cc, i_dts ); OutputPSISection( p_output, p_output->p_sdt_section, SDT_PID,
while ( p_block != NULL ) &p_output->i_sdt_cc, i_dts, NULL, NULL );
{
block_t *p_next = p_block->p_next;
p_block->i_refcount--;
output_Put( pp_outputs[i], p_block );
p_block = p_next;
}
}
}
} }
} }
/***************************************************************************** /*****************************************************************************
* SendEIT * SendEIT
*****************************************************************************/ *****************************************************************************/
static void SendEIT( dvbpsi_psi_section_t *p_section, uint16_t i_sid, static void SendEIT( sid_t *p_sid, mtime_t i_dts, uint8_t *p_eit )
uint8_t i_table_id )
{ {
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 ) && pp_outputs[i]->i_sid == i_sid ) output_t *p_output = pp_outputs[i];
{
block_t *p_block;
if( b_unique_tsid ) if ( (p_output->i_config & OUTPUT_VALID)
&& p_output->i_sid == p_sid->i_sid )
{
if ( b_unique_tsid )
{ {
p_section->p_data[8] = (pp_outputs[i]->i_ts_id >> 8) & 0xff; eit_set_tsid( p_eit, p_output->i_ts_id );
p_section->p_data[9] = pp_outputs[i]->i_ts_id & 0xff; psi_set_crc( p_eit );
dvbpsi_BuildPSISection( p_section );
} }
p_block = WritePSISection( p_section, OutputPSISection( p_output, p_eit, EIT_PID, &p_output->i_eit_cc,
EIT_PID, i_dts, &p_output->p_eit_ts_buffer,
&pp_outputs[i]->i_eit_cc, i_wallclock ); &p_output->i_eit_ts_buffer_offset );
while ( p_block != NULL )
{
block_t *p_next = p_block->p_next;
p_block->i_refcount--;
output_Put( pp_outputs[i], p_block );
p_block = p_next;
}
} }
} }
} }
/*****************************************************************************
* FlushEIT
*****************************************************************************/
static void FlushEIT( output_t *p_output, mtime_t i_dts )
{
block_t *p_block = p_output->p_eit_ts_buffer;
psi_split_end( p_block->p_ts, &p_output->i_eit_ts_buffer_offset );
p_block->i_dts = i_dts;
p_block->i_refcount--;
output_Put( p_output, p_block );
p_output->p_eit_ts_buffer = NULL;
p_output->i_eit_ts_buffer_offset = 0;
}
/***************************************************************************** /*****************************************************************************
* NewPAT * NewPAT
*****************************************************************************/ *****************************************************************************/
static void NewPAT( output_t *p_output ) static void NewPAT( output_t *p_output )
{ {
dvbpsi_pat_t pat; const uint8_t *p_program;
dvbpsi_pat_program_t *p_program; uint8_t *p;
if ( p_output->p_pat_section != NULL ) free( p_output->p_pat_section );
dvbpsi_DeletePSISections( p_output->p_pat_section );
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->i_sid ) return;
if ( p_current_pat == NULL ) return; if ( !psi_table_validate(pp_current_pat_sections) ) return;
for( p_program = p_current_pat->p_first_program; p_program != NULL;
p_program = p_program->p_next )
if ( p_program->i_number == p_output->i_sid )
break;
p_program = pat_table_find_program( pp_current_pat_sections,
p_output->i_sid );
if ( p_program == NULL ) return; if ( p_program == NULL ) return;
p = p_output->p_pat_section = psi_allocate();
pat_init( p );
pat_set_length( p, PAT_PROGRAM_SIZE );
if ( b_unique_tsid ) if ( b_unique_tsid )
dvbpsi_InitPAT( &pat, p_output->i_ts_id, p_output->i_pat_version, 1 ); pat_set_tsid( p, p_output->i_ts_id );
else else
dvbpsi_InitPAT( &pat, p_current_pat->i_ts_id, p_output->i_pat_version, 1 ); pat_set_tsid( p, psi_table_get_tableidext(pp_current_pat_sections) );
psi_set_version( p, p_output->i_pat_version );
dvbpsi_PATAddProgram( &pat, p_output->i_sid, p_program->i_pid ); psi_set_current( p );
psi_set_section( p, 0 );
p_output->p_pat_section = dvbpsi_GenPATSections( &pat, 0 ); psi_set_lastsection( p, 0 );
dvbpsi_EmptyPAT( &pat );
p = pat_get_program( p, 0 );
patn_init( p );
patn_set_program( p, p_output->i_sid );
patn_set_pid( p, patn_get_pid( p_program ) );
psi_set_crc( p_output->p_pat_section );
} }
/***************************************************************************** /*****************************************************************************
* NewSDT * NewPMT
*****************************************************************************/ *****************************************************************************/
static void NewSDT( output_t *p_output ) static void CopyDescriptors( uint8_t *p_descs, uint8_t *p_current_descs )
{ {
dvbpsi_sdt_t sdt; uint8_t *p_desc;
dvbpsi_sdt_service_t *p_service, *p_new_service ; const uint8_t *p_current_desc;
dvbpsi_descriptor_t *p_descriptor; uint16_t j = 0, k = 0;
if ( p_output->p_sdt_section != NULL ) descs_set_length( p_descs, DESCS_MAX_SIZE );
dvbpsi_DeletePSISections( p_output->p_sdt_section );
p_output->p_sdt_section = NULL;
p_output->i_sdt_version++;
if ( !p_output->i_sid ) return; while ( (p_current_desc = descs_get_desc( p_current_descs, j )) != NULL )
if ( p_current_sdt == NULL ) return; {
uint8_t i_tag = desc_get_tag( p_current_desc );
for( p_service = p_current_sdt->p_first_service; p_service != NULL; j++;
p_service = p_service->p_next ) /* A descrambled stream is not supposed to carry CA descriptors. */
if ( p_service->i_service_id == p_output->i_sid ) if ( i_ca_handle && i_tag == 0x9 ) continue;
break;
if ( p_service == NULL ) p_desc = descs_get_desc( p_descs, k );
{ if ( p_desc == NULL ) continue; /* This shouldn't happen */
if ( p_output->p_pat_section != NULL && k++;
p_output->p_pat_section->i_length == 9 ) memcpy( p_desc, p_current_desc,
{ DESC_HEADER_SIZE + desc_get_length( p_current_desc ) );
dvbpsi_DeletePSISections( p_output->p_pat_section );
p_output->p_pat_section = NULL;
p_output->i_pat_version++;
}
return;
} }
if ( b_unique_tsid ) p_desc = descs_get_desc( p_descs, k );
dvbpsi_InitSDT( &sdt, p_output->i_ts_id, if ( p_desc == NULL )
p_output->i_sdt_version, 1, /* This shouldn't happen if the incoming PMT is valid */
p_current_sdt->i_network_id ); descs_set_length( p_descs, 0 );
else else
dvbpsi_InitSDT( &sdt, p_current_sdt->i_ts_id, descs_set_length( p_descs, p_desc - p_descs - DESCS_HEADER_SIZE );
p_output->i_sdt_version, 1,
p_current_sdt->i_network_id );
p_new_service = dvbpsi_SDTAddService( &sdt,
p_service->i_service_id, p_service->b_eit_schedule,
p_service->b_eit_present, p_service->i_running_status, 0 );
for( p_descriptor = p_service->p_first_descriptor; p_descriptor != NULL;
p_descriptor = p_descriptor->p_next )
dvbpsi_SDTServiceAddDescriptor( p_new_service,
p_descriptor->i_tag, p_descriptor->i_length,
p_descriptor->p_data );
p_output->p_sdt_section = dvbpsi_GenSDTSections( &sdt );
dvbpsi_EmptySDT( &sdt );
} }
/*****************************************************************************
* NewPMT
*****************************************************************************/
static void NewPMT( output_t *p_output ) static void NewPMT( output_t *p_output )
{ {
dvbpsi_pmt_t pmt, *p_current_pmt; uint8_t *p_current_pmt;
dvbpsi_pmt_es_t *p_current_es; uint8_t *p_es, *p_current_es;
dvbpsi_descriptor_t *p_dr; uint8_t *p;
int i; int i;
uint16_t j, k;
if ( p_output->p_pmt_section != NULL ) free( p_output->p_pmt_section );
dvbpsi_DeletePSISections( p_output->p_pmt_section );
p_output->p_pmt_section = NULL; p_output->p_pmt_section = NULL;
p_output->i_pmt_version++; p_output->i_pmt_version++;
...@@ -976,101 +951,243 @@ static void NewPMT( output_t *p_output ) ...@@ -976,101 +951,243 @@ static void NewPMT( output_t *p_output )
if ( pp_sids[i]->i_sid == p_output->i_sid ) if ( pp_sids[i]->i_sid == p_output->i_sid )
break; break;
if ( i == i_nb_sids ) if ( i == i_nb_sids ) return;
return;
if ( pp_sids[i]->p_current_pmt == NULL ) return; if ( pp_sids[i]->p_current_pmt == NULL ) return;
p_current_pmt = pp_sids[i]->p_current_pmt; p_current_pmt = pp_sids[i]->p_current_pmt;
dvbpsi_InitPMT( &pmt, p_output->i_sid, p_output->i_pmt_version, 1, p = p_output->p_pmt_section = psi_allocate();
p_current_pmt->i_pcr_pid ); pmt_init( p );
psi_set_length( p, PSI_MAX_SIZE );
pmt_set_program( p, p_output->i_sid );
psi_set_version( p, p_output->i_pmt_version );
psi_set_current( p );
pmt_set_pcrpid( p, pmt_get_pcrpid( p_current_pmt ) );
pmt_set_desclength( p, 0 );
for ( p_dr = p_current_pmt->p_first_descriptor; p_dr != NULL; CopyDescriptors( pmt_get_descs( p ), pmt_get_descs( p_current_pmt ) );
p_dr = p_dr->p_next )
dvbpsi_PMTAddDescriptor( &pmt, p_dr->i_tag, p_dr->i_length,
p_dr->p_data );
for( p_current_es = p_current_pmt->p_first_es; p_current_es != NULL; j = 0; k = 0;
p_current_es = p_current_es->p_next ) while ( (p_current_es = pmt_get_es( p_current_pmt, j )) != NULL )
{ {
if ( (!p_output->i_nb_pids && PIDWouldBeSelected( p_current_es )) uint16_t i_pid = pmtn_get_pid( p_current_es );
|| IsIn( p_output->pi_pids, p_output->i_nb_pids,
p_current_es->i_pid ) )
{
dvbpsi_pmt_es_t *p_es = dvbpsi_PMTAddES( &pmt, p_current_es->i_type,
p_current_es->i_pid );
for ( p_dr = p_current_es->p_first_descriptor; p_dr != NULL; j++;
p_dr = p_dr->p_next ) if ( (p_output->i_nb_pids || !PIDWouldBeSelected( p_current_es ))
dvbpsi_PMTESAddDescriptor( p_es, p_dr->i_tag, p_dr->i_length, && !IsIn( p_output->pi_pids, p_output->i_nb_pids, i_pid ) )
p_dr->p_data ); continue;
}
p_es = pmt_get_es( p, k );
if ( p_es == NULL ) continue; /* This shouldn't happen */
k++;
pmtn_init( p_es );
pmtn_set_streamtype( p_es, pmtn_get_streamtype( p_current_es ) );
pmtn_set_pid( p_es, i_pid );
pmtn_set_desclength( p_es, 0 );
CopyDescriptors( pmtn_get_descs( p_es ),
pmtn_get_descs( p_current_es ) );
} }
p_output->p_pmt_section = dvbpsi_GenPMTSections( &pmt ); p_es = pmt_get_es( p, k );
dvbpsi_EmptyPMT( &pmt ); if ( p_es == NULL )
/* This shouldn't happen if the incoming PMT is valid */
pmt_set_length( p, 0 );
else
pmt_set_length( p, p_es - p - PMT_HEADER_SIZE );
psi_set_crc( p );
} }
/***************************************************************************** /*****************************************************************************
* UpdatePAT * NewNIT
*****************************************************************************/ *****************************************************************************/
static void UpdatePAT( uint16_t i_sid ) static void NewNIT( output_t *p_output )
{ {
int i; uint8_t *p_ts;
uint8_t *p_descs;
uint8_t *p_desc;
uint8_t *p_header2;
uint8_t *p;
for ( i = 0; i < i_nb_outputs; i++ ) free( p_output->p_nit_section );
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) && pp_outputs[i]->i_sid == i_sid ) p_output->p_nit_section = NULL;
NewPAT( pp_outputs[i] ); p_output->i_nit_version++;
if ( !p_output->i_sid ) return;
if ( !psi_table_validate(pp_current_pat_sections) ) return;
p = p_output->p_nit_section = psi_allocate();
nit_init( p, true );
nit_set_length( p, PSI_MAX_SIZE );
nit_set_nid( p, i_network_id );
psi_set_version( p, p_output->i_nit_version );
psi_set_current( p );
psi_set_section( p, 0 );
psi_set_lastsection( p, 0 );
nit_set_desclength( p, DESCS_MAX_SIZE );
p_descs = nit_get_descs( p );
p_desc = descs_get_desc( p_descs, 0 );
desc40_init( p_desc );
desc40_set_networkname( p_desc, psz_network_name );
p_desc = descs_get_desc( p_descs, 1 );
descs_set_length( p_descs, p_desc - p_descs - DESCS_HEADER_SIZE );
p_header2 = nit_get_header2( p );
nith_init( p_header2 );
nith_set_tslength( p_header2, NIT_TS_SIZE );
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_onid( p_ts, i_network_id );
nitn_set_desclength( p_ts, 0 );
p_ts = nit_get_ts( p, 1 );
if ( p_ts == NULL )
/* This shouldn't happen */
nit_set_length( p, 0 );
else
nit_set_length( p, p_ts - p - NIT_HEADER_SIZE );
psi_set_crc( p_output->p_nit_section );
}
/*****************************************************************************
* NewSDT
*****************************************************************************/
static void NewSDT( output_t *p_output )
{
uint8_t *p_service, *p_current_service;
uint8_t *p;
free( p_output->p_sdt_section );
p_output->p_sdt_section = NULL;
p_output->i_sdt_version++;
if ( !p_output->i_sid ) return;
if ( !psi_table_validate(pp_current_sdt_sections) ) return;
p_current_service = sdt_table_find_service( pp_current_sdt_sections,
p_output->i_sid );
if ( p_current_service == NULL )
{
if ( p_output->p_pat_section != NULL &&
pat_get_program( p_output->p_pat_section, 0 ) == NULL )
{
/* Empty PAT and no SDT anymore */
free( p_output->p_pat_section );
p_output->p_pat_section = NULL;
p_output->i_pat_version++;
}
return;
}
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) );
psi_set_version( p, p_output->i_sdt_version );
psi_set_current( p );
psi_set_section( p, 0 );
psi_set_lastsection( p, 0 );
sdt_set_onid( p,
sdt_get_onid( psi_table_get_section( pp_current_sdt_sections, 0 ) ) );
p_service = sdt_get_service( p, 0 );
sdtn_init( p_service );
sdtn_set_sid( p_service, p_output->i_sid );
if ( sdtn_get_eitschedule(p_current_service) )
sdtn_set_eitschedule(p_service);
if ( sdtn_get_eitpresent(p_current_service) )
sdtn_set_eitpresent(p_service);
sdtn_set_running( p_service, sdtn_get_running(p_current_service) );
/* Do not set free_ca */
sdtn_set_desclength( p_service, sdtn_get_desclength(p_current_service) );
memcpy( descs_get_desc( sdtn_get_descs(p_service), 0 ),
descs_get_desc( sdtn_get_descs(p_current_service), 0 ),
sdtn_get_desclength(p_current_service) );
p_service = sdt_get_service( p, 1 );
if ( p_service == NULL )
/* This shouldn't happen if the incoming SDT is valid */
sdt_set_length( p, 0 );
else
sdt_set_length( p, p_service - p - SDT_HEADER_SIZE );
psi_set_crc( p_output->p_sdt_section );
} }
/***************************************************************************** /*****************************************************************************
* UpdatePMT * UpdatePAT/PMT/SDT/NIT
*****************************************************************************/ *****************************************************************************/
static void UpdatePMT( uint16_t i_sid ) #define DECLARE_UPDATE_FUNC( table ) \
static void Update##table( uint16_t i_sid ) \
{ \
int i; \
\
for ( i = 0; i < i_nb_outputs; i++ ) \
if ( ( pp_outputs[i]->i_config & OUTPUT_VALID ) \
&& pp_outputs[i]->i_sid == i_sid ) \
New##table( pp_outputs[i] ); \
}
DECLARE_UPDATE_FUNC(PAT)
DECLARE_UPDATE_FUNC(PMT)
DECLARE_UPDATE_FUNC(SDT)
static void UpdateNIT(void)
{ {
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 ) && pp_outputs[i]->i_sid == i_sid ) if ( pp_outputs[i]->i_config & OUTPUT_VALID )
NewPMT( pp_outputs[i] ); NewNIT( pp_outputs[i] );
} }
/***************************************************************************** /*****************************************************************************
* SIDIsSelected * SIDIsSelected
*****************************************************************************/ *****************************************************************************/
static int SIDIsSelected( uint16_t i_sid ) 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 ) && pp_outputs[i]->i_sid == i_sid ) if ( ( pp_outputs[i]->i_config & OUTPUT_VALID )
return 1; && pp_outputs[i]->i_sid == i_sid )
return true;
return 0; return false;
} }
/***************************************************************************** /*****************************************************************************
* PIDIsSelected * demux_PIDIsSelected
*****************************************************************************/ *****************************************************************************/
int PIDIsSelected( uint16_t i_pid ) bool demux_PIDIsSelected( uint16_t i_pid )
{ {
int i; int i;
for ( i = 0; i < p_pids[i_pid].i_nb_outputs; i++ ) for ( i = 0; i < p_pids[i_pid].i_nb_outputs; i++ )
if ( p_pids[i_pid].pp_outputs[i] != NULL ) if ( p_pids[i_pid].pp_outputs[i] != NULL )
return 1; return true;
return 0; return false;
} }
/***************************************************************************** /*****************************************************************************
* PIDWouldBeSelected * PIDWouldBeSelected
*****************************************************************************/ *****************************************************************************/
int PIDWouldBeSelected( dvbpsi_pmt_es_t *p_es ) static bool PIDWouldBeSelected( uint8_t *p_es )
{ {
dvbpsi_descriptor_t *p_dr; uint8_t i_type = pmtn_get_streamtype( p_es );
switch ( p_es->i_type ) switch ( i_type )
{ {
case 0x1: /* video MPEG-1 */ case 0x1: /* video MPEG-1 */
case 0x2: /* video */ case 0x2: /* video */
...@@ -1078,34 +1195,43 @@ int PIDWouldBeSelected( dvbpsi_pmt_es_t *p_es ) ...@@ -1078,34 +1195,43 @@ int PIDWouldBeSelected( dvbpsi_pmt_es_t *p_es )
case 0x4: /* audio */ case 0x4: /* audio */
case 0xf: /* audio AAC */ case 0xf: /* audio AAC */
case 0x1b: /* video H264 */ case 0x1b: /* video H264 */
return 1; return true;
break; break;
case 0x6: case 0x6:
for( p_dr = p_es->p_first_descriptor; p_dr != NULL; {
p_dr = p_dr->p_next ) uint16_t j = 0;
const uint8_t *p_desc;
while ( (p_desc = descs_get_desc( pmtn_get_descs( p_es ), j )) != NULL )
{ {
if( p_dr->i_tag == 0x56 /* ttx */ uint8_t i_tag = desc_get_tag( p_desc );
|| p_dr->i_tag == 0x59 /* dvbsub */ j++;
|| p_dr->i_tag == 0x6a /* A/52 */ )
return 1; if( i_tag == 0x56 /* ttx */
|| i_tag == 0x59 /* dvbsub */
|| i_tag == 0x6a /* A/52 */ )
return true;
} }
break; break;
}
default: default:
break; break;
} }
/* FIXME: also parse IOD */ /* FIXME: also parse IOD */
return 0; return false;
} }
/***************************************************************************** /*****************************************************************************
* PIDCarriesPES * PIDCarriesPES
*****************************************************************************/ *****************************************************************************/
int PIDCarriesPES( dvbpsi_pmt_es_t *p_es ) static bool PIDCarriesPES( const uint8_t *p_es )
{ {
switch ( p_es->i_type ) uint8_t i_type = pmtn_get_streamtype( p_es );
switch ( i_type )
{ {
case 0x1: /* video MPEG-1 */ case 0x1: /* video MPEG-1 */
case 0x2: /* video */ case 0x2: /* video */
...@@ -1114,11 +1240,11 @@ int PIDCarriesPES( dvbpsi_pmt_es_t *p_es ) ...@@ -1114,11 +1240,11 @@ int PIDCarriesPES( dvbpsi_pmt_es_t *p_es )
case 0x6: /* private PES data */ case 0x6: /* private PES data */
case 0xf: /* audio AAC */ case 0xf: /* audio AAC */
case 0x1b: /* video H264 */ case 0x1b: /* video H264 */
return 1; return true;
break; break;
default: default:
return 0; return false;
break; break;
} }
} }
...@@ -1126,22 +1252,37 @@ int PIDCarriesPES( dvbpsi_pmt_es_t *p_es ) ...@@ -1126,22 +1252,37 @@ int PIDCarriesPES( dvbpsi_pmt_es_t *p_es )
/***************************************************************************** /*****************************************************************************
* PMTNeedsDescrambling * PMTNeedsDescrambling
*****************************************************************************/ *****************************************************************************/
static int PMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt ) static bool PMTNeedsDescrambling( uint8_t *p_pmt )
{ {
dvbpsi_descriptor_t *p_dr; uint8_t i;
dvbpsi_pmt_es_t *p_es; uint16_t j;
uint8_t *p_es;
const uint8_t *p_desc;
j = 0;
while ( (p_desc = descs_get_desc( pmt_get_descs( p_pmt ), j )) != NULL )
{
uint8_t i_tag = desc_get_tag( p_desc );
j++;
if ( i_tag == 0x9 ) return true;
}
for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next ) i = 0;
if( p_dr->i_tag == 0x9 ) while ( (p_es = pmt_get_es( p_pmt, i )) != NULL )
return 1; {
i++;
j = 0;
while ( (p_desc = descs_get_desc( pmtn_get_descs( p_es ), j )) != NULL )
{
uint8_t i_tag = desc_get_tag( p_desc );
j++;
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) if ( i_tag == 0x9 ) return true;
for( p_dr = p_es->p_first_descriptor; p_dr != NULL; }
p_dr = p_dr->p_next ) }
if( p_dr->i_tag == 0x9 )
return 1;
return 0; return false;
} }
/***************************************************************************** /*****************************************************************************
...@@ -1160,334 +1301,621 @@ void demux_ResendCAPMTs( void ) ...@@ -1160,334 +1301,621 @@ void demux_ResendCAPMTs( void )
/***************************************************************************** /*****************************************************************************
* DeleteProgram * DeleteProgram
*****************************************************************************/ *****************************************************************************/
static void DeleteProgram( dvbpsi_pat_program_t *p_program ) static void DeleteProgram( uint16_t i_sid, uint16_t i_pid )
{ {
int i_pmt; int i_pmt;
UnselectPSI( p_program->i_number, p_program->i_pid ); UnselectPSI( i_sid, i_pid );
for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ ) for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ )
{ {
if ( pp_sids[i_pmt]->i_sid == p_program->i_number ) sid_t *p_sid = pp_sids[i_pmt];
if ( p_sid->i_sid == i_sid )
{ {
dvbpsi_pmt_t *p_pmt = pp_sids[i_pmt]->p_current_pmt; uint8_t *p_pmt = p_sid->p_current_pmt;
if ( p_pmt != NULL ) if ( p_pmt != NULL )
{ {
dvbpsi_pmt_es_t *p_es; uint16_t i_pcr_pid = pmt_get_pcrpid( p_pmt );
uint8_t *p_es;
uint8_t j;
if ( i_ca_handle if ( i_ca_handle
&& SIDIsSelected( p_program->i_number ) && SIDIsSelected( i_sid )
&& PMTNeedsDescrambling( p_pmt ) ) && PMTNeedsDescrambling( p_pmt ) )
en50221_DeletePMT( p_pmt ); en50221_DeletePMT( p_pmt );
if ( p_pmt->i_pcr_pid != PADDING_PID if ( i_pcr_pid != PADDING_PID
&& p_pmt->i_pcr_pid != pp_sids[i_pmt]->i_pmt_pid ) && i_pcr_pid != p_sid->i_pmt_pid )
UnselectPID( p_program->i_number, p_pmt->i_pcr_pid ); UnselectPID( i_sid, i_pcr_pid );
for( p_es = p_pmt->p_first_es; p_es != NULL; j = 0;
p_es = p_es->p_next ) while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{ {
uint16_t i_pid = pmtn_get_pid( p_es );
j++;
if ( PIDWouldBeSelected( p_es ) ) if ( PIDWouldBeSelected( p_es ) )
UnselectPID( p_program->i_number, p_es->i_pid ); UnselectPID( i_sid, i_pid );
} }
dvbpsi_DeletePMT( p_pmt ); free( p_pmt );
pp_sids[i_pmt]->p_current_pmt = NULL;
} }
pp_sids[i_pmt]->p_current_pmt = NULL;
pp_sids[i_pmt]->i_sid = 0; pp_sids[i_pmt]->i_sid = 0;
pp_sids[i_pmt]->i_pmt_pid = 0; pp_sids[i_pmt]->i_pmt_pid = 0;
dvbpsi_DetachPMT( pp_sids[i_pmt]->p_dvbpsi_handle );
break; break;
} }
} }
} }
/***************************************************************************** /*****************************************************************************
* dvbpsi callbacks * HandlePAT
*****************************************************************************/ *****************************************************************************/
static void PATCallback( void *_unused, dvbpsi_pat_t *p_pat ) static void HandlePAT( mtime_t i_dts )
{ {
dvbpsi_pat_program_t *p_program, *p_old_program; bool b_display;
dvbpsi_pat_t *p_old_pat = p_current_pat; PSI_TABLE_DECLARE( pp_old_pat_sections );
uint8_t i_last_section = psi_table_get_lastsection( pp_next_pat_sections );
if( p_current_pat != NULL && uint8_t i;
( !p_pat->b_current_next ||
p_pat->i_version == p_current_pat->i_version ) ) 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 );
psi_table_copy( pp_current_pat_sections, pp_next_pat_sections );
psi_table_init( pp_next_pat_sections );
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();
for ( i = 0; i <= i_last_section; i++ )
{ {
dvbpsi_DeletePAT( p_pat ); uint8_t *p_section =
return; psi_table_get_section( pp_current_pat_sections, i );
} const uint8_t *p_program;
int j = 0;
msg_Dbg( NULL, "new PAT ts_id=%d version=%d current_next=%d",
p_pat->i_ts_id, p_pat->i_version, p_pat->b_current_next );
p_current_pat = p_pat;
for( p_program = p_pat->p_first_program; p_program != NULL; while ( (p_program = pat_get_program( p_section, j )) != NULL )
p_program = p_program->p_next ) {
{ uint16_t i_sid = patn_get_program( p_program );
int i_pmt; uint16_t i_pid = patn_get_pid( p_program );
int i_pmt;
j++;
msg_Dbg( NULL, " * number=%d pid=%d", p_program->i_number, if ( b_display )
p_program->i_pid ); msg_Dbg( NULL, " * number=%hu pid=%hu", i_sid, i_pid );
if( p_program->i_number == 0 ) if ( i_sid == 0 )
continue; {
if ( b_display && b_dvb_compliance && i_pid != NIT_PID )
msg_Warn( NULL,
"NIT is carried on PID %u which isn't DVB compliant",
i_pid );
continue; /* NIT */
}
if ( p_old_pat != NULL ) if ( psi_table_validate( pp_old_pat_sections ) )
{ {
for ( p_old_program = p_old_pat->p_first_program; const uint8_t *p_old_program = pat_table_find_program(
p_old_program != NULL; pp_old_pat_sections, i_sid );
p_old_program = p_old_program->p_next ) if ( p_old_program != NULL )
if ( p_old_program->i_number == p_program->i_number ) {
break; 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 );
}
}
if ( p_old_program != NULL && SelectPSI( i_sid, i_pid );
p_old_program->i_pid == p_program->i_pid )
continue; /* No change */
if ( p_old_program != NULL && for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ )
p_old_program->i_pid != p_program->i_pid ) if ( pp_sids[i_pmt]->i_sid == 0 )
DeleteProgram( p_old_program ); break;
}
SelectPSI( p_program->i_number, p_program->i_pid ); 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;
}
for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ ) pp_sids[i_pmt]->i_sid = i_sid;
if ( pp_sids[i_pmt]->i_sid == 0 ) pp_sids[i_pmt]->i_pmt_pid = i_pid;
break;
if ( i_pmt == i_nb_sids ) UpdatePAT( i_sid );
{
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 = p_program->i_number;
pp_sids[i_pmt]->i_pmt_pid = p_program->i_pid;
pp_sids[i_pmt]->p_dvbpsi_handle = dvbpsi_AttachPMT( p_program->i_number,
PMTCallback, NULL );
UpdatePAT( p_program->i_number );
} }
if ( p_old_pat != NULL ) if ( psi_table_validate( pp_old_pat_sections ) )
{ {
for ( p_old_program = p_old_pat->p_first_program; i_last_section = psi_table_get_lastsection( pp_old_pat_sections );
p_old_program != NULL; for ( i = 0; i <= i_last_section; i++ )
p_old_program = p_old_program->p_next )
{ {
if( p_old_program->i_number == 0 ) uint8_t *p_section =
continue; psi_table_get_section( pp_old_pat_sections, i );
const uint8_t *p_program;
int j = 0;
for( p_program = p_pat->p_first_program; p_program != NULL; while ( (p_program = pat_get_program( p_section, j )) != NULL )
p_program = p_program->p_next )
if ( p_program->i_number == p_old_program->i_number )
break;
if ( p_program == NULL )
{ {
msg_Dbg( NULL, " * removed number=%d pid=%d", uint16_t i_sid = patn_get_program( p_program );
p_old_program->i_number, uint16_t i_pid = patn_get_pid( p_program );
p_old_program->i_pid ); const uint8_t *p_new_program;
j++;
if ( i_sid == 0 )
continue; /* NIT */
DeleteProgram( p_old_program ); p_new_program = pat_table_find_program(
UpdatePAT( p_old_program->i_number ); pp_current_pat_sections, i_sid );
if ( p_new_program == NULL )
{
if ( b_display )
msg_Dbg( NULL, " * removed number=%hu pid=%hu",
i_sid, i_pid );
DeleteProgram( i_sid, i_pid );
UpdatePAT( i_sid );
}
} }
} }
dvbpsi_DeletePAT( p_old_pat ); psi_table_free( pp_old_pat_sections );
} }
if ( b_display )
msg_Dbg( NULL, "end PAT" );
SendPAT( i_dts );
} }
static void PMTCallback( void *_unused, dvbpsi_pmt_t *p_pmt ) /*****************************************************************************
* HandlePATSection
*****************************************************************************/
static void HandlePATSection( uint16_t i_pid, uint8_t *p_section,
mtime_t i_dts )
{ {
dvbpsi_pmt_t *p_current_pmt = NULL; if ( i_pid != PAT_PID || !pat_validate( p_section ) )
dvbpsi_pmt_es_t *p_es, *p_current_es;
int b_needs_descrambling = PMTNeedsDescrambling( p_pmt );
int b_needed_descrambling = 0;
int b_is_selected = SIDIsSelected( p_pmt->i_program_number );
int i_pmt;
for ( i_pmt = 0; i_pmt < i_nb_sids; i_pmt++ )
{ {
if ( pp_sids[i_pmt]->i_sid == p_pmt->i_program_number ) msg_Warn( NULL, "invalid PAT section received on PID %u", i_pid );
{ free( p_section );
p_current_pmt = pp_sids[i_pmt]->p_current_pmt; return;
if ( p_current_pmt != NULL )
b_needed_descrambling = PMTNeedsDescrambling( p_current_pmt );
break;
}
} }
if ( i_pmt == i_nb_sids ) if ( !psi_table_section( pp_next_pat_sections, p_section ) )
return;
HandlePAT( i_dts );
}
/*****************************************************************************
* 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;
uint16_t i_sid = pmt_get_program( p_pmt );
sid_t *p_sid;
bool b_needs_descrambling, b_needed_descrambling, b_is_selected;
uint16_t i_pcr_pid;
uint8_t *p_es;
int i;
uint16_t j;
for ( i = 0; i < i_nb_sids; i++ )
if ( pp_sids[i]->i_sid && pp_sids[i]->i_sid == i_sid )
break;
if ( i == i_nb_sids )
{ {
msg_Err( NULL, "unknown service %d", p_pmt->i_program_number ); /* Unwanted SID (happens when the same PMT PID is used for several
dvbpsi_DeletePMT( p_pmt ); * programs). */
free( p_pmt );
return; return;
} }
p_sid = pp_sids[i];
if ( p_current_pmt != NULL && if ( i_pid != p_sid->i_pmt_pid || !pmt_validate( p_pmt ) )
( !p_pmt->b_current_next ||
p_pmt->i_version == p_current_pmt->i_version ) )
{ {
dvbpsi_DeletePMT( p_pmt ); msg_Warn( NULL, "invalid PMT section received on PID %u", i_pid );
free( p_pmt );
return; return;
} }
b_needs_descrambling = PMTNeedsDescrambling( p_pmt );
b_needed_descrambling = p_sid->p_current_pmt != NULL ?
PMTNeedsDescrambling( p_sid->p_current_pmt ) :
false;
b_is_selected = SIDIsSelected( i_sid );
i_pcr_pid = pmt_get_pcrpid( p_pmt );
if ( i_ca_handle && b_is_selected && if ( i_ca_handle && b_is_selected &&
!b_needs_descrambling && b_needed_descrambling ) !b_needs_descrambling && b_needed_descrambling )
en50221_DeletePMT( p_current_pmt ); en50221_DeletePMT( p_sid->p_current_pmt );
msg_Dbg( NULL, "new PMT program number=%d version=%d pid_pcr=%d", b_display = p_sid->p_current_pmt == NULL
p_pmt->i_program_number, p_pmt->i_version, p_pmt->i_pcr_pid ); || 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 );
if ( p_pmt->i_pcr_pid != PADDING_PID if ( p_sid->p_current_pmt == NULL
&& p_pmt->i_pcr_pid != pp_sids[i_pmt]->i_pmt_pid ) || i_pcr_pid != pmt_get_pcrpid( p_sid->p_current_pmt ) )
SelectPID( p_pmt->i_program_number, p_pmt->i_pcr_pid );
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
{ {
msg_Dbg( NULL, " * es pid=%d type=%d", if ( i_pcr_pid != PADDING_PID
p_es->i_pid, p_es->i_type ); && i_pcr_pid != p_sid->i_pmt_pid )
{
if ( PIDWouldBeSelected( p_es ) ) b_change = true;
SelectPID( p_pmt->i_program_number, p_es->i_pid ); SelectPID( i_sid, i_pcr_pid );
p_pids[p_es->i_pid].b_pes = PIDCarriesPES( p_es ); }
} }
if ( p_current_pmt != NULL ) j = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{ {
if ( p_current_pmt->i_pcr_pid != p_pmt->i_pcr_pid uint8_t i_type = pmtn_get_streamtype( p_es );
&& p_current_pmt->i_pcr_pid != PADDING_PID ) 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 ) )
{ {
for( p_es = p_pmt->p_first_es; p_es != NULL; b_change = true;
p_es = p_es->p_next ) if ( PIDWouldBeSelected( p_es ) )
if ( p_es->i_pid == p_current_pmt->i_pcr_pid ) SelectPID( i_sid, i_pid );
break; p_pids[i_pid].b_pes = PIDCarriesPES( p_es );
}
}
if ( p_es == NULL ) if ( p_sid->p_current_pmt != NULL )
{
uint16_t i_current_pcr_pid = pmt_get_pcrpid( p_sid->p_current_pmt );
if ( i_current_pcr_pid != i_pcr_pid
&& i_current_pcr_pid != PADDING_PID )
{
if ( pmt_find_es( p_pmt, i_current_pcr_pid ) == NULL )
{ {
msg_Dbg( NULL, " * removed pcr pid=%d", b_change = true;
p_current_pmt->i_pcr_pid ); if ( b_display )
UnselectPID( p_pmt->i_program_number, msg_Dbg( NULL, " * removed pid_pcr=%hu",
p_current_pmt->i_pcr_pid ); i_current_pcr_pid );
UnselectPID( i_sid, i_current_pcr_pid );
} }
} }
for( p_current_es = p_current_pmt->p_first_es; p_current_es != NULL; j = 0;
p_current_es = p_current_es->p_next ) while ( (p_es = pmt_get_es( p_sid->p_current_pmt, j )) != NULL )
{ {
if ( PIDWouldBeSelected( p_current_es ) ) j++;
if ( PIDWouldBeSelected( p_es ) )
{ {
for( p_es = p_pmt->p_first_es; p_es != NULL; uint8_t i_current_type = pmtn_get_streamtype( p_es );
p_es = p_es->p_next ) uint16_t i_current_pid = pmtn_get_pid( p_es );
if ( p_es->i_pid == p_current_es->i_pid )
break;
if ( p_es == NULL ) if ( pmt_find_es( p_pmt, i_current_pid ) == NULL )
{ {
msg_Dbg( NULL, " * removed es pid=%d type=%d", b_change = true;
p_current_es->i_pid, p_current_es->i_type ); if ( b_display )
UnselectPID( p_pmt->i_program_number, p_current_es->i_pid ); msg_Dbg( NULL, " * removed es pid=%hu type=%hhu",
i_current_pid, i_current_type );
UnselectPID( i_sid, i_current_pid );
} }
} }
} }
dvbpsi_DeletePMT( p_current_pmt ); free( p_sid->p_current_pmt );
}
p_sid->p_current_pmt = p_pmt;
if ( b_change )
{
if ( i_ca_handle && b_is_selected )
{
if ( b_needs_descrambling && !b_needed_descrambling )
en50221_AddPMT( p_pmt );
else if ( b_needs_descrambling && b_needed_descrambling )
en50221_UpdatePMT( p_pmt );
}
UpdatePMT( i_sid );
} }
pp_sids[i_pmt]->p_current_pmt = p_pmt; if ( b_display )
msg_Dbg( NULL, "end PMT" );
SendPMT( p_sid, i_dts );
}
if ( i_ca_handle && b_is_selected ) /*****************************************************************************
* HandleNITSection
*****************************************************************************/
static void HandleNITSection( uint16_t i_pid, uint8_t *p_section,
mtime_t i_dts )
{
if ( i_pid != NIT_PID || !nit_validate( p_section ) )
{ {
if ( b_needs_descrambling && !b_needed_descrambling ) msg_Warn( NULL, "invalid NIT section received on PID %u", i_pid );
en50221_AddPMT( p_pmt ); free( p_section );
else if ( b_needs_descrambling && b_needed_descrambling ) return;
en50221_UpdatePMT( p_pmt );
} }
UpdatePMT( p_pmt->i_program_number ); /* Actually we don't care about the incoming NIT. Just build our own. */
free( p_section );
SendNIT( i_dts );
} }
static void PSITableCallback( void *_unused, dvbpsi_handle h_dvbpsi,
uint8_t i_table_id, uint16_t i_extension ) /*****************************************************************************
* HandleSDT
*****************************************************************************/
static void CheckServiceDescriptor( uint8_t *p_service )
{ {
/* EIT tables */ uint16_t j = 0;
const uint8_t *p_desc;
if ( i_table_id == 0x4e || ( i_table_id >= 0x50 && i_table_id <= 0x5f ) ) while ( (p_desc = descs_get_desc( sdtn_get_descs(p_service), j )) != NULL )
{ {
SendEIT( h_dvbpsi->p_current_section, i_extension, i_table_id ); 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 );
}
} }
}
/* SDT tables */ 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;
if ( i_table_id == 0x42 ) return memcmp( p_service1, p_service2,
SDT_SERVICE_SIZE + sdtn_get_desclength( p_service1 ) );
}
static void HandleSDT( mtime_t i_dts )
{
bool b_display;
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 ) ) );
/* Switch tables. */
psi_table_copy( pp_old_sdt_sections, pp_current_sdt_sections );
psi_table_copy( pp_current_sdt_sections, pp_next_sdt_sections );
psi_table_init( pp_next_sdt_sections );
for ( i = 0; i <= i_last_section; i++ )
{ {
dvbpsi_AttachSDT( h_dvbpsi, i_table_id, i_extension, SDTCallback, uint8_t *p_section =
NULL ); psi_table_get_section( pp_current_sdt_sections, i );
uint8_t *p_service;
j = 0;
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 )
{
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 ) )
UpdateSDT( i_sid );
}
}
if ( psi_table_validate( pp_old_sdt_sections ) )
psi_table_free( pp_old_sdt_sections );
if ( b_display )
msg_Dbg( NULL, "end SDT" );
SendSDT( i_dts );
}
/*****************************************************************************
* HandleSDTSection
*****************************************************************************/
static void HandleSDTSection( uint16_t i_pid, uint8_t *p_section,
mtime_t i_dts )
{
if ( i_pid != SDT_PID || !sdt_validate( p_section ) )
{
msg_Warn( NULL, "invalid SDT section received on PID %u", i_pid );
free( p_section );
return;
} }
if ( !psi_table_section( pp_next_sdt_sections, p_section ) )
return;
HandleSDT( i_dts );
} }
static void SDTCallback( void *_unused, dvbpsi_sdt_t *p_sdt ) /*****************************************************************************
* HandleEITSection
*****************************************************************************/
static void HandleEIT( uint16_t i_pid, uint8_t *p_eit, mtime_t i_dts )
{ {
dvbpsi_sdt_t *p_old_sdt = p_current_sdt; uint16_t i_sid = eit_get_sid( p_eit );
dvbpsi_sdt_service_t *p_srv; sid_t *p_sid;
dvbpsi_descriptor_t *p_dr;
int i; int i;
if( p_current_sdt != NULL && for ( i = 0; i < i_nb_sids; i++ )
( !p_sdt->b_current_next || if ( pp_sids[i]->i_sid && pp_sids[i]->i_sid == i_sid )
p_sdt->i_version == p_current_sdt->i_version ) ) break;
if ( i == i_nb_sids )
{
/* Not a selected program. */
free( p_eit );
return;
}
p_sid = pp_sids[i];
if ( i_pid != EIT_PID || !eit_validate( p_eit ) )
{
msg_Warn( NULL, "invalid EIT section received on PID %u", i_pid );
free( p_eit );
return;
}
SendEIT( p_sid, i_dts, p_eit );
free( p_eit );
}
/*****************************************************************************
* HandleSection
*****************************************************************************/
static void HandleSection( uint16_t i_pid, uint8_t *p_section, mtime_t i_dts )
{
uint8_t i_table_id = psi_get_tableid( p_section );
if ( !psi_validate( p_section ) )
{ {
dvbpsi_DeleteSDT( p_sdt ); msg_Warn( NULL, "invalid section CRC on PID %u", i_pid );
free( p_section );
return; return;
} }
msg_Dbg( NULL, "new SDT ts_id=%d version=%d current_next=%d " if ( !psi_get_current( p_section ) )
"network_id=%d", {
p_sdt->i_ts_id, p_sdt->i_version, p_sdt->b_current_next, /* Ignore sections which are not in use yet. */
p_sdt->i_network_id ); free( p_section );
return;
}
for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next ) switch ( i_table_id )
{ {
msg_Dbg( NULL, " * service id=%d eit schedule=%d present=%d " case PAT_TABLE_ID:
"running=%d free_ca=%d", HandlePATSection( i_pid, p_section, i_dts );
p_srv->i_service_id, p_srv->b_eit_schedule, break;
p_srv->b_eit_present, p_srv->i_running_status,
p_srv->b_free_ca );
for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next ) case PMT_TABLE_ID:
HandlePMT( i_pid, p_section, i_dts );
break;
case NIT_TABLE_ID_ACTUAL:
HandleNITSection( i_pid, p_section, i_dts );
break;
case SDT_TABLE_ID_ACTUAL:
HandleSDTSection( i_pid, p_section, i_dts );
break;
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_LAST) )
{ {
if( p_dr->i_tag == 0x48 ) HandleEIT( i_pid, p_section, i_dts );
{ break;
dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
char str1[256];
char str2[256];
memcpy( str1, pD->i_service_provider_name,
pD->i_service_provider_name_length );
str1[pD->i_service_provider_name_length] = '\0';
memcpy( str2, pD->i_service_name, pD->i_service_name_length );
str2[pD->i_service_name_length] = '\0';
msg_Dbg( NULL, " - type=%d provider=%s name=%s",
pD->i_service_type, str1, str2 );
}
} }
free( p_section );
break;
} }
}
p_current_sdt = p_sdt; /*****************************************************************************
* HandlePSIPacket
*****************************************************************************/
static void HandlePSIPacket( uint8_t *p_ts, mtime_t i_dts )
{
uint16_t i_pid = ts_get_pid( p_ts );
ts_pid_t *p_pid = &p_pids[i_pid];
uint8_t i_cc = ts_get_cc( p_ts );
const uint8_t *p_payload = ts_payload( p_ts );
uint8_t i_length;
if ( ts_check_duplicate( i_cc, p_pid->i_last_cc )
|| !ts_has_payload( p_ts ) )
return;
for ( i = 0; i < i_nb_outputs; i++ ) if ( p_pid->i_last_cc != -1
&& ts_check_discontinuity( i_cc, p_pid->i_last_cc ) )
psi_assemble_reset( &p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used );
if ( psi_assemble_empty( &p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used ) )
p_payload = ts_section( p_ts );
i_length = p_ts + TS_SIZE - p_payload;
while ( i_length )
{ {
if ( pp_outputs[i]->i_config & OUTPUT_VALID ) uint8_t *p_section = psi_assemble_payload( &p_pid->p_psi_buffer,
NewSDT( pp_outputs[i] ); &p_pid->i_psi_buffer_used,
&p_payload, &i_length );
if ( p_section != NULL )
HandleSection( i_pid, p_section, i_dts );
} }
if ( p_old_sdt != NULL )
dvbpsi_DeleteSDT( p_old_sdt );
} }
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <pthread.h> #include <pthread.h>
#include <netdb.h> #include <netdb.h>
...@@ -63,8 +64,11 @@ int i_bandwidth = 8; ...@@ -63,8 +64,11 @@ int i_bandwidth = 8;
char *psz_modulation = NULL; char *psz_modulation = NULL;
int b_budget_mode = 0; int b_budget_mode = 0;
int b_output_udp = 0; int b_output_udp = 0;
int b_dvb_compliance = 0;
int b_enable_epg = 0; int b_enable_epg = 0;
int b_unique_tsid = 0; int b_unique_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_output_latency = DEFAULT_OUTPUT_LATENCY;
mtime_t i_max_retention = DEFAULT_MAX_RETENTION; mtime_t i_max_retention = DEFAULT_MAX_RETENTION;
volatile int b_hup_received = 0; volatile int b_hup_received = 0;
...@@ -317,14 +321,13 @@ static void DisplayVersion() ...@@ -317,14 +321,13 @@ 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>] [-e] [-T]" ); 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]" );
msg_Raw( NULL, "Input:" ); msg_Raw( NULL, "Input:" );
msg_Raw( NULL, " -a --adapter <adapter>" ); msg_Raw( NULL, " -a --adapter <adapter>" );
msg_Raw( NULL, " -A --asi-adapter read packets from an ASI adapter (0-n)" ); msg_Raw( NULL, " -A --asi-adapter read packets from an ASI adapter (0-n)" );
msg_Raw( NULL, " -b --bandwidth frontend bandwith" ); msg_Raw( NULL, " -b --bandwidth frontend bandwith" );
msg_Raw( NULL, " -D --rtp-input read packets from a multicast address instead of a DVB card" ); msg_Raw( NULL, " -D --rtp-input read packets from a multicast address instead of a DVB card" );
msg_Raw( NULL, " -e --epg-passthrough enable EPG pass through (EIT data)" );
msg_Raw( NULL, " -f --frequency frontend frequency" ); msg_Raw( NULL, " -f --frequency frontend frequency" );
msg_Raw( NULL, " -F --fec-inner Forward Error Correction (FEC Inner)"); msg_Raw( NULL, " -F --fec-inner Forward Error Correction (FEC Inner)");
msg_Raw( NULL, " DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999)"); msg_Raw( NULL, " DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999)");
...@@ -338,25 +341,29 @@ void usage() ...@@ -338,25 +341,29 @@ void usage()
msg_Raw( NULL, " DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" ); msg_Raw( NULL, " DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" );
msg_Raw( NULL, " -s --symbole-rate" ); msg_Raw( NULL, " -s --symbole-rate" );
msg_Raw( NULL, " -S --diseqc satellite number for diseqc (0: no diseqc, 1-4, A or B)" ); msg_Raw( NULL, " -S --diseqc satellite number for diseqc (0: no diseqc, 1-4, A or B)" );
msg_Raw( NULL, " -T --unique-ts-id generate unique TS ID for each program" );
msg_Raw( NULL, " -u --budget-mode turn on budget mode (no hardware PID filtering)" ); msg_Raw( NULL, " -u --budget-mode turn on budget mode (no hardware PID filtering)" );
msg_Raw( NULL, " -v --voltage voltage to apply to the LNB (QPSK)" ); msg_Raw( NULL, " -v --voltage voltage to apply to the LNB (QPSK)" );
msg_Raw( NULL, "Output:" ); msg_Raw( NULL, "Output:" );
msg_Raw( NULL, " -c --config-file <config file>" ); msg_Raw( NULL, " -c --config-file <config file>" );
msg_Raw( NULL, " -L --latency maximum latency allowed between input and output (default: 100 ms)" ); msg_Raw( NULL, " -C --dvb-compliance pass through or build the mandatory DVB tables" );
msg_Raw( NULL, " -E --retention maximum retention allowed between input and output (default: 40 ms)" );
msg_Raw( NULL, " -d --duplicate duplicate all received packets to a given destination" ); msg_Raw( NULL, " -d --duplicate duplicate all received packets to a given destination" );
msg_Raw( NULL, " -e --epg-passthrough pass through DVB EIT schedule tables" );
msg_Raw( NULL, " -E --retention maximum retention allowed between input and output (default: 40 ms)" );
msg_Raw( NULL, " -L --latency maximum latency allowed between input and output (default: 100 ms)" );
msg_Raw( NULL, " -M --network-name DVB network name to declare in the NIT" );
msg_Raw( NULL, " -N --network-id DVB network ID to declare in the NIT" );
msg_Raw( NULL, " -o --rtp-output <SSRC IP>" ); msg_Raw( NULL, " -o --rtp-output <SSRC IP>" );
msg_Raw( NULL, " -t --ttl <ttl> TTL of the output stream" ); msg_Raw( NULL, " -t --ttl <ttl> TTL of the output stream" );
msg_Raw( NULL, " -T --unique-ts-id generate random unique TS ID for each output" );
msg_Raw( NULL, " -U --udp use raw UDP rather than RTP (required by some IPTV set top boxes)" ); msg_Raw( NULL, " -U --udp use raw UDP rather than RTP (required by some IPTV set top boxes)" );
msg_Raw( NULL, "Misc:" ); msg_Raw( NULL, "Misc:" );
msg_Raw( NULL, " -h --help display this full help" ); msg_Raw( NULL, " -h --help display this full help" );
msg_Raw( NULL, " -i --priority <RT pritority>" ); msg_Raw( NULL, " -i --priority <RT pritority>" );
msg_Raw( NULL, " -q be quiet (less verbosity, repeat or use number for even quieter)" ); msg_Raw( NULL, " -q be quiet (less verbosity, repeat or use number for even quieter)" );
msg_Raw( NULL, " -l --logger use syslog for logging messages instead of stderr" );
msg_Raw( NULL, " -r --remote-socket <remote socket>" ); msg_Raw( NULL, " -r --remote-socket <remote socket>" );
msg_Raw( NULL, " -l --logger use syslog for logging messages instead of stderr" );
msg_Raw( NULL, " -V --version only display the version" ); msg_Raw( NULL, " -V --version only display the version" );
exit(1); exit(1);
} }
...@@ -401,14 +408,17 @@ int main( int i_argc, char **pp_argv ) ...@@ -401,14 +408,17 @@ int main( int i_argc, char **pp_argv )
{ "duplicate", required_argument, NULL, 'd' }, { "duplicate", required_argument, NULL, 'd' },
{ "rtp-input", required_argument, NULL, 'D' }, { "rtp-input", required_argument, NULL, 'D' },
{ "asi-adapter", required_argument, NULL, 'A' }, { "asi-adapter", required_argument, NULL, 'A' },
{ "dvb-compliance", no_argument, NULL, 'C' },
{ "epg-passthrough", no_argument, NULL, 'e' }, { "epg-passthrough", no_argument, NULL, 'e' },
{ "network-name", no_argument, NULL, 'M' },
{ "network-id", no_argument, NULL, 'N' },
{ "logger", no_argument, NULL, 'l' }, { "logger", no_argument, NULL, 'l' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ 0, 0, 0, 0} { 0, 0, 0, 0}
}; };
while ( ( c = getopt_long(i_argc, pp_argv, "q::c:r:t:o:i:a:n:f:F:R:s:S:v:pb:m:uUTL:E:d:D:A:lehV", long_options, NULL)) != -1 ) while ( ( c = getopt_long(i_argc, pp_argv, "q::c:r:t:o:i:a:n:f:F:R:s:S:v:pb:m:uUTL:E:d:D:A:lCeM:N:hV", long_options, NULL)) != -1 )
{ {
switch ( c ) switch ( c )
{ {
...@@ -670,10 +680,22 @@ int main( int i_argc, char **pp_argv ) ...@@ -670,10 +680,22 @@ int main( int i_argc, char **pp_argv )
pf_UnsetFilter = asi_UnsetFilter; pf_UnsetFilter = asi_UnsetFilter;
break; break;
case 'C':
b_dvb_compliance = 1;
break;
case 'e': case 'e':
b_enable_epg = 1; b_enable_epg = 1;
break; break;
case 'M':
psz_network_name = optarg;
break;
case 'N':
i_network_id = strtoul( optarg, NULL, 0 );
break;
case 'l': case 'l':
b_enable_syslog = 1; b_enable_syslog = 1;
break; break;
...@@ -705,6 +727,12 @@ int main( int i_argc, char **pp_argv ) ...@@ -705,6 +727,12 @@ int main( int i_argc, char **pp_argv )
msg_Warn( NULL, "for DVB-IP compliance you should use RTP." ); msg_Warn( NULL, "for DVB-IP compliance you should use RTP." );
} }
if ( b_enable_epg && !b_dvb_compliance )
{
msg_Dbg( NULL, "turning on DVB compliance, required by EPG information" );
b_dvb_compliance = 1;
}
signal( SIGHUP, SigHandler ); signal( SIGHUP, SigHandler );
srand( time(NULL) * getpid() ); srand( time(NULL) * getpid() );
......
...@@ -22,12 +22,8 @@ ...@@ -22,12 +22,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#include <dvbpsi/dvbpsi.h> #include <netdb.h>
#include <dvbpsi/descriptor.h> #include <sys/socket.h>
#include <dvbpsi/pmt.h>
#include "netdb.h"
#include "sys/socket.h"
#define DEFAULT_PORT 3001 #define DEFAULT_PORT 3001
#define TS_SIZE 188 #define TS_SIZE 188
...@@ -42,6 +38,7 @@ ...@@ -42,6 +38,7 @@
#define MAX_POLL_TIMEOUT 100000 /* 100 ms */ #define MAX_POLL_TIMEOUT 100000 /* 100 ms */
#define DEFAULT_OUTPUT_LATENCY 200000 /* 200 ms */ #define DEFAULT_OUTPUT_LATENCY 200000 /* 200 ms */
#define DEFAULT_MAX_RETENTION 40000 /* 40 ms */ #define DEFAULT_MAX_RETENTION 40000 /* 40 ms */
#define MAX_EIT_RETENTION 500000 /* 500 ms */
/***************************************************************************** /*****************************************************************************
* Output configuration flags (for output_t -> i_config) - bit values * Output configuration flags (for output_t -> i_config) - bit values
...@@ -89,13 +86,16 @@ typedef struct output_t ...@@ -89,13 +86,16 @@ typedef struct output_t
/* demux */ /* demux */
int i_nb_errors; int i_nb_errors;
mtime_t i_last_error; mtime_t i_last_error;
dvbpsi_psi_section_t *p_pat_section; uint8_t *p_pat_section;
uint8_t i_pat_version, i_pat_cc; uint8_t i_pat_version, i_pat_cc;
dvbpsi_psi_section_t *p_pmt_section; uint8_t *p_pmt_section;
uint8_t i_pmt_version, i_pmt_cc; uint8_t i_pmt_version, i_pmt_cc;
dvbpsi_psi_section_t *p_sdt_section; uint8_t *p_nit_section;
uint8_t i_nit_version, i_nit_cc;
uint8_t *p_sdt_section;
uint8_t i_sdt_version, i_sdt_cc; uint8_t i_sdt_version, i_sdt_cc;
uint8_t i_eit_cc; block_t *p_eit_ts_buffer;
uint8_t i_eit_ts_buffer_offset, i_eit_cc;
uint16_t i_ts_id; uint16_t i_ts_id;
/* configuration */ /* configuration */
...@@ -126,8 +126,11 @@ extern int i_bandwidth; ...@@ -126,8 +126,11 @@ extern int i_bandwidth;
extern char *psz_modulation; extern char *psz_modulation;
extern int b_budget_mode; extern int b_budget_mode;
extern int b_output_udp; extern int b_output_udp;
extern int b_dvb_compliance;
extern int b_enable_epg; extern int b_enable_epg;
extern int b_unique_tsid; extern int b_unique_tsid;
extern uint16_t i_network_id;
extern const char *psz_network_name;
extern mtime_t i_output_latency; extern mtime_t i_output_latency;
extern mtime_t i_max_retention; extern mtime_t i_max_retention;
extern mtime_t i_wallclock; extern mtime_t i_wallclock;
...@@ -185,7 +188,7 @@ void demux_Run( block_t *p_ts ); ...@@ -185,7 +188,7 @@ 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, uint16_t i_sid,
uint16_t *pi_pids, int i_nb_pids ); uint16_t *pi_pids, int i_nb_pids );
void demux_ResendCAPMTs( void ); void demux_ResendCAPMTs( void );
int PIDIsSelected( uint16_t i_pid ); bool demux_PIDIsSelected( uint16_t i_pid );
output_t *output_Create( uint8_t i_config, const char *psz_displayname, output_t *output_Create( uint8_t i_config, const char *psz_displayname,
void *p_init_data ); void *p_init_data );
...@@ -229,86 +232,3 @@ static inline void block_DeleteChain( block_t *p_block ) ...@@ -229,86 +232,3 @@ static inline void block_DeleteChain( block_t *p_block )
p_block = p_next; p_block = p_next;
} }
} }
/*****************************************************************************
* block_GetSync
*****************************************************************************/
static inline uint8_t block_GetSync( block_t *p_block )
{
return p_block->p_ts[0];
}
/*****************************************************************************
* block_HasTransportError
*****************************************************************************/
static inline uint8_t block_HasTransportError( block_t *p_block )
{
return p_block->p_ts[1] & 0x80;
}
/*****************************************************************************
* block_UnitStart
*****************************************************************************/
static inline uint8_t block_UnitStart( block_t *p_block )
{
return p_block->p_ts[1] & 0x40;
}
/*****************************************************************************
* block_GetPID
*****************************************************************************/
static inline uint16_t block_GetPID( block_t *p_block )
{
return (((uint16_t)p_block->p_ts[1] & 0x1f) << 8)
| p_block->p_ts[2];
}
/*****************************************************************************
* block_GetScrambling
*****************************************************************************/
static inline uint8_t block_GetScrambling( block_t *p_block )
{
return p_block->p_ts[3] & 0xc0;
}
/*****************************************************************************
* block_GetCC
*****************************************************************************/
static inline uint8_t block_GetCC( block_t *p_block )
{
return p_block->p_ts[3] & 0xf;
}
/*****************************************************************************
* block_HasPCR
*****************************************************************************/
static inline int block_HasPCR( block_t *p_block )
{
return ( p_block->p_ts[3] & 0x20 ) && /* adaptation field present */
( p_block->p_ts[4] >= 7 ) && /* adaptation field size */
( p_block->p_ts[5] & 0x10 ); /* has PCR */
}
/*****************************************************************************
* block_GetPCR
*****************************************************************************/
static inline mtime_t block_GetPCR( block_t *p_block )
{
return ( (mtime_t)p_block->p_ts[6] << 25 ) |
( (mtime_t)p_block->p_ts[7] << 17 ) |
( (mtime_t)p_block->p_ts[8] << 9 ) |
( (mtime_t)p_block->p_ts[9] << 1 ) |
( (mtime_t)p_block->p_ts[10] >> 7 );
}
/*****************************************************************************
* block_GetPayload
*****************************************************************************/
static inline uint8_t *block_GetPayload( block_t *p_block )
{
if ( !(p_block->p_ts[3] & 0x10) )
return NULL;
if ( !(p_block->p_ts[3] & 0x20) )
return &p_block->p_ts[4];
return &p_block->p_ts[ 5 + p_block->p_ts[4] ];
}
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -45,8 +46,8 @@ int i_syslog = 0; ...@@ -45,8 +46,8 @@ int i_syslog = 0;
void usage() void usage()
{ {
msg_Raw( NULL, "DVBlastctl %d.%d.%d%s", VERSION_MAJOR, VERSION_MINOR, msg_Raw( NULL, "DVBlastctl %d.%d.%d%s", VERSION_MAJOR, VERSION_MINOR,
VERSION_REVISION, VERSION_EXTRA ); VERSION_REVISION, VERSION_EXTRA );
msg_Raw( NULL, "Usage: dvblastctl -r <remote socket> reload|shutdown|fe_status|mmi_status|mmi_open|mmi_close|mmi_get|mmi_send_text|mmi_send_choice [<CAM slot>] [<text/choice>]" ); msg_Raw( NULL, "Usage: dvblastctl -r <remote socket> reload|shutdown|fe_status|mmi_status|mmi_open|mmi_close|mmi_get|mmi_send_text|mmi_send_choice [<CAM slot>] [<text/choice>]" );
exit(1); exit(1);
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* en50221.c : implementation of the transport, session and applications * en50221.c : implementation of the transport, session and applications
* layers of EN 50 221 * layers of EN 50 221
***************************************************************************** *****************************************************************************
* Copyright (C) 2004-2005 VideoLAN * Copyright (C) 2004-2005, 2010 VideoLAN
* $Id$ * $Id$
* *
* Authors: Christophe Massiot <massiot@via.ecp.fr> * Authors: Christophe Massiot <massiot@via.ecp.fr>
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
#include <linux/dvb/frontend.h> #include <linux/dvb/frontend.h>
#include <linux/dvb/ca.h> #include <linux/dvb/ca.h>
#include <bitstream/mpeg/psi.h>
#include <bitstream/dvb/ci.h>
#include "dvblast.h" #include "dvblast.h"
#include "en50221.h" #include "en50221.h"
#include "comm.h" #include "comm.h"
...@@ -820,6 +823,7 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot, ...@@ -820,6 +823,7 @@ static void SPDUHandle( access_t * p_access, uint8_t i_slot,
#define AOT_CA_INFO 0x9F8031 #define AOT_CA_INFO 0x9F8031
#define AOT_CA_PMT 0x9F8032 #define AOT_CA_PMT 0x9F8032
#define AOT_CA_PMT_REPLY 0x9F8033 #define AOT_CA_PMT_REPLY 0x9F8033
#define AOT_CA_UPDATE 0x9F8034
#define AOT_TUNE 0x9F8400 #define AOT_TUNE 0x9F8400
#define AOT_REPLACE 0x9F8401 #define AOT_REPLACE 0x9F8401
#define AOT_CLEAR_REPLACE 0x9F8402 #define AOT_CLEAR_REPLACE 0x9F8402
...@@ -1010,7 +1014,6 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id, ...@@ -1010,7 +1014,6 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
uint8_t *d = APDUGetLength( p_apdu, &l ); uint8_t *d = APDUGetLength( p_apdu, &l );
if ( l < 4 ) break; if ( l < 4 ) break;
p_apdu[l + 4] = '\0';
i_type = *d++; i_type = *d++;
i_manufacturer = ((int)d[0] << 8) | d[1]; i_manufacturer = ((int)d[0] << 8) | d[1];
...@@ -1018,9 +1021,14 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id, ...@@ -1018,9 +1021,14 @@ static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
i_code = ((int)d[0] << 8) | d[1]; i_code = ((int)d[0] << 8) | d[1];
d += 2; d += 2;
d = GetLength( d, &l ); d = GetLength( d, &l );
d[l] = '\0';
msg_Info( p_access, "CAM: %s, %02X, %04X, %04X", {
d, i_type, i_manufacturer, i_code ); char psz_name[l + 1];
memcpy( psz_name, d, l );
psz_name[l] = '\0';
msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
psz_name, i_type, i_manufacturer, i_code );
}
break; break;
} }
default: default:
...@@ -1072,206 +1080,144 @@ static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id ) ...@@ -1072,206 +1080,144 @@ static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
/***************************************************************************** /*****************************************************************************
* CAPMTBuild * CAPMTBuild
*****************************************************************************/ *****************************************************************************/
static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr ) static bool HasCADescriptors( system_ids_t *p_ids, uint8_t *p_descs )
{ {
int i_cad_size = 0; const uint8_t *p_desc;
uint16_t j = 0;
while ( p_dr != NULL ) while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
{ {
if( p_dr->i_tag == 0x9 ) uint8_t i_tag = desc_get_tag( p_desc );
{ j++;
uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
| p_dr->p_data[1]; if ( i_tag == 0x9
if ( CheckSystemID( p_ids, i_sysid ) ) && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
i_cad_size += p_dr->i_length + 2; return true;
}
p_dr = p_dr->p_next;
} }
return i_cad_size; return false;
} }
static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt, static void CopyCADescriptors( system_ids_t *p_ids, uint8_t i_cmd,
uint16_t i_program_number, uint8_t i_version, uint8_t *p_infos, uint8_t *p_descs )
int i_size, dvbpsi_descriptor_t *p_dr,
uint8_t i_cmd )
{ {
uint8_t *p_data; const uint8_t *p_desc;
uint16_t j = 0, k = 0;
if ( i_size )
p_data = malloc( 7 + i_size );
else
p_data = malloc( 6 );
p_data[0] = i_list_mgt; capmti_init( p_infos );
p_data[1] = i_program_number >> 8; capmti_set_length( p_infos, 0xfff );
p_data[2] = i_program_number & 0xff; capmti_set_cmd( p_infos, i_cmd );
p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
if ( i_size ) while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
{ {
int i; uint8_t i_tag = desc_get_tag( p_desc );
j++;
p_data[4] = (i_size + 1) >> 8;
p_data[5] = (i_size + 1) & 0xff;
p_data[6] = i_cmd;
i = 7;
while ( p_dr != NULL ) if ( i_tag == 0x9
&& CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
{ {
if( p_dr->i_tag == 0x9 ) uint8_t *p_info = capmti_get_info( p_infos, k );
{ k++;
uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8) memcpy( p_info, p_desc,
| p_dr->p_data[1]; DESC_HEADER_SIZE + desc_get_length( p_desc ) );
if ( CheckSystemID( p_ids, i_sysid ) )
{
p_data[i] = 0x9;
p_data[i + 1] = p_dr->i_length;
memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
// p_data[i+4] &= 0x1f;
i += p_dr->i_length + 2;
}
}
p_dr = p_dr->p_next;
} }
} }
else
{
p_data[4] = 0;
p_data[5] = 0;
}
return p_data;
}
static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
int i_capmt_size, uint8_t i_type, uint16_t i_pid,
int i_size, dvbpsi_descriptor_t *p_dr,
uint8_t i_cmd )
{
uint8_t *p_data;
int i;
if ( i_size )
p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
else
p_data = realloc( p_capmt, i_capmt_size + 5 );
i = i_capmt_size;
p_data[i] = i_type; if ( k )
p_data[i + 1] = i_pid >> 8;
p_data[i + 2] = i_pid & 0xff;
if ( i_size )
{ {
p_data[i + 3] = (i_size + 1) >> 8; uint8_t *p_info = capmti_get_info( p_infos, k );
p_data[i + 4] = (i_size + 1) & 0xff; capmti_set_length( p_infos, p_info - p_infos - DESCS_HEADER_SIZE );
p_data[i + 5] = i_cmd;
i += 6;
while ( p_dr != NULL )
{
if( p_dr->i_tag == 0x9 )
{
uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
| p_dr->p_data[1];
if ( CheckSystemID( p_ids, i_sysid ) )
{
p_data[i] = 0x9;
p_data[i + 1] = p_dr->i_length;
memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
i += p_dr->i_length + 2;
}
}
p_dr = p_dr->p_next;
}
} }
else else
{ capmti_set_length( p_infos, 0 );
p_data[i + 3] = 0;
p_data[i + 4] = 0;
}
return p_data;
} }
static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id, static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt, uint8_t *p_pmt, uint8_t i_list_mgt,
uint8_t i_cmd, int *pi_capmt_size ) uint8_t i_cmd, int *pi_capmt_size )
{ {
system_ids_t *p_ids = system_ids_t *p_ids =
(system_ids_t *)p_sessions[i_session_id - 1].p_sys; (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
dvbpsi_pmt_es_t *p_es; uint8_t *p_es;
int i_cad_size, i_cad_program_size; uint8_t *p_capmt, *p_capmt_n;
uint8_t *p_capmt; uint16_t j, k;
bool b_has_ca = HasCADescriptors( p_ids, pmt_get_descs( p_pmt ) );
bool b_has_es = false;
j = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{
uint16_t i_pid = pmtn_get_pid( p_es );
j++;
if ( demux_PIDIsSelected( i_pid ) )
{
b_has_es = true;
b_has_ca = b_has_ca
|| HasCADescriptors( p_ids, pmtn_get_descs( p_es ) );
}
}
i_cad_size = i_cad_program_size = if ( !b_has_es )
GetCADSize( p_ids, p_pmt->p_first_descriptor );
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
{ {
i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor ); *pi_capmt_size = 0;
return NULL;
} }
if ( !i_cad_size ) if ( !b_has_ca )
{ {
msg_Warn( p_access, msg_Warn( p_access,
"no compatible scrambling system for SID %d on session %d", "no compatible scrambling system for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
*pi_capmt_size = 0; *pi_capmt_size = 0;
return NULL; return NULL;
} }
p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number, p_capmt = capmt_allocate();
p_pmt->i_version, i_cad_program_size, capmt_init( p_capmt );
p_pmt->p_first_descriptor, i_cmd ); capmt_set_listmanagement( p_capmt, i_list_mgt );
capmt_set_program( p_capmt, pmt_get_program( p_pmt ) );
capmt_set_version( p_capmt, psi_get_version( p_pmt ) );
if ( i_cad_program_size ) CopyCADescriptors( p_ids, i_cmd, capmt_get_infos( p_capmt ),
*pi_capmt_size = 7 + i_cad_program_size; pmt_get_descs( p_pmt ) );
else
*pi_capmt_size = 6;
for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next ) j = 0; k = 0;
while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
{ {
if ( !PIDIsSelected( p_es->i_pid ) ) uint16_t i_pid = pmtn_get_pid( p_es );
j++;
if ( !demux_PIDIsSelected( i_pid ) )
continue; continue;
i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor ); p_capmt_n = capmt_get_es( p_capmt, k );
k++;
if ( i_cad_size || i_cad_program_size ) capmtn_init( p_capmt_n );
{ capmtn_set_streamtype( p_capmt_n, pmtn_get_streamtype( p_es ) );
p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type, capmtn_set_pid( p_capmt_n, pmtn_get_pid( p_es ) );
p_es->i_pid, i_cad_size,
p_es->p_first_descriptor, i_cmd );
if ( i_cad_size )
*pi_capmt_size += 6 + i_cad_size;
else
*pi_capmt_size += 5;
}
}
if ( *pi_capmt_size <= 7 + i_cad_program_size ) CopyCADescriptors( p_ids, i_cmd, capmtn_get_infos( p_capmt_n ),
{ pmtn_get_descs( p_es ) );
msg_Dbg( p_access, "CAPMT not needed, no ES selected" );
free( p_capmt );
*pi_capmt_size = 0;
return NULL;
} }
p_capmt_n = capmt_get_es( p_capmt, k );
*pi_capmt_size = p_capmt_n - p_capmt;
return p_capmt; return p_capmt;
} }
/***************************************************************************** /*****************************************************************************
* CAPMTFirst * CAPMTFirst
*****************************************************************************/ *****************************************************************************/
static void CAPMTFirst( access_t * p_access, int i_session_id, static void CAPMTFirst( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; int i_capmt_size;
msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d", msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x3 /* only */, 0x1 /* ok_descrambling */, 0x3 /* only */, 0x1 /* ok_descrambling */,
...@@ -1287,8 +1233,7 @@ static void CAPMTFirst( access_t * p_access, int i_session_id, ...@@ -1287,8 +1233,7 @@ static void CAPMTFirst( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* CAPMTAdd * CAPMTAdd
*****************************************************************************/ *****************************************************************************/
static void CAPMTAdd( access_t * p_access, int i_session_id, static void CAPMTAdd( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
system_ids_t *p_ids = system_ids_t *p_ids =
(system_ids_t *)p_sessions[i_session_id - 1].p_sys; (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
...@@ -1303,7 +1248,7 @@ static void CAPMTAdd( access_t * p_access, int i_session_id, ...@@ -1303,7 +1248,7 @@ static void CAPMTAdd( access_t * p_access, int i_session_id,
} }
msg_Dbg( p_access, "adding CAPMT for SID %d on session %d", msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x4 /* add */, 0x1 /* ok_descrambling */, 0x4 /* add */, 0x1 /* ok_descrambling */,
...@@ -1319,14 +1264,13 @@ static void CAPMTAdd( access_t * p_access, int i_session_id, ...@@ -1319,14 +1264,13 @@ static void CAPMTAdd( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* CAPMTUpdate * CAPMTUpdate
*****************************************************************************/ *****************************************************************************/
static void CAPMTUpdate( access_t * p_access, int i_session_id, static void CAPMTUpdate( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
uint8_t *p_capmt; uint8_t *p_capmt;
int i_capmt_size; int i_capmt_size;
msg_Dbg( p_access, "updating CAPMT for SID %d on session %d", msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x5 /* update */, 0x1 /* ok_descrambling */, 0x5 /* update */, 0x1 /* ok_descrambling */,
...@@ -1342,8 +1286,7 @@ static void CAPMTUpdate( access_t * p_access, int i_session_id, ...@@ -1342,8 +1286,7 @@ static void CAPMTUpdate( access_t * p_access, int i_session_id,
/***************************************************************************** /*****************************************************************************
* CAPMTDelete * CAPMTDelete
*****************************************************************************/ *****************************************************************************/
static void CAPMTDelete( access_t * p_access, int i_session_id, static void CAPMTDelete( access_t * p_access, int i_session_id, uint8_t *p_pmt )
dvbpsi_pmt_t *p_pmt )
{ {
system_ids_t *p_ids = system_ids_t *p_ids =
(system_ids_t *)p_sessions[i_session_id - 1].p_sys; (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
...@@ -1352,7 +1295,7 @@ static void CAPMTDelete( access_t * p_access, int i_session_id, ...@@ -1352,7 +1295,7 @@ static void CAPMTDelete( access_t * p_access, int i_session_id,
p_ids->i_selected_programs--; p_ids->i_selected_programs--;
msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d", msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
p_pmt->i_program_number, i_session_id ); pmt_get_program( p_pmt ), i_session_id );
p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
0x5 /* update */, 0x4 /* not selected */, 0x5 /* update */, 0x4 /* not selected */,
...@@ -1401,6 +1344,12 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id, ...@@ -1401,6 +1344,12 @@ static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
break; break;
} }
case AOT_CA_UPDATE:
/* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */
case AOT_CA_PMT_REPLY:
/* We do not care */
break;
default: default:
msg_Err( p_access, msg_Err( p_access,
"unexpected tag in ConditionalAccessHandle (0x%x)", "unexpected tag in ConditionalAccessHandle (0x%x)",
...@@ -2215,7 +2164,7 @@ void en50221_Poll( void ) ...@@ -2215,7 +2164,7 @@ void en50221_Poll( void )
/***************************************************************************** /*****************************************************************************
* en50221_AddPMT : * en50221_AddPMT :
*****************************************************************************/ *****************************************************************************/
void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ) void en50221_AddPMT( uint8_t *p_pmt )
{ {
int i_session_id; int i_session_id;
...@@ -2228,7 +2177,7 @@ void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ) ...@@ -2228,7 +2177,7 @@ void en50221_AddPMT( dvbpsi_pmt_t *p_pmt )
/***************************************************************************** /*****************************************************************************
* en50221_UpdatePMT : * en50221_UpdatePMT :
*****************************************************************************/ *****************************************************************************/
void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ) void en50221_UpdatePMT( uint8_t *p_pmt )
{ {
int i_session_id; int i_session_id;
...@@ -2241,7 +2190,7 @@ void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ) ...@@ -2241,7 +2190,7 @@ void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt )
/***************************************************************************** /*****************************************************************************
* en50221_DeletePMT : * en50221_DeletePMT :
*****************************************************************************/ *****************************************************************************/
void en50221_DeletePMT( dvbpsi_pmt_t *p_pmt ) void en50221_DeletePMT( uint8_t *p_pmt )
{ {
int i_session_id; int i_session_id;
......
...@@ -80,9 +80,9 @@ void en50221_Init( void ); ...@@ -80,9 +80,9 @@ void en50221_Init( void );
void en50221_Reset( void ); void en50221_Reset( void );
void en50221_Read( void ); void en50221_Read( void );
void en50221_Poll( void ); void en50221_Poll( void );
void en50221_AddPMT( dvbpsi_pmt_t *p_pmt ); void en50221_AddPMT( uint8_t *p_pmt );
void en50221_UpdatePMT( dvbpsi_pmt_t *p_pmt ); void en50221_UpdatePMT( uint8_t *p_pmt );
void en50221_DeletePMT( dvbpsi_pmt_t *p_pmt ); void en50221_DeletePMT( uint8_t *p_pmt );
uint8_t en50221_StatusMMI( uint8_t *p_answer, ssize_t *pi_size ); uint8_t en50221_StatusMMI( uint8_t *p_answer, ssize_t *pi_size );
uint8_t en50221_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size, uint8_t en50221_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size,
uint8_t *p_answer, ssize_t *pi_size ); uint8_t *p_answer, ssize_t *pi_size );
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>
...@@ -37,6 +38,8 @@ ...@@ -37,6 +38,8 @@
#include "dvblast.h" #include "dvblast.h"
#include <bitstream/mpeg/ts.h>
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
...@@ -119,14 +122,19 @@ int output_Init( output_t *p_output, uint8_t i_config, ...@@ -119,14 +122,19 @@ int output_Init( output_t *p_output, uint8_t i_config,
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;
p_output->i_nit_cc = rand() & 0xf;
p_output->i_sdt_cc = rand() & 0xf; p_output->i_sdt_cc = rand() & 0xf;
p_output->i_eit_cc = rand() & 0xf; p_output->i_eit_cc = rand() & 0xf;
p_output->i_pat_version = rand() & 0xff; p_output->i_pat_version = rand() & 0xff;
p_output->i_pmt_version = rand() & 0xff; p_output->i_pmt_version = rand() & 0xff;
p_output->i_nit_version = rand() & 0xff;
p_output->i_sdt_version = rand() & 0xff; p_output->i_sdt_version = rand() & 0xff;
p_output->p_pat_section = NULL; p_output->p_pat_section = NULL;
p_output->p_pmt_section = NULL; p_output->p_pmt_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->i_eit_ts_buffer_offset = 0;
if ( b_unique_tsid ) if ( b_unique_tsid )
p_output->i_ts_id = rand() & 0xffff; p_output->i_ts_id = rand() & 0xffff;
p_output->i_ref_timestamp = 0; p_output->i_ref_timestamp = 0;
...@@ -175,6 +183,12 @@ void output_Close( output_t *p_output ) ...@@ -175,6 +183,12 @@ 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->psz_displayname );
free( p_output->p_pat_section );
free( p_output->p_pmt_section );
free( p_output->p_nit_section );
free( p_output->p_sdt_section );
free( p_output->p_eit_ts_buffer );
free( p_output->p_addr );
p_output->i_config &= ~OUTPUT_VALID; p_output->i_config &= ~OUTPUT_VALID;
close( p_output->i_handle ); close( p_output->i_handle );
} }
...@@ -251,7 +265,9 @@ void output_Put( output_t *p_output, block_t *p_block ) ...@@ -251,7 +265,9 @@ void output_Put( output_t *p_output, block_t *p_block )
&& p_output->p_last_packet->i_dts + i_max_retention > p_block->i_dts ) && p_output->p_last_packet->i_dts + i_max_retention > p_block->i_dts )
{ {
p_packet = p_output->p_last_packet; p_packet = p_output->p_last_packet;
if ( block_HasPCR( p_block ) ) if ( ts_has_adaptation( p_block->p_ts )
&& ts_get_adaptation( p_block->p_ts )
&& tsaf_has_pcr( p_block->p_ts ) )
p_packet->i_dts = p_block->i_dts; p_packet->i_dts = p_block->i_dts;
} }
else else
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <sys/time.h> #include <sys/time.h>
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#define VERSION_MAJOR 1 #define VERSION_MAJOR 2
#define VERSION_MINOR 3 #define VERSION_MINOR 0
#define VERSION_REVISION 0 #define VERSION_REVISION 0
#define VERSION_EXTRA "-svn" #define VERSION_EXTRA "-svn"
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