input.c 24.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
Sam Hocevar's avatar
   
Sam Hocevar committed
7
 * $Id: input.c,v 1.114 2001/05/30 22:16:07 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
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
37
38
39
#ifdef STRNCASECMP_IN_STRINGS_H
#   include <strings.h>
#endif
40
#include <errno.h>
Michel Kaempf's avatar
Michel Kaempf committed
41

42
43
44
45
46
47
48
/* WinSock Includes */

#ifdef WIN32
#include <winsock2.h>
#endif


Henri Fallon's avatar
   
Henri Fallon committed
49
50
/* Network functions */

Sam Hocevar's avatar
   
Sam Hocevar committed
51
52
#if !defined( SYS_BEOS ) && !defined( SYS_NTO ) && !defined( WIN32 )
#include <netdb.h>                                            /* hostent ... */
Henri Fallon's avatar
   
Henri Fallon committed
53
54
55
56
57
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
58
#endif
Henri Fallon's avatar
   
Henri Fallon committed
59

60
61
62
63
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
64
#include "config.h"
65
66
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
67
#include "mtime.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
68
#include "netutils.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
69
#include "modules.h"
70

71
#include "intf_msg.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
72
#include "intf_playlist.h"
73

74
75
76
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,    
Michel Lespinasse committed
77

78
#include "input.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
79
80
81
#include "interface.h"

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

Henri Fallon's avatar
   
Henri Fallon committed
83

84
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
85
 * Local prototypes
86
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
87
88
89
90
91
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
92

Sam Hocevar's avatar
   
Sam Hocevar committed
93
94
95
96
97
static void FileOpen        ( input_thread_t *p_input );
static void FileClose       ( input_thread_t *p_input );
static void NetworkOpen     ( input_thread_t *p_input );
static void NetworkClose    ( input_thread_t *p_input );

98
/*****************************************************************************
99
 * input_CreateThread: creates a new input thread
100
 *****************************************************************************
101
102
103
104
 * 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.
105
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
106
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
107
{
108
109
110
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

111
112
113
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
114
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
115
116
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
117
118
        return( NULL );
    }
119

120
121
122
    /* Packets read once */
    p_input->i_read_once = INPUT_READ_ONCE;

123
124
125
    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
126
127
128
129
130
    p_input->b_eof              = 0;

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

131
    /* I have never understood that stuff --Meuuh */
132
133
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
134

135
    /* Initialize stream description */
136
137
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
138
    p_input->stream.i_pgrm_number = 0;
139
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
Christophe Massiot's avatar
Christophe Massiot committed
140
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
141

142
    /* no stream, no area */
Stéphane Borel's avatar
   
Stéphane Borel committed
143
144
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
145
    p_input->stream.p_selected_area = NULL;
146
    p_input->stream.p_new_area = NULL;
Stéphane Borel's avatar
   
Stéphane Borel committed
147
148
    /* By default there is one areas in a stream */
    input_AddArea( p_input );
149
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
Stéphane Borel's avatar
   
Stéphane Borel committed
150

151
152
153
154
155
    /* 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
156

Sam Hocevar's avatar
   
Sam Hocevar committed
157
158
159
160
161
162
163
164
    /* Setup callbacks */
    p_input->pf_file_open     = FileOpen;
    p_input->pf_file_close    = FileClose;
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
    p_input->pf_network_open  = NetworkOpen;
    p_input->pf_network_close = NetworkClose;
#endif

Michel Kaempf's avatar
Michel Kaempf committed
165
    /* Create thread and set locks. */
166
    vlc_mutex_init( &p_input->stream.stream_lock );
167
    vlc_cond_init( &p_input->stream.stream_wait );
168
169
170
    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
171
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
172
173
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
174
175
176
        free( p_input );
        return( NULL );
    }
177

178
179
180
181
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
182
        {
183
            msleep( THREAD_SLEEP );
184
        } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
Sam Hocevar's avatar
   
Sam Hocevar committed
185
                && (i_status != THREAD_FATAL) );
186
187
        if( i_status != THREAD_READY )
        {
188
189
            return( NULL );
        }
190
    }
Michel Kaempf's avatar
Michel Kaempf committed
191
192
193
    return( p_input );
}

194
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
195
 * input_DestroyThread: mark an input thread as zombie
196
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
197
 * This function should not return until the thread is effectively cancelled.
198
 *****************************************************************************/
199
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
200
{
201
    int         i_status;                                   /* thread status */
202
203
204

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

207
208
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
209

210
211
212
213
214
    /* 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 );

215
216
217
218
219
220
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
221
222
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
223
    }
Michel Kaempf's avatar
Michel Kaempf committed
224
225
}

226
/*****************************************************************************
227
 * RunThread: main thread loop
228
 *****************************************************************************
229
 * Thread in charge of processing the network packets and demultiplexing.
230
 *****************************************************************************/
231
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
232
{
233
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
234

Sam Hocevar's avatar
   
Sam Hocevar committed
235
236
237
238
239
240
241
242
243
244
    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
245

246
247
248
249
250
    /* initialization is completed */
    vlc_mutex_lock( &p_input->stream.stream_lock );
    p_input->stream.b_changed = 1;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
   
Sam Hocevar committed
251
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
252
    {
253
        data_packet_t *         pp_packets[p_input->i_read_once];
254

Sam Hocevar's avatar
   
Sam Hocevar committed
255
#ifdef STATS
Sam Hocevar's avatar
   
Sam Hocevar committed
256
        p_input->c_loops++;
Sam Hocevar's avatar
   
Sam Hocevar committed
257
258
#endif

259
        vlc_mutex_lock( &p_input->stream.stream_lock );
260

261
262
263
264
265
266
        if( p_input->stream.p_new_area )
        {
            p_input->pf_set_area( p_input, p_input->stream.p_new_area );
            p_input->stream.p_new_area = NULL;
        }

267
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
Sam Hocevar's avatar
   
Sam Hocevar committed
268
        {
269
270
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
271
272
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
273
274
275
276
277
278
279
280
281
282
283
284
285

                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;
                }
            }
286
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
Sam Hocevar's avatar
   
Sam Hocevar committed
287
        }
288

289
290
291
292
293
294
295
296
297
298
299
300
        if( p_input->stream.p_removed_es )
        {
            input_UnselectES( p_input, p_input->stream.p_removed_es );
            p_input->stream.p_removed_es = NULL;
        }

        if( p_input->stream.p_newly_selected_es )
        {
            input_SelectES( p_input, p_input->stream.p_newly_selected_es );
            p_input->stream.p_newly_selected_es = NULL;
        }

301
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
302
303

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

Sam Hocevar's avatar
   
Sam Hocevar committed
305
        /* Demultiplex read packets. */
306
        for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
307
308
309
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
310

Sam Hocevar's avatar
   
Sam Hocevar committed
311
312
313
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
314
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
315
316
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
317
                intf_WarnMsg( 3, "input: EOF reached" );
Sam Hocevar's avatar
   
Sam Hocevar committed
318
                p_input->b_eof = 1;
Sam Hocevar's avatar
   
Sam Hocevar committed
319
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
320
            else
Sam Hocevar's avatar
   
Sam Hocevar committed
321
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
322
                p_input->b_error = 1;
Sam Hocevar's avatar
Sam Hocevar committed
323
            }
324
325
326
        }
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
327
    if( p_input->b_error || p_input->b_eof )
328
329
330
    {
        ErrorThread( p_input );
    }
331

332
    EndThread( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
333
334
335

    DestroyThread( p_input );

Sam Hocevar's avatar
   
Sam Hocevar committed
336
    intf_DbgMsg("input: Thread end");
337
338
}

339
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
340
 * InitThread: init the input Thread
341
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
342
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
343
344
345
{

#ifdef STATS
346
347
348
349
350
351
    /* 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
352
#endif
Sam Hocevar's avatar
Sam Hocevar committed
353

Sam Hocevar's avatar
   
Sam Hocevar committed
354
355
356
357
    /* Default, might get overwritten */
    p_input->pf_open = p_input->pf_file_open;
    p_input->pf_close = p_input->pf_file_close;

Sam Hocevar's avatar
   
Sam Hocevar committed
358
    p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT,
Sam Hocevar's avatar
   
Sam Hocevar committed
359
                                           (probedata_t *)p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
360
361

    if( p_input->p_input_module == NULL )
362
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
363
364
        intf_ErrMsg( "input error: no suitable input module for `%s'",
                     p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
365
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
366
    }
367

Sam Hocevar's avatar
   
Sam Hocevar committed
368
369
#define f p_input->p_input_module->p_functions->input.functions.input
    p_input->pf_init          = f.pf_init;
Sam Hocevar's avatar
   
Sam Hocevar committed
370
371
372
373
374
375
376
377
    if( f.pf_open != NULL )
    {
        p_input->pf_open          = f.pf_open;
    }
    if( f.pf_close != NULL )
    {
        p_input->pf_close         = f.pf_close;
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
378
379
    p_input->pf_end           = f.pf_end;
    p_input->pf_read          = f.pf_read;
380
    p_input->pf_set_area      = f.pf_set_area;
Sam Hocevar's avatar
   
Sam Hocevar committed
381
382
383
384
385
386
387
388
389
    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 );
390

Sam Hocevar's avatar
   
Sam Hocevar committed
391
    if( p_input->b_error )
392
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
393
394
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
395
        module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
   
Sam Hocevar committed
396
        return( -1 );
397
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
398
399

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

401
402
403
404
    if( p_input->b_error )
    {
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
405
        module_Unneed( p_input->p_input_module );
406
407
408
        return( -1 );
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
409
    *p_input->pi_status = THREAD_READY;
Sam Hocevar's avatar
   
Sam Hocevar committed
410
411

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
412
413
}

414
/*****************************************************************************
415
 * ErrorThread: RunThread() error loop
416
 *****************************************************************************
417
 * This function is called when an error occured during thread main's loop.
418
 *****************************************************************************/
419
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
420
{
421
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
422
    {
423
424
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
425
426
427
    }
}

428
/*****************************************************************************
429
 * EndThread: end the input thread
430
 *****************************************************************************/
431
static void EndThread( input_thread_t * p_input )
432
{
433
    int *       pi_status;                                  /* thread status */
434

435
436
437
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
438

Sam Hocevar's avatar
   
Sam Hocevar committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
#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
455
456
457
    /* Close stream */
    p_input->pf_close( p_input );

Sam Hocevar's avatar
   
Sam Hocevar committed
458
    /* Release modules */
Sam Hocevar's avatar
   
Sam Hocevar committed
459
    module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
   
Sam Hocevar committed
460

Sam Hocevar's avatar
   
Sam Hocevar committed
461
462
463
464
465
466
467
468
469
470
471
}

/*****************************************************************************
 * 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
472

Henri Fallon's avatar
   
Henri Fallon committed
473
474
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
   
Henri Fallon committed
475
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
   
Henri Fallon committed
476
    
477
    /* Free input structure */
478
    free( p_input );
479

480
481
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
482
}
483

Sam Hocevar's avatar
Sam Hocevar committed
484
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
485
 * FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
486
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
487
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
488
{
489
    struct stat         stat_info;
Sam Hocevar's avatar
   
Sam Hocevar committed
490
491
492
    int                 i_stat;

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

Sam Hocevar's avatar
   
Sam Hocevar committed
494
495
496
    /* 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
497
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
498
499
500
501
502
503
504
505
        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
506
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
507
        else if( ( i_size > 5 )
Sam Hocevar's avatar
   
Sam Hocevar committed
508
509
510
511
512
                 && !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
513
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
514

Sam Hocevar's avatar
   
Sam Hocevar committed
515
        if( i_stat == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
516
517
518
519
520
521
        {
            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
522
523
524
525
526
527
528
529
530
531
532
    }

    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;
533
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
Sam Hocevar's avatar
   
Sam Hocevar committed
534
    }
Jean-Marc Dressler's avatar
   
Jean-Marc Dressler committed
535
    else if( S_ISFIFO(stat_info.st_mode)
Sam Hocevar's avatar
   
Sam Hocevar committed
536
#if !defined( SYS_BEOS ) && !defined( WIN32 )
Jean-Marc Dressler's avatar
   
Jean-Marc Dressler committed
537
538
539
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
Sam Hocevar's avatar
Sam Hocevar committed
540
    {
541
        p_input->stream.b_seekable = 0;
542
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
   
Benoit Steiner committed
543
544
545
    }
    else
    {
546
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
547
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
   
Sam Hocevar committed
548
                     psz_name );
Sam Hocevar's avatar
   
Sam Hocevar committed
549
550
551
        p_input->b_error = 1;
        return;
    }
552

553
    p_input->stream.p_selected_area->i_tell = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
554
555
    vlc_mutex_unlock( &p_input->stream.stream_lock );

556
    intf_WarnMsg( 1, "input: opening file `%s'", p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
557
#ifndef WIN32
Sam Hocevar's avatar
   
Sam Hocevar committed
558
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
   
Sam Hocevar committed
559
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
560
561
562
563
#else
    if( (p_input->i_handle = open( psz_name, O_BINARY
                                   /*O_NONBLOCK | O_LARGEFILE*/ )) == (-1) )
#endif
Sam Hocevar's avatar
   
Sam Hocevar committed
564
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
565
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
   
Sam Hocevar committed
566
567
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
568
569
570
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
571
572

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
573
 * FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
574
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
575
static void FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
576
{
577
    intf_WarnMsg( 1, "input: closing file `%s'", p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
578
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
579

Sam Hocevar's avatar
   
Sam Hocevar committed
580
    return;
Stéphane Borel's avatar
Stéphane Borel committed
581
}
Sam Hocevar's avatar
   
Sam Hocevar committed
582

Henri Fallon's avatar
   
Henri Fallon committed
583

584
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Henri Fallon's avatar
   
Henri Fallon committed
585
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
586
 * NetworkOpen : open a network socket 
Henri Fallon's avatar
   
Henri Fallon committed
587
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
588
static void NetworkOpen( input_thread_t * p_input )
Henri Fallon's avatar
   
Henri Fallon committed
589
{
Henri Fallon's avatar
   
Henri Fallon committed
590
    char                *psz_server = NULL;
591
    char                *psz_broadcast = NULL;
Henri Fallon's avatar
   
Henri Fallon committed
592
    int                 i_port = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
593
594
    int                 i_opt;
    struct sockaddr_in  sock;
595

Sam Hocevar's avatar
   
Sam Hocevar committed
596
597
598
599
600
601
602
603
604
605
606
#ifdef WIN32
    /* WinSock Library Init. */
    WSADATA Data;
    int i_err = WSAStartup( MAKEWORD( 1, 1 ), &Data );

    if( i_err )
    {
        intf_ErrMsg( "input: can't initiate WinSocks, error %i", i_err );
        return ;
    }
#endif
Henri Fallon's avatar
   
Henri Fallon committed
607
608
    
    /* Get the remote server */
Sam Hocevar's avatar
   
Sam Hocevar committed
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
    if( p_input->p_source != NULL )
    {
        psz_server = p_input->p_source;

        /* Skip the protocol name */
        while( *psz_server && *psz_server != ':' )
        {
            psz_server++;
        }

        /* Skip the "://" part */
        while( *psz_server && (*psz_server == ':' || *psz_server == '/') )
        {
            psz_server++;
        }

        /* Found a server name */
        if( *psz_server )
        {
            char *psz_port = psz_server;

            /* Skip the hostname part */
            while( *psz_port && *psz_port != ':' )
            {
                psz_port++;
            }

            /* Found a port name */
            if( *psz_port )
            {
                /* Replace ':' with '\0' */
                *psz_port = '\0';
                psz_port++;

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
                psz_broadcast = psz_port;
                while( *psz_broadcast && *psz_broadcast != ':' )
                {
                    psz_broadcast++;
                }

                if( *psz_broadcast )
                {
                    *psz_broadcast = '\0';
                    psz_broadcast++;
                    while( *psz_broadcast && *psz_broadcast == ':' )
                    {
                        psz_broadcast++;
                    }
                }
                else
                {
                    psz_broadcast = NULL;
                }

                /* port before broadcast address */
                if( *psz_port != ':' )
                {
                    i_port = atoi( psz_port );
                }
Sam Hocevar's avatar
   
Sam Hocevar committed
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
            }
        }
        else
        {
            psz_server = NULL;
        }
    }

    /* Check that we got a valid server */
    if( psz_server == NULL )
    {
        psz_server = main_GetPszVariable( INPUT_SERVER_VAR, 
                                          INPUT_SERVER_DEFAULT );
    }

    /* Check that we got a valid port */
    if( i_port == 0 )
Henri Fallon's avatar
   
Henri Fallon committed
685
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
686
        i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
Henri Fallon's avatar
   
Henri Fallon committed
687
    }
688
689
690
691

    if( psz_broadcast == NULL )
    {
        /* Are we broadcasting ? */
692
693
694
695
696
697
698
699
700
701
        if( main_GetIntVariable( INPUT_BROADCAST_VAR,
                                 INPUT_BROADCAST_DEFAULT ) )
        {
            psz_broadcast = main_GetPszVariable( INPUT_BCAST_ADDR_VAR,
                                                 INPUT_BCAST_ADDR_DEFAULT );
        }
        else
        {
           psz_broadcast = NULL; 
        }
702
703
704
705
706
    }

    intf_WarnMsg( 2, "input: server: %s port: %d broadcast: %s",
                     psz_server, i_port, psz_broadcast );

Henri Fallon's avatar
   
Henri Fallon committed
707
708
709
710
711
    /* 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 )
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
712
        intf_ErrMsg("input error: can't create socket : %s", strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
713
714
715
716
717
        p_input->b_error = 1;
        return;
    }

    /* We may want to reuse an already used socket */
Sam Hocevar's avatar
   
Sam Hocevar committed
718
    i_opt = 1;
Henri Fallon's avatar
   
Henri Fallon committed
719
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
Sam Hocevar's avatar
   
Sam Hocevar committed
720
                    &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
721
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
722
723
        intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
                     strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
724
725
726
727
728
729
730
        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 */
Sam Hocevar's avatar
   
Sam Hocevar committed
731
732
733
    i_opt = 0x80000;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
                    &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
734
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
735
736
        intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", 
                     strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
737
738
739
740
741
742
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build the local socket */
Sam Hocevar's avatar
   
Sam Hocevar committed
743
    if ( network_BuildLocalAddr( &sock, i_port, psz_broadcast ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
744
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
745
        intf_ErrMsg( "input error: can't build local address" );
Henri Fallon's avatar
   
Henri Fallon committed
746
747
748
749
750
751
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
    
    /* Bind it */
Sam Hocevar's avatar
   
Sam Hocevar committed
752
753
    if( bind( p_input->i_handle, (struct sockaddr *)&sock, 
              sizeof( sock ) ) < 0 )
Henri Fallon's avatar
   
Henri Fallon committed
754
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
755
        intf_ErrMsg("input error: can't bind socket (%s)", strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
756
757
758
759
760
761
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build socket for remote connection */
Sam Hocevar's avatar
   
Sam Hocevar committed
762
    if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
763
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
764
        intf_ErrMsg( "input error: can't build remote address" );
Henri Fallon's avatar
   
Henri Fallon committed
765
766
767
768
769
770
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* And connect it ... should we really connect ? */
Sam Hocevar's avatar
   
Sam Hocevar committed
771
772
    if( connect( p_input->i_handle, (struct sockaddr *) &sock,
                 sizeof( sock ) ) == (-1) )
Henri Fallon's avatar
   
Henri Fallon committed
773
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
774
        intf_ErrMsg( "input error: can't connect socket, %s", 
Henri Fallon's avatar
   
Henri Fallon committed
775
                     strerror(errno) );
Henri Fallon's avatar
   
Henri Fallon committed
776
777
778
779
780
781
782
        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. */
Henri Fallon's avatar
   
Henri Fallon committed
783
    p_input->stream.b_pace_control = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
784
    p_input->stream.b_seekable = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
785
786

    intf_WarnMsg( 3, "input: successfully opened network mode" );
Henri Fallon's avatar
   
Henri Fallon committed
787
788
789
790
791
    
    return;
}

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
792
 * NetworkClose : close a network socket
Henri Fallon's avatar
   
Henri Fallon committed
793
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
794
static void NetworkClose( input_thread_t * p_input )
Henri Fallon's avatar
   
Henri Fallon committed
795
796
{
    close( p_input->i_handle );
797

Sam Hocevar's avatar
   
Sam Hocevar committed
798
799
800
#ifdef WIN32 
    WSACleanup();
#endif
801

Henri Fallon's avatar
   
Henri Fallon committed
802
}
Sam Hocevar's avatar
   
Sam Hocevar committed
803
#endif
Sam Hocevar's avatar
   
Sam Hocevar committed
804