* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
* Jean-Paul Saman <jpsaman #_at_# m2x.nl>
+ * Wallace Wadge <wwadge #_at_# gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#ifdef HAVE_DVBPSI_DR_H
# include <dvbpsi/dvbpsi.h>
+# include <dvbpsi/demux.h>
# include <dvbpsi/descriptor.h>
# include <dvbpsi/pat.h>
# include <dvbpsi/pmt.h>
+# include <dvbpsi/sdt.h>
# include <dvbpsi/dr.h>
# include <dvbpsi/psi.h>
#else
# include "dvbpsi.h"
+# include "demux.h"
# include "descriptor.h"
# include "tables/pat.h"
# include "tables/pmt.h"
+# include "tables/sdt.h"
# include "descriptors/dr.h"
# include "psi.h"
#endif
#define PMTPID_LONGTEXT N_("Assigns a fixed PID to the PMT")
#define TSID_TEXT N_("TS ID")
#define TSID_LONGTEXT N_("Assigns a fixed Transport Stream ID.")
-#define PMTPROG_TEXT N_("PMT Program number")
-#define PMTPROG_LONGTEXT N_("Assigns a program number to the PMT.")
+#define NETID_TEXT N_("NET ID")
+#define NETID_LONGTEXT N_("Assigns a fixed Network ID (for SDT table)")
+#define PMTPROG_TEXT N_("PMT Program numbers (requires --sout-ts-es-id-pid)")
+#define PMTPROG_LONGTEXT N_("Assigns a program number to each PMT")
+#define MUXPMT_TEXT N_("Mux PMT (requires --sout-ts-es-id-pid)")
+#define MUXPMT_LONGTEXT N_("Defines the pids to add to each pmt." )
+
+#define SDTDESC_TEXT N_("SDT Descriptors (requires --sout-ts-es-id-pid)")
+#define SDTDESC_LONGTEXT N_("Defines the descriptors of each SDT" )
#define PID_TEXT N_("Set PID to id of ES")
#define PID_LONGTEXT N_("set PID to id of es")
"encrypting. " )
#define SOUT_CFG_PREFIX "sout-ts-"
+#ifdef HAVE_BSEARCH
+# define MAX_PMT 64 /* Maximum number of programs. FIXME: I just chose an arbitary number. Where is the maximum in the spec? */
+#else
+# define MAX_PMT 1
+#endif
+#define MAX_PMT_PID 64 /* Maximum pids in each pmt. FIXME: I just chose an arbitary number. Where is the maximum in the spec? */
vlc_module_begin();
set_description( _("TS muxer (libdvbpsi)") );
PMTPID_LONGTEXT, VLC_TRUE );
add_integer( SOUT_CFG_PREFIX "tsid", 0, NULL, TSID_TEXT,
TSID_LONGTEXT, VLC_TRUE );
- add_integer( SOUT_CFG_PREFIX "program-pmt", 1, NULL, PMTPROG_TEXT,
- PMTPROG_LONGTEXT, VLC_TRUE );
+ add_integer( SOUT_CFG_PREFIX "netid", 0, NULL, NETID_TEXT,
+ NETID_LONGTEXT, VLC_TRUE );
+ add_string( SOUT_CFG_PREFIX "program-pmt", NULL, NULL, PMTPROG_TEXT,
+ PMTPROG_LONGTEXT, VLC_TRUE );
add_bool( SOUT_CFG_PREFIX "es-id-pid", 0, NULL, PID_TEXT, PID_LONGTEXT,
VLC_TRUE );
+ add_string( SOUT_CFG_PREFIX "muxpmt", NULL, NULL, MUXPMT_TEXT, MUXPMT_LONGTEXT, VLC_TRUE );
+ add_string( SOUT_CFG_PREFIX "sdtdesc", NULL, NULL, SDTDESC_TEXT, SDTDESC_LONGTEXT, VLC_TRUE );
add_integer( SOUT_CFG_PREFIX "shaping", 200, NULL,SHAPING_TEXT,
SHAPING_LONGTEXT, VLC_TRUE );
* Local data structures
*****************************************************************************/
static const char *ppsz_sout_options[] = {
- "pid-video", "pid-audio", "pid-spu", "pid-pmt", "tsid", "program-pmt",
+ "pid-video", "pid-audio", "pid-spu", "pid-pmt", "tsid", "netid",
"es-id-pid", "shaping", "pcr", "bmin", "bmax", "use-key-frames",
"dts-delay", "csa-ck", "csa-pkt", "crypt-audio", "crypt-video",
+ "muxpmt", "sdtdesc", "program-pmt",
NULL
};
+typedef struct pmt_map_t /* Holds the mapping between the pmt-pid/pmt table */
+{
+ int i_pid;
+ unsigned long i_prog;
+} pmt_map_t;
+
+typedef struct sdt_desc_t
+{
+ char *psz_provider;
+ char *psz_service_name; /* name of program */
+} sdt_desc_t;
+
typedef struct
{
int i_depth;
int i_video_bound;
vlc_bool_t b_es_id_pid;
+ vlc_bool_t b_sdt;
int i_pid_video;
int i_pid_audio;
int i_pid_spu;
int i_pid_free; /* first usable pid */
int i_tsid;
+ int i_netid;
+ int i_num_pmt;
+ int i_pmtslots;
int i_pat_version_number;
ts_stream_t pat;
int i_pmt_version_number;
- ts_stream_t pmt; /* Up to now only one program */
- int i_pmt_program_number;
+ ts_stream_t pmt[MAX_PMT];
+ pmt_map_t pmtmap[MAX_PMT_PID];
+ int i_pmt_program_number[MAX_PMT];
+ sdt_desc_t sdt_descriptors[MAX_PMT];
int i_mpeg4_streams;
int i_null_continuity_counter; /* Needed ? */
+ ts_stream_t sdt;
+ dvbpsi_pmt_t *dvbpmt;
/* for TS building */
int64_t i_bitrate_min;
return i_pid;
}
+static int pmtcompare( const void *pa, const void *pb )
+{
+ if ( ((pmt_map_t *)pa)->i_pid < ((pmt_map_t *)pb)->i_pid )
+ return -1;
+ else if ( ((pmt_map_t *)pa)->i_pid > ((pmt_map_t *)pb)->i_pid )
+ return 1;
+ else
+ return 0;
+}
+
+static int intcompare( const void *pa, const void *pb )
+{
+ if ( *(int *)pa < *(int *)pb )
+ return -1;
+ else if ( *(int *)pa > *(int *)pb )
+ return 1;
+ else
+ return 0;
+}
+
/*****************************************************************************
* Local prototypes
*****************************************************************************/
sout_mux_t *p_mux =(sout_mux_t*)p_this;
sout_mux_sys_t *p_sys = NULL;
vlc_value_t val;
+ int i;
- msg_Dbg( p_mux, "Open" );
sout_CfgParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
p_sys = malloc( sizeof( sout_mux_sys_t ) );
if( !p_sys )
return VLC_ENOMEM;
+ p_sys->i_pmtslots = p_sys->b_sdt = 0;
+ p_sys->i_num_pmt = 1;
+ p_sys->dvbpmt = NULL;
+ memset( &p_sys->pmtmap, 0, sizeof(p_sys->pmtmap) );
p_mux->pf_control = Control;
p_mux->pf_addstream = AddStream;
p_mux->p_sys = p_sys;
srand( (uint32_t)mdate() );
+ for ( i = 0; i < MAX_PMT; i++ )
+ p_sys->sdt_descriptors[i].psz_service_name
+ = p_sys->sdt_descriptors[i].psz_provider = NULL;
+ memset( p_sys->sdt_descriptors, 0, sizeof(sdt_desc_t) );
p_sys->i_audio_bound = 0;
p_sys->i_video_bound = 0;
+ var_Get( p_mux, SOUT_CFG_PREFIX "es-id-pid", &val );
+ p_sys->b_es_id_pid = val.b_bool;
+
+ var_Get( p_mux, SOUT_CFG_PREFIX "muxpmt", &val );
+ /*
+ fetch string of pmts. Here's a sample: --sout-ts-muxpmt="0x451,0x200,0x28a,0x240,,0x450,0x201,0x28b,0x241,,0x452,0x202,0x28c,0x242"
+ This would mean 0x451, 0x200, 0x28a, 0x240 would fall under one pmt (program), 0x450,0x201,0x28b,0x241 would fall under another
+ */
+ if( val.psz_string != NULL && *val.psz_string )
+ {
+ char *psz_next;
+ char *psz = val.psz_string;
+ uint16_t i_pid;
+ psz_next = psz;
+
+ while( psz != NULL )
+ {
+ i_pid = strtoul( psz, &psz_next, 0 );
+
+ if ( strlen(psz_next) > 0 )
+ psz = &psz_next[1];
+ if ( i_pid == 0 )
+ {
+ p_sys->i_num_pmt++;
+ if ( p_sys->i_num_pmt > MAX_PMT )
+ {
+ msg_Err( p_mux,
+ "Number of PMTs greater than compiled maximum (%d)", MAX_PMT );
+ p_sys->i_num_pmt = MAX_PMT;
+ }
+ }
+ else
+ {
+ p_sys->pmtmap[p_sys->i_pmtslots].i_pid = i_pid;
+ p_sys->pmtmap[p_sys->i_pmtslots].i_prog = p_sys->i_num_pmt - 1;
+ p_sys->i_pmtslots++;
+ if ( p_sys->i_pmtslots > MAX_PMT_PID )
+ {
+ msg_Err( p_mux,
+ "Number of pids in PMT greater than compiled maximum (%d)",
+ MAX_PMT_PID );
+ p_sys->i_pmtslots = MAX_PMT_PID;
+ }
+ }
+
+ /* Now sort according to pids for fast search later on */
+ qsort( (void *)p_sys->pmtmap, p_sys->i_pmtslots,
+ sizeof(pmt_map_t), &pmtcompare );
+ if ( !*psz_next )
+ psz = NULL;
+ }
+ }
+ if( val.psz_string != NULL) free( val.psz_string );
+
p_sys->i_pat_version_number = rand() % 32;
p_sys->pat.i_pid = 0;
p_sys->pat.i_continuity_counter = 0;
p_sys->i_tsid = val.i_int;
else
p_sys->i_tsid = rand() % 65536;
+ var_Get( p_mux, SOUT_CFG_PREFIX "netid", &val );
+ if ( val.i_int )
+ p_sys->i_netid = val.i_int;
+ else
+ p_sys->i_netid = rand() % 65536;
p_sys->i_pmt_version_number = rand() % 32;
- p_sys->pmt.i_continuity_counter = 0;
+ p_sys->sdt.i_continuity_counter = 0;
+
+ for( i = 0; i < p_sys->i_num_pmt; i++ )
+ p_sys->pmt[i].i_continuity_counter = 0;
+
+ var_Get( p_mux, SOUT_CFG_PREFIX "sdtdesc", &val );
+ p_sys->b_sdt = val.psz_string && *val.psz_string ? VLC_TRUE : VLC_FALSE;
+
+ /* Syntax is provider_sdt1,service_name_sdt1,provider_sdt2,service_name_sdt2... */
+ if( val.psz_string != NULL && *val.psz_string )
+ {
+
+ char *psz = val.psz_string;
+ char *psz_sdttoken = psz;
+
+ i = 0;
+ while ( psz_sdttoken != NULL )
+ {
+ char *psz_end = strchr( psz_sdttoken, ',' );
+ if( psz_end != NULL )
+ {
+ *psz_end++ = '\0';
+ }
+ if ( !(i % 2) )
+ {
+ p_sys->sdt_descriptors[i/2].psz_provider
+ = strdup(psz_sdttoken);
+ }
+ else
+ {
+ p_sys->sdt_descriptors[i/2].psz_service_name
+ = strdup(psz_sdttoken);
+ }
+
+ i++;
+ psz_sdttoken = psz_end;
+ }
+ }
+ if( val.psz_string != NULL ) free( val.psz_string );
var_Get( p_mux, SOUT_CFG_PREFIX "program-pmt", &val );
- if (val.i_int )
+ if( val.psz_string && *val.psz_string )
{
- p_sys->i_pmt_program_number = val.i_int;
+ char *psz_next;
+ char *psz = val.psz_string;
+ uint16_t i_pid;
+
+ psz_next = psz;
+ i = 0;
+ while ( psz != NULL )
+ {
+ i_pid = strtoul( psz, &psz_next, 0 );
+ if( strlen(psz_next) > 0 )
+ psz = &psz_next[1];
+ else
+ psz = NULL;
+
+ if( i_pid == 0 )
+ {
+ if( i > MAX_PMT )
+ msg_Err( p_mux, "Number of PMTs > maximum (%d)",
+ MAX_PMT );
+ }
+ else
+ {
+ p_sys->i_pmt_program_number[i] = i_pid;
+ i++;
+ }
+ }
}
else
{
- p_sys->i_pmt_program_number = 1;
+ /* Option not specified, use 1, 2, 3... */
+ for( i = 0; i < p_sys->i_num_pmt; i++ )
+ p_sys->i_pmt_program_number[i] = i + 1;
}
+ if( val.psz_string != NULL ) free( val.psz_string );
var_Get( p_mux, SOUT_CFG_PREFIX "pid-pmt", &val );
- if (val.i_int )
+ if( val.i_int )
{
- p_sys->pmt.i_pid = val.i_int;
+ for( i = 0; i < p_sys->i_num_pmt; i++ )
+ p_sys->pmt[i].i_pid = val.i_int + i; /* Does this make any sense? */
}
else
{
- p_sys->pmt.i_pid = 0x42;
+ for( i = 0; i < p_sys->i_num_pmt; i++ )
+ p_sys->pmt[i].i_pid = 0x42 + i;
}
- p_sys->i_pid_free = p_sys->pmt.i_pid + 1;
-
- var_Get( p_mux, SOUT_CFG_PREFIX "es-id-pid", &val );
- p_sys->b_es_id_pid = val.b_bool;
+ p_sys->i_pid_free = p_sys->pmt[p_sys->i_num_pmt - 1].i_pid + 1;
var_Get( p_mux, SOUT_CFG_PREFIX "pid-video", &val );
p_sys->i_pid_video = val.i_int;
{
sout_mux_t *p_mux = (sout_mux_t*)p_this;
sout_mux_sys_t *p_sys = p_mux->p_sys;
+ int i;
- msg_Dbg( p_mux, "Close" );
if( p_sys->csa )
{
csa_Delete( p_sys->csa );
}
+ for( i = 0; i < MAX_PMT; i++ )
+ {
+ if( p_sys->sdt_descriptors[i].psz_service_name != NULL )
+ free( p_sys->sdt_descriptors[i].psz_service_name );
+ if( p_sys->sdt_descriptors[i].psz_provider != NULL )
+ free( p_sys->sdt_descriptors[i].psz_provider );
+ }
+
+ if( p_sys->dvbpmt != NULL ) /* safety */
+ free ( p_sys->dvbpmt );
free( p_sys );
}
sout_buffer_chain_t *c )
{
sout_mux_sys_t *p_sys = p_mux->p_sys;
- block_t *p_pat;
+ block_t *p_pat;
dvbpsi_pat_t pat;
dvbpsi_psi_section_t *p_section;
+ int i;
dvbpsi_InitPAT( &pat, p_sys->i_tsid, p_sys->i_pat_version_number,
1 ); /* b_current_next */
- /* add all program (only one) */
- dvbpsi_PATAddProgram( &pat,
- p_sys->i_pmt_program_number, /* i_number */
- p_sys->pmt.i_pid ); /* i_pid */
+ /* add all programs */
+ for ( i = 0; i < p_sys->i_num_pmt; i++ )
+ dvbpsi_PATAddProgram( &pat,
+ p_sys->i_pmt_program_number[i],
+ p_sys->pmt[i].i_pid );
p_section = dvbpsi_GenPATSections( &pat,
0 ); /* max program per section */
sout_buffer_chain_t *c )
{
sout_mux_sys_t *p_sys = p_mux->p_sys;
- block_t *p_pmt;
+ block_t *p_pmt[MAX_PMT];
+ block_t *p_sdt;
- dvbpsi_pmt_t pmt;
+ dvbpsi_sdt_t sdt;
dvbpsi_pmt_es_t *p_es;
- dvbpsi_psi_section_t *p_section;
+ dvbpsi_psi_section_t *p_section[MAX_PMT], *p_section2;
+ dvbpsi_sdt_service_t *p_service;
+ char *psz_sdt_desc;
+ int i_pidinput;
+
+ int i_stream;
+ int i;
+ int *p_usepid = NULL;
- int i_stream;
+ if( p_sys->dvbpmt == NULL )
+ p_sys->dvbpmt = malloc( p_sys->i_num_pmt * sizeof(dvbpsi_pmt_t) );
+ if( p_sys->b_sdt )
+ dvbpsi_InitSDT( &sdt, p_sys->i_tsid, 1, 1, p_sys->i_netid );
- dvbpsi_InitPMT( &pmt,
- p_sys->i_pmt_program_number, /* program number */
- p_sys->i_pmt_version_number,
- 1, /* b_current_next */
- p_sys->i_pcr_pid );
+ for( i = 0; i < p_sys->i_num_pmt; i++ )
+ {
+ dvbpsi_InitPMT( &p_sys->dvbpmt[i],
+ p_sys->i_pmt_program_number[i], /* program number */
+ p_sys->i_pmt_version_number,
+ 1, /* b_current_next */
+ p_sys->i_pcr_pid );
+
+ if( p_sys->b_sdt )
+ {
+ p_service = dvbpsi_SDTAddService( &sdt,
+ p_sys->i_pmt_program_number[i], /* service id */
+ 0, /* eit schedule */
+ 0, /* eit present */
+ 4, /* running status ("4=RUNNING") */
+ 0 ); /* free ca */
+
+#define psz_sdtprov p_sys->sdt_descriptors[i].psz_provider
+#define psz_sdtserv p_sys->sdt_descriptors[i].psz_service_name
+
+ /* FIXME: Ineffecient malloc's & ugly code...... */
+ if( psz_sdtprov != NULL && psz_sdtserv != NULL )
+ {
+ psz_sdt_desc = malloc( 3 + strlen(psz_sdtprov)
+ + strlen(psz_sdtserv) );
+ psz_sdt_desc[0] = 0x01; /* digital television service */
+
+ /* service provider name length */
+ psz_sdt_desc[1] = (char)strlen(psz_sdtprov);
+ memcpy( &psz_sdt_desc[2], psz_sdtprov, strlen(psz_sdtprov) );
+
+ /* service name length */
+ psz_sdt_desc[ 2 + strlen(psz_sdtprov) ]
+ = (char)strlen(psz_sdtserv);
+ memcpy( &psz_sdt_desc[3+strlen(psz_sdtprov)], psz_sdtserv,
+ strlen(psz_sdtserv) );
+
+ dvbpsi_SDTServiceAddDescriptor( p_service, 0x48,
+ 3 + strlen(psz_sdtprov) + strlen(psz_sdtserv),
+ psz_sdt_desc );
+ free( psz_sdt_desc );
+ }
+#undef psz_sdtprov
+#undef psz_sdtserv
+ }
+ }
if( p_sys->i_mpeg4_streams > 0 )
{
bits_write( &bits_fix_IOD, 24,
GetDescriptorLength24b( bits.i_data -
bits_fix_IOD.i_data - 3 ) );
- dvbpsi_PMTAddDescriptor( &pmt, 0x1d, bits.i_data, bits.p_data );
+
+#ifdef HAVE_BSEARCH
+ i_pidinput = ((es_format_t *)(p_mux->pp_inputs[i]->p_fmt))->i_id;
+ p_usepid = bsearch( &i_pidinput, p_sys->pmtmap, p_sys->i_pmtslots,
+ sizeof(pmt_map_t), intcompare );
+ p_usepid = bsearch( &p_usepid, p_sys->pmtmap, p_sys->i_num_pmt,
+ sizeof(pmt_map_t), pmtcompare );
+ if( p_usepid != NULL )
+ dvbpsi_PMTAddDescriptor(
+ &p_sys->dvbpmt[((pmt_map_t *)p_usepid)->i_prog], 0x1d,
+ bits.i_data, bits.p_data );
+ else
+ msg_Err( p_mux, "Received an unmapped PID" );
+#else
+ dvbpsi_PMTAddDescriptor( &p_sys->dvbpmt[0], 0x1d, bits.i_data,
+ bits.p_data );
+#endif
}
for( i_stream = 0; i_stream < p_mux->i_nb_inputs; i_stream++ )
ts_stream_t *p_stream;
p_stream = (ts_stream_t*)p_mux->pp_inputs[i_stream]->p_sys;
+ i_pidinput = ((es_format_t *)(p_mux->pp_inputs[i_stream]->p_fmt))->i_id;
+ p_usepid = bsearch( &i_pidinput, p_sys->pmtmap, p_sys->i_pmtslots,
+ sizeof(pmt_map_t), intcompare );
+
+#ifdef HAVE_BSEARCH
+ if( p_usepid != NULL )
+ p_es = dvbpsi_PMTAddES(
+ &p_sys->dvbpmt[((pmt_map_t *)p_usepid)->i_prog],
+ p_stream->i_stream_type, p_stream->i_pid );
+ else
+ /* If there's an error somewhere, dump it to the first pmt */
+#endif
+ p_es = dvbpsi_PMTAddES( &p_sys->dvbpmt[0], p_stream->i_stream_type,
+ p_stream->i_pid );
- p_es = dvbpsi_PMTAddES( &pmt, p_stream->i_stream_type,
- p_stream->i_pid );
if( p_stream->i_stream_id == 0xfa || p_stream->i_stream_id == 0xfb )
{
uint8_t es_id[2];
}
}
- p_section = dvbpsi_GenPMTSections( &pmt );
-
- p_pmt = WritePSISection( p_mux->p_sout, p_section );
-
- PEStoTS( p_mux->p_sout, c, p_pmt, &p_sys->pmt );
+ for( i = 0; i < p_sys->i_num_pmt; i++ )
+ {
+ p_section[i] = dvbpsi_GenPMTSections( &p_sys->dvbpmt[i] );
+ p_pmt[i] = WritePSISection( p_mux->p_sout, p_section[i] );
+ PEStoTS( p_mux->p_sout, c, p_pmt[i], &p_sys->pmt[i] );
+ dvbpsi_DeletePSISections( p_section[i] );
+ dvbpsi_EmptyPMT( &p_sys->dvbpmt[i] );
+ }
- dvbpsi_DeletePSISections( p_section );
- dvbpsi_EmptyPMT( &pmt );
+ if( p_sys->b_sdt )
+ {
+ p_section2 = dvbpsi_GenSDTSections( &sdt );
+ p_sdt = WritePSISection( p_mux->p_sout, p_section2 );
+ p_sys->sdt.i_pid = 0x11;
+ PEStoTS( p_mux->p_sout, c, p_sdt, &p_sys->sdt );
+ dvbpsi_DeletePSISections( p_section2 );
+ dvbpsi_EmptySDT( &sdt );
+ }
}