1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004, 2008-2011, 2015, 2020 VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
7 * Andy Gatward <a.j.gatward@reading.ac.uk>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
49 #include <bitstream/dvb/si.h>
50 #include <bitstream/ietf/rtp.h>
54 /*****************************************************************************
56 *****************************************************************************/
57 struct ev_loop
*event_loop
;
58 output_t
**pp_outputs
= NULL
;
61 bool b_passthrough
= false;
62 static const char *psz_conf_file
= NULL
;
63 char *psz_srv_socket
= NULL
;
64 static int i_priority
= -1;
68 char *psz_delsys
= NULL
;
70 char *psz_lnb_type
= "universal";
73 int i_srate
= 27500000;
77 int i_uncommitted
= 0;
81 char *psz_modulation
= NULL
;
84 char *psz_mis_pls_mode
= "ROOT";
85 int i_mis_pls_mode
= 0;
86 int i_mis_pls_code
= 0;
90 int i_transmission
= -1;
92 mtime_t i_frontend_timeout_duration
= DEFAULT_FRONTEND_TIMEOUT
;
93 mtime_t i_quit_timeout_duration
= 0;
94 int b_budget_mode
= 0;
96 int b_select_pmts
= 0;
97 int b_random_tsid
= 0;
98 char *psz_udp_src
= NULL
;
99 int i_asi_adapter
= 0;
100 const char *psz_native_charset
= "UTF-8//IGNORE";
101 print_type_t i_print_type
= PRINT_TEXT
;
102 bool b_print_enabled
= false;
104 mtime_t i_print_period
= 0;
105 mtime_t i_es_timeout
= 0;
106 mtime_t i_udp_lock_timeout
= DEFAULT_UDP_LOCK_TIMEOUT
;
108 int i_verbose
= DEFAULT_VERBOSITY
;
110 char *psz_syslog_ident
= NULL
;
112 bool b_enable_emm
= false;
113 bool b_enable_ecm
= false;
115 uint8_t pi_ssrc_global
[4] = { 0, 0, 0, 0 };
116 static bool b_udp_global
= false;
117 static bool b_dvb_global
= false;
118 static bool b_epg_global
= false;
119 static mtime_t i_latency_global
= DEFAULT_OUTPUT_LATENCY
;
120 static mtime_t i_retention_global
= DEFAULT_MAX_RETENTION
;
121 static int i_ttl_global
= 64;
123 static const char *psz_dvb_charset
= "UTF-8//IGNORE";
124 static iconv_t conf_iconv
= (iconv_t
)-1;
125 static uint16_t i_network_id
= 0xffff;
126 static dvb_string_t network_name
;
127 static dvb_string_t provider_name
;
129 /* TPS Input log filename */
130 char * psz_mrtg_file
= NULL
;
133 bool b_do_remap
= false;
134 uint16_t pi_newpids
[ N_MAP_PIDS
]; /* pmt, audio, video, spu */
136 void (*pf_Open
)( void ) = NULL
;
137 void (*pf_Reset
)( void ) = NULL
;
138 int (*pf_SetFilter
)( uint16_t i_pid
) = NULL
;
139 void (*pf_UnsetFilter
)( int i_fd
, uint16_t i_pid
) = NULL
;
141 /*****************************************************************************
142 * Configuration files
143 *****************************************************************************/
144 void config_Init( output_config_t
*p_config
)
146 memset( p_config
, 0, sizeof(output_config_t
) );
148 p_config
->psz_displayname
= NULL
;
149 p_config
->i_network_id
= i_network_id
;
150 dvb_string_init(&p_config
->network_name
);
151 dvb_string_init(&p_config
->service_name
);
152 dvb_string_init(&p_config
->provider_name
);
153 p_config
->psz_srcaddr
= NULL
;
155 p_config
->i_family
= AF_UNSPEC
;
156 p_config
->connect_addr
.ss_family
= AF_UNSPEC
;
157 p_config
->bind_addr
.ss_family
= AF_UNSPEC
;
158 p_config
->i_if_index_v6
= -1;
159 p_config
->i_srcport
= 0;
161 p_config
->pi_pids
= NULL
;
162 p_config
->b_passthrough
= false;
163 p_config
->b_do_remap
= false;
165 for ( i
= 0; i
< N_MAP_PIDS
; i
++ ) {
166 p_config
->pi_confpids
[i
] = UNUSED_PID
;
170 void config_Free( output_config_t
*p_config
)
172 free( p_config
->psz_displayname
);
173 dvb_string_clean( &p_config
->network_name
);
174 dvb_string_clean( &p_config
->service_name
);
175 dvb_string_clean( &p_config
->provider_name
);
176 free( p_config
->pi_pids
);
177 free( p_config
->psz_srcaddr
);
180 static void config_Defaults( output_config_t
*p_config
)
182 config_Init( p_config
);
184 p_config
->i_config
= (b_udp_global
? OUTPUT_UDP
: 0) |
185 (b_dvb_global
? OUTPUT_DVB
: 0) |
186 (b_epg_global
? OUTPUT_EPG
: 0);
187 p_config
->i_max_retention
= i_retention_global
;
188 p_config
->i_output_latency
= i_latency_global
;
189 p_config
->i_tsid
= -1;
190 p_config
->i_ttl
= i_ttl_global
;
191 memcpy( p_config
->pi_ssrc
, pi_ssrc_global
, 4 * sizeof(uint8_t) );
192 dvb_string_copy(&p_config
->network_name
, &network_name
);
193 dvb_string_copy(&p_config
->provider_name
, &provider_name
);
196 char *config_stropt( const char *psz_string
)
199 if ( !psz_string
|| strlen( psz_string
) == 0 )
201 ret
= tmp
= strdup( psz_string
);
214 static uint8_t *config_striconv( const char *psz_string
,
215 const char *psz_charset
, size_t *pi_length
)
217 char *psz_input
= config_stropt(psz_string
);
218 *pi_length
= strlen(psz_input
);
220 /* do not convert ASCII strings */
221 const char *c
= psz_string
;
226 return (uint8_t *)psz_input
;
228 if ( !strcasecmp( psz_native_charset
, psz_charset
) )
229 return (uint8_t *)psz_input
;
232 if ( conf_iconv
== (iconv_t
)-1 )
234 conf_iconv
= iconv_open( psz_charset
, psz_native_charset
);
235 if ( conf_iconv
== (iconv_t
)-1 )
236 return (uint8_t *)psz_input
;
239 char *psz_tmp
= psz_input
;
240 size_t i_input
= *pi_length
;
241 size_t i_output
= i_input
* 6;
242 size_t i_available
= i_output
;
243 char *p_output
= malloc( i_output
);
245 if ( iconv( conf_iconv
, &psz_tmp
, &i_input
, &p
, &i_available
) == (size_t)-1 )
249 return (uint8_t *)psz_input
;
253 *pi_length
= i_output
- i_available
;
254 return (uint8_t *)p_output
;
257 "unable to convert from %s to %s (iconv is not available)",
258 psz_native_charset
, psz_charset
);
259 return (uint8_t *)psz_input
;
263 static void config_strdvb( dvb_string_t
*p_dvb_string
, const char *psz_string
,
264 const char *psz_charset
)
266 if (psz_string
== NULL
)
268 dvb_string_init(p_dvb_string
);
271 dvb_string_clean(p_dvb_string
);
274 uint8_t *p_iconv
= config_striconv(psz_string
, psz_charset
, &i_iconv
);
275 p_dvb_string
->p
= dvb_string_set(p_iconv
, i_iconv
, psz_charset
,
280 static bool config_ParseHost( output_config_t
*p_config
, char *psz_string
)
282 struct addrinfo
*p_ai
;
285 p_config
->psz_displayname
= strdup( psz_string
);
287 p_ai
= ParseNodeService( psz_string
, &psz_string
, DEFAULT_PORT
);
288 if ( p_ai
== NULL
) return false;
289 memcpy( &p_config
->connect_addr
, p_ai
->ai_addr
, p_ai
->ai_addrlen
);
290 freeaddrinfo( p_ai
);
292 p_config
->i_family
= p_config
->connect_addr
.ss_family
;
293 if ( p_config
->i_family
== AF_UNSPEC
) return false;
295 if ( psz_string
== NULL
|| !*psz_string
) goto end
;
297 if ( *psz_string
== '@' )
300 p_ai
= ParseNodeService( psz_string
, &psz_string
, 0 );
301 if ( p_ai
== NULL
|| p_ai
->ai_family
!= p_config
->i_family
)
302 msg_Warn( NULL
, "invalid bind address" );
304 memcpy( &p_config
->bind_addr
, p_ai
->ai_addr
, p_ai
->ai_addrlen
);
305 freeaddrinfo( p_ai
);
308 const char *psz_charset
= psz_dvb_charset
;
309 const char *psz_network_name
= NULL
;
310 const char *psz_service_name
= NULL
;
311 const char *psz_provider_name
= NULL
;
313 while ( (psz_string
= strchr( psz_string
, '/' )) != NULL
)
315 *psz_string
++ = '\0';
317 #define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) ))
318 #define ARG_OPTION( option ) (psz_string + strlen(option))
320 if ( IS_OPTION("udp") )
321 p_config
->i_config
|= OUTPUT_UDP
;
322 else if ( IS_OPTION("dvb") )
323 p_config
->i_config
|= OUTPUT_DVB
;
324 else if ( IS_OPTION("epg") )
325 p_config
->i_config
|= OUTPUT_EPG
;
326 else if ( IS_OPTION("tsid=") )
327 p_config
->i_tsid
= strtol( ARG_OPTION("tsid="), NULL
, 0 );
328 else if ( IS_OPTION("retention=") )
329 p_config
->i_max_retention
= strtoll( ARG_OPTION("retention="),
331 else if ( IS_OPTION("latency=") )
332 p_config
->i_output_latency
= strtoll( ARG_OPTION("latency="),
334 else if ( IS_OPTION("ttl=") )
335 p_config
->i_ttl
= strtol( ARG_OPTION("ttl="), NULL
, 0 );
336 else if ( IS_OPTION("tos=") )
337 p_config
->i_tos
= strtol( ARG_OPTION("tos="), NULL
, 0 );
338 else if ( IS_OPTION("mtu=") )
339 p_config
->i_mtu
= strtol( ARG_OPTION("mtu="), NULL
, 0 );
340 else if ( IS_OPTION("ifindex=") )
341 p_config
->i_if_index_v6
= strtol( ARG_OPTION("ifindex="), NULL
, 0 );
342 else if ( IS_OPTION("networkid=") )
343 p_config
->i_network_id
= strtol( ARG_OPTION("networkid="), NULL
, 0 );
344 else if ( IS_OPTION("onid=") )
345 p_config
->i_onid
= strtol( ARG_OPTION("onid="), NULL
, 0 );
346 else if ( IS_OPTION("charset=") )
347 psz_charset
= ARG_OPTION("charset=");
348 else if ( IS_OPTION("networkname=") )
349 psz_network_name
= ARG_OPTION("networkname=");
350 else if ( IS_OPTION("srvname=") )
351 psz_service_name
= ARG_OPTION("srvname=");
352 else if ( IS_OPTION("srvprovider=") )
353 psz_provider_name
= ARG_OPTION("srvprovider=");
354 else if ( IS_OPTION("srcaddr=") )
356 if ( p_config
->i_family
!= AF_INET
) {
357 msg_Err( NULL
, "RAW sockets currently implemented for ipv4 only");
360 free( p_config
->psz_srcaddr
);
361 p_config
->psz_srcaddr
= config_stropt( ARG_OPTION("srcaddr=") );
362 p_config
->i_config
|= OUTPUT_RAW
;
364 else if ( IS_OPTION("srcport=") )
365 p_config
->i_srcport
= strtol( ARG_OPTION("srcport="), NULL
, 0 );
366 else if ( IS_OPTION("ssrc=") )
368 in_addr_t i_addr
= inet_addr( ARG_OPTION("ssrc=") );
369 memcpy( p_config
->pi_ssrc
, &i_addr
, 4 * sizeof(uint8_t) );
371 else if ( IS_OPTION("pidmap=") )
374 char *saveptr
= NULL
;
377 for (i
= 0, str1
= config_stropt( (ARG_OPTION("pidmap="))); i
< N_MAP_PIDS
; i
++, str1
= NULL
)
379 tok
= strtok_r(str1
, ",", &saveptr
);
382 i_newpid
= strtoul(tok
, NULL
, 0);
383 p_config
->pi_confpids
[i
] = i_newpid
;
385 p_config
->b_do_remap
= true;
387 else if ( IS_OPTION("newsid=") )
388 p_config
->i_new_sid
= strtol( ARG_OPTION("newsid="), NULL
, 0 );
390 msg_Warn( NULL
, "unrecognized option %s", psz_string
);
396 if (psz_network_name
!= NULL
)
397 config_strdvb( &p_config
->network_name
, psz_network_name
, psz_charset
);
398 if (psz_service_name
!= NULL
)
399 config_strdvb( &p_config
->service_name
, psz_service_name
, psz_charset
);
400 if (psz_provider_name
!= NULL
)
401 config_strdvb( &p_config
->provider_name
, psz_provider_name
,
405 i_mtu
= p_config
->i_family
== AF_INET6
? DEFAULT_IPV6_MTU
:
408 if ( !p_config
->i_mtu
)
409 p_config
->i_mtu
= i_mtu
;
410 else if ( p_config
->i_mtu
< TS_SIZE
+ RTP_HEADER_SIZE
)
412 msg_Warn( NULL
, "invalid MTU %d, setting %d", p_config
->i_mtu
, i_mtu
);
413 p_config
->i_mtu
= i_mtu
;
419 static void config_Print( output_config_t
*p_config
)
421 if ( p_config
->b_passthrough
)
423 msg_Dbg( NULL
, "conf: %s config=0x%"PRIx64
" sid=*",
424 p_config
->psz_displayname
, p_config
->i_config
);
428 const char *psz_base
= "conf: %s config=0x%"PRIx64
" sid=%hu pids[%d]=";
429 size_t i_len
= strlen(psz_base
) + 6 * p_config
->i_nb_pids
+ 1;
430 char psz_format
[i_len
];
431 int i
, j
= strlen(psz_base
);
433 strcpy( psz_format
, psz_base
);
434 for ( i
= 0; i
< p_config
->i_nb_pids
; i
++ )
435 j
+= sprintf( psz_format
+ j
, "%u,", p_config
->pi_pids
[i
] );
436 psz_format
[j
- 1] = '\0';
438 msg_Dbg( NULL
, psz_format
, p_config
->psz_displayname
, p_config
->i_config
,
439 p_config
->i_sid
, p_config
->i_nb_pids
);
442 void config_ReadFile(void)
448 if ( psz_conf_file
== NULL
)
450 msg_Err( NULL
, "no config file" );
454 if ( (p_file
= fopen( psz_conf_file
, "r" )) == NULL
)
456 msg_Err( NULL
, "can't fopen config file %s", psz_conf_file
);
460 while ( fgets( psz_line
, sizeof(psz_line
), p_file
) != NULL
)
462 output_config_t config
;
464 char *psz_token
, *psz_parser
;
466 psz_parser
= strchr( psz_line
, '#' );
467 if ( psz_parser
!= NULL
)
468 *psz_parser
-- = '\0';
469 while ( psz_parser
>= psz_line
&& isblank( *psz_parser
) )
470 *psz_parser
-- = '\0';
471 if ( psz_line
[0] == '\0' )
474 config_Defaults( &config
);
476 psz_token
= strtok_r( psz_line
, "\t\n ", &psz_parser
);
477 if ( psz_token
== NULL
|| !config_ParseHost( &config
, psz_token
))
479 config_Free( &config
);
483 psz_token
= strtok_r( NULL
, "\t\n ", &psz_parser
);
484 if ( psz_token
== NULL
)
486 config_Free( &config
);
489 if( atoi( psz_token
) == 1 )
490 config
.i_config
|= OUTPUT_WATCH
;
492 config
.i_config
&= ~OUTPUT_WATCH
;
494 psz_token
= strtok_r( NULL
, "\t\n ", &psz_parser
);
495 if ( psz_token
== NULL
)
497 config_Free( &config
);
501 if ( psz_token
[0] == '*' )
503 config
.b_passthrough
= true;
507 config
.i_sid
= strtol(psz_token
, NULL
, 0);
509 psz_token
= strtok_r( NULL
, "\t\n ", &psz_parser
);
510 if ( psz_token
!= NULL
)
515 psz_token
= strtok_r( psz_token
, ",", &psz_parser
);
516 if ( psz_token
== NULL
)
518 config
.pi_pids
= realloc( config
.pi_pids
,
519 (config
.i_nb_pids
+ 1) * sizeof(uint16_t) );
520 config
.pi_pids
[config
.i_nb_pids
++] = strtol(psz_token
, NULL
, 0);
526 config_Print( &config
);
528 p_output
= output_Find( &config
);
530 if ( p_output
== NULL
)
531 p_output
= output_Create( &config
);
533 if ( p_output
!= NULL
)
535 free( p_output
->config
.psz_displayname
);
536 p_output
->config
.psz_displayname
= strdup( config
.psz_displayname
);
538 config
.i_config
|= OUTPUT_VALID
| OUTPUT_STILL_PRESENT
;
539 output_Change( p_output
, &config
);
540 demux_Change( p_output
, &config
);
543 config_Free( &config
);
548 for ( i
= 0; i
< i_nb_outputs
; i
++ )
550 output_t
*p_output
= pp_outputs
[i
];
551 output_config_t config
;
553 config_Init( &config
);
555 if ( (p_output
->config
.i_config
& OUTPUT_VALID
) &&
556 !(p_output
->config
.i_config
& OUTPUT_STILL_PRESENT
) )
558 msg_Dbg( NULL
, "closing %s", p_output
->config
.psz_displayname
);
559 demux_Change( p_output
, &config
);
560 output_Close( p_output
);
563 p_output
->config
.i_config
&= ~OUTPUT_STILL_PRESENT
;
564 config_Free( &config
);
568 /*****************************************************************************
570 *****************************************************************************/
571 static void signal_watcher_init(struct ev_signal
*w
, struct ev_loop
*loop
,
572 void (*cb
)(struct ev_loop
*, struct ev_signal
*, int),
575 ev_signal_init(w
, cb
, signum
);
576 ev_signal_start(loop
, w
);
580 static void sighandler(struct ev_loop
*loop
, struct ev_signal
*w
, int revents
)
587 msg_Info( NULL
, "Shutdown was requested." );
588 ev_break(loop
, EVBREAK_ALL
);
592 msg_Info( NULL
, "Configuration reload was requested." );
598 /*****************************************************************************
600 *****************************************************************************/
601 static void quit_cb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
)
603 ev_break(loop
, EVBREAK_ALL
);
606 /*****************************************************************************
608 *****************************************************************************/
609 static void DisplayVersion()
611 msg_Raw( NULL
, "DVBlast %s (%s)", VERSION
, VERSION_EXTRA
);
614 /*****************************************************************************
616 *****************************************************************************/
620 msg_Raw( NULL
, "Usage: dvblast [-q] [-c <config file>] [-r <remote socket>] [-t <ttl>] [-o <SSRC IP>] "
621 "[-i <RT priority>] "
622 #ifdef HAVE_ASI_SUPPORT
625 #ifdef HAVE_DVB_SUPPORT
626 "[-a <adapter>] [-n <frontend number>] [-S <diseqc>] [-k <uncommitted port>]"
628 "[-s <symbol rate>] [-v <0|13|18>] [-p] [-b <bandwidth>] [-I <inversion>] "
629 "[-F <fec inner>] [-m <modulation] [-R <rolloff>] [-P <pilot>] [-K <fec lp>] "
630 "[-G <guard interval>] [-H <hierarchy>] [-X <transmission>] [-O <lock timeout>] "
632 "[-D [<src host>[:<src port>]@]<src mcast>[:<port>][/<opts>]*] "
633 "[-u] [-w] [-U] [-L <latency>] [-E <retention>] [-d <dest IP>[<:port>][/<opts>]*] [-3] "
634 "[-z] [-C [-e] [-M <network name>] [-N <network ID>]] [-T] [-j <system charset>] "
635 "[-W] [-Y] [-l] [-g <logger ident>] [-Z <mrtg file>] [-V] [-h] [-B <provider_name>] "
636 "[-1 <mis_id>] [-2 <size>] [-5 <DVBS|DVBS2|DVBC_ANNEX_A|DVBC_ANNEX_B|DVBT|DVBT2|ATSC|ISDBT>] -y <ca_dev_number> "
637 "[-J <DVB charset>] [-Q <quit timeout>] [-0 pid_mapping] [-x <text|xml>]"
638 "[-6 <print period>] [-7 <ES timeout>] [-4 <UDP lock timeout>]" );
640 msg_Raw( NULL
, "Input:" );
641 #ifdef HAVE_ASI_SUPPORT
642 msg_Raw( NULL
, " -A --asi-adapter read packets from an ASI adapter (0-n)" );
644 #ifdef HAVE_DVB_SUPPORT
645 msg_Raw( NULL
, " -a --adapter read packets from a Linux-DVB adapter (typically 0-n)" );
646 msg_Raw( NULL
, " -b --bandwidth frontend bandwidth" );
648 msg_Raw( NULL
, " -D --rtp-input read packets from a multicast address instead of a DVB card" );
649 #ifdef HAVE_DVB_SUPPORT
650 msg_Raw( NULL
, " -5 --delsys delivery system" );
651 msg_Raw( NULL
, " DVBS|DVBS2|DVBC_ANNEX_A|DVBT|DVBT2|ATSC|ISDBT|DVBC_ANNEX_B(ATSC-C/QAMB) (default guessed)");
652 msg_Raw( NULL
, " -f --frequency frontend frequency" );
653 msg_Raw( NULL
, " -8 --lnb-type <type> Set LNB type')" );
654 msg_Raw( NULL
, " universal old-sky (default: universal)");
655 msg_Raw( NULL
, " -9 --dvb-plp-id <number> Switch PLP of the DVB-T2 transmission (default: 0)" );
656 msg_Raw( NULL
, " -F --fec-inner Forward Error Correction (FEC Inner)");
657 msg_Raw( NULL
, " DVB-S2 0|12|23|34|35|56|78|89|910|999 (default auto: 999)");
658 msg_Raw( NULL
, " -I --inversion Inversion (-1 auto, 0 off, 1 on)" );
659 msg_Raw( NULL
, " -m --modulation Modulation type" );
660 msg_Raw( NULL
, " DVB-C qpsk|qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" );
661 msg_Raw( NULL
, " DVB-T qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" );
662 msg_Raw( NULL
, " DVB-S2 qpsk|psk_8|apsk_16|apsk_32 (default legacy DVB-S)" );
663 msg_Raw( NULL
, " -n --frontend-number <frontend number>" );
664 msg_Raw( NULL
, " -p --force-pulse force 22kHz pulses for high-band selection (DVB-S)" );
665 msg_Raw( NULL
, " -P --pilot DVB-S2 Pilot (-1 auto, 0 off, 1 on)" );
666 msg_Raw( NULL
, " -R --rolloff DVB-S2 Rolloff value" );
667 msg_Raw( NULL
, " DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" );
668 msg_Raw( NULL
, " -1 --multistream-id Set stream ID (0-2147483648, default: 0)." );
669 msg_Raw( NULL
, " --multistream-id-pls-mode Set multistream PLS mode (ROOT, GOLD, COMBO, default: ROOT)" );
670 msg_Raw( NULL
, " --multistream-id-pls-code Set multistream PLS code (0-262143, default: 0)" );
671 msg_Raw( NULL
, " --multistream-id-is-id Set multistream IS id (0-255, default: 0)" );
672 msg_Raw( NULL
, " -K --fec-lp DVB-T low priority FEC (default auto)" );
673 msg_Raw( NULL
, " -G --guard DVB-T guard interval" );
674 msg_Raw( NULL
, " DVB-T 32 (1/32)|16 (1/16)|8 (1/8)|4 (1/4)|-1 (auto, default)" );
675 msg_Raw( NULL
, " -H --hierarchy DVB-T hierarchy (0, 1, 2, 4 or -1 auto, default)" );
676 msg_Raw( NULL
, " -X --transmission DVB-T transmission (2, 4, 8 or -1 auto, default)" );
677 msg_Raw( NULL
, " -s --symbol-rate" );
678 msg_Raw( NULL
, " -S --diseqc satellite number for diseqc (0: no diseqc, 1-4, A or B)" );
679 msg_Raw( NULL
, " -k --uncommitted port number for uncommitted DiSEqC switch (0: no uncommitted DiSEqC switch, 1-16)" );
680 msg_Raw( NULL
, " -u --budget-mode turn on budget mode (no hardware PID filtering)" );
681 msg_Raw( NULL
, " -v --voltage voltage to apply to the LNB (QPSK)" );
682 msg_Raw( NULL
, " -w --select-pmts set a PID filter on all PMTs (auto on, when config file is used)" );
683 msg_Raw( NULL
, " -O --lock-timeout timeout for the lock operation (in ms)" );
684 msg_Raw( NULL
, " -y --ca-number <ca_device_number>" );
685 msg_Raw( NULL
, " -2 --dvr-buf-size <size> set the size of the DVR TS buffer in bytes (default: %d)", i_dvr_buffer_size
);
688 msg_Raw( NULL
, "Output:" );
689 msg_Raw( NULL
, " -c --config-file <config file>" );
690 msg_Raw( NULL
, " -C --dvb-compliance pass through or build the mandatory DVB tables" );
691 msg_Raw( NULL
, " -d --duplicate duplicate all received packets to a given destination" );
692 msg_Raw( NULL
, " -3 --passthrough duplicate all received packets to stdout" );
693 msg_Raw( NULL
, " -W --emm-passthrough pass through EMM data (CA system data)" );
694 msg_Raw( NULL
, " -Y --ecm-passthrough pass through ECM data (CA program data)" );
695 msg_Raw( NULL
, " -e --epg-passthrough pass through DVB EIT schedule tables" );
696 msg_Raw( NULL
, " -E --retention maximum retention allowed between input and output (default: 40 ms)" );
697 msg_Raw( NULL
, " -L --latency maximum latency allowed between input and output (default: 100 ms)" );
698 msg_Raw( NULL
, " -M --network-name DVB network name to declare in the NIT" );
699 msg_Raw( NULL
, " -N --network-id DVB network ID to declare in the NIT" );
700 msg_Raw( NULL
, " -B --provider-name Service provider name to declare in the SDT" );
701 msg_Raw( NULL
, " -o --rtp-output <SSRC IP>" );
702 msg_Raw( NULL
, " -t --ttl <ttl> TTL of the output stream" );
703 msg_Raw( NULL
, " -T --unique-ts-id generate random unique TS ID for each output" );
704 msg_Raw( NULL
, " -U --udp use raw UDP rather than RTP (required by some IPTV set top boxes)" );
705 msg_Raw( NULL
, " -z --any-type pass through all ESs from the PMT, of any type" );
706 msg_Raw( NULL
, " -0 --pidmap <pmt_pid,audio_pid,video_pid,spu_pid>");
708 msg_Raw( NULL
, "Misc:" );
709 msg_Raw( NULL
, " -h --help display this full help" );
710 msg_Raw( NULL
, " -i --priority <RT priority>" );
711 msg_Raw( NULL
, " -j --system-charset character set used for printing messages (default UTF-8//IGNORE)" );
712 msg_Raw( NULL
, " -J --dvb-charset character set used in output DVB tables (default UTF-8//IGNORE)" );
713 msg_Raw( NULL
, " -l --logger use syslog for logging messages instead of stderr" );
714 msg_Raw( NULL
, " -g --logger-ident program name that will be used in syslog messages" );
715 msg_Raw( NULL
, " -x --print print interesting events on stdout in a given format" );
716 msg_Raw( NULL
, " -q --quiet be quiet (less verbosity, repeat or use number for even quieter)" );
717 msg_Raw( NULL
, " -Q --quit-timeout when locked, quit after this delay (in ms), or after the first lock timeout" );
718 msg_Raw( NULL
, " -6 --print-period periodicity at which we print bitrate and errors (in ms)" );
719 msg_Raw( NULL
, " -7 --es-timeout time of inactivy before which a PID is reported down (in ms)" );
720 msg_Raw( NULL
, " -4 --udp lock-timeout time of inactivy before which a UDP stream is reported down (in ms)" );
721 msg_Raw( NULL
, " -r --remote-socket <remote socket>" );
722 msg_Raw( NULL
, " -Z --mrtg-file <file> Log input packets and errors into mrtg-file" );
723 msg_Raw( NULL
, " -V --version only display the version" );
727 int main( int i_argc
, char **pp_argv
)
729 const char *psz_network_name
= "DVBlast - videolan.org";
730 const char *psz_provider_name
= NULL
;
731 char *psz_dup_config
= NULL
;
732 struct sched_param param
;
735 int b_enable_syslog
= 0;
736 struct ev_signal sigint_watcher
, sigterm_watcher
, sighup_watcher
;
737 struct ev_timer quit_watcher
;
745 * No short options are left.
747 static const struct option long_options
[] =
749 { "config-file", required_argument
, NULL
, 'c' },
750 { "remote-socket", required_argument
, NULL
, 'r' },
751 { "ttl", required_argument
, NULL
, 't' },
752 { "rtp-output", required_argument
, NULL
, 'o' },
753 { "priority", required_argument
, NULL
, 'i' },
754 { "adapter", required_argument
, NULL
, 'a' },
755 { "frontend-number", required_argument
, NULL
, 'n' },
756 { "delsys", required_argument
, NULL
, '5' },
757 { "dvb-plp-id", required_argument
, NULL
, '9' },
758 { "frequency", required_argument
, NULL
, 'f' },
759 { "lnb-type", required_argument
, NULL
, '8' },
760 { "fec-inner", required_argument
, NULL
, 'F' },
761 { "rolloff", required_argument
, NULL
, 'R' },
762 { "symbol-rate", required_argument
, NULL
, 's' },
763 { "diseqc", required_argument
, NULL
, 'S' },
764 { "uncommitted", required_argument
, NULL
, 'k' },
765 { "voltage", required_argument
, NULL
, 'v' },
766 { "force-pulse", no_argument
, NULL
, 'p' },
767 { "bandwidth", required_argument
, NULL
, 'b' },
768 { "inversion", required_argument
, NULL
, 'I' },
769 { "modulation", required_argument
, NULL
, 'm' },
770 { "pilot", required_argument
, NULL
, 'P' },
771 { "multistream-id", required_argument
, NULL
, '1' },
772 { "multistream-id-pls-mode", required_argument
, NULL
, 0x100001 },
773 { "multistream-id-pls-code", required_argument
, NULL
, 0x100002 },
774 { "multistream-id-is-id" , required_argument
, NULL
, 0x100003 },
775 { "fec-lp", required_argument
, NULL
, 'K' },
776 { "guard", required_argument
, NULL
, 'G' },
777 { "hierarchy", required_argument
, NULL
, 'H' },
778 { "transmission", required_argument
, NULL
, 'X' },
779 { "lock-timeout", required_argument
, NULL
, 'O' },
780 { "budget-mode", no_argument
, NULL
, 'u' },
781 { "select-pmts", no_argument
, NULL
, 'w' },
782 { "udp", no_argument
, NULL
, 'U' },
783 { "unique-ts-id", no_argument
, NULL
, 'T' },
784 { "latency", required_argument
, NULL
, 'L' },
785 { "retention", required_argument
, NULL
, 'E' },
786 { "duplicate", required_argument
, NULL
, 'd' },
787 { "passthrough", no_argument
, NULL
, '3' },
788 { "rtp-input", required_argument
, NULL
, 'D' },
789 { "asi-adapter", required_argument
, NULL
, 'A' },
790 { "any-type", no_argument
, NULL
, 'z' },
791 { "dvb-compliance", no_argument
, NULL
, 'C' },
792 { "emm-passthrough", no_argument
, NULL
, 'W' },
793 { "ecm-passthrough", no_argument
, NULL
, 'Y' },
794 { "epg-passthrough", no_argument
, NULL
, 'e' },
795 { "network-name", no_argument
, NULL
, 'M' },
796 { "network-id", no_argument
, NULL
, 'N' },
797 { "system-charset", required_argument
, NULL
, 'j' },
798 { "dvb-charset", required_argument
, NULL
, 'J' },
799 { "provider-name", required_argument
, NULL
, 'B' },
800 { "logger", no_argument
, NULL
, 'l' },
801 { "logger-ident", required_argument
, NULL
, 'g' },
802 { "print", required_argument
, NULL
, 'x' },
803 { "quit-timeout", required_argument
, NULL
, 'Q' },
804 { "print-period", required_argument
, NULL
, '6' },
805 { "es-timeout", required_argument
, NULL
, '7' },
806 { "udp-lock-timeout", required_argument
, NULL
, '4' },
807 { "quiet", no_argument
, NULL
, 'q' },
808 { "help", no_argument
, NULL
, 'h' },
809 { "version", no_argument
, NULL
, 'V' },
810 { "mrtg-file", required_argument
, NULL
, 'Z' },
811 { "ca-number", required_argument
, NULL
, 'y' },
812 { "pidmap", required_argument
, NULL
, '0' },
813 { "dvr-buf-size", required_argument
, NULL
, '2' },
817 while ( (c
= getopt_long(i_argc
, pp_argv
, "q::c:r:t:o:i:a:n:5:f:F:R:s:S:k:v:pb:I:m:P:K:G:H:X:O:uwUTL:E:d:3D:A:lg:zCWYeM:N:j:J:B:x:Q:6:7:4:hVZ:y:0:1:2:9:", long_options
, NULL
)) != -1 )
824 if ( *optarg
== 'q' ) /* e.g. -qqq */
827 while ( *optarg
== 'q' )
835 i_verbose
-= atoi( optarg
); /* e.g. -q2 */
840 i_verbose
--; /* -q */
845 psz_conf_file
= optarg
;
847 * When configuration file is used it is reasonable to assume that
848 * services may be added/removed. If b_select_pmts is not set dvblast
849 * is unable to start streaming newly added services in the config.
855 psz_srv_socket
= optarg
;
859 i_ttl_global
= strtol( optarg
, NULL
, 0 );
864 struct in_addr maddr
;
865 if ( !inet_aton( optarg
, &maddr
) )
867 memcpy( pi_ssrc_global
, &maddr
.s_addr
, 4 * sizeof(uint8_t) );
872 i_priority
= strtol( optarg
, NULL
, 0 );
876 i_adapter
= strtol( optarg
, NULL
, 0 );
880 i_fenum
= strtol( optarg
, NULL
, 0 );
884 i_canum
= strtol( optarg
, NULL
, 0 );
891 dvb_plp_id
= strtol( optarg
, NULL
, 0 );
894 if (optarg
&& optarg
[0] != '-')
895 i_frequency
= strtol( optarg
, NULL
, 0 );
896 if ( pf_Open
!= NULL
)
898 #ifdef HAVE_DVB_SUPPORT
900 pf_Reset
= dvb_Reset
;
901 pf_SetFilter
= dvb_SetFilter
;
902 pf_UnsetFilter
= dvb_UnsetFilter
;
904 msg_Err( NULL
, "DVBlast is compiled without DVB support.");
910 psz_lnb_type
= optarg
;
914 i_fec
= strtol( optarg
, NULL
, 0 );
918 i_rolloff
= strtol( optarg
, NULL
, 0 );
922 i_srate
= strtol( optarg
, NULL
, 0 );
926 i_satnum
= strtol( optarg
, NULL
, 16 );
930 i_uncommitted
= strtol( optarg
, NULL
, 10 );
934 i_voltage
= strtol( optarg
, NULL
, 0 );
942 i_bandwidth
= strtol( optarg
, NULL
, 0 );
946 i_inversion
= strtol( optarg
, NULL
, 0 );
950 psz_modulation
= optarg
;
954 i_pilot
= strtol( optarg
, NULL
, 0 );
958 i_mis
= strtol( optarg
, NULL
, 0 );
961 case 0x100001: // --multistream-id-pls-mode
962 psz_mis_pls_mode
= optarg
;
963 if ( streq( psz_mis_pls_mode
, "ROOT" ) ) {
965 } else if ( streq( psz_mis_pls_mode
, "GOLD" ) ) {
967 } else if ( streq( psz_mis_pls_mode
, "COMBO" ) ) {
970 msg_Err(NULL
, "Invalid --multistream-id-pls-mode '%s', valid options are: ROOT GOLD COMBO", optarg
);
975 case 0x100002: // --multistream-id-pls-code
976 i_mis_pls_code
= strtol( optarg
, NULL
, 0 );
977 if ( i_mis_pls_code
< 0 || i_mis_pls_code
> 262143 ) {
978 msg_Err(NULL
, "ERROR: Invalid --multistream-id-pls-code '%s', valid options are: 0-262143", optarg
);
983 case 0x100003: // --multistream-id-is-id
984 i_mis_is_id
= strtol( optarg
, NULL
, 0 );
985 if ( i_mis_is_id
< 0 || i_mis_is_id
> 255 ) {
986 msg_Err(NULL
, "ERROR: Invalid --multistream-id-is-id '%s', valid options are: 0-255", optarg
);
992 i_fec_lp
= strtol( optarg
, NULL
, 0 );
996 i_guard
= strtol( optarg
, NULL
, 0 );
1000 i_transmission
= strtol( optarg
, NULL
, 0 );
1004 i_frontend_timeout_duration
= strtoll( optarg
, NULL
, 0 ) * 1000;
1008 i_hierarchy
= strtol( optarg
, NULL
, 0 );
1016 b_select_pmts
= !b_select_pmts
;
1020 b_udp_global
= true;
1024 i_latency_global
= strtoll( optarg
, NULL
, 0 ) * 1000;
1028 i_retention_global
= strtoll( optarg
, NULL
, 0 ) * 1000;
1032 psz_dup_config
= optarg
;
1036 b_passthrough
= true;
1041 psz_udp_src
= optarg
;
1042 if ( pf_Open
!= NULL
)
1045 pf_Reset
= udp_Reset
;
1046 pf_SetFilter
= udp_SetFilter
;
1047 pf_UnsetFilter
= udp_UnsetFilter
;
1051 #ifdef HAVE_ASI_SUPPORT
1052 if ( pf_Open
!= NULL
)
1054 if ( strncmp(optarg
, "deltacast:", 10) == 0)
1056 #ifdef HAVE_ASI_DELTACAST_SUPPORT
1057 i_asi_adapter
= strtol( optarg
+10, NULL
, 0 );
1058 pf_Open
= asi_deltacast_Open
;
1059 pf_Reset
= asi_deltacast_Reset
;
1060 pf_SetFilter
= asi_deltacast_SetFilter
;
1061 pf_UnsetFilter
= asi_deltacast_UnsetFilter
;
1063 msg_Err( NULL
, "DVBlast is compiled without Deltacast ASI support.");
1069 i_asi_adapter
= strtol( optarg
, NULL
, 0 );
1071 pf_Reset
= asi_Reset
;
1072 pf_SetFilter
= asi_SetFilter
;
1073 pf_UnsetFilter
= asi_UnsetFilter
;
1076 msg_Err( NULL
, "DVBlast is compiled without ASI support.");
1086 b_dvb_global
= true;
1090 b_enable_emm
= true;
1094 b_enable_ecm
= true;
1098 b_epg_global
= true;
1102 psz_network_name
= optarg
;
1106 i_network_id
= strtoul( optarg
, NULL
, 0 );
1114 psz_native_charset
= optarg
;
1118 psz_dvb_charset
= optarg
;
1122 psz_provider_name
= optarg
;
1126 b_enable_syslog
= 1;
1130 psz_syslog_ident
= optarg
;
1134 b_print_enabled
= true;
1135 if ( !strcmp(optarg
, "text") )
1136 i_print_type
= PRINT_TEXT
;
1137 else if ( !strcmp(optarg
, "xml") )
1138 i_print_type
= PRINT_XML
;
1141 b_print_enabled
= false;
1142 msg_Warn( NULL
, "unrecognized print type %s", optarg
);
1147 i_quit_timeout_duration
= strtoll( optarg
, NULL
, 0 ) * 1000;
1151 i_print_period
= strtoll( optarg
, NULL
, 0 ) * 1000;
1155 i_es_timeout
= strtoll( optarg
, NULL
, 0 ) * 1000;
1159 i_udp_lock_timeout
= strtoll( optarg
, NULL
, 0 ) * 1000;
1168 psz_mrtg_file
= optarg
;
1172 /* We expect a comma separated list of numbers.
1173 Put them into the pi_newpids array as they appear */
1175 char *saveptr
= NULL
;
1178 for (i
= 0, str1
= optarg
; i
< N_MAP_PIDS
; i
++, str1
= NULL
)
1180 tok
= strtok_r(str1
, ",", &saveptr
);
1183 i_newpid
= strtoul(tok
, NULL
, 0);
1185 msg_Err( NULL
, "Invalid pidmap string" );
1188 pi_newpids
[i
] = i_newpid
;
1193 #ifdef HAVE_DVB_SUPPORT
1195 i_dvr_buffer_size
= strtol( optarg
, NULL
, 0 );
1196 if (!i_dvr_buffer_size
)
1197 usage(); // it exits
1198 /* roundup to packet size */
1199 i_dvr_buffer_size
+= TS_SIZE
- 1;
1200 i_dvr_buffer_size
/= TS_SIZE
;
1201 i_dvr_buffer_size
*= TS_SIZE
;
1209 if ( optind
< i_argc
|| pf_Open
== NULL
)
1212 if ( b_enable_syslog
)
1213 msg_Connect( psz_syslog_ident
? psz_syslog_ident
: pp_argv
[0] );
1215 if ( b_print_enabled
)
1217 /* Make std* line-buffered */
1218 setvbuf(print_fh
, NULL
, _IOLBF
, 0);
1224 msg_Warn( NULL
, "restarting" );
1226 switch (i_print_type
)
1229 fprintf(print_fh
, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1230 fprintf(print_fh
, "<TS>\n");
1238 msg_Warn( NULL
, "raw UDP output is deprecated. Please consider using RTP." );
1239 msg_Warn( NULL
, "for DVB-IP compliance you should use RTP." );
1242 if ( b_epg_global
&& !b_dvb_global
)
1244 msg_Dbg( NULL
, "turning on DVB compliance, required by EPG information" );
1245 b_dvb_global
= true;
1248 if ((event_loop
= ev_default_loop(0)) == NULL
)
1250 msg_Err( NULL
, "unable to initialize libev" );
1254 memset( &output_dup
, 0, sizeof(output_dup
) );
1255 if ( psz_dup_config
!= NULL
)
1257 output_config_t config
;
1259 config_Defaults( &config
);
1260 if ( !config_ParseHost( &config
, psz_dup_config
) )
1261 msg_Err( NULL
, "Invalid target address for -d switch" );
1264 output_Init( &output_dup
, &config
);
1265 output_Change( &output_dup
, &config
);
1268 config_Free( &config
);
1271 config_strdvb( &network_name
, psz_network_name
, psz_dvb_charset
);
1272 config_strdvb( &provider_name
, psz_provider_name
, psz_dvb_charset
);
1274 /* Set signal handlers */
1275 signal_watcher_init(&sigint_watcher
, event_loop
, sighandler
, SIGINT
);
1276 signal_watcher_init(&sigterm_watcher
, event_loop
, sighandler
, SIGTERM
);
1277 signal_watcher_init(&sighup_watcher
, event_loop
, sighandler
, SIGHUP
);
1279 srand( time(NULL
) * getpid() );
1281 if ( i_mis_pls_mode
|| i_mis_pls_code
|| i_mis_is_id
)
1283 i_mis
= calc_multistream_id( i_mis_pls_mode
, i_mis_pls_code
, i_mis_is_id
);
1284 msg_Info( NULL
, "Calculating multistream-id using pls-mode: %s (%d) pls-code: %d is-id: %d. Resulting multistream-id: %d (0x%x)",
1285 psz_mis_pls_mode
, i_mis_pls_mode
, i_mis_pls_code
, i_mis_is_id
, i_mis
, i_mis
);
1289 i_mis_pls_mode
= (i_mis
>> 26) & 0x03;
1290 i_mis_pls_code
= (i_mis
>> 8) & 0x3ffff;
1291 i_mis_is_id
= i_mis
& 0xff;
1293 i_mis_pls_mode
== 0 ? "ROOT" :
1294 i_mis_pls_mode
== 1 ? "GOLD" :
1295 i_mis_pls_mode
== 2 ? "COMBO" : "UNKNOWN";
1297 msg_Info( NULL
, "Calculated multistream pls-mode: %s (%d) pls-code: %d is-id: %d from multistream-id: %d (0x%x)",
1298 psz_mis_pls_mode
, i_mis_pls_mode
, i_mis_pls_code
, i_mis_is_id
, i_mis
, i_mis
);
1303 // init the mrtg logfile
1304 mrtgInit(psz_mrtg_file
);
1306 if ( i_priority
> 0 )
1308 memset( ¶m
, 0, sizeof(struct sched_param
) );
1309 param
.sched_priority
= i_priority
;
1310 if ( (i_error
= pthread_setschedparam( pthread_self(), SCHED_RR
,
1313 msg_Warn( NULL
, "couldn't set thread priority: %s",
1314 strerror(i_error
) );
1320 if ( psz_srv_socket
!= NULL
)
1323 if ( i_quit_timeout_duration
)
1325 ev_timer_init(&quit_watcher
, quit_cb
,
1326 i_quit_timeout_duration
/ 1000000., 0);
1327 ev_timer_start(event_loop
, &quit_watcher
);
1332 ev_run(event_loop
, 0);
1335 outputs_Close( i_nb_outputs
);
1337 dvb_string_clean( &network_name
);
1338 dvb_string_clean( &provider_name
);
1339 if ( conf_iconv
!= (iconv_t
)-1 )
1340 iconv_close( conf_iconv
);
1342 switch (i_print_type
)
1345 fprintf(print_fh
, "</TS>\n");
1351 if ( b_enable_syslog
)
1356 ev_loop_destroy(event_loop
);
1358 return EXIT_SUCCESS
;