* Added a new kfir: access module, designed to work around a bug in the
[vlc.git] / 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.17 2003/04/02 23:16:30 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 #if defined( WIN32 ) && !defined( UNDER_CE )
52 #   ifdef lseek
53 #       undef lseek
54 #   endif
55 #   define lseek _lseeki64
56 #endif
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     struct stat         stat_info;
107 #endif
108     _input_socket_t *   p_access_data;
109     vlc_bool_t          b_stdin, b_kfir = 0;
110
111     p_input->i_mtu = 0;
112
113     b_stdin = psz_name[0] == '-' && psz_name[1] == '\0';
114
115 #ifdef HAVE_SYS_STAT_H
116     if( !b_stdin && (i_stat = stat( psz_name, &stat_info )) == (-1) )
117     {
118 #   ifdef HAVE_ERRNO_H
119         msg_Warn( p_input, "cannot stat() file `%s' (%s)",
120                   psz_name, strerror(errno));
121 #   else
122         msg_Warn( p_input, "cannot stat() file `%s'", psz_name );
123 #   endif
124         return VLC_EGENERIC;
125     }
126 #endif
127
128     p_input->pf_read = Read;
129     p_input->pf_set_program = input_SetProgram;
130     p_input->pf_set_area = NULL;
131     p_input->pf_seek = Seek;
132
133     vlc_mutex_lock( &p_input->stream.stream_lock );
134
135     if( *p_input->psz_access && !strncmp( p_input->psz_access, "stream", 7 ) )
136     {
137         /* stream:%s */
138         p_input->stream.b_pace_control = 0;
139         p_input->stream.b_seekable = 0;
140         p_input->stream.p_selected_area->i_size = 0;
141     }
142     else if( *p_input->psz_access &&
143              !strncmp( p_input->psz_access, "kfir", 7 ) )
144     {
145         /* stream:%s */
146         p_input->stream.b_pace_control = 0;
147         p_input->stream.b_seekable = 0;
148         p_input->stream.p_selected_area->i_size = 0;
149         b_kfir = 1;
150     }
151     else
152     {
153         /* file:%s or %s */
154         p_input->stream.b_pace_control = 1;
155
156         if( b_stdin )
157         {
158             p_input->stream.b_seekable = 0;
159             p_input->stream.p_selected_area->i_size = 0;
160         }
161 #ifdef UNDER_CE
162         else if( VLC_TRUE )
163         {
164             /* We'll update i_size after it's been opened */
165             p_input->stream.b_seekable = 1;
166         }
167 #elif defined( HAVE_SYS_STAT_H )
168         else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode)
169                   || S_ISBLK(stat_info.st_mode) )
170         {
171             p_input->stream.b_seekable = 1;
172             p_input->stream.p_selected_area->i_size = stat_info.st_size;
173         }
174         else if( S_ISFIFO(stat_info.st_mode)
175 #   if !defined( SYS_BEOS ) && !defined( WIN32 )
176                   || S_ISSOCK(stat_info.st_mode)
177 #   endif
178                )
179         {
180             p_input->stream.b_seekable = 0;
181             p_input->stream.p_selected_area->i_size = 0;
182         }
183 #endif
184         else
185         {
186             vlc_mutex_unlock( &p_input->stream.stream_lock );
187             msg_Err( p_input, "unknown file type for `%s'", psz_name );
188             return VLC_EGENERIC;
189         }
190     }
191  
192     p_input->stream.p_selected_area->i_tell = 0;
193     p_input->stream.i_method = INPUT_METHOD_FILE;
194     vlc_mutex_unlock( &p_input->stream.stream_lock );
195  
196     msg_Dbg( p_input, "opening file `%s'", psz_name );
197     p_access_data = malloc( sizeof(_input_socket_t) );
198     p_input->p_access_data = (void *)p_access_data;
199     if( p_access_data == NULL )
200     {
201         msg_Err( p_input, "out of memory" );
202         return VLC_ENOMEM;
203     }
204
205     p_access_data->i_nb_reads = 0;
206     p_access_data->b_kfir = b_kfir;
207     if( b_stdin )
208     {
209         p_access_data->_socket.i_handle = 0;
210     }
211     else
212     {
213 #ifdef UNDER_CE
214         wchar_t psz_filename[MAX_PATH];
215         MultiByteToWideChar( CP_ACP, 0, psz_name, -1, psz_filename, MAX_PATH );
216
217         p_access_data->_socket.i_handle = (int)CreateFile( psz_filename,
218             GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 
219             FILE_ATTRIBUTE_NORMAL, NULL );
220
221         if( (HANDLE)p_access_data->_socket.i_handle == INVALID_HANDLE_VALUE )
222         {
223             msg_Err( p_input, "cannot open file %s", psz_name );
224             free( p_access_data );
225             return VLC_EGENERIC;
226         }
227         p_input->stream.p_selected_area->i_size =
228                 GetFileSize( (HANDLE)p_access_data->_socket.i_handle, NULL );
229 #else
230         p_access_data->_socket.i_handle = open( psz_name,
231                                                 O_NONBLOCK /*| O_LARGEFILE*/ );
232         if( p_access_data->_socket.i_handle == -1 )
233         {
234 #   ifdef HAVE_ERRNO_H
235             msg_Err( p_input, "cannot open file %s (%s)", psz_name,
236                               strerror(errno) );
237 #   else
238             msg_Err( p_input, "cannot open file %s", psz_name );
239 #   endif
240             free( p_access_data );
241             return VLC_EGENERIC;
242         }
243 #endif
244     }
245
246     if ( p_input->stream.b_seekable
247           && !p_input->stream.p_selected_area->i_size )
248     {
249         msg_Err( p_input, "file %s is empty, aborting", psz_name );
250         free( p_access_data );
251         return VLC_EGENERIC;
252     }
253
254     /* Update default_pts to a suitable value for file access */
255     p_input->i_pts_delay = config_GetInt( p_input, "file-caching" ) * 1000;
256
257     return VLC_SUCCESS;
258 }
259
260 /*****************************************************************************
261  * Close: close the target
262  *****************************************************************************/
263 static void Close( vlc_object_t * p_this )
264 {
265     input_thread_t * p_input = (input_thread_t *)p_this;
266     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
267
268     msg_Info( p_input, "closing `%s/%s://%s'", 
269               p_input->psz_access, p_input->psz_demux, p_input->psz_name );
270  
271 #ifdef UNDER_CE
272     CloseHandle( (HANDLE)p_access_data->i_handle );
273 #else
274     close( p_access_data->i_handle );
275 #endif
276
277     free( p_access_data );
278 }
279
280 /*****************************************************************************
281  * Read: standard read on a file descriptor.
282  *****************************************************************************/
283 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
284 {
285     _input_socket_t * p_access_data = (_input_socket_t *)p_input->p_access_data;
286     ssize_t i_ret;
287  
288 #ifdef UNDER_CE
289     if( !ReadFile( (HANDLE)p_access_data->_socket.i_handle, p_buffer, i_len,
290                    (LPDWORD)&i_ret, NULL ) )
291     {
292         i_ret = -1;
293     }
294 #else
295 #ifndef WIN32
296     if ( !p_input->stream.b_pace_control )
297     {
298         if ( !p_access_data->b_kfir )
299         {
300             /* Find if some data is available. This won't work under Windows. */
301             struct timeval  timeout;
302             fd_set          fds;
303
304             /* Initialize file descriptor set */
305             FD_ZERO( &fds );
306             FD_SET( p_access_data->_socket.i_handle, &fds );
307
308             /* We'll wait 0.5 second if nothing happens */
309             timeout.tv_sec = 0;
310             timeout.tv_usec = 500000;
311
312             /* Find if some data is available */
313             while( (i_ret = select( p_access_data->_socket.i_handle + 1, &fds,
314                                     NULL, NULL, &timeout )) == 0
315                     || (i_ret < 0 && errno == EINTR) )
316             {
317                 FD_ZERO( &fds );
318                 FD_SET( p_access_data->_socket.i_handle, &fds );
319                 timeout.tv_sec = 0;
320                 timeout.tv_usec = 500000;
321
322                 if( p_input->b_die || p_input->b_error )
323                 {
324                     return 0;
325                 }
326             }
327
328             if( i_ret < 0 )
329             {
330                 msg_Err( p_input, "select error (%s)", strerror(errno) );
331                 return -1;
332             }
333
334             i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len );
335         }
336         else
337         {
338             /* b_kfir ; work around a buggy poll() driver implementation */
339             while ( (i_ret = read( p_access_data->_socket.i_handle, p_buffer,
340                                    i_len )) == 0 &&
341                       !p_input->b_die && !p_input->b_error )
342             {
343                 msleep(INPUT_ERROR_SLEEP);
344             }
345         }
346     }
347     else
348 #   endif
349     {
350         /* b_pace_control || WIN32 */
351         i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len );
352     }
353 #endif
354
355     if( i_ret < 0 )
356     {
357 #ifdef HAVE_ERRNO_H
358         if ( errno != EINTR && errno != EAGAIN )
359             msg_Err( p_input, "read failed (%s)", strerror(errno) );
360 #else
361         msg_Err( p_input, "read failed" );
362 #endif
363
364         /* Delay a bit to avoid consuming all the CPU. This is particularly
365          * useful when reading from an unconnected FIFO. */
366         msleep( INPUT_ERROR_SLEEP );
367     }
368  
369     p_access_data->i_nb_reads++;
370 #ifdef HAVE_SYS_STAT_H
371     if ( p_input->stream.p_selected_area->i_size != 0
372             && (p_access_data->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
373     {
374         struct stat stat_info;
375         if ( fstat( p_access_data->_socket.i_handle, &stat_info ) == -1 )
376         {
377 #   ifdef HAVE_ERRNO_H
378             msg_Warn( p_input, "couldn't stat again the file (%s)",
379                       strerror(errno) );
380 #   else
381             msg_Warn( p_input, "couldn't stat again the file" );
382 #   endif
383         }
384         else if ( p_input->stream.p_selected_area->i_size != stat_info.st_size )
385         {
386             p_input->stream.p_selected_area->i_size = stat_info.st_size;
387             p_input->stream.b_changed = 1;
388         }
389     }
390 #endif
391
392     return i_ret;
393 }
394
395 /*****************************************************************************
396  * Seek: seek to a specific location in a file
397  *****************************************************************************/
398 static void Seek( input_thread_t * p_input, off_t i_pos )
399 {
400 #define S p_input->stream
401     input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data;
402
403     lseek( p_access_data->i_handle, i_pos, SEEK_SET );
404
405     vlc_mutex_lock( &S.stream_lock );
406     S.p_selected_area->i_tell = i_pos;
407     if( S.p_selected_area->i_tell > S.p_selected_area->i_size )
408     {
409         msg_Err( p_input, "seeking too far" );
410         S.p_selected_area->i_tell = S.p_selected_area->i_size;
411     }
412     else if( S.p_selected_area->i_tell < 0 )
413     {
414         msg_Err( p_input, "seeking too early" );
415         S.p_selected_area->i_tell = 0;
416     }
417     vlc_mutex_unlock( &S.stream_lock );
418 #undef S
419 }
420