input.c 24.9 KB
Newer Older
1 2
/*****************************************************************************
 * input.c: input thread
Michel Kaempf's avatar
Michel Kaempf committed
3 4
 * Read an MPEG2 stream, demultiplex and parse it before sending it to
 * decoders.
5 6
 *****************************************************************************
 * Copyright (C) 1998, 1999, 2000 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
7
 * $Id: input.c,v 1.115 2001/05/31 01:37:08 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
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
35 36

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

42
#include <string.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
43 44 45
#ifdef STRNCASECMP_IN_STRINGS_H
#   include <strings.h>
#endif
46
#include <errno.h>
Michel Kaempf's avatar
Michel Kaempf committed
47

48 49 50 51 52 53 54
/* WinSock Includes */

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


Henri Fallon's avatar
 
Henri Fallon committed
55 56
/* Network functions */

Sam Hocevar's avatar
 
Sam Hocevar committed
57 58
#if !defined( SYS_BEOS ) && !defined( SYS_NTO ) && !defined( WIN32 )
#include <netdb.h>                                            /* hostent ... */
Henri Fallon's avatar
 
Henri Fallon committed
59 60 61 62 63
#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
64
#endif
Henri Fallon's avatar
 
Henri Fallon committed
65

66 67 68 69
#ifdef STATS
#   include <sys/times.h>
#endif

Michel Kaempf's avatar
Michel Kaempf committed
70
#include "config.h"
71 72
#include "common.h"
#include "threads.h"
Michel Kaempf's avatar
Michel Kaempf committed
73
#include "mtime.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
74
#include "netutils.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
75
#include "modules.h"
76

77
#include "intf_msg.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
78
#include "intf_playlist.h"
79

80 81 82
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
Michel Lespinasse's avatar
Yop,  
Michel Lespinasse committed
83

84
#include "input.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
85 86 87
#include "interface.h"

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

Henri Fallon's avatar
 
Henri Fallon committed
89

90
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
91
 * Local prototypes
92
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
93 94 95 96 97
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
98

Sam Hocevar's avatar
 
Sam Hocevar committed
99 100 101 102 103
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 );

104
/*****************************************************************************
105
 * input_CreateThread: creates a new input thread
106
 *****************************************************************************
107 108 109 110
 * 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.
111
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
112
input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
113
{
114 115 116
    input_thread_t *    p_input;                        /* thread descriptor */
    int                 i_status;                           /* thread status */

117 118 119
    /* Allocate descriptor */
    p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
120
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
121 122
        intf_ErrMsg( "input error: can't allocate input thread (%s)",
                     strerror(errno) );
Michel Kaempf's avatar
Michel Kaempf committed
123 124
        return( NULL );
    }
125

126 127 128
    /* Packets read once */
    p_input->i_read_once = INPUT_READ_ONCE;

129 130 131
    /* Initialize thread properties */
    p_input->b_die              = 0;
    p_input->b_error            = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
132 133 134 135 136
    p_input->b_eof              = 0;

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

137
    /* I have never understood that stuff --Meuuh */
138 139
    p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
    *p_input->pi_status         = THREAD_CREATE;
Michel Kaempf's avatar
Michel Kaempf committed
140

141
    /* Initialize stream description */
142 143
    p_input->stream.i_es_number = 0;
    p_input->stream.i_selected_es_number = 0;
144
    p_input->stream.i_pgrm_number = 0;
145
    p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
Christophe Massiot's avatar
Christophe Massiot committed
146
    p_input->stream.i_mux_rate = 0;
Michel Kaempf's avatar
Michel Kaempf committed
147

148
    /* no stream, no area */
Stéphane Borel's avatar
 
Stéphane Borel committed
149 150
    p_input->stream.i_area_nb = 0;
    p_input->stream.pp_areas = NULL;
151
    p_input->stream.p_selected_area = NULL;
152
    p_input->stream.p_new_area = NULL;
Stéphane Borel's avatar
 
Stéphane Borel committed
153 154
    /* By default there is one areas in a stream */
    input_AddArea( p_input );
155
    p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
Stéphane Borel's avatar
 
Stéphane Borel committed
156

157 158 159 160 161
    /* 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
162

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

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

200
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
201
 * input_DestroyThread: mark an input thread as zombie
202
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
203
 * This function should not return until the thread is effectively cancelled.
204
 *****************************************************************************/
205
void input_DestroyThread( input_thread_t *p_input, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
206
{
207
    int         i_status;                                   /* thread status */
208 209 210

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

213 214
    /* Request thread destruction */
    p_input->b_die = 1;
Michel Kaempf's avatar
Michel Kaempf committed
215

216 217 218 219 220
    /* 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 );

221 222 223 224 225 226
    /* If status is NULL, wait until thread has been destroyed */
    if( pi_status == NULL )
    {
        do
        {
            msleep( THREAD_SLEEP );
227 228
        } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
                  && (i_status != THREAD_FATAL) );
229
    }
Michel Kaempf's avatar
Michel Kaempf committed
230 231
}

232
/*****************************************************************************
233
 * RunThread: main thread loop
234
 *****************************************************************************
235
 * Thread in charge of processing the network packets and demultiplexing.
236
 *****************************************************************************/
237
static void RunThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
238
{
239
    int                     i_error, i;
Sam Hocevar's avatar
 
Sam Hocevar committed
240
    data_packet_t **        pp_packets;
Michel Kaempf's avatar
Michel Kaempf committed
241

Sam Hocevar's avatar
 
Sam Hocevar committed
242 243 244 245 246 247 248 249 250 251
    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
252

253 254 255 256 257
    /* 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
258 259 260
    pp_packets = (data_packet_t **) malloc( p_input->i_read_once *
                                        sizeof( data_packet_t * ) );
    if( pp_packets == NULL )
261
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
262 263 264
        intf_ErrMsg( "input error: out of memory" );
        p_input->b_error = 1;
    }
265

Sam Hocevar's avatar
 
Sam Hocevar committed
266 267
    while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
268
#ifdef STATS
Sam Hocevar's avatar
 
Sam Hocevar committed
269
        p_input->c_loops++;
Sam Hocevar's avatar
 
Sam Hocevar committed
270 271
#endif

272
        vlc_mutex_lock( &p_input->stream.stream_lock );
273

274 275 276 277 278 279
        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;
        }

280
        if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
Sam Hocevar's avatar
 
Sam Hocevar committed
281
        {
282 283
            if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
            {
284 285
                p_input->pf_seek( p_input,
                                  p_input->stream.p_selected_area->i_seek );
286 287 288 289 290 291 292 293 294 295 296 297 298

                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;
                }
            }
299
            p_input->stream.p_selected_area->i_seek = NO_SEEK;
Sam Hocevar's avatar
 
Sam Hocevar committed
300
        }
301

302 303 304 305 306 307 308 309 310 311 312 313
        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;
        }

314
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
315 316

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

Sam Hocevar's avatar
 
Sam Hocevar committed
318
        /* Demultiplex read packets. */
319
        for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
320 321 322
        {
            p_input->pf_demux( p_input, pp_packets[i] );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
323

Sam Hocevar's avatar
 
Sam Hocevar committed
324 325 326
        if( i_error )
        {
            if( i_error == 1 )
Sam Hocevar's avatar
Sam Hocevar committed
327
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
328 329
                /* End of file - we do not set b_die because only the
                 * interface is allowed to do so. */
330
                intf_WarnMsg( 3, "input: EOF reached" );
Sam Hocevar's avatar
 
Sam Hocevar committed
331
                p_input->b_eof = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
332
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
333
            else
Sam Hocevar's avatar
 
Sam Hocevar committed
334
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
335
                p_input->b_error = 1;
Sam Hocevar's avatar
Sam Hocevar committed
336
            }
337 338 339
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
340 341
    free( pp_packets );

Sam Hocevar's avatar
 
Sam Hocevar committed
342
    if( p_input->b_error || p_input->b_eof )
343 344 345
    {
        ErrorThread( p_input );
    }
346

347
    EndThread( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
348 349 350

    DestroyThread( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
351
    intf_DbgMsg("input: Thread end");
352 353
}

354
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
355
 * InitThread: init the input Thread
356
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
357
static int InitThread( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
358 359 360
{

#ifdef STATS
361 362 363 364 365 366
    /* 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
367
#endif
Sam Hocevar's avatar
Sam Hocevar committed
368

Sam Hocevar's avatar
 
Sam Hocevar committed
369 370 371 372
    /* 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
373
    p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT,
Sam Hocevar's avatar
 
Sam Hocevar committed
374
                                           (probedata_t *)p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
375 376

    if( p_input->p_input_module == NULL )
377
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
378 379
        intf_ErrMsg( "input error: no suitable input module for `%s'",
                     p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
380
        return( -1 );
Michel Kaempf's avatar
Michel Kaempf committed
381
    }
382

Sam Hocevar's avatar
 
Sam Hocevar committed
383 384
#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
385 386 387 388 389 390 391 392
    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
393 394
    p_input->pf_end           = f.pf_end;
    p_input->pf_read          = f.pf_read;
395
    p_input->pf_set_area      = f.pf_set_area;
Sam Hocevar's avatar
 
Sam Hocevar committed
396 397 398 399 400 401 402 403 404
    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 );
405

Sam Hocevar's avatar
 
Sam Hocevar committed
406
    if( p_input->b_error )
407
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
408 409
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
410
        module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
411
        return( -1 );
412
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
413 414

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

416 417 418 419
    if( p_input->b_error )
    {
        /* We barfed -- exit nicely */
        p_input->pf_close( p_input );
Sam Hocevar's avatar
 
Sam Hocevar committed
420
        module_Unneed( p_input->p_input_module );
421 422 423
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
424
    *p_input->pi_status = THREAD_READY;
Sam Hocevar's avatar
 
Sam Hocevar committed
425 426

    return( 0 );
Michel Kaempf's avatar
Michel Kaempf committed
427 428
}

429
/*****************************************************************************
430
 * ErrorThread: RunThread() error loop
431
 *****************************************************************************
432
 * This function is called when an error occured during thread main's loop.
433
 *****************************************************************************/
434
static void ErrorThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
435
{
436
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
437
    {
438 439
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
440 441 442
    }
}

443
/*****************************************************************************
444
 * EndThread: end the input thread
445
 *****************************************************************************/
446
static void EndThread( input_thread_t * p_input )
447
{
448
    int *       pi_status;                                  /* thread status */
449

450 451 452
    /* Store status */
    pi_status = p_input->pi_status;
    *pi_status = THREAD_END;
Sam Hocevar's avatar
Sam Hocevar committed
453

Sam Hocevar's avatar
 
Sam Hocevar committed
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
#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
470 471 472
    /* Close stream */
    p_input->pf_close( p_input );

Sam Hocevar's avatar
 
Sam Hocevar committed
473
    /* Release modules */
Sam Hocevar's avatar
 
Sam Hocevar committed
474
    module_Unneed( p_input->p_input_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
475

Sam Hocevar's avatar
 
Sam Hocevar committed
476 477 478 479 480 481 482 483 484 485 486
}

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

Henri Fallon's avatar
 
Henri Fallon committed
488 489
    /* Destroy Mutex locks */
    vlc_mutex_destroy( &p_input->stream.control.control_lock );
Henri Fallon's avatar
 
Henri Fallon committed
490
    vlc_mutex_destroy( &p_input->stream.stream_lock );
Henri Fallon's avatar
 
Henri Fallon committed
491
    
492
    /* Free input structure */
493
    free( p_input );
494

495 496
    /* Update status */
    *pi_status = THREAD_OVER;
Sam Hocevar's avatar
Sam Hocevar committed
497
}
498

Sam Hocevar's avatar
Sam Hocevar committed
499
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
500
 * FileOpen : open a file descriptor
Sam Hocevar's avatar
Sam Hocevar committed
501
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
502
static void FileOpen( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
503
{
504
    struct stat         stat_info;
Sam Hocevar's avatar
 
Sam Hocevar committed
505 506 507
    int                 i_stat;

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

Sam Hocevar's avatar
 
Sam Hocevar committed
509 510 511
    /* 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
512
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
513 514 515 516 517 518 519 520
        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
521
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
522
        else if( ( i_size > 5 )
Sam Hocevar's avatar
 
Sam Hocevar committed
523 524 525 526 527
                 && !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
528
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
529

Sam Hocevar's avatar
 
Sam Hocevar committed
530
        if( i_stat == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
531 532 533 534 535 536
        {
            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
537 538 539 540 541 542 543 544 545 546 547
    }

    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;
548
        p_input->stream.p_selected_area->i_size = stat_info.st_size;
Sam Hocevar's avatar
 
Sam Hocevar committed
549
    }
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
550
    else if( S_ISFIFO(stat_info.st_mode)
Sam Hocevar's avatar
 
Sam Hocevar committed
551
#if !defined( SYS_BEOS ) && !defined( WIN32 )
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
552 553 554
             || S_ISSOCK(stat_info.st_mode)
#endif
             )
Sam Hocevar's avatar
Sam Hocevar committed
555
    {
556
        p_input->stream.b_seekable = 0;
557
        p_input->stream.p_selected_area->i_size = 0;
Benoit Steiner's avatar
 
Benoit Steiner committed
558 559 560
    }
    else
    {
561
        vlc_mutex_unlock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
562
        intf_ErrMsg( "input error: unknown file type for `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
563
                     psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
564 565 566
        p_input->b_error = 1;
        return;
    }
567

568
    p_input->stream.p_selected_area->i_tell = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
569 570
    vlc_mutex_unlock( &p_input->stream.stream_lock );

571
    intf_WarnMsg( 1, "input: opening file `%s'", p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
572
#ifndef WIN32
Sam Hocevar's avatar
 
Sam Hocevar committed
573
    if( (p_input->i_handle = open( psz_name,
Sam Hocevar's avatar
 
Sam Hocevar committed
574
                                   /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
Sam Hocevar's avatar
 
Sam Hocevar committed
575 576 577 578
#else
    if( (p_input->i_handle = open( psz_name, O_BINARY
                                   /*O_NONBLOCK | O_LARGEFILE*/ )) == (-1) )
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
579
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
580
        intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
Sam Hocevar's avatar
 
Sam Hocevar committed
581 582
        p_input->b_error = 1;
        return;
Michel Kaempf's avatar
Michel Kaempf committed
583 584 585
    }

}
Stéphane Borel's avatar
Stéphane Borel committed
586 587

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
588
 * FileClose : close a file descriptor
Stéphane Borel's avatar
Stéphane Borel committed
589
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
590
static void FileClose( input_thread_t * p_input )
Stéphane Borel's avatar
Stéphane Borel committed
591
{
592
    intf_WarnMsg( 1, "input: closing file `%s'", p_input->p_source );
Sam Hocevar's avatar
 
Sam Hocevar committed
593
    close( p_input->i_handle );
Stéphane Borel's avatar
Stéphane Borel committed
594

Sam Hocevar's avatar
 
Sam Hocevar committed
595
    return;
Stéphane Borel's avatar
Stéphane Borel committed
596
}
Sam Hocevar's avatar
 
Sam Hocevar committed
597

Henri Fallon's avatar
 
Henri Fallon committed
598

599
#if !defined( SYS_BEOS ) && !defined( SYS_NTO )
Henri Fallon's avatar
 
Henri Fallon committed
600
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
601
 * NetworkOpen : open a network socket 
Henri Fallon's avatar
 
Henri Fallon committed
602
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
603
static void NetworkOpen( input_thread_t * p_input )
Henri Fallon's avatar
 
Henri Fallon committed
604
{
Henri Fallon's avatar
 
Henri Fallon committed
605
    char                *psz_server = NULL;
606
    char                *psz_broadcast = NULL;
Henri Fallon's avatar
 
Henri Fallon committed
607
    int                 i_port = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
608 609
    int                 i_opt;
    struct sockaddr_in  sock;
610

Sam Hocevar's avatar
 
Sam Hocevar committed
611 612 613 614 615 616 617 618 619 620 621
#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
622 623
    
    /* Get the remote server */
Sam Hocevar's avatar
 
Sam Hocevar committed
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
    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++;

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
                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
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
            }
        }
        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
700
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
701
        i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
Henri Fallon's avatar
 
Henri Fallon committed
702
    }
703 704 705 706

    if( psz_broadcast == NULL )
    {
        /* Are we broadcasting ? */
707 708 709 710 711 712 713 714 715 716
        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; 
        }
717 718 719 720 721
    }

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

Henri Fallon's avatar
 
Henri Fallon committed
722 723 724 725 726
    /* 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
727
        intf_ErrMsg("input error: can't create socket : %s", strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
728 729 730 731 732
        p_input->b_error = 1;
        return;
    }

    /* We may want to reuse an already used socket */
Sam Hocevar's avatar
 
Sam Hocevar committed
733
    i_opt = 1;
Henri Fallon's avatar
 
Henri Fallon committed
734
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
Sam Hocevar's avatar
 
Sam Hocevar committed
735
                    (void*) &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
736
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
737 738
        intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
                     strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
739 740 741 742 743 744 745
        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
746 747
    i_opt = 0x80000;
    if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
Sam Hocevar's avatar
 
Sam Hocevar committed
748
                    (void*) &i_opt, sizeof( i_opt ) ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
749
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
750 751
        intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", 
                     strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
752 753 754 755 756 757
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build the local socket */
Sam Hocevar's avatar
 
Sam Hocevar committed
758
    if ( network_BuildLocalAddr( &sock, i_port, psz_broadcast ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
759
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
760
        intf_ErrMsg( "input error: can't build local address" );
Henri Fallon's avatar
 
Henri Fallon committed
761 762 763 764 765 766
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }
    
    /* Bind it */
Sam Hocevar's avatar
 
Sam Hocevar committed
767 768
    if( bind( p_input->i_handle, (struct sockaddr *)&sock, 
              sizeof( sock ) ) < 0 )
Henri Fallon's avatar
 
Henri Fallon committed
769
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
770
        intf_ErrMsg("input error: can't bind socket (%s)", strerror(errno));
Henri Fallon's avatar
 
Henri Fallon committed
771 772 773 774 775 776
        close( p_input->i_handle );
        p_input->b_error = 1;
        return;
    }

    /* Build socket for remote connection */
Sam Hocevar's avatar
 
Sam Hocevar committed
777
    if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 )
Henri Fallon's avatar
 
Henri Fallon committed
778
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
779
        intf_ErrMsg( "input error: can't build remote address" );
Henri Fallon's avatar
 
Henri Fallon committed
780 781 782 783 784 785
        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
786 787
    if( connect( p_input->i_handle, (struct sockaddr *) &sock,
                 sizeof( sock ) ) == (-1) )
Henri Fallon's avatar
 
Henri Fallon committed
788
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
789
        intf_ErrMsg( "input error: can't connect socket, %s", 
Henri Fallon's avatar
 
Henri Fallon committed
790
                     strerror(errno) );
Henri Fallon's avatar
 
Henri Fallon committed
791 792 793 794 795 796 797
        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
798
    p_input->stream.b_pace_control = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
799
    p_input->stream.b_seekable = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
800 801

    intf_WarnMsg( 3, "input: successfully opened network mode" );
Henri Fallon's avatar
 
Henri Fallon committed
802 803 804 805 806
    
    return;
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
807
 * NetworkClose : close a network socket
Henri Fallon's avatar
 
Henri Fallon committed
808
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
809
static void NetworkClose( input_thread_t * p_input )
Henri Fallon's avatar
 
Henri Fallon committed
810 811
{
    close( p_input->i_handle );
812

Sam Hocevar's avatar
 
Sam Hocevar committed
813 814 815
#ifdef WIN32 
    WSACleanup();
#endif
816

Henri Fallon's avatar
 
Henri Fallon committed
817
}
Sam Hocevar's avatar
 
Sam Hocevar committed
818
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
819