X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Finput.c;h=e444286d9a4b244d7603e6a68d3432fea7ca55ee;hb=440f9992ee947ea5fd0debbf35fdd1011c6404b3;hp=89f97f0883aec8eedd0649087a6efc2af4a84405;hpb=1cd850a85689771b56d6a44b6bc06b46e072b6d3;p=vlc diff --git a/src/input/input.c b/src/input/input.c index 89f97f0883..e444286d9a 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -4,9 +4,9 @@ * decoders. ***************************************************************************** * Copyright (C) 1998, 1999, 2000 VideoLAN - * $Id: input.c,v 1.70 2001/01/16 04:41:20 stef Exp $ + * $Id: input.c,v 1.113 2001/05/30 17:03:12 sam Exp $ * - * Authors: + * Authors: Christophe Massiot * * 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 @@ -34,8 +34,29 @@ #include #include #include +#ifdef STRNCASECMP_IN_STRINGS_H +# include +#endif #include +/* WinSock Includes */ + +#ifdef WIN32 +#include +#endif + + +/* Network functions */ + +#if !defined( SYS_BEOS ) && !defined( SYS_NTO ) && !defined( WIN32 ) +#include /* hostent ... */ +#include +#include +#include +#include +#include +#endif + #ifdef STATS # include #endif @@ -44,25 +65,35 @@ #include "common.h" #include "threads.h" #include "mtime.h" +#include "netutils.h" +#include "modules.h" #include "intf_msg.h" +#include "intf_playlist.h" #include "stream_control.h" #include "input_ext-intf.h" #include "input_ext-dec.h" #include "input.h" +#include "interface.h" + +#include "main.h" + /***************************************************************************** * Local prototypes *****************************************************************************/ -static void RunThread ( input_thread_t *p_input ); -static void InitThread ( input_thread_t *p_input ); -static void ErrorThread ( input_thread_t *p_input ); -static void EndThread ( input_thread_t *p_input ); -static void NetworkOpen ( input_thread_t *p_input ); -static void FileOpen ( input_thread_t *p_input ); -static void DvdOpen ( input_thread_t *p_input ); +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 ); + +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 ); /***************************************************************************** * input_CreateThread: creates a new input thread @@ -72,7 +103,7 @@ static void DvdOpen ( input_thread_t *p_input ); * 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. *****************************************************************************/ -input_thread_t *input_CreateThread ( input_config_t * p_config, int *pi_status ) +input_thread_t *input_CreateThread ( playlist_item_t *p_item, int *pi_status ) { input_thread_t * p_input; /* thread descriptor */ int i_status; /* thread status */ @@ -83,33 +114,57 @@ input_thread_t *input_CreateThread ( input_config_t * p_config, int *pi_status ) { intf_ErrMsg( "input error: can't allocate input thread (%s)", strerror(errno) ); - free( p_config ); return( NULL ); } + /* Packets read once */ + p_input->i_read_once = INPUT_READ_ONCE; + /* Initialize thread properties */ p_input->b_die = 0; p_input->b_error = 0; + p_input->b_eof = 0; + + /* Set target */ + p_input->p_source = p_item->psz_name; + /* I have never understood that stuff --Meuuh */ p_input->pi_status = (pi_status != NULL) ? pi_status : &i_status; *p_input->pi_status = THREAD_CREATE; - p_input->p_config = p_config; /* Initialize stream description */ p_input->stream.i_es_number = 0; p_input->stream.i_selected_es_number = 0; p_input->stream.i_pgrm_number = 0; + p_input->stream.i_new_status = p_input->stream.i_new_rate = 0; + p_input->stream.i_mux_rate = 0; + + /* no stream, no area */ + p_input->stream.i_area_nb = 0; + p_input->stream.pp_areas = NULL; + p_input->stream.p_selected_area = NULL; + p_input->stream.p_new_area = NULL; + /* By default there is one areas in a stream */ + input_AddArea( p_input ); + p_input->stream.p_selected_area = p_input->stream.pp_areas[0]; /* Initialize stream control properties. */ p_input->stream.control.i_status = PLAYING_S; p_input->stream.control.i_rate = DEFAULT_RATE; - p_input->stream.control.i_ref_sysdate = 0; - p_input->stream.control.i_ref_clock = 0; p_input->stream.control.b_mute = 0; p_input->stream.control.b_bw = 0; + /* 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 + /* Create thread and set locks. */ vlc_mutex_init( &p_input->stream.stream_lock ); + vlc_cond_init( &p_input->stream.stream_wait ); vlc_mutex_init( &p_input->stream.control.control_lock ); if( vlc_thread_create( &p_input->thread_id, "input", (void *) RunThread, (void *) p_input ) ) @@ -117,7 +172,6 @@ input_thread_t *input_CreateThread ( input_config_t * p_config, int *pi_status ) intf_ErrMsg( "input error: can't create input thread (%s)", strerror(errno) ); free( p_input ); - free( p_config ); return( NULL ); } @@ -153,6 +207,11 @@ void input_DestroyThread( input_thread_t *p_input, int *pi_status ) /* Request thread destruction */ p_input->b_die = 1; + /* 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 ); + /* If status is NULL, wait until thread has been destroyed */ if( pi_status == NULL ) { @@ -171,65 +230,117 @@ void input_DestroyThread( input_thread_t *p_input, int *pi_status ) *****************************************************************************/ static void RunThread( input_thread_t *p_input ) { - data_packet_t * pp_packets[INPUT_READ_ONCE]; int i_error, i; - InitThread( p_input ); + if( InitThread( p_input ) ) + { - while( !p_input->b_die && !p_input->b_error ) + /* 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; + } + + /* 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 ); + + while( !p_input->b_die && !p_input->b_error && !p_input->b_eof ) { + data_packet_t * pp_packets[p_input->i_read_once]; + #ifdef STATS p_input->c_loops++; #endif - vlc_mutex_lock( &p_input->stream.control.control_lock ); - if( p_input->stream.control.i_status == BACKWARD_S - && p_input->p_plugin->pf_rewind != NULL ) + vlc_mutex_lock( &p_input->stream.stream_lock ); + + if( p_input->stream.p_new_area ) { - p_input->p_plugin->pf_rewind( p_input ); - /* FIXME: probably don't do it every loop, but when ? */ + p_input->pf_set_area( p_input, p_input->stream.p_new_area ); + p_input->stream.p_new_area = NULL; } - vlc_mutex_unlock( &p_input->stream.control.control_lock ); - i_error = p_input->p_plugin->pf_read( p_input, pp_packets ); + if( p_input->stream.p_selected_area->i_seek != NO_SEEK ) + { + if( p_input->stream.b_seekable && p_input->pf_seek != NULL ) + { + p_input->pf_seek( p_input, + p_input->stream.p_selected_area->i_seek ); + + for( i = 0; i < p_input->stream.i_pgrm_number; i++ ) + { + pgrm_descriptor_t * p_pgrm + = p_input->stream.pp_programs[i]; + /* Escape all decoders for the stream discontinuity they + * will encounter. */ + input_EscapeDiscontinuity( p_input, p_pgrm ); + + /* Reinitialize synchro. */ + p_pgrm->i_synchro_state = SYNCHRO_REINIT; + } + } + p_input->stream.p_selected_area->i_seek = NO_SEEK; + } + + 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; + } + + vlc_mutex_unlock( &p_input->stream.stream_lock ); + + i_error = p_input->pf_read( p_input, pp_packets ); /* Demultiplex read packets. */ - for( i = 0; i < INPUT_READ_ONCE && pp_packets[i] != NULL; i++ ) + for( i = 0; i < p_input->i_read_once && pp_packets[i] != NULL; i++ ) { - p_input->p_plugin->pf_demux( p_input, pp_packets[i] ); + p_input->pf_demux( p_input, pp_packets[i] ); } if( i_error ) { if( i_error == 1 ) { - /* End of file */ - intf_WarnMsg( 1, "End of file reached" ); - /* FIXME: don't treat that as an error */ + /* End of file - we do not set b_die because only the + * interface is allowed to do so. */ + intf_WarnMsg( 3, "input: EOF reached" ); + p_input->b_eof = 1; + } + else + { + p_input->b_error = 1; } - p_input->b_error = 1; } } - if( p_input->b_error ) + if( p_input->b_error || p_input->b_eof ) { ErrorThread( p_input ); } EndThread( p_input ); - intf_DbgMsg("Thread end"); + + DestroyThread( p_input ); + + intf_DbgMsg("input: Thread end"); } /***************************************************************************** - * InitThread: init the input thread + * InitThread: init the input Thread *****************************************************************************/ -input_capabilities_t * PSKludge( void ); -input_capabilities_t * DVDKludge( void ); -static void InitThread( input_thread_t * p_input ) +static int InitThread( input_thread_t * p_input ) { - /* Initialize default settings for spawned decoders */ - p_input->p_default_aout = p_input->p_config->p_default_aout; - p_input->p_default_vout = p_input->p_config->p_default_vout; #ifdef STATS /* Initialize statistics */ @@ -240,50 +351,54 @@ static void InitThread( input_thread_t * p_input ) p_input->c_packets_trashed = 0; #endif - /* Use the appropriate input method */ - switch( p_input->p_config->i_method ) + p_input->p_input_module = module_Need( MODULE_CAPABILITY_INPUT, + (probedata_t *)p_input ); + + if( p_input->p_input_module == NULL ) { - case INPUT_METHOD_FILE: /* file methods */ - FileOpen( p_input ); - /* Probe plugin (FIXME: load plugins before & write this) */ - p_input->p_plugin = PSKludge(); - break; - case INPUT_METHOD_DVD: /* DVD method */ - DvdOpen( p_input ); - /* DVD plugin */ - p_input->p_plugin = DVDKludge(); - break; - case INPUT_METHOD_VLAN_BCAST: /* vlan network method */ -/* if( !p_main->b_vlans ) - { - intf_ErrMsg("input error: vlans are not activated"); - free( p_input ); - return( NULL ); - } */ /* la-lala */ - /* ... pass through */ - case INPUT_METHOD_UCAST: /* network methods */ - case INPUT_METHOD_MCAST: - case INPUT_METHOD_BCAST: - NetworkOpen( p_input ); - break; -#ifdef DEBUG - default: - intf_ErrMsg( "input error: unknow method 0x%.4x", - p_input->p_config->i_method ); - free( p_input->p_config ); - p_input->b_error = 1; - break; -#endif + intf_ErrMsg( "input error: no suitable input module for `%s'", + p_input->p_source ); + return( -1 ); + } + +#define f p_input->p_input_module->p_functions->input.functions.input + p_input->pf_init = f.pf_init; + p_input->pf_open = f.pf_open; + p_input->pf_close = f.pf_close; + p_input->pf_end = f.pf_end; + p_input->pf_read = f.pf_read; + p_input->pf_set_area = f.pf_set_area; + 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 ); + + if( p_input->b_error ) + { + /* We barfed -- exit nicely */ + p_input->pf_close( p_input ); + module_Unneed( p_input->p_input_module ); + return( -1 ); } - free( p_input->p_config ); + p_input->pf_init( p_input ); - if( !p_input->b_error ) + if( p_input->b_error ) { - p_input->p_plugin->pf_init( p_input ); + /* We barfed -- exit nicely */ + p_input->pf_close( p_input ); + module_Unneed( p_input->p_input_module ); + return( -1 ); } *p_input->pi_status = THREAD_READY; + + return( 0 ); } /***************************************************************************** @@ -325,8 +440,25 @@ static void EndThread( input_thread_t * p_input ) input_EndStream( p_input ); /* Free demultiplexer's data */ - p_input->p_plugin->pf_end( p_input ); - free( p_input->p_plugin ); + p_input->pf_end( p_input ); + + /* Close stream */ + p_input->pf_close( p_input ); + + /* Release modules */ + module_Unneed( p_input->p_input_module ); + +} + +/***************************************************************************** + * 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; /* Destroy Mutex locks */ vlc_mutex_destroy( &p_input->stream.control.control_lock ); @@ -339,34 +471,44 @@ static void EndThread( input_thread_t * p_input ) *pi_status = THREAD_OVER; } -/***************************************************************************** - * NetworkOpen : open a network socket descriptor - *****************************************************************************/ -static void NetworkOpen( input_thread_t * p_input ) -{ - /* straight copy & paste of input_network.c of input-I */ - - /* We cannot rewind nor lseek() */ - p_input->stream.b_seekable = 0; - /* We cannot control the pace */ - p_input->stream.b_pace_control = 0; -} - /***************************************************************************** * FileOpen : open a file descriptor *****************************************************************************/ static void FileOpen( input_thread_t * p_input ) { struct stat stat_info; + int i_stat; -#define p_config p_input->p_config + char *psz_name = p_input->p_source; - if( stat( p_config->p_source, &stat_info ) == (-1) ) + /* 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) ) { - intf_ErrMsg( "input error: cannot stat() file %s (%s)", - p_config->p_source, strerror(errno)); - p_input->b_error = 1; - return; + int i_size = strlen( psz_name ); + + if( ( i_size > 4 ) + && !strncasecmp( psz_name, "dvd:", 4 ) ) + { + /* get rid of the 'dvd:' stuff and try again */ + psz_name += 4; + i_stat = stat( psz_name, &stat_info ); + } + else if( ( i_size > 5 ) + && !strncasecmp( psz_name, "file:", 5 ) ) + { + /* get rid of the 'file:' stuff and try again */ + psz_name += 5; + i_stat = stat( psz_name, &stat_info ); + } + + if( i_stat == (-1) ) + { + intf_ErrMsg( "input error: cannot stat() file `%s' (%s)", + psz_name, strerror(errno)); + p_input->b_error = 1; + return; + } } vlc_mutex_lock( &p_input->stream.stream_lock ); @@ -378,57 +520,275 @@ static void FileOpen( input_thread_t * p_input ) || S_ISBLK(stat_info.st_mode) ) { p_input->stream.b_seekable = 1; - p_input->stream.i_size = stat_info.st_size; + p_input->stream.p_selected_area->i_size = stat_info.st_size; } - else if( S_ISFIFO(stat_info.st_mode) || S_ISSOCK(stat_info.st_mode) ) + else if( S_ISFIFO(stat_info.st_mode) +#if !defined( SYS_BEOS ) && !defined( WIN32 ) + || S_ISSOCK(stat_info.st_mode) +#endif + ) { p_input->stream.b_seekable = 0; - p_input->stream.i_size = 0; + p_input->stream.p_selected_area->i_size = 0; } else { vlc_mutex_unlock( &p_input->stream.stream_lock ); - intf_ErrMsg( "input error: unknown file type for %s", - p_config->p_source ); + intf_ErrMsg( "input error: unknown file type for `%s'", + psz_name ); p_input->b_error = 1; return; } - p_input->stream.i_tell = 0; + p_input->stream.p_selected_area->i_tell = 0; vlc_mutex_unlock( &p_input->stream.stream_lock ); - intf_Msg( "input: opening file %s", p_config->p_source ); - if( (p_input->i_handle = open( p_config->p_source, + intf_WarnMsg( 1, "input: opening file `%s'", p_input->p_source ); +#ifndef WIN32 + if( (p_input->i_handle = open( psz_name, /*O_NONBLOCK | O_LARGEFILE*/0 )) == (-1) ) +#else + if( (p_input->i_handle = open( psz_name, O_BINARY + /*O_NONBLOCK | O_LARGEFILE*/ )) == (-1) ) +#endif { - intf_ErrMsg( "input error: cannot open file %s", strerror(errno) ); + intf_ErrMsg( "input error: cannot open file (%s)", strerror(errno) ); p_input->b_error = 1; return; } -#undef p_config } /***************************************************************************** - * DvdOpen : open the dvd device + * FileClose : close a file descriptor + *****************************************************************************/ +static void FileClose( input_thread_t * p_input ) +{ + intf_WarnMsg( 1, "input: closing file `%s'", p_input->p_source ); + close( p_input->i_handle ); + + return; +} + + +#if !defined( SYS_BEOS ) && !defined( SYS_NTO ) +/***************************************************************************** + * NetworkOpen : open a network socket *****************************************************************************/ -static void DvdOpen( input_thread_t * p_input ) +static void NetworkOpen( input_thread_t * p_input ) { - intf_Msg( "input: opening DVD %s", p_input->p_config->p_source ); - if( (p_input->i_handle = open( p_input->p_config->p_source, - O_RDONLY|O_LARGEFILE )) == (-1) ) + char *psz_server = NULL; + char *psz_broadcast = NULL; + int i_port = 0; + int i_opt; + struct sockaddr_in sock; + +#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 + + /* Get the remote server */ + 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++; + + 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 ); + } + } + } + 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 ) + { + i_port = main_GetIntVariable( INPUT_PORT_VAR, INPUT_PORT_DEFAULT ); + } + + if( psz_broadcast == NULL ) + { + /* Are we broadcasting ? */ + 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; + } + } + + intf_WarnMsg( 2, "input: server: %s port: %d broadcast: %s", + psz_server, i_port, psz_broadcast ); + + /* 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 ) { - intf_ErrMsg( "input error: cannot open device %s", strerror(errno) ); + intf_ErrMsg("input error: can't create socket : %s", strerror(errno)); p_input->b_error = 1; return; } - vlc_mutex_lock( &p_input->stream.stream_lock ); + /* We may want to reuse an already used socket */ + i_opt = 1; + if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_REUSEADDR, + &i_opt, sizeof( i_opt ) ) == -1 ) + { + intf_ErrMsg( "input error: can't configure socket (SO_REUSEADDR: %s)", + strerror(errno)); + close( p_input->i_handle ); + p_input->b_error = 1; + return; + } - p_input->stream.b_pace_control = 1; - p_input->stream.b_seekable = 1; - p_input->stream.i_size = 0; - p_input->stream.i_tell = 0; + /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid + * packet loss caused by scheduling problems */ + i_opt = 0x80000; + if( setsockopt( p_input->i_handle, SOL_SOCKET, SO_RCVBUF, + &i_opt, sizeof( i_opt ) ) == -1 ) + { + intf_ErrMsg( "input error: can't configure socket (SO_RCVBUF: %s)", + strerror(errno)); + close( p_input->i_handle ); + p_input->b_error = 1; + return; + } - vlc_mutex_unlock( &p_input->stream.stream_lock ); + /* Build the local socket */ + if ( network_BuildLocalAddr( &sock, i_port, psz_broadcast ) == -1 ) + { + intf_ErrMsg( "input error: can't build local address" ); + close( p_input->i_handle ); + p_input->b_error = 1; + return; + } + + /* Bind it */ + if( bind( p_input->i_handle, (struct sockaddr *)&sock, + sizeof( sock ) ) < 0 ) + { + intf_ErrMsg("input error: can't bind socket (%s)", strerror(errno)); + close( p_input->i_handle ); + p_input->b_error = 1; + return; + } + + /* Build socket for remote connection */ + if ( network_BuildRemoteAddr( &sock, psz_server ) == -1 ) + { + intf_ErrMsg( "input error: can't build remote address" ); + close( p_input->i_handle ); + p_input->b_error = 1; + return; + } + + /* And connect it ... should we really connect ? */ + if( connect( p_input->i_handle, (struct sockaddr *) &sock, + sizeof( sock ) ) == (-1) ) + { + intf_ErrMsg( "input error: can't connect socket, %s", + strerror(errno) ); + 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. */ + p_input->stream.b_pace_control = 0; + p_input->stream.b_seekable = 0; + + intf_WarnMsg( 3, "input: successfully opened network mode" ); + + return; } + +/***************************************************************************** + * NetworkClose : close a network socket + *****************************************************************************/ +static void NetworkClose( input_thread_t * p_input ) +{ + close( p_input->i_handle ); + +#ifdef WIN32 + WSACleanup(); +#endif + +} +#endif +