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