예제 #1
0
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;
}
예제 #2
0
/* 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;
  }
}
예제 #3
0
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;
  }
}
예제 #4
0
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;
}