1 /*****************************************************************************
2 * file.c: file input (file: access plug-in)
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <vlc/input.h>
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
35 #ifdef HAVE_SYS_TIME_H
36 # include <sys/time.h>
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
50 #elif defined( WIN32 ) && !defined( UNDER_CE )
54 /* stat() support for large files on win32 */
55 #if defined( WIN32 ) && !defined( UNDER_CE )
57 #define fstat(a,b) _fstati64(a,b)
61 # define close(a) CloseHandle((HANDLE)a)
64 /*****************************************************************************
66 *****************************************************************************/
67 static int Open ( vlc_object_t * );
68 static void Close ( vlc_object_t * );
70 static void Seek ( input_thread_t *, off_t );
71 static ssize_t Read ( input_thread_t *, byte_t *, size_t );
73 static int _OpenFile( input_thread_t *, char * );
75 /*****************************************************************************
77 *****************************************************************************/
78 #define CACHING_TEXT N_("Caching value in ms")
79 #define CACHING_LONGTEXT N_( \
80 "Allows you to modify the default caching value for file streams. This " \
81 "value should be set in miliseconds units." )
82 #define CAT_TEXT N_("Concatenate with additional files")
83 #define CAT_LONGTEXT N_( \
84 "Allows you to play splitted files as they were part of a unique file. " \
85 "Specify a coma (',') separated list of files." )
88 set_description( _("Standard filesystem file input") );
89 add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
90 add_string( "file-cat", NULL, NULL, CAT_TEXT, CAT_LONGTEXT, VLC_TRUE );
91 set_capability( "access", 50 );
92 add_shortcut( "file" );
93 add_shortcut( "stream" );
94 add_shortcut( "kfir" );
95 set_callbacks( Open, Close );
98 /*****************************************************************************
99 * _input_socket_t: private access plug-in data, modified to add private
101 *****************************************************************************/
102 typedef struct file_entry_s
109 typedef struct _input_socket_s
111 input_socket_t _socket;
113 unsigned int i_nb_reads;
117 file_entry_t **p_files;
125 /*****************************************************************************
126 * Open: open the file
127 *****************************************************************************/
128 static int Open( vlc_object_t *p_this )
130 input_thread_t * p_input = (input_thread_t *)p_this;
131 char * psz_name = p_input->psz_name;
132 #ifdef HAVE_SYS_STAT_H
134 struct stat stat_info;
136 _input_socket_t * p_access_data;
137 vlc_bool_t b_stdin, b_kfir = 0;
140 file_entry_t * p_file;
144 b_stdin = psz_name[0] == '-' && psz_name[1] == '\0';
146 #ifdef HAVE_SYS_STAT_H
147 if( !b_stdin && (i_stat = stat( psz_name, &stat_info )) == (-1) )
150 msg_Warn( p_input, "cannot stat() file `%s' (%s)",
151 psz_name, strerror(errno));
153 msg_Warn( p_input, "cannot stat() file `%s'", psz_name );
159 p_input->pf_read = Read;
160 p_input->pf_set_program = input_SetProgram;
161 p_input->pf_set_area = NULL;
162 p_input->pf_seek = Seek;
164 vlc_mutex_lock( &p_input->stream.stream_lock );
166 if( *p_input->psz_access && !strncmp( p_input->psz_access, "stream", 7 ) )
169 p_input->stream.b_pace_control = 0;
170 p_input->stream.b_seekable = 0;
171 p_input->stream.p_selected_area->i_size = 0;
173 else if( *p_input->psz_access &&
174 !strncmp( p_input->psz_access, "kfir", 7 ) )
177 p_input->stream.b_pace_control = 0;
178 p_input->stream.b_seekable = 0;
179 p_input->stream.p_selected_area->i_size = 0;
185 p_input->stream.b_pace_control = 1;
189 p_input->stream.b_seekable = 0;
190 p_input->stream.p_selected_area->i_size = 0;
195 /* We'll update i_size after it's been opened */
196 p_input->stream.b_seekable = 1;
198 #elif defined( HAVE_SYS_STAT_H )
199 else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode)
200 || S_ISBLK(stat_info.st_mode) )
202 p_input->stream.b_seekable = 1;
203 p_input->stream.p_selected_area->i_size = stat_info.st_size;
205 else if( S_ISFIFO(stat_info.st_mode)
206 # if !defined( SYS_BEOS ) && !defined( WIN32 )
207 || S_ISSOCK(stat_info.st_mode)
211 p_input->stream.b_seekable = 0;
212 p_input->stream.p_selected_area->i_size = 0;
217 vlc_mutex_unlock( &p_input->stream.stream_lock );
218 msg_Err( p_input, "unknown file type for `%s'", psz_name );
223 p_input->stream.p_selected_area->i_tell = 0;
224 p_input->stream.i_method = INPUT_METHOD_FILE;
225 vlc_mutex_unlock( &p_input->stream.stream_lock );
227 msg_Dbg( p_input, "opening file `%s'", psz_name );
228 p_access_data = malloc( sizeof(_input_socket_t) );
229 p_input->p_access_data = (void *)p_access_data;
230 if( p_access_data == NULL )
232 msg_Err( p_input, "out of memory" );
236 p_access_data->i_nb_reads = 0;
237 p_access_data->b_kfir = b_kfir;
240 p_access_data->_socket.i_handle = 0;
244 if( _OpenFile( p_input, psz_name ) != VLC_SUCCESS )
246 free( p_access_data );
251 if ( p_input->stream.b_seekable
252 && !p_input->stream.p_selected_area->i_size )
254 msg_Err( p_input, "file %s is empty, aborting", psz_name );
255 free( p_access_data );
259 /* Update default_pts to a suitable value for file access */
260 var_Create( p_input, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
261 var_Get( p_input, "file-caching", &val );
262 p_input->i_pts_delay = val.i_int * 1000;
265 * Get the additional list of files
267 p_access_data->p_files = NULL;
268 p_access_data->i_files = p_access_data->i_index = 0;
269 p_file = (file_entry_t *)malloc( sizeof(file_entry_t) );
270 p_file->i_size = p_input->stream.p_selected_area->i_size;
271 p_file->psz_name = strdup( psz_name );
272 TAB_APPEND( p_access_data->i_files, p_access_data->p_files, p_file );
274 var_Create( p_input, "file-cat", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
275 var_Get( p_input, "file-cat", &val );
277 if( val.psz_string && *val.psz_string )
279 char *psz_parser = psz_name = val.psz_string;
282 while( psz_name && *psz_name )
284 psz_parser = strchr( psz_name, ',' );
285 if( psz_parser ) *psz_parser = 0;
287 psz_name = strdup( psz_name );
290 msg_Dbg( p_input, "adding file `%s'", psz_name );
292 #ifdef HAVE_SYS_STAT_H
293 if( !stat( psz_name, &stat_info ) )
295 p_input->stream.p_selected_area->i_size +=
297 i_size = stat_info.st_size;
301 msg_Dbg( p_input, "cannot stat() file `%s'", psz_name );
306 p_file = (file_entry_t *)malloc( sizeof(file_entry_t) );
307 p_file->i_size = i_size;
308 p_file->psz_name = psz_name;
310 TAB_APPEND( p_access_data->i_files, p_access_data->p_files,
314 psz_name = psz_parser;
315 if( psz_name ) psz_name++;
319 if( val.psz_string ) free( val.psz_string );
324 /*****************************************************************************
325 * Close: close the target
326 *****************************************************************************/
327 static void Close( vlc_object_t * p_this )
329 input_thread_t *p_input = (input_thread_t *)p_this;
330 _input_socket_t *p_access_data = (_input_socket_t *)p_input->p_access_data;
333 msg_Info( p_input, "closing `%s/%s://%s'",
334 p_input->psz_access, p_input->psz_demux, p_input->psz_name );
336 close( p_access_data->_socket.i_handle );
338 for( i = 0; i < p_access_data->i_files; i++ )
340 free( p_access_data->p_files[i]->psz_name );
341 free( p_access_data->p_files[i] );
343 if( p_access_data->p_files ) free( p_access_data->p_files );
345 free( p_access_data );
348 /*****************************************************************************
349 * Read: standard read on a file descriptor.
350 *****************************************************************************/
351 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
353 _input_socket_t *p_access_data = (_input_socket_t *)p_input->p_access_data;
357 if( !ReadFile( (HANDLE)p_access_data->_socket.i_handle, p_buffer, i_len,
358 (LPDWORD)&i_ret, NULL ) )
364 if ( !p_input->stream.b_pace_control )
366 if ( !p_access_data->b_kfir )
368 /* Find if some data is available. This won't work under Windows. */
369 struct timeval timeout;
372 /* Initialize file descriptor set */
374 FD_SET( p_access_data->_socket.i_handle, &fds );
376 /* We'll wait 0.5 second if nothing happens */
378 timeout.tv_usec = 500000;
380 /* Find if some data is available */
381 while( (i_ret = select( p_access_data->_socket.i_handle + 1, &fds,
382 NULL, NULL, &timeout )) == 0
383 || (i_ret < 0 && errno == EINTR) )
386 FD_SET( p_access_data->_socket.i_handle, &fds );
388 timeout.tv_usec = 500000;
390 if( p_input->b_die || p_input->b_error )
398 msg_Err( p_input, "select error (%s)", strerror(errno) );
402 i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len );
406 /* b_kfir ; work around a buggy poll() driver implementation */
407 while ( (i_ret = read( p_access_data->_socket.i_handle, p_buffer,
409 !p_input->b_die && !p_input->b_error )
411 msleep(INPUT_ERROR_SLEEP);
418 /* b_pace_control || WIN32 */
419 i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len );
426 if ( errno != EINTR && errno != EAGAIN )
427 msg_Err( p_input, "read failed (%s)", strerror(errno) );
429 msg_Err( p_input, "read failed" );
432 /* Delay a bit to avoid consuming all the CPU. This is particularly
433 * useful when reading from an unconnected FIFO. */
434 msleep( INPUT_ERROR_SLEEP );
437 p_access_data->i_nb_reads++;
438 #ifdef HAVE_SYS_STAT_H
439 if ( p_input->stream.p_selected_area->i_size != 0
440 && (p_access_data->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
442 struct stat stat_info;
443 int i_file = p_access_data->i_index;
445 if ( fstat( p_access_data->_socket.i_handle, &stat_info ) == -1 )
448 msg_Warn( p_input, "couldn't stat again the file (%s)",
451 msg_Warn( p_input, "couldn't stat again the file" );
454 else if ( p_access_data->p_files[i_file]->i_size != stat_info.st_size )
456 p_input->stream.p_selected_area->i_size +=
457 (stat_info.st_size - p_access_data->p_files[i_file]->i_size);
458 p_input->stream.b_changed = 1;
463 /* If we reached an EOF then switch to the next file in the list */
464 if ( i_ret == 0 && p_access_data->i_index + 1 < p_access_data->i_files )
466 int i_handle = p_access_data->_socket.i_handle;
468 p_access_data->p_files[p_access_data->i_index + 1]->psz_name;
470 msg_Dbg( p_input, "opening file `%s'", psz_name );
472 if ( _OpenFile( p_input, psz_name ) != VLC_SUCCESS )
474 p_access_data->_socket.i_handle = i_handle;
479 p_access_data->i_index++;
481 /* We have to read some data */
482 return Read( p_input, p_buffer, i_len );
488 /*****************************************************************************
489 * Seek: seek to a specific location in a file
490 *****************************************************************************/
491 static void Seek( input_thread_t * p_input, off_t i_pos )
493 #define S p_input->stream
494 _input_socket_t *p_access_data = (_input_socket_t *)p_input->p_access_data;
497 /* Check which file we need to access */
498 if ( p_access_data->i_files > 1 )
500 int i, i_handle = p_access_data->_socket.i_handle;
503 for ( i = 0; i < p_access_data->i_files - 1; i++ )
505 if( i_pos < p_access_data->p_files[i]->i_size + i_size ) break;
506 i_size += p_access_data->p_files[i]->i_size;
509 psz_name = p_access_data->p_files[i]->psz_name;
511 msg_Dbg( p_input, "opening file `%s'", psz_name );
512 if ( i != p_access_data->i_index &&
513 _OpenFile( p_input, psz_name ) == VLC_SUCCESS )
520 p_access_data->i_index = i;
521 p_access_data->_socket.i_handle = i_handle;
525 #if defined( WIN32 ) && !defined( UNDER_CE )
526 _lseeki64( p_access_data->_socket.i_handle, i_pos - i_size, SEEK_SET );
528 lseek( p_access_data->_socket.i_handle, i_pos - i_size, SEEK_SET );
531 vlc_mutex_lock( &S.stream_lock );
532 S.p_selected_area->i_tell = i_pos;
533 if( S.p_selected_area->i_tell > S.p_selected_area->i_size )
535 msg_Err( p_input, "seeking too far" );
536 S.p_selected_area->i_tell = S.p_selected_area->i_size;
538 else if( S.p_selected_area->i_tell < 0 )
540 msg_Err( p_input, "seeking too early" );
541 S.p_selected_area->i_tell = 0;
543 vlc_mutex_unlock( &S.stream_lock );
547 /*****************************************************************************
548 * OpenFile: Opens a specific file
549 *****************************************************************************/
550 static int _OpenFile( input_thread_t * p_input, char * psz_name )
552 input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
555 wchar_t psz_filename[MAX_PATH];
556 MultiByteToWideChar( CP_ACP, 0, psz_name, -1, psz_filename, MAX_PATH );
558 p_access_data->i_handle =
559 (int)CreateFile( psz_filename, GENERIC_READ, FILE_SHARE_READ,
560 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
562 if ( (HANDLE)p_access_data->i_handle == INVALID_HANDLE_VALUE )
564 msg_Err( p_input, "cannot open file %s", psz_name );
567 p_input->stream.p_selected_area->i_size =
568 GetFileSize( (HANDLE)p_access_data->i_handle, NULL );
571 p_access_data->i_handle = open( psz_name, O_NONBLOCK /*| O_LARGEFILE*/ );
572 if ( p_access_data->i_handle == -1 )
575 msg_Err( p_input, "cannot open file %s (%s)", psz_name,
578 msg_Err( p_input, "cannot open file %s", psz_name );