]> git.sesse.net Git - vlc/blob - modules/access/file.c
* now 0.6.0-cvs
[vlc] / modules / access / file.c
1 /*****************************************************************************
2  * file.c: file input (file: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: file.c,v 1.20 2003/05/15 22:27:36 massiot Exp $
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_STAT_H
36 #   include <sys/stat.h>
37 #endif
38 #ifdef HAVE_ERRNO_H
39 #   include <errno.h>
40 #endif
41 #ifdef HAVE_FCNTL_H
42 #   include <fcntl.h>
43 #endif
44
45 #ifdef HAVE_UNISTD_H
46 #   include <unistd.h>
47 #elif defined( WIN32 ) && !defined( UNDER_CE )
48 #   include <io.h>
49 #endif
50
51 /* stat() support for large files on win32 */
52 #if defined( WIN32 ) && !defined( UNDER_CE )
53 #define stat(a,b) _stati64(a,b)
54 #define fstat(a,b) _fstati64(a,b)
55 #endif
56
57 /*****************************************************************************
58  * Exported prototypes
59  *****************************************************************************/
60 static int     Open   ( vlc_object_t * );
61 static void    Close  ( vlc_object_t * );
62
63 static void    Seek   ( input_thread_t *, off_t );
64 static ssize_t Read   ( input_thread_t *, byte_t *, size_t );
65
66 /*****************************************************************************
67  * Module descriptor
68  *****************************************************************************/
69 #define CACHING_TEXT N_("Caching value in ms")
70 #define CACHING_LONGTEXT N_( \
71     "Allows you to modify the default caching value for file streams. This " \
72     "value should be set in miliseconds units." )
73
74 vlc_module_begin();
75     set_description( _("Standard filesystem file input") );
76     add_category_hint( N_("file"), NULL, VLC_TRUE );
77     add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
78     set_capability( "access", 50 );
79     add_shortcut( "file" );
80     add_shortcut( "stream" );
81     add_shortcut( "kfir" );
82     set_callbacks( Open, Close );
83 vlc_module_end();
84  
85 /*****************************************************************************
86  * _input_socket_t: private access plug-in data, modified to add private
87  *                  fields
88  *****************************************************************************/
89 typedef struct _input_socket_s
90 {
91     input_socket_t      _socket;
92
93     unsigned int        i_nb_reads;
94     vlc_bool_t          b_kfir;
95 } _input_socket_t;
96
97 /*****************************************************************************
98  * Open: open the file
99  *****************************************************************************/
100 static int Open( vlc_object_t *p_this )
101 {
102     input_thread_t *    p_input = (input_thread_t *)p_this;
103     char *              psz_name = p_input->psz_name;
104 #ifdef HAVE_SYS_STAT_H
105     int                 i_stat;
106 #if defined( WIN32 ) && !defined( UNDER_CE )
107     struct _stati64     stat_info;
108 #else
109     struct stat         stat_info;
110 #endif
111 #endif
112     _input_socket_t *   p_access_data;
113     vlc_bool_t          b_stdin, b_kfir = 0;
114
115     p_input->i_mtu = 0;
116
117     b_stdin = psz_name[0] == '-' && psz_name[1] == '\0';
118
119 #ifdef HAVE_SYS_STAT_H
120     if( !b_stdin && (i_stat = stat( psz_name, &stat_info )) == (-1) )
121     {
122 #   ifdef HAVE_ERRNO_H
123         msg_Warn( p_input, "cannot stat() file `%s' (%s)",
124                   psz_name, strerror(errno));
125 #   else
126         msg_Warn( p_input, "cannot stat() file `%s'", psz_name );
127 #   endif
128         return VLC_EGENERIC;
129     }
130 #endif
131
132     p_input->pf_read = Read;
133     p_input->pf_set_program = input_SetProgram;
134     p_input->pf_set_area = NULL;
135     p_input->pf_seek = Seek;
136
137     vlc_mutex_lock( &p_input->stream.stream_lock );
138
139     if( *p_input->psz_access && !strncmp( p_input->psz_access, "stream", 7 ) )
140     {
141         /* stream:%s */
142         p_input->stream.b_pace_control = 0;
143         p_input->stream.b_seekable = 0;
144         p_input->stream.p_selected_area->i_size = 0;
145     }
146     else if( *p_input->psz_access &&
147              !strncmp( p_input->psz_access, "kfir", 7 ) )
148     {
149         /* stream:%s */
150         p_input->stream.b_pace_control = 0;
151         p_input->stream.b_seekable = 0;
152         p_input->stream.p_selected_area->i_size = 0;
153         b_kfir = 1;
154     }
155     else
156     {
157         /* file:%s or %s */
158         p_input->stream.b_pace_control = 1;
159
160         if( b_stdin )
161         {
162             p_input->stream.b_seekable = 0;
163             p_input->stream.p_selected_area->i_size = 0;
164         }
165 #ifdef UNDER_CE
166         else if( VLC_TRUE )
167         {
168             /* We'll update i_size after it's been opened */
169             p_input->stream.b_seekable = 1;
170         }
171 #elif defined( HAVE_SYS_STAT_H )
172         else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode)
173                   || S_ISBLK(stat_info.st_mode) )
174         {
175             p_input->stream.b_seekable = 1;
176             p_input->stream.p_selected_area->i_size = stat_info.st_size;
177         }
178         else if( S_ISFIFO(stat_info.st_mode)
179 #   if !defined( SYS_BEOS ) && !defined( WIN32 )
180                   || S_ISSOCK(stat_info.st_mode)
181 #   endif
182                )
183         {
184             p_input->stream.b_seekable = 0;
185             p_input->stream.p_selected_area->i_size = 0;
186         }
187 #endif
188         else
189         {
190             vlc_mutex_unlock( &p_input->stream.stream_lock );
191             msg_Err( p_input, "unknown file type for `%s'", psz_name );
192             return VLC_EGENERIC;
193         }
194     }
195  
196     p_input->stream.p_selected_area->i_tell = 0;
197     p_input->stream.i_method = INPUT_METHOD_FILE;
198     vlc_mutex_unlock( &p_input->stream.stream_lock );
199  
200     msg_Dbg( p_input, "opening file `%s'", psz_name );
201     p_access_data = malloc( sizeof(_input_socket_t) );
202     p_input->p_access_data = (void *)p_access_data;
203     if( p_access_data == NULL )
204     {
205         msg_Err( p_input, "out of memory" );
206         return VLC_ENOMEM;
207     }
208
209     p_access_data->i_nb_reads = 0;
210     p_access_data->b_kfir = b_kfir;
211     if( b_stdin )
212     {
213         p_access_data->_socket.i_handle = 0;
214     }
215     else
216     {
217 #ifdef UNDER_CE
218         wchar_t psz_filename[MAX_PATH];
219         MultiByteToWideChar( CP_ACP, 0, psz_name, -1, psz_filename, MAX_PATH );
220
221         p_access_data->_socket.i_handle = (int)CreateFile( psz_filename,
222             GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 
223             FILE_ATTRIBUTE_NORMAL, NULL );
224
225         if( (HANDLE)p_access_data->_socket.i_handle == INVALID_HANDLE_VALUE )
226         {
227             msg_Err( p_input, "cannot open file %s", psz_name );
228             free( p_access_data );
229             return VLC_EGENERIC;
230         }
231         p_input->stream.p_selected_area->i_size =
232                 GetFileSize( (HANDLE)p_access_data->_socket.i_handle, NULL );
233 #else
234         p_access_data->_socket.i_handle = open( psz_name,
235                                                 O_NONBLOCK /*| O_LARGEFILE*/ );
236         if( p_access_data->_socket.i_handle == -1 )
237         {
238 #   ifdef HAVE_ERRNO_H
239             msg_Err( p_input, "cannot open file %s (%s)", psz_name,
240                               strerror(errno) );
241 #   else
242             msg_Err( p_input, "cannot open file %s", psz_name );
243 #   endif
244             free( p_access_data );
245             return VLC_EGENERIC;
246         }
247 #endif
248     }
249
250     if ( p_input->stream.b_seekable
251           && !p_input->stream.p_selected_area->i_size )
252     {
253         msg_Err( p_input, "file %s is empty, aborting", psz_name );
254         free( p_access_data );
255         return VLC_EGENERIC;
256     }
257
258     /* Update default_pts to a suitable value for file access */
259     p_input->i_pts_delay = config_GetInt( p_input, "file-caching" ) * 1000;
260
261     return VLC_SUCCESS;
262 }
263
264 /*****************************************************************************
265  * Close: close the target
266  *****************************************************************************/
267 static void Close( vlc_object_t * p_this )
268 {
269     input_thread_t * p_input = (input_thread_t *)p_this;
270     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
271
272     msg_Info( p_input, "closing `%s/%s://%s'", 
273               p_input->psz_access, p_input->psz_demux, p_input->psz_name );
274  
275 #ifdef UNDER_CE
276     CloseHandle( (HANDLE)p_access_data->i_handle );
277 #else
278     close( p_access_data->i_handle );
279 #endif
280
281     free( p_access_data );
282 }
283
284 /*****************************************************************************
285  * Read: standard read on a file descriptor.
286  *****************************************************************************/
287 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
288 {
289     _input_socket_t * p_access_data = (_input_socket_t *)p_input->p_access_data;
290     ssize_t i_ret;
291  
292 #ifdef UNDER_CE
293     if( !ReadFile( (HANDLE)p_access_data->_socket.i_handle, p_buffer, i_len,
294                    (LPDWORD)&i_ret, NULL ) )
295     {
296         i_ret = -1;
297     }
298 #else
299 #ifndef WIN32
300     if ( !p_input->stream.b_pace_control )
301     {
302         if ( !p_access_data->b_kfir )
303         {
304             /* Find if some data is available. This won't work under Windows. */
305             struct timeval  timeout;
306             fd_set          fds;
307
308             /* Initialize file descriptor set */
309             FD_ZERO( &fds );
310             FD_SET( p_access_data->_socket.i_handle, &fds );
311
312             /* We'll wait 0.5 second if nothing happens */
313             timeout.tv_sec = 0;
314             timeout.tv_usec = 500000;
315
316             /* Find if some data is available */
317             while( (i_ret = select( p_access_data->_socket.i_handle + 1, &fds,
318                                     NULL, NULL, &timeout )) == 0
319                     || (i_ret < 0 && errno == EINTR) )
320             {
321                 FD_ZERO( &fds );
322                 FD_SET( p_access_data->_socket.i_handle, &fds );
323                 timeout.tv_sec = 0;
324                 timeout.tv_usec = 500000;
325
326                 if( p_input->b_die || p_input->b_error )
327                 {
328                     return 0;
329                 }
330             }
331
332             if( i_ret < 0 )
333             {
334                 msg_Err( p_input, "select error (%s)", strerror(errno) );
335                 return -1;
336             }
337
338             i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len );
339         }
340         else
341         {
342             /* b_kfir ; work around a buggy poll() driver implementation */
343             while ( (i_ret = read( p_access_data->_socket.i_handle, p_buffer,
344                                    i_len )) == 0 &&
345                       !p_input->b_die && !p_input->b_error )
346             {
347                 msleep(INPUT_ERROR_SLEEP);
348             }
349         }
350     }
351     else
352 #   endif
353     {
354         /* b_pace_control || WIN32 */
355         i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len );
356     }
357 #endif
358
359     if( i_ret < 0 )
360     {
361 #ifdef HAVE_ERRNO_H
362         if ( errno != EINTR && errno != EAGAIN )
363             msg_Err( p_input, "read failed (%s)", strerror(errno) );
364 #else
365         msg_Err( p_input, "read failed" );
366 #endif
367
368         /* Delay a bit to avoid consuming all the CPU. This is particularly
369          * useful when reading from an unconnected FIFO. */
370         msleep( INPUT_ERROR_SLEEP );
371     }
372  
373     p_access_data->i_nb_reads++;
374 #ifdef HAVE_SYS_STAT_H
375     if ( p_input->stream.p_selected_area->i_size != 0
376             && (p_access_data->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
377     {
378 #if defined( WIN32 ) && !defined( UNDER_CE )
379         struct _stati64 stat_info;
380 #else
381         struct stat stat_info;
382 #endif
383         if ( fstat( p_access_data->_socket.i_handle, &stat_info ) == -1 )
384         {
385 #   ifdef HAVE_ERRNO_H
386             msg_Warn( p_input, "couldn't stat again the file (%s)",
387                       strerror(errno) );
388 #   else
389             msg_Warn( p_input, "couldn't stat again the file" );
390 #   endif
391         }
392         else if ( p_input->stream.p_selected_area->i_size != stat_info.st_size )
393         {
394             p_input->stream.p_selected_area->i_size = stat_info.st_size;
395             p_input->stream.b_changed = 1;
396         }
397     }
398 #endif
399
400     return i_ret;
401 }
402
403 /*****************************************************************************
404  * Seek: seek to a specific location in a file
405  *****************************************************************************/
406 static void Seek( input_thread_t * p_input, off_t i_pos )
407 {
408 #define S p_input->stream
409     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
410
411 #if defined( WIN32 ) && !defined( UNDER_CE )
412     _lseeki64( p_access_data->i_handle, i_pos, SEEK_SET );
413 #else
414     lseek( p_access_data->i_handle, i_pos, SEEK_SET );
415 #endif
416
417     vlc_mutex_lock( &S.stream_lock );
418     S.p_selected_area->i_tell = i_pos;
419     if( S.p_selected_area->i_tell > S.p_selected_area->i_size )
420     {
421         msg_Err( p_input, "seeking too far" );
422         S.p_selected_area->i_tell = S.p_selected_area->i_size;
423     }
424     else if( S.p_selected_area->i_tell < 0 )
425     {
426         msg_Err( p_input, "seeking too early" );
427         S.p_selected_area->i_tell = 0;
428     }
429     vlc_mutex_unlock( &S.stream_lock );
430 #undef S
431 }
432