input.c 13.9 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.75 2001/02/08 04:43:27 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 66
static void InitLoop    ( input_thread_t *p_input );
static void StopLoop    ( input_thread_t *p_input );
67 68
static void ErrorThread ( input_thread_t *p_input );
static void EndThread   ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
69

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

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

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

99
    /* Initialize stream description */
100 101
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
102
    p_input->stream.i_pgrm_number = 0;
103 104
    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
105

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

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

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

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

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

157 158
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
159

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

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

Sam Hocevar's avatar
 
Sam Hocevar committed
181
    *p_input->pi_status = THREAD_READY;
Michel Kaempf's avatar
Michel Kaempf committed
182

183 184
    while( !p_input->b_die && !p_input->b_error )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
185
        InitLoop( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
186

Sam Hocevar's avatar
 
Sam Hocevar committed
187
        if( p_input->b_die || p_input->b_error )
188
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
189
            break;
Michel Kaempf's avatar
Michel Kaempf committed
190
        }
191

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

Sam Hocevar's avatar
 
Sam Hocevar committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
#ifdef STATS
            p_input->c_loops++;
#endif

            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 );

            /* Demultiplex read packets. */
            for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ )
212
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
                p_input->pf_demux( p_input, pp_packets[i] );
            }

            if( i_error )
            {
                if( i_error == 1 )
                {
                    /* End of file */
                    intf_WarnMsg( 1, "End of file reached" );
                    /* FIXME: don't treat that as an error */
                    p_input->b_eof = 1;
                }
                else
                {
                    p_input->b_error = 1;
                }
229
            }
230
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
231 232 233 234 235

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

        StopLoop( p_input );
236 237
    }

238 239 240 241
    if( p_input->b_error )
    {
        ErrorThread( p_input );
    }
242

243 244
    EndThread( p_input );
    intf_DbgMsg("Thread end");
245 246
}

247
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
248
 * InitLoop: init the input loop
249
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
250
static void InitLoop( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
251
{
Sam Hocevar's avatar
 
Sam Hocevar committed
252 253 254 255 256 257 258 259 260 261 262 263
    playlist_Next( p_main->p_playlist );

    if( p_main->p_playlist->i_index == -1 )
    {
        /*    FIXME: wait for user to add stuff to playlist ? */
        /* FIXME II: we shouldn't set b_error but rather b_die */
        intf_Msg( "playlist: end" );
        p_input->b_error = 1;
        return;
    }

    p_input->p_source = p_main->p_playlist->current.psz_name;
Michel Kaempf's avatar
Michel Kaempf committed
264 265

#ifdef STATS
266 267 268 269 270 271
    /* 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
272
#endif
273

Sam Hocevar's avatar
 
Sam Hocevar committed
274 275 276 277
    p_input->p_input_module = module_Need( p_main->p_module_bank,
                                           MODULE_CAPABILITY_INPUT, NULL );

    if( p_input->p_input_module == NULL )
278
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
279
        intf_ErrMsg( "input error: no suitable input module" );
280
        p_input->b_error = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
281
        return;
Michel Kaempf's avatar
Michel Kaempf committed
282
    }
283

Sam Hocevar's avatar
 
Sam Hocevar committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
#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->b_eof = 0;
    p_input->pf_open( p_input );
301

Sam Hocevar's avatar
 
Sam Hocevar committed
302
    if( p_input->b_error )
303
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
304 305
        module_Unneed( p_main->p_module_bank, p_input->p_input_module );
        return;
306
    }
307

Sam Hocevar's avatar
 
Sam Hocevar committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    p_input->pf_init( p_input );

    return;
}

/*****************************************************************************
 * StopLoop: stop the input loop
 *****************************************************************************/
static void StopLoop( input_thread_t * p_input )
{
#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 demultiplexer's data */
    p_input->pf_end( p_input );

    /* Release modules */
    module_Unneed( p_main->p_module_bank, p_input->p_input_module );
Michel Kaempf's avatar
Michel Kaempf committed
333 334
}

335
/*****************************************************************************
336
 * ErrorThread: RunThread() error loop
337
 *****************************************************************************
338
 * This function is called when an error occured during thread main's loop.
339
 *****************************************************************************/
340
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
341
{
342
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
343
    {
344 345
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
346 347 348
    }
}

349
/*****************************************************************************
350
 * EndThread: end the input thread
351
 *****************************************************************************/
352
static void EndThread( input_thread_t * p_input )
353
{
354
    int *       pi_status;                                  /* thread status */
355

356 357 358
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
359

Henri Fallon's avatar
 
Henri Fallon committed
360 361
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
 
Henri Fallon committed
362
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
 
Henri Fallon committed
363
    
364
    /* Free input structure */
365
    free( p_input );
366

367 368
    /* Update status */
    *pi_status = THREAD_OVER;
369
}
370

371
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
372
 * input_FileOpen : open a file descriptor
373
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
374
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
375
{
376
    struct stat         stat_info;
Michel Kaempf's avatar
Michel Kaempf committed
377

Sam Hocevar's avatar
 
Sam Hocevar committed
378
    if( stat( p_input->p_source, &stat_info ) == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
379
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
380
        intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
Sam Hocevar's avatar
 
Sam Hocevar committed
381
                     p_input->p_source, strerror(errno));
Sam Hocevar's avatar
 
Sam Hocevar committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
        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) )
398
    {
399 400
        p_input->stream.b_seekable = 0;
        p_input->stream.i_size = 0;
Benoit Steiner's avatar
 
Benoit Steiner committed
401 402 403
    }
    else
    {
404
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
405
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
406
                     p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
407 408 409
        p_input->b_error = 1;
        return;
    }
410

Sam Hocevar's avatar
 
Sam Hocevar committed
411 412 413
    p_input->stream.i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
414 415
    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
416 417
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
418
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
 
Sam Hocevar committed
419 420
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
421 422 423
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
424 425

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
426
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
427
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
428
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
429
{
Sam Hocevar's avatar
 
Sam Hocevar committed
430
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
431

Sam Hocevar's avatar
 
Sam Hocevar committed
432
    return;
Stéphane Borel's avatar
Stéphane Borel committed
433
}
Sam Hocevar's avatar
 
Sam Hocevar committed
434