Use var_Inherit* when applicable.
[vlc/asuraparaju-public.git] / modules / access / mmap.c
blob2c7dfbfc3544cb931e1eb32af33893f85cfb2c25
1 /*****************************************************************************
2 * mmap.c: memory-mapped file input
3 *****************************************************************************
4 * Copyright © 2007-2008 Rémi Denis-Courmont
5 * $Id$
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_access.h>
29 #include <vlc_input.h>
30 #include <vlc_fs.h>
31 #include <vlc_dialog.h>
33 #include <assert.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
41 #define FILE_MMAP_TEXT N_("Use file memory mapping")
42 #define FILE_MMAP_LONGTEXT N_( \
43 "Try to use memory mapping to read files and block devices." )
45 #ifndef NDEBUG
46 /*# define MMAP_DEBUG 1*/
47 #endif
49 static int Open (vlc_object_t *);
50 static void Close (vlc_object_t *);
52 vlc_module_begin ()
53 set_shortname (N_("MMap"))
54 set_description (N_("Memory-mapped file input"))
55 set_category (CAT_INPUT)
56 set_subcategory (SUBCAT_INPUT_ACCESS)
57 set_capability ("access", 52)
58 add_shortcut ("file")
59 set_callbacks (Open, Close)
60 #ifdef __APPLE__
61 add_bool ("file-mmap", true, NULL,
62 FILE_MMAP_TEXT, FILE_MMAP_LONGTEXT, true)
63 #else
64 add_bool ("file-mmap", false, NULL,
65 FILE_MMAP_TEXT, FILE_MMAP_LONGTEXT, true)
66 #endif
67 vlc_module_end ()
69 static block_t *Block (access_t *);
70 static int Seek (access_t *, uint64_t);
71 static int Control (access_t *, int, va_list);
73 struct access_sys_t
75 size_t page_size;
76 size_t mtu;
77 int fd;
80 #define MMAP_SIZE (1 << 20)
82 static int Open (vlc_object_t *p_this)
84 access_t *p_access = (access_t *)p_this;
85 access_sys_t *p_sys;
86 const char *path = p_access->psz_filepath;
87 int fd;
89 assert ((INT64_C(1) << 63) == ((off_t)(INT64_C(1) << 63)));
91 if (!var_InheritBool (p_this, "file-mmap"))
92 return VLC_EGENERIC; /* disabled */
94 STANDARD_BLOCK_ACCESS_INIT;
96 msg_Dbg (p_access, "opening file %s", path);
97 fd = vlc_open (path, O_RDONLY | O_NOCTTY);
99 if (fd == -1)
101 msg_Warn (p_access, "cannot open %s: %m", path);
102 goto error;
105 /* mmap() is only safe for regular and block special files.
106 * For other types, it may be some idiosyncrasic interface (e.g. packet
107 * sockets are a good example), if it works at all. */
108 struct stat st;
110 if (fstat (fd, &st))
112 msg_Err (p_access, "cannot stat %s: %m", path);
113 goto error;
116 if (!S_ISREG (st.st_mode) && !S_ISBLK (st.st_mode))
118 msg_Dbg (p_access, "skipping non-regular file %s", path);
119 goto error;
122 #if defined(HAVE_FCNTL_H)
123 /* We'd rather use any available memory for reading ahead
124 * than for caching what we've already mmap'ed */
125 # if defined(F_RDAHEAD)
126 fcntl (fd, F_RDAHEAD, 1);
127 # endif
128 # if defined(F_NOCACHE)
129 fcntl (fd, F_NOCACHE, 1);
130 # endif
131 #endif
133 /* Autodetect mmap() support */
134 if (st.st_size > 0)
136 void *addr = mmap (NULL, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
137 if (addr != MAP_FAILED)
138 munmap (addr, 1);
139 else
140 goto error;
143 p_sys->page_size = sysconf (_SC_PAGE_SIZE);
144 p_sys->mtu = MMAP_SIZE;
145 if (p_sys->mtu < p_sys->page_size)
146 p_sys->mtu = p_sys->page_size;
147 p_sys->fd = fd;
149 p_access->info.i_size = st.st_size;
150 #ifdef HAVE_POSIX_FADVISE
151 posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
152 #endif
154 return VLC_SUCCESS;
156 error:
157 if (fd != -1)
158 close (fd);
159 free (p_sys);
160 return VLC_EGENERIC;
164 static void Close (vlc_object_t * p_this)
166 access_t *p_access = (access_t *)p_this;
167 access_sys_t *p_sys = p_access->p_sys;
169 close (p_sys->fd); /* don't care about error when only reading */
170 free (p_sys);
173 static block_t *Block (access_t *p_access)
175 access_sys_t *p_sys = p_access->p_sys;
177 /* Check if file size changed... */
178 struct stat st;
180 if ((fstat (p_sys->fd, &st) == 0)
181 && (st.st_size != p_access->info.i_size))
183 p_access->info.i_size = st.st_size;
184 p_access->info.i_update |= INPUT_UPDATE_SIZE;
187 if (p_access->info.i_pos >= p_access->info.i_size)
189 /* We are at end of file */
190 p_access->info.b_eof = true;
191 msg_Dbg (p_access, "at end of memory mapped file");
192 return NULL;
195 #ifdef MMAP_DEBUG
196 uint64_t dbgpos = lseek (p_sys->fd, 0, SEEK_CUR);
197 if (dbgpos != p_access->info.i_pos)
198 msg_Err (p_access, "position: 0x%016"PRIx64" instead of 0x%016"PRIx64,
199 p_access->info.i_pos, dbgpos);
200 #endif
202 const uintptr_t page_mask = p_sys->page_size - 1;
203 /* Start the mapping on a page boundary: */
204 off_t outer_offset = p_access->info.i_pos & ~(off_t)page_mask;
205 /* Skip useless bytes at the beginning of the first page: */
206 size_t inner_offset = p_access->info.i_pos & page_mask;
207 /* Map no more bytes than remain: */
208 size_t length = p_sys->mtu;
209 if (outer_offset + length > p_access->info.i_size)
210 length = p_access->info.i_size - outer_offset;
212 assert (outer_offset <= p_access->info.i_pos); /* and */
213 assert (p_access->info.i_pos < p_access->info.i_size); /* imply */
214 assert (outer_offset < p_access->info.i_size); /* imply */
215 assert (length > 0);
217 /* NOTE: We use PROT_WRITE and MAP_PRIVATE so that the block can be
218 * modified down the chain, without messing up with the underlying
219 * original file. This does NOT need open write permission. */
220 void *addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE
221 #ifdef MAP_NO_CACHE
222 | MAP_NOCACHE
223 #endif
224 , p_sys->fd, outer_offset);
225 if (addr == MAP_FAILED)
227 msg_Err (p_access, "memory mapping failed (%m)");
228 dialog_Fatal (p_access, _("File reading failed"), "%s",
229 _("VLC could not read the file."));
230 goto fatal;
232 #ifdef HAVE_POSIX_MADVISE
233 posix_madvise (addr, length, POSIX_MADV_SEQUENTIAL);
234 #endif
236 block_t *block = block_mmap_Alloc (addr, length);
237 if (block == NULL)
238 goto fatal;
240 block->p_buffer += inner_offset;
241 block->i_buffer -= inner_offset;
243 #ifdef MMAP_DEBUG
244 msg_Dbg (p_access, "mapped 0x%zx bytes at %p from offset 0x%"PRIx64,
245 length, addr, (uint64_t)outer_offset);
247 /* Compare normal I/O with memory mapping */
248 char *buf = malloc (block->i_buffer);
249 ssize_t i_read = read (p_sys->fd, buf, block->i_buffer);
251 if (i_read != (ssize_t)block->i_buffer)
252 msg_Err (p_access, "read %zd instead of %zu bytes", i_read,
253 block->i_buffer);
254 if (memcmp (buf, block->p_buffer, block->i_buffer))
255 msg_Err (p_access, "inconsistent data buffer");
256 free (buf);
257 #endif
259 p_access->info.i_pos = outer_offset + length;
260 return block;
262 fatal:
263 p_access->info.b_eof = true;
264 return NULL;
268 static int Seek (access_t *p_access, uint64_t i_pos)
270 #ifdef MMAP_DEBUG
271 lseek (p_access->p_sys->fd, i_pos, SEEK_SET);
272 #endif
274 p_access->info.i_pos = i_pos;
275 p_access->info.b_eof = false;
276 return VLC_SUCCESS;
280 static int Control (access_t *p_access, int query, va_list args)
282 switch (query)
284 case ACCESS_CAN_SEEK:
285 case ACCESS_CAN_FASTSEEK:
286 case ACCESS_CAN_PAUSE:
287 case ACCESS_CAN_CONTROL_PACE:
288 *va_arg(args, bool *) = true;
289 return VLC_SUCCESS;
291 case ACCESS_GET_PTS_DELAY:
293 int64_t delay_ms = var_CreateGetInteger (p_access, "file-caching");
294 *va_arg(args, int64_t *) = delay_ms * INT64_C (1000);
295 return VLC_SUCCESS;
298 case ACCESS_GET_TITLE_INFO:
299 case ACCESS_GET_META:
300 break;
302 case ACCESS_SET_PAUSE_STATE:
303 return VLC_SUCCESS;
305 case ACCESS_SET_TITLE:
306 case ACCESS_SET_SEEKPOINT:
307 case ACCESS_SET_PRIVATE_ID_STATE:
308 case ACCESS_SET_PRIVATE_ID_CA:
309 case ACCESS_GET_PRIVATE_ID_STATE:
310 case ACCESS_GET_CONTENT_TYPE:
311 break;
313 default:
314 msg_Warn (p_access, "unimplemented query %d in control", query);
317 return VLC_EGENERIC;