input.c 13.1 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.76 2001/02/08 07:24:25 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
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
122
123
124
    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
125
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
126
127
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
128
129
130
        free( p_input );
        return( NULL );
    }
131

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

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

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

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

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

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

Sam Hocevar's avatar
   
Sam Hocevar committed
185
    InitThread( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
186

Sam Hocevar's avatar
   
Sam Hocevar committed
187
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
188
189
    {

Sam Hocevar's avatar
   
Sam Hocevar committed
190
#ifdef STATS
Sam Hocevar's avatar
   
Sam Hocevar committed
191
        p_input->c_loops++;
Sam Hocevar's avatar
   
Sam Hocevar committed
192
193
#endif

Sam Hocevar's avatar
   
Sam Hocevar committed
194
195
196
197
198
199
200
201
202
203
        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
204

Sam Hocevar's avatar
   
Sam Hocevar committed
205
206
207
208
209
        /* 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
210

Sam Hocevar's avatar
   
Sam Hocevar committed
211
212
213
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
214
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
215
216
217
218
                /* 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
219
            }
Sam Hocevar's avatar
   
Sam Hocevar committed
220
            else
Sam Hocevar's avatar
   
Sam Hocevar committed
221
            {
Sam Hocevar's avatar
   
Sam Hocevar committed
222
                p_input->b_error = 1;
Sam Hocevar's avatar
Sam Hocevar committed
223
            }
224
225
226
        }
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
227
    if( p_input->b_error || p_input->b_eof )
228
229
230
    {
        ErrorThread( p_input );
    }
231

232
233
    EndThread( p_input );
    intf_DbgMsg("Thread end");
234
235
}

236
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
237
 * InitThread: init the input Thread
238
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
239
static void InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
240
241
242
{

#ifdef STATS
243
244
245
246
247
248
    /* 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
249
#endif
Sam Hocevar's avatar
Sam Hocevar committed
250

Sam Hocevar's avatar
   
Sam Hocevar committed
251
252
253
254
    p_input->p_input_module = module_Need( p_main->p_module_bank,
                                           MODULE_CAPABILITY_INPUT, NULL );

    if( p_input->p_input_module == NULL )
255
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
256
        intf_ErrMsg( "input error: no suitable input module" );
257
        p_input->b_error = 1;
Sam Hocevar's avatar
   
Sam Hocevar committed
258
        return;
Michel Kaempf's avatar
Michel Kaempf committed
259
    }
260

Sam Hocevar's avatar
   
Sam Hocevar committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#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 );
277

Sam Hocevar's avatar
   
Sam Hocevar committed
278
    if( p_input->b_error )
279
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
280
        module_Unneed( p_main->p_module_bank, p_input->p_input_module );
281
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
282
    else
Sam Hocevar's avatar
   
Sam Hocevar committed
283
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
284
        p_input->pf_init( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
285
286
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
287
    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
288
289
}

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

304
/*****************************************************************************
305
 * EndThread: end the input thread
306
 *****************************************************************************/
307
static void EndThread( input_thread_t * p_input )
308
{
309
    int *       pi_status;                                  /* thread status */
310

311
312
313
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
314

Sam Hocevar's avatar
   
Sam Hocevar committed
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#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
337
338
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
   
Henri Fallon committed
339
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
   
Henri Fallon committed
340
    
341
    /* Free input structure */
342
    free( p_input );
343

344
345
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
346
}
347

Sam Hocevar's avatar
Sam Hocevar committed
348
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
349
 * input_FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
350
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
351
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
352
{
353
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
354

Sam Hocevar's avatar
   
Sam Hocevar committed
355
    if( stat( p_input->p_source, &stat_info ) == (-1) )
Sam Hocevar's avatar
   
Sam Hocevar committed
356
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
357
        intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
Sam Hocevar's avatar
   
Sam Hocevar committed
358
                     p_input->p_source, strerror(errno));
Sam Hocevar's avatar
   
Sam Hocevar committed
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
        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
375
    {
376
377
        p_input->stream.b_seekable = 0;
        p_input->stream.i_size = 0;
Benoit Steiner's avatar
   
Benoit Steiner committed
378
379
380
    }
    else
    {
381
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
   
Sam Hocevar committed
382
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
   
Sam Hocevar committed
383
                     p_input->p_source );
Sam Hocevar's avatar
   
Sam Hocevar committed
384
385
386
        p_input->b_error = 1;
        return;
    }
387

Sam Hocevar's avatar
   
Sam Hocevar committed
388
389
390
    p_input->stream.i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
   
Sam Hocevar committed
391
392
    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
393
394
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
   
Sam Hocevar committed
395
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
   
Sam Hocevar committed
396
397
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
398
399
400
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
401
402

/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
403
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
404
 *****************************************************************************/
Sam Hocevar's avatar
   
Sam Hocevar committed
405
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
406
{
Sam Hocevar's avatar
   
Sam Hocevar committed
407
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
408

Sam Hocevar's avatar
   
Sam Hocevar committed
409
    return;
Stéphane Borel's avatar
Stéphane Borel committed
410
}
Sam Hocevar's avatar
   
Sam Hocevar committed
411