Branch data Line data Source code
1 : : /* GStreamer
2 : : * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 : : * 2000,2005 Wim Taymans <wim@fluendo.com>
4 : : *
5 : : * gstfilesrc.c:
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Library General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2 of the License, or (at your option) any later version.
11 : : *
12 : : * This library 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 GNU
15 : : * Library General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Library General Public
18 : : * License along with this library; if not, write to the
19 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 : : * Boston, MA 02111-1307, USA.
21 : : */
22 : : /**
23 : : * SECTION:element-filesrc
24 : : * @see_also: #GstFileSrc
25 : : *
26 : : * Read data from a file in the local file system.
27 : : *
28 : : * <refsect2>
29 : : * <title>Example launch line</title>
30 : : * |[
31 : : * gst-launch filesrc location=song.ogg ! decodebin2 ! autoaudiosink
32 : : * ]| Play a song.ogg from local dir.
33 : : * </refsect2>
34 : : */
35 : :
36 : : #ifdef HAVE_CONFIG_H
37 : : # include "config.h"
38 : : #endif
39 : :
40 : : #include <gst/gst.h>
41 : : #include "gstfilesrc.h"
42 : :
43 : : #include <stdio.h>
44 : : #include <sys/types.h>
45 : : #ifdef G_OS_WIN32
46 : : #include <io.h> /* lseek, open, close, read */
47 : : /* On win32, stat* default to 32 bit; we need the 64-bit
48 : : * variants, so explicitly define it that way. */
49 : : #define stat __stat64
50 : : #define fstat _fstat64
51 : : #undef lseek
52 : : #define lseek _lseeki64
53 : : #undef off_t
54 : : #define off_t guint64
55 : : /* Prevent stat.h from defining the stat* functions as
56 : : * _stat*, since we're explicitly overriding that */
57 : : #undef _INC_STAT_INL
58 : : #endif
59 : : #include <sys/stat.h>
60 : : #include <fcntl.h>
61 : :
62 : : #ifdef HAVE_UNISTD_H
63 : : # include <unistd.h>
64 : : #endif
65 : :
66 : : #ifdef HAVE_MMAP
67 : : # include <sys/mman.h>
68 : : #endif
69 : :
70 : : #include <errno.h>
71 : : #include <string.h>
72 : :
73 : : #include "../../gst/gst-i18n-lib.h"
74 : :
75 : : static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
76 : : GST_PAD_SRC,
77 : : GST_PAD_ALWAYS,
78 : : GST_STATIC_CAPS_ANY);
79 : :
80 : : /* FIXME we should be using glib for this */
81 : : #ifndef S_ISREG
82 : : #define S_ISREG(mode) ((mode)&_S_IFREG)
83 : : #endif
84 : : #ifndef S_ISDIR
85 : : #define S_ISDIR(mode) ((mode)&_S_IFDIR)
86 : : #endif
87 : : #ifndef S_ISSOCK
88 : : #define S_ISSOCK(x) (0)
89 : : #endif
90 : : #ifndef O_BINARY
91 : : #define O_BINARY (0)
92 : : #endif
93 : :
94 : : /* Copy of glib's g_open due to win32 libc/cross-DLL brokenness: we can't
95 : : * use the 'file descriptor' opened in glib (and returned from this function)
96 : : * in this library, as they may have unrelated C runtimes. */
97 : : static int
98 : 4 : gst_open (const gchar * filename, int flags, int mode)
99 : : {
100 : : #ifdef G_OS_WIN32
101 : : wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
102 : : int retval;
103 : : int save_errno;
104 : :
105 : : if (wfilename == NULL) {
106 : : errno = EINVAL;
107 : : return -1;
108 : : }
109 : :
110 : : retval = _wopen (wfilename, flags, mode);
111 : : save_errno = errno;
112 : :
113 : : g_free (wfilename);
114 : :
115 : : errno = save_errno;
116 : : return retval;
117 : : #else
118 : 4 : return open (filename, flags, mode);
119 : : #endif
120 : : }
121 : :
122 : :
123 : : /**********************************************************************
124 : : * GStreamer Default File Source
125 : : * Theory of Operation
126 : : *
127 : : * Update: see GstFileSrc:use-mmap property documentation below
128 : : * for why use of mmap() is disabled by default.
129 : : *
130 : : * This source uses mmap(2) to efficiently load data from a file.
131 : : * To do this without seriously polluting the applications' memory
132 : : * space, it must do so in smaller chunks, say 1-4MB at a time.
133 : : * Buffers are then subdivided from these mmap'd chunks, to directly
134 : : * make use of the mmap.
135 : : *
136 : : * To handle refcounting so that the mmap can be freed at the appropriate
137 : : * time, a buffer will be created for each mmap'd region, and all new
138 : : * buffers will be sub-buffers of this top-level buffer. As they are
139 : : * freed, the refcount goes down on the mmap'd buffer and its free()
140 : : * function is called, which will call munmap(2) on itself.
141 : : *
142 : : * If a buffer happens to cross the boundaries of an mmap'd region, we
143 : : * have to decide whether it's more efficient to copy the data into a
144 : : * new buffer, or mmap() just that buffer. There will have to be a
145 : : * breakpoint size to determine which will be done. The mmap() size
146 : : * has a lot to do with this as well, because you end up in double-
147 : : * jeopardy: the larger the outgoing buffer, the more data to copy when
148 : : * it overlaps, *and* the more frequently you'll have buffers that *do*
149 : : * overlap.
150 : : *
151 : : * Seeking is another tricky aspect to do efficiently. The initial
152 : : * implementation of this source won't make use of these features, however.
153 : : * The issue is that if an application seeks backwards in a file, *and*
154 : : * that region of the file is covered by an mmap that hasn't been fully
155 : : * deallocated, we really should re-use it. But keeping track of these
156 : : * regions is tricky because we have to lock the structure that holds
157 : : * them. We need to settle on a locking primitive (GMutex seems to be
158 : : * a really good option...), then we can do that.
159 : : */
160 : :
161 : :
162 : : GST_DEBUG_CATEGORY_STATIC (gst_file_src_debug);
163 : : #define GST_CAT_DEFAULT gst_file_src_debug
164 : :
165 : : /* FileSrc signals and args */
166 : : enum
167 : : {
168 : : /* FILL ME */
169 : : LAST_SIGNAL
170 : : };
171 : :
172 : : #define DEFAULT_BLOCKSIZE 4*1024
173 : : #define DEFAULT_MMAPSIZE 4*1024*1024
174 : : #define DEFAULT_TOUCH TRUE
175 : : #define DEFAULT_USEMMAP FALSE
176 : : #define DEFAULT_SEQUENTIAL FALSE
177 : :
178 : : enum
179 : : {
180 : : ARG_0,
181 : : ARG_LOCATION,
182 : : ARG_FD,
183 : : ARG_MMAPSIZE,
184 : : ARG_SEQUENTIAL,
185 : : ARG_TOUCH,
186 : : ARG_USEMMAP
187 : : };
188 : :
189 : : static void gst_file_src_finalize (GObject * object);
190 : :
191 : : static void gst_file_src_set_property (GObject * object, guint prop_id,
192 : : const GValue * value, GParamSpec * pspec);
193 : : static void gst_file_src_get_property (GObject * object, guint prop_id,
194 : : GValue * value, GParamSpec * pspec);
195 : :
196 : : static gboolean gst_file_src_start (GstBaseSrc * basesrc);
197 : : static gboolean gst_file_src_stop (GstBaseSrc * basesrc);
198 : :
199 : : static gboolean gst_file_src_is_seekable (GstBaseSrc * src);
200 : : static gboolean gst_file_src_get_size (GstBaseSrc * src, guint64 * size);
201 : : static GstFlowReturn gst_file_src_create (GstBaseSrc * src, guint64 offset,
202 : : guint length, GstBuffer ** buffer);
203 : : static gboolean gst_file_src_query (GstBaseSrc * src, GstQuery * query);
204 : :
205 : : static void gst_file_src_uri_handler_init (gpointer g_iface,
206 : : gpointer iface_data);
207 : :
208 : : static void
209 : 153 : _do_init (GType filesrc_type)
210 : : {
211 : : static const GInterfaceInfo urihandler_info = {
212 : : gst_file_src_uri_handler_init,
213 : : NULL,
214 : : NULL
215 : : };
216 : :
217 : 153 : g_type_add_interface_static (filesrc_type, GST_TYPE_URI_HANDLER,
218 : : &urihandler_info);
219 [ + - ]: 153 : GST_DEBUG_CATEGORY_INIT (gst_file_src_debug, "filesrc", 0, "filesrc element");
220 : 153 : }
221 : :
222 [ + + ]: 330 : GST_BOILERPLATE_FULL (GstFileSrc, gst_file_src, GstBaseSrc, GST_TYPE_BASE_SRC,
223 : 330 : _do_init);
224 : :
225 : : static void
226 : 21 : gst_file_src_base_init (gpointer g_class)
227 : : {
228 : 21 : GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
229 : :
230 : 21 : gst_element_class_set_details_simple (gstelement_class,
231 : : "File Source",
232 : : "Source/File",
233 : : "Read from arbitrary point in a file",
234 : : "Erik Walthinsen <omega@cse.ogi.edu>");
235 : 21 : gst_element_class_add_pad_template (gstelement_class,
236 : : gst_static_pad_template_get (&srctemplate));
237 : 21 : }
238 : :
239 : : static void
240 : 21 : gst_file_src_class_init (GstFileSrcClass * klass)
241 : : {
242 : : GObjectClass *gobject_class;
243 : : GstBaseSrcClass *gstbasesrc_class;
244 : :
245 : 21 : gobject_class = G_OBJECT_CLASS (klass);
246 : 21 : gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
247 : :
248 : 21 : gobject_class->set_property = gst_file_src_set_property;
249 : 21 : gobject_class->get_property = gst_file_src_get_property;
250 : :
251 : 21 : g_object_class_install_property (gobject_class, ARG_FD,
252 : : g_param_spec_int ("fd", "File-descriptor",
253 : : "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0,
254 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
255 : 21 : g_object_class_install_property (gobject_class, ARG_LOCATION,
256 : : g_param_spec_string ("location", "File Location",
257 : : "Location of the file to read", NULL,
258 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
259 : : GST_PARAM_MUTABLE_READY));
260 : 21 : g_object_class_install_property (gobject_class, ARG_MMAPSIZE,
261 : : g_param_spec_ulong ("mmapsize", "mmap() Block Size",
262 : : "Size in bytes of mmap()d regions", 0, G_MAXULONG, DEFAULT_MMAPSIZE,
263 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
264 : : GST_PARAM_MUTABLE_PLAYING));
265 : 21 : g_object_class_install_property (gobject_class, ARG_TOUCH,
266 : : g_param_spec_boolean ("touch", "Touch mapped region read data",
267 : : "Touch mmapped data regions to force them to be read from disk",
268 : : DEFAULT_TOUCH,
269 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
270 : : GST_PARAM_MUTABLE_PLAYING));
271 : : /**
272 : : * GstFileSrc:use-mmap
273 : : *
274 : : * Whether to use mmap(). Set to TRUE to force use of mmap() instead of
275 : : * read() for reading data.
276 : : *
277 : : * Use of mmap() is disabled by default since with mmap() there are a
278 : : * number of occasions where the process/application will be notified of
279 : : * read errors via a SIGBUS signal from the kernel, which will lead to
280 : : * the application being killed if not handled by the application. This
281 : : * is something that is difficult to work around for a library like
282 : : * GStreamer, hence use of mmap() is disabled by default. Said errors
283 : : * can occur for example when an external device (e.g. an external hard
284 : : * drive or a portable music player) are unplugged while in use, or when
285 : : * a CD/DVD medium cannot be be read because the medium is scratched or
286 : : * otherwise damaged.
287 : : *
288 : : **/
289 : 21 : g_object_class_install_property (gobject_class, ARG_USEMMAP,
290 : : g_param_spec_boolean ("use-mmap", "Use mmap to read data",
291 : : "Whether to use mmap() instead of read()",
292 : : DEFAULT_USEMMAP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
293 : : GST_PARAM_MUTABLE_READY));
294 : 21 : g_object_class_install_property (gobject_class, ARG_SEQUENTIAL,
295 : : g_param_spec_boolean ("sequential", "Optimise for sequential mmap access",
296 : : "Whether to use madvise to hint to the kernel that access to "
297 : : "mmap pages will be sequential",
298 : : DEFAULT_SEQUENTIAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
299 : : GST_PARAM_MUTABLE_PLAYING));
300 : :
301 : 21 : gobject_class->finalize = gst_file_src_finalize;
302 : :
303 : 21 : gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_file_src_start);
304 : 21 : gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_file_src_stop);
305 : 21 : gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_file_src_is_seekable);
306 : 21 : gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_file_src_get_size);
307 : 21 : gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_file_src_create);
308 : 21 : gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_file_src_query);
309 : :
310 : : if (sizeof (off_t) < 8) {
311 : : GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT "!",
312 : : sizeof (off_t));
313 : : }
314 : 21 : }
315 : :
316 : : static void
317 : 23 : gst_file_src_init (GstFileSrc * src, GstFileSrcClass * g_class)
318 : : {
319 : : #ifdef HAVE_MMAP
320 : 23 : src->pagesize = getpagesize ();
321 : : #endif
322 : :
323 : 23 : src->filename = NULL;
324 : 23 : src->fd = 0;
325 : 23 : src->uri = NULL;
326 : :
327 : 23 : src->touch = DEFAULT_TOUCH;
328 : :
329 : 23 : src->mapbuf = NULL;
330 : 23 : src->mapsize = DEFAULT_MMAPSIZE; /* default is 4MB */
331 : 23 : src->use_mmap = DEFAULT_USEMMAP;
332 : 23 : src->sequential = DEFAULT_SEQUENTIAL;
333 : :
334 : 23 : src->is_regular = FALSE;
335 : 23 : }
336 : :
337 : : static void
338 : 23 : gst_file_src_finalize (GObject * object)
339 : : {
340 : : GstFileSrc *src;
341 : :
342 : 23 : src = GST_FILE_SRC (object);
343 : :
344 : 23 : g_free (src->filename);
345 : 23 : g_free (src->uri);
346 : :
347 : 23 : G_OBJECT_CLASS (parent_class)->finalize (object);
348 : 23 : }
349 : :
350 : : static gboolean
351 : 32 : gst_file_src_set_location (GstFileSrc * src, const gchar * location)
352 : : {
353 : : GstState state;
354 : :
355 : : /* the element must be stopped in order to do this */
356 : 32 : GST_OBJECT_LOCK (src);
357 : 32 : state = GST_STATE (src);
358 [ + + ][ - + ]: 32 : if (state != GST_STATE_READY && state != GST_STATE_NULL)
359 : 0 : goto wrong_state;
360 : 32 : GST_OBJECT_UNLOCK (src);
361 : :
362 : 32 : g_free (src->filename);
363 : 32 : g_free (src->uri);
364 : :
365 : : /* clear the filename if we get a NULL (is that possible?) */
366 [ + + ]: 32 : if (location == NULL) {
367 : 1 : src->filename = NULL;
368 : 1 : src->uri = NULL;
369 : : } else {
370 : : /* we store the filename as received by the application. On Windows this
371 : : * should be UTF8 */
372 : 31 : src->filename = g_strdup (location);
373 : 31 : src->uri = gst_filename_to_uri (location, NULL);
374 [ - + ]: 31 : GST_INFO ("filename : %s", src->filename);
375 [ - + ]: 31 : GST_INFO ("uri : %s", src->uri);
376 : : }
377 : 32 : g_object_notify (G_OBJECT (src), "location");
378 : 32 : gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri);
379 : :
380 : 32 : return TRUE;
381 : :
382 : : /* ERROR */
383 : : wrong_state:
384 : : {
385 : 0 : g_warning ("Changing the `location' property on filesrc when a file is "
386 : : "open is not supported.");
387 : 0 : GST_OBJECT_UNLOCK (src);
388 : 32 : return FALSE;
389 : : }
390 : : }
391 : :
392 : : static void
393 : 27 : gst_file_src_set_property (GObject * object, guint prop_id,
394 : : const GValue * value, GParamSpec * pspec)
395 : : {
396 : : GstFileSrc *src;
397 : :
398 [ - + ][ + - ]: 54 : g_return_if_fail (GST_IS_FILE_SRC (object));
[ + - ][ - + ]
399 : :
400 : 27 : src = GST_FILE_SRC (object);
401 : :
402 [ + - - - : 27 : switch (prop_id) {
- - ]
403 : : case ARG_LOCATION:
404 : 27 : gst_file_src_set_location (src, g_value_get_string (value));
405 : 27 : break;
406 : : case ARG_MMAPSIZE:
407 [ # # ]: 0 : if ((src->mapsize % src->pagesize) == 0) {
408 : 0 : src->mapsize = g_value_get_ulong (value);
409 : : } else {
410 [ # # ]: 0 : GST_INFO_OBJECT (src,
411 : : "invalid mapsize, must be a multiple of pagesize, which is %d",
412 : : src->pagesize);
413 : : }
414 : 0 : break;
415 : : case ARG_TOUCH:
416 : 0 : src->touch = g_value_get_boolean (value);
417 : 0 : break;
418 : : case ARG_SEQUENTIAL:
419 : 0 : src->sequential = g_value_get_boolean (value);
420 : 0 : break;
421 : : case ARG_USEMMAP:
422 : 0 : src->use_mmap = g_value_get_boolean (value);
423 : 0 : break;
424 : : default:
425 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
426 : 0 : break;
427 : : }
428 : : }
429 : :
430 : : static void
431 : 6 : gst_file_src_get_property (GObject * object, guint prop_id, GValue * value,
432 : : GParamSpec * pspec)
433 : : {
434 : : GstFileSrc *src;
435 : :
436 [ - + ][ + - ]: 12 : g_return_if_fail (GST_IS_FILE_SRC (object));
[ + - ][ - + ]
437 : :
438 : 6 : src = GST_FILE_SRC (object);
439 : :
440 [ + - - - : 6 : switch (prop_id) {
- - - ]
441 : : case ARG_LOCATION:
442 : 6 : g_value_set_string (value, src->filename);
443 : 6 : break;
444 : : case ARG_FD:
445 : 0 : g_value_set_int (value, src->fd);
446 : 0 : break;
447 : : case ARG_MMAPSIZE:
448 : 0 : g_value_set_ulong (value, src->mapsize);
449 : 0 : break;
450 : : case ARG_TOUCH:
451 : 0 : g_value_set_boolean (value, src->touch);
452 : 0 : break;
453 : : case ARG_SEQUENTIAL:
454 : 0 : g_value_set_boolean (value, src->sequential);
455 : 0 : break;
456 : : case ARG_USEMMAP:
457 : 0 : g_value_set_boolean (value, src->use_mmap);
458 : 0 : break;
459 : : default:
460 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
461 : 0 : break;
462 : : }
463 : : }
464 : :
465 : : /***
466 : : * mmap code below
467 : : */
468 : :
469 : : #ifdef HAVE_MMAP
470 : :
471 : : /* GstMmapBuffer */
472 : :
473 : : typedef struct _GstMmapBuffer GstMmapBuffer;
474 : : typedef struct _GstMmapBufferClass GstMmapBufferClass;
475 : :
476 : : #define GST_TYPE_MMAP_BUFFER (gst_mmap_buffer_get_type())
477 : :
478 : : #define GST_IS_MMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MMAP_BUFFER))
479 : : #define GST_IS_MMAP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MMAP_BUFFER))
480 : : #define GST_MMAP_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass))
481 : : #define GST_MMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBuffer))
482 : : #define GST_MMAP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass))
483 : :
484 : :
485 : :
486 : : struct _GstMmapBuffer
487 : : {
488 : : GstBuffer buffer;
489 : :
490 : : GstFileSrc *filesrc;
491 : : };
492 : :
493 : : struct _GstMmapBufferClass
494 : : {
495 : : GstBufferClass buffer_class;
496 : : };
497 : :
498 : : static void gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer);
499 : :
500 : : GType gst_mmap_buffer_get_type (void);
501 : :
502 [ # # ]: 0 : G_DEFINE_TYPE (GstMmapBuffer, gst_mmap_buffer, GST_TYPE_BUFFER);
503 : :
504 : : static void
505 : 0 : gst_mmap_buffer_class_init (GstMmapBufferClass * g_class)
506 : : {
507 : 0 : GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
508 : :
509 : 0 : mini_object_class->finalize =
510 : : (GstMiniObjectFinalizeFunction) gst_mmap_buffer_finalize;
511 : 0 : }
512 : :
513 : : static void
514 : 0 : gst_mmap_buffer_init (GstMmapBuffer * buf)
515 : : {
516 : 0 : GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
517 : : /* before we re-enable this flag, we probably need to fix _copy()
518 : : * _make_writable(), etc. in GstMiniObject/GstBuffer as well */
519 : : /* GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_ORIGINAL); */
520 : 0 : }
521 : :
522 : : static void
523 : 0 : gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer)
524 : : {
525 : : guint size;
526 : : gpointer data;
527 : : guint64 offset;
528 : : GstFileSrc *src;
529 : 0 : GstBuffer *buffer = GST_BUFFER (mmap_buffer);
530 : :
531 : : /* get info */
532 : 0 : size = GST_BUFFER_SIZE (buffer);
533 : 0 : offset = GST_BUFFER_OFFSET (buffer);
534 : 0 : data = GST_BUFFER_DATA (buffer);
535 : 0 : src = mmap_buffer->filesrc;
536 : :
537 [ # # ]: 0 : GST_LOG ("freeing mmap()d buffer at %" G_GUINT64_FORMAT "+%u", offset, size);
538 : :
539 : : #ifdef MADV_DONTNEED
540 : : /* madvise to tell the kernel what to do with it */
541 [ # # ]: 0 : if (madvise (data, size, MADV_DONTNEED) < 0) {
542 [ # # ]: 0 : GST_WARNING_OBJECT (src, "warning: madvise failed: %s", g_strerror (errno));
543 : : }
544 : : #endif
545 : :
546 : : /* now unmap the memory */
547 [ # # ]: 0 : if (munmap (data, size) < 0) {
548 [ # # ]: 0 : GST_WARNING_OBJECT (src, "warning: munmap failed: %s", g_strerror (errno));
549 : : }
550 : :
551 : : /* cast to unsigned long, since there's no gportable way to print
552 : : * guint64 as hex */
553 [ # # ]: 0 : GST_LOG ("unmapped region %08lx+%08lx at %p",
554 : : (gulong) offset, (gulong) size, data);
555 : :
556 : 0 : GST_MINI_OBJECT_CLASS (gst_mmap_buffer_parent_class)->finalize
557 : 0 : (GST_MINI_OBJECT (mmap_buffer));
558 : 0 : }
559 : :
560 : : static GstBuffer *
561 : 0 : gst_file_src_map_region (GstFileSrc * src, off_t offset, gsize size,
562 : : gboolean testonly)
563 : : {
564 : : GstBuffer *buf;
565 : : void *mmapregion;
566 : :
567 [ # # ]: 0 : g_return_val_if_fail (offset >= 0, NULL);
568 : :
569 : : /* FIXME ? use goffset and friends if we require glib >= 2.20 */
570 [ # # ]: 0 : GST_LOG_OBJECT (src, "mapping region %08" G_GINT64_MODIFIER "x+%08lx "
571 : : "from file into memory", (gint64) offset, (gulong) size);
572 : :
573 : 0 : mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset);
574 : :
575 [ # # ][ # # ]: 0 : if (mmapregion == NULL || mmapregion == MAP_FAILED)
576 : : goto mmap_failed;
577 : :
578 [ # # ]: 0 : GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p",
579 : : (gulong) offset, (gulong) size, mmapregion);
580 : :
581 : : /* time to allocate a new mapbuf */
582 : 0 : buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_MMAP_BUFFER);
583 : : /* mmap() the data into this new buffer */
584 : 0 : GST_BUFFER_DATA (buf) = mmapregion;
585 : 0 : GST_MMAP_BUFFER (buf)->filesrc = src;
586 : :
587 : : #ifdef MADV_SEQUENTIAL
588 [ # # ]: 0 : if (src->sequential) {
589 : : /* madvise to tell the kernel what to do with it */
590 [ # # ]: 0 : if (madvise (mmapregion, size, MADV_SEQUENTIAL) < 0) {
591 [ # # ]: 0 : GST_WARNING_OBJECT (src, "warning: madvise failed: %s",
592 : : g_strerror (errno));
593 : : }
594 : : }
595 : : #endif
596 : :
597 : : /* fill in the rest of the fields */
598 : 0 : GST_BUFFER_SIZE (buf) = size;
599 : 0 : GST_BUFFER_OFFSET (buf) = offset;
600 : 0 : GST_BUFFER_OFFSET_END (buf) = offset + size;
601 : 0 : GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
602 : :
603 : 0 : return buf;
604 : :
605 : : /* ERROR */
606 : : mmap_failed:
607 : : {
608 [ # # ]: 0 : if (!testonly) {
609 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
[ # # ][ # # ]
610 : : ("mmap (0x%08lx, %d, 0x%" G_GINT64_MODIFIER "x) failed: %s",
611 : : (gulong) size, src->fd, (guint64) offset, g_strerror (errno)));
612 : : }
613 : 0 : return NULL;
614 : : }
615 : : }
616 : :
617 : : static GstBuffer *
618 : 0 : gst_file_src_map_small_region (GstFileSrc * src, off_t offset, gsize size)
619 : : {
620 : : GstBuffer *ret;
621 : : off_t mod;
622 : : guint pagesize;
623 : :
624 [ # # ]: 0 : GST_LOG_OBJECT (src,
625 : : "attempting to map a small buffer at %" G_GUINT64_FORMAT "+%d",
626 : : (guint64) offset, (gint) size);
627 : :
628 : 0 : pagesize = src->pagesize;
629 : :
630 : 0 : mod = offset % pagesize;
631 : :
632 : : /* if the offset starts at a non-page boundary, we have to special case */
633 [ # # ]: 0 : if (mod != 0) {
634 : : gsize mapsize;
635 : : off_t mapbase;
636 : : GstBuffer *map;
637 : :
638 : 0 : mapbase = offset - mod;
639 : 0 : mapsize = ((size + mod + pagesize - 1) / pagesize) * pagesize;
640 : :
641 [ # # ]: 0 : GST_LOG_OBJECT (src,
642 : : "not on page boundaries, resizing to map to %" G_GUINT64_FORMAT "+%d",
643 : : (guint64) mapbase, (gint) mapsize);
644 : :
645 : 0 : map = gst_file_src_map_region (src, mapbase, mapsize, FALSE);
646 [ # # ]: 0 : if (map == NULL)
647 : 0 : return NULL;
648 : :
649 : 0 : ret = gst_buffer_create_sub (map, offset - mapbase, size);
650 : 0 : GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET (map) + offset - mapbase;
651 : :
652 : 0 : gst_buffer_unref (map);
653 : : } else {
654 : 0 : ret = gst_file_src_map_region (src, offset, size, FALSE);
655 : : }
656 : :
657 : 0 : return ret;
658 : : }
659 : :
660 : : static GstFlowReturn
661 : 0 : gst_file_src_create_mmap (GstFileSrc * src, guint64 offset, guint length,
662 : : GstBuffer ** buffer)
663 : : {
664 : 0 : GstBuffer *buf = NULL;
665 : : gsize readsize, mapsize;
666 : : off_t readend, mapstart, mapend;
667 : : int i;
668 : :
669 : : /* calculate end pointers so we don't have to do so repeatedly later */
670 : 0 : readsize = length;
671 : 0 : readend = offset + readsize; /* note this is the byte *after* the read */
672 : :
673 : 0 : mapstart = GST_BUFFER_OFFSET (src->mapbuf);
674 : 0 : mapsize = GST_BUFFER_SIZE (src->mapbuf);
675 : 0 : mapend = mapstart + mapsize; /* note this is the byte *after* the map */
676 : :
677 [ # # ]: 0 : GST_LOG ("attempting to read %08lx, %08lx, %08lx, %08lx",
678 : : (unsigned long) readsize, (unsigned long) readend,
679 : : (unsigned long) mapstart, (unsigned long) mapend);
680 : :
681 : : /* if the start is past the mapstart */
682 [ # # ]: 0 : if (offset >= mapstart) {
683 : : /* if the end is before the mapend, the buffer is in current mmap region... */
684 : : /* ('cause by definition if readend is in the buffer, so's readstart) */
685 [ # # ]: 0 : if (readend <= mapend) {
686 [ # # ]: 0 : GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u lives in "
687 : : "current mapbuf %u+%u, creating subbuffer of mapbuf",
688 : : offset, (guint) readsize, (guint) mapstart, (guint) mapsize);
689 : 0 : buf = gst_buffer_create_sub (src->mapbuf, offset - mapstart, readsize);
690 : 0 : GST_BUFFER_OFFSET (buf) = offset;
691 : :
692 : : /* if the start actually is within the current mmap region, map an overlap buffer */
693 [ # # ]: 0 : } else if (offset < mapend) {
694 [ # # ]: 0 : GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u starts in "
695 : : "mapbuf %u+%u but ends outside, creating new mmap",
696 : : offset, (guint) readsize, (guint) mapstart, (guint) mapsize);
697 : 0 : buf = gst_file_src_map_small_region (src, offset, readsize);
698 [ # # ]: 0 : if (buf == NULL)
699 : 0 : goto could_not_mmap;
700 : : }
701 : :
702 : : /* the only other option is that buffer is totally outside, which means we search for it */
703 : :
704 : : /* now we can assume that the start is *before* the current mmap region */
705 : : /* if the readend is past mapstart, we have two options */
706 [ # # ]: 0 : } else if (readend >= mapstart) {
707 : : /* either the read buffer overlaps the start of the mmap region */
708 : : /* or the read buffer fully contains the current mmap region */
709 : : /* either way, it's really not relevant, we just create a new region anyway */
710 [ # # ]: 0 : GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d starts before "
711 : : "mapbuf %d+%d, but overlaps it", (guint64) offset, (gint) readsize,
712 : : (gint) mapstart, (gint) mapsize);
713 : 0 : buf = gst_file_src_map_small_region (src, offset, readsize);
714 [ # # ]: 0 : if (buf == NULL)
715 : 0 : goto could_not_mmap;
716 : : }
717 : :
718 : : /* then deal with the case where the read buffer is totally outside */
719 [ # # ]: 0 : if (buf == NULL) {
720 : : /* first check to see if there's a map that covers the right region already */
721 [ # # ]: 0 : GST_LOG_OBJECT (src, "searching for mapbuf to cover %" G_GUINT64_FORMAT
722 : : "+%d", offset, (int) readsize);
723 : :
724 : : /* if the read buffer crosses a mmap region boundary, create a one-off region */
725 [ # # ]: 0 : if ((offset / src->mapsize) != (readend / src->mapsize)) {
726 [ # # ]: 0 : GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d crosses a "
727 : : "%d-byte boundary, creating a one-off", offset, (int) readsize,
728 : : (int) src->mapsize);
729 : 0 : buf = gst_file_src_map_small_region (src, offset, readsize);
730 [ # # ]: 0 : if (buf == NULL)
731 : 0 : goto could_not_mmap;
732 : :
733 : : /* otherwise we will create a new mmap region and set it to the default */
734 : : } else {
735 : : gsize mapsize;
736 : :
737 : 0 : off_t nextmap = offset - (offset % src->mapsize);
738 : :
739 [ # # ]: 0 : GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d in new mapbuf "
740 : : "at %" G_GUINT64_FORMAT "+%d, mapping and subbuffering",
741 : : offset, (gint) readsize, (guint64) nextmap, (gint) src->mapsize);
742 : : /* first, we're done with the old mapbuf */
743 : 0 : gst_buffer_unref (src->mapbuf);
744 : 0 : mapsize = src->mapsize;
745 : :
746 : : /* double the mapsize as long as the readsize is smaller */
747 [ # # ]: 0 : while (readsize + offset > nextmap + mapsize) {
748 [ # # ]: 0 : GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d",
749 : : (guint) readsize, (gint) mapsize);
750 : 0 : mapsize <<= 1;
751 : : }
752 : : /* create a new one */
753 : 0 : src->mapbuf = gst_file_src_map_region (src, nextmap, mapsize, FALSE);
754 [ # # ]: 0 : if (src->mapbuf == NULL)
755 : 0 : goto could_not_mmap;
756 : :
757 : : /* subbuffer it */
758 : 0 : buf = gst_buffer_create_sub (src->mapbuf, offset - nextmap, readsize);
759 : 0 : GST_BUFFER_OFFSET (buf) =
760 : 0 : GST_BUFFER_OFFSET (src->mapbuf) + offset - nextmap;
761 : : }
762 : : }
763 : :
764 : : /* if we need to touch the buffer (to bring it into memory), do so */
765 [ # # ]: 0 : if (src->touch) {
766 : 0 : volatile guchar *p = GST_BUFFER_DATA (buf);
767 : :
768 : : /* read first byte of each page */
769 [ # # ]: 0 : for (i = 0; i < GST_BUFFER_SIZE (buf); i += src->pagesize)
770 : 0 : (void) p[i];
771 : : }
772 : :
773 : : /* we're done, return the buffer */
774 : 0 : *buffer = buf;
775 : :
776 : 0 : return GST_FLOW_OK;
777 : :
778 : : /* ERROR */
779 : : could_not_mmap:
780 : : {
781 : 0 : return GST_FLOW_ERROR;
782 : : }
783 : : }
784 : : #endif
785 : :
786 : : /***
787 : : * read code below
788 : : * that is to say, you shouldn't read the code below, but the code that reads
789 : : * stuff is below. Well, you shouldn't not read the code below, feel free
790 : : * to read it of course. It's just that "read code below" is a pretty crappy
791 : : * documentation string because it sounds like we're expecting you to read
792 : : * the code to understand what it does, which, while true, is really not
793 : : * the sort of attitude we want to be advertising. No sir.
794 : : *
795 : : */
796 : :
797 : : static GstFlowReturn
798 : 15 : gst_file_src_create_read (GstFileSrc * src, guint64 offset, guint length,
799 : : GstBuffer ** buffer)
800 : : {
801 : : int ret;
802 : : GstBuffer *buf;
803 : :
804 [ + + ]: 15 : if (G_UNLIKELY (src->read_position != offset)) {
805 : : off_t res;
806 : :
807 : 12 : res = lseek (src->fd, offset, SEEK_SET);
808 [ + - ][ + - ]: 12 : if (G_UNLIKELY (res < 0 || res != offset))
809 : : goto seek_failed;
810 : :
811 : 12 : src->read_position = offset;
812 : : }
813 : :
814 : 15 : buf = gst_buffer_try_new_and_alloc (length);
815 [ - + ][ # # ]: 15 : if (G_UNLIKELY (buf == NULL && length > 0)) {
816 [ # # ]: 0 : GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", length);
817 : 0 : return GST_FLOW_ERROR;
818 : : }
819 : :
820 : : /* No need to read anything if length is 0 */
821 [ + + ]: 15 : if (length > 0) {
822 [ - + ]: 14 : GST_LOG_OBJECT (src, "Reading %d bytes at offset 0x%" G_GINT64_MODIFIER "x",
823 : : length, offset);
824 : 14 : ret = read (src->fd, GST_BUFFER_DATA (buf), length);
825 [ - + ]: 14 : if (G_UNLIKELY (ret < 0))
826 : 0 : goto could_not_read;
827 : :
828 : : /* seekable regular files should have given us what we expected */
829 [ - + ][ # # ]: 14 : if (G_UNLIKELY ((guint) ret < length && src->seekable))
830 : 0 : goto unexpected_eos;
831 : :
832 : : /* other files should eos if they read 0 and more was requested */
833 [ - + ][ # # ]: 14 : if (G_UNLIKELY (ret == 0 && length > 0))
834 : 0 : goto eos;
835 : :
836 : 14 : length = ret;
837 : 14 : GST_BUFFER_SIZE (buf) = length;
838 : 14 : GST_BUFFER_OFFSET (buf) = offset;
839 : 14 : GST_BUFFER_OFFSET_END (buf) = offset + length;
840 : :
841 : 14 : src->read_position += length;
842 : : }
843 : :
844 : 15 : *buffer = buf;
845 : :
846 : 15 : return GST_FLOW_OK;
847 : :
848 : : /* ERROR */
849 : : seek_failed:
850 : : {
851 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
[ # # ][ # # ]
852 : 0 : return GST_FLOW_ERROR;
853 : : }
854 : : could_not_read:
855 : : {
856 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
[ # # ][ # # ]
857 : 0 : gst_buffer_unref (buf);
858 : 0 : return GST_FLOW_ERROR;
859 : : }
860 : : unexpected_eos:
861 : : {
862 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
[ # # ][ # # ]
863 : : ("unexpected end of file."));
864 : 0 : gst_buffer_unref (buf);
865 : 0 : return GST_FLOW_ERROR;
866 : : }
867 : : eos:
868 : : {
869 [ # # ]: 0 : GST_DEBUG ("non-regular file hits EOS");
870 : 0 : gst_buffer_unref (buf);
871 : 15 : return GST_FLOW_UNEXPECTED;
872 : : }
873 : : }
874 : :
875 : : static GstFlowReturn
876 : 15 : gst_file_src_create (GstBaseSrc * basesrc, guint64 offset, guint length,
877 : : GstBuffer ** buffer)
878 : : {
879 : : GstFileSrc *src;
880 : : GstFlowReturn ret;
881 : :
882 : 15 : src = GST_FILE_SRC_CAST (basesrc);
883 : :
884 : : #ifdef HAVE_MMAP
885 [ - + ]: 15 : if (src->using_mmap) {
886 : 0 : ret = gst_file_src_create_mmap (src, offset, length, buffer);
887 : : } else {
888 : 15 : ret = gst_file_src_create_read (src, offset, length, buffer);
889 : : }
890 : : #else
891 : : ret = gst_file_src_create_read (src, offset, length, buffer);
892 : : #endif
893 : :
894 : 15 : return ret;
895 : : }
896 : :
897 : : static gboolean
898 : 16 : gst_file_src_query (GstBaseSrc * basesrc, GstQuery * query)
899 : : {
900 : 16 : gboolean ret = FALSE;
901 : 16 : GstFileSrc *src = GST_FILE_SRC (basesrc);
902 : :
903 [ + + ]: 16 : switch (GST_QUERY_TYPE (query)) {
904 : : case GST_QUERY_URI:
905 : 14 : gst_query_set_uri (query, src->uri);
906 : 14 : ret = TRUE;
907 : 14 : break;
908 : : default:
909 : 2 : ret = FALSE;
910 : 2 : break;
911 : : }
912 : :
913 [ + + ]: 16 : if (!ret)
914 : 2 : ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
915 : :
916 : 16 : return ret;
917 : : }
918 : :
919 : : static gboolean
920 : 5 : gst_file_src_is_seekable (GstBaseSrc * basesrc)
921 : : {
922 : 5 : GstFileSrc *src = GST_FILE_SRC (basesrc);
923 : :
924 : 5 : return src->seekable;
925 : : }
926 : :
927 : : static gboolean
928 : 11 : gst_file_src_get_size (GstBaseSrc * basesrc, guint64 * size)
929 : : {
930 : : struct stat stat_results;
931 : : GstFileSrc *src;
932 : :
933 : 11 : src = GST_FILE_SRC (basesrc);
934 : :
935 [ - + ]: 11 : if (!src->seekable) {
936 : : /* If it isn't seekable, we won't know the length (but fstat will still
937 : : * succeed, and wrongly say our length is zero. */
938 : 0 : return FALSE;
939 : : }
940 : :
941 [ - + ]: 11 : if (fstat (src->fd, &stat_results) < 0)
942 : 0 : goto could_not_stat;
943 : :
944 : 11 : *size = stat_results.st_size;
945 : :
946 : 11 : return TRUE;
947 : :
948 : : /* ERROR */
949 : : could_not_stat:
950 : : {
951 : 11 : return FALSE;
952 : : }
953 : : }
954 : :
955 : : /* open the file and mmap it, necessary to go to READY state */
956 : : static gboolean
957 : 22 : gst_file_src_start (GstBaseSrc * basesrc)
958 : : {
959 : 22 : GstFileSrc *src = GST_FILE_SRC (basesrc);
960 : : struct stat stat_results;
961 : :
962 [ + + ][ + - ]: 22 : if (src->filename == NULL || src->filename[0] == '\0')
963 : : goto no_filename;
964 : :
965 [ - + ]: 4 : GST_INFO_OBJECT (src, "opening file %s", src->filename);
966 : :
967 : : /* open the file */
968 : 4 : src->fd = gst_open (src->filename, O_RDONLY | O_BINARY, 0);
969 : :
970 [ + + ]: 4 : if (src->fd < 0)
971 : 1 : goto open_failed;
972 : :
973 : : /* check if it is a regular file, otherwise bail out */
974 [ - + ]: 3 : if (fstat (src->fd, &stat_results) < 0)
975 : 0 : goto no_stat;
976 : :
977 [ - + ]: 3 : if (S_ISDIR (stat_results.st_mode))
978 : 0 : goto was_directory;
979 : :
980 [ - + ]: 3 : if (S_ISSOCK (stat_results.st_mode))
981 : 0 : goto was_socket;
982 : :
983 : 3 : src->using_mmap = FALSE;
984 : 3 : src->read_position = 0;
985 : :
986 : : /* record if it's a regular (hence seekable and lengthable) file */
987 [ + - ]: 3 : if (S_ISREG (stat_results.st_mode))
988 : 3 : src->is_regular = TRUE;
989 : :
990 : : #ifdef HAVE_MMAP
991 [ - + ]: 3 : if (src->use_mmap) {
992 : : /* FIXME: maybe we should only try to mmap if it's a regular file */
993 : : /* allocate the first mmap'd region if it's a regular file ? */
994 : 0 : src->mapbuf = gst_file_src_map_region (src, 0, src->mapsize, TRUE);
995 [ # # ]: 0 : if (src->mapbuf != NULL) {
996 [ # # ]: 0 : GST_DEBUG_OBJECT (src, "using mmap for file");
997 : 0 : src->using_mmap = TRUE;
998 : 0 : src->seekable = TRUE;
999 : : }
1000 : : }
1001 [ + - ]: 3 : if (src->mapbuf == NULL)
1002 : : #endif
1003 : : {
1004 : : /* If not in mmap mode, we need to check if the underlying file is
1005 : : * seekable. */
1006 : 3 : off_t res = lseek (src->fd, 0, SEEK_END);
1007 : :
1008 [ - + ]: 3 : if (res < 0) {
1009 [ # # ]: 0 : GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek "
1010 : : "failed: %s", g_strerror (errno));
1011 : 0 : src->seekable = FALSE;
1012 : : } else {
1013 : 3 : src->seekable = TRUE;
1014 : : }
1015 : 3 : lseek (src->fd, 0, SEEK_SET);
1016 : : }
1017 : :
1018 : : /* We can only really do seeking on regular files - for other file types, we
1019 : : * don't know their length, so seeking isn't useful/meaningful */
1020 [ + - ][ + - ]: 3 : src->seekable = src->seekable && src->is_regular;
1021 : :
1022 : 3 : return TRUE;
1023 : :
1024 : : /* ERROR */
1025 : : no_filename:
1026 : : {
1027 [ + - ][ - + ]: 18 : GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
[ - + ][ # # ]
1028 : : (_("No file name specified for reading.")), (NULL));
1029 : 18 : return FALSE;
1030 : : }
1031 : : open_failed:
1032 : : {
1033 [ + - ]: 1 : switch (errno) {
1034 : : case ENOENT:
1035 [ - + ][ # # ]: 1 : GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
[ + - ][ - + ]
1036 : : ("No such file \"%s\"", src->filename));
1037 : 1 : break;
1038 : : default:
1039 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
[ # # ][ # # ]
1040 : : (_("Could not open file \"%s\" for reading."), src->filename),
1041 : : GST_ERROR_SYSTEM);
1042 : 0 : break;
1043 : : }
1044 : 1 : return FALSE;
1045 : : }
1046 : : no_stat:
1047 : : {
1048 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
[ # # ][ # # ]
1049 : : (_("Could not get info on \"%s\"."), src->filename), (NULL));
1050 : 0 : close (src->fd);
1051 : 0 : return FALSE;
1052 : : }
1053 : : was_directory:
1054 : : {
1055 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
[ # # ][ # # ]
1056 : : (_("\"%s\" is a directory."), src->filename), (NULL));
1057 : 0 : close (src->fd);
1058 : 0 : return FALSE;
1059 : : }
1060 : : was_socket:
1061 : : {
1062 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
[ # # ][ # # ]
1063 : : (_("File \"%s\" is a socket."), src->filename), (NULL));
1064 : 0 : close (src->fd);
1065 : 22 : return FALSE;
1066 : : }
1067 : : }
1068 : :
1069 : : /* unmap and close the file */
1070 : : static gboolean
1071 : 3 : gst_file_src_stop (GstBaseSrc * basesrc)
1072 : : {
1073 : 3 : GstFileSrc *src = GST_FILE_SRC (basesrc);
1074 : :
1075 : : /* close the file */
1076 : 3 : close (src->fd);
1077 : :
1078 : : /* zero out a lot of our state */
1079 : 3 : src->fd = 0;
1080 : 3 : src->is_regular = FALSE;
1081 : :
1082 [ - + ]: 3 : if (src->mapbuf) {
1083 : 0 : gst_buffer_unref (src->mapbuf);
1084 : 0 : src->mapbuf = NULL;
1085 : : }
1086 : :
1087 : 3 : return TRUE;
1088 : : }
1089 : :
1090 : : /*** GSTURIHANDLER INTERFACE *************************************************/
1091 : :
1092 : : static GstURIType
1093 : 4 : gst_file_src_uri_get_type (void)
1094 : : {
1095 : 4 : return GST_URI_SRC;
1096 : : }
1097 : :
1098 : : static gchar **
1099 : 4 : gst_file_src_uri_get_protocols (void)
1100 : : {
1101 : : static gchar *protocols[] = { (char *) "file", NULL };
1102 : :
1103 : 4 : return protocols;
1104 : : }
1105 : :
1106 : : static const gchar *
1107 : 4 : gst_file_src_uri_get_uri (GstURIHandler * handler)
1108 : : {
1109 : 4 : GstFileSrc *src = GST_FILE_SRC (handler);
1110 : :
1111 : 4 : return src->uri;
1112 : : }
1113 : :
1114 : : static gboolean
1115 : 6 : gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
1116 : : {
1117 : 6 : gchar *location, *hostname = NULL;
1118 : 6 : gboolean ret = FALSE;
1119 : 6 : GstFileSrc *src = GST_FILE_SRC (handler);
1120 : 6 : GError *error = NULL;
1121 : :
1122 [ - + ]: 6 : if (strcmp (uri, "file://") == 0) {
1123 : : /* Special case for "file://" as this is used by some applications
1124 : : * to test with gst_element_make_from_uri if there's an element
1125 : : * that supports the URI protocol. */
1126 : 0 : gst_file_src_set_location (src, NULL);
1127 : 0 : return TRUE;
1128 : : }
1129 : :
1130 : 6 : location = g_filename_from_uri (uri, &hostname, &error);
1131 : :
1132 [ + - ][ - + ]: 6 : if (!location || error) {
1133 [ # # ]: 0 : if (error) {
1134 [ # # ]: 0 : GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc: %s", uri,
1135 : : error->message);
1136 : 0 : g_error_free (error);
1137 : : } else {
1138 [ # # ]: 0 : GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc", uri);
1139 : : }
1140 : 0 : goto beach;
1141 : : }
1142 : :
1143 [ + + ][ + + ]: 6 : if ((hostname) && (strcmp (hostname, "localhost"))) {
1144 : : /* Only 'localhost' is permitted */
1145 [ - + ]: 1 : GST_WARNING_OBJECT (src, "Invalid hostname '%s' for filesrc", hostname);
1146 : 1 : goto beach;
1147 : : }
1148 : : #ifdef G_OS_WIN32
1149 : : /* Unfortunately, g_filename_from_uri() doesn't handle some UNC paths
1150 : : * correctly on windows, it leaves them with an extra backslash
1151 : : * at the start if they're of the mozilla-style file://///host/path/file
1152 : : * form. Correct this.
1153 : : */
1154 : : if (location[0] == '\\' && location[1] == '\\' && location[2] == '\\')
1155 : : g_memmove (location, location + 1, strlen (location + 1) + 1);
1156 : : #endif
1157 : :
1158 : 5 : ret = gst_file_src_set_location (src, location);
1159 : :
1160 : : beach:
1161 [ + - ]: 6 : if (location)
1162 : 6 : g_free (location);
1163 [ + + ]: 6 : if (hostname)
1164 : 2 : g_free (hostname);
1165 : :
1166 : 6 : return ret;
1167 : : }
1168 : :
1169 : : static void
1170 : 21 : gst_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1171 : : {
1172 : 21 : GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1173 : :
1174 : 21 : iface->get_type = gst_file_src_uri_get_type;
1175 : 21 : iface->get_protocols = gst_file_src_uri_get_protocols;
1176 : 21 : iface->get_uri = gst_file_src_uri_get_uri;
1177 : 21 : iface->set_uri = gst_file_src_uri_set_uri;
1178 : 21 : }
|