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