input.c 15.7 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
Christophe Massiot's avatar
Christophe Massiot committed
7
 * $Id: input.c,v 1.84 2001/02/19 19:08:59 massiot Exp $
8
 *
9
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
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
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
64 65 66 67 68
static void RunThread       ( input_thread_t *p_input );
static  int InitThread      ( input_thread_t *p_input );
static void ErrorThread     ( input_thread_t *p_input );
static void DestroyThread   ( 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 ( playlist_item_t *p_item, 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;
Sam Hocevar's avatar
 
Sam Hocevar committed
95 96 97 98 99
    p_input->b_eof              = 0;

    /* Set target */
    p_input->p_source           = p_item->psz_name;

100
    /* I have never understood that stuff --Meuuh */
101 102
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
103

104
    /* Initialize stream description */
105 106
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
107
    p_input->stream.i_pgrm_number = 0;
108
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
109
    p_input->stream.i_seek = NO_SEEK;
Christophe Massiot's avatar
Christophe Massiot committed
110
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
111

112 113 114 115 116
    /* 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
117

Sam Hocevar's avatar
 
Sam Hocevar committed
118 119
    /* Initialize default settings for spawned decoders */
    p_input->p_default_aout = p_main->p_aout;
Sam Hocevar's avatar
 
Sam Hocevar committed
120
    p_input->p_default_vout = p_main->p_vout;
Sam Hocevar's avatar
 
Sam Hocevar committed
121

Michel Kaempf's avatar
Michel Kaempf committed
122
    /* Create thread and set locks. */
123
    vlc_mutex_init( &p_input->stream.stream_lock );
124
    vlc_cond_init( &p_input->stream.stream_wait );
125 126 127
    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
128
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
129 130
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
131 132 133
        free( p_input );
        return( NULL );
    }
134

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

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

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

164 165
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
166

167 168 169 170 171
    /* Make the thread exit of an eventual vlc_cond_wait() */
    vlc_mutex_lock( &p_input->stream.stream_lock );
    vlc_cond_signal( &p_input->stream.stream_wait );
    vlc_mutex_unlock( &p_input->stream.stream_lock );

172 173 174 175 176 177
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
178 179
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
180
    }
Michel Kaempf's avatar
Michel Kaempf committed
181 182
}

183
/*****************************************************************************
184
 * RunThread: main thread loop
185
 *****************************************************************************
186
 * Thread in charge of processing the network packets and demultiplexing.
187
 *****************************************************************************/
188
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
189
{
190 191
    data_packet_t *         pp_packets[INPUT_READ_ONCE];
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
192

Sam Hocevar's avatar
 
Sam Hocevar committed
193 194 195 196 197 198 199 200 201 202
    if( InitThread( p_input ) )
    {

        /* If we failed, wait before we are killed, and exit */
        *p_input->pi_status = THREAD_ERROR;
        p_input->b_error = 1;
        ErrorThread( p_input );
        DestroyThread( p_input );
        return;
    }
Michel Kaempf's avatar
Michel Kaempf committed
203

Sam Hocevar's avatar
 
Sam Hocevar committed
204
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
205 206
    {

Sam Hocevar's avatar
 
Sam Hocevar committed
207
#ifdef STATS
Sam Hocevar's avatar
 
Sam Hocevar committed
208
        p_input->c_loops++;
Sam Hocevar's avatar
 
Sam Hocevar committed
209 210
#endif

211
        vlc_mutex_lock( &p_input->stream.stream_lock );
212
        if( p_input->stream.i_seek != NO_SEEK )
Sam Hocevar's avatar
 
Sam Hocevar committed
213
        {
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
                p_input->pf_seek( p_input, p_input->stream.i_seek );

                for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
                {
                    pgrm_descriptor_t * p_pgrm
                                            = p_input->stream.pp_programs[i];
                    /* Escape all decoders for the stream discontinuity they
                     * will encounter. */
                    input_EscapeDiscontinuity( p_input, p_pgrm );

                    /* Reinitialize synchro. */
                    p_pgrm->i_synchro_state = SYNCHRO_REINIT;
                }
            }
230
            p_input->stream.i_seek = NO_SEEK;
Sam Hocevar's avatar
 
Sam Hocevar committed
231
        }
232
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
233 234

        i_error = p_input->pf_read( p_input, pp_packets );
Sam Hocevar's avatar
 
Sam Hocevar committed
235

Sam Hocevar's avatar
 
Sam Hocevar committed
236 237 238 239 240
        /* 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
241

Sam Hocevar's avatar
 
Sam Hocevar committed
242 243 244
        if( i_error )
        {
            if( i_error == 1 )
245
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
246 247 248 249
                /* 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
250
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
251
            else
Sam Hocevar's avatar
 
Sam Hocevar committed
252
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
253
                p_input->b_error = 1;
254
            }
255 256 257
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
258
    if( p_input->b_error || p_input->b_eof )
259 260 261
    {
        ErrorThread( p_input );
    }
262

263
    EndThread( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
264 265 266

    DestroyThread( p_input );

267
    intf_DbgMsg("Thread end");
268 269
}

270
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
271
 * InitThread: init the input Thread
272
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
273
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
274 275 276
{

#ifdef STATS
277 278 279 280 281 282
    /* 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
283
#endif
284

Sam Hocevar's avatar
 
Sam Hocevar committed
285
    p_input->p_input_module = module_Need( p_main->p_bank,
Sam Hocevar's avatar
 
Sam Hocevar committed
286 287
                                           MODULE_CAPABILITY_INPUT,
                                           (probedata_t *)p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
288 289

    if( p_input->p_input_module == NULL )
290
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
291
        intf_ErrMsg( "input error: no suitable input module" );
Sam Hocevar's avatar
 
Sam Hocevar committed
292 293
        module_Unneed( p_main->p_bank, p_input->p_input_module );
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
294
    }
295

Sam Hocevar's avatar
 
Sam Hocevar committed
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
#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 );
312

Sam Hocevar's avatar
 
Sam Hocevar committed
313
    if( p_input->b_error )
314
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
315 316
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
317
        module_Unneed( p_main->p_bank, p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
318
        return( -1 );
319
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
320 321

    p_input->pf_init( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
322

Sam Hocevar's avatar
 
Sam Hocevar committed
323
    *p_input->pi_status = THREAD_READY;
Sam Hocevar's avatar
 
Sam Hocevar committed
324 325

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
326 327
}

328
/*****************************************************************************
329
 * ErrorThread: RunThread() error loop
330
 *****************************************************************************
331
 * This function is called when an error occured during thread main's loop.
332
 *****************************************************************************/
333
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
334
{
335
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
336
    {
337 338
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
339 340 341
    }
}

342
/*****************************************************************************
343
 * EndThread: end the input thread
344
 *****************************************************************************/
345
static void EndThread( input_thread_t * p_input )
346
{
347
    int *       pi_status;                                  /* thread status */
348

349 350 351
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
352

Sam Hocevar's avatar
 
Sam Hocevar committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
#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 );

    /* Free demultiplexer's data */
    p_input->pf_end( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
369 370 371
    /* Close stream */
    p_input->pf_close( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
372
    /* Release modules */
Sam Hocevar's avatar
 
Sam Hocevar committed
373
    module_Unneed( p_main->p_bank, p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
374

Sam Hocevar's avatar
 
Sam Hocevar committed
375 376 377 378 379 380 381 382 383 384 385
}

/*****************************************************************************
 * DestroyThread: destroy the input thread
 *****************************************************************************/
static void DestroyThread( input_thread_t * p_input )
{
    int *       pi_status;                                  /* thread status */

    /* Store status */
    pi_status = p_input->pi_status;
Sam Hocevar's avatar
 
Sam Hocevar committed
386

Henri Fallon's avatar
 
Henri Fallon committed
387 388
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
 
Henri Fallon committed
389
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
 
Henri Fallon committed
390
    
391
    /* Free input structure */
392
    free( p_input );
393

394 395
    /* Update status */
    *pi_status = THREAD_OVER;
396
}
397

398
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
399
 * input_FileOpen : open a file descriptor
400
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
401
void input_FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
402
{
403
    struct stat         stat_info;
Sam Hocevar's avatar
 
Sam Hocevar committed
404 405 406
    int                 i_stat;

    char *psz_name = p_input->p_source;
Michel Kaempf's avatar
Michel Kaempf committed
407

Sam Hocevar's avatar
 
Sam Hocevar committed
408 409 410
    /* FIXME: this code ought to be in the plugin so that code can
     * be shared with the *_Probe function */
    if( ( i_stat = stat( psz_name, &stat_info ) ) == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
411
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
        int i_size = strlen( psz_name );

        if( ( i_size > 4 )
            && !strncasecmp( psz_name, "dvd:", 4 ) )
        {
            /* get rid of the 'dvd:' stuff and try again */
            psz_name += 4;
            i_stat = stat( psz_name, &stat_info );
	}
	else if( ( i_size > 5 )
                 && !strncasecmp( psz_name, "file:", 5 ) )
        {
            /* get rid of the 'file:' stuff and try again */
            psz_name += 5;
            i_stat = stat( psz_name, &stat_info );
	}

	if( i_stat == (-1) )
        {
            intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
                         psz_name, strerror(errno));
            p_input->b_error = 1;
            return;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
436 437 438 439 440 441 442 443 444 445 446 447 448
    }

    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;
    }
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
449 450 451 452 453
    else if( S_ISFIFO(stat_info.st_mode)
#ifndef SYS_BEOS
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
454
    {
455 456
        p_input->stream.b_seekable = 0;
        p_input->stream.i_size = 0;
Benoit Steiner's avatar
 
Benoit Steiner committed
457 458 459
    }
    else
    {
460
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
461
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
462
                     psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
463 464 465
        p_input->b_error = 1;
        return;
    }
466

Sam Hocevar's avatar
 
Sam Hocevar committed
467 468 469
    p_input->stream.i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
470 471
    intf_Msg( "input: opening %s", p_input->p_source );
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
 
Sam Hocevar committed
472 473
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
474
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
 
Sam Hocevar committed
475 476
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
477 478 479
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
480 481

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
482
 * input_FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
483
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
484
void input_FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
485
{
Sam Hocevar's avatar
 
Sam Hocevar committed
486
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
487

Sam Hocevar's avatar
 
Sam Hocevar committed
488
    return;
Stéphane Borel's avatar
Stéphane Borel committed
489
}
Sam Hocevar's avatar
 
Sam Hocevar committed
490