2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000,2005 Wim Taymans <wim@fluendo.com>
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.
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.
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.
24 * @short_description: read from arbitrary point in a file
25 * @see_also: #GstFileSrc
27 * Read data from a file in the local file system. The implementation is using
28 * mmap(2) to read chunks from the file in an efficient way.
36 #include "gstfilesrc.h"
39 #include <sys/types.h>
48 # include <sys/mman.h>
52 # include <io.h> /* lseek, open, close, read */
58 #include "../../gst/gst-i18n-lib.h"
60 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
65 /* FIXME we should be using glib for this */
67 #define S_ISREG(mode) ((mode)&_S_IFREG)
70 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
73 #define S_ISSOCK(x) (0)
80 /**********************************************************************
81 * GStreamer Default File Source
84 * This source uses mmap(2) to efficiently load data from a file.
85 * To do this without seriously polluting the applications' memory
86 * space, it must do so in smaller chunks, say 1-4MB at a time.
87 * Buffers are then subdivided from these mmap'd chunks, to directly
88 * make use of the mmap.
90 * To handle refcounting so that the mmap can be freed at the appropriate
91 * time, a buffer will be created for each mmap'd region, and all new
92 * buffers will be sub-buffers of this top-level buffer. As they are
93 * freed, the refcount goes down on the mmap'd buffer and its free()
94 * function is called, which will call munmap(2) on itself.
96 * If a buffer happens to cross the boundaries of an mmap'd region, we
97 * have to decide whether it's more efficient to copy the data into a
98 * new buffer, or mmap() just that buffer. There will have to be a
99 * breakpoint size to determine which will be done. The mmap() size
100 * has a lot to do with this as well, because you end up in double-
101 * jeopardy: the larger the outgoing buffer, the more data to copy when
102 * it overlaps, *and* the more frequently you'll have buffers that *do*
105 * Seeking is another tricky aspect to do efficiently. The initial
106 * implementation of this source won't make use of these features, however.
107 * The issue is that if an application seeks backwards in a file, *and*
108 * that region of the file is covered by an mmap that hasn't been fully
109 * deallocated, we really should re-use it. But keeping track of these
110 * regions is tricky because we have to lock the structure that holds
111 * them. We need to settle on a locking primitive (GMutex seems to be
112 * a really good option...), then we can do that.
116 GST_DEBUG_CATEGORY_STATIC (gst_file_src_debug);
117 #define GST_CAT_DEFAULT gst_file_src_debug
119 static GstElementDetails gst_file_src_details =
120 GST_ELEMENT_DETAILS ("File Source",
122 "Read from arbitrary point in a file",
123 "Erik Walthinsen <omega@cse.ogi.edu>");
125 /* FileSrc signals and args */
132 #define DEFAULT_BLOCKSIZE 4*1024
133 #define DEFAULT_MMAPSIZE 4*1024*1024
134 #define DEFAULT_TOUCH FALSE
145 static void gst_file_src_finalize (GObject * object);
147 static void gst_file_src_set_property (GObject * object, guint prop_id,
148 const GValue * value, GParamSpec * pspec);
149 static void gst_file_src_get_property (GObject * object, guint prop_id,
150 GValue * value, GParamSpec * pspec);
152 static gboolean gst_file_src_start (GstBaseSrc * basesrc);
153 static gboolean gst_file_src_stop (GstBaseSrc * basesrc);
155 static gboolean gst_file_src_is_seekable (GstBaseSrc * src);
156 static gboolean gst_file_src_get_size (GstBaseSrc * src, guint64 * size);
157 static GstFlowReturn gst_file_src_create (GstBaseSrc * src, guint64 offset,
158 guint length, GstBuffer ** buffer);
160 static void gst_file_src_uri_handler_init (gpointer g_iface,
161 gpointer iface_data);
164 _do_init (GType filesrc_type)
166 static const GInterfaceInfo urihandler_info = {
167 gst_file_src_uri_handler_init,
172 g_type_add_interface_static (filesrc_type, GST_TYPE_URI_HANDLER,
174 GST_DEBUG_CATEGORY_INIT (gst_file_src_debug, "filesrc", 0, "filesrc element");
177 GST_BOILERPLATE_FULL (GstFileSrc, gst_file_src, GstBaseSrc, GST_TYPE_BASE_SRC,
181 gst_file_src_base_init (gpointer g_class)
183 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
185 gst_element_class_add_pad_template (gstelement_class,
186 gst_static_pad_template_get (&srctemplate));
188 gst_element_class_set_details (gstelement_class, &gst_file_src_details);
192 gst_file_src_class_init (GstFileSrcClass * klass)
194 GObjectClass *gobject_class;
195 GstElementClass *gstelement_class;
196 GstBaseSrcClass *gstbasesrc_class;
198 gobject_class = (GObjectClass *) klass;
199 gstelement_class = (GstElementClass *) klass;
200 gstbasesrc_class = (GstBaseSrcClass *) klass;
202 gobject_class->set_property = gst_file_src_set_property;
203 gobject_class->get_property = gst_file_src_get_property;
205 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FD,
206 g_param_spec_int ("fd", "File-descriptor",
207 "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0,
209 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
210 g_param_spec_string ("location", "File Location",
211 "Location of the file to read", NULL, G_PARAM_READWRITE));
212 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MMAPSIZE,
213 g_param_spec_ulong ("mmapsize", "mmap() Block Size",
214 "Size in bytes of mmap()d regions", 0, G_MAXULONG, DEFAULT_MMAPSIZE,
216 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOUCH,
217 g_param_spec_boolean ("touch", "Touch read data",
218 "Touch data to force disk read", DEFAULT_TOUCH, G_PARAM_READWRITE));
220 gobject_class->finalize = gst_file_src_finalize;
222 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_file_src_start);
223 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_file_src_stop);
224 gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_file_src_is_seekable);
225 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_file_src_get_size);
226 gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_file_src_create);
228 if (sizeof (off_t) < 8) {
229 GST_LOG ("No large file support, sizeof (off_t) = %u!", sizeof (off_t));
234 gst_file_src_init (GstFileSrc * src, GstFileSrcClass * g_class)
237 src->pagesize = getpagesize ();
240 src->filename = NULL;
244 src->touch = DEFAULT_TOUCH;
247 src->mapsize = DEFAULT_MMAPSIZE; /* default is 4MB */
249 src->is_regular = FALSE;
253 gst_file_src_finalize (GObject * object)
257 src = GST_FILE_SRC (object);
259 g_free (src->filename);
262 G_OBJECT_CLASS (parent_class)->finalize (object);
266 gst_file_src_set_location (GstFileSrc * src, const gchar * location)
268 /* the element must be stopped in order to do this */
269 GST_STATE_LOCK (src);
273 state = GST_STATE (src);
274 if (state != GST_STATE_READY && state != GST_STATE_NULL)
277 GST_STATE_UNLOCK (src);
279 g_free (src->filename);
282 /* clear the filename if we get a NULL (is that possible?) */
283 if (location == NULL) {
284 src->filename = NULL;
287 src->filename = g_strdup (location);
288 src->uri = gst_uri_construct ("file", src->filename);
290 g_object_notify (G_OBJECT (src), "location");
291 gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri);
298 GST_DEBUG_OBJECT (src, "setting location in wrong state");
299 GST_STATE_UNLOCK (src);
305 gst_file_src_set_property (GObject * object, guint prop_id,
306 const GValue * value, GParamSpec * pspec)
310 g_return_if_fail (GST_IS_FILE_SRC (object));
312 src = GST_FILE_SRC (object);
316 gst_file_src_set_location (src, g_value_get_string (value));
319 if ((src->mapsize % src->pagesize) == 0) {
320 src->mapsize = g_value_get_ulong (value);
321 g_object_notify (G_OBJECT (src), "mmapsize");
323 GST_INFO_OBJECT (src,
324 "invalid mapsize, must be a multiple of pagesize, which is %d",
329 src->touch = g_value_get_boolean (value);
330 g_object_notify (G_OBJECT (src), "touch");
333 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339 gst_file_src_get_property (GObject * object, guint prop_id, GValue * value,
344 g_return_if_fail (GST_IS_FILE_SRC (object));
346 src = GST_FILE_SRC (object);
350 g_value_set_string (value, src->filename);
353 g_value_set_int (value, src->fd);
356 g_value_set_ulong (value, src->mapsize);
359 g_value_set_boolean (value, src->touch);
362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
375 typedef struct _GstMmapBuffer GstMmapBuffer;
376 typedef struct _GstMmapBufferClass GstMmapBufferClass;
378 #define GST_TYPE_MMAP_BUFFER (gst_mmap_buffer_get_type())
380 #define GST_IS_MMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MMAP_BUFFER))
381 #define GST_IS_MMAP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MMAP_BUFFER))
382 #define GST_MMAP_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass))
383 #define GST_MMAP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBuffer))
384 #define GST_MMAP_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass))
388 struct _GstMmapBuffer
395 struct _GstMmapBufferClass
397 GstBufferClass buffer_class;
400 static void gst_mmap_buffer_init (GTypeInstance * instance, gpointer g_class);
401 static void gst_mmap_buffer_class_init (gpointer g_class, gpointer class_data);
402 static void gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer);
405 gst_mmap_buffer_get_type (void)
407 static GType _gst_mmap_buffer_type;
409 if (G_UNLIKELY (_gst_mmap_buffer_type == 0)) {
410 static const GTypeInfo mmap_buffer_info = {
411 sizeof (GstMmapBufferClass),
414 gst_mmap_buffer_class_init,
417 sizeof (GstMmapBuffer),
419 gst_mmap_buffer_init,
423 _gst_mmap_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
424 "GstMmapBuffer", &mmap_buffer_info, 0);
426 return _gst_mmap_buffer_type;
430 gst_mmap_buffer_class_init (gpointer g_class, gpointer class_data)
432 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
434 mini_object_class->finalize =
435 (GstMiniObjectFinalizeFunction) gst_mmap_buffer_finalize;
439 gst_mmap_buffer_init (GTypeInstance * instance, gpointer g_class)
441 GstBuffer *buf = (GstBuffer *) instance;
443 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
444 /* before we re-enable this flag, we probably need to fix _copy()
445 * _make_writable(), etc. in GstMiniObject/GstBuffer as well */
446 /* GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_ORIGINAL); */
450 gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer)
456 GstBuffer *buffer = GST_BUFFER (mmap_buffer);
459 size = GST_BUFFER_SIZE (buffer);
460 offset = GST_BUFFER_OFFSET (buffer);
461 data = GST_BUFFER_DATA (buffer);
462 src = mmap_buffer->filesrc;
464 GST_LOG ("freeing mmap()d buffer at %" G_GUINT64_FORMAT "+%u", offset, size);
467 /* madvise to tell the kernel what to do with it */
468 if (madvise (data, size, MADV_DONTNEED) < 0) {
469 GST_WARNING_OBJECT (src, "warning: madvise failed: %s", strerror (errno));
473 /* now unmap the memory */
474 if (munmap (data, size) < 0) {
475 GST_WARNING_OBJECT (src, "warning: munmap failed: %s", strerror (errno));
478 /* cast to unsigned long, since there's no gportable way to print
480 GST_LOG ("unmapped region %08lx+%08lx at %p",
481 (gulong) offset, (gulong) size, data);
485 gst_file_src_map_region (GstFileSrc * src, off_t offset, size_t size,
491 g_return_val_if_fail (offset >= 0, NULL);
493 GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory",
494 offset, (gulong) size);
496 mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset);
498 if (mmapregion == NULL || mmapregion == MAP_FAILED)
501 GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p",
502 (gulong) offset, (gulong) size, mmapregion);
504 /* time to allocate a new mapbuf */
505 buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_MMAP_BUFFER);
506 /* mmap() the data into this new buffer */
507 GST_BUFFER_DATA (buf) = mmapregion;
508 GST_MMAP_BUFFER (buf)->filesrc = src;
510 #ifdef MADV_SEQUENTIAL
511 /* madvise to tell the kernel what to do with it */
512 if (madvise (mmapregion, size, MADV_SEQUENTIAL) < 0) {
513 GST_WARNING_OBJECT (src, "warning: madvise failed: %s", strerror (errno));
517 /* fill in the rest of the fields */
518 GST_BUFFER_SIZE (buf) = size;
519 GST_BUFFER_OFFSET (buf) = offset;
520 GST_BUFFER_OFFSET_END (buf) = offset + size;
521 GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
529 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
530 ("mmap (0x%08lx, %d, 0x%llx) failed: %s",
531 (gulong) size, src->fd, offset, strerror (errno)));
538 gst_file_src_map_small_region (GstFileSrc * src, off_t offset, size_t size)
545 "attempting to map a small buffer at %llu+%d",
546 (unsigned long long) offset, (gint) size);
548 pagesize = src->pagesize;
550 mod = offset % pagesize;
552 /* if the offset starts at a non-page boundary, we have to special case */
558 mapbase = offset - mod;
559 mapsize = ((size + mod + pagesize - 1) / pagesize) * pagesize;
562 "not on page boundaries, resizing to map to %llu+%d",
563 (unsigned long long) mapbase, (gint) mapsize);
565 map = gst_file_src_map_region (src, mapbase, mapsize, FALSE);
569 ret = gst_buffer_create_sub (map, offset - mapbase, size);
570 GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET (map) + offset - mapbase;
572 gst_buffer_unref (map);
574 ret = gst_file_src_map_region (src, offset, size, FALSE);
581 gst_file_src_create_mmap (GstFileSrc * src, guint64 offset, guint length,
584 GstBuffer *buf = NULL;
585 size_t readsize, mapsize;
586 off_t readend, mapstart, mapend;
589 /* calculate end pointers so we don't have to do so repeatedly later */
591 readend = offset + readsize; /* note this is the byte *after* the read */
593 mapstart = GST_BUFFER_OFFSET (src->mapbuf);
594 mapsize = GST_BUFFER_SIZE (src->mapbuf);
595 mapend = mapstart + mapsize; /* note this is the byte *after* the map */
597 GST_LOG ("attempting to read %08lx, %08lx, %08lx, %08lx",
598 (unsigned long) readsize, (unsigned long) readend,
599 (unsigned long) mapstart, (unsigned long) mapend);
601 /* if the start is past the mapstart */
602 if (offset >= mapstart) {
603 /* if the end is before the mapend, the buffer is in current mmap region... */
604 /* ('cause by definition if readend is in the buffer, so's readstart) */
605 if (readend <= mapend) {
607 "read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf",
608 offset, (int) readsize, mapstart, mapsize);
609 buf = gst_buffer_create_sub (src->mapbuf, offset - mapstart, readsize);
610 GST_BUFFER_OFFSET (buf) = offset;
612 /* if the start actually is within the current mmap region, map an overlap buffer */
613 } else if (offset < mapend) {
615 "read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap",
616 (unsigned long long) offset, (gint) readsize, (gint) mapstart,
618 buf = gst_file_src_map_small_region (src, offset, readsize);
623 /* the only other option is that buffer is totally outside, which means we search for it */
625 /* now we can assume that the start is *before* the current mmap region */
626 /* if the readend is past mapstart, we have two options */
627 } else if (readend >= mapstart) {
628 /* either the read buffer overlaps the start of the mmap region */
629 /* or the read buffer fully contains the current mmap region */
630 /* either way, it's really not relevant, we just create a new region anyway */
632 "read buf %llu+%d starts before mapbuf %d+%d, but overlaps it",
633 (unsigned long long) offset, (gint) readsize, (gint) mapstart,
635 buf = gst_file_src_map_small_region (src, offset, readsize);
640 /* then deal with the case where the read buffer is totally outside */
642 /* first check to see if there's a map that covers the right region already */
643 GST_LOG_OBJECT (src, "searching for mapbuf to cover %llu+%d",
644 offset, (int) readsize);
646 /* if the read buffer crosses a mmap region boundary, create a one-off region */
647 if ((offset / src->mapsize) != (readend / src->mapsize)) {
649 "read buf %llu+%d crosses a %d-byte boundary, creating a one-off",
650 offset, (int) readsize, (int) src->mapsize);
651 buf = gst_file_src_map_small_region (src, offset, readsize);
655 /* otherwise we will create a new mmap region and set it to the default */
659 off_t nextmap = offset - (offset % src->mapsize);
662 "read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering",
663 offset, readsize, nextmap, src->mapsize);
664 /* first, we're done with the old mapbuf */
665 gst_buffer_unref (src->mapbuf);
666 mapsize = src->mapsize;
668 /* double the mapsize as long as the readsize is smaller */
669 while (readsize + offset > nextmap + mapsize) {
670 GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d",
671 readsize, (int) mapsize);
674 /* create a new one */
675 src->mapbuf = gst_file_src_map_region (src, nextmap, mapsize, FALSE);
676 if (src->mapbuf == NULL)
680 buf = gst_buffer_create_sub (src->mapbuf, offset - nextmap, readsize);
681 GST_BUFFER_OFFSET (buf) =
682 GST_BUFFER_OFFSET (src->mapbuf) + offset - nextmap;
686 /* if we need to touch the buffer (to bring it into memory), do so */
688 volatile guchar *p = GST_BUFFER_DATA (buf), c;
690 /* read first byte of each page */
691 for (i = 0; i < GST_BUFFER_SIZE (buf); i += src->pagesize)
695 /* we're done, return the buffer */
703 return GST_FLOW_ERROR;
710 * that is to say, you shouldn't read the code below, but the code that reads
711 * stuff is below. Well, you shouldn't not read the code below, feel free
712 * to read it of course. It's just that "read code below" is a pretty crappy
713 * documentation string because it sounds like we're expecting you to read
714 * the code to understand what it does, which, while true, is really not
715 * the sort of attitude we want to be advertising. No sir.
720 gst_file_src_create_read (GstFileSrc * src, guint64 offset, guint length,
726 if (src->read_position != offset) {
729 res = lseek (src->fd, offset, SEEK_SET);
730 if (res < 0 || res != offset)
734 buf = gst_buffer_new_and_alloc (length);
736 GST_LOG_OBJECT (src, "Reading %d bytes", length);
737 ret = read (src->fd, GST_BUFFER_DATA (buf), length);
741 /* regular files should have given us what we expected */
742 if ((guint) ret < length && src->is_regular)
745 /* other files should eos if they read 0 */
747 GST_DEBUG ("non-regular file hits EOS");
748 gst_buffer_unref (buf);
749 return GST_FLOW_UNEXPECTED;
753 GST_BUFFER_SIZE (buf) = length;
754 GST_BUFFER_OFFSET (buf) = offset;
755 GST_BUFFER_OFFSET_END (buf) = offset + length;
759 src->read_position += length;
766 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
767 return GST_FLOW_ERROR;
771 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
772 gst_buffer_unref (buf);
773 return GST_FLOW_ERROR;
777 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
778 ("unexpected end of file."));
779 gst_buffer_unref (buf);
780 return GST_FLOW_ERROR;
785 gst_file_src_create (GstBaseSrc * basesrc, guint64 offset, guint length,
791 src = GST_FILE_SRC (basesrc);
794 if (src->using_mmap) {
795 ret = gst_file_src_create_mmap (src, offset, length, buffer);
797 ret = gst_file_src_create_read (src, offset, length, buffer);
800 ret = gst_file_src_create_read (src, offset, length, buffer);
807 gst_file_src_is_seekable (GstBaseSrc * basesrc)
809 GstFileSrc *src = GST_FILE_SRC (basesrc);
811 return src->seekable;
815 gst_file_src_get_size (GstBaseSrc * basesrc, guint64 * size)
817 struct stat stat_results;
820 src = GST_FILE_SRC (basesrc);
822 if (!src->seekable) {
823 /* If it isn't seekable, we won't know the length (but fstat will still
824 * succeed, and wrongly say our length is zero. */
828 if (fstat (src->fd, &stat_results) < 0)
831 *size = stat_results.st_size;
842 /* open the file and mmap it, necessary to go to READY state */
844 gst_file_src_start (GstBaseSrc * basesrc)
846 GstFileSrc *src = GST_FILE_SRC (basesrc);
847 struct stat stat_results;
849 if (src->filename == NULL || src->filename[0] == '\0')
852 GST_INFO_OBJECT (src, "opening file %s", src->filename);
855 src->fd = open (src->filename, O_RDONLY | O_BINARY);
859 /* check if it is a regular file, otherwise bail out */
860 if (fstat (src->fd, &stat_results) < 0)
863 if (S_ISDIR (stat_results.st_mode))
866 if (S_ISSOCK (stat_results.st_mode))
869 src->using_mmap = FALSE;
870 src->read_position = 0;
872 /* record if it's a regular (hence seekable and lengthable) file */
873 if (S_ISREG (stat_results.st_mode))
874 src->is_regular = TRUE;
877 /* FIXME: maybe we should only try to mmap if it's a regular file */
878 /* allocate the first mmap'd region if it's a regular file ? */
879 src->mapbuf = gst_file_src_map_region (src, 0, src->mapsize, TRUE);
880 if (src->mapbuf != NULL) {
881 GST_DEBUG_OBJECT (src, "using mmap for file");
882 src->using_mmap = TRUE;
883 src->seekable = TRUE;
887 /* If not in mmap mode, we need to check if the underlying file is
889 off_t res = lseek (src->fd, 0, SEEK_CUR);
892 GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek "
893 "failed: %s", strerror (errno));
894 src->seekable = FALSE;
896 src->seekable = TRUE;
900 /* We can only really do seeking on regular files - for other file types, we
901 * don't know their length, so seeking isn't useful/meaningful */
902 src->seekable = src->seekable && src->is_regular;
909 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
910 (_("No file name specified for reading.")), (NULL));
917 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
918 ("No such file \"%s\"", src->filename));
921 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
922 (_("Could not open file \"%s\" for reading: %s."), src->filename,
923 strerror (errno)), GST_ERROR_SYSTEM);
930 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
931 (_("could not get info on \"%s\"."), src->filename), (NULL));
937 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
938 (_("\"%s\" is a directory."), src->filename), (NULL));
944 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
945 (_("File \"%s\" is a socket."), src->filename), (NULL));
951 /* unmap and close the file */
953 gst_file_src_stop (GstBaseSrc * basesrc)
955 GstFileSrc *src = GST_FILE_SRC (basesrc);
960 /* zero out a lot of our state */
962 src->is_regular = FALSE;
965 gst_buffer_unref (src->mapbuf);
972 /*** GSTURIHANDLER INTERFACE *************************************************/
975 gst_file_src_uri_get_type (void)
980 gst_file_src_uri_get_protocols (void)
982 static gchar *protocols[] = { "file", NULL };
987 gst_file_src_uri_get_uri (GstURIHandler * handler)
989 GstFileSrc *src = GST_FILE_SRC (handler);
995 gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
997 gchar *protocol, *location;
999 GstFileSrc *src = GST_FILE_SRC (handler);
1001 protocol = gst_uri_get_protocol (uri);
1002 if (strcmp (protocol, "file") != 0) {
1007 location = gst_uri_get_location (uri);
1008 ret = gst_file_src_set_location (src, location);
1015 gst_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1017 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1019 iface->get_type = gst_file_src_uri_get_type;
1020 iface->get_protocols = gst_file_src_uri_get_protocols;
1021 iface->get_uri = gst_file_src_uri_get_uri;
1022 iface->set_uri = gst_file_src_uri_set_uri;