input.c 13.4 KB
Newer Older
1
2
/*****************************************************************************
 * input.c: input thread
Michel Kaempf's avatar
Michel Kaempf committed
3
4
 * Read an MPEG2 stream, demultiplex and parse it before sending it to
 * decoders.
5
6
 *****************************************************************************
 * Copyright (C) 1998, 1999, 2000 VideoLAN
7
 * $Id: input.c,v 1.77 2001/02/08 13:08:02 massiot 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
static void InitThread  ( input_thread_t *p_input );
66
67
static void ErrorThread ( input_thread_t *p_input );
static void EndThread   ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
68

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

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

    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
94
95
96
97
98
    p_input->b_eof              = 0;

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

99
    /* I have never understood that stuff --Meuuh */
100
101
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
102

103
    /* Initialize stream description */
104
105
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
106
    p_input->stream.i_pgrm_number = 0;
107
108
    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
109

110
111
112
113
114
    /* 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
115

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

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

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

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

162
163
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
164

165
166
167
168
169
    /* 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 );

170
171
172
173
174
175
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
176
177
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
178
    }
Michel Kaempf's avatar
Michel Kaempf committed
179
180
}

181
/*****************************************************************************
182
 * RunThread: main thread loop
183
 *****************************************************************************
184
 * Thread in charge of processing the network packets and demultiplexing.
185
 *****************************************************************************/
186
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
187
{
188
189
    data_packet_t *         pp_packets[INPUT_READ_ONCE];
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
190

Sam Hocevar's avatar
   
Sam Hocevar committed
191
    InitThread( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
192

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

Sam Hocevar's avatar
   
Sam Hocevar committed
196
#ifdef STATS
Sam Hocevar's avatar
   
Sam Hocevar committed
197
        p_input->c_loops++;
Sam Hocevar's avatar
   
Sam Hocevar committed
198
199
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
200
201
202
203
204
205
206
207
208
209
        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 );
Sam Hocevar's avatar
   
Sam Hocevar committed
210

Sam Hocevar's avatar
   
Sam Hocevar committed
211
212
213
214
215
        /* Demultiplex read packets. */
        for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
Sam Hocevar's avatar
   
Sam Hocevar committed
216

Sam Hocevar's avatar
   
Sam Hocevar committed
217
218
219
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
220
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
221
222
223
224
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
                intf_WarnMsg( 1, "End of file reached" );
                p_input->b_eof = 1;
Sam Hocevar's avatar
   
Sam Hocevar committed
225
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
226
            else
Sam Hocevar's avatar
   
Sam Hocevar committed
227
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
228
                p_input->b_error = 1;
Sam Hocevar's avatar
Sam Hocevar committed
229
            }
230
231
232
        }
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
233
    if( p_input->b_error || p_input->b_eof )
234
235
236
    {
        ErrorThread( p_input );
    }
237

238
239
    EndThread( p_input );
    intf_DbgMsg("Thread end");
240
241
}

242
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
243
 * InitThread: init the input Thread
244
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
245
static void InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
246
247
248
{

#ifdef STATS
249
250
251
252
253
254
    /* 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
255
#endif
Sam Hocevar's avatar
Sam Hocevar committed
256

Sam Hocevar's avatar
   
Sam Hocevar committed
257
258
259
260
    p_input->p_input_module = module_Need( p_main->p_module_bank,
                                           MODULE_CAPABILITY_INPUT, NULL );

    if( p_input->p_input_module == NULL )
261
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
262
        intf_ErrMsg( "input error: no suitable input module" );
263
        p_input->b_error = 1;
Sam Hocevar's avatar
   
Sam Hocevar committed
264
        return;
Michel Kaempf's avatar
Michel Kaempf committed
265
    }
266

Sam Hocevar's avatar
   
Sam Hocevar committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#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->pf_open( p_input );
283

Sam Hocevar's avatar
   
Sam Hocevar committed
284
    if( p_input->b_error )
285
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
286
        module_Unneed( p_main->p_module_bank, p_input->p_input_module );
287
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
288
    else
Sam Hocevar's avatar
   
Sam Hocevar committed
289
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
290
        p_input->pf_init( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
291
292
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
293
    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
294
295
}

296
/*****************************************************************************
297
 * ErrorThread: RunThread() error loop
298
 *****************************************************************************
299
 * This function is called when an error occured during thread main's loop.
300
 *****************************************************************************/
301
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
302
{
303
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
304
    {
305
306
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
307
308
309
    }
}

310
/*****************************************************************************
311
 * EndThread: end the input thread
312
 *****************************************************************************/
313
static void EndThread( input_thread_t * p_input )
314
{
315
    int *       pi_status;                                  /* thread status */
316

317
318
319
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
320

Sam Hocevar's avatar
   
Sam Hocevar committed
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#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 );

    /* Close stream */
    p_input->pf_close( p_input );

    /* Free demultiplexer's data */
    p_input->pf_end( p_input );

    /* Release modules */
    module_Unneed( p_main->p_module_bank, p_input->p_input_module );

Henri Fallon's avatar
   
Henri Fallon committed
343
344
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
   
Henri Fallon committed
345
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
   
Henri Fallon committed
346
    
347
    /* Free input structure */
348
    free( p_input );
349

350
351
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
352
}
353

Sam Hocevar's avatar
Sam Hocevar committed
354
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
355
 * input_FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
356
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
357
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
358
{
359
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
360

Sam Hocevar's avatar
   
Sam Hocevar committed
361
    if( stat( p_input->p_source, &stat_info ) == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
362
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
363
        intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
Sam Hocevar's avatar
   
Sam Hocevar committed
364
                     p_input->p_source, strerror(errno));
Sam Hocevar's avatar
   
Sam Hocevar committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
        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
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
   
Sam Hocevar committed
389
                     p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
390
391
392
        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
398
    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
399
400
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
401
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
   
Sam Hocevar committed
402
403
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
404
405
406
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
407
408

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
409
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
410
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
411
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
412
{
Sam Hocevar's avatar
   
Sam Hocevar committed
413
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
414

Sam Hocevar's avatar
   
Sam Hocevar committed
415
    return;
Stéphane Borel's avatar
Stéphane Borel committed
416
}
Sam Hocevar's avatar
   
Sam Hocevar committed
417