input.c 14.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.69 2001/01/15 06:18:23 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 );
Stéphane Borel's avatar
Stéphane Borel committed
65
static void DvdOpen     ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

243 244 245 246 247
    /* Use the appropriate input method */
    switch( p_input->p_config->i_method )
    {
    case INPUT_METHOD_FILE:                                  /* file methods */
        FileOpen( p_input );
Stéphane Borel's avatar
Stéphane Borel committed
248 249 250 251 252 253 254
        /* Probe plugin (FIXME: load plugins before & write this) */
        p_input->p_plugin = PSKludge();
       break;
    case INPUT_METHOD_DVD:                                     /* DVD method */
        DvdOpen( p_input );
        /* DVD plugin */
        p_input->p_plugin = DVDKludge();
255 256 257
        break;
    case INPUT_METHOD_VLAN_BCAST:                     /* vlan network method */
/*        if( !p_main->b_vlans )
Michel Kaempf's avatar
Michel Kaempf committed
258
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
259
            intf_ErrMsg("input error: vlans are not activated");
260 261 262 263 264 265 266 267 268 269 270
            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
271 272
        intf_ErrMsg( "input error: unknow method 0x%.4x",
                     p_input->p_config->i_method );
273 274 275
        free( p_input->p_config );
        p_input->b_error = 1;
        break;
276
#endif
Michel Kaempf's avatar
Michel Kaempf committed
277
    }
278 279 280 281 282 283

    free( p_input->p_config );

    p_input->p_plugin->pf_init( p_input );

    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
284 285
}

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

300
/*****************************************************************************
301
 * EndThread: end the input thread
302
 *****************************************************************************/
303
static void EndThread( input_thread_t * p_input )
304
{
305
    int *       pi_status;                                  /* thread status */
306

307 308 309
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
310

311 312 313 314 315
#ifdef STATS
    {
        struct tms cpu_usage;
        times( &cpu_usage );

Sam Hocevar's avatar
 
Sam Hocevar committed
316
        intf_Msg("input stats: cpu usage (user: %d, system: %d)",
317 318 319 320
                 cpu_usage.tms_utime, cpu_usage.tms_stime);
    }
#endif

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

324
    /* Free demultiplexer's data */
325 326 327
    p_input->p_plugin->pf_end( p_input );
    free( p_input->p_plugin );

Henri Fallon's avatar
 
Henri Fallon committed
328 329
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
 
Henri Fallon committed
330
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
 
Henri Fallon committed
331
    
332
    /* Free input structure */
333
    free( p_input );
334

335 336
    /* Update status */
    *pi_status = THREAD_OVER;
337
}
338

339
/*****************************************************************************
340
 * NetworkOpen : open a network socket descriptor
341
 *****************************************************************************/
342
static void NetworkOpen( input_thread_t * p_input )
343
{
344
    /* straight copy & paste of input_network.c of input-I */
345

346 347 348 349
    /* We cannot rewind nor lseek() */
    p_input->stream.b_seekable = 0;
    /* We cannot control the pace */
    p_input->stream.b_pace_control = 0;
350 351
}

352
/*****************************************************************************
353
 * FileOpen : open a file descriptor
354
 *****************************************************************************/
355
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
356
{
357
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
358

359
#define p_config    p_input->p_config
Michel Kaempf's avatar
Michel Kaempf committed
360

Sam Hocevar's avatar
 
Sam Hocevar committed
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    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) )
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 389 390 391 392
        intf_ErrMsg( "input error: unknown file type for %s",
                     p_config->p_source );
        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
    intf_Msg( "input: opening file %s", p_config->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
398 399 400 401 402 403
    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
404 405
    }

406
#undef p_config
Michel Kaempf's avatar
Michel Kaempf committed
407
}
Stéphane Borel's avatar
Stéphane Borel committed
408 409 410 411 412 413

/*****************************************************************************
 * DvdOpen : open the dvd device
 *****************************************************************************/
static void DvdOpen( input_thread_t * p_input )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
414
    intf_Msg( "input: opening DVD %s", p_input->p_config->p_source );
Stéphane Borel's avatar
Stéphane Borel committed
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    if( (p_input->i_handle = open( p_input->p_config->p_source,
                                   O_RDONLY|O_LARGEFILE )) == (-1) )
    {
        intf_ErrMsg( "input error: cannot open device %s", strerror(errno) );
        p_input->b_error = 1;
        return;
    }

    vlc_mutex_lock( &p_input->stream.stream_lock );

    p_input->stream.b_pace_control = 1;
    p_input->stream.b_seekable = 1;
    p_input->stream.i_size = 0;
    p_input->stream.i_tell = 0;

    vlc_mutex_unlock( &p_input->stream.stream_lock );
}