]> git.sesse.net Git - vlc/blob - src/input/input.c
Some heavy changes today:
[vlc] / src / input / input.c
1 /*****************************************************************************
2  * input.c: input thread
3  * Read an MPEG2 stream, demultiplex and parse it before sending it to
4  * decoders.
5  *****************************************************************************
6  * Copyright (C) 1998-2001 VideoLAN
7  * $Id: input.c,v 1.167 2001/12/30 07:09:56 sam Exp $
8  *
9  * Authors: Christophe Massiot <massiot@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33
34 #include <videolan/vlc.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>
38 #elif defined( _MSC_VER ) && defined( _WIN32 )
39 #   include <io.h>
40 #endif
41
42 #include <string.h>
43 #include <errno.h>
44
45 #ifdef STRNCASECMP_IN_STRINGS_H
46 #   include <strings.h>
47 #endif
48
49 #ifdef WIN32
50 #   include <winsock2.h>
51 #   include <ws2tcpip.h>
52 #elif !defined( SYS_BEOS ) && !defined( SYS_NTO )
53 #   include <netdb.h>                                         /* hostent ... */
54 #   include <sys/socket.h>
55 #   include <netinet/in.h>
56 #   ifdef HAVE_ARPA_INET_H
57 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
58 #   endif
59 #endif
60
61 #ifdef HAVE_SYS_TIMES_H
62 #   include <sys/times.h>
63 #endif
64
65 #include "netutils.h"
66
67 #include "intf_playlist.h"
68
69 #include "stream_control.h"
70 #include "input_ext-intf.h"
71 #include "input_ext-dec.h"
72 #include "input_ext-plugins.h"
73
74 #include "interface.h"
75
76 /*****************************************************************************
77  * Local prototypes
78  *****************************************************************************/
79 static void RunThread       ( input_thread_t *p_input );
80 static  int InitThread      ( input_thread_t *p_input );
81 static void ErrorThread     ( input_thread_t *p_input );
82 static void CloseThread     ( input_thread_t *p_input );
83 static void DestroyThread   ( input_thread_t *p_input );
84 static void EndThread       ( input_thread_t *p_input );
85
86 static void FileOpen        ( input_thread_t *p_input );
87 static void StdOpen         ( input_thread_t *p_input );
88 static void FileClose       ( input_thread_t *p_input );
89 #if !defined( SYS_BEOS ) && !defined( SYS_NTO )
90 static void NetworkOpen     ( input_thread_t *p_input );
91 static void HTTPOpen        ( input_thread_t *p_input );
92 static void NetworkClose    ( input_thread_t *p_input );
93 #endif
94
95 /*****************************************************************************
96  * input_CreateThread: creates a new input thread
97  *****************************************************************************
98  * This function creates a new input, and returns a pointer
99  * to its description. On error, it returns NULL.
100  * If pi_status is NULL, then the function will block until the thread is ready.
101  * If not, it will be updated using one of the THREAD_* constants.
102  *****************************************************************************/
103 input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status )
104 {
105     input_thread_t *    p_input;                        /* thread descriptor */
106     int                 i_status;                           /* thread status */
107
108     /* Allocate descriptor */
109     p_input = (input_thread_t *)malloc( sizeof(input_thread_t) );
110     if( p_input == NULL )
111     {
112         intf_ErrMsg( "input error: can't allocate input thread (%s)",
113                      strerror(errno) );
114         return( NULL );
115     }
116
117     /* Initialize thread properties */
118     p_input->b_die              = 0;
119     p_input->b_error            = 0;
120     p_input->b_eof              = 0;
121
122     /* Set target */
123     p_input->p_source           = p_item->psz_name;
124
125     /* I have never understood that stuff --Meuuh */
126     p_input->pi_status          = (pi_status != NULL) ? pi_status : &i_status;
127     *p_input->pi_status         = THREAD_CREATE;
128
129     /* Initialize stream description */
130     p_input->stream.i_es_number = 0;
131     p_input->stream.i_selected_es_number = 0;
132     p_input->stream.i_pgrm_number = 0;
133     p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
134     p_input->stream.b_new_mute = MUTE_NO_CHANGE;
135     p_input->stream.i_mux_rate = 0;
136
137     /* no stream, no area */
138     p_input->stream.i_area_nb = 0;
139     p_input->stream.pp_areas = NULL;
140     p_input->stream.p_selected_area = NULL;
141     p_input->stream.p_new_area = NULL;
142
143     /* By default there is one area in a stream */
144     input_AddArea( p_input );
145     p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
146
147     /* Initialize stream control properties. */
148     p_input->stream.control.i_status = PLAYING_S;
149     p_input->stream.control.i_rate = DEFAULT_RATE;
150     p_input->stream.control.b_mute = 0;
151     p_input->stream.control.b_grayscale = main_GetIntVariable(
152                             VOUT_GRAYSCALE_VAR, VOUT_GRAYSCALE_DEFAULT );
153     p_input->stream.control.i_smp = main_GetIntVariable(
154                             VDEC_SMP_VAR, VDEC_SMP_DEFAULT );
155
156     intf_WarnMsg( 1, "input: playlist item `%s'", p_input->p_source );
157
158     /* Create thread. */
159     if( vlc_thread_create( &p_input->thread_id, "input", (void *) RunThread,
160                            (void *) p_input ) )
161     {
162         intf_ErrMsg( "input error: can't create input thread (%s)",
163                      strerror(errno) );
164         free( p_input );
165         return( NULL );
166     }
167
168     /* If status is NULL, wait until the thread is created */
169     if( pi_status == NULL )
170     {
171         do
172         {
173             msleep( THREAD_SLEEP );
174         } while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
175                 && (i_status != THREAD_FATAL) );
176         if( i_status != THREAD_READY )
177         {
178             return( NULL );
179         }
180     }
181     return( p_input );
182 }
183
184 /*****************************************************************************
185  * input_DestroyThread: mark an input thread as zombie
186  *****************************************************************************
187  * This function should not return until the thread is effectively cancelled.
188  *****************************************************************************/
189 void input_DestroyThread( input_thread_t *p_input, int *pi_status )
190 {
191     int         i_status;                                   /* thread status */
192
193     /* Set status */
194     p_input->pi_status = (pi_status != NULL) ? pi_status : &i_status;
195     *p_input->pi_status = THREAD_DESTROY;
196
197     /* Request thread destruction */
198     p_input->b_die = 1;
199
200     /* Make the thread exit from a possible vlc_cond_wait() */
201     vlc_mutex_lock( &p_input->stream.stream_lock );
202     vlc_cond_signal( &p_input->stream.stream_wait );
203     vlc_mutex_unlock( &p_input->stream.stream_lock );
204
205     /* If status is NULL, wait until thread has been destroyed */
206     if( pi_status == NULL )
207     {
208         do
209         {
210             msleep( THREAD_SLEEP );
211         } while ( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
212                   && (i_status != THREAD_FATAL) );
213     }
214 }
215
216 /*****************************************************************************
217  * RunThread: main thread loop
218  *****************************************************************************
219  * Thread in charge of processing the network packets and demultiplexing.
220  *****************************************************************************/
221 static void RunThread( input_thread_t *p_input )
222 {
223     if( InitThread( p_input ) )
224     {
225         /* If we failed, wait before we are killed, and exit */
226         *p_input->pi_status = THREAD_ERROR;
227         p_input->b_error = 1;
228         ErrorThread( p_input );
229         DestroyThread( p_input );
230         return;
231     }
232
233     /* initialization is complete */
234     vlc_mutex_lock( &p_input->stream.stream_lock );
235     p_input->stream.b_changed = 1;
236     vlc_mutex_unlock( &p_input->stream.stream_lock );
237
238     while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
239     {
240         data_packet_t * p_data;
241         int i_count, i;
242
243         p_input->c_loops++;
244
245         vlc_mutex_lock( &p_input->stream.stream_lock );
246
247         if( p_input->stream.p_new_program )
248         {
249             if( p_input->pf_set_program != NULL )
250             {
251
252                 p_input->pf_set_program( p_input, 
253                         p_input->stream.p_new_program );
254
255                 for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
256                 {
257                     pgrm_descriptor_t * p_pgrm
258                                             = p_input->stream.pp_programs[i];
259                     /* Escape all decoders for the stream discontinuity they
260                      * will encounter. */
261                     input_EscapeDiscontinuity( p_input, p_pgrm );
262
263                     /* Reinitialize synchro. */
264                     p_pgrm->i_synchro_state = SYNCHRO_REINIT;
265                 }
266             }
267             p_input->stream.p_new_program = NULL;
268         }
269         
270         if( p_input->stream.p_new_area )
271         {
272             if( p_input->stream.b_seekable && p_input->pf_set_area != NULL )
273             {
274
275                 p_input->pf_set_area( p_input, p_input->stream.p_new_area );
276
277                 for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
278                 {
279                     pgrm_descriptor_t * p_pgrm
280                                             = p_input->stream.pp_programs[i];
281                     /* Escape all decoders for the stream discontinuity they
282                      * will encounter. */
283                     input_EscapeDiscontinuity( p_input, p_pgrm );
284
285                     /* Reinitialize synchro. */
286                     p_pgrm->i_synchro_state = SYNCHRO_REINIT;
287                 }
288             }
289             p_input->stream.p_new_area = NULL;
290         }
291
292         if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
293         {
294             if( p_input->stream.b_seekable && p_input->pf_seek != NULL )
295             {
296                 p_input->pf_seek( p_input,
297                                   p_input->stream.p_selected_area->i_seek );
298
299                 for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
300                 {
301                     pgrm_descriptor_t * p_pgrm
302                                             = p_input->stream.pp_programs[i];
303                     /* Escape all decoders for the stream discontinuity they
304                      * will encounter. */
305                     input_EscapeDiscontinuity( p_input, p_pgrm );
306
307                     /* Reinitialize synchro. */
308                     p_pgrm->i_synchro_state = SYNCHRO_REINIT;
309                 }
310             }
311             p_input->stream.p_selected_area->i_seek = NO_SEEK;
312         }
313
314         if( p_input->stream.p_removed_es )
315         {
316             input_UnselectES( p_input, p_input->stream.p_removed_es );
317             p_input->stream.p_removed_es = NULL;
318         }
319
320         if( p_input->stream.p_newly_selected_es )
321         {
322             input_SelectES( p_input, p_input->stream.p_newly_selected_es );
323             p_input->stream.p_newly_selected_es = NULL;
324         }
325
326         if( p_input->stream.b_new_mute != MUTE_NO_CHANGE )
327         {
328             if( p_input->stream.b_new_mute )
329             {
330                 input_EscapeAudioDiscontinuity( p_input );
331             }
332
333             vlc_mutex_lock( &p_input->stream.control.control_lock );
334             p_input->stream.control.b_mute = p_input->stream.b_new_mute;
335             vlc_mutex_unlock( &p_input->stream.control.control_lock );
336
337             p_input->stream.b_new_mute = MUTE_NO_CHANGE;
338         }
339
340         vlc_mutex_unlock( &p_input->stream.stream_lock );
341
342         i_count = p_input->pf_read( p_input, &p_data );
343
344         /* Demultiplex read packets. */
345         while( p_data != NULL )
346         {
347             data_packet_t * p_next = p_data->p_next;
348             p_data->p_next = NULL;
349
350             p_input->stream.c_packets_read++;
351             p_input->pf_demux( p_input, p_data );
352
353             p_data = p_next;
354         }
355
356         if( i_count == 0 && p_input->stream.b_seekable )
357         {
358             /* End of file - we do not set b_die because only the
359              * interface is allowed to do so. */
360             intf_WarnMsg( 3, "input: EOF reached" );
361             p_input->b_eof = 1;
362         }
363         else if( i_count < 0 )
364         {
365             p_input->b_error = 1;
366         }
367     }
368
369     if( p_input->b_error || p_input->b_eof )
370     {
371         ErrorThread( p_input );
372     }
373
374     EndThread( p_input );
375
376     DestroyThread( p_input );
377
378     intf_DbgMsg("input: Thread end");
379 }
380
381 /*****************************************************************************
382  * InitThread: init the input Thread
383  *****************************************************************************/
384 static int InitThread( input_thread_t * p_input )
385 {
386     /* Initialize statistics */
387     p_input->c_loops                    = 0;
388     p_input->stream.c_packets_read      = 0;
389     p_input->stream.c_packets_trashed   = 0;
390     p_input->p_stream                   = NULL;
391
392     /* Set locks. */
393     vlc_mutex_init( &p_input->stream.stream_lock );
394     vlc_cond_init( &p_input->stream.stream_wait );
395     vlc_mutex_init( &p_input->stream.control.control_lock );
396
397     /* Find appropriate module. */
398     p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT,
399                                  main_GetPszVariable( INPUT_METHOD_VAR, NULL ),
400                                  (probedata_t *)p_input );
401
402     if( p_input->p_input_module == NULL )
403     {
404         intf_ErrMsg( "input error: no suitable input module for `%s'",
405                      p_input->p_source );
406         return( -1 );
407     }
408
409 #define f p_input->p_input_module->p_functions->input.functions.input
410     p_input->pf_init          = f.pf_init;
411     p_input->pf_end           = f.pf_end;
412     p_input->pf_init_bit_stream= f.pf_init_bit_stream;
413     p_input->pf_read          = f.pf_read;
414     p_input->pf_set_area      = f.pf_set_area;
415     p_input->pf_set_program   = f.pf_set_program;
416     p_input->pf_demux         = f.pf_demux;
417     p_input->pf_new_packet    = f.pf_new_packet;
418     p_input->pf_new_pes       = f.pf_new_pes;
419     p_input->pf_delete_packet = f.pf_delete_packet;
420     p_input->pf_delete_pes    = f.pf_delete_pes;
421     p_input->pf_rewind        = f.pf_rewind;
422     p_input->pf_seek          = f.pf_seek;
423
424     if( f.pf_open != NULL )
425     {
426         f.pf_open( p_input );
427         p_input->stream.i_method = INPUT_METHOD_DVD;
428     }
429 #if !defined( SYS_BEOS ) && !defined( SYS_NTO )
430     /* FIXME : this is waaaay too kludgy */
431     else if( ( strlen( p_input->p_source ) >= 10
432                && !strncasecmp( p_input->p_source, "udpstream:", 10 ) )
433                || ( strlen( p_input->p_source ) >= 4
434                      && !strncasecmp( p_input->p_source, "udp:", 4 ) ) )
435     {
436         /* Network stream */
437         NetworkOpen( p_input );
438         p_input->stream.i_method = INPUT_METHOD_NETWORK;
439     }
440     else if( ( strlen( p_input->p_source ) > 5 )
441                && !strncasecmp( p_input->p_source, "http:", 5 ) )
442     {
443         /* HTTP stream */
444         HTTPOpen( p_input );
445         p_input->stream.i_method = INPUT_METHOD_NETWORK;
446     }
447 #endif
448     else if( ( strlen( p_input->p_source ) == 1 )
449                && *p_input->p_source == '-' )
450     {
451         /* Stdin */
452         StdOpen( p_input );
453     }
454     else
455     {
456         /* File input */
457         FileOpen( p_input );
458         p_input->stream.i_method = INPUT_METHOD_FILE;
459     }
460 #undef f
461
462     if( p_input->b_error )
463     {
464         /* We barfed -- exit nicely */
465         module_Unneed( p_input->p_input_module );
466         return( -1 );
467     }
468
469     p_input->pf_init( p_input );
470
471     if( p_input->b_error )
472     {
473         /* We barfed -- exit nicely */
474         CloseThread( p_input );
475         module_Unneed( p_input->p_input_module );
476         return( -1 );
477     }
478
479     *p_input->pi_status = THREAD_READY;
480
481     return( 0 );
482 }
483
484 /*****************************************************************************
485  * ErrorThread: RunThread() error loop
486  *****************************************************************************
487  * This function is called when an error occured during thread main's loop.
488  *****************************************************************************/
489 static void ErrorThread( input_thread_t *p_input )
490 {
491     while( !p_input->b_die )
492     {
493         /* Sleep a while */
494         msleep( INPUT_IDLE_SLEEP );
495     }
496 }
497
498 /*****************************************************************************
499  * EndThread: end the input thread
500  *****************************************************************************/
501 static void EndThread( input_thread_t * p_input )
502 {
503     int *       pi_status;                                  /* thread status */
504
505     /* Store status */
506     pi_status = p_input->pi_status;
507     *pi_status = THREAD_END;
508
509     if( p_main->b_stats )
510     {
511 #ifdef HAVE_SYS_TIMES_H
512         /* Display statistics */
513         struct tms  cpu_usage;
514         times( &cpu_usage );
515
516         intf_StatMsg( "input stats: %d loops consuming user: %d, system: %d",
517                       p_input->c_loops,
518                       cpu_usage.tms_utime, cpu_usage.tms_stime );
519 #else
520         intf_StatMsg( "input stats: %d loops", p_input->c_loops );
521 #endif
522
523         input_DumpStream( p_input );
524     }
525
526     /* Free all ES and destroy all decoder threads */
527     input_EndStream( p_input );
528
529     /* Free demultiplexer's data */
530     p_input->pf_end( p_input );
531
532     /* Close the input method */
533     CloseThread( p_input );
534
535     /* Release modules */
536     module_Unneed( p_input->p_input_module );
537 }
538
539 /*****************************************************************************
540  * CloseThread: close the target
541  *****************************************************************************/
542 static void CloseThread( input_thread_t * p_input )
543 {
544 #define f p_input->p_input_module->p_functions->input.functions.input
545
546     if( f.pf_close != NULL )
547     {
548         f.pf_close( p_input );
549     }
550 #if !defined( SYS_BEOS ) && !defined( SYS_NTO )
551     /* Close stream */
552     else if( ( strlen( p_input->p_source ) > 10
553                && !strncasecmp( p_input->p_source, "udpstream:", 10 ) )
554                || ( strlen( p_input->p_source ) > 4
555                      && !strncasecmp( p_input->p_source, "udp:", 4 ) ) )
556     {
557         NetworkClose( p_input );
558     }
559     else if( ( strlen( p_input->p_source ) > 5 )
560                && !strncasecmp( p_input->p_source, "http:", 5 ) )
561     {
562         NetworkClose( p_input );
563     }
564 #endif
565     else
566     {
567         FileClose( p_input );
568     }
569 #undef f
570 }
571
572 /*****************************************************************************
573  * DestroyThread: destroy the input thread
574  *****************************************************************************/
575 static void DestroyThread( input_thread_t * p_input )
576 {
577     int *       pi_status;                                  /* thread status */
578
579     /* Store status */
580     pi_status = p_input->pi_status;
581
582     /* Destroy Mutex locks */
583     vlc_mutex_destroy( &p_input->stream.control.control_lock );
584     vlc_mutex_destroy( &p_input->stream.stream_lock );
585     
586     /* Free input structure */
587     free( p_input );
588
589     /* Update status */
590     *pi_status = THREAD_OVER;
591 }
592
593 /*****************************************************************************
594  * StdOpen : open standard input
595  *****************************************************************************/
596 static void StdOpen( input_thread_t * p_input )
597 {
598     vlc_mutex_lock( &p_input->stream.stream_lock );
599
600     /* Suppose we can control the pace - this won't work in some cases ! */
601     p_input->stream.b_pace_control = 1;
602
603     p_input->stream.b_seekable = 0;
604     p_input->stream.p_selected_area->i_size = 0;
605     p_input->stream.p_selected_area->i_tell = 0;
606     vlc_mutex_unlock( &p_input->stream.stream_lock );
607
608     intf_WarnMsg( 2, "input: opening stdin" );
609     p_input->i_handle = 0;
610 }
611
612 /*****************************************************************************
613  * FileOpen : open a file descriptor
614  *****************************************************************************/
615 static void FileOpen( input_thread_t * p_input )
616 {
617     struct stat         stat_info;
618     int                 i_stat;
619
620     char *psz_name = p_input->p_source;
621
622     if( ( i_stat = stat( psz_name, &stat_info ) ) == (-1) )
623     {
624         int i_size = strlen( psz_name );
625
626         if( ( i_size > 8 )
627             && !strncasecmp( psz_name, "dvdread:", 8 ) )
628         {
629             /* get rid of the 'dvdread:' stuff and try again */
630             psz_name += 8;
631             i_stat = stat( psz_name, &stat_info );
632         }
633         else if( ( i_size > 4 )
634             && !strncasecmp( psz_name, "dvd:", 4 ) )
635         {
636             /* get rid of the 'dvd:' stuff and try again */
637             psz_name += 4;
638             i_stat = stat( psz_name, &stat_info );
639         }
640         else if( ( i_size > 4 )
641                  && !strncasecmp( psz_name, "vcd:", 4 ) )
642         {
643             /* get rid of the 'vcd:' stuff and try again */
644             psz_name += 4;
645             i_stat = stat( psz_name, &stat_info );
646         }
647         else if( ( i_size > 5 )
648                  && !strncasecmp( psz_name, "file:", 5 ) )
649         {
650             /* get rid of the 'file:' stuff and try again */
651             psz_name += 5;
652             i_stat = stat( psz_name, &stat_info );
653         }
654
655         if( i_stat == (-1) )
656         {
657             intf_ErrMsg( "input error: cannot stat() file `%s' (%s)",
658                          psz_name, strerror(errno));
659             p_input->b_error = 1;
660             return;
661         }
662     }
663
664     vlc_mutex_lock( &p_input->stream.stream_lock );
665
666     /* If we are here we can control the pace... */
667     p_input->stream.b_pace_control = 1;
668
669     if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode)
670          || S_ISBLK(stat_info.st_mode) )
671     {
672         p_input->stream.b_seekable = 1;
673         p_input->stream.p_selected_area->i_size = stat_info.st_size;
674     }
675     else if( S_ISFIFO(stat_info.st_mode)
676 #if !defined( SYS_BEOS ) && !defined( WIN32 )
677              || S_ISSOCK(stat_info.st_mode)
678 #endif
679              )
680     {
681         p_input->stream.b_seekable = 0;
682         p_input->stream.p_selected_area->i_size = 0;
683     }
684     else
685     {
686         vlc_mutex_unlock( &p_input->stream.stream_lock );
687         intf_ErrMsg( "input error: unknown file type for `%s'",
688                      psz_name );
689         p_input->b_error = 1;
690         return;
691     }
692
693     p_input->stream.p_selected_area->i_tell = 0;
694     vlc_mutex_unlock( &p_input->stream.stream_lock );
695
696     intf_WarnMsg( 2, "input: opening file `%s'", p_input->p_source );
697     if( (p_input->i_handle = open( psz_name,
698                                    /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) )
699     {
700         intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) );
701         p_input->b_error = 1;
702         return;
703     }
704
705 }
706
707 /*****************************************************************************
708  * FileClose : close a file descriptor
709  *****************************************************************************/
710 static void FileClose( input_thread_t * p_input )
711 {
712     intf_WarnMsg( 2, "input: closing file `%s'", p_input->p_source );
713
714     close( p_input->i_handle );
715
716     return;
717 }
718
719 #if !defined( SYS_BEOS ) && !defined( SYS_NTO )
720 /*****************************************************************************
721  * NetworkOpen : open a network socket 
722  *****************************************************************************/
723 static void NetworkOpen( input_thread_t * p_input )
724 {
725     char                *psz_server = NULL;
726     char                *psz_bind = NULL;
727     int                 i_server_port = 0;
728     int                 i_bind_port = 0;
729     int                 i_opt;
730     int                 i_opt_size;
731     struct sockaddr_in  sock;
732
733     /* Get the remote server. Syntax is :
734      * udp[stream]:[/][/][serveraddr[:serverport]][@[bindaddr]:[bindport]] */
735     if( p_input->p_source != NULL )
736     {
737         char * psz_parser = p_input->p_source;
738         char * psz_server_port = NULL;
739         char * psz_bind_port = NULL;
740
741         /* Skip the protocol name */
742         while( *psz_parser && *psz_parser != ':' )
743         {
744             psz_parser++;
745         }
746
747         /* Skip the "://" part */
748         while( *psz_parser && (*psz_parser == ':' || *psz_parser == '/') )
749         {
750             psz_parser++;
751         }
752
753         if( *psz_parser && *psz_parser != '@' )
754         {
755             /* Found server */
756             psz_server = psz_parser;
757
758             while( *psz_parser && *psz_parser != ':' && *psz_parser != '@' )
759             {
760                 psz_parser++;
761             }
762
763             if( *psz_parser == ':' )
764             {
765                 /* Found server port */
766                 *psz_parser = '\0'; /* Terminate server name */
767                 psz_parser++;
768                 psz_server_port = psz_parser;
769
770                 while( *psz_parser && *psz_parser != '@' )
771                 {
772                     psz_parser++;
773                 }
774             }
775         }
776
777         if( *psz_parser == '@' )
778         {
779             /* Found bind address or bind port */
780             *psz_parser = '\0'; /* Terminate server port or name if necessary */
781             psz_parser++;
782
783             if( *psz_parser && *psz_parser != ':' )
784             {
785                 /* Found bind address */
786                 psz_bind = psz_parser;
787
788                 while( *psz_parser && *psz_parser != ':' )
789                 {
790                     psz_parser++;
791                 }
792             }
793
794             if( *psz_parser == ':' )
795             {
796                 /* Found bind port */
797                 *psz_parser = '\0'; /* Terminate bind address if necessary */
798                 psz_parser++;
799
800                 psz_bind_port = psz_parser;
801             }
802         }
803
804         /* Convert ports format */
805         if( psz_server_port != NULL )
806         {
807             i_server_port = strtol( psz_server_port, &psz_parser, 10 );
808             if( *psz_parser )
809             {
810                 intf_ErrMsg( "input error: cannot parse server port near %s",
811                              psz_parser );
812                 p_input->b_error = 1;
813                 return;
814             }
815         }
816
817         if( psz_bind_port != NULL )
818         {
819             i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
820             if( *psz_parser )
821             {
822                 intf_ErrMsg( "input error: cannot parse bind port near %s",
823                              psz_parser );
824                 p_input->b_error = 1;
825                 return;
826             }
827         }
828     }
829     else
830     {
831         /* This is required or NetworkClose will never be called */
832         p_input->p_source = "ts: network input";
833     }
834
835     /* Check that we got a valid port */
836     if( i_bind_port == 0 )
837     {
838         i_bind_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT );
839     }
840
841     intf_WarnMsg( 2, "input: server=%s:%d local=%s:%d",
842                      psz_server, i_server_port, psz_bind, i_bind_port );
843
844     /* Open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
845      * protocol */
846     p_input->i_handle = socket( AF_INET, SOCK_DGRAM, 0 );
847     if( p_input->i_handle == -1 )
848     {
849         intf_ErrMsg( "input error: can't create socket (%s)", strerror(errno) );
850         p_input->b_error = 1;
851         return;
852     }
853
854     /* We may want to reuse an already used socket */
855     i_opt = 1;
856     if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
857                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
858     {
859         intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
860                      strerror(errno));
861         close( p_input->i_handle );
862         p_input->b_error = 1;
863         return;
864     }
865
866     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
867      * packet loss caused by scheduling problems */
868     i_opt = 0x80000;
869     if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
870                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
871     {
872         intf_WarnMsg( 1, "input warning: can't configure socket (SO_RCVBUF: %s)", 
873                          strerror(errno));
874     }
875
876     /* Check if we really got what we have asked for, because Linux, etc.
877      * will silently limit the max buffer size to net.core.rmem_max which
878      * is typically only 65535 bytes */
879     i_opt = 0;
880     i_opt_size = sizeof( i_opt );
881     if( getsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF,
882                     (void*) &i_opt, &i_opt_size ) == -1 )
883     {
884         intf_WarnMsg( 1, "input warning: can't query socket (SO_RCVBUF: %s)", 
885                          strerror(errno));
886     }
887     else if( i_opt < 0x80000 )
888     {
889         intf_WarnMsg( 1, "input warning: socket buffer size is 0x%x"
890                          " instead of 0x%x", i_opt, 0x80000 );
891     }
892
893     /* Build the local socket */
894     if ( network_BuildAddr( &sock, psz_bind, i_bind_port ) == -1 )
895     {
896         intf_ErrMsg( "input error: can't build local address" );
897         close( p_input->i_handle );
898         p_input->b_error = 1;
899         return;
900     }
901
902     /* Bind it */
903     if( bind( p_input->i_handle, (struct sockaddr *)&sock, 
904               sizeof( sock ) ) < 0 )
905     {
906         intf_ErrMsg( "input error: can't bind socket (%s)", strerror(errno) );
907         close( p_input->i_handle );
908         p_input->b_error = 1;
909         return;
910     }
911
912     /* Allow broadcast reception if we bound on INADDR_ANY */
913     if( psz_bind == NULL )
914     {
915         i_opt = 1;
916         if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_BROADCAST,
917                         (void*) &i_opt, sizeof( i_opt ) ) == -1 )
918         {
919             intf_WarnMsg( 1, "input warning: can't configure socket (SO_BROADCAST: %s)", 
920                              strerror(errno));
921         }
922     }
923
924     /* Join the multicast group if the socket is a multicast address */
925 #ifndef IN_MULTICAST
926 #   define IN_MULTICAST(a)         IN_CLASSD(a)
927 #endif
928
929     if( IN_MULTICAST( ntohl(sock.sin_addr.s_addr) ) )
930     {
931         struct ip_mreq imr;
932
933         imr.imr_interface.s_addr = INADDR_ANY;
934         imr.imr_multiaddr.s_addr = sock.sin_addr.s_addr;
935         if( setsockopt( p_input->i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
936                         (char*)&imr, sizeof(struct ip_mreq) ) == -1 )
937         {
938             intf_ErrMsg( "input error: failed to join IP multicast group (%s)",
939                          strerror(errno) );
940             close( p_input->i_handle );
941             p_input->b_error = 1;
942             return;
943         }
944     }
945     
946     if( psz_server != NULL )
947     {
948         /* Build socket for remote connection */
949         if ( network_BuildAddr( &sock, psz_server, i_server_port ) == -1 )
950         {
951             intf_ErrMsg( "input error: can't build remote address" );
952             close( p_input->i_handle );
953             p_input->b_error = 1;
954             return;
955         }
956
957         /* Connect the socket */
958         if( connect( p_input->i_handle, (struct sockaddr *) &sock,
959                      sizeof( sock ) ) == (-1) )
960         {
961             intf_ErrMsg( "input error: can't connect socket (%s)", 
962                          strerror(errno) );
963             close( p_input->i_handle );
964             p_input->b_error = 1;
965             return;
966         }
967     }
968
969     p_input->stream.b_pace_control = 0;
970     p_input->stream.b_seekable = 0;
971
972     intf_WarnMsg( 3, "input: successfully opened network mode" );
973     
974     return;
975 }
976
977 /*****************************************************************************
978  * NetworkClose : close a network socket
979  *****************************************************************************/
980 static void NetworkClose( input_thread_t * p_input )
981 {
982     intf_WarnMsg( 2, "input: closing network target `%s'", p_input->p_source );
983
984     close( p_input->i_handle );
985 }
986
987 /*****************************************************************************
988  * HTTPOpen : make an HTTP request
989  *****************************************************************************/
990 static void HTTPOpen( input_thread_t * p_input )
991 {
992     char                *psz_server = NULL;
993     char                *psz_path = NULL;
994     char                *psz_proxy;
995     int                 i_port = 0;
996     int                 i_opt;
997     struct sockaddr_in  sock;
998     char                psz_buffer[256];
999
1000     /* Get the remote server */
1001     if( p_input->p_source != NULL )
1002     {
1003         psz_server = p_input->p_source;
1004
1005         /* Skip the protocol name */
1006         while( *psz_server && *psz_server != ':' )
1007         {
1008             psz_server++;
1009         }
1010
1011         /* Skip the "://" part */
1012         while( *psz_server && (*psz_server == ':' || *psz_server == '/') )
1013         {
1014             psz_server++;
1015         }
1016
1017         /* Found a server name */
1018         if( *psz_server )
1019         {
1020             char *psz_port = psz_server;
1021
1022             /* Skip the hostname part */
1023             while( *psz_port && *psz_port != ':' && *psz_port != '/' )
1024             {
1025                 psz_port++;
1026             }
1027
1028             /* Found a port name */
1029             if( *psz_port )
1030             {
1031                 if( *psz_port == ':' )
1032                 {
1033                     /* Replace ':' with '\0' */
1034                     *psz_port = '\0';
1035                     psz_port++;
1036                 }
1037
1038                 psz_path = psz_port;
1039                 while( *psz_path && *psz_path != '/' )
1040                 {
1041                     psz_path++;
1042                 }
1043
1044                 if( *psz_path )
1045                 {
1046                     *psz_path = '\0';
1047                     psz_path++;
1048                 }
1049                 else
1050                 {
1051                     psz_path = NULL;
1052                 }
1053
1054                 if( *psz_port != '\0' )
1055                 {
1056                     i_port = atoi( psz_port );
1057                 }
1058             }
1059         }
1060         else
1061         {
1062             psz_server = NULL;
1063         }
1064     }
1065
1066     /* Check that we got a valid server */
1067     if( psz_server == NULL )
1068     {
1069         intf_ErrMsg( "input error: No server given" );
1070         p_input->b_error = 1;
1071         return;
1072     }
1073
1074     /* Check that we got a valid port */
1075     if( i_port == 0 )
1076     {
1077         i_port = 80; /* FIXME */
1078     }
1079
1080     intf_WarnMsg( 2, "input: server=%s port=%d path=%s", psz_server,
1081                   i_port, psz_path );
1082
1083     /* Open a SOCK_STREAM (TCP) socket, in the AF_INET domain, automatic (0)
1084      *      * protocol */
1085     p_input->i_handle = socket( AF_INET, SOCK_STREAM, 0 );
1086     if( p_input->i_handle == -1 )
1087     {
1088         intf_ErrMsg( "input error: can't create socket (%s)", strerror(errno) );        p_input->b_error = 1;
1089         return;
1090     }
1091
1092     /* We may want to reuse an already used socket */
1093     i_opt = 1;
1094     if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR,
1095                     (void *) &i_opt, sizeof( i_opt ) ) == -1 )
1096     {
1097         intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)",
1098                      strerror(errno));
1099         close( p_input->i_handle );
1100         p_input->b_error = 1;
1101         return;
1102     }
1103
1104     /* Check proxy */
1105     if( (psz_proxy = main_GetPszVariable( "http_proxy", NULL )) != NULL )
1106     {
1107         /* http://myproxy.mydomain:myport/ */
1108         int                 i_proxy_port = 0;
1109
1110         /* Skip the protocol name */
1111         while( *psz_proxy && *psz_proxy != ':' )
1112         {
1113             psz_proxy++;
1114         }
1115
1116         /* Skip the "://" part */
1117         while( *psz_proxy && (*psz_proxy == ':' || *psz_proxy == '/') )
1118         {
1119             psz_proxy++;
1120         }
1121
1122         /* Found a proxy name */
1123         if( *psz_proxy )
1124         {
1125             char *psz_port = psz_proxy;
1126
1127             /* Skip the hostname part */
1128             while( *psz_port && *psz_port != ':' && *psz_port != '/' )
1129             {
1130                 psz_port++;
1131             }
1132
1133             /* Found a port name */
1134             if( *psz_port )
1135             {
1136                 char * psz_junk;
1137
1138                 /* Replace ':' with '\0' */
1139                 *psz_port = '\0';
1140                 psz_port++;
1141
1142                 psz_junk = psz_port;
1143                 while( *psz_junk && *psz_junk != '/' )
1144                 {
1145                     psz_junk++;
1146                 }
1147
1148                 if( *psz_junk )
1149                 {
1150                     *psz_junk = '\0';
1151                 }
1152
1153                 if( *psz_port != '\0' )
1154                 {
1155                     i_proxy_port = atoi( psz_port );
1156                 }
1157             }
1158         }
1159         else
1160         {
1161             intf_ErrMsg( "input error: http_proxy environment variable is invalid !" );
1162             close( p_input->i_handle );
1163             p_input->b_error = 1;
1164             return;
1165         }
1166
1167         /* Build socket for proxy connection */
1168         if ( network_BuildAddr( &sock, psz_proxy, i_proxy_port ) == -1 )
1169         {
1170             intf_ErrMsg( "input error: can't build remote address" );
1171             close( p_input->i_handle );
1172             p_input->b_error = 1;
1173             return;
1174         }
1175     }
1176     else
1177     {
1178         /* No proxy, direct connection */
1179         if ( network_BuildAddr( &sock, psz_server, i_port ) == -1 )
1180         {
1181             intf_ErrMsg( "input error: can't build remote address" );
1182             close( p_input->i_handle );
1183             p_input->b_error = 1;
1184             return;
1185         }
1186     }
1187
1188     /* Connect the socket */
1189     if( connect( p_input->i_handle, (struct sockaddr *) &sock,
1190                  sizeof( sock ) ) == (-1) )
1191     {
1192         intf_ErrMsg( "input error: can't connect socket (%s)",
1193                      strerror(errno) );
1194         close( p_input->i_handle );
1195         p_input->b_error = 1;
1196         return;
1197     }
1198
1199     p_input->stream.b_seekable = 0;
1200     p_input->stream.b_pace_control = 1; /* TCP/IP... */
1201
1202 #   define HTTP_USERAGENT "User-Agent: " COPYRIGHT_MESSAGE "\r\n"
1203 #   define HTTP_END       "\r\n"
1204
1205     /* Prepare GET ... */
1206     if( psz_proxy != NULL )
1207     {
1208         snprintf( psz_buffer, sizeof(psz_buffer),
1209                   "GET http://%s:%d/%s HTTP/1.0\r\n"
1210                   HTTP_USERAGENT HTTP_END,
1211                   psz_server, i_port, psz_path );
1212     }
1213     else
1214     {
1215         snprintf( psz_buffer, sizeof(psz_buffer),
1216                   "GET /%s HTTP/1.0\r\nHost: %s\r\n"
1217                   HTTP_USERAGENT HTTP_END,
1218                   psz_path, psz_server );
1219     }
1220     psz_buffer[sizeof(psz_buffer) - 1] = '\0';
1221
1222     /* Send GET ... */
1223     if( write( p_input->i_handle, psz_buffer, strlen( psz_buffer ) ) == (-1) )
1224     {
1225         intf_ErrMsg( "input error: can't send request (%s)", strerror(errno) );
1226         close( p_input->i_handle );
1227         p_input->b_error = 1;
1228         return;
1229     }
1230
1231     /* Read HTTP header - this is gonna be fun with plug-ins which do not
1232      * use p_input->p_stream :-( */
1233     if( (p_input->p_stream = fdopen( p_input->i_handle, "r+" )) == NULL )
1234     {
1235         intf_ErrMsg( "input error: can't reopen socket (%s)", strerror(errno) );
1236         close( p_input->i_handle );
1237         p_input->b_error = 1;
1238         return;
1239     }
1240
1241     while( !feof( p_input->p_stream ) && !ferror( p_input->p_stream ) )
1242     {
1243         if( fgets( psz_buffer, sizeof(psz_buffer), p_input->p_stream ) == NULL
1244              || *psz_buffer == '\r' || *psz_buffer == '\0' )
1245         {
1246             break;
1247         }
1248         /* FIXME : check Content-Type one day */
1249     }
1250
1251     intf_WarnMsg( 3, "input: successfully opened HTTP mode" );
1252 }
1253
1254 #endif /* !defined( SYS_BEOS ) && !defined( SYS_NTO ) */
1255