]> git.sesse.net Git - vlc/blob - modules/access/file.c
* modules/access/file.c: got rid of the #ifdef HAVE_ERRNO_H + WinCE compilation fixes.
[vlc] / modules / access / file.c
1 /*****************************************************************************
2  * file.c: file input (file: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/input.h>
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #ifdef HAVE_SYS_TYPES_H
34 #   include <sys/types.h>
35 #endif
36 #ifdef HAVE_SYS_TIME_H
37 #   include <sys/time.h>
38 #endif
39 #ifdef HAVE_SYS_STAT_H
40 #   include <sys/stat.h>
41 #endif
42 #ifdef HAVE_FCNTL_H
43 #   include <fcntl.h>
44 #endif
45
46 #ifdef HAVE_UNISTD_H
47 #   include <unistd.h>
48 #elif defined( WIN32 ) && !defined( UNDER_CE )
49 #   include <io.h>
50 #endif
51
52 /* stat() support for large files on win32 */
53 #if defined( WIN32 ) && !defined( UNDER_CE )
54 #define stat _stati64
55 #define fstat(a,b) _fstati64(a,b)
56 #endif
57
58 #ifdef UNDER_CE
59 #   define close(a) CloseHandle((HANDLE)a)
60 #endif
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 static int  Open ( vlc_object_t * );
66 static void Close( vlc_object_t * );
67
68 #define CACHING_TEXT N_("Caching value in ms")
69 #define CACHING_LONGTEXT N_( \
70     "Allows you to modify the default caching value for file streams. This " \
71     "value should be set in millisecond units." )
72 #define CAT_TEXT N_("Concatenate with additional files")
73 #define CAT_LONGTEXT N_( \
74     "Allows you to play split files as if they were part of a unique file. " \
75     "Specify a comma-separated list of files." )
76
77 vlc_module_begin();
78     set_description( _("Standard filesystem file input") );
79     add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
80     add_string( "file-cat", NULL, NULL, CAT_TEXT, CAT_LONGTEXT, VLC_TRUE );
81     set_capability( "access2", 50 );
82     add_shortcut( "file" );
83     add_shortcut( "stream" );
84     add_shortcut( "kfir" );
85     set_callbacks( Open, Close );
86 vlc_module_end();
87
88
89 /*****************************************************************************
90  * Exported prototypes
91  *****************************************************************************/
92 static int  Seek( access_t *, int64_t );
93 static int  Read( access_t *, uint8_t *, int );
94 static int  Control( access_t *, int, va_list );
95
96 static int  _OpenFile( access_t *, char * );
97
98 typedef struct
99 {
100     char     *psz_name;
101     int64_t  i_size;
102
103 } file_entry_t;
104
105 struct access_sys_t
106 {
107     unsigned int i_nb_reads;
108     vlc_bool_t   b_kfir;
109
110     /* Files list */
111     int          i_file;
112     file_entry_t **file;
113
114     /* Current file */
115     int  i_index;
116     int  fd;
117 #ifdef UNDER_CE
118     HANDLE fd_handle;
119 #endif
120
121     /* */
122     vlc_bool_t b_seekable;
123     vlc_bool_t b_pace_control;
124 };
125
126 /*****************************************************************************
127  * Open: open the file
128  *****************************************************************************/
129 static int Open( vlc_object_t *p_this )
130 {
131     access_t     *p_access = (access_t*)p_this;
132     access_sys_t *p_sys;
133     char *psz_name = p_access->psz_path;
134     char *psz;
135
136 #ifdef HAVE_SYS_STAT_H
137     int                 i_stat;
138     struct stat         stat_info;
139 #endif
140     vlc_bool_t          b_stdin;
141
142     file_entry_t *      p_file;
143
144
145     b_stdin = psz_name[0] == '-' && psz_name[1] == '\0';
146
147 #ifdef HAVE_SYS_STAT_H
148     if( !b_stdin && (i_stat = stat( psz_name, &stat_info )) == (-1) )
149     {
150         msg_Warn( p_access, "cannot stat() file `%s' (%s)",
151                   psz_name, strerror(errno));
152         return VLC_EGENERIC;
153     }
154 #endif
155
156     p_access->pf_read = Read;
157     p_access->pf_block = NULL;
158     p_access->pf_seek = Seek;
159     p_access->pf_control = Control;
160     p_access->info.i_update = 0;
161     p_access->info.i_size = 0;
162     p_access->info.i_pos = 0;
163     p_access->info.b_eof = VLC_FALSE;
164     p_access->info.i_title = 0;
165     p_access->info.i_seekpoint = 0;
166     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
167     p_sys->i_nb_reads = 0;
168     p_sys->b_kfir = VLC_FALSE;
169     p_sys->file = NULL;
170     p_sys->i_file = 0;
171     p_sys->i_index = 0;
172     p_sys->fd = -1;
173
174     if( !strcasecmp( p_access->psz_access, "stream" ) )
175     {
176         p_sys->b_seekable = VLC_FALSE;
177         p_sys->b_pace_control = VLC_FALSE;
178     }
179     else if( !strcasecmp( p_access->psz_access, "kfir" ) )
180     {
181         p_sys->b_seekable = VLC_FALSE;
182         p_sys->b_pace_control = VLC_FALSE;
183         p_sys->b_kfir = VLC_TRUE;
184     }
185     else
186     {
187         /* file:%s or %s */
188         p_sys->b_pace_control = VLC_TRUE;
189
190         if( b_stdin )
191         {
192             p_sys->b_seekable = VLC_FALSE;
193         }
194 #ifdef UNDER_CE
195         else if( VLC_TRUE )
196         {
197             /* We'll update i_size after it's been opened */
198             p_sys->b_seekable = VLC_TRUE;
199         }
200 #elif defined( HAVE_SYS_STAT_H )
201         else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode) ||
202                  S_ISBLK(stat_info.st_mode) )
203         {
204             p_sys->b_seekable = VLC_TRUE;
205             p_access->info.i_size = stat_info.st_size;
206         }
207         else if( S_ISFIFO(stat_info.st_mode)
208 #   if !defined( SYS_BEOS ) && !defined( WIN32 )
209                   || S_ISSOCK(stat_info.st_mode)
210 #   endif
211                )
212         {
213             p_sys->b_seekable = VLC_FALSE;
214         }
215 #endif
216         else
217         {
218             msg_Err( p_access, "unknown file type for `%s'", psz_name );
219             return VLC_EGENERIC;
220         }
221     }
222
223     msg_Dbg( p_access, "opening file `%s'", psz_name );
224
225     if( b_stdin )
226     {
227         p_sys->fd = 0;
228     }
229     else if( _OpenFile( p_access, psz_name ) )
230     {
231         free( p_sys );
232         return VLC_EGENERIC;
233     }
234
235     if( p_sys->b_seekable && !p_access->info.i_size )
236     {
237         /* FIXME that's bad because all others access will be probed */
238         msg_Err( p_access, "file %s is empty, aborting", psz_name );
239         free( p_sys );
240         return VLC_EGENERIC;
241     }
242
243     /* Update default_pts to a suitable value for file access */
244     var_Create( p_access, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
245
246     /*
247      * Get the additional list of files
248      */
249     p_file = malloc( sizeof(file_entry_t) );
250     p_file->i_size = p_access->info.i_size;
251     p_file->psz_name = strdup( psz_name );
252     TAB_APPEND( p_sys->i_file, p_sys->file, p_file );
253
254     psz = var_CreateGetString( p_access, "file-cat" );
255     if( *psz )
256     {
257         char *psz_parser = psz_name = psz;
258         int64_t i_size;
259
260         while( psz_name && *psz_name )
261         {
262             psz_parser = strchr( psz_name, ',' );
263             if( psz_parser ) *psz_parser = 0;
264
265             psz_name = strdup( psz_name );
266             if( psz_name )
267             {
268                 msg_Dbg( p_access, "adding file `%s'", psz_name );
269                 i_size = 0;
270
271 #ifdef HAVE_SYS_STAT_H
272                 if( !stat( psz_name, &stat_info ) )
273                 {
274                     p_access->info.i_size += stat_info.st_size;
275                     i_size = stat_info.st_size;
276                 }
277                 else
278                 {
279                     msg_Dbg( p_access, "cannot stat() file `%s'", psz_name );
280                 }
281 #endif
282                 p_file = malloc( sizeof(file_entry_t) );
283                 p_file->i_size = i_size;
284                 p_file->psz_name = psz_name;
285
286                 TAB_APPEND( p_sys->i_file, p_sys->file, p_file );
287             }
288
289             psz_name = psz_parser;
290             if( psz_name ) psz_name++;
291         }
292     }
293     free( psz );
294
295     return VLC_SUCCESS;
296 }
297
298 /*****************************************************************************
299  * Close: close the target
300  *****************************************************************************/
301 static void Close( vlc_object_t * p_this )
302 {
303     access_t     *p_access = (access_t*)p_this;
304     access_sys_t *p_sys = p_access->p_sys;
305     int i;
306
307     close( p_sys->fd );
308
309     for( i = 0; i < p_sys->i_file; i++ )
310     {
311         free( p_sys->file[i]->psz_name );
312         free( p_sys->file[i] );
313     }
314     free( p_sys->file );
315
316     free( p_sys );
317 }
318
319 /*****************************************************************************
320  * Read: standard read on a file descriptor.
321  *****************************************************************************/
322 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
323 {
324     access_sys_t *p_sys = p_access->p_sys;
325     int i_ret;
326
327 #if !defined(WIN32) && !defined(UNDER_CE)
328     if( !p_sys->b_pace_control )
329     {
330         if( !p_sys->b_kfir )
331         {
332             /* Find if some data is available. This won't work under Windows. */
333             struct timeval  timeout;
334             fd_set          fds;
335
336             /* Initialize file descriptor set */
337             FD_ZERO( &fds );
338             FD_SET( p_sys->fd, &fds );
339
340             /* We'll wait 0.5 second if nothing happens */
341             timeout.tv_sec = 0;
342             timeout.tv_usec = 500000;
343
344             /* Find if some data is available */
345             while( (i_ret = select( p_sys->fd + 1, &fds, NULL, NULL, &timeout )) == 0
346                     || (i_ret < 0 && errno == EINTR) )
347             {
348                 FD_ZERO( &fds );
349                 FD_SET( p_sys->fd, &fds );
350                 timeout.tv_sec = 0;
351                 timeout.tv_usec = 500000;
352
353                 if( p_access->b_die )
354                     return 0;
355             }
356
357             if( i_ret < 0 )
358             {
359                 msg_Err( p_access, "select error (%s)", strerror(errno) );
360                 return -1;
361             }
362
363             i_ret = read( p_sys->fd, p_buffer, i_len );
364         }
365         else
366         {
367             /* b_kfir ; work around a buggy poll() driver implementation */
368             while ( (i_ret = read( p_sys->fd, p_buffer, i_len )) == 0 && !p_access->b_die )
369             {
370                 msleep( INPUT_ERROR_SLEEP );
371             }
372         }
373     }
374     else
375 #endif /* WIN32 || UNDER_CE */
376 #ifdef UNDER_CE
377     if( !ReadFile( p_sys->fd_handle, p_buffer, i_len, (LPDWORD)&i_ret, 0 ) )
378     {
379         i_ret = -1;
380     }
381 #else
382     {
383         /* b_pace_control || WIN32 */
384         i_ret = read( p_sys->fd, p_buffer, i_len );
385     }
386 #endif
387
388     if( i_ret < 0 )
389     {
390         if( errno != EINTR && errno != EAGAIN )
391             msg_Err( p_access, "read failed (%s)", strerror(errno) );
392
393         /* Delay a bit to avoid consuming all the CPU. This is particularly
394          * useful when reading from an unconnected FIFO. */
395         msleep( INPUT_ERROR_SLEEP );
396     }
397
398     p_sys->i_nb_reads++;
399 #ifdef HAVE_SYS_STAT_H
400     if( p_access->info.i_size != 0 &&
401         (p_sys->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
402     {
403         struct stat stat_info;
404         int i_file = p_sys->i_index;
405
406         if ( fstat( p_sys->fd, &stat_info ) == -1 )
407         {
408             msg_Warn( p_access, "couldn't stat again the file (%s)", strerror(errno) );
409         }
410         else if ( p_sys->file[i_file]->i_size != stat_info.st_size )
411         {
412             p_access->info.i_size += (stat_info.st_size - p_sys->file[i_file]->i_size );
413             p_access->info.i_update |= INPUT_UPDATE_SIZE;
414         }
415     }
416 #endif
417
418     /* If we reached an EOF then switch to the next file in the list */
419     if ( i_ret == 0 && p_sys->i_index + 1 < p_sys->i_file )
420     {
421         int fd = p_sys->fd;
422         char *psz_name = p_sys->file[++p_sys->i_index]->psz_name;
423
424         msg_Dbg( p_access, "opening file `%s'", psz_name );
425
426         if ( _OpenFile( p_access, psz_name ) )
427         {
428             p_sys->fd = fd;
429             return 0;
430         }
431
432         close( fd );
433
434         /* We have to read some data */
435         return Read( p_access, p_buffer, i_len );
436     }
437
438     if( i_ret > 0 )
439         p_access->info.i_pos += i_ret;
440     else if( i_ret == 0 )
441         p_access->info.b_eof = VLC_TRUE;
442
443     return i_ret;
444 }
445
446 /*****************************************************************************
447  * Seek: seek to a specific location in a file
448  *****************************************************************************/
449 static int Seek( access_t *p_access, int64_t i_pos )
450 {
451     access_sys_t *p_sys = p_access->p_sys;
452     int64_t i_size = 0;
453
454     /* Check which file we need to access */
455     if( p_sys->i_file > 1 )
456     {
457         int fd = p_sys->fd;
458         int i;
459         char *psz_name;
460
461         for( i = 0; i < p_sys->i_file - 1; i++ )
462         {
463             if( i_pos < p_sys->file[i]->i_size + i_size )
464                 break;
465             i_size += p_sys->file[i]->i_size;
466         }
467         psz_name = p_sys->file[i]->psz_name;
468
469         msg_Dbg( p_access, "opening file `%s'", psz_name );
470
471         if ( i != p_sys->i_index && !_OpenFile( p_access, psz_name ) )
472         {
473             /* Close old file */
474             close( fd );
475             p_sys->i_index = i;
476         }
477         else
478         {
479             p_sys->fd = fd;
480         }
481     }
482
483 #if defined( WIN32 ) && !defined( UNDER_CE )
484     _lseeki64( p_sys->fd, i_pos - i_size, SEEK_SET );
485 #else
486     lseek( p_sys->fd, i_pos - i_size, SEEK_SET );
487 #endif
488
489     p_access->info.i_pos = i_pos;
490     if( p_access->info.i_size < p_access->info.i_pos )
491     {
492         msg_Err( p_access, "seeking too far" );
493         p_access->info.i_pos = p_access->info.i_size;
494     }
495     else if( p_access->info.i_pos < 0 )
496     {
497         msg_Err( p_access, "seeking too early" );
498         p_access->info.i_pos = 0;
499     }
500     /* Reset eof */
501     p_access->info.b_eof = VLC_FALSE;
502
503     /* FIXME */
504     return VLC_SUCCESS;
505 }
506
507 /*****************************************************************************
508  * Control:
509  *****************************************************************************/
510 static int Control( access_t *p_access, int i_query, va_list args )
511 {
512     access_sys_t *p_sys = p_access->p_sys;
513     vlc_bool_t   *pb_bool;
514     int          *pi_int;
515     int64_t      *pi_64;
516
517     switch( i_query )
518     {
519         /* */
520         case ACCESS_CAN_SEEK:
521         case ACCESS_CAN_FASTSEEK:
522             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
523             *pb_bool = p_sys->b_seekable;
524             break;
525
526         case ACCESS_CAN_PAUSE:
527         case ACCESS_CAN_CONTROL_PACE:
528             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
529             *pb_bool = p_sys->b_pace_control;
530             break;
531
532         /* */
533         case ACCESS_GET_MTU:
534             pi_int = (int*)va_arg( args, int * );
535             *pi_int = 0;
536             break;
537
538         case ACCESS_GET_PTS_DELAY:
539             pi_64 = (int64_t*)va_arg( args, int64_t * );
540             *pi_64 = (int64_t)var_GetInteger( p_access, "file-caching" ) * I64C(1000);
541             break;
542         /* */
543         case ACCESS_SET_PAUSE_STATE:
544             /* Nothing to do */
545             break;
546
547         case ACCESS_GET_TITLE_INFO:
548         case ACCESS_SET_TITLE:
549         case ACCESS_SET_SEEKPOINT:
550         case ACCESS_SET_PRIVATE_ID_STATE:
551             return VLC_EGENERIC;
552
553         default:
554             msg_Warn( p_access, "unimplemented query in control" );
555             return VLC_EGENERIC;
556
557     }
558     return VLC_SUCCESS;
559 }
560
561
562 /*****************************************************************************
563  * OpenFile: Opens a specific file
564  *****************************************************************************/
565 static int _OpenFile( access_t * p_access, char * psz_name )
566 {
567     access_sys_t *p_sys = p_access->p_sys;
568
569 #ifdef UNDER_CE
570     wchar_t psz_filename[MAX_PATH];
571     MultiByteToWideChar( CP_ACP, 0, psz_name, -1, psz_filename, MAX_PATH );
572
573     p_sys->fd_handle =
574        CreateFile( psz_filename, GENERIC_READ, FILE_SHARE_READ,
575                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
576     p_sys->fd = (int)p_sys->fd_handle;
577
578     if ( p_sys->fd_handle == INVALID_HANDLE_VALUE )
579     {
580         msg_Err( p_access, "cannot open file %s", psz_name );
581         return VLC_EGENERIC;
582     }
583     p_access->info.i_size =
584         GetFileSize( p_sys->fd_handle, NULL );
585     p_access->info.i_update |= INPUT_UPDATE_SIZE;
586 #else
587
588     p_sys->fd = open( psz_name, O_NONBLOCK /*| O_LARGEFILE*/ );
589     if ( p_sys->fd == -1 )
590     {
591         msg_Err( p_access, "cannot open file %s (%s)", psz_name,
592                  strerror(errno) );
593         return VLC_EGENERIC;
594     }
595 #endif
596
597     return VLC_SUCCESS;
598 }