input.c 24.5 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.114 2001/05/30 22:16:07 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
#include "defs.h"

31 32 33 34 35 36
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
37 38 39
#ifdef STRNCASECMP_IN_STRINGS_H
#   include <strings.h>
#endif
40
#include <errno.h>
Michel Kaempf's avatar
Michel Kaempf committed
41

42 43 44 45 46 47 48
/* WinSock Includes */

#ifdef WIN32
#include <winsock2.h>
#endif


Henri Fallon's avatar
 
Henri Fallon committed
49 50
/* Network functions */

Sam Hocevar's avatar
 
Sam Hocevar committed
51 52
#if !defined( SYS_BEOS ) && !defined( SYS_NTO ) && !defined( WIN32 )
#include <netdb.h>                                            /* hostent ... */
Henri Fallon's avatar
 
Henri Fallon committed
53 54 55 56 57
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
58
#endif
Henri Fallon's avatar
 
Henri Fallon committed
59

60 61 62 63
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
64
#include "config.h"
65 66
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
67
#include "mtime.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
68
#include "netutils.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
69
#include "modules.h"
70

71
#include "intf_msg.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
72
#include "intf_playlist.h"
73

74 75 76
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,  
Michel Lespinasse committed
77

78
#include "input.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
79 80 81
#include "interface.h"

#include "main.h"
Michel Kaempf's avatar
Michel Kaempf committed
82

Henri Fallon's avatar
 
Henri Fallon committed
83

84
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
85
 * Local prototypes
86
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
87 88 89 90 91
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
92

Sam Hocevar's avatar
 
Sam Hocevar committed
93 94 95 96 97
static void FileOpen        ( input_thread_t *p_input );
static void FileClose       ( input_thread_t *p_input );
static void NetworkOpen     ( input_thread_t *p_input );
static void NetworkClose    ( input_thread_t *p_input );

98
/*****************************************************************************
99
 * input_CreateThread: creates a new input thread
100
 *****************************************************************************
101 102 103 104
 * 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.
105
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
106
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
107
{
108 109 110
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

111 112 113
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
114
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
115 116
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
117 118
        return( NULL );
    }
119

120 121 122
    /* Packets read once */
    p_input->i_read_once = INPUT_READ_ONCE;

123 124 125
    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
126 127 128 129 130
    p_input->b_eof              = 0;

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

131
    /* I have never understood that stuff --Meuuh */
132 133
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
134

135
    /* Initialize stream description */
136 137
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
138
    p_input->stream.i_pgrm_number = 0;
139
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
Christophe Massiot's avatar
Christophe Massiot committed
140
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
141

142
    /* no stream, no area */
Stéphane Borel's avatar
 
Stéphane Borel committed
143 144
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
145
    p_input->stream.p_selected_area = NULL;
146
    p_input->stream.p_new_area = NULL;
Stéphane Borel's avatar
 
Stéphane Borel committed
147 148
    /* By default there is one areas in a stream */
    input_AddArea( p_input );
149
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
Stéphane Borel's avatar
 
Stéphane Borel committed
150

151 152 153 154 155
    /* 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
156

Sam Hocevar's avatar
 
Sam Hocevar committed
157 158 159 160 161 162 163 164
    /* Setup callbacks */
    p_input->pf_file_open     = FileOpen;
    p_input->pf_file_close    = FileClose;
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
    p_input->pf_network_open  = NetworkOpen;
    p_input->pf_network_close = NetworkClose;
#endif

Michel Kaempf's avatar
Michel Kaempf committed
165
    /* Create thread and set locks. */
166
    vlc_mutex_init( &p_input->stream.stream_lock );
167
    vlc_cond_init( &p_input->stream.stream_wait );
168 169 170
    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
171
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
172 173
        intf_ErrMsg( "input error: can't create input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
174 175 176
        free( p_input );
        return( NULL );
    }
177

178 179 180 181
    /* If status is NULL, wait until the thread is created */
    if( pi_status == NULL )
    {
        do
182
        {
183
            msleep( THREAD_SLEEP );
184
        } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
Sam Hocevar's avatar
 
Sam Hocevar committed
185
                && (i_status != THREAD_FATAL) );
186 187
        if( i_status != THREAD_READY )
        {
188 189
            return( NULL );
        }
190
    }
Michel Kaempf's avatar
Michel Kaempf committed
191 192 193
    return( p_input );
}

194
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
195
 * input_DestroyThread: mark an input thread as zombie
196
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
197
 * This function should not return until the thread is effectively cancelled.
198
 *****************************************************************************/
199
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
200
{
201
    int         i_status;                                   /* thread status */
202 203 204

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

207 208
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
209

210 211 212 213 214
    /* 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 );

215 216 217 218 219 220
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
221 222
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
223
    }
Michel Kaempf's avatar
Michel Kaempf committed
224 225
}

226
/*****************************************************************************
227
 * RunThread: main thread loop
228
 *****************************************************************************
229
 * Thread in charge of processing the network packets and demultiplexing.
230
 *****************************************************************************/
231
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
232
{
233
    int                     i_error, i;
Michel Kaempf's avatar
Michel Kaempf committed
234

Sam Hocevar's avatar
 
Sam Hocevar committed
235 236 237 238 239 240 241 242 243 244
    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
245

246 247 248 249 250
    /* initialization is completed */
    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
251
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
252
    {
253
        data_packet_t *         pp_packets[p_input->i_read_once];
254

Sam Hocevar's avatar
 
Sam Hocevar committed
255
#ifdef STATS
Sam Hocevar's avatar
 
Sam Hocevar committed
256
        p_input->c_loops++;
Sam Hocevar's avatar
 
Sam Hocevar committed
257 258
#endif

259
        vlc_mutex_lock( &p_input->stream.stream_lock );
260

261 262 263 264 265 266
        if( p_input->stream.p_new_area )
        {
            p_input->pf_set_area( p_input, p_input->stream.p_new_area );
            p_input->stream.p_new_area = NULL;
        }

267
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
Sam Hocevar's avatar
 
Sam Hocevar committed
268
        {
269 270
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
271 272
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
273 274 275 276 277 278 279 280 281 282 283 284 285

                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;
                }
            }
286
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
Sam Hocevar's avatar
 
Sam Hocevar committed
287
        }
288

289 290 291 292 293 294 295 296 297 298 299 300
        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;
        }

301
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
302 303

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

Sam Hocevar's avatar
 
Sam Hocevar committed
305
        /* Demultiplex read packets. */
306
        for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
307 308 309
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
310

Sam Hocevar's avatar
 
Sam Hocevar committed
311 312 313
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
314
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
315 316
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
317
                intf_WarnMsg( 3, "input: EOF reached" );
Sam Hocevar's avatar
 
Sam Hocevar committed
318
                p_input->b_eof = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
319
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
320
            else
Sam Hocevar's avatar
 
Sam Hocevar committed
321
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
322
                p_input->b_error = 1;
Sam Hocevar's avatar
Sam Hocevar committed
323
            }
324 325 326
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
327
    if( p_input->b_error || p_input->b_eof )
328 329 330
    {
        ErrorThread( p_input );
    }
331

332
    EndThread( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
333 334 335

    DestroyThread( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
336
    intf_DbgMsg("input: Thread end");
337 338
}

339
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
340
 * InitThread: init the input Thread
341
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
342
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
343 344 345
{

#ifdef STATS
346 347 348 349 350 351
    /* 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
352
#endif
Sam Hocevar's avatar
Sam Hocevar committed
353

Sam Hocevar's avatar
 
Sam Hocevar committed
354 355 356 357
    /* Default, might get overwritten */
    p_input->pf_open = p_input->pf_file_open;
    p_input->pf_close = p_input->pf_file_close;

Sam Hocevar's avatar
 
Sam Hocevar committed
358
    p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT,
Sam Hocevar's avatar
 
Sam Hocevar committed
359
                                           (probedata_t *)p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
360 361

    if( p_input->p_input_module == NULL )
362
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
363 364
        intf_ErrMsg( "input error: no suitable input module for `%s'",
                     p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
365
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
366
    }
367

Sam Hocevar's avatar
 
Sam Hocevar committed
368 369
#define f p_input->p_input_module->p_functions->input.functions.input
    p_input->pf_init          = f.pf_init;
Sam Hocevar's avatar
 
Sam Hocevar committed
370 371 372 373 374 375 376 377
    if( f.pf_open != NULL )
    {
        p_input->pf_open          = f.pf_open;
    }
    if( f.pf_close != NULL )
    {
        p_input->pf_close         = f.pf_close;
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
378 379
    p_input->pf_end           = f.pf_end;
    p_input->pf_read          = f.pf_read;
380
    p_input->pf_set_area      = f.pf_set_area;
Sam Hocevar's avatar
 
Sam Hocevar committed
381 382 383 384 385 386 387 388 389
    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 );
390

Sam Hocevar's avatar
 
Sam Hocevar committed
391
    if( p_input->b_error )
392
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
393 394
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
395
        module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
396
        return( -1 );
397
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
398 399

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

401 402 403 404
    if( p_input->b_error )
    {
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
405
        module_Unneed( p_input->p_input_module );
406 407 408
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
409
    *p_input->pi_status = THREAD_READY;
Sam Hocevar's avatar
 
Sam Hocevar committed
410 411

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
412 413
}

414
/*****************************************************************************
415
 * ErrorThread: RunThread() error loop
416
 *****************************************************************************
417
 * This function is called when an error occured during thread main's loop.
418
 *****************************************************************************/
419
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
420
{
421
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
422
    {
423 424
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
425 426 427
    }
}

428
/*****************************************************************************
429
 * EndThread: end the input thread
430
 *****************************************************************************/
431
static void EndThread( input_thread_t * p_input )
432
{
433
    int *       pi_status;                                  /* thread status */
434

435 436 437
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
438

Sam Hocevar's avatar
 
Sam Hocevar committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
#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
455 456 457
    /* Close stream */
    p_input->pf_close( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
458
    /* Release modules */
Sam Hocevar's avatar
 
Sam Hocevar committed
459
    module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
460

Sam Hocevar's avatar
 
Sam Hocevar committed
461 462 463 464 465 466 467 468 469 470 471
}

/*****************************************************************************
 * 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
472

Henri Fallon's avatar
 
Henri Fallon committed
473 474
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
 
Henri Fallon committed
475
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
 
Henri Fallon committed
476
    
477
    /* Free input structure */
478
    free( p_input );
479

480 481
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
482
}
483

Sam Hocevar's avatar
Sam Hocevar committed
484
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
485
 * FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
486
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
487
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
488
{
489
    struct stat         stat_info;
Sam Hocevar's avatar
 
Sam Hocevar committed
490 491 492
    int                 i_stat;

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

Sam Hocevar's avatar
 
Sam Hocevar committed
494 495 496
    /* 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
497
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
498 499 500 501 502 503 504 505
        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 );
Henri Fallon's avatar
 
Henri Fallon committed
506
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
507
        else if( ( i_size > 5 )
Sam Hocevar's avatar
 
Sam Hocevar committed
508 509 510 511 512
                 && !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
513
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
514

Sam Hocevar's avatar
 
Sam Hocevar committed
515
        if( i_stat == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
516 517 518 519 520 521
        {
            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
522 523 524 525 526 527 528 529 530 531 532
    }

    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;
533
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
534
    }
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
535
    else if( S_ISFIFO(stat_info.st_mode)
Sam Hocevar's avatar
 
Sam Hocevar committed
536
#if !defined( SYS_BEOS ) && !defined( WIN32 )
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
537 538 539
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
Sam Hocevar's avatar
Sam Hocevar committed
540
    {
541
        p_input->stream.b_seekable = 0;
542
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
 
Benoit Steiner committed
543 544 545
    }
    else
    {
546
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
547
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
548
                     psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
549 550 551
        p_input->b_error = 1;
        return;
    }
552

553
    p_input->stream.p_selected_area->i_tell = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
554 555
    vlc_mutex_unlock( &p_input->stream.stream_lock );

556
    intf_WarnMsg( 1, "input: opening file `%s'", p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
557
#ifndef WIN32
Sam Hocevar's avatar
 
Sam Hocevar committed
558
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
 
Sam Hocevar committed
559
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
560 561 562 563
#else
    if( (p_input->i_handle = open( psz_name, O_BINARY
                                   /*O_NONBLOCK | O_LARGEFILE*/ )) == (-1) )
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
564
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
565
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
 
Sam Hocevar committed
566 567
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
568 569 570
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
571 572

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
573
 * FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
574
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
575
static void FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
576
{
577
    intf_WarnMsg( 1, "input: closing file `%s'", p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
578
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
579

Sam Hocevar's avatar
 
Sam Hocevar committed
580
    return;
Stéphane Borel's avatar
Stéphane Borel committed
581
}
Sam Hocevar's avatar
 
Sam Hocevar committed
582

Henri Fallon's avatar
 
Henri Fallon committed
583

584
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Henri Fallon's avatar
 
Henri Fallon committed
585
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
586
 * NetworkOpen : open a network socket 
Henri Fallon's avatar
 
Henri Fallon committed
587
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
588
static void NetworkOpen( input_thread_t * p_input )
Henri Fallon's avatar
 
Henri Fallon committed
589
{
Henri Fallon's avatar
 
Henri Fallon committed
590
    char                *psz_server = NULL;
591
    char                *psz_broadcast = NULL;
Henri Fallon's avatar
 
Henri Fallon committed
592
    int                 i_port = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
593 594
    int                 i_opt;
    struct sockaddr_in  sock;
595

Sam Hocevar's avatar
 
Sam Hocevar committed
596 597 598 599 600 601 602 603 604 605 606
#ifdef WIN32
    /* WinSock Library Init. */
    WSADATA Data;
    int i_err = WSAStartup( MAKEWORD( 1, 1 ), &Data );

    if( i_err )
    {
        intf_ErrMsg( "input: can't initiate WinSocks, error %i", i_err );
        return ;
    }
#endif
Henri Fallon's avatar
 
Henri Fallon committed
607 608
    
    /* Get the remote server */
Sam Hocevar's avatar
 
Sam Hocevar committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
    if( p_input->p_source != NULL )
    {
        psz_server = p_input->p_source;

        /* Skip the protocol name */
        while( *psz_server && *psz_server != ':' )
        {
            psz_server++;
        }

        /* Skip the "://" part */
        while( *psz_server && (*psz_server == ':' || *psz_server == '/') )
        {
            psz_server++;
        }

        /* Found a server name */
        if( *psz_server )
        {
            char *psz_port = psz_server;

            /* Skip the hostname part */
            while( *psz_port && *psz_port != ':' )
            {
                psz_port++;
            }

            /* Found a port name */
            if( *psz_port )
            {
                /* Replace ':' with '\0' */
                *psz_port = '\0';
                psz_port++;

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
                psz_broadcast = psz_port;
                while( *psz_broadcast && *psz_broadcast != ':' )
                {
                    psz_broadcast++;
                }

                if( *psz_broadcast )
                {
                    *psz_broadcast = '\0';
                    psz_broadcast++;
                    while( *psz_broadcast && *psz_broadcast == ':' )
                    {
                        psz_broadcast++;
                    }
                }
                else
                {
                    psz_broadcast = NULL;
                }

                /* port before broadcast address */
                if( *psz_port != ':' )
                {
                    i_port = atoi( psz_port );
                }
Sam Hocevar's avatar
 
Sam Hocevar committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
            }
        }
        else
        {
            psz_server = NULL;
        }
    }

    /* Check that we got a valid server */
    if( psz_server == NULL )
    {
        psz_server = main_GetPszVariable( INPUT_SERVER_VAR, 
                                          INPUT_SERVER_DEFAULT );
    }

    /* Check that we got a valid port */
    if( i_port == 0 )
Henri Fallon's avatar
 
Henri Fallon committed
685
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
686
        i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
Henri Fallon's avatar
 
Henri Fallon committed
687
    }
688 689 690 691

    if( psz_broadcast == NULL )
    {
        /* Are we broadcasting ? */
692 693 694 695 696 697 698 699 700 701
        if( main_GetIntVariable( INPUT_BROADCAST_VAR,
                                 INPUT_BROADCAST_DEFAULT ) )
        {
            psz_broadcast = main_GetPszVariable( INPUT_BCAST_ADDR_VAR,
                                                 INPUT_BCAST_ADDR_DEFAULT );
        }
        else
        {
           psz_broadcast = NULL; 
        }
702 703 704 705 706
    }

    intf_WarnMsg( 2, "input: server: %s port: %d broadcast: %s",
                     psz_server, i_port, psz_broadcast );

Henri Fallon's avatar
 
Henri Fallon committed
707 708 709 710 711
    /* 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
712
        intf_ErrMsg("input error: can't create socket : %s", strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
713 714 715 716 717
        p_input->b_error = 1;
        return;
    }

    /* We may want to reuse an already used socket */
Sam Hocevar's avatar
 
Sam Hocevar committed
718
    i_opt = 1;
Henri Fallon's avatar
 
Henri Fallon committed
719
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
Sam Hocevar's avatar
 
Sam Hocevar committed
720
                    &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
721
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
722 723
        intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
                     strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
724 725 726 727 728 729 730
        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
731 732 733
    i_opt = 0x80000;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
                    &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
734
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
735 736
        intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", 
                     strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
737 738 739 740 741 742
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build the local socket */
Sam Hocevar's avatar
 
Sam Hocevar committed
743
    if ( network_BuildLocalAddr( &sock, i_port, psz_broadcast ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
744
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
745
        intf_ErrMsg( "input error: can't build local address" );
Henri Fallon's avatar
 
Henri Fallon committed
746 747 748 749 750 751
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
    
    /* Bind it */
Sam Hocevar's avatar
 
Sam Hocevar committed
752 753
    if( bind( p_input->i_handle, (struct sockaddr *)&sock, 
              sizeof( sock ) ) < 0 )
Henri Fallon's avatar
 
Henri Fallon committed
754
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
755
        intf_ErrMsg("input error: can't bind socket (%s)", strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
756 757 758 759 760 761
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build socket for remote connection */
Sam Hocevar's avatar
 
Sam Hocevar committed
762
    if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
763
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
764
        intf_ErrMsg( "input error: can't build remote address" );
Henri Fallon's avatar
 
Henri Fallon committed
765 766 767 768 769 770
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* And connect it ... should we really connect ? */
Sam Hocevar's avatar
 
Sam Hocevar committed
771 772
    if( connect( p_input->i_handle, (struct sockaddr *) &sock,
                 sizeof( sock ) ) == (-1) )
Henri Fallon's avatar
 
Henri Fallon committed
773
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
774
        intf_ErrMsg( "input error: can't connect socket, %s", 
Henri Fallon's avatar
 
Henri Fallon committed
775
                     strerror(errno) );
Henri Fallon's avatar
 
Henri Fallon committed
776 777 778 779 780 781 782
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* We can't pace control, but FIXME : bug in meuuh's code to sync PCR
     * with the server. */
Henri Fallon's avatar
 
Henri Fallon committed
783
    p_input->stream.b_pace_control = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
784
    p_input->stream.b_seekable = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
785 786

    intf_WarnMsg( 3, "input: successfully opened network mode" );
Henri Fallon's avatar
 
Henri Fallon committed
787 788 789 790 791
    
    return;
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
792
 * NetworkClose : close a network socket
Henri Fallon's avatar
 
Henri Fallon committed
793
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
794
static void NetworkClose( input_thread_t * p_input )
Henri Fallon's avatar
 
Henri Fallon committed
795 796
{
    close( p_input->i_handle );
797

Sam Hocevar's avatar
 
Sam Hocevar committed
798 799 800
#ifdef WIN32 
    WSACleanup();
#endif
801

Henri Fallon's avatar
 
Henri Fallon committed
802
}
Sam Hocevar's avatar
 
Sam Hocevar committed
803
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
804