input.c 40 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-2001 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
7
 * $Id: input.c,v 1.169 2002/01/09 02:01:14 sam 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 31 32
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
33

Sam Hocevar's avatar
 
Sam Hocevar committed
34 35
#include <videolan/vlc.h>

Sam Hocevar's avatar
 
Sam Hocevar committed
36
#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
 
Sam Hocevar committed
37
#   include <unistd.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
38
#elif defined( _MSC_VER ) && defined( _WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
39
#   include <io.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
40 41
#endif

42
#include <string.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
43 44
#include <errno.h>

Sam Hocevar's avatar
 
Sam Hocevar committed
45 46 47
#ifdef STRNCASECMP_IN_STRINGS_H
#   include <strings.h>
#endif
48 49

#ifdef WIN32
Sam Hocevar's avatar
 
Sam Hocevar committed
50
#   include <winsock2.h>
51
#   include <ws2tcpip.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
52 53 54 55
#elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
#   include <netdb.h>                                         /* hostent ... */
#   include <sys/socket.h>
#   include <netinet/in.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
56 57 58
#   ifdef HAVE_ARPA_INET_H
#       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
#   endif
Sam Hocevar's avatar
 
Sam Hocevar committed
59
#endif
Henri Fallon's avatar
 
Henri Fallon committed
60

61 62
#ifdef HAVE_SYS_TIMES_H
#   include <sys/times.h>
63
#endif
64

Sam Hocevar's avatar
 
Sam Hocevar committed
65
#include "netutils.h"
66

Sam Hocevar's avatar
 
Sam Hocevar committed
67
#include "intf_playlist.h"
68

69 70 71
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
72
#include "input_ext-plugins.h"
Michel Lespinasse's avatar
Yop,  
Michel Lespinasse committed
73

Sam Hocevar's avatar
 
Sam Hocevar committed
74 75
#include "interface.h"

76
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
77
 * Local prototypes
78
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
79 80 81
static void RunThread       ( input_thread_t *p_input );
static  int InitThread      ( input_thread_t *p_input );
static void ErrorThread     ( input_thread_t *p_input );
82
static void CloseThread     ( input_thread_t *p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
83 84
static void DestroyThread   ( input_thread_t *p_input );
static void EndThread       ( input_thread_t *p_input );
Michel Kaempf's avatar
Michel Kaempf committed
85

Sam Hocevar's avatar
 
Sam Hocevar committed
86
static void FileOpen        ( input_thread_t *p_input );
Christophe Massiot's avatar
Christophe Massiot committed
87
static void StdOpen         ( input_thread_t *p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
88
static void FileClose       ( input_thread_t *p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
89
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Sam Hocevar's avatar
 
Sam Hocevar committed
90
static void NetworkOpen     ( input_thread_t *p_input );
91
static void HTTPOpen        ( input_thread_t *p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
92
static void NetworkClose    ( input_thread_t *p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
93
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
94

Sam Hocevar's avatar
 
Sam Hocevar committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
/*****************************************************************************
 * input_InitBank: initialize the input bank.
 *****************************************************************************/
void input_InitBank ( void )
{
    p_input_bank->i_count = 0;

    /* XXX: Workaround for old interface modules */
    p_input_bank->pp_input[0] = NULL;

    vlc_mutex_init( &p_input_bank->lock );
}

/*****************************************************************************
 * input_EndBank: empty the input bank.
 *****************************************************************************
 * This function ends all unused inputs and empties the bank in
 * case of success.
 *****************************************************************************/
void input_EndBank ( void )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
116 117
    int i_input;

Sam Hocevar's avatar
 
Sam Hocevar committed
118
    /* Ask all remaining video outputs to die */
Sam Hocevar's avatar
 
Sam Hocevar committed
119
    for( i_input = 0; i_input < p_input_bank->i_count; i_input++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
120
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
121 122
        input_StopThread(
                p_input_bank->pp_input[ i_input ], NULL );
Sam Hocevar's avatar
 
Sam Hocevar committed
123
        input_DestroyThread(
Sam Hocevar's avatar
 
Sam Hocevar committed
124
                p_input_bank->pp_input[ i_input ] );
Sam Hocevar's avatar
 
Sam Hocevar committed
125 126 127 128 129
    }

    vlc_mutex_destroy( &p_input_bank->lock );
}

130
/*****************************************************************************
131
 * input_CreateThread: creates a new input thread
132
 *****************************************************************************
133 134 135 136
 * 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.
137
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
138
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
139
{
140 141
    input_thread_t *    p_input;                        /* thread descriptor */

142 143 144
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
145
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
146 147
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
148 149
        return( NULL );
    }
150 151

    /* Initialize thread properties */
Sam Hocevar's avatar
 
Sam Hocevar committed
152 153 154
    p_input->b_die      = 0;
    p_input->b_error    = 0;
    p_input->b_eof      = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
155 156

    /* Set target */
Sam Hocevar's avatar
 
Sam Hocevar committed
157
    p_input->p_source   = p_item->psz_name;
Sam Hocevar's avatar
 
Sam Hocevar committed
158

Sam Hocevar's avatar
 
Sam Hocevar committed
159 160
    /* Set status */
    p_input->i_status   = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
161

162
    /* Initialize stream description */
163 164
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
165
    p_input->stream.i_pgrm_number = 0;
166
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
167
    p_input->stream.b_new_mute = MUTE_NO_CHANGE;
Christophe Massiot's avatar
Christophe Massiot committed
168
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
169

170
    /* no stream, no area */
Stéphane Borel's avatar
 
Stéphane Borel committed
171 172
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
173
    p_input->stream.p_selected_area = NULL;
174
    p_input->stream.p_new_area = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
175 176

    /* By default there is one area in a stream */
Stéphane Borel's avatar
 
Stéphane Borel committed
177
    input_AddArea( p_input );
178
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
Stéphane Borel's avatar
 
Stéphane Borel committed
179

180 181 182 183
    /* 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;
184 185 186 187
    p_input->stream.control.b_grayscale = main_GetIntVariable(
                            VOUT_GRAYSCALE_VAR, VOUT_GRAYSCALE_DEFAULT );
    p_input->stream.control.i_smp = main_GetIntVariable(
                            VDEC_SMP_VAR, VDEC_SMP_DEFAULT );
Michel Kaempf's avatar
Michel Kaempf committed
188

Sam Hocevar's avatar
 
Sam Hocevar committed
189 190
    intf_WarnMsg( 1, "input: playlist item `%s'", p_input->p_source );

Sam Hocevar's avatar
 
Sam Hocevar committed
191
    /* Create thread. */
192 193
    if( vlc_thread_create( &p_input->thread_id, "input", (void *) RunThread,
                           (void *) p_input ) )
Michel Kaempf's avatar
Michel Kaempf committed
194
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
195 196
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
197 198 199
        free( p_input );
        return( NULL );
    }
200

Sam Hocevar's avatar
 
Sam Hocevar committed
201
#if 0
202 203 204 205
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
206
        {
207
            msleep( THREAD_SLEEP );
208
        } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
Sam Hocevar's avatar
 
Sam Hocevar committed
209
                && (i_status != THREAD_FATAL) );
210
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
211 212
#endif

Michel Kaempf's avatar
Michel Kaempf committed
213 214 215
    return( p_input );
}

216
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
217
 * input_StopThread: mark an input thread as zombie
218
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
219
 * This function should not return until the thread is effectively cancelled.
220
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
221
void input_StopThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
222
{
Sam Hocevar's avatar
 
Sam Hocevar committed
223 224
    /* Make the thread exit from a possible vlc_cond_wait() */
    vlc_mutex_lock( &p_input->stream.stream_lock );
225

226 227
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
228

229 230 231
    vlc_cond_signal( &p_input->stream.stream_wait );
    vlc_mutex_unlock( &p_input->stream.stream_lock );

232
    /* If status is NULL, wait until thread has been destroyed */
Sam Hocevar's avatar
 
Sam Hocevar committed
233
#if 0
234 235 236 237 238
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
239 240
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
241
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
#endif
}

/*****************************************************************************
 * input_DestroyThread: mark an input thread as zombie
 *****************************************************************************
 * This function should not return until the thread is effectively cancelled.
 *****************************************************************************/
void input_DestroyThread( input_thread_t *p_input )
{
    /* Join the thread */
    vlc_thread_join( p_input->thread_id );

    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
    vlc_mutex_destroy( &p_input->stream.stream_lock );
    
    /* Free input structure */
    free( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
261 262
}

263
/*****************************************************************************
264
 * RunThread: main thread loop
265
 *****************************************************************************
266
 * Thread in charge of processing the network packets and demultiplexing.
267
 *****************************************************************************/
268
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
269
{
Sam Hocevar's avatar
 
Sam Hocevar committed
270 271 272
    if( InitThread( p_input ) )
    {
        /* If we failed, wait before we are killed, and exit */
Sam Hocevar's avatar
 
Sam Hocevar committed
273
        p_input->i_status = THREAD_ERROR;
Sam Hocevar's avatar
 
Sam Hocevar committed
274 275 276 277 278
        p_input->b_error = 1;
        ErrorThread( p_input );
        DestroyThread( p_input );
        return;
    }
Michel Kaempf's avatar
Michel Kaempf committed
279

Sam Hocevar's avatar
 
Sam Hocevar committed
280 281
    p_input->i_status = THREAD_READY;

282
    /* initialization is complete */
283 284 285 286
    vlc_mutex_lock( &p_input->stream.stream_lock );
    p_input->stream.b_changed = 1;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
287 288
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
    {
289 290 291
        data_packet_t * p_data;
        int i_count, i;

Sam Hocevar's avatar
 
Sam Hocevar committed
292
        p_input->c_loops++;
Sam Hocevar's avatar
 
Sam Hocevar committed
293

294
        vlc_mutex_lock( &p_input->stream.stream_lock );
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
        if( p_input->stream.p_new_program )
        {
            if( p_input->pf_set_program != NULL )
            {

                p_input->pf_set_program( p_input, 
                        p_input->stream.p_new_program );

                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;
                }
            }
            p_input->stream.p_new_program = NULL;
        }
        
319 320
        if( p_input->stream.p_new_area )
        {
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
            if( p_input->stream.b_seekable && p_input->pf_set_area != NULL )
            {

                p_input->pf_set_area( p_input, p_input->stream.p_new_area );

                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;
                }
            }
338 339 340
            p_input->stream.p_new_area = NULL;
        }

341
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
Sam Hocevar's avatar
 
Sam Hocevar committed
342
        {
343 344
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
345 346
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
347 348 349 350 351 352 353 354 355 356 357 358 359

                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;
                }
            }
360
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
Sam Hocevar's avatar
 
Sam Hocevar committed
361
        }
362

363 364 365 366 367 368 369 370 371 372 373 374
        if( p_input->stream.p_removed_es )
        {
            input_UnselectES( p_input, p_input->stream.p_removed_es );
            p_input->stream.p_removed_es = NULL;
        }

        if( p_input->stream.p_newly_selected_es )
        {
            input_SelectES( p_input, p_input->stream.p_newly_selected_es );
            p_input->stream.p_newly_selected_es = NULL;
        }

375 376 377 378 379 380 381 382 383 384 385 386 387 388
        if( p_input->stream.b_new_mute != MUTE_NO_CHANGE )
        {
            if( p_input->stream.b_new_mute )
            {
                input_EscapeAudioDiscontinuity( p_input );
            }

            vlc_mutex_lock( &p_input->stream.control.control_lock );
            p_input->stream.control.b_mute = p_input->stream.b_new_mute;
            vlc_mutex_unlock( &p_input->stream.control.control_lock );

            p_input->stream.b_new_mute = MUTE_NO_CHANGE;
        }

389
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
390

391
        i_count = p_input->pf_read( p_input, &p_data );
Sam Hocevar's avatar
 
Sam Hocevar committed
392

Sam Hocevar's avatar
 
Sam Hocevar committed
393
        /* Demultiplex read packets. */
394
        while( p_data != NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
395
        {
396 397 398
            data_packet_t * p_next = p_data->p_next;
            p_data->p_next = NULL;

399
            p_input->stream.c_packets_read++;
400 401 402
            p_input->pf_demux( p_input, p_data );

            p_data = p_next;
Sam Hocevar's avatar
 
Sam Hocevar committed
403
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
404

405
        if( i_count == 0 && p_input->stream.b_seekable )
Sam Hocevar's avatar
 
Sam Hocevar committed
406
        {
407 408 409 410 411 412 413 414
            /* End of file - we do not set b_die because only the
             * interface is allowed to do so. */
            intf_WarnMsg( 3, "input: EOF reached" );
            p_input->b_eof = 1;
        }
        else if( i_count < 0 )
        {
            p_input->b_error = 1;
415 416 417
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
418
    if( p_input->b_error || p_input->b_eof )
419 420 421
    {
        ErrorThread( p_input );
    }
422

423
    EndThread( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
424 425 426

    DestroyThread( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
427
    intf_DbgMsg("input: Thread end");
428 429
}

430
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
431
 * InitThread: init the input Thread
432
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
433
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
434
{
435 436
    /* Initialize statistics */
    p_input->c_loops                    = 0;
437 438
    p_input->stream.c_packets_read      = 0;
    p_input->stream.c_packets_trashed   = 0;
439
    p_input->p_stream                   = NULL;
Sam Hocevar's avatar
Sam Hocevar committed
440

Sam Hocevar's avatar
 
Sam Hocevar committed
441 442 443 444 445
    /* Set locks. */
    vlc_mutex_init( &p_input->stream.stream_lock );
    vlc_cond_init( &p_input->stream.stream_wait );
    vlc_mutex_init( &p_input->stream.control.control_lock );

446
    /* Find appropriate module. */
Sam Hocevar's avatar
 
Sam Hocevar committed
447
    p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT,
Sam Hocevar's avatar
 
Sam Hocevar committed
448 449
                                 main_GetPszVariable( INPUT_METHOD_VAR, NULL ),
                                 (probedata_t *)p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
450 451

    if( p_input->p_input_module == NULL )
452
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
453 454
        intf_ErrMsg( "input error: no suitable input module for `%s'",
                     p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
455
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
456
    }
457

Sam Hocevar's avatar
 
Sam Hocevar committed
458 459 460
#define f p_input->p_input_module->p_functions->input.functions.input
    p_input->pf_init          = f.pf_init;
    p_input->pf_end           = f.pf_end;
461
    p_input->pf_init_bit_stream= f.pf_init_bit_stream;
Sam Hocevar's avatar
 
Sam Hocevar committed
462
    p_input->pf_read          = f.pf_read;
463
    p_input->pf_set_area      = f.pf_set_area;
464
    p_input->pf_set_program   = f.pf_set_program;
Sam Hocevar's avatar
 
Sam Hocevar committed
465 466 467 468 469 470 471
    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;
Sam Hocevar's avatar
 
Sam Hocevar committed
472

Sam Hocevar's avatar
 
Sam Hocevar committed
473 474 475 476 477
    if( f.pf_open != NULL )
    {
        f.pf_open( p_input );
        p_input->stream.i_method = INPUT_METHOD_DVD;
    }
478
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
479
    /* FIXME : this is waaaay too kludgy */
Sam Hocevar's avatar
 
Sam Hocevar committed
480 481 482 483
    else if( ( strlen( p_input->p_source ) >= 10
               && !strncasecmp( p_input->p_source, "udpstream:", 10 ) )
               || ( strlen( p_input->p_source ) >= 4
                     && !strncasecmp( p_input->p_source, "udp:", 4 ) ) )
484 485 486 487 488
    {
        /* Network stream */
        NetworkOpen( p_input );
        p_input->stream.i_method = INPUT_METHOD_NETWORK;
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
489 490
    else if( ( strlen( p_input->p_source ) > 5 )
               && !strncasecmp( p_input->p_source, "http:", 5 ) )
491 492 493 494 495
    {
        /* HTTP stream */
        HTTPOpen( p_input );
        p_input->stream.i_method = INPUT_METHOD_NETWORK;
    }
496
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
497 498
    else if( ( strlen( p_input->p_source ) == 1 )
               && *p_input->p_source == '-' )
Christophe Massiot's avatar
Christophe Massiot committed
499 500 501 502
    {
        /* Stdin */
        StdOpen( p_input );
    }
503 504 505 506 507 508
    else
    {
        /* File input */
        FileOpen( p_input );
        p_input->stream.i_method = INPUT_METHOD_FILE;
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
509
#undef f
510

Sam Hocevar's avatar
 
Sam Hocevar committed
511
    if( p_input->b_error )
512
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
513
        /* We barfed -- exit nicely */
Sam Hocevar's avatar
 
Sam Hocevar committed
514
        module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
515
        return( -1 );
516
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
517 518

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

520 521 522
    if( p_input->b_error )
    {
        /* We barfed -- exit nicely */
Sam Hocevar's avatar
 
Sam Hocevar committed
523
        CloseThread( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
524
        module_Unneed( p_input->p_input_module );
525 526 527
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
528
    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
529 530
}

531
/*****************************************************************************
532
 * ErrorThread: RunThread() error loop
533
 *****************************************************************************
534
 * This function is called when an error occured during thread main's loop.
535
 *****************************************************************************/
536
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
537
{
538
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
539
    {
540 541
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
542 543 544
    }
}

545
/*****************************************************************************
546
 * EndThread: end the input thread
547
 *****************************************************************************/
548
static void EndThread( input_thread_t * p_input )
549
{
550
    /* Store status */
Sam Hocevar's avatar
 
Sam Hocevar committed
551
    p_input->i_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
552

553
    if( p_main->b_stats )
Sam Hocevar's avatar
 
Sam Hocevar committed
554
    {
555
#ifdef HAVE_SYS_TIMES_H
556 557
        /* Display statistics */
        struct tms  cpu_usage;
Sam Hocevar's avatar
 
Sam Hocevar committed
558 559
        times( &cpu_usage );

560 561 562
        intf_StatMsg( "input stats: %d loops consuming user: %d, system: %d",
                      p_input->c_loops,
                      cpu_usage.tms_utime, cpu_usage.tms_stime );
563 564 565
#else
        intf_StatMsg( "input stats: %d loops", p_input->c_loops );
#endif
566 567

        input_DumpStream( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
568 569 570 571 572 573 574 575
    }

    /* 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
576 577 578 579 580 581 582 583 584 585 586 587 588 589
    /* Close the input method */
    CloseThread( p_input );

    /* Release modules */
    module_Unneed( p_input->p_input_module );
}

/*****************************************************************************
 * CloseThread: close the target
 *****************************************************************************/
static void CloseThread( input_thread_t * p_input )
{
#define f p_input->p_input_module->p_functions->input.functions.input

Sam Hocevar's avatar
 
Sam Hocevar committed
590 591 592 593
    if( f.pf_close != NULL )
    {
        f.pf_close( p_input );
    }
594
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Sam Hocevar's avatar
 
Sam Hocevar committed
595
    /* Close stream */
Sam Hocevar's avatar
 
Sam Hocevar committed
596 597 598 599
    else if( ( strlen( p_input->p_source ) > 10
               && !strncasecmp( p_input->p_source, "udpstream:", 10 ) )
               || ( strlen( p_input->p_source ) > 4
                     && !strncasecmp( p_input->p_source, "udp:", 4 ) ) )
600 601 602
    {
        NetworkClose( p_input );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
603 604
    else if( ( strlen( p_input->p_source ) > 5 )
               && !strncasecmp( p_input->p_source, "http:", 5 ) )
605 606 607
    {
        NetworkClose( p_input );
    }
608
#endif
609 610 611 612
    else
    {
        FileClose( p_input );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
613
#undef f
Sam Hocevar's avatar
 
Sam Hocevar committed
614 615 616 617 618 619 620
}

/*****************************************************************************
 * DestroyThread: destroy the input thread
 *****************************************************************************/
static void DestroyThread( input_thread_t * p_input )
{
621
    /* Update status */
Sam Hocevar's avatar
 
Sam Hocevar committed
622
    p_input->i_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
623
}
624

Christophe Massiot's avatar
Christophe Massiot committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
/*****************************************************************************
 * StdOpen : open standard input
 *****************************************************************************/
static void StdOpen( input_thread_t * p_input )
{
    vlc_mutex_lock( &p_input->stream.stream_lock );

    /* Suppose we can control the pace - this won't work in some cases ! */
    p_input->stream.b_pace_control = 1;

    p_input->stream.b_seekable = 0;
    p_input->stream.p_selected_area->i_size = 0;
    p_input->stream.p_selected_area->i_tell = 0;
    vlc_mutex_unlock( &p_input->stream.stream_lock );

    intf_WarnMsg( 2, "input: opening stdin" );
    p_input->i_handle = 0;
}

Sam Hocevar's avatar
Sam Hocevar committed
644
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
645
 * FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
646
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
647
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
648
{
649
    struct stat         stat_info;
Sam Hocevar's avatar
 
Sam Hocevar committed
650 651 652
    int                 i_stat;

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

Sam Hocevar's avatar
 
Sam Hocevar committed
654
    if( ( i_stat = stat( psz_name, &stat_info ) ) == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
655
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
656 657
        int i_size = strlen( psz_name );

Stéphane Borel's avatar
Stéphane Borel committed
658 659 660 661 662 663 664
        if( ( i_size > 8 )
            && !strncasecmp( psz_name, "dvdread:", 8 ) )
        {
            /* get rid of the 'dvdread:' stuff and try again */
            psz_name += 8;
            i_stat = stat( psz_name, &stat_info );
        }
665
        else if( ( i_size > 4 )
Sam Hocevar's avatar
 
Sam Hocevar committed
666 667 668 669 670
            && !strncasecmp( psz_name, "dvd:", 4 ) )
        {
            /* get rid of the 'dvd:' stuff and try again */
            psz_name += 4;
            i_stat = stat( psz_name, &stat_info );
Henri Fallon's avatar
 
Henri Fallon committed
671
        }
672 673
        else if( ( i_size > 4 )
                 && !strncasecmp( psz_name, "vcd:", 4 ) )
674 675 676 677 678
        {
            /* get rid of the 'vcd:' stuff and try again */
            psz_name += 4;
            i_stat = stat( psz_name, &stat_info );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
679
        else if( ( i_size > 5 )
Sam Hocevar's avatar
 
Sam Hocevar committed
680 681 682 683 684
                 && !strncasecmp( psz_name, "file:", 5 ) )
        {
            /* get rid of the 'file:' stuff and try again */
            psz_name += 5;
            i_stat = stat( psz_name, &stat_info );
Henri Fallon's avatar
 
Henri Fallon committed
685
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
686

Sam Hocevar's avatar
 
Sam Hocevar committed
687
        if( i_stat == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
688 689 690 691 692 693
        {
            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
694 695 696 697 698 699 700 701
    }

    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)
Sam Hocevar's avatar
 
Sam Hocevar committed
702
         || S_ISBLK(stat_info.st_mode) )
Sam Hocevar's avatar
 
Sam Hocevar committed
703 704
    {
        p_input->stream.b_seekable = 1;
705
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
706
    }
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
707
    else if( S_ISFIFO(stat_info.st_mode)
Sam Hocevar's avatar
 
Sam Hocevar committed
708
#if !defined( SYS_BEOS ) && !defined( WIN32 )
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
709 710 711
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
Sam Hocevar's avatar
Sam Hocevar committed
712
    {
713
        p_input->stream.b_seekable = 0;
714
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
 
Benoit Steiner committed
715 716 717
    }
    else
    {
718
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
719
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
720
                     psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
721 722 723
        p_input->b_error = 1;
        return;
    }
724

725
    p_input->stream.p_selected_area->i_tell = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
726 727
    vlc_mutex_unlock( &p_input->stream.stream_lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
728
    intf_WarnMsg( 2, "input: opening file `%s'", p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
729
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
 
Sam Hocevar committed
730 731
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
732
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
 
Sam Hocevar committed
733 734
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
735 736 737
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
738 739

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
740
 * FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
741
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
742
static void FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
743
{
Sam Hocevar's avatar
 
Sam Hocevar committed
744
    intf_WarnMsg( 2, "input: closing file `%s'", p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
745

Sam Hocevar's avatar
 
Sam Hocevar committed
746
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
747

Sam Hocevar's avatar
 
Sam Hocevar committed
748
    return;
Stéphane Borel's avatar
Stéphane Borel committed
749
}
Sam Hocevar's avatar
 
Sam Hocevar committed
750

751
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Henri Fallon's avatar
 
Henri Fallon committed
752
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
753
 * NetworkOpen : open a network socket 
Henri Fallon's avatar
 
Henri Fallon committed
754
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
755
static void NetworkOpen( input_thread_t * p_input )
Henri Fallon's avatar
 
Henri Fallon committed
756
{
Henri Fallon's avatar
 
Henri Fallon committed
757
    char                *psz_server = NULL;
758 759 760
    char                *psz_bind = NULL;
    int                 i_server_port = 0;
    int                 i_bind_port = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
761
    int                 i_opt;
762
    int                 i_opt_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
763
    struct sockaddr_in  sock;
764

765 766
    /* Get the remote server. Syntax is :
     * udp[stream]:[/][/][serveraddr[:serverport]][@[bindaddr]:[bindport]] */
Sam Hocevar's avatar
 
Sam Hocevar committed
767 768
    if( p_input->p_source != NULL )
    {
769 770 771
        char * psz_parser = p_input->p_source;
        char * psz_server_port = NULL;
        char * psz_bind_port = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
772 773

        /* Skip the protocol name */
774
        while( *psz_parser && *psz_parser != ':' )
Sam Hocevar's avatar
 
Sam Hocevar committed
775
        {
776
            psz_parser++;
Sam Hocevar's avatar
 
Sam Hocevar committed
777 778 779
        }

        /* Skip the "://" part */
780
        while( *psz_parser && (*psz_parser == ':' || *psz_parser == '/') )
Sam Hocevar's avatar
 
Sam Hocevar committed
781
        {
782
            psz_parser++;
Sam Hocevar's avatar
 
Sam Hocevar committed
783 784
        }

785
        if( *psz_parser && *psz_parser != '@' )
Sam Hocevar's avatar
 
Sam Hocevar committed
786
        {
787 788
            /* Found server */
            psz_server = psz_parser;
Sam Hocevar's avatar
 
Sam Hocevar committed
789

790
            while( *psz_parser && *psz_parser != ':' && *psz_parser != '@' )
Sam Hocevar's avatar
 
Sam Hocevar committed
791
            {
792
                psz_parser++;
Sam Hocevar's avatar
 
Sam Hocevar committed
793 794
            }

795
            if( *psz_parser == ':' )
Sam Hocevar's avatar
 
Sam Hocevar committed
796
            {
797 798 799 800
                /* Found server port */
                *psz_parser = '\0'; /* Terminate server name */
                psz_parser++;
                psz_server_port = psz_parser;
Sam Hocevar's avatar
 
Sam Hocevar committed
801

802
                while( *psz_parser && *psz_parser != '@' )
803
                {
804
                    psz_parser++;
805
                }
806 807
            }
        }
808

809 810 811 812 813 814 815 816 817 818 819 820
        if( *psz_parser == '@' )
        {
            /* Found bind address or bind port */
            *psz_parser = '\0'; /* Terminate server port or name if necessary */
            psz_parser++;

            if( *psz_parser && *psz_parser != ':' )
            {
                /* Found bind address */
                psz_bind = psz_parser;

                while( *psz_parser && *psz_parser != ':' )
821
                {
822
                    psz_parser++;
823
                }
Sam Hocevar's avatar
 
Sam Hocevar committed
824
            }
825 826 827 828 829 830 831 832 833

            if( *psz_parser == ':' )
            {
                /* Found bind port */
                *psz_parser = '\0'; /* Terminate bind address if necessary */
                psz_parser++;

                psz_bind_port = psz_parser;
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
834
        }
835 836 837

        /* Convert ports format */
        if( psz_server_port != NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
838
        {
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
            i_server_port = strtol( psz_server_port, &psz_parser, 10 );
            if( *psz_parser )
            {
                intf_ErrMsg( "input error: cannot parse server port near %s",
                             psz_parser );
                p_input->b_error = 1;
                return;
            }
        }

        if( psz_bind_port != NULL )
        {
            i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
            if( *psz_parser )
            {
                intf_ErrMsg( "input error: cannot parse bind port near %s",
                             psz_parser );
                p_input->b_error = 1;
                return;
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
859 860
        }
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
861 862 863 864 865
    else
    {
        /* This is required or NetworkClose will never be called */
        p_input->p_source = "ts: network input";
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
866 867

    /* Check that we got a valid port */
868
    if( i_bind_port == 0 )
869
    {
870
        i_bind_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
871 872
    }

873 874
    intf_WarnMsg( 2, "input: server=%s:%d local=%s:%d",
                     psz_server, i_server_port, psz_bind, i_bind_port );
875

Henri Fallon's avatar
 
Henri Fallon committed
876 877 878 879 880
    /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
     * protocol */
    p_input->i_handle = socket( AF_INET, SOCK_DGRAM, 0 );
    if( p_input->i_handle == -1 )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
881
        intf_ErrMsg( "input error: can't create socket (%s)", strerror(errno) );
Henri Fallon's avatar
 
Henri Fallon committed
882 883 884 885 886
        p_input->b_error = 1;
        return;
    }

    /* We may want to reuse an already used socket */
Sam Hocevar's avatar
 
Sam Hocevar committed
887
    i_opt = 1;
Henri Fallon's avatar
 
Henri Fallon committed
888
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
889
                    (void *) &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
890
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
891 892
        intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
                     strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
893 894 895 896 897 898 899
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
     * packet loss caused by scheduling problems */
Sam Hocevar's avatar
 
Sam Hocevar committed
900 901
    i_opt = 0x80000;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
902
                    (void *) &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
903
    {
904
        intf_WarnMsg( 1, "input warning: can't configure socket (SO_RCVBUF: %s)", 
Sam Hocevar's avatar
 
Sam Hocevar committed
905
                         strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
906 907
    }

908 909 910 911 912 913 914 915
    /* Check if we really got what we have asked for, because Linux, etc.
     * will silently limit the max buffer size to net.core.rmem_max which
     * is typically only 65535 bytes */
    i_opt = 0;
    i_opt_size = sizeof( i_opt );
    if( getsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
                    (void*) &i_opt, &i_opt_size ) == -1 )
    {
916
        intf_WarnMsg( 1, "input warning: can't query socket (SO_RCVBUF: %s)", 
Sam Hocevar's avatar
 
Sam Hocevar committed
917
                         strerror(errno));
918
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
919
    else if( i_opt < 0x80000 )
920
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
921 922
        intf_WarnMsg( 1, "input warning: socket buffer size is 0x%x"
                         " instead of 0x%x", i_opt, 0x80000 );
923 924
    }

Henri Fallon's avatar
 
Henri Fallon committed
925
    /* Build the local socket */
926
    if ( network_BuildAddr( &sock, psz_bind, i_bind_port ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
927
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
928
        intf_ErrMsg( "input error: can't build local address" );
Henri Fallon's avatar
 
Henri Fallon committed
929 930 931 932
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
933

Henri Fallon's avatar
 
Henri Fallon committed
934