input.c 13 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.65 2001/01/05 14:45:47 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 );
Michel Kaempf's avatar
Michel Kaempf committed
65

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

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

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

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

102 103 104 105 106 107 108
    /* 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
109 110

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

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

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

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

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

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

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

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

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

184 185 186
        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 )
187
        {
188 189
            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
190
        }
191
        vlc_mutex_unlock( &p_input->stream.control.control_lock );
192

193 194 195 196
        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++ )
197
        {
198 199
            p_input->p_plugin->pf_demux( p_input, pp_packets[i] );
        }
200

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

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

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

222
/*****************************************************************************
223
 * InitThread: init the input thread
224
 *****************************************************************************/
225 226
input_capabilities_t * PSKludge( void );
static void InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
227
{
228 229 230
    /* 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
231 232

#ifdef STATS
233 234 235 236 237 238
    /* 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
239
#endif
240

241 242 243 244 245 246 247 248
    /* Use the appropriate input method */
    switch( p_input->p_config->i_method )
    {
    case INPUT_METHOD_FILE:                                  /* file methods */
        FileOpen( p_input );
        break;
    case INPUT_METHOD_VLAN_BCAST:                     /* vlan network method */
/*        if( !p_main->b_vlans )
Michel Kaempf's avatar
Michel Kaempf committed
249
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
250
            intf_ErrMsg("input error: vlans are not activated");
251 252 253 254 255 256 257 258 259 260 261
            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
262 263
        intf_ErrMsg( "input error: unknow method 0x%.4x",
                     p_input->p_config->i_method );
264 265 266
        free( p_input->p_config );
        p_input->b_error = 1;
        break;
267
#endif
Michel Kaempf's avatar
Michel Kaempf committed
268
    }
269 270 271 272 273 274 275 276

    free( p_input->p_config );

    /* Probe plugin (FIXME: load plugins before & write this) */
    p_input->p_plugin = PSKludge();
    p_input->p_plugin->pf_init( p_input );

    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
277 278
}

279
/*****************************************************************************
280
 * ErrorThread: RunThread() error loop
281
 *****************************************************************************
282
 * This function is called when an error occured during thread main's loop.
283
 *****************************************************************************/
284
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
285
{
286
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
287
    {
288 289
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
290 291 292
    }
}

293
/*****************************************************************************
294
 * EndThread: end the input thread
295
 *****************************************************************************/
296
static void EndThread( input_thread_t * p_input )
297
{
298
    int *       pi_status;                                  /* thread status */
299

300 301 302
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
303

304 305 306 307 308
#ifdef STATS
    {
        struct tms cpu_usage;
        times( &cpu_usage );

Sam Hocevar's avatar
 
Sam Hocevar committed
309
        intf_Msg("input stats: cpu usage (user: %d, system: %d)",
310 311 312 313
                 cpu_usage.tms_utime, cpu_usage.tms_stime);
    }
#endif

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

317
    /* Free demultiplexer's data */
318 319 320
    p_input->p_plugin->pf_end( p_input );
    free( p_input->p_plugin );

321
    /* Free input structure */
322
    free( p_input );
323

324 325
    /* Update status */
    *pi_status = THREAD_OVER;
326
}
327

328
/*****************************************************************************
329
 * NetworkOpen : open a network socket descriptor
330
 *****************************************************************************/
331
static void NetworkOpen( input_thread_t * p_input )
332
{
333
    /* straight copy & paste of input_network.c of input-I */
334

335 336 337 338
    /* We cannot rewind nor lseek() */
    p_input->stream.b_seekable = 0;
    /* We cannot control the pace */
    p_input->stream.b_pace_control = 0;
339 340
}

341
/*****************************************************************************
342
 * FileOpen : open a file descriptor
343
 *****************************************************************************/
344
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
345
{
346
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
347

348
#define p_config    p_input->p_config
Michel Kaempf's avatar
Michel Kaempf committed
349

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

Sam Hocevar's avatar
 
Sam Hocevar committed
383 384 385 386 387 388 389 390 391 392
    p_input->stream.i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

    intf_Msg( "Opening file %s", p_config->p_source );
    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
393 394
    }

395
#undef p_config
Michel Kaempf's avatar
Michel Kaempf committed
396
}