static GstBuffer * gst_file_src_map_small_region (GstFileSrc * src, off_t offset, gsize size) { GstBuffer *ret; off_t mod; guint pagesize; GST_LOG_OBJECT (src, "attempting to map a small buffer at %" G_GUINT64_FORMAT "+%d", (guint64) offset, (gint) size); pagesize = src->pagesize; mod = offset % pagesize; /* if the offset starts at a non-page boundary, we have to special case */ if (mod != 0) { gsize mapsize; off_t mapbase; GstBuffer *map; mapbase = offset - mod; mapsize = ((size + mod + pagesize - 1) / pagesize) * pagesize; GST_LOG_OBJECT (src, "not on page boundaries, resizing to map to %" G_GUINT64_FORMAT "+%d", (guint64) mapbase, (gint) mapsize); map = gst_file_src_map_region (src, mapbase, mapsize, FALSE); if (map == NULL) return NULL; ret = gst_buffer_create_sub (map, offset - mapbase, size); GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET (map) + offset - mapbase; gst_buffer_unref (map); } else { ret = gst_file_src_map_region (src, offset, size, FALSE); } return ret; }
/* open the file and mmap it, necessary to go to READY state */ static gboolean gst_file_src_start (GstBaseSrc * basesrc) { GstFileSrc *src = GST_FILE_SRC (basesrc); struct stat stat_results; if (src->filename == NULL || src->filename[0] == '\0') goto no_filename; GST_INFO_OBJECT (src, "opening file %s", src->filename); /* open the file */ src->fd = gst_open (src->filename, O_RDONLY | O_BINARY, 0); if (src->fd < 0) goto open_failed; /* check if it is a regular file, otherwise bail out */ if (fstat (src->fd, &stat_results) < 0) goto no_stat; if (S_ISDIR (stat_results.st_mode)) goto was_directory; if (S_ISSOCK (stat_results.st_mode)) goto was_socket; src->using_mmap = FALSE; src->read_position = 0; /* record if it's a regular (hence seekable and lengthable) file */ if (S_ISREG (stat_results.st_mode)) src->is_regular = TRUE; #ifdef HAVE_MMAP if (src->use_mmap) { /* FIXME: maybe we should only try to mmap if it's a regular file */ /* allocate the first mmap'd region if it's a regular file ? */ src->mapbuf = gst_file_src_map_region (src, 0, src->mapsize, TRUE); if (src->mapbuf != NULL) { GST_DEBUG_OBJECT (src, "using mmap for file"); src->using_mmap = TRUE; src->seekable = TRUE; } } if (src->mapbuf == NULL) #endif { /* If not in mmap mode, we need to check if the underlying file is * seekable. */ off_t res = lseek (src->fd, 0, SEEK_END); if (res < 0) { GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek " "failed: %s", g_strerror (errno)); src->seekable = FALSE; } else { src->seekable = TRUE; } lseek (src->fd, 0, SEEK_SET); } /* We can only really do seeking on regular files - for other file types, we * don't know their length, so seeking isn't useful/meaningful */ src->seekable = src->seekable && src->is_regular; return TRUE; /* ERROR */ no_filename: { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (_("No file name specified for reading.")), (NULL)); return FALSE; } open_failed: { switch (errno) { case ENOENT: GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), ("No such file \"%s\"", src->filename)); break; default: GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("Could not open file \"%s\" for reading."), src->filename), GST_ERROR_SYSTEM); break; } return FALSE; } no_stat: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("Could not get info on \"%s\"."), src->filename), (NULL)); close (src->fd); return FALSE; } was_directory: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("\"%s\" is a directory."), src->filename), (NULL)); close (src->fd); return FALSE; } was_socket: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("File \"%s\" is a socket."), src->filename), (NULL)); close (src->fd); return FALSE; } }
static GstFlowReturn gst_file_src_create_mmap (GstFileSrc * src, guint64 offset, guint length, GstBuffer ** buffer) { GstBuffer *buf = NULL; gsize readsize, mapsize; off_t readend, mapstart, mapend; int i; /* calculate end pointers so we don't have to do so repeatedly later */ readsize = length; readend = offset + readsize; /* note this is the byte *after* the read */ mapstart = GST_BUFFER_OFFSET (src->mapbuf); mapsize = GST_BUFFER_SIZE (src->mapbuf); mapend = mapstart + mapsize; /* note this is the byte *after* the map */ GST_LOG ("attempting to read %08lx, %08lx, %08lx, %08lx", (unsigned long) readsize, (unsigned long) readend, (unsigned long) mapstart, (unsigned long) mapend); /* if the start is past the mapstart */ if (offset >= mapstart) { /* if the end is before the mapend, the buffer is in current mmap region... */ /* ('cause by definition if readend is in the buffer, so's readstart) */ if (readend <= mapend) { GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u lives in " "current mapbuf %u+%u, creating subbuffer of mapbuf", offset, (guint) readsize, (guint) mapstart, (guint) mapsize); buf = gst_buffer_create_sub (src->mapbuf, offset - mapstart, readsize); GST_BUFFER_OFFSET (buf) = offset; /* if the start actually is within the current mmap region, map an overlap buffer */ } else if (offset < mapend) { GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u starts in " "mapbuf %u+%u but ends outside, creating new mmap", offset, (guint) readsize, (guint) mapstart, (guint) mapsize); buf = gst_file_src_map_small_region (src, offset, readsize); if (buf == NULL) goto could_not_mmap; } /* the only other option is that buffer is totally outside, which means we search for it */ /* now we can assume that the start is *before* the current mmap region */ /* if the readend is past mapstart, we have two options */ } else if (readend >= mapstart) { /* either the read buffer overlaps the start of the mmap region */ /* or the read buffer fully contains the current mmap region */ /* either way, it's really not relevant, we just create a new region anyway */ GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d starts before " "mapbuf %d+%d, but overlaps it", (guint64) offset, (gint) readsize, (gint) mapstart, (gint) mapsize); buf = gst_file_src_map_small_region (src, offset, readsize); if (buf == NULL) goto could_not_mmap; } /* then deal with the case where the read buffer is totally outside */ if (buf == NULL) { /* first check to see if there's a map that covers the right region already */ GST_LOG_OBJECT (src, "searching for mapbuf to cover %" G_GUINT64_FORMAT "+%d", offset, (int) readsize); /* if the read buffer crosses a mmap region boundary, create a one-off region */ if ((offset / src->mapsize) != (readend / src->mapsize)) { GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d crosses a " "%d-byte boundary, creating a one-off", offset, (int) readsize, (int) src->mapsize); buf = gst_file_src_map_small_region (src, offset, readsize); if (buf == NULL) goto could_not_mmap; /* otherwise we will create a new mmap region and set it to the default */ } else { gsize mapsize; off_t nextmap = offset - (offset % src->mapsize); GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d in new mapbuf " "at %" G_GUINT64_FORMAT "+%d, mapping and subbuffering", offset, (gint) readsize, (guint64) nextmap, (gint) src->mapsize); /* first, we're done with the old mapbuf */ gst_buffer_unref (src->mapbuf); mapsize = src->mapsize; /* double the mapsize as long as the readsize is smaller */ while (readsize + offset > nextmap + mapsize) { GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d", (guint) readsize, (gint) mapsize); mapsize <<= 1; } /* create a new one */ src->mapbuf = gst_file_src_map_region (src, nextmap, mapsize, FALSE); if (src->mapbuf == NULL) goto could_not_mmap; /* subbuffer it */ buf = gst_buffer_create_sub (src->mapbuf, offset - nextmap, readsize); GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET (src->mapbuf) + offset - nextmap; } } /* if we need to touch the buffer (to bring it into memory), do so */ if (src->touch) { volatile guchar *p = GST_BUFFER_DATA (buf); /* read first byte of each page */ for (i = 0; i < GST_BUFFER_SIZE (buf); i += src->pagesize) (void) p[i]; } /* we're done, return the buffer */ *buffer = buf; return GST_FLOW_OK; /* ERROR */ could_not_mmap: { return GST_FLOW_ERROR; } }
static GstBuffer * gst_file_src_map_region (GstFileSrc * src, off_t offset, gsize size, gboolean testonly) { GstBuffer *buf; void *mmapregion; g_return_val_if_fail (offset >= 0, NULL); GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory", offset, (gulong) size); mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset); if (mmapregion == NULL || mmapregion == MAP_FAILED) goto mmap_failed; GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p", (gulong) offset, (gulong) size, mmapregion); /* time to allocate a new mapbuf */ buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_MMAP_BUFFER); /* mmap() the data into this new buffer */ GST_BUFFER_DATA (buf) = mmapregion; GST_MMAP_BUFFER (buf)->filesrc = src; #ifdef MADV_SEQUENTIAL if (src->sequential) { /* madvise to tell the kernel what to do with it */ #ifndef __SYMBIAN32__ if (madvise (mmapregion, size, MADV_SEQUENTIAL) < 0) { GST_WARNING_OBJECT (src, "warning: madvise failed: %s", g_strerror (errno)); } #endif #endif /* fill in the rest of the fields */ GST_BUFFER_SIZE (buf) = size; GST_BUFFER_OFFSET (buf) = offset; GST_BUFFER_OFFSET_END (buf) = offset + size; GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; return buf; /* ERROR */ mmap_failed: { if (!testonly) { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("mmap (0x%08lx, %d, 0x%" G_GINT64_MODIFIER "x) failed: %s", (gulong) size, src->fd, (guint64) offset, g_strerror (errno))); } return NULL; } } static GstBuffer * gst_file_src_map_small_region (GstFileSrc * src, off_t offset, gsize size) { GstBuffer *ret; off_t mod; guint pagesize; GST_LOG_OBJECT (src, "attempting to map a small buffer at %" G_GUINT64_FORMAT "+%d", (guint64) offset, (gint) size); pagesize = src->pagesize; mod = offset % pagesize; /* if the offset starts at a non-page boundary, we have to special case */ if (mod != 0) { gsize mapsize; off_t mapbase; GstBuffer *map; mapbase = offset - mod; mapsize = ((size + mod + pagesize - 1) / pagesize) * pagesize; GST_LOG_OBJECT (src, "not on page boundaries, resizing to map to %" G_GUINT64_FORMAT "+%d", (guint64) mapbase, (gint) mapsize); map = gst_file_src_map_region (src, mapbase, mapsize, FALSE); if (map == NULL) return NULL; ret = gst_buffer_create_sub (map, offset - mapbase, size); GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET (map) + offset - mapbase; gst_buffer_unref (map); } else { ret = gst_file_src_map_region (src, offset, size, FALSE); } return ret; }