input.c 20.5 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
7
 * $Id: input.c,v 1.98 2001/04/15 04:19:58 sam 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

39 40
/* Network functions */

41
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
42 43 44 45 46 47
#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
#endif
49

50 51 52 53
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
54
#include "config.h"
55 56
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
57
#include "mtime.h"
58
#include "netutils.h"
59
#include "modules.h"
60

61
#include "intf_msg.h"
62
#include "intf_playlist.h"
63

64 65 66
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,  
Michel Lespinasse committed
67

68
#include "input.h"
69 70 71
#include "interface.h"

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

73 74 75
 /* #include <netutils.h> */


76
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
77
 * Local prototypes
78
 *****************************************************************************/
79 80 81 82 83
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
84

85
/*****************************************************************************
86
 * input_CreateThread: creates a new input thread
87
 *****************************************************************************
88 89 90 91
 * 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.
92
 *****************************************************************************/
93
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
94
{
95 96 97
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

98 99 100
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
101
    {
102 103
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
104 105
        return( NULL );
    }
106

107 108 109
    /* Packets read once */
    p_input->i_read_once = INPUT_READ_ONCE;

110 111 112
    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
113 114 115 116 117
    p_input->b_eof              = 0;

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

118
    /* I have never understood that stuff --Meuuh */
119 120
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
121

122
    /* Initialize stream description */
123 124
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
125
    p_input->stream.i_pgrm_number = 0;
126
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
Christophe Massiot's avatar
Christophe Massiot committed
127
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
128

129
    /* no stream, no area */
130 131
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
132
    p_input->stream.p_selected_area = NULL;
133 134
    /* By default there is one areas in a stream */
    input_AddArea( p_input );
135
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
136

137 138 139 140 141
    /* 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
142

143 144
    /* Initialize default settings for spawned decoders */
    p_input->p_default_aout = p_main->p_aout;
Sam Hocevar's avatar
Sam Hocevar committed
145
    p_input->p_default_vout = p_main->p_vout;
146

Michel Kaempf's avatar
Michel Kaempf committed
147
    /* Create thread and set locks. */
148
    vlc_mutex_init( &p_input->stream.stream_lock );
149
    vlc_cond_init( &p_input->stream.stream_wait );
150 151 152
    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
153
    {
154 155
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
156 157 158
        free( p_input );
        return( NULL );
    }
159

160 161 162 163
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
164
        {
165
            msleep( THREAD_SLEEP );
166
        } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
167
                && (i_status != THREAD_FATAL) );
168 169
        if( i_status != THREAD_READY )
        {
170 171
            return( NULL );
        }
172
    }
Michel Kaempf's avatar
Michel Kaempf committed
173 174 175
    return( p_input );
}

176
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
177
 * input_DestroyThread: mark an input thread as zombie
178
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
179
 * This function should not return until the thread is effectively cancelled.
180
 *****************************************************************************/
181
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
182
{
183
    int         i_status;                                   /* thread status */
184 185 186

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

189 190
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
191

192 193 194 195 196
    /* 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 );

197 198 199 200 201 202
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
203 204
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
205
    }
Michel Kaempf's avatar
Michel Kaempf committed
206 207
}

208
/*****************************************************************************
209
 * RunThread: main thread loop
210
 *****************************************************************************
211
 * Thread in charge of processing the network packets and demultiplexing.
212
 *****************************************************************************/
213
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
214
{
215
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
216

217 218 219 220 221 222 223 224 225 226
    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
227

228
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
229
    {
230
        data_packet_t *         pp_packets[p_input->i_read_once];
231

232
#ifdef STATS
233
        p_input->c_loops++;
234 235
#endif

236
        vlc_mutex_lock( &p_input->stream.stream_lock );
237

238
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
239
        {
240 241
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
242 243
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
244 245 246 247 248 249 250 251 252 253 254 255 256

                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;
                }
            }
257
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
258
        }
259

260
        vlc_mutex_unlock( &p_input->stream.stream_lock );
261 262

        i_error = p_input->pf_read( p_input, pp_packets );
263

264
        /* Demultiplex read packets. */
265
        for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ )
266 267 268
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
269

270 271 272
        if( i_error )
        {
            if( i_error == 1 )
273
            {
274 275
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
276
                intf_WarnMsg( 1, "input: EOF reached" );
277
                p_input->b_eof = 1;
278
            }
279
            else
280
            {
281
                p_input->b_error = 1;
282
            }
283 284 285
        }
    }

286
    if( p_input->b_error || p_input->b_eof )
287 288 289
    {
        ErrorThread( p_input );
    }
290

291
    EndThread( p_input );
292 293 294

    DestroyThread( p_input );

295
    intf_DbgMsg("input: Thread end");
296 297
}

298
/*****************************************************************************
299
 * InitThread: init the input Thread
300
 *****************************************************************************/
301
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
302 303 304
{

#ifdef STATS
305 306 307 308 309 310
    /* 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
311
#endif
312

Sam Hocevar's avatar
Sam Hocevar committed
313
    p_input->p_input_module = module_Need( p_main->p_bank,
314 315
                                           MODULE_CAPABILITY_INPUT,
                                           (probedata_t *)p_input );
316 317

    if( p_input->p_input_module == NULL )
318
    {
319 320
        intf_ErrMsg( "input error: no suitable input module for `%s'",
                     p_input->p_source );
321
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
322
    }
323

324 325 326 327 328 329
#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;
330
    p_input->pf_set_area      = f.pf_set_area;
331 332 333 334 335 336 337 338 339
    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 );
340

341
    if( p_input->b_error )
342
    {
343 344
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
Sam Hocevar committed
345
        module_Unneed( p_main->p_bank, p_input->p_input_module );
346
        return( -1 );
347
    }
348 349

    p_input->pf_init( p_input );
350

351 352 353 354 355 356 357 358
    if( p_input->b_error )
    {
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
        module_Unneed( p_main->p_bank, p_input->p_input_module );
        return( -1 );
    }

359
    *p_input->pi_status = THREAD_READY;
360 361

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
362 363
}

364
/*****************************************************************************
365
 * ErrorThread: RunThread() error loop
366
 *****************************************************************************
367
 * This function is called when an error occured during thread main's loop.
368
 *****************************************************************************/
369
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
370
{
371
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
372
    {
373 374
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
375 376 377
    }
}

378
/*****************************************************************************
379
 * EndThread: end the input thread
380
 *****************************************************************************/
381
static void EndThread( input_thread_t * p_input )
382
{
383
    int *       pi_status;                                  /* thread status */
384

385 386 387
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
388

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
#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 );

405 406 407
    /* Close stream */
    p_input->pf_close( p_input );

408
    /* Release modules */
Sam Hocevar's avatar
Sam Hocevar committed
409
    module_Unneed( p_main->p_bank, p_input->p_input_module );
410

411 412 413 414 415 416 417 418 419 420 421
}

/*****************************************************************************
 * 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;
422

423 424
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
425
    vlc_mutex_destroy( &p_input->stream.stream_lock );
426
    
427
    /* Free input structure */
428
    free( p_input );
429

430 431
    /* Update status */
    *pi_status = THREAD_OVER;
432
}
433

434
/*****************************************************************************
435
 * input_FileOpen : open a file descriptor
436
 *****************************************************************************/
437
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
438
{
439
    struct stat         stat_info;
440 441 442
    int                 i_stat;

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

444 445 446
    /* 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) )
447
    {
448 449 450 451 452 453 454 455
        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 );
456
        }
457
        else if( ( i_size > 5 )
458 459 460 461 462
                 && !strncasecmp( psz_name, "file:", 5 ) )
        {
            /* get rid of the 'file:' stuff and try again */
            psz_name += 5;
            i_stat = stat( psz_name, &stat_info );
463
        }
464

465
        if( i_stat == (-1) )
466 467 468 469 470 471
        {
            intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
                         psz_name, strerror(errno));
            p_input->b_error = 1;
            return;
        }
472 473 474 475 476 477 478 479 480 481 482
    }

    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;
483
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
484
    }
485 486 487 488 489
    else if( S_ISFIFO(stat_info.st_mode)
#ifndef SYS_BEOS
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
490
    {
491
        p_input->stream.b_seekable = 0;
492
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
Benoit Steiner committed
493 494 495
    }
    else
    {
496
        vlc_mutex_unlock( &p_input->stream.stream_lock );
497
        intf_ErrMsg( "input error: unknown file type for `%s'",
498
                     psz_name );
499 500 501
        p_input->b_error = 1;
        return;
    }
502

503
    p_input->stream.p_selected_area->i_tell = 0;
504 505
    vlc_mutex_unlock( &p_input->stream.stream_lock );

506 507
    intf_Msg( "input: opening %s", p_input->p_source );
    if( (p_input->i_handle = open( psz_name,
508 509
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
510
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
511 512
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
513 514 515
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
516 517

/*****************************************************************************
518
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
519
 *****************************************************************************/
520
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
521
{
522
    intf_Msg( "input: closing %s", p_input->p_source );
523
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
524

525
    return;
Stéphane Borel's avatar
Stéphane Borel committed
526
}
527

528

529
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
530
/*****************************************************************************
531
 * input_NetworkOpen : open a network socket 
532
 *****************************************************************************/
533
void input_NetworkOpen( input_thread_t * p_input )
534
{
535 536 537 538
 
    int                 i_option_value, i_port;
    struct sockaddr_in  s_socket;
    boolean_t           b_broadcast;
539
    
540
    /* FIXME : we don't handle channels for the moment */
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
    
    /* Get the remote server */
    if( p_input->p_source == NULL )
    {
        p_input->p_source = main_GetPszVariable( INPUT_SERVER_VAR, 
                                                 INPUT_SERVER_DEFAULT );
    }
    
    /* 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;
    }

    /* 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;
    }

    /* 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 */
589
    
590
    /* Build the local socket */
591
    if ( network_BuildLocalAddr( &s_socket, i_port, b_broadcast ) 
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
         == -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 */
610
    if ( network_BuildRemoteAddr( &s_socket, p_input->p_source ) 
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
         == -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 */
}
643
#endif