input.c 23.3 KB
Newer Older
1 2
/*****************************************************************************
 * input.c: input thread
Michel Kaempf's avatar
Michel Kaempf committed
3 4
 * Read an MPEG2 stream, demultiplex and parse it before sending it to
 * decoders.
5 6
 *****************************************************************************
 * Copyright (C) 1998, 1999, 2000 VideoLAN
Henri Fallon's avatar
 
Henri Fallon committed
7
 * $Id: input.c,v 1.89 2001/03/07 00:18:46 henri Exp $
8
 *
9
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
10 11 12 13 14
 *
 * 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.
15
 * 
16 17
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
20
 *
21 22 23
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
25

26
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
27
 * Preamble
28
 *****************************************************************************/
29 30
#include "defs.h"

31 32 33 34 35 36 37
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
Michel Kaempf's avatar
Michel Kaempf committed
38

Henri Fallon's avatar
 
Henri Fallon committed
39 40 41 42 43 44 45 46 47
/* Network functions */

#include <netdb.h>                                             /* hostent ... */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

48 49 50 51
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
52
#include "config.h"
53 54
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
55
#include "mtime.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
56
#include "modules.h"
57

58
#include "intf_msg.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
59
#include "intf_plst.h"
60

61 62 63
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,  
Michel Lespinasse committed
64

65
#include "input.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
66 67 68
#include "interface.h"

#include "main.h"
Michel Kaempf's avatar
Michel Kaempf committed
69

70
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
71
 * Local prototypes
72
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
73 74 75 76 77
static void RunThread       ( input_thread_t *p_input );
static  int InitThread      ( input_thread_t *p_input );
static void ErrorThread     ( input_thread_t *p_input );
static void DestroyThread   ( input_thread_t *p_input );
static void EndThread       ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
78

79
/*****************************************************************************
80
 * input_CreateThread: creates a new input thread
81
 *****************************************************************************
82 83 84 85
 * This function creates a new input, and returns a pointer
 * to its description. On error, it returns NULL.
 * If pi_status is NULL, then the function will block until the thread is ready.
 * If not, it will be updated using one of the THREAD_* constants.
86
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
87
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
88
{
89 90 91
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

92 93 94
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
95
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
96 97
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
98 99
        return( NULL );
    }
100

101 102 103
    /* Packets read once */
    p_input->i_read_once = INPUT_READ_ONCE;

104 105 106
    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
107 108 109 110 111
    p_input->b_eof              = 0;

    /* Set target */
    p_input->p_source           = p_item->psz_name;

112
    /* I have never understood that stuff --Meuuh */
113 114
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
115

116
    /* Initialize stream description */
117 118
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
119
    p_input->stream.i_pgrm_number = 0;
120
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
Christophe Massiot's avatar
Christophe Massiot committed
121
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
122

Stéphane Borel's avatar
 
Stéphane Borel committed
123 124 125 126
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
    /* By default there is one areas in a stream */
    input_AddArea( p_input );
127 128
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
    p_input->stream.p_selected_area->i_seek = NO_SEEK;
Stéphane Borel's avatar
 
Stéphane Borel committed
129

130 131 132 133 134
    /* Initialize stream control properties. */
    p_input->stream.control.i_status = PLAYING_S;
    p_input->stream.control.i_rate = DEFAULT_RATE;
    p_input->stream.control.b_mute = 0;
    p_input->stream.control.b_bw = 0;
Michel Kaempf's avatar
Michel Kaempf committed
135

Sam Hocevar's avatar
 
Sam Hocevar committed
136 137
    /* Initialize default settings for spawned decoders */
    p_input->p_default_aout = p_main->p_aout;
Sam Hocevar's avatar
 
Sam Hocevar committed
138
    p_input->p_default_vout = p_main->p_vout;
Sam Hocevar's avatar
 
Sam Hocevar committed
139

Michel Kaempf's avatar
Michel Kaempf committed
140
    /* Create thread and set locks. */
141
    vlc_mutex_init( &p_input->stream.stream_lock );
142
    vlc_cond_init( &p_input->stream.stream_wait );
143 144 145
    vlc_mutex_init( &p_input->stream.control.control_lock );
    if( vlc_thread_create( &p_input->thread_id, "input", (void *) RunThread,
                           (void *) p_input ) )
Michel Kaempf's avatar
Michel Kaempf committed
146
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
147 148
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
149 150 151
        free( p_input );
        return( NULL );
    }
152

153 154 155 156
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
157
        {
158
            msleep( THREAD_SLEEP );
159
        } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
Sam Hocevar's avatar
 
Sam Hocevar committed
160
                && (i_status != THREAD_FATAL) );
161 162
        if( i_status != THREAD_READY )
        {
163 164
            return( NULL );
        }
165
    }
Michel Kaempf's avatar
Michel Kaempf committed
166 167 168
    return( p_input );
}

169
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
170
 * input_DestroyThread: mark an input thread as zombie
171
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
172
 * This function should not return until the thread is effectively cancelled.
173
 *****************************************************************************/
174
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
175
{
176
    int         i_status;                                   /* thread status */
177 178 179

    /* Set status */
    p_input->pi_status = (pi_status != NULL) ? pi_status : &i_status;
180 181
    *p_input->pi_status = THREAD_DESTROY;

182 183
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
184

185 186 187 188 189
    /* Make the thread exit of an eventual vlc_cond_wait() */
    vlc_mutex_lock( &p_input->stream.stream_lock );
    vlc_cond_signal( &p_input->stream.stream_wait );
    vlc_mutex_unlock( &p_input->stream.stream_lock );

190 191 192 193 194 195
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
196 197
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
198
    }
Michel Kaempf's avatar
Michel Kaempf committed
199 200
}

201
/*****************************************************************************
202
 * RunThread: main thread loop
203
 *****************************************************************************
204
 * Thread in charge of processing the network packets and demultiplexing.
205
 *****************************************************************************/
206
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
207
{
208
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
209

Sam Hocevar's avatar
 
Sam Hocevar committed
210 211 212 213 214 215 216 217 218 219
    if( InitThread( p_input ) )
    {

        /* If we failed, wait before we are killed, and exit */
        *p_input->pi_status = THREAD_ERROR;
        p_input->b_error = 1;
        ErrorThread( p_input );
        DestroyThread( p_input );
        return;
    }
Michel Kaempf's avatar
Michel Kaempf committed
220

Sam Hocevar's avatar
 
Sam Hocevar committed
221
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
222
    {
223
        data_packet_t *         pp_packets[p_input->i_read_once];
224

Sam Hocevar's avatar
 
Sam Hocevar committed
225
#ifdef STATS
Sam Hocevar's avatar
 
Sam Hocevar committed
226
        p_input->c_loops++;
Sam Hocevar's avatar
 
Sam Hocevar committed
227 228
#endif

229
        vlc_mutex_lock( &p_input->stream.stream_lock );
230
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
Sam Hocevar's avatar
 
Sam Hocevar committed
231
        {
232 233
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
234 235
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
236 237 238 239 240 241 242 243 244 245 246 247 248

                for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
                {
                    pgrm_descriptor_t * p_pgrm
                                            = p_input->stream.pp_programs[i];
                    /* Escape all decoders for the stream discontinuity they
                     * will encounter. */
                    input_EscapeDiscontinuity( p_input, p_pgrm );

                    /* Reinitialize synchro. */
                    p_pgrm->i_synchro_state = SYNCHRO_REINIT;
                }
            }
249
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
Sam Hocevar's avatar
 
Sam Hocevar committed
250
        }
251
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
252 253

        i_error = p_input->pf_read( p_input, pp_packets );
Sam Hocevar's avatar
 
Sam Hocevar committed
254

Sam Hocevar's avatar
 
Sam Hocevar committed
255
        /* Demultiplex read packets. */
256
        for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
257 258 259
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
260

Sam Hocevar's avatar
 
Sam Hocevar committed
261 262 263
        if( i_error )
        {
            if( i_error == 1 )
264
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
265 266 267 268
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
                intf_WarnMsg( 1, "End of file reached" );
                p_input->b_eof = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
269
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
270
            else
Sam Hocevar's avatar
 
Sam Hocevar committed
271
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
272
                p_input->b_error = 1;
273
            }
274 275 276
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
277
    if( p_input->b_error || p_input->b_eof )
278 279 280
    {
        ErrorThread( p_input );
    }
281

282
    EndThread( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
283 284 285

    DestroyThread( p_input );

286
    intf_DbgMsg("Thread end");
287 288
}

289
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
290
 * InitThread: init the input Thread
291
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
292
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
293 294 295
{

#ifdef STATS
296 297 298 299 300 301
    /* Initialize statistics */
    p_input->c_loops                    = 0;
    p_input->c_bytes                    = 0;
    p_input->c_payload_bytes            = 0;
    p_input->c_packets_read             = 0;
    p_input->c_packets_trashed          = 0;
Michel Kaempf's avatar
Michel Kaempf committed
302
#endif
303

Sam Hocevar's avatar
 
Sam Hocevar committed
304
    p_input->p_input_module = module_Need( p_main->p_bank,
Sam Hocevar's avatar
 
Sam Hocevar committed
305 306
                                           MODULE_CAPABILITY_INPUT,
                                           (probedata_t *)p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
307 308

    if( p_input->p_input_module == NULL )
309
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
310
        intf_ErrMsg( "input error: no suitable input module" );
Sam Hocevar's avatar
 
Sam Hocevar committed
311
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
312
    }
313

Sam Hocevar's avatar
 
Sam Hocevar committed
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
#define f p_input->p_input_module->p_functions->input.functions.input
    p_input->pf_init          = f.pf_init;
    p_input->pf_open          = f.pf_open;
    p_input->pf_close         = f.pf_close;
    p_input->pf_end           = f.pf_end;
    p_input->pf_read          = f.pf_read;
    p_input->pf_demux         = f.pf_demux;
    p_input->pf_new_packet    = f.pf_new_packet;
    p_input->pf_new_pes       = f.pf_new_pes;
    p_input->pf_delete_packet = f.pf_delete_packet;
    p_input->pf_delete_pes    = f.pf_delete_pes;
    p_input->pf_rewind        = f.pf_rewind;
    p_input->pf_seek          = f.pf_seek;
#undef f
    p_input->pf_open( p_input );
329

Sam Hocevar's avatar
 
Sam Hocevar committed
330
    if( p_input->b_error )
331
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
332 333
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
334
        module_Unneed( p_main->p_bank, p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
335
        return( -1 );
336
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
337 338

    p_input->pf_init( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
339

Sam Hocevar's avatar
 
Sam Hocevar committed
340
    *p_input->pi_status = THREAD_READY;
Sam Hocevar's avatar
 
Sam Hocevar committed
341 342

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
343 344
}

345
/*****************************************************************************
346
 * ErrorThread: RunThread() error loop
347
 *****************************************************************************
348
 * This function is called when an error occured during thread main's loop.
349
 *****************************************************************************/
350
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
351
{
352
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
353
    {
354 355
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
356 357 358
    }
}

359
/*****************************************************************************
360
 * EndThread: end the input thread
361
 *****************************************************************************/
362
static void EndThread( input_thread_t * p_input )
363
{
364
    int *       pi_status;                                  /* thread status */
365

366 367 368
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
369

Sam Hocevar's avatar
 
Sam Hocevar committed
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
#ifdef STATS
    {
        struct tms cpu_usage;
        times( &cpu_usage );

        intf_Msg("input stats: cpu usage (user: %d, system: %d)",
                 cpu_usage.tms_utime, cpu_usage.tms_stime);
    }
#endif

    /* Free all ES and destroy all decoder threads */
    input_EndStream( p_input );

    /* Free demultiplexer's data */
    p_input->pf_end( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
386 387 388
    /* Close stream */
    p_input->pf_close( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
389
    /* Release modules */
Sam Hocevar's avatar
 
Sam Hocevar committed
390
    module_Unneed( p_main->p_bank, p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
391

Sam Hocevar's avatar
 
Sam Hocevar committed
392 393 394 395 396 397 398 399 400 401 402
}

/*****************************************************************************
 * DestroyThread: destroy the input thread
 *****************************************************************************/
static void DestroyThread( input_thread_t * p_input )
{
    int *       pi_status;                                  /* thread status */

    /* Store status */
    pi_status = p_input->pi_status;
Sam Hocevar's avatar
 
Sam Hocevar committed
403

Henri Fallon's avatar
 
Henri Fallon committed
404 405
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
 
Henri Fallon committed
406
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
 
Henri Fallon committed
407
    
408
    /* Free input structure */
409
    free( p_input );
410

411 412
    /* Update status */
    *pi_status = THREAD_OVER;
413
}
414

415
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
416
 * input_FileOpen : open a file descriptor
417
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
418
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
419
{
420
    struct stat         stat_info;
Sam Hocevar's avatar
 
Sam Hocevar committed
421 422 423
    int                 i_stat;

    char *psz_name = p_input->p_source;
Michel Kaempf's avatar
Michel Kaempf committed
424

Sam Hocevar's avatar
 
Sam Hocevar committed
425 426 427
    /* FIXME: this code ought to be in the plugin so that code can
     * be shared with the *_Probe function */
    if( ( i_stat = stat( psz_name, &stat_info ) ) == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
428
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
429 430 431 432 433 434 435 436
        int i_size = strlen( psz_name );

        if( ( i_size > 4 )
            && !strncasecmp( psz_name, "dvd:", 4 ) )
        {
            /* get rid of the 'dvd:' stuff and try again */
            psz_name += 4;
            i_stat = stat( psz_name, &stat_info );
Henri Fallon's avatar
 
Henri Fallon committed
437
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
438 439 440 441 442 443
	else if( ( i_size > 5 )
                 && !strncasecmp( psz_name, "file:", 5 ) )
        {
            /* get rid of the 'file:' stuff and try again */
            psz_name += 5;
            i_stat = stat( psz_name, &stat_info );
Henri Fallon's avatar
 
Henri Fallon committed
444
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
445

Henri Fallon's avatar
 
Henri Fallon committed
446
    	if( i_stat == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
447 448 449 450 451 452
        {
            intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
                         psz_name, strerror(errno));
            p_input->b_error = 1;
            return;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
453 454 455 456 457 458 459 460 461 462 463
    }

    vlc_mutex_lock( &p_input->stream.stream_lock );

    /* If we are here we can control the pace... */
    p_input->stream.b_pace_control = 1;

    if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode)
         || S_ISBLK(stat_info.st_mode) )
    {
        p_input->stream.b_seekable = 1;
464
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
465
    }
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
466 467 468 469 470
    else if( S_ISFIFO(stat_info.st_mode)
#ifndef SYS_BEOS
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
471
    {
472
        p_input->stream.b_seekable = 0;
473
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
 
Benoit Steiner committed
474 475 476
    }
    else
    {
477
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
478
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
479
                     psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
480 481 482
        p_input->b_error = 1;
        return;
    }
483

484
    p_input->stream.p_selected_area->i_tell = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
485 486
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
487 488
    intf_Msg( "input: opening %s", p_input->p_source );
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
 
Sam Hocevar committed
489 490
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
491
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
 
Sam Hocevar committed
492 493
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
494 495 496
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
497 498

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
499
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
500
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
501
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
502
{
Sam Hocevar's avatar
 
Sam Hocevar committed
503
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
504

Sam Hocevar's avatar
 
Sam Hocevar committed
505
    return;
Stéphane Borel's avatar
Stéphane Borel committed
506
}
Sam Hocevar's avatar
 
Sam Hocevar committed
507

Henri Fallon's avatar
 
Henri Fallon committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 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 716
/*****************************************************************************
 * input_BuildLocalAddr : fill a sockaddr_in structure for local binding
 *****************************************************************************/
int input_BuildLocalAddr( struct sockaddr_in * p_socket, int i_port, 
                      boolean_t b_broadcast )
{
    char                psz_hostname[INPUT_MAX_SOURCE_LENGTH];
    struct hostent    * p_hostent;
    
    /* Reset struct */
    memset( p_socket, 0, sizeof( struct sockaddr_in ) );
    p_socket->sin_family = AF_INET;                                 /* family */
    p_socket->sin_port = htons( i_port );
    
    if( !b_broadcast )
    {
        /* Try to get our own IP */
        if( gethostname( psz_hostname, sizeof(psz_hostname) ) )
        {
            intf_ErrMsg( "BuildLocalAddr : unable to resolve local name : %s",
                         strerror( errno ) );
            return( -1 );
        }

    }
    else
    {
        /* Using broadcast address. There are many ways of doing it, one of
         * the simpliest being a #define ...
         * FIXME : this is ugly */ 
        strncpy( psz_hostname, INPUT_BCAST_ADDR,INPUT_MAX_SOURCE_LENGTH );
    }

    /* Try to convert address directly from in_addr - this will work if
     * psz_in_addr is dotted decimal. */

#ifdef HAVE_ARPA_INET_H
    if( !inet_aton( psz_hostname, &p_socket->sin_addr) )
#else
    if( (p_socket->sin_addr.s_addr = inet_addr( psz_hostname )) == -1 )
#endif
    {
        /* We have a fqdn, try to find its address */
        if ( (p_hostent = gethostbyname( psz_hostname )) == NULL )
        {
            intf_ErrMsg( "BuildLocalAddr: unknown host %s", psz_hostname );
            return( -1 );
        }
        
        /* Copy the first address of the host in the socket address */
        memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0], 
                 p_hostent->h_length );
    }

    return( 0 );
}

/*****************************************************************************
 * input_BuildRemoteAddr : fill a sockaddr_in structure for remote host
 *****************************************************************************/
int input_BuildRemoteAddr( input_thread_t * p_input, 
                           struct sockaddr_in * p_socket )
{
    struct hostent            * p_hostent;

    /* Reset structure */
    memset( p_socket, 0, sizeof( struct sockaddr_in ) );
    p_socket->sin_family = AF_INET;                                 /* family */
    p_socket->sin_port = htons( 0 );                /* This is for remote end */
    
    /* Get the remote server */
    if( p_input->p_source == NULL )
    {
        p_input->p_source = main_GetPszVariable( INPUT_SERVER_VAR, 
                                                 INPUT_SERVER_DEFAULT );
    }

     /* Try to convert address directly from in_addr - this will work if
      * psz_in_addr is dotted decimal. */
#ifdef HAVE_ARPA_INET_H
    if( !inet_aton( p_input->p_source, &p_socket->sin_addr) )
#else
    if( (p_socket->sin_addr.s_addr = inet_addr( p_input->p_source )) == -1 )
#endif
    {
        /* We have a fqdn, try to find its address */
        if ( (p_hostent = gethostbyname(p_input->p_source)) == NULL )
        {
            intf_ErrMsg( "BuildRemoteAddr: unknown host %s", 
                         p_input->p_source );
            return( -1 );
        }
        
        /* Copy the first address of the host in the socket address */
        memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0], 
                 p_hostent->h_length );
    }

    return( 0 );
}

/*****************************************************************************
 * input_NetworkOpen : open a network socket 
 *****************************************************************************/
void input_NetworkOpen( input_thread_t * p_input )
{
 
    int                 i_option_value, i_port;
    struct sockaddr_in  s_socket;
    boolean_t           b_broadcast;
    
    /* FIXME : we don't handle channels for the moment */

    /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
     * protocol */
    p_input->i_handle = socket( AF_INET, SOCK_DGRAM, 0 );
    if( p_input->i_handle == -1 )
    {
        intf_ErrMsg("NetworkOpen : can't create socket : %s", strerror(errno));
        p_input->b_error = 1;
        return;
    }

    /* We may want to reuse an already used socket */
    i_option_value = 1;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
                    &i_option_value,sizeof( i_option_value ) ) == -1 )
    {
        intf_ErrMsg("NetworkOpen : can't configure socket (SO_REUSEADDR: %s)",
                    strerror(errno));
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

#ifndef SYS_BEOS
    /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
     * packet loss caused by scheduling problems */
    i_option_value = 524288;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF, &i_option_value,
                    sizeof( i_option_value ) ) == -1 )
    {
        intf_ErrMsg("NetworkOpen : can't configure socket (SO_RCVBUF: %s)", 
                    strerror(errno));
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
#endif /* SYS_BEOS */

    /* Get details about what we are supposed to do */
    b_broadcast = (boolean_t)main_GetIntVariable( INPUT_BROADCAST_VAR, 0 );
    i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );

    /* TODO : here deal with channel stufs */

    /* Build the local socket */
    if ( input_BuildLocalAddr( &s_socket, i_port, b_broadcast ) 
         == -1 )
    {
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
    
    /* Bind it */
    if( bind( p_input->i_handle, (struct sockaddr *)&s_socket, 
              sizeof( s_socket ) ) < 0 )
    {
        intf_ErrMsg("NetworkOpen: can't bind socket (%s)", strerror(errno));
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build socket for remote connection */
    if ( input_BuildRemoteAddr( p_input, &s_socket ) 
         == -1 )
    {
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* And connect it ... should we really connect ? */
    if( connect( p_input->i_handle, (struct sockaddr *) &s_socket,
                 sizeof( s_socket ) ) == (-1) )
    {
        intf_ErrMsg("NetworkOpen: can't connect socket" );
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* We can't pace control, but FIXME : bug in meuuh's code to sync PCR
     * with the server. */
    p_input->stream.b_pace_control = 1;
    
    return;
}

/*****************************************************************************
 * input_NetworkClose : close a network socket
 *****************************************************************************/
void input_NetworkClose( input_thread_t * p_input )
{
    close( p_input->i_handle );
    /* FIXME: deal with channels */
}