input.c 13.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.64 2000/12/22 13:04:44 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
        intf_ErrMsg("error: %s", strerror(errno));
84
        free( p_config );
Michel Kaempf's avatar
Michel Kaempf committed
85 86
        return( NULL );
    }
87 88 89 90

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

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

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

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

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

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

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

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

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

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

174
    InitThread( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
175

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

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

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

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

211 212 213 214
    if( p_input->b_error )
    {
        ErrorThread( p_input );
    }
215

216 217
    EndThread( p_input );
    intf_DbgMsg("Thread end");
218 219
}

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

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

239 240 241 242 243 244 245 246
    /* 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
247
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
248
            intf_ErrMsg("error: vlans are not activated");
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
            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:
        intf_ErrMsg("Unknow input method");
        free( p_input->p_config );
        p_input->b_error = 1;
        break;
264
#endif
Michel Kaempf's avatar
Michel Kaempf committed
265
    }
266 267 268 269 270 271 272 273

    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
274 275
}

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

290
/*****************************************************************************
291
 * EndThread: end the input thread
292
 *****************************************************************************/
293
static void EndThread( input_thread_t * p_input )
294
{
295
    int *       pi_status;                                  /* thread status */
296

297 298 299
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
300

301 302 303 304 305
#ifdef STATS
    {
        struct tms cpu_usage;
        times( &cpu_usage );

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

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

314
    /* Free demultiplexer's data */
315 316 317
    p_input->p_plugin->pf_end( p_input );
    free( p_input->p_plugin );

318
    /* Free input structure */
319
    free( p_input );
320

321 322
    /* Update status */
    *pi_status = THREAD_OVER;
323
}
324

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

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

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

345
#define p_config    p_input->p_config
Michel Kaempf's avatar
Michel Kaempf committed
346

347
    if( !strncmp( p_config->p_source, "-", 1 ) )
348
    {
349 350 351 352 353
        /* stdin */
        p_input->i_handle = 0;
        
        vlc_mutex_lock( &p_input->stream.stream_lock );
        p_input->stream.b_pace_control = 1;
354 355
        p_input->stream.b_seekable = 0;
        p_input->stream.i_size = 0;
356 357
        p_input->stream.i_tell = 0;
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Benoit Steiner's avatar
 
Benoit Steiner committed
358 359 360
    }
    else
    {
361 362 363 364 365 366 367
        if( stat( p_config->p_source, &stat_info ) == (-1) )
        {
            intf_ErrMsg("Cannot stat() file %s (%s)", p_config->p_source,
                        strerror(errno));
            p_input->b_error = 1;
            return;
        }
Michel Kaempf's avatar
Michel Kaempf committed
368

369
        vlc_mutex_lock( &p_input->stream.stream_lock );
Michel Kaempf's avatar
Michel Kaempf committed
370

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
        /* 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) )
        {
            p_input->stream.b_seekable = 0;
            p_input->stream.i_size = 0;
        }
        else
        {
            vlc_mutex_unlock( &p_input->stream.stream_lock );
            intf_ErrMsg("Unknown file type");
            p_input->b_error = 1;
            return;
        }

        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("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
}