input.c 24.9 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.115 2001/05/31 01:37:08 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
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
35
36

#ifdef HAVE_UNISTD_H
37
#include <unistd.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
38
39
40
41
#elif defined( _MSC_VER ) && defined( _WIN32 )
#include <io.h>
#endif

42
#include <string.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
43
44
45
#ifdef STRNCASECMP_IN_STRINGS_H
#   include <strings.h>
#endif
46
#include <errno.h>
Michel Kaempf's avatar
Michel Kaempf committed
47

48
49
50
51
52
53
54
/* WinSock Includes */

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


Henri Fallon's avatar
   
Henri Fallon committed
55
56
/* Network functions */

Sam Hocevar's avatar
   
Sam Hocevar committed
57
58
#if !defined( SYS_BEOS ) && !defined( SYS_NTO ) && !defined( WIN32 )
#include <netdb.h>                                            /* hostent ... */
Henri Fallon's avatar
   
Henri Fallon committed
59
60
61
62
63
#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
64
#endif
Henri Fallon's avatar
   
Henri Fallon committed
65

66
67
68
69
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
70
#include "config.h"
71
72
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
73
#include "mtime.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
74
#include "netutils.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
75
#include "modules.h"
76

77
#include "intf_msg.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
78
#include "intf_playlist.h"
79

80
81
82
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,    
Michel Lespinasse committed
83

84
#include "input.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
85
86
87
#include "interface.h"

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

Henri Fallon's avatar
   
Henri Fallon committed
89

90
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
91
 * Local prototypes
92
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
93
94
95
96
97
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
98

Sam Hocevar's avatar
   
Sam Hocevar committed
99
100
101
102
103
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 );

104
/*****************************************************************************
105
 * input_CreateThread: creates a new input thread
106
 *****************************************************************************
107
108
109
110
 * 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.
111
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
112
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
113
{
114
115
116
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

117
118
119
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
120
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
121
122
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
123
124
        return( NULL );
    }
125

126
127
128
    /* Packets read once */
    p_input->i_read_once = INPUT_READ_ONCE;

129
130
131
    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
132
133
134
135
136
    p_input->b_eof              = 0;

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

137
    /* I have never understood that stuff --Meuuh */
138
139
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
140

141
    /* Initialize stream description */
142
143
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
144
    p_input->stream.i_pgrm_number = 0;
145
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
Christophe Massiot's avatar
Christophe Massiot committed
146
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
147

148
    /* no stream, no area */
Stéphane Borel's avatar
   
Stéphane Borel committed
149
150
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
151
    p_input->stream.p_selected_area = NULL;
152
    p_input->stream.p_new_area = NULL;
Stéphane Borel's avatar
   
Stéphane Borel committed
153
154
    /* By default there is one areas in a stream */
    input_AddArea( p_input );
155
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
Stéphane Borel's avatar
   
Stéphane Borel committed
156

157
158
159
160
161
    /* 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
162

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

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

200
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
201
 * input_DestroyThread: mark an input thread as zombie
202
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
203
 * This function should not return until the thread is effectively cancelled.
204
 *****************************************************************************/
205
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
206
{
207
    int         i_status;                                   /* thread status */
208
209
210

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

213
214
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
215

216
217
218
219
220
    /* 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 );

221
222
223
224
225
226
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
227
228
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
229
    }
Michel Kaempf's avatar
Michel Kaempf committed
230
231
}

232
/*****************************************************************************
233
 * RunThread: main thread loop
234
 *****************************************************************************
235
 * Thread in charge of processing the network packets and demultiplexing.
236
 *****************************************************************************/
237
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
238
{
239
    int                     i_error, i;
Sam Hocevar's avatar
   
Sam Hocevar committed
240
    data_packet_t **        pp_packets;
Michel Kaempf's avatar
Michel Kaempf committed
241

Sam Hocevar's avatar
   
Sam Hocevar committed
242
243
244
245
246
247
248
249
250
251
    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
252

253
254
255
256
257
    /* 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
258
259
260
    pp_packets = (data_packet_t **) malloc( p_input->i_read_once *
                                        sizeof( data_packet_t * ) );
    if( pp_packets == NULL )
261
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
262
263
264
        intf_ErrMsg( "input error: out of memory" );
        p_input->b_error = 1;
    }
265

Sam Hocevar's avatar
   
Sam Hocevar committed
266
267
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
268
#ifdef STATS
Sam Hocevar's avatar
   
Sam Hocevar committed
269
        p_input->c_loops++;
Sam Hocevar's avatar
   
Sam Hocevar committed
270
271
#endif

272
        vlc_mutex_lock( &p_input->stream.stream_lock );
273

274
275
276
277
278
279
        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;
        }

280
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
Sam Hocevar's avatar
   
Sam Hocevar committed
281
        {
282
283
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
284
285
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
286
287
288
289
290
291
292
293
294
295
296
297
298

                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;
                }
            }
299
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
Sam Hocevar's avatar
   
Sam Hocevar committed
300
        }
301

302
303
304
305
306
307
308
309
310
311
312
313
        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;
        }

314
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
315
316

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

Sam Hocevar's avatar
   
Sam Hocevar committed
318
        /* Demultiplex read packets. */
319
        for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ )
Sam Hocevar's avatar
   
Sam Hocevar committed
320
321
322
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
323

Sam Hocevar's avatar
   
Sam Hocevar committed
324
325
326
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
327
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
328
329
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
330
                intf_WarnMsg( 3, "input: EOF reached" );
Sam Hocevar's avatar
   
Sam Hocevar committed
331
                p_input->b_eof = 1;
Sam Hocevar's avatar
   
Sam Hocevar committed
332
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
333
            else
Sam Hocevar's avatar
   
Sam Hocevar committed
334
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
335
                p_input->b_error = 1;
Sam Hocevar's avatar
Sam Hocevar committed
336
            }
337
338
339
        }
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
340
341
    free( pp_packets );

Sam Hocevar's avatar
   
Sam Hocevar committed
342
    if( p_input->b_error || p_input->b_eof )
343
344
345
    {
        ErrorThread( p_input );
    }
346

347
    EndThread( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
348
349
350

    DestroyThread( p_input );

Sam Hocevar's avatar
   
Sam Hocevar committed
351
    intf_DbgMsg("input: Thread end");
352
353
}

354
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
355
 * InitThread: init the input Thread
356
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
357
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
358
359
360
{

#ifdef STATS
361
362
363
364
365
366
    /* 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
367
#endif
Sam Hocevar's avatar
Sam Hocevar committed
368

Sam Hocevar's avatar
   
Sam Hocevar committed
369
370
371
372
    /* 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
373
    p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT,
Sam Hocevar's avatar
   
Sam Hocevar committed
374
                                           (probedata_t *)p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
375
376

    if( p_input->p_input_module == NULL )
377
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
378
379
        intf_ErrMsg( "input error: no suitable input module for `%s'",
                     p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
380
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
381
    }
382

Sam Hocevar's avatar
   
Sam Hocevar committed
383
384
#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
385
386
387
388
389
390
391
392
    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
393
394
    p_input->pf_end           = f.pf_end;
    p_input->pf_read          = f.pf_read;
395
    p_input->pf_set_area      = f.pf_set_area;
Sam Hocevar's avatar
   
Sam Hocevar committed
396
397
398
399
400
401
402
403
404
    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 );
405

Sam Hocevar's avatar
   
Sam Hocevar committed
406
    if( p_input->b_error )
407
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
408
409
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
410
        module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
   
Sam Hocevar committed
411
        return( -1 );
412
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
413
414

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

416
417
418
419
    if( p_input->b_error )
    {
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
420
        module_Unneed( p_input->p_input_module );
421
422
423
        return( -1 );
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
424
    *p_input->pi_status = THREAD_READY;
Sam Hocevar's avatar
   
Sam Hocevar committed
425
426

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
427
428
}

429
/*****************************************************************************
430
 * ErrorThread: RunThread() error loop
431
 *****************************************************************************
432
 * This function is called when an error occured during thread main's loop.
433
 *****************************************************************************/
434
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
435
{
436
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
437
    {
438
439
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
440
441
442
    }
}

443
/*****************************************************************************
444
 * EndThread: end the input thread
445
 *****************************************************************************/
446
static void EndThread( input_thread_t * p_input )
447
{
448
    int *       pi_status;                                  /* thread status */
449

450
451
452
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
453

Sam Hocevar's avatar
   
Sam Hocevar committed
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
#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
470
471
472
    /* Close stream */
    p_input->pf_close( p_input );

Sam Hocevar's avatar
   
Sam Hocevar committed
473
    /* Release modules */
Sam Hocevar's avatar
   
Sam Hocevar committed
474
    module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
   
Sam Hocevar committed
475

Sam Hocevar's avatar
   
Sam Hocevar committed
476
477
478
479
480
481
482
483
484
485
486
}

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

Henri Fallon's avatar
   
Henri Fallon committed
488
489
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
   
Henri Fallon committed
490
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
   
Henri Fallon committed
491
    
492
    /* Free input structure */
493
    free( p_input );
494

495
496
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
497
}
498

Sam Hocevar's avatar
Sam Hocevar committed
499
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
500
 * FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
501
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
502
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
503
{
504
    struct stat         stat_info;
Sam Hocevar's avatar
   
Sam Hocevar committed
505
506
507
    int                 i_stat;

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

Sam Hocevar's avatar
   
Sam Hocevar committed
509
510
511
    /* 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
512
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
513
514
515
516
517
518
519
520
        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
521
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
522
        else if( ( i_size > 5 )
Sam Hocevar's avatar
   
Sam Hocevar committed
523
524
525
526
527
                 && !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
528
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
529

Sam Hocevar's avatar
   
Sam Hocevar committed
530
        if( i_stat == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
531
532
533
534
535
536
        {
            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
537
538
539
540
541
542
543
544
545
546
547
    }

    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;
548
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
Sam Hocevar's avatar
   
Sam Hocevar committed
549
    }
Jean-Marc Dressler's avatar
   
Jean-Marc Dressler committed
550
    else if( S_ISFIFO(stat_info.st_mode)
Sam Hocevar's avatar
   
Sam Hocevar committed
551
#if !defined( SYS_BEOS ) && !defined( WIN32 )
Jean-Marc Dressler's avatar
   
Jean-Marc Dressler committed
552
553
554
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
Sam Hocevar's avatar
Sam Hocevar committed
555
    {
556
        p_input->stream.b_seekable = 0;
557
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
   
Benoit Steiner committed
558
559
560
    }
    else
    {
561
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
562
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
   
Sam Hocevar committed
563
                     psz_name );
Sam Hocevar's avatar
   
Sam Hocevar committed
564
565
566
        p_input->b_error = 1;
        return;
    }
567

568
    p_input->stream.p_selected_area->i_tell = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
569
570
    vlc_mutex_unlock( &p_input->stream.stream_lock );

571
    intf_WarnMsg( 1, "input: opening file `%s'", p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
572
#ifndef WIN32
Sam Hocevar's avatar
   
Sam Hocevar committed
573
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
   
Sam Hocevar committed
574
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
575
576
577
578
#else
    if( (p_input->i_handle = open( psz_name, O_BINARY
                                   /*O_NONBLOCK | O_LARGEFILE*/ )) == (-1) )
#endif
Sam Hocevar's avatar
   
Sam Hocevar committed
579
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
580
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
   
Sam Hocevar committed
581
582
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
583
584
585
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
586
587

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
588
 * FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
589
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
590
static void FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
591
{
592
    intf_WarnMsg( 1, "input: closing file `%s'", p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
593
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
594

Sam Hocevar's avatar
   
Sam Hocevar committed
595
    return;
Stéphane Borel's avatar
Stéphane Borel committed
596
}
Sam Hocevar's avatar
   
Sam Hocevar committed
597

Henri Fallon's avatar
   
Henri Fallon committed
598

599
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Henri Fallon's avatar
   
Henri Fallon committed
600
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
601
 * NetworkOpen : open a network socket 
Henri Fallon's avatar
   
Henri Fallon committed
602
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
603
static void NetworkOpen( input_thread_t * p_input )
Henri Fallon's avatar
   
Henri Fallon committed
604
{
Henri Fallon's avatar
   
Henri Fallon committed
605
    char                *psz_server = NULL;
606
    char                *psz_broadcast = NULL;
Henri Fallon's avatar
   
Henri Fallon committed
607
    int                 i_port = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
608
609
    int                 i_opt;
    struct sockaddr_in  sock;
610

Sam Hocevar's avatar
   
Sam Hocevar committed
611
612
613
614
615
616
617
618
619
620
621
#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
622
623
    
    /* Get the remote server */
Sam Hocevar's avatar
   
Sam Hocevar committed
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
    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++;

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
                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
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
            }
        }
        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
700
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
701
        i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
Henri Fallon's avatar
   
Henri Fallon committed
702
    }
703
704
705
706

    if( psz_broadcast == NULL )
    {
        /* Are we broadcasting ? */
707
708
709
710
711
712
713
714
715
716
        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; 
        }
717
718
719
720
721
    }

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

Henri Fallon's avatar
   
Henri Fallon committed
722
723
724
725
726
    /* 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
727
        intf_ErrMsg("input error: can't create socket : %s", strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
728
729
730
731
732
        p_input->b_error = 1;
        return;
    }

    /* We may want to reuse an already used socket */
Sam Hocevar's avatar
   
Sam Hocevar committed
733
    i_opt = 1;
Henri Fallon's avatar
   
Henri Fallon committed
734
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
Sam Hocevar's avatar
   
Sam Hocevar committed
735
                    (void*) &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
736
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
737
738
        intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
                     strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
739
740
741
742
743
744
745
        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
746
747
    i_opt = 0x80000;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
Sam Hocevar's avatar
   
Sam Hocevar committed
748
                    (void*) &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
749
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
750
751
        intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", 
                     strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
752
753
754
755
756
757
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build the local socket */
Sam Hocevar's avatar
   
Sam Hocevar committed
758
    if ( network_BuildLocalAddr( &sock, i_port, psz_broadcast ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
759
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
760
        intf_ErrMsg( "input error: can't build local address" );
Henri Fallon's avatar
   
Henri Fallon committed
761
762
763
764
765
766
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
    
    /* Bind it */
Sam Hocevar's avatar
   
Sam Hocevar committed
767
768
    if( bind( p_input->i_handle, (struct sockaddr *)&sock, 
              sizeof( sock ) ) < 0 )
Henri Fallon's avatar
   
Henri Fallon committed
769
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
770
        intf_ErrMsg("input error: can't bind socket (%s)", strerror(errno));
Henri Fallon's avatar
   
Henri Fallon committed
771
772
773
774
775
776
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build socket for remote connection */
Sam Hocevar's avatar
   
Sam Hocevar committed
777
    if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 )
Henri Fallon's avatar
   
Henri Fallon committed
778
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
779
        intf_ErrMsg( "input error: can't build remote address" );
Henri Fallon's avatar
   
Henri Fallon committed
780
781
782
783
784
785
        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
786
787
    if( connect( p_input->i_handle, (struct sockaddr *) &sock,
                 sizeof( sock ) ) == (-1) )
Henri Fallon's avatar
   
Henri Fallon committed
788
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
789
        intf_ErrMsg( "input error: can't connect socket, %s", 
Henri Fallon's avatar
   
Henri Fallon committed
790
                     strerror(errno) );
Henri Fallon's avatar
   
Henri Fallon committed
791
792
793
794
795
796
797
        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
798
    p_input->stream.b_pace_control = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
799
    p_input->stream.b_seekable = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
800
801

    intf_WarnMsg( 3, "input: successfully opened network mode" );
Henri Fallon's avatar
   
Henri Fallon committed
802
803
804
805
806
    
    return;
}

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
807
 * NetworkClose : close a network socket
Henri Fallon's avatar
   
Henri Fallon committed
808
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
809
static void NetworkClose( input_thread_t * p_input )
Henri Fallon's avatar
   
Henri Fallon committed
810
811
{
    close( p_input->i_handle );
812

Sam Hocevar's avatar
   
Sam Hocevar committed
813
814
815
#ifdef WIN32 
    WSACleanup();
#endif
816

Henri Fallon's avatar
   
Henri Fallon committed
817
}
Sam Hocevar's avatar
   
Sam Hocevar committed
818
#endif
Sam Hocevar's avatar
   
Sam Hocevar committed
819