dvblast.c 30.2 KB
Newer Older
1 2 3
/*****************************************************************************
 * dvblast.c
 *****************************************************************************
4
 * Copyright (C) 2004, 2008-2010 VideoLAN
5
 * $Id$
6 7
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Andy Gatward <a.j.gatward@reading.ac.uk>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * 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
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
29
#include <inttypes.h>
30
#include <stdbool.h>
31 32
#include <string.h>
#include <pthread.h>
33
#include <netdb.h>
34 35 36 37
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
ivoire's avatar
ivoire committed
38
#include <getopt.h>
39 40

#include "dvblast.h"
41
#include "version.h"
42

43 44 45 46 47 48
#ifdef HAVE_ICONV
#include <iconv.h>
#endif

#include <bitstream/dvb/si.h>

49 50 51
/*****************************************************************************
 * Local declarations
 *****************************************************************************/
52
mtime_t i_wallclock = 0;
53 54
output_t **pp_outputs = NULL;
int i_nb_outputs = 0;
55
output_t output_dup = { 0 };
56 57 58 59 60
static char *psz_conf_file = NULL;
char *psz_srv_socket = NULL;
in_addr_t i_ssrc = 0;
static int i_priority = -1;
int i_adapter = 0;
61
int i_fenum = 0;
62 63
int i_frequency = 0;
int i_srate = 27500000;
64
int i_fec = 999;
65
int i_rolloff = 35;
66
int i_satnum = 0;
67 68 69 70 71
int i_voltage = 13;
int b_tone = 0;
int i_bandwidth = 8;
char *psz_modulation = NULL;
int b_budget_mode = 0;
72
int b_random_tsid = 0;
73
uint16_t i_network_id = 0xffff;
74 75
uint8_t *p_network_name;
size_t i_network_name_size;
76
volatile int b_hup_received = 0;
77
int i_verbose = DEFAULT_VERBOSITY;
jpsaman's avatar
jpsaman committed
78
int i_syslog = 0;
79 80 81
uint16_t i_src_port = DEFAULT_PORT;
in_addr_t i_src_addr = { 0 };
int b_src_rawudp = 0;
82
int i_asi_adapter = 0;
83 84
const char *psz_native_charset = "UTF-8";
const char *psz_dvb_charset = "ISO_8859-1";
85

86 87 88 89 90
static int b_udp_global = 0;
static int b_dvb_global = 0;
static int b_epg_global = 0;
static mtime_t i_latency_global = DEFAULT_OUTPUT_LATENCY;
static mtime_t i_retention_global = DEFAULT_MAX_RETENTION;
91
static int i_ttl_global = 64;
92

93
void (*pf_Open)( void ) = NULL;
94
block_t * (*pf_Read)( mtime_t i_poll_timeout ) = NULL;
95 96
int (*pf_SetFilter)( uint16_t i_pid ) = NULL;
void (*pf_UnsetFilter)( int i_fd, uint16_t i_pid ) = NULL;
97 98 99 100 101 102 103 104 105 106

/*****************************************************************************
 * Configuration files
 *****************************************************************************/
static void ReadConfiguration( char *psz_file )
{
    FILE *p_file;
    char psz_line[2048];
    int i;

107 108 109 110 111 112
    if ( psz_file == NULL )
    {
        msg_Err( NULL, "no config file" );
        return;
    }

113 114 115 116 117 118 119 120 121
    if ( (p_file = fopen( psz_file, "r" )) == NULL )
    {
        msg_Err( NULL, "can't fopen config file %s", psz_file );
        return;
    }

    while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL )
    {
        output_t *p_output = NULL;
122
        char *psz_parser, *psz_token, *psz_token2;
123 124
        struct addrinfo *p_addr;
        struct addrinfo ai_hints;
125
        char sz_port[6];
126
        char *psz_displayname;
127 128 129
        uint16_t i_sid = 0;
        uint16_t *pi_pids = NULL;
        int i_nb_pids = 0;
130 131 132 133 134 135
        uint8_t i_config = (b_udp_global ? OUTPUT_UDP : 0) |
                           (b_dvb_global ? OUTPUT_DVB : 0) |
                           (b_epg_global ? OUTPUT_EPG : 0);
        mtime_t i_retention = i_retention_global;
        mtime_t i_latency = i_latency_global;
        int i_tsid = -1;
136
        int i_ttl = i_ttl_global;
137 138 139 140 141

        snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT );

        if ( !strncmp( psz_line, "#", 1 ) )
            continue;
142 143 144 145 146

        psz_token = strtok_r( psz_line, "\t\n ", &psz_parser );
        if ( psz_token == NULL )
            continue;

147 148
        psz_token2 = psz_token;
        while ( (psz_token2 = strchr( psz_token2, '/' )) != NULL )
149
        {
150 151
            *psz_token2++ = '\0';
            if ( !strncasecmp( psz_token2, "udp", 3 ) )
152
                i_config |= OUTPUT_UDP;
153 154 155 156 157 158 159 160 161 162
            else if ( !strncasecmp( psz_token2, "dvb", 3 ) )
                i_config |= OUTPUT_DVB;
            else if ( !strncasecmp( psz_token2, "epg", 3 ) )
                i_config |= OUTPUT_EPG;
            else if ( !strncasecmp( psz_token2, "tsid=", 5 ) )
                i_tsid = strtol( psz_token2 + 5, NULL, 0 );
            else if ( !strncasecmp( psz_token2, "retention=", 10 ) )
                i_retention = strtoll( psz_token2 + 10, NULL, 0 ) * 1000;
            else if ( !strncasecmp( psz_token2, "latency=", 8 ) )
                i_latency = strtoll( psz_token2 + 8, NULL, 0 ) * 1000;
163 164
            else if ( !strncasecmp( psz_token2, "ttl=", 4 ) )
                i_ttl = strtol( psz_token2 + 4, NULL, 0 );
165 166
            else
                msg_Warn( NULL, "unrecognized option %s", psz_token2 );
167
        }
168 169

        if ( !strncmp( psz_token, "[", 1 ) )
170
        {
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
            if ( (psz_token2 = strchr( psz_token, ']' ) ) == NULL )
              continue;

            char *psz_maddr = malloc( psz_token2 - psz_token );
            memset( psz_maddr, '\0', ( psz_token2 - psz_token ) );
            strncpy( psz_maddr, psz_token + 1, ( psz_token2 - psz_token - 1 ));

            if ( (psz_token2 = strchr( psz_token2, ':' )) != NULL )
            {
                *psz_token2 = '\0';
                snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token2 + 1 ) );
            }

            p_addr = malloc( sizeof( p_addr ) );

            memset( &ai_hints, 0, sizeof( ai_hints ) );
            ai_hints.ai_socktype = SOCK_DGRAM;
            ai_hints.ai_flags    = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
            ai_hints.ai_family   = AF_INET6;
190

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
            int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_addr );
            if ( i_ai != 0 )
            {
                msg_Err( NULL, "Cannot configure output [%s]:%s: %s", psz_maddr,
                         sz_port, gai_strerror( i_ai ) );
                continue;
            }

            psz_displayname = malloc( INET6_ADDRSTRLEN + 8 );
            snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 8 ), "[%s]:%s",
                      psz_maddr, sz_port );

            free( psz_maddr );
        }
        else
        {
            if ( (psz_token2 = strrchr( psz_token, ':' )) != NULL )
            {
                *psz_token2 = '\0';
                snprintf( sz_port, sizeof( sz_port ), "%d",  atoi( psz_token2 + 1 ) );
            }

            p_addr = malloc( sizeof( p_addr ) );

            memset( &ai_hints, 0, sizeof( ai_hints ) );
            ai_hints.ai_socktype = SOCK_DGRAM;
            ai_hints.ai_flags    = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
            ai_hints.ai_family   = AF_INET;

            int i_ai = getaddrinfo( psz_token, sz_port, &ai_hints, &p_addr );
            if ( i_ai != 0 )
            {
                msg_Err( NULL, "Cannot configure output %s:%s: %s", psz_token,
                         sz_port, gai_strerror( i_ai ) );
                continue;
            }

            psz_displayname = malloc( INET_ADDRSTRLEN + 6 );
            snprintf( psz_displayname, ( INET_ADDRSTRLEN + 6 ), "%s:%s",
                      psz_token, sz_port );
231 232 233 234 235
        }

        psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
        if ( psz_token == NULL )
            continue;
236 237
        if( atoi( psz_token ) == 1 )
            i_config |= OUTPUT_WATCH;
238
        else
239
            i_config &= ~OUTPUT_WATCH;
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

        psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
        if ( psz_token == NULL )
            continue;
        i_sid = strtol(psz_token, NULL, 0);

        psz_token = strtok_r( NULL, "\t\n ", &psz_parser );
        if ( psz_token != NULL )
        {
            psz_parser = NULL;
            for ( ; ; )
            {
                psz_token = strtok_r( psz_token, ",", &psz_parser );
                if ( psz_token == NULL )
                    break;
                pi_pids = realloc( pi_pids,
                                   (i_nb_pids + 1) * sizeof(uint16_t) );
                pi_pids[i_nb_pids++] = strtol(psz_token, NULL, 0);
                psz_token = NULL;
            }
        }

262 263 264 265 266 267 268 269
        msg_Dbg( NULL,
            "conf: %s config=0x%x ttl=%d sid=%d pids[%d]=%d,%d,%d,%d,%d...",
            psz_displayname, i_config, i_ttl, i_sid, i_nb_pids,
            i_nb_pids < 1 ? -1 : pi_pids[0],
            i_nb_pids < 2 ? -1 : pi_pids[1],
            i_nb_pids < 3 ? -1 : pi_pids[2],
            i_nb_pids < 4 ? -1 : pi_pids[3],
            i_nb_pids < 5 ? -1 : pi_pids[4] );
270 271 272

        for ( i = 0; i < i_nb_outputs; i++ )
        {
273
            if ( pp_outputs[i]->p_addr->ss_family == AF_INET )
274
            {
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
                struct sockaddr_in *p_esad = (struct sockaddr_in *)pp_outputs[i]->p_addr;
                struct sockaddr_in *p_nsad = (struct sockaddr_in *)p_addr->ai_addr;

                if ( ( p_esad->sin_addr.s_addr == p_nsad->sin_addr.s_addr ) &&
                     ( p_esad->sin_port == p_nsad->sin_port ) )
                {
                    p_output = pp_outputs[i];
                    break;
                }
            }
            else if ( pp_outputs[i]->p_addr->ss_family == AF_INET6 )
            {
                struct sockaddr_in6 *p_esad = (struct sockaddr_in6 *)pp_outputs[i]->p_addr;
                struct sockaddr_in6 *p_nsad = (struct sockaddr_in6 *)p_addr->ai_addr;

                if ( ( p_esad->sin6_addr.s6_addr32[0] == p_nsad->sin6_addr.s6_addr32[0] ) &&
                     ( p_esad->sin6_addr.s6_addr32[1] == p_nsad->sin6_addr.s6_addr32[1] ) &&
                     ( p_esad->sin6_addr.s6_addr32[2] == p_nsad->sin6_addr.s6_addr32[2] ) &&
                     ( p_esad->sin6_addr.s6_addr32[3] == p_nsad->sin6_addr.s6_addr32[3] ) &&
                     ( p_esad->sin6_port == p_nsad->sin6_port ) )
                {
                    p_output = pp_outputs[i];
                    break;
                }
299 300
            }
        }
301

302
        if ( i == i_nb_outputs )
303
            p_output = output_Create( psz_displayname, p_addr );
304 305 306

        if ( p_output != NULL )
        {
307 308 309 310
            p_output->i_config = OUTPUT_VALID | OUTPUT_STILL_PRESENT | i_config;
            p_output->i_output_latency = i_latency;
            p_output->i_max_retention = i_retention;
            demux_Change( p_output, i_tsid, i_sid, pi_pids, i_nb_pids );
311 312
            if ( p_output->i_ttl != i_ttl )
                output_SetTTL( p_output, i_ttl );
313 314
        }

ivoire's avatar
ivoire committed
315
        free( psz_displayname );
316
        free( pi_pids );
317
        freeaddrinfo( p_addr );
318 319 320 321 322 323
    }

    fclose( p_file );

    for ( i = 0; i < i_nb_outputs; i++ )
    {
324 325 326 327
        output_t *p_output = pp_outputs[i];

        if ( (p_output->i_config & OUTPUT_VALID) &&
             !(p_output->i_config & OUTPUT_STILL_PRESENT) )
328
        {
329
            msg_Dbg( NULL, "closing %s", pp_outputs[i]->psz_displayname );
330
            demux_Change( pp_outputs[i], -1, 0, NULL, 0 );
331 332 333
            output_Close( pp_outputs[i] );
        }

334
        pp_outputs[i]->i_config &= ~OUTPUT_STILL_PRESENT;
335 336 337 338 339 340 341 342 343 344 345
    }
}

/*****************************************************************************
 * Signal Handler
 *****************************************************************************/
static void SigHandler( int i_signal )
{
    b_hup_received = 1;
}

346 347 348 349 350 351 352 353 354
/*****************************************************************************
 * Version
 *****************************************************************************/
static void DisplayVersion()
{
    msg_Raw( NULL, "DVBlast %d.%d.%d%s", VERSION_MAJOR, VERSION_MINOR,
                                         VERSION_REVISION, VERSION_EXTRA );
}

355 356 357 358 359
/*****************************************************************************
 * Entry point
 *****************************************************************************/
void usage()
{
360
    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] [-j <system charset>] [-J <DVB charset>]" );
361 362 363 364 365 366 367

    msg_Raw( NULL, "Input:" );
    msg_Raw( NULL, "  -a --adapter <adapter>" );
    msg_Raw( NULL, "  -A --asi-adapter      read packets from an ASI adapter (0-n)" );
    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, "  -f --frequency        frontend frequency" );
368 369
    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)");
370 371 372 373 374 375
    msg_Raw( NULL, "  -m --modulation       Modulation type" );
    msg_Raw( NULL, "    DVB-C  qpsk|qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" );
    msg_Raw( NULL, "    DVB-T  qam_16|qam_32|qam_64|qam_128|qam_256 (default qam_auto)" );
    msg_Raw( NULL, "    DVB-S2 qpsk|psk_8 (default legacy DVB-S)" );
    msg_Raw( NULL, "  -n --frontend-number <frontend number>" );
    msg_Raw( NULL, "  -p --force-pulse      force 22kHz pulses for high-band selection (DVB-S)" );
376 377
    msg_Raw( NULL, "  -R --rolloff          DVB-S2 Rolloff value" );
    msg_Raw( NULL, "    DVB-S2 35=0.35|25=0.25|20=0.20|0=AUTO (default: 35)" );
378 379 380 381 382 383 384
    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, "  -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, "Output:" );
    msg_Raw( NULL, "  -c --config-file <config file>" );
385
    msg_Raw( NULL, "  -C --dvb-compliance   pass through or build the mandatory DVB tables" );
386
    msg_Raw( NULL, "  -d --duplicate        duplicate all received packets to a given destination" );
387 388 389 390 391
    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" );
392 393
    msg_Raw( NULL, "  -o --rtp-output <SSRC IP>" );
    msg_Raw( NULL, "  -t --ttl <ttl>        TTL of the output stream" );
394
    msg_Raw( NULL, "  -T --unique-ts-id     generate random unique TS ID for each output" );
ivoire's avatar
ivoire committed
395
    msg_Raw( NULL, "  -U --udp              use raw UDP rather than RTP (required by some IPTV set top boxes)" );
396 397 398

    msg_Raw( NULL, "Misc:" );
    msg_Raw( NULL, "  -h --help             display this full help" );
399 400 401 402
    msg_Raw( NULL, "  -i --priority <RT priority>" );
    msg_Raw( NULL, "  -j --system-charset   character set used for printing messages (default UTF-8)" );
    msg_Raw( NULL, "  -J --dvb-charset      character set used in output DVB tables (default ISO_8859-1)" );
    msg_Raw( NULL, "  -l --logger           use syslog for logging messages instead of stderr" );
403 404 405
    msg_Raw( NULL, "  -q                    be quiet (less verbosity, repeat or use number for even quieter)" );
    msg_Raw( NULL, "  -r --remote-socket <remote socket>" );
    msg_Raw( NULL, "  -V --version          only display the version" );
406 407 408 409 410
    exit(1);
}

int main( int i_argc, char **pp_argv )
{
411 412 413
    const char *psz_network_name = "DVBlast - http://www.videolan.org/projects/dvblast.html";
    char *p_network_name_tmp = NULL;
    size_t i_network_name_tmp_size;
414
    mtime_t i_poll_timeout = MAX_POLL_TIMEOUT;
415 416
    struct sched_param param;
    int i_error;
417
    int c;
418

jpsaman's avatar
jpsaman committed
419 420
    int b_enable_syslog = 0;

421 422
    DisplayVersion();

423 424 425
    if ( i_argc == 1 )
        usage();

ivoire's avatar
ivoire committed
426 427 428 429 430 431 432 433 434 435
    static const struct option long_options[] =
    {
        { "config-file",     required_argument, NULL, 'c' },
        { "remote-socket",   required_argument, NULL, 'r' },
        { "ttl",             required_argument, NULL, 't' },
        { "rtp-output",      required_argument, NULL, 'o' },
        { "priority",        required_argument, NULL, 'i' },
        { "adapter",         required_argument, NULL, 'a' },
        { "frontend-number", required_argument, NULL, 'n' },
        { "frequency",       required_argument, NULL, 'f' },
436
        { "fec-inner",       required_argument, NULL, 'F' },
437
        { "rolloff",         required_argument, NULL, 'R' },
ivoire's avatar
ivoire committed
438 439 440 441
        { "symbol-rate",     required_argument, NULL, 's' },
        { "diseqc",          required_argument, NULL, 'S' },
        { "voltage",         required_argument, NULL, 'v' },
        { "force-pulse",     no_argument,       NULL, 'p' },
ivoire's avatar
ivoire committed
442
        { "bandwidth",       required_argument, NULL, 'b' },
ivoire's avatar
ivoire committed
443 444
        { "modulation",      required_argument, NULL, 'm' },
        { "budget-mode",     no_argument,       NULL, 'u' },
ivoire's avatar
ivoire committed
445
        { "udp",             no_argument,       NULL, 'U' },
ivoire's avatar
ivoire committed
446
        { "unique-ts-id",    no_argument,       NULL, 'T' },
447 448
        { "latency",         required_argument, NULL, 'L' },
        { "retention",       required_argument, NULL, 'E' },
ivoire's avatar
ivoire committed
449 450 451
        { "duplicate",       required_argument, NULL, 'd' },
        { "rtp-input",       required_argument, NULL, 'D' },
        { "asi-adapter",     required_argument, NULL, 'A' },
452
        { "dvb-compliance",  no_argument,       NULL, 'C' },
ivoire's avatar
ivoire committed
453
        { "epg-passthrough", no_argument,       NULL, 'e' },
454 455
        { "network-name",    no_argument,       NULL, 'M' },
        { "network-id",      no_argument,       NULL, 'N' },
456 457
        { "system-charset",  required_argument, NULL, 'j' },
        { "dvb-charset",     required_argument, NULL, 'J' },
jpsaman's avatar
jpsaman committed
458
        { "logger",          no_argument,       NULL, 'l' },
ivoire's avatar
ivoire committed
459 460
        { "help",            no_argument,       NULL, 'h' },
        { "version",         no_argument,       NULL, 'V' },
461
        { 0, 0, 0, 0 }
462
    };
ivoire's avatar
ivoire committed
463

464
    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:j:J:hV", long_options, NULL)) != -1 )
465 466 467
    {
        switch ( c )
        {
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
        case 'q':
            if ( optarg )
            {
                if ( *optarg == 'q' )  /* e.g. -qqq */
                {
                    i_verbose--;
                    while ( *optarg == 'q' )
                    {
                        i_verbose--;
                        optarg++;
                    }
                }
                else
                {
                    i_verbose -= atoi( optarg );  /* e.g. -q2 */
                }
            }
            else
            {
                i_verbose--;  /* -q */
            }
            break;

491 492 493 494 495
        case 'c':
            psz_conf_file = optarg;
            break;

        case 'r':
496 497 498 499 500
            if ( pf_Open != dvb_Open && pf_Open != NULL )
            {
                msg_Err( NULL, "-r is only available for linux-dvb input" );
                usage();
            }
501 502 503 504
            psz_srv_socket = optarg;
            break;

        case 't':
505
            i_ttl_global = strtol( optarg, NULL, 0 );
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
            break;

        case 'o':
        {
            struct in_addr maddr;
            if ( !inet_aton( optarg, &maddr ) )
                usage();
            i_ssrc = maddr.s_addr;
            break;
        }

        case 'i':
            i_priority = strtol( optarg, NULL, 0 );
            break;

        case 'a':
            i_adapter = strtol( optarg, NULL, 0 );
            break;

525 526 527 528
        case 'n':
            i_fenum = strtol( optarg, NULL, 0 );
            break;

529 530
        case 'f':
            i_frequency = strtol( optarg, NULL, 0 );
531 532 533 534 535 536
            if ( pf_Open != NULL )
                usage();
            pf_Open = dvb_Open;
            pf_Read = dvb_Read;
            pf_SetFilter = dvb_SetFilter;
            pf_UnsetFilter = dvb_UnsetFilter;
537 538
            break;

539 540 541 542
        case 'F':
            i_fec = strtol( optarg, NULL, 0 );
            break;

543 544 545 546
        case 'R':
            i_rolloff = strtol( optarg, NULL, 0 );
            break;

547 548 549 550
        case 's':
            i_srate = strtol( optarg, NULL, 0 );
            break;

551 552 553 554
        case 'S':
            i_satnum = strtol( optarg, NULL, 16 );
            break;

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
        case 'v':
            i_voltage = strtol( optarg, NULL, 0 );
            break;

        case 'p':
            b_tone = 1;
            break;

        case 'b':
            i_bandwidth = strtol( optarg, NULL, 0 );
            break;

        case 'm':
            psz_modulation = optarg;
            break;

        case 'u':
            b_budget_mode = 1;
            break;

575
        case 'U':
576
            b_udp_global = 1;
577 578
            break;

579
        case 'L':
580
            i_latency_global = strtoll( optarg, NULL, 0 ) * 1000;
581 582 583
            break;

        case 'E':
584
            i_retention_global = strtoll( optarg, NULL, 0 ) * 1000;
585 586
            break;

587 588
        case 'd':
        {
589
            char *psz_token, *psz_displayname;
590
            char sz_port[6];
591 592 593 594 595 596 597 598 599 600
            struct addrinfo *p_daddr;
            struct addrinfo ai_hints;
            int i_dup_config = 0;

            snprintf( sz_port, sizeof( sz_port ), "%d", DEFAULT_PORT );

            p_daddr = malloc( sizeof( p_daddr ) );
            memset( p_daddr, '\0', sizeof( p_daddr ) );

            if ( !strncmp( optarg, "[", 1 ) )
601
            {
602 603 604 605 606
                if ( (psz_token = strchr( optarg, ']' ) ) == NULL )
                {
                    msg_Err(NULL, "Invalid target address for -d switch");
                    break;
                }
607

608 609 610
                char *psz_maddr = malloc( psz_token - optarg );
                memset( psz_maddr, '\0', ( psz_token - optarg ) );
                strncpy( psz_maddr, optarg + 1, ( psz_token - optarg - 1 ));
611

612 613 614 615 616
                if ( (psz_token = strchr( psz_token, ':' )) != NULL )
                {
                    *psz_token = '\0';
                    snprintf( sz_port, sizeof( sz_port ), "%d", atoi( psz_token + 1 ) );
                }
617

618 619 620 621
                memset( &ai_hints, 0, sizeof( ai_hints ) );
                ai_hints.ai_socktype = SOCK_DGRAM;
                ai_hints.ai_flags    = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
                ai_hints.ai_family   = AF_INET6;
622

623 624 625 626 627 628 629 630 631 632 633 634 635
                int i_ai = getaddrinfo( psz_maddr, sz_port, &ai_hints, &p_daddr );
                if ( i_ai != 0 )
                {
                    msg_Err( NULL, "Cannot duplicate to [%s]:%s: %s", psz_maddr,
                             sz_port, gai_strerror( i_ai ) );
                    break;
                }

                psz_displayname = malloc( INET6_ADDRSTRLEN + 20 );
                snprintf( psz_displayname, ( INET6_ADDRSTRLEN + 20 ),
                          "duplicate ([%s]:%s)", psz_maddr, sz_port );

                i_dup_config |= OUTPUT_VALID;
636

637
                free( psz_maddr );
638
            }
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
            else
            {
                if ( (psz_token = strrchr( optarg, ':' )) != NULL )
                {
                    *psz_token = '\0';
                    snprintf( sz_port, sizeof( sz_port ), "%d",  atoi( psz_token + 1 ) );
                }

                memset( &ai_hints, 0, sizeof( ai_hints ) );
                ai_hints.ai_socktype = SOCK_DGRAM;
                ai_hints.ai_flags    = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV;
                ai_hints.ai_family   = AF_INET;

                int i_ai = getaddrinfo( optarg, sz_port, &ai_hints, &p_daddr );
                if ( i_ai != 0 )
                {
                    msg_Err( NULL, "Cannot duplicate to %s:%s: %s", optarg,
                             sz_port, gai_strerror( i_ai ) );
                    break;
                }

                psz_displayname = malloc( INET_ADDRSTRLEN + 18 );
661
                snprintf( psz_displayname, ( INET_ADDRSTRLEN + 18 ),
662 663 664 665 666
                          "duplicate (%s:%s)", optarg, sz_port );

                i_dup_config |= OUTPUT_VALID;
            }

667 668 669 670 671 672
            if ( i_dup_config & OUTPUT_VALID )
            {
                output_dup.i_config = i_dup_config;
                output_dup.i_output_latency = i_latency_global;
                output_dup.i_max_retention = i_retention_global;
                output_Init( &output_dup, psz_displayname, p_daddr );
673 674
            }
            else
675 676
                msg_Err( NULL, "Invalid configuration for -d switch: %s" ,
                         optarg);
677

ivoire's avatar
ivoire committed
678
            free( psz_displayname );
679
            freeaddrinfo( p_daddr );
680 681 682
            break;
        }

683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
        case 'D':
        {
            char *psz_token;
            struct in_addr maddr;
            if ( pf_Open != NULL )
                usage();
            if ( psz_srv_socket != NULL )
            {
                msg_Err( NULL, "-r is only available for linux-dvb input" );
                usage();
            }

            pf_Open = udp_Open;
            pf_Read = udp_Read;
            pf_SetFilter = udp_SetFilter;
            pf_UnsetFilter = udp_UnsetFilter;

            if ( (psz_token = strrchr( optarg, '/' )) != NULL )
            {
                *psz_token = '\0';
                b_src_rawudp = ( strncasecmp( psz_token + 1, "udp", 3 ) == 0 );
            }
            if ( (psz_token = strrchr( optarg, ':' )) != NULL )
            {
                *psz_token = '\0';
                i_src_port = atoi( psz_token + 1 );
            }
            if ( !inet_aton( optarg, &maddr ) )
                usage();
            i_src_addr = maddr.s_addr;
            break;
        }

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
        case 'A':
            i_asi_adapter = strtol( optarg, NULL, 0 );
            if ( pf_Open != NULL )
                usage();
            if ( psz_srv_socket != NULL )
            {
                msg_Err( NULL, "-r is only available for linux-dvb input" );
                usage();
            }
            pf_Open = asi_Open;
            pf_Read = asi_Read;
            pf_SetFilter = asi_SetFilter;
            pf_UnsetFilter = asi_UnsetFilter;
            break;

731
        case 'C':
732
            b_dvb_global = 1;
733 734
            break;

735
        case 'e':
736
            b_epg_global = 1;
737 738
            break;

739 740 741 742 743 744 745 746
        case 'M':
            psz_network_name = optarg;
            break;

        case 'N':
            i_network_id = strtoul( optarg, NULL, 0 );
            break;

747 748 749 750 751 752 753 754
        case 'j':
            psz_native_charset = optarg;
            break;

        case 'J':
            psz_dvb_charset = optarg;
            break;

jpsaman's avatar
jpsaman committed
755 756 757 758
        case 'l':
            b_enable_syslog = 1;
            break;

759
        case 'T':
760
            b_random_tsid = 1;
761 762
            break;

763 764 765 766
        case 'V':
            exit(0);
            break;

767 768 769 770 771
        case 'h':
        default:
            usage();
        }
    }
772
    if ( optind < i_argc || pf_Open == NULL )
773 774
        usage();

jpsaman's avatar
jpsaman committed
775 776 777
    if ( b_enable_syslog )
        msg_Connect( pp_argv[0] );

778
    msg_Warn( NULL, "restarting" );
779

780
    if ( b_udp_global )
781 782 783 784 785
    {
        msg_Warn( NULL, "raw UDP output is deprecated.  Please consider using RTP." );
        msg_Warn( NULL, "for DVB-IP compliance you should use RTP." );
    }

786
    if ( b_epg_global && !b_dvb_global )
787 788
    {
        msg_Dbg( NULL, "turning on DVB compliance, required by EPG information" );
789
        b_dvb_global = 1;
790 791
    }

792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
    if ( strcasecmp( psz_native_charset, psz_dvb_charset ) )
    {
#ifdef HAVE_ICONV
        iconv_t iconv_system = iconv_open( psz_dvb_charset,
                                           psz_native_charset );
        if ( iconv_system != (iconv_t)-1 )
        {
            size_t i = strlen( psz_network_name );
            char *p, *psz_string;
            i_network_name_tmp_size = i * 6;
            p = psz_string = malloc(i_network_name_tmp_size);
            if ( iconv( iconv_system, (char **)&psz_network_name, &i, &p,
                        &i_network_name_tmp_size ) == -1 )
                free( psz_string );
            else
            {
                p_network_name_tmp = psz_string;
                i_network_name_tmp_size = p - psz_string;
            }
            iconv_close( iconv_system );
        }
#else
        msg_Warn( NULL,
                  "unable to convert from %s to %s (iconv is not available)",
                  psz_native_charset, psz_dvb_charset );
#endif
    }
    if ( p_network_name_tmp == NULL )
    {
        p_network_name_tmp = strdup(psz_network_name);
        i_network_name_tmp_size = strlen(psz_network_name);
    }
    p_network_name = dvb_string_set( (uint8_t *)p_network_name_tmp,
                                     i_network_name_tmp_size, psz_dvb_charset,
                                     &i_network_name_size );
    free( p_network_name_tmp );

829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
    signal( SIGHUP, SigHandler );
    srand( time(NULL) * getpid() );

    demux_Open();

    if ( i_priority > 0 )
    {
        memset( &param, 0, sizeof(struct sched_param) );
        param.sched_priority = i_priority;
        if ( (i_error = pthread_setschedparam( pthread_self(), SCHED_RR,
                                               &param )) )
        {
            msg_Warn( NULL, "couldn't set thread priority: %s",
                      strerror(i_error) );
        }
    }

    ReadConfiguration( psz_conf_file );

    if ( psz_srv_socket != NULL )
        comm_Open();

    for ( ; ; )
    {
853 854
        block_t *p_ts;

855 856 857 858 859 860 861
        if ( b_hup_received )
        {
            b_hup_received = 0;
            msg_Warn( NULL, "HUP received, reloading" );
            ReadConfiguration( psz_conf_file );
        }

862 863 864 865 866 867
        p_ts = pf_Read( i_poll_timeout );
        if ( p_ts != NULL )
            demux_Run( p_ts );
        i_poll_timeout = output_Send();
        if ( i_poll_timeout == -1 || i_poll_timeout > MAX_POLL_TIMEOUT )
            i_poll_timeout = MAX_POLL_TIMEOUT;
868
    }
jpsaman's avatar
jpsaman committed
869 870 871

    if ( b_enable_syslog )
        msg_Disconnect();
872
}