00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <vlc/vlc.h>
00028 #include <vlc/input.h>
00029
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <errno.h>
00033 #ifdef HAVE_SYS_TYPES_H
00034 # include <sys/types.h>
00035 #endif
00036 #ifdef HAVE_SYS_TIME_H
00037 # include <sys/time.h>
00038 #endif
00039 #ifdef HAVE_SYS_STAT_H
00040 # include <sys/stat.h>
00041 #endif
00042 #ifdef HAVE_FCNTL_H
00043 # include <fcntl.h>
00044 #endif
00045
00046 #ifdef HAVE_UNISTD_H
00047 # include <unistd.h>
00048 #elif defined( WIN32 ) && !defined( UNDER_CE )
00049 # include <io.h>
00050 #endif
00051
00052 #if defined( WIN32 ) && !defined( UNDER_CE )
00053
00054 # define stat _stati64
00055 # define fstat(a,b) _fstati64(a,b)
00056 # ifdef lseek
00057 # undef lseek
00058 # endif
00059 # define lseek _lseeki64
00060 #elif defined( UNDER_CE )
00061 # ifdef read
00062 # undef read
00063 # endif
00064 # define read(a,b,c) fread(b,1,c,a)
00065 # define close(a) fclose(a)
00066 # ifdef lseek
00067 # undef lseek
00068 # endif
00069 # define lseek fseek
00070 #endif
00071
00072 #include "charset.h"
00073
00074
00075
00076
00077 static int Open ( vlc_object_t * );
00078 static void Close( vlc_object_t * );
00079
00080 #define CACHING_TEXT N_("Caching value in ms")
00081 #define CACHING_LONGTEXT N_( \
00082 "Allows you to modify the default caching value for file streams. This " \
00083 "value should be set in millisecond units." )
00084 #define CAT_TEXT N_("Concatenate with additional files")
00085 #define CAT_LONGTEXT N_( \
00086 "Allows you to play split files as if they were part of a unique file. " \
00087 "Specify a comma-separated list of files." )
00088
00089 vlc_module_begin();
00090 set_description( _("Standard filesystem file input") );
00091 set_shortname( _("File") );
00092 set_category( CAT_INPUT );
00093 set_subcategory( SUBCAT_INPUT_ACCESS );
00094 add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
00095 add_string( "file-cat", NULL, NULL, CAT_TEXT, CAT_LONGTEXT, VLC_TRUE );
00096 set_capability( "access2", 50 );
00097 add_shortcut( "file" );
00098 add_shortcut( "stream" );
00099 add_shortcut( "kfir" );
00100 set_callbacks( Open, Close );
00101 vlc_module_end();
00102
00103
00104
00105
00106
00107 static int Seek( access_t *, int64_t );
00108 static int Read( access_t *, uint8_t *, int );
00109 static int Control( access_t *, int, va_list );
00110
00111 static int _OpenFile( access_t *, const char * );
00112
00113 typedef struct
00114 {
00115 char *psz_name;
00116 int64_t i_size;
00117
00118 } file_entry_t;
00119
00120 struct access_sys_t
00121 {
00122 unsigned int i_nb_reads;
00123 vlc_bool_t b_kfir;
00124
00125
00126 int i_file;
00127 file_entry_t **file;
00128
00129
00130 int i_index;
00131 #ifndef UNDER_CE
00132 int fd;
00133 int fd_backup;
00134 #else
00135 FILE *fd;
00136 FILE *fd_backup;
00137 #endif
00138
00139
00140 vlc_bool_t b_seekable;
00141 vlc_bool_t b_pace_control;
00142 };
00143
00144
00145
00146
00147 static int Open( vlc_object_t *p_this )
00148 {
00149 access_t *p_access = (access_t*)p_this;
00150 access_sys_t *p_sys;
00151 char *psz_name = strdup( p_access->psz_path );
00152 char *psz;
00153
00154 #ifdef HAVE_SYS_STAT_H
00155 struct stat stat_info;
00156 #endif
00157 vlc_bool_t b_stdin;
00158
00159 file_entry_t * p_file;
00160
00161 b_stdin = psz_name[0] == '-' && psz_name[1] == '\0';
00162
00163 if( !b_stdin )
00164 {
00165 if( psz_name[0] == '~' && psz_name[1] == '/' )
00166 {
00167 psz = malloc( strlen(p_access->p_vlc->psz_homedir)
00168 + strlen(psz_name) );
00169
00170
00171 sprintf( psz, "%s/%s", p_access->p_vlc->psz_homedir, psz_name + 2 );
00172 free( psz_name );
00173 psz_name = psz;
00174 }
00175 #if defined(WIN32)
00176 else if( !strcasecmp( p_access->psz_access, "file" )
00177 && ('/' == psz_name[0]) && psz_name[1]
00178 && (':' == psz_name[2]) && ('/' == psz_name[3]) )
00179 {
00180
00181
00182
00183
00184 ++psz_name;
00185 }
00186 #endif
00187
00188 #ifdef HAVE_SYS_STAT_H
00189 psz = ToLocale( psz_name );
00190 if( stat( psz, &stat_info ) )
00191 {
00192 msg_Warn( p_access, "%s: %s", psz_name, strerror( errno ) );
00193 LocaleFree( psz );
00194 free( psz_name );
00195 return VLC_EGENERIC;
00196 }
00197 LocaleFree( psz );
00198 #endif
00199 }
00200
00201 p_access->pf_read = Read;
00202 p_access->pf_block = NULL;
00203 p_access->pf_seek = Seek;
00204 p_access->pf_control = Control;
00205 p_access->info.i_update = 0;
00206 p_access->info.i_size = 0;
00207 p_access->info.i_pos = 0;
00208 p_access->info.b_eof = VLC_FALSE;
00209 p_access->info.i_title = 0;
00210 p_access->info.i_seekpoint = 0;
00211 p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
00212 p_sys->i_nb_reads = 0;
00213 p_sys->b_kfir = VLC_FALSE;
00214 p_sys->file = NULL;
00215 p_sys->i_file = 0;
00216 p_sys->i_index = 0;
00217 #ifndef UNDER_CE
00218 p_sys->fd = -1;
00219 #endif
00220
00221 if( !strcasecmp( p_access->psz_access, "stream" ) )
00222 {
00223 p_sys->b_seekable = VLC_FALSE;
00224 p_sys->b_pace_control = VLC_FALSE;
00225 }
00226 else if( !strcasecmp( p_access->psz_access, "kfir" ) )
00227 {
00228 p_sys->b_seekable = VLC_FALSE;
00229 p_sys->b_pace_control = VLC_FALSE;
00230 p_sys->b_kfir = VLC_TRUE;
00231 }
00232 else
00233 {
00234
00235 p_sys->b_pace_control = VLC_TRUE;
00236
00237 if( b_stdin )
00238 {
00239 p_sys->b_seekable = VLC_FALSE;
00240 }
00241 #ifdef UNDER_CE
00242 else if( VLC_TRUE )
00243 {
00244
00245 p_sys->b_seekable = VLC_TRUE;
00246 }
00247 #elif defined( HAVE_SYS_STAT_H )
00248 else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode) ||
00249 S_ISBLK(stat_info.st_mode) )
00250 {
00251 p_sys->b_seekable = VLC_TRUE;
00252 p_access->info.i_size = stat_info.st_size;
00253 }
00254 else if( S_ISFIFO(stat_info.st_mode)
00255 # if !defined( SYS_BEOS ) && !defined( WIN32 )
00256 || S_ISSOCK(stat_info.st_mode)
00257 # endif
00258 )
00259 {
00260 p_sys->b_seekable = VLC_FALSE;
00261 }
00262 #endif
00263 else
00264 {
00265 msg_Err( p_access, "unknown file type for `%s'", psz_name );
00266 return VLC_EGENERIC;
00267 }
00268 }
00269
00270 msg_Dbg( p_access, "opening file `%s'", psz_name );
00271
00272 if( b_stdin )
00273 {
00274 p_sys->fd = 0;
00275 }
00276 else if( _OpenFile( p_access, psz_name ) )
00277 {
00278 free( p_sys );
00279 free( psz_name );
00280 return VLC_EGENERIC;
00281 }
00282
00283 if( p_sys->b_seekable && !p_access->info.i_size )
00284 {
00285
00286 msg_Err( p_access, "file %s is empty, aborting", psz_name );
00287 free( p_sys );
00288 free( psz_name );
00289 return VLC_EGENERIC;
00290 }
00291
00292
00293 var_Create( p_access, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00294
00295
00296
00297
00298 p_file = malloc( sizeof(file_entry_t) );
00299 p_file->i_size = p_access->info.i_size;
00300 p_file->psz_name = psz_name;
00301 TAB_APPEND( p_sys->i_file, p_sys->file, p_file );
00302
00303 psz = var_CreateGetString( p_access, "file-cat" );
00304 if( *psz )
00305 {
00306 char *psz_parser = psz_name = psz;
00307 int64_t i_size;
00308
00309 while( psz_name && *psz_name )
00310 {
00311 psz_parser = strchr( psz_name, ',' );
00312 if( psz_parser ) *psz_parser = 0;
00313
00314 psz_name = strdup( psz_name );
00315 if( psz_name )
00316 {
00317 msg_Dbg( p_access, "adding file `%s'", psz_name );
00318 i_size = 0;
00319
00320 #ifdef HAVE_SYS_STAT_H
00321 if( !stat( psz_name, &stat_info ) )
00322 {
00323 p_access->info.i_size += stat_info.st_size;
00324 i_size = stat_info.st_size;
00325 }
00326 else
00327 {
00328 msg_Dbg( p_access, "cannot stat() file `%s'", psz_name );
00329 }
00330 #endif
00331 p_file = malloc( sizeof(file_entry_t) );
00332 p_file->i_size = i_size;
00333 p_file->psz_name = psz_name;
00334
00335 TAB_APPEND( p_sys->i_file, p_sys->file, p_file );
00336 }
00337
00338 psz_name = psz_parser;
00339 if( psz_name ) psz_name++;
00340 }
00341 }
00342 free( psz );
00343
00344 return VLC_SUCCESS;
00345 }
00346
00347
00348
00349
00350 static void Close( vlc_object_t * p_this )
00351 {
00352 access_t *p_access = (access_t*)p_this;
00353 access_sys_t *p_sys = p_access->p_sys;
00354 int i;
00355
00356 close( p_sys->fd );
00357
00358 for( i = 0; i < p_sys->i_file; i++ )
00359 {
00360 free( p_sys->file[i]->psz_name );
00361 free( p_sys->file[i] );
00362 }
00363 free( p_sys->file );
00364
00365 free( p_sys );
00366 }
00367
00368
00369
00370
00371 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
00372 {
00373 access_sys_t *p_sys = p_access->p_sys;
00374 int i_ret;
00375
00376 #if !defined(WIN32) && !defined(UNDER_CE)
00377 if( !p_sys->b_pace_control )
00378 {
00379 if( !p_sys->b_kfir )
00380 {
00381
00382 struct timeval timeout;
00383 fd_set fds;
00384
00385
00386 FD_ZERO( &fds );
00387 FD_SET( p_sys->fd, &fds );
00388
00389
00390 timeout.tv_sec = 0;
00391 timeout.tv_usec = 500000;
00392
00393
00394 while( (i_ret = select( p_sys->fd + 1, &fds, NULL, NULL, &timeout )) == 0
00395 || (i_ret < 0 && errno == EINTR) )
00396 {
00397 FD_ZERO( &fds );
00398 FD_SET( p_sys->fd, &fds );
00399 timeout.tv_sec = 0;
00400 timeout.tv_usec = 500000;
00401
00402 if( p_access->b_die )
00403 return 0;
00404 }
00405
00406 if( i_ret < 0 )
00407 {
00408 msg_Err( p_access, "select error (%s)", strerror(errno) );
00409 return -1;
00410 }
00411
00412 i_ret = read( p_sys->fd, p_buffer, i_len );
00413 }
00414 else
00415 {
00416
00417 while ( (i_ret = read( p_sys->fd, p_buffer, i_len )) == 0 &&
00418 !p_access->b_die )
00419 {
00420 msleep( INPUT_ERROR_SLEEP );
00421 }
00422 }
00423 }
00424 else
00425 #endif
00426 {
00427
00428 i_ret = read( p_sys->fd, p_buffer, i_len );
00429 }
00430
00431 if( i_ret < 0 )
00432 {
00433 if( errno != EINTR && errno != EAGAIN )
00434 msg_Err( p_access, "read failed (%s)", strerror(errno) );
00435
00436
00437
00438 msleep( INPUT_ERROR_SLEEP );
00439 }
00440
00441 p_sys->i_nb_reads++;
00442 #ifdef HAVE_SYS_STAT_H
00443 if( p_access->info.i_size != 0 &&
00444 (p_sys->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
00445 {
00446 struct stat stat_info;
00447 int i_file = p_sys->i_index;
00448
00449 if ( fstat( p_sys->fd, &stat_info ) == -1 )
00450 {
00451 msg_Warn( p_access, "couldn't stat again the file (%s)", strerror(errno) );
00452 }
00453 else if ( p_sys->file[i_file]->i_size != stat_info.st_size )
00454 {
00455 p_access->info.i_size += (stat_info.st_size - p_sys->file[i_file]->i_size );
00456 p_sys->file[i_file]->i_size = stat_info.st_size;
00457 p_access->info.i_update |= INPUT_UPDATE_SIZE;
00458 }
00459 }
00460 #endif
00461
00462
00463 if ( i_ret == 0 && p_sys->i_index + 1 < p_sys->i_file )
00464 {
00465 char *psz_name = p_sys->file[++p_sys->i_index]->psz_name;
00466 p_sys->fd_backup = p_sys->fd;
00467
00468 msg_Dbg( p_access, "opening file `%s'", psz_name );
00469
00470 if ( _OpenFile( p_access, psz_name ) )
00471 {
00472 p_sys->fd = p_sys->fd_backup;
00473 return 0;
00474 }
00475
00476 close( p_sys->fd_backup );
00477
00478
00479 return Read( p_access, p_buffer, i_len );
00480 }
00481
00482 if( i_ret > 0 )
00483 p_access->info.i_pos += i_ret;
00484 else if( i_ret == 0 )
00485 p_access->info.b_eof = VLC_TRUE;
00486
00487 return i_ret;
00488 }
00489
00490
00491
00492
00493 static int Seek( access_t *p_access, int64_t i_pos )
00494 {
00495 access_sys_t *p_sys = p_access->p_sys;
00496 int64_t i_size = 0;
00497
00498
00499 if( p_sys->i_file > 1 )
00500 {
00501 int i;
00502 char *psz_name;
00503 p_sys->fd_backup = p_sys->fd;
00504
00505 for( i = 0; i < p_sys->i_file - 1; i++ )
00506 {
00507 if( i_pos < p_sys->file[i]->i_size + i_size )
00508 break;
00509 i_size += p_sys->file[i]->i_size;
00510 }
00511 psz_name = p_sys->file[i]->psz_name;
00512
00513 msg_Dbg( p_access, "opening file `%s'", psz_name );
00514
00515 if ( i != p_sys->i_index && !_OpenFile( p_access, psz_name ) )
00516 {
00517
00518 close( p_sys->fd_backup );
00519 p_sys->i_index = i;
00520 }
00521 else
00522 {
00523 p_sys->fd = p_sys->fd_backup;
00524 }
00525 }
00526
00527 lseek( p_sys->fd, i_pos - i_size, SEEK_SET );
00528
00529 p_access->info.i_pos = i_pos;
00530 if( p_access->info.i_size < p_access->info.i_pos )
00531 {
00532 msg_Err( p_access, "seeking too far" );
00533 p_access->info.i_pos = p_access->info.i_size;
00534 }
00535 else if( p_access->info.i_pos < 0 )
00536 {
00537 msg_Err( p_access, "seeking too early" );
00538 p_access->info.i_pos = 0;
00539 }
00540
00541 p_access->info.b_eof = VLC_FALSE;
00542
00543
00544 return VLC_SUCCESS;
00545 }
00546
00547
00548
00549
00550 static int Control( access_t *p_access, int i_query, va_list args )
00551 {
00552 access_sys_t *p_sys = p_access->p_sys;
00553 vlc_bool_t *pb_bool;
00554 int *pi_int;
00555 int64_t *pi_64;
00556
00557 switch( i_query )
00558 {
00559
00560 case ACCESS_CAN_SEEK:
00561 case ACCESS_CAN_FASTSEEK:
00562 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00563 *pb_bool = p_sys->b_seekable;
00564 break;
00565
00566 case ACCESS_CAN_PAUSE:
00567 case ACCESS_CAN_CONTROL_PACE:
00568 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00569 *pb_bool = p_sys->b_pace_control;
00570 break;
00571
00572
00573 case ACCESS_GET_MTU:
00574 pi_int = (int*)va_arg( args, int * );
00575 *pi_int = 0;
00576 break;
00577
00578 case ACCESS_GET_PTS_DELAY:
00579 pi_64 = (int64_t*)va_arg( args, int64_t * );
00580 *pi_64 = var_GetInteger( p_access, "file-caching" ) * I64C(1000);
00581 break;
00582
00583
00584 case ACCESS_SET_PAUSE_STATE:
00585
00586 break;
00587
00588 case ACCESS_GET_TITLE_INFO:
00589 case ACCESS_SET_TITLE:
00590 case ACCESS_SET_SEEKPOINT:
00591 case ACCESS_SET_PRIVATE_ID_STATE:
00592 case ACCESS_GET_META:
00593 return VLC_EGENERIC;
00594
00595 default:
00596 msg_Warn( p_access, "unimplemented query in control" );
00597 return VLC_EGENERIC;
00598
00599 }
00600 return VLC_SUCCESS;
00601 }
00602
00603
00604
00605
00606
00607 static int _OpenFile( access_t * p_access, const char * psz_name )
00608 {
00609 access_sys_t *p_sys = p_access->p_sys;
00610 const char *psz_localname;
00611
00612 psz_localname = ToLocale( psz_name );
00613
00614 #ifdef UNDER_CE
00615 p_sys->fd = fopen( psz_localname, "rb" );
00616 LocaleFree( psz_localname );
00617 if ( !p_sys->fd )
00618 {
00619 msg_Err( p_access, "cannot open file %s", psz_name );
00620 return VLC_EGENERIC;
00621 }
00622
00623 fseek( p_sys->fd, 0, SEEK_END );
00624 p_access->info.i_size = ftell( p_sys->fd );
00625 p_access->info.i_update |= INPUT_UPDATE_SIZE;
00626 fseek( p_sys->fd, 0, SEEK_SET );
00627 #else
00628
00629 p_sys->fd = open( psz_localname, O_NONBLOCK );
00630 LocaleFree( psz_localname );
00631 if ( p_sys->fd == -1 )
00632 {
00633 msg_Err( p_access, "cannot open file %s (%s)", psz_name,
00634 strerror(errno) );
00635 return VLC_EGENERIC;
00636 }
00637 #endif
00638
00639 return VLC_SUCCESS;
00640 }