#include <vlc_common.h>
#include <vlc_plugin.h>
-#include <ctype.h>
#include <assert.h>
#include <vlc_access.h> /* DVB-specific things */
#include <vlc_iso_lang.h>
#include <vlc_network.h>
#include <vlc_charset.h>
+#include <vlc_fs.h>
#include "../mux/mpeg/csa.h"
# include "tables/eit.h"
# endif
#endif
-#include <time.h>
+
+/* TDT support */
+#ifdef _DVBPSI_DR_58_H_
+# define TS_USE_TDT 1
+# ifdef HAVE_DVBPSI_DR_H
+# include <dvbpsi/tot.h>
+# else
+# include "tables/tot.h"
+# endif
+#else
+# include <time.h>
+#endif
+
#undef TS_DEBUG
/*****************************************************************************
"Tweak the buffer size for reading and writing an integer number of packets." \
"Specify the size of the buffer here and not the number of packets." )
+#define SPLIT_ES_TEXT N_("Separate sub-streams")
+#define SPLIT_ES_LONGTEXT N_( \
+ "Separate teletex/dvbs pages into independant ES. " \
+ "It can be usefull to turn off this option when using stream output." )
+
vlc_module_begin ()
set_description( N_("MPEG Transport Stream demuxer") )
set_shortname ( "MPEG-TS" )
add_bool( "ts-dump-append", false, NULL, APPEND_TEXT, APPEND_LONGTEXT, false )
add_integer( "ts-dump-size", 16384, NULL, DUMPSIZE_TEXT,
DUMPSIZE_LONGTEXT, true )
+ add_bool( "ts-split-es", true, NULL, SPLIT_ES_TEXT, SPLIT_ES_LONGTEXT, false )
set_capability( "demux", 10 )
set_callbacks( Open, Close )
csa_t *csa;
int i_csa_pkt_size;
bool b_silent;
+ bool b_split_es;
bool b_udp_out;
int fd; /* udp socket */
/* */
bool b_dvb_meta;
+ int64_t i_tdt_delta;
int64_t i_dvb_start;
int64_t i_dvb_length;
+ bool b_broken_charset; /* True if broken encoding is used in EPG/SDT */
/* */
int i_current_program;
bool b_start_record;
};
-static int i_broken_epg;
-
static int Demux ( demux_t *p_demux );
static int DemuxFile( demux_t *p_demux );
static int Control( demux_t *p_demux, int i_query, va_list args );
msg_Info( p_demux, "dumping raw stream to standard output" );
p_sys->p_file = stdout;
}
- else if( ( p_sys->p_file = utf8_fopen( p_sys->psz_file, psz_mode ) ) == NULL )
+ else if( ( p_sys->p_file = vlc_fopen( p_sys->psz_file, psz_mode ) ) == NULL )
{
msg_Err( p_demux, "cannot create `%s' for writing", p_sys->psz_file );
p_sys->b_file_out = false;
{
p_sys->i_ts_read = 1500 / p_sys->i_packet_size;
}
- p_sys->buffer = malloc( p_sys->i_packet_size * p_sys->i_ts_read );
+ p_sys->buffer = xmalloc( p_sys->i_packet_size * p_sys->i_ts_read );
msg_Info( p_demux, "%s raw stream to file `%s' reading packets %d",
b_append ? "appending" : "dumping", p_sys->psz_file,
p_sys->i_ts_read );
p_sys->b_dvb_meta = true;
p_sys->b_access_control = true;
p_sys->i_current_program = 0;
+ p_sys->i_tdt_delta = 0;
p_sys->i_dvb_start = 0;
p_sys->i_dvb_length = 0;
+ p_sys->b_broken_charset = false;
+
for( i = 0; i < 8192; i++ )
{
ts_pid_t *pid = &p_sys->pid[i];
eit->psi->handle =
dvbpsi_AttachDemux( (dvbpsi_demux_new_cb_t)PSINewTableCallBack,
p_demux );
+#ifdef TS_USE_TDT
+ ts_pid_t *tdt = &p_sys->pid[0x14];
+ PIDInit( tdt, true, NULL );
+ tdt->psi->handle =
+ dvbpsi_AttachDemux( (dvbpsi_demux_new_cb_t)PSINewTableCallBack,
+ p_demux );
+#endif
if( p_sys->b_access_control )
{
if( stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_STATE, 0x11, true ) ||
+#ifdef TS_USE_TDT
+ stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
+ ACCESS_SET_PRIVATE_ID_STATE, 0x14, true ) ||
+#endif
stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
ACCESS_SET_PRIVATE_ID_STATE, 0x12, true ) )
p_sys->b_access_control = false;
free( psz_string );
p_sys->b_silent = var_CreateGetBool( p_demux, "ts-silent" );
+ p_sys->b_split_es = var_InheritBool( p_demux, "ts-split-es" );
return VLC_SUCCESS;
}
free( pid->psi );
break;
default:
- if( p_sys->b_dvb_meta && ( pid->i_pid == 0x11 || pid->i_pid == 0x12 ) )
+ if( p_sys->b_dvb_meta && ( pid->i_pid == 0x11 || pid->i_pid == 0x12 || pid->i_pid == 0x14 ) )
{
- /* SDT or EIT */
+ /* SDT or EIT or TDT */
dvbpsi_DetachDemux( pid->psi->handle );
free( pid->psi );
}
/* If in dump mode, then close the file */
if( p_sys->b_file_out )
{
- msg_Info( p_demux ,"closing %s (%"PRId64" Kbytes dumped)",
+ msg_Info( p_demux ,"closing %s (%"PRId64" KiB dumped)",
p_sys->psz_file, p_sys->i_write / 1024 );
if( p_sys->p_file != stdout )
{
if( p_pid->psi )
{
- if( p_pid->i_pid == 0 || ( p_sys->b_dvb_meta && ( p_pid->i_pid == 0x11 || p_pid->i_pid == 0x12 ) ) )
+ if( p_pid->i_pid == 0 || ( p_sys->b_dvb_meta && ( p_pid->i_pid == 0x11 || p_pid->i_pid == 0x12 || p_pid->i_pid == 0x14 ) ) )
{
dvbpsi_PushPacket( p_pid->psi->handle, p_pkt->p_buffer );
}
if( pi_time )
*pi_time = 0;
- if( p_sys->b_access_control && p_sys->i_dvb_length > 0 )
+ if( p_sys->i_dvb_length > 0 )
{
- /* FIXME we should not use time() but read the date from the tdt */
- const time_t t = time( NULL );
+#ifdef TS_USE_TDT
+ const int64_t t = mdate() + p_sys->i_tdt_delta;
+#else
+ const int64_t t = CLOCK_FREQ * time ( NULL );
+#endif
+
if( p_sys->i_dvb_start <= t && t < p_sys->i_dvb_start + p_sys->i_dvb_length )
{
if( pi_length )
- *pi_length = p_sys->i_dvb_length * INT64_C(1000000);
+ *pi_length = p_sys->i_dvb_length;
if( pi_time )
- *pi_time = (t - p_sys->i_dvb_start) * INT64_C(1000000);
+ *pi_time = t - p_sys->i_dvb_start;
return VLC_SUCCESS;
}
}
if( !b_old_valid )
{
- pid->psi = malloc( sizeof( ts_psi_t ) );
+ pid->psi = xmalloc( sizeof( ts_psi_t ) );
if( pid->psi )
{
pid->psi->handle = NULL;
int i;
if( i_dts >= 0 )
- {
- p_pes->i_dts = i_dts * 100 / 9;
- }
+ p_pes->i_dts = VLC_TS_0 + i_dts * 100 / 9;
+
if( i_pts >= 0 )
- {
- p_pes->i_pts = i_pts * 100 / 9;
- }
+ p_pes->i_pts = VLC_TS_0 + i_pts * 100 / 9;
+
p_pes->i_length = i_length * 100 / 9;
p_block = block_ChainGather( p_pes );
{
es_out_Control( p_demux->out, ES_OUT_SET_GROUP_PCR,
(int)p_sys->pmt[i]->psi->prg[i_prg]->i_number,
- (int64_t)(i_pcr * 100 / 9) );
+ (int64_t)(VLC_TS_0 + i_pcr * 100 / 9) );
}
}
}
pid->i_cc = i_cc;
if( pid->es->p_pes && pid->es->fmt.i_cat != VIDEO_ES )
{
- /* Small video artifacts are usually better then
+ /* Small video artifacts are usually better than
* dropping full frames */
pid->es->p_pes->i_flags |= BLOCK_FLAG_CORRUPTED;
}
case 0x04: /* MPEG-2 audio */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MPGA );
break;
- case 0x11: /* MPEG4 (audio) */
+ case 0x11: /* MPEG4 (audio) LATM */
case 0x0f: /* ISO/IEC 13818-7 Audio with ADTS transport syntax */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MP4A );
break;
case 0x85: /* DTS (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_DTS );
break;
+ case 0x87: /* E-AC3 */
+ es_format_Init( fmt, AUDIO_ES, VLC_CODEC_EAC3 );
+ break;
case 0x91: /* A52 vls (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', 'b' ) );
{
demux_sys_t *p_sys = p_demux->p_sys;
- if( !p_sys->b_dvb_meta || ( i_pid != 0x11 && i_pid != 0x12 ) )
+ if( !p_sys->b_dvb_meta || ( i_pid != 0x11 && i_pid != 0x12 && i_pid != 0x14 ) )
return;
msg_Warn( p_demux, "Switching to non DVB mode" );
/* This doesn't look like a DVB stream so don't try
- * parsing the SDT/EDT */
+ * parsing the SDT/EDT/TDT */
- for( int i = 0x11; i <= 0x12; i++ )
+ for( int i = 0x11; i <= 0x14; i++ )
{
+ if( i == 0x13 ) continue;
ts_pid_t *p_pid = &p_sys->pid[i];
if( p_pid->psi )
{
#ifdef TS_USE_DVB_SI
/* FIXME same than dvbsi_to_utf8 from dvb access */
static char *EITConvertToUTF8( const unsigned char *psz_instring,
- size_t i_length )
+ size_t i_length,
+ bool b_broken )
{
const char *psz_encoding;
char *psz_outstring;
{
/* According to ETSI EN 300 468 Annex A, this should be ISO6937,
* but some broadcasters use different charset... */
- if ( i_broken_epg == 1 )
- {
+ if( b_broken )
psz_encoding = "ISO_8859-1";
- }
else
- {
psz_encoding = "ISO_6937";
- }
offset = 0;
}
p_sdt->i_ts_id, p_sdt->i_version, p_sdt->b_current_next,
p_sdt->i_network_id );
- i_broken_epg = 0;
+ p_sys->b_broken_charset = false;
for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
{
/* Workarounds for broadcasters with broken EPG */
- if ( p_sdt->i_network_id == 133 )
- i_broken_epg = 1; /* SKY DE & BetaDigital use ISO8859-1 */
+ if( p_sdt->i_network_id == 133 )
+ p_sys->b_broken_charset = true; /* SKY DE & BetaDigital use ISO8859-1 */
- if ( (pD->i_service_provider_name_length == 4) &&
- !strncmp(pD->i_service_provider_name, "CSAT", 4) )
- i_broken_epg = 1; /* CanalSat FR uses ISO8859-1 */
+ /* List of providers using ISO8859-1 */
+ static const char ppsz_broken_providers[][8] = {
+ "CSAT", /* CanalSat FR */
+ "GR1", /* France televisions */
+ "MULTI4", /* NT1 */
+ "MR5", /* France 2/M6 HD */
+ ""
+ };
+ for( int i = 0; *ppsz_broken_providers[i]; i++ )
+ {
+ const size_t i_length = strlen(ppsz_broken_providers[i]);
+ if( pD->i_service_provider_name_length == i_length &&
+ !strncmp( pD->i_service_provider_name, ppsz_broken_providers[i], i_length ) )
+ p_sys->b_broken_charset = true;
+ }
/* FIXME: Digital+ ES also uses ISO8859-1 */
str1 = EITConvertToUTF8(pD->i_service_provider_name,
- pD->i_service_provider_name_length);
+ pD->i_service_provider_name_length,
+ p_sys->b_broken_charset );
str2 = EITConvertToUTF8(pD->i_service_name,
- pD->i_service_name_length);
+ pD->i_service_name_length,
+ p_sys->b_broken_charset );
msg_Dbg( p_demux, " - type=%d provider=%s name=%s",
pD->i_service_type, str1, str2 );
}
#undef CVT_FROM_BCD
+#ifdef TS_USE_TDT
+static void TDTCallBack( demux_t *p_demux, dvbpsi_tot_t *p_tdt )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ p_sys->i_tdt_delta = CLOCK_FREQ * EITConvertStartTime( p_tdt->i_utc_time )
+ - mdate();
+ dvbpsi_DeleteTOT(p_tdt);
+}
+#endif
+
static void EITCallBack( demux_t *p_demux,
dvbpsi_eit_t *p_eit, bool b_current_following )
{
dvbpsi_short_event_dr_t *pE = dvbpsi_DecodeShortEventDr( p_dr );
- if( pE )
+ /* Only take first description, as we don't handle language-info
+ for epg atm*/
+ if( pE && psz_name == NULL)
{
- psz_name = EITConvertToUTF8( pE->i_event_name, pE->i_event_name_length);
- psz_text = EITConvertToUTF8( pE->i_text, pE->i_text_length );
+ psz_name = EITConvertToUTF8( pE->i_event_name, pE->i_event_name_length,
+ p_sys->b_broken_charset );
+ psz_text = EITConvertToUTF8( pE->i_text, pE->i_text_length,
+ p_sys->b_broken_charset );
msg_Dbg( p_demux, " - short event lang=%3.3s '%s' : '%s'",
pE->i_iso_639_code, psz_name, psz_text );
}
if( pE->i_text_length > 0 )
{
- char *psz_text = EITConvertToUTF8( pE->i_text, pE->i_text_length );
+ char *psz_text = EITConvertToUTF8( pE->i_text, pE->i_text_length,
+ p_sys->b_broken_charset );
if( psz_text )
{
msg_Dbg( p_demux, " - text='%s'", psz_text );
- psz_extra = realloc( psz_extra, strlen(psz_extra) + strlen(psz_text) + 1 );
+ psz_extra = xrealloc( psz_extra,
+ strlen(psz_extra) + strlen(psz_text) + 1 );
strcat( psz_extra, psz_text );
free( psz_text );
}
for( int i = 0; i < pE->i_entry_count; i++ )
{
char *psz_dsc = EITConvertToUTF8( pE->i_item_description[i],
- pE->i_item_description_length[i] );
- char *psz_itm = EITConvertToUTF8( pE->i_item[i], pE->i_item_length[i] );
+ pE->i_item_description_length[i],
+ p_sys->b_broken_charset );
+ char *psz_itm = EITConvertToUTF8( pE->i_item[i], pE->i_item_length[i],
+ p_sys->b_broken_charset );
if( psz_dsc && psz_itm )
{
msg_Dbg( p_demux, " - desc='%s' item='%s'", psz_dsc, psz_itm );
#if 0
- psz_extra = realloc( psz_extra, strlen(psz_extra) + strlen(psz_dsc) + strlen(psz_itm) + 3 + 1 );
+ psz_extra = xrealloc( psz_extra,
+ strlen(psz_extra) + strlen(psz_dsc) +
+ strlen(psz_itm) + 3 + 1 );
strcat( psz_extra, "(" );
strcat( psz_extra, psz_dsc );
strcat( psz_extra, " " );
}
if( p_epg->i_event > 0 )
{
- if( p_eit->i_service_id == p_sys->i_current_program && b_current_following )
+ if( b_current_following &&
+ ( p_sys->i_current_program == -1 ||
+ p_sys->i_current_program == p_eit->i_service_id ) )
{
p_sys->i_dvb_length = 0;
p_sys->i_dvb_start = 0;
if( p_epg->p_current )
{
- p_sys->i_dvb_start = p_epg->p_current->i_start;
- p_sys->i_dvb_length = p_epg->p_current->i_duration;
+ p_sys->i_dvb_start = CLOCK_FREQ * p_epg->p_current->i_start;
+ p_sys->i_dvb_length = CLOCK_FREQ * p_epg->p_current->i_duration;
}
}
es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, p_eit->i_service_id, p_epg );
(dvbpsi_eit_callback)EITCallBackSchedule;
dvbpsi_AttachEIT( h, i_table_id, i_extension, cb, p_demux );
}
+#ifdef TS_USE_TDT
+ else if( p_demux->p_sys->pid[0x11].psi->i_sdt_version != -1 &&
+ i_table_id == 0x70 ) /* TDT */
+ {
+ msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
+ i_table_id, i_table_id, i_extension, i_extension );
+ dvbpsi_AttachTOT( h, i_table_id, i_extension,
+ (dvbpsi_tot_callback)TDTCallBack, p_demux);
+ }
+#endif
+
}
#endif
/* */
es_format_Init( p_fmt, SPU_ES, VLC_CODEC_TELETEXT );
- /* In stream output mode, do not separate the stream by page */
- if( p_demux->out->b_sout || i_page <= 0 )
+ if( !p_demux->p_sys->b_split_es || i_page <= 0 )
{
p_fmt->subs.teletext.i_magazine = -1;
p_fmt->subs.teletext.i_page = 0;
if( !p_dr )
p_dr = PMTEsFindDescriptor( p_es, 0x56 );
- if( p_demux->out->b_sout && p_dr && p_dr->i_length > 0 )
+ if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
{
- /* Descriptor pass-through for sout */
+ /* Descriptor pass-through */
p_fmt->p_extra = malloc( p_dr->i_length );
if( p_fmt->p_extra )
{
/* */
const ts_teletext_page_t *p = &p_page[i];
+ p_es->fmt.i_priority = (p->i_type == 0x02 || p->i_type == 0x05) ? 0 : -1;
p_es->fmt.psz_language = strndup( p->p_iso639, 3 );
p_es->fmt.psz_description = strdup(vlc_gettext(ppsz_teletext_type[p->i_type]));
p_es->fmt.subs.teletext.i_magazine = p->i_magazine;
}
#endif
- /* In stream output mode, do not separate the stream by page */
- if( p_demux->out->b_sout || i_page <= 0 )
+ if( !p_demux->p_sys->b_split_es || i_page <= 0 )
{
p_fmt->subs.dvb.i_id = -1;
p_fmt->psz_description = strdup( _("DVB subtitles") );
- if( p_demux->out->b_sout && p_dr && p_dr->i_length > 0 )
+ if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
{
- /* Descriptor pass-through for sout */
+ /* Descriptor pass-through */
p_fmt->p_extra = malloc( p_dr->i_length );
if( p_fmt->p_extra )
{
break;
case 0x84: /* E-AC3 */
- case 0x87: /* E-AC3 */
case 0xA1: /* Secondary E-AC3 */
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_EAC3;
break;
}
}
+
+static void PMTSetupEsRegistration( demux_t *p_demux, ts_pid_t *pid,
+ const dvbpsi_pmt_es_t *p_es )
+{
+ static const struct
+ {
+ char psz_tag[5];
+ int i_cat;
+ vlc_fourcc_t i_codec;
+ } p_regs[] = {
+ { "AC-3", AUDIO_ES, VLC_CODEC_A52 },
+ { "DTS1", AUDIO_ES, VLC_CODEC_DTS },
+ { "DTS2", AUDIO_ES, VLC_CODEC_DTS },
+ { "DTS3", AUDIO_ES, VLC_CODEC_DTS },
+ { "BSSD", AUDIO_ES, VLC_CODEC_302M },
+ { "VC-1", VIDEO_ES, VLC_CODEC_VC1 },
+ { "drac", VIDEO_ES, VLC_CODEC_DIRAC },
+ { "", UNKNOWN_ES, 0 }
+ };
+ es_format_t *p_fmt = &pid->es->fmt;
+
+ for( int i = 0; p_regs[i].i_cat != UNKNOWN_ES; i++ )
+ {
+ if( PMTEsHasRegistration( p_demux, p_es, p_regs[i].psz_tag ) )
+ {
+ p_fmt->i_cat = p_regs[i].i_cat;
+ p_fmt->i_codec = p_regs[i].i_codec;
+ break;
+ }
+ }
+}
+
static void PMTParseEsIso639( demux_t *p_demux, ts_pid_t *pid,
const dvbpsi_pmt_es_t *p_es )
{
{
PMTSetupEsHDMV( p_demux, pid, p_es );
}
+ else if( p_es->i_type >= 0x80 )
+ {
+ PMTSetupEsRegistration( p_demux, pid, p_es );
+ }
if( pid->es->fmt.i_cat == AUDIO_ES ||
( pid->es->fmt.i_cat == SPU_ES &&
}
}
- if( ProgramIsSelected( p_demux, prg->i_number ) )
- {
- /* Set CAM descrambling */
- stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
- ACCESS_SET_PRIVATE_ID_CA, p_pmt );
- }
- else
- {
+ /* Set CAM descrambling */
+ if( !ProgramIsSelected( p_demux, prg->i_number )
+ || stream_Control( p_demux->s, STREAM_CONTROL_ACCESS,
+ ACCESS_SET_PRIVATE_ID_CA, p_pmt ) != VLC_SUCCESS )
dvbpsi_DeletePMT( p_pmt );
- }
for( int i = 0; i < i_clean; i++ )
{