input.c 14.3 KB
Newer Older
1
2
/*****************************************************************************
 * input.c: input thread
Michel Kaempf's avatar
Michel Kaempf committed
3
4
 * Read an MPEG2 stream, demultiplex and parse it before sending it to
 * decoders.
5
6
 *****************************************************************************
 * Copyright (C) 1998, 1999, 2000 VideoLAN
Sam Hocevar's avatar
   
Sam Hocevar committed
7
 * $Id: input.c,v 1.69 2001/01/15 06:18:23 sam Exp $
8
 *
9
 * Authors: 
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
41
42
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
43
#include "config.h"
44
45
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
46
#include "mtime.h"
47

48
#include "intf_msg.h"
49

50
51
52
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,    
Michel Lespinasse committed
53

54
#include "input.h"
Michel Kaempf's avatar
Michel Kaempf committed
55

56
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
57
 * Local prototypes
58
 *****************************************************************************/
59
static void RunThread   ( input_thread_t *p_input );
60
static void InitThread  ( input_thread_t *p_input );
61
62
static void ErrorThread ( input_thread_t *p_input );
static void EndThread   ( input_thread_t *p_input );
63
64
static void NetworkOpen ( input_thread_t *p_input );
static void FileOpen    ( input_thread_t *p_input );
Stéphane Borel's avatar
Stéphane Borel committed
65
static void DvdOpen     ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
66

67
/*****************************************************************************
68
 * input_CreateThread: creates a new input thread
69
 *****************************************************************************
70
71
72
73
 * 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.
74
 *****************************************************************************/
75
input_thread_t *input_CreateThread ( input_config_t * p_config, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
76
{
77
78
79
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

80
81
82
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
83
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
84
85
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
86
        free( p_config );
Michel Kaempf's avatar
Michel Kaempf committed
87
88
        return( NULL );
    }
89
90
91
92

    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
93
    /* I have never understood that stuff --Meuuh */
94
95
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
96
    p_input->p_config = p_config;
Michel Kaempf's avatar
Michel Kaempf committed
97

98
    /* Initialize stream description */
99
100
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
101
    p_input->stream.i_pgrm_number = 0;
Michel Kaempf's avatar
Michel Kaempf committed
102

103
104
105
106
107
108
109
    /* Initialize stream control properties. */
    p_input->stream.control.i_status = PLAYING_S;
    p_input->stream.control.i_rate = DEFAULT_RATE;
    p_input->stream.control.i_ref_sysdate = 0;
    p_input->stream.control.i_ref_clock = 0;
    p_input->stream.control.b_mute = 0;
    p_input->stream.control.b_bw = 0;
Michel Kaempf's avatar
Michel Kaempf committed
110
111

    /* Create thread and set locks. */
112
113
114
115
    vlc_mutex_init( &p_input->stream.stream_lock );
    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
116
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
117
118
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
119
        free( p_input );
120
        free( p_config );
Michel Kaempf's avatar
Michel Kaempf committed
121
122
        return( NULL );
    }
123

124
125
126
127
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
128
        {
129
            msleep( THREAD_SLEEP );
130
        } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
131
132
133
                && (i_status != THREAD_FATAL) );
        if( i_status != THREAD_READY )
        {
134
135
            return( NULL );
        }
136
    }
Michel Kaempf's avatar
Michel Kaempf committed
137
138
139
    return( p_input );
}

140
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
141
 * input_DestroyThread: mark an input thread as zombie
142
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
143
 * This function should not return until the thread is effectively cancelled.
144
 *****************************************************************************/
145
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
146
{
147
    int         i_status;                                   /* thread status */
148
149
150

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

153
154
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
155

156
157
158
159
160
161
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
162
163
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
164
    }
Michel Kaempf's avatar
Michel Kaempf committed
165
166
}

167
/*****************************************************************************
168
 * RunThread: main thread loop
169
 *****************************************************************************
170
 * Thread in charge of processing the network packets and demultiplexing.
171
 *****************************************************************************/
172
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
173
{
174
175
    data_packet_t *         pp_packets[INPUT_READ_ONCE];
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
176

177
    InitThread( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
178

179
180
181
182
    while( !p_input->b_die && !p_input->b_error )
    {
#ifdef STATS
        p_input->c_loops++;
Michel Kaempf's avatar
Michel Kaempf committed
183
184
#endif

185
186
187
        vlc_mutex_lock( &p_input->stream.control.control_lock );
        if( p_input->stream.control.i_status == BACKWARD_S
             && p_input->p_plugin->pf_rewind != NULL )
Sam Hocevar's avatar
Sam Hocevar committed
188
        {
189
190
            p_input->p_plugin->pf_rewind( p_input );
            /* FIXME: probably don't do it every loop, but when ? */
Michel Kaempf's avatar
Michel Kaempf committed
191
        }
192
        vlc_mutex_unlock( &p_input->stream.control.control_lock );
Sam Hocevar's avatar
Sam Hocevar committed
193

194
195
196
197
        i_error = p_input->p_plugin->pf_read( p_input, pp_packets );

        /* Demultiplex read packets. */
        for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
Sam Hocevar's avatar
Sam Hocevar committed
198
        {
199
200
            p_input->p_plugin->pf_demux( p_input, pp_packets[i] );
        }
201

202
203
204
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
205
            {
206
207
208
                /* End of file */
                intf_WarnMsg( 1, "End of file reached" );
                /* FIXME: don't treat that as an error */
Sam Hocevar's avatar
Sam Hocevar committed
209
            }
210
            p_input->b_error = 1;
211
212
213
        }
    }

214
215
216
217
    if( p_input->b_error )
    {
        ErrorThread( p_input );
    }
218

219
220
    EndThread( p_input );
    intf_DbgMsg("Thread end");
221
222
}

223
/*****************************************************************************
224
 * InitThread: init the input thread
225
 *****************************************************************************/
226
input_capabilities_t * PSKludge( void );
Stéphane Borel's avatar
Stéphane Borel committed
227
input_capabilities_t * DVDKludge( void );
228
static void InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
229
{
230
231
232
    /* Initialize default settings for spawned decoders */
    p_input->p_default_aout     = p_input->p_config->p_default_aout;
    p_input->p_default_vout     = p_input->p_config->p_default_vout;
Michel Kaempf's avatar
Michel Kaempf committed
233
234

#ifdef STATS
235
236
237
238
239
240
    /* 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
241
#endif
Sam Hocevar's avatar
Sam Hocevar committed
242

243
244
245
246
247
    /* Use the appropriate input method */
    switch( p_input->p_config->i_method )
    {
    case INPUT_METHOD_FILE:                                  /* file methods */
        FileOpen( p_input );
Stéphane Borel's avatar
Stéphane Borel committed
248
249
250
251
252
253
254
        /* Probe plugin (FIXME: load plugins before & write this) */
        p_input->p_plugin = PSKludge();
       break;
    case INPUT_METHOD_DVD:                                     /* DVD method */
        DvdOpen( p_input );
        /* DVD plugin */
        p_input->p_plugin = DVDKludge();
255
256
257
        break;
    case INPUT_METHOD_VLAN_BCAST:                     /* vlan network method */
/*        if( !p_main->b_vlans )
Michel Kaempf's avatar
Michel Kaempf committed
258
        {
Sam Hocevar's avatar
   
Sam Hocevar committed
259
            intf_ErrMsg("input error: vlans are not activated");
260
261
262
263
264
265
266
267
268
269
270
            free( p_input );
            return( NULL );
        } */ /* la-lala */
        /* ... pass through */
    case INPUT_METHOD_UCAST:                              /* network methods */
    case INPUT_METHOD_MCAST:
    case INPUT_METHOD_BCAST:
        NetworkOpen( p_input );
        break;
#ifdef DEBUG
    default:
Sam Hocevar's avatar
   
Sam Hocevar committed
271
272
        intf_ErrMsg( "input error: unknow method 0x%.4x",
                     p_input->p_config->i_method );
273
274
275
        free( p_input->p_config );
        p_input->b_error = 1;
        break;
Sam Hocevar's avatar
Sam Hocevar committed
276
#endif
Michel Kaempf's avatar
Michel Kaempf committed
277
    }
278
279
280
281
282
283

    free( p_input->p_config );

    p_input->p_plugin->pf_init( p_input );

    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
284
285
}

286
/*****************************************************************************
287
 * ErrorThread: RunThread() error loop
288
 *****************************************************************************
289
 * This function is called when an error occured during thread main's loop.
290
 *****************************************************************************/
291
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
292
{
293
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
294
    {
295
296
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
297
298
299
    }
}

300
/*****************************************************************************
301
 * EndThread: end the input thread
302
 *****************************************************************************/
303
static void EndThread( input_thread_t * p_input )
304
{
305
    int *       pi_status;                                  /* thread status */
306

307
308
309
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
310

311
312
313
314
315
#ifdef STATS
    {
        struct tms cpu_usage;
        times( &cpu_usage );

Sam Hocevar's avatar
   
Sam Hocevar committed
316
        intf_Msg("input stats: cpu usage (user: %d, system: %d)",
317
318
319
320
                 cpu_usage.tms_utime, cpu_usage.tms_stime);
    }
#endif

321
322
    /* Free all ES and destroy all decoder threads */
    input_EndStream( p_input );
Sam Hocevar's avatar
Sam Hocevar committed
323

324
    /* Free demultiplexer's data */
325
326
327
    p_input->p_plugin->pf_end( p_input );
    free( p_input->p_plugin );

Henri Fallon's avatar
   
Henri Fallon committed
328
329
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
   
Henri Fallon committed
330
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
   
Henri Fallon committed
331
    
332
    /* Free input structure */
333
    free( p_input );
334

335
336
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
337
}
338

Sam Hocevar's avatar
Sam Hocevar committed
339
/*****************************************************************************
340
 * NetworkOpen : open a network socket descriptor
Sam Hocevar's avatar
Sam Hocevar committed
341
 *****************************************************************************/
342
static void NetworkOpen( input_thread_t * p_input )
Sam Hocevar's avatar
Sam Hocevar committed
343
{
344
    /* straight copy & paste of input_network.c of input-I */
345

346
347
348
349
    /* We cannot rewind nor lseek() */
    p_input->stream.b_seekable = 0;
    /* We cannot control the pace */
    p_input->stream.b_pace_control = 0;
350
351
}

352
/*****************************************************************************
353
 * FileOpen : open a file descriptor
354
 *****************************************************************************/
355
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
356
{
357
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
358

359
#define p_config    p_input->p_config
Michel Kaempf's avatar
Michel Kaempf committed
360

Sam Hocevar's avatar
   
Sam Hocevar committed
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
    if( stat( p_config->p_source, &stat_info ) == (-1) )
    {
        intf_ErrMsg( "input error: cannot stat() file %s (%s)",
                     p_config->p_source, strerror(errno));
        p_input->b_error = 1;
        return;
    }

    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;
        p_input->stream.i_size = stat_info.st_size;
    }
    else if( S_ISFIFO(stat_info.st_mode) || S_ISSOCK(stat_info.st_mode) )
Sam Hocevar's avatar
Sam Hocevar committed
381
    {
382
383
        p_input->stream.b_seekable = 0;
        p_input->stream.i_size = 0;
Benoit Steiner's avatar
   
Benoit Steiner committed
384
385
386
    }
    else
    {
387
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
388
389
390
391
392
        intf_ErrMsg( "input error: unknown file type for %s",
                     p_config->p_source );
        p_input->b_error = 1;
        return;
    }
393

Sam Hocevar's avatar
   
Sam Hocevar committed
394
395
396
    p_input->stream.i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
   
Sam Hocevar committed
397
    intf_Msg( "input: opening file %s", p_config->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
398
399
400
401
402
403
    if( (p_input->i_handle = open( p_config->p_source,
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
        intf_ErrMsg( "input error: cannot open file %s", strerror(errno) );
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
404
405
    }

406
#undef p_config
Michel Kaempf's avatar
Michel Kaempf committed
407
}
Stéphane Borel's avatar
Stéphane Borel committed
408
409
410
411
412
413

/*****************************************************************************
 * DvdOpen : open the dvd device
 *****************************************************************************/
static void DvdOpen( input_thread_t * p_input )
{
Sam Hocevar's avatar
   
Sam Hocevar committed
414
    intf_Msg( "input: opening DVD %s", p_input->p_config->p_source );
Stéphane Borel's avatar
Stéphane Borel committed
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
    if( (p_input->i_handle = open( p_input->p_config->p_source,
                                   O_RDONLY|O_LARGEFILE )) == (-1) )
    {
        intf_ErrMsg( "input error: cannot open device %s", strerror(errno) );
        p_input->b_error = 1;
        return;
    }

    vlc_mutex_lock( &p_input->stream.stream_lock );

    p_input->stream.b_pace_control = 1;
    p_input->stream.b_seekable = 1;
    p_input->stream.i_size = 0;
    p_input->stream.i_tell = 0;

    vlc_mutex_unlock( &p_input->stream.stream_lock );
}