input.c 13.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.75 2001/02/08 04:43:27 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"
Sam Hocevar's avatar
   
Sam Hocevar committed
47
#include "modules.h"
48

49
#include "intf_msg.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
50
#include "intf_plst.h"
51

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

56
#include "input.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
57
58
59
#include "interface.h"

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

61
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
62
 * Local prototypes
63
 *****************************************************************************/
64
static void RunThread   ( input_thread_t *p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
65
66
static void InitLoop    ( input_thread_t *p_input );
static void StopLoop    ( input_thread_t *p_input );
67
68
static void ErrorThread ( input_thread_t *p_input );
static void EndThread   ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
69

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

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

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

99
    /* Initialize stream description */
100
101
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
102
    p_input->stream.i_pgrm_number = 0;
103
104
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
    p_input->stream.i_seek = 0;
Michel Kaempf's avatar
Michel Kaempf committed
105

106
107
108
109
110
    /* 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
111

Sam Hocevar's avatar
   
Sam Hocevar committed
112
113
114
115
    /* Initialize default settings for spawned decoders */
    p_input->p_default_aout = p_main->p_aout;
    p_input->p_default_vout = p_main->p_intf->p_vout;

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

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

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

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

157
158
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
159

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

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

Sam Hocevar's avatar
   
Sam Hocevar committed
181
    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
182

183
184
    while( !p_input->b_die && !p_input->b_error )
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
185
        InitLoop( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
186

Sam Hocevar's avatar
   
Sam Hocevar committed
187
        if( p_input->b_die || p_input->b_error )
Sam Hocevar's avatar
Sam Hocevar committed
188
        {
Sam Hocevar's avatar
   
Sam Hocevar committed
189
            break;
Michel Kaempf's avatar
Michel Kaempf committed
190
        }
Sam Hocevar's avatar
Sam Hocevar committed
191

Sam Hocevar's avatar
   
Sam Hocevar committed
192
        while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
Sam Hocevar's avatar
Sam Hocevar committed
193
        {
194

Sam Hocevar's avatar
   
Sam Hocevar committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#ifdef STATS
            p_input->c_loops++;
#endif

            vlc_mutex_lock( &p_input->stream.control.control_lock );
            if( p_input->stream.control.i_status == BACKWARD_S
                 && p_input->pf_rewind != NULL )
            {
                p_input->pf_rewind( p_input );
                /* FIXME: probably don't do it every loop, but when ? */
            }
            vlc_mutex_unlock( &p_input->stream.control.control_lock );

            i_error = p_input->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
212
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
                p_input->pf_demux( p_input, pp_packets[i] );
            }

            if( i_error )
            {
                if( i_error == 1 )
                {
                    /* End of file */
                    intf_WarnMsg( 1, "End of file reached" );
                    /* FIXME: don't treat that as an error */
                    p_input->b_eof = 1;
                }
                else
                {
                    p_input->b_error = 1;
                }
Sam Hocevar's avatar
Sam Hocevar committed
229
            }
230
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
231
232
233
234
235

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

        StopLoop( p_input );
236
237
    }

238
239
240
241
    if( p_input->b_error )
    {
        ErrorThread( p_input );
    }
242

243
244
    EndThread( p_input );
    intf_DbgMsg("Thread end");
245
246
}

247
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
248
 * InitLoop: init the input loop
249
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
250
static void InitLoop( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
251
{
Sam Hocevar's avatar
   
Sam Hocevar committed
252
253
254
255
256
257
258
259
260
261
262
263
    playlist_Next( p_main->p_playlist );

    if( p_main->p_playlist->i_index == -1 )
    {
        /*    FIXME: wait for user to add stuff to playlist ? */
        /* FIXME II: we shouldn't set b_error but rather b_die */
        intf_Msg( "playlist: end" );
        p_input->b_error = 1;
        return;
    }

    p_input->p_source = p_main->p_playlist->current.psz_name;
Michel Kaempf's avatar
Michel Kaempf committed
264
265

#ifdef STATS
266
267
268
269
270
271
    /* 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
272
#endif
Sam Hocevar's avatar
Sam Hocevar committed
273

Sam Hocevar's avatar
   
Sam Hocevar committed
274
275
276
277
    p_input->p_input_module = module_Need( p_main->p_module_bank,
                                           MODULE_CAPABILITY_INPUT, NULL );

    if( p_input->p_input_module == NULL )
278
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
279
        intf_ErrMsg( "input error: no suitable input module" );
280
        p_input->b_error = 1;
Sam Hocevar's avatar
   
Sam Hocevar committed
281
        return;
Michel Kaempf's avatar
Michel Kaempf committed
282
    }
283

Sam Hocevar's avatar
   
Sam Hocevar committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#define f p_input->p_input_module->p_functions->input.functions.input
    p_input->pf_init          = f.pf_init;
    p_input->pf_open          = f.pf_open;
    p_input->pf_close         = f.pf_close;
    p_input->pf_end           = f.pf_end;
    p_input->pf_read          = f.pf_read;
    p_input->pf_demux         = f.pf_demux;
    p_input->pf_new_packet    = f.pf_new_packet;
    p_input->pf_new_pes       = f.pf_new_pes;
    p_input->pf_delete_packet = f.pf_delete_packet;
    p_input->pf_delete_pes    = f.pf_delete_pes;
    p_input->pf_rewind        = f.pf_rewind;
    p_input->pf_seek          = f.pf_seek;
#undef f

    p_input->b_eof = 0;
    p_input->pf_open( p_input );
301

Sam Hocevar's avatar
   
Sam Hocevar committed
302
    if( p_input->b_error )
303
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
304
305
        module_Unneed( p_main->p_module_bank, p_input->p_input_module );
        return;
306
    }
307

Sam Hocevar's avatar
   
Sam Hocevar committed
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    p_input->pf_init( p_input );

    return;
}

/*****************************************************************************
 * StopLoop: stop the input loop
 *****************************************************************************/
static void StopLoop( input_thread_t * p_input )
{
#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 demultiplexer's data */
    p_input->pf_end( p_input );

    /* Release modules */
    module_Unneed( p_main->p_module_bank, p_input->p_input_module );
Michel Kaempf's avatar
Michel Kaempf committed
333
334
}

335
/*****************************************************************************
336
 * ErrorThread: RunThread() error loop
337
 *****************************************************************************
338
 * This function is called when an error occured during thread main's loop.
339
 *****************************************************************************/
340
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
341
{
342
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
343
    {
344
345
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
346
347
348
    }
}

349
/*****************************************************************************
350
 * EndThread: end the input thread
351
 *****************************************************************************/
352
static void EndThread( input_thread_t * p_input )
353
{
354
    int *       pi_status;                                  /* thread status */
355

356
357
358
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
359

Henri Fallon's avatar
   
Henri Fallon committed
360
361
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
   
Henri Fallon committed
362
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
   
Henri Fallon committed
363
    
364
    /* Free input structure */
365
    free( p_input );
366

367
368
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
369
}
370

Sam Hocevar's avatar
Sam Hocevar committed
371
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
372
 * input_FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
373
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
374
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
375
{
376
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
377

Sam Hocevar's avatar
   
Sam Hocevar committed
378
    if( stat( p_input->p_source, &stat_info ) == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
379
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
380
        intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
Sam Hocevar's avatar
   
Sam Hocevar committed
381
                     p_input->p_source, strerror(errno));
Sam Hocevar's avatar
   
Sam Hocevar committed
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
        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
398
    {
399
400
        p_input->stream.b_seekable = 0;
        p_input->stream.i_size = 0;
Benoit Steiner's avatar
   
Benoit Steiner committed
401
402
403
    }
    else
    {
404
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
405
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
   
Sam Hocevar committed
406
                     p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
407
408
409
        p_input->b_error = 1;
        return;
    }
410

Sam Hocevar's avatar
   
Sam Hocevar committed
411
412
413
    p_input->stream.i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
   
Sam Hocevar committed
414
415
    intf_Msg( "input: opening file %s", p_input->p_source );
    if( (p_input->i_handle = open( p_input->p_source,
Sam Hocevar's avatar
   
Sam Hocevar committed
416
417
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
418
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
   
Sam Hocevar committed
419
420
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
421
422
423
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
424
425

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
426
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
427
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
428
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
429
{
Sam Hocevar's avatar
   
Sam Hocevar committed
430
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
431

Sam Hocevar's avatar
   
Sam Hocevar committed
432
    return;
Stéphane Borel's avatar
Stéphane Borel committed
433
}
Sam Hocevar's avatar
   
Sam Hocevar committed
434