static gboolean gst_file_src_is_seekable (GstBaseSrc * basesrc) { GstFileSrc *src = GST_FILE_SRC (basesrc); return src->seekable; }
static gboolean gst_file_src_get_size (GstBaseSrc * basesrc, guint64 * size) { struct stat stat_results; GstFileSrc *src; src = GST_FILE_SRC (basesrc); if (!src->seekable) { /* If it isn't seekable, we won't know the length (but fstat will still * succeed, and wrongly say our length is zero. */ return FALSE; } if (fstat (src->fd, &stat_results) < 0) goto could_not_stat; *size = stat_results.st_size; return TRUE; /* ERROR */ could_not_stat: { return FALSE; } }
static void gst_file_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstFileSrc *src; g_return_if_fail (GST_IS_FILE_SRC (object)); src = GST_FILE_SRC (object); switch (prop_id) { case ARG_LOCATION: g_value_set_string (value, src->filename); break; case ARG_FD: g_value_set_int (value, src->fd); break; case ARG_MMAPSIZE: g_value_set_ulong (value, src->mapsize); break; case ARG_TOUCH: g_value_set_boolean (value, src->touch); break; case ARG_SEQUENTIAL: g_value_set_boolean (value, src->sequential); break; case ARG_USEMMAP: g_value_set_boolean (value, src->use_mmap); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static const gchar * gst_file_src_uri_get_uri (GstURIHandler * handler) { GstFileSrc *src = GST_FILE_SRC (handler); return src->uri; }
static gchar * gst_file_src_uri_get_uri (GstURIHandler * handler) { GstFileSrc *src = GST_FILE_SRC (handler); /* FIXME: make thread-safe */ return g_strdup (src->uri); }
static gboolean gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) { gchar *location, *hostname = NULL; gboolean ret = FALSE; GstFileSrc *src = GST_FILE_SRC (handler); GError *error = NULL; if (strcmp (uri, "file://") == 0) { /* Special case for "file://" as this is used by some applications * to test with gst_element_make_from_uri if there's an element * that supports the URI protocol. */ gst_file_src_set_location (src, NULL); return TRUE; } location = g_filename_from_uri (uri, &hostname, &error); if (!location || error) { if (error) { GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc: %s", uri, error->message); g_error_free (error); } else { GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc", uri); } goto beach; } if ((hostname) && (strcmp (hostname, "localhost"))) { /* Only 'localhost' is permitted */ GST_WARNING_OBJECT (src, "Invalid hostname '%s' for filesrc", hostname); goto beach; } #ifdef G_OS_WIN32 /* Unfortunately, g_filename_from_uri() doesn't handle some UNC paths * correctly on windows, it leaves them with an extra backslash * at the start if they're of the mozilla-style file://///host/path/file * form. Correct this. */ if (location[0] == '\\' && location[1] == '\\' && location[2] == '\\') g_memmove (location, location + 1, strlen (location + 1) + 1); #endif ret = gst_file_src_set_location (src, location); beach: if (location) g_free (location); if (hostname) g_free (hostname); return ret; }
static void gst_file_src_finalize (GObject * object) { GstFileSrc *src; src = GST_FILE_SRC (object); g_free (src->filename); g_free (src->uri); G_OBJECT_CLASS (parent_class)->finalize (object); }
/* unmap and close the file */ static gboolean gst_file_src_stop (GstBaseSrc * basesrc) { GstFileSrc *src = GST_FILE_SRC (basesrc); /* close the file */ close (src->fd); /* zero out a lot of our state */ src->fd = 0; src->is_regular = FALSE; return TRUE; }
static gboolean gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) { gchar *protocol, *location; gboolean ret; GstFileSrc *src = GST_FILE_SRC (handler); protocol = gst_uri_get_protocol (uri); if (strcmp (protocol, "file") != 0) { g_free (protocol); return FALSE; } g_free (protocol); /* allow file://localhost/foo/bar by stripping localhost but fail * for every other hostname */ if (g_str_has_prefix (uri, "file://localhost/")) { char *tmp; /* 16 == strlen ("file://localhost") */ tmp = g_strconcat ("file://", uri + 16, NULL); /* we use gst_uri_get_location() although we already have the * "location" with uri + 16 because it provides unescaping */ location = gst_uri_get_location (tmp); g_free (tmp); } else if (strcmp (uri, "file://") == 0) { /* Special case for "file://" as this is used by some applications * to test with gst_element_make_from_uri if there's an element * that supports the URI protocol. */ gst_file_src_set_location (src, NULL); return TRUE; } else { location = gst_uri_get_location (uri); } if (!location) return FALSE; if (!g_path_is_absolute (location)) { g_free (location); return FALSE; } ret = gst_file_src_set_location (src, location); g_free (location); return ret; }
/* unmap and close the file */ static gboolean gst_file_src_stop (GstBaseSrc * basesrc) { GstFileSrc *src = GST_FILE_SRC (basesrc); /* close the file */ close (src->fd); /* zero out a lot of our state */ src->fd = 0; src->is_regular = FALSE; if (src->mapbuf) { gst_buffer_unref (src->mapbuf); src->mapbuf = NULL; } return TRUE; }
static void gst_file_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstFileSrc *src; g_return_if_fail (GST_IS_FILE_SRC (object)); src = GST_FILE_SRC (object); switch (prop_id) { case PROP_LOCATION: g_value_set_string (value, src->filename); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_file_src_query (GstBaseSrc * basesrc, GstQuery * query) { gboolean ret = FALSE; GstFileSrc *src = GST_FILE_SRC (basesrc); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_URI: gst_query_set_uri (query, src->uri); ret = TRUE; break; default: ret = FALSE; break; } if (!ret) ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); return ret; }
static GstFlowReturn gst_file_src_create (GstBaseSrc * basesrc, guint64 offset, guint length, GstBuffer ** buffer) { GstFileSrc *src; GstFlowReturn ret; src = GST_FILE_SRC (basesrc); #ifdef HAVE_MMAP if (src->using_mmap) { ret = gst_file_src_create_mmap (src, offset, length, buffer); } else { ret = gst_file_src_create_read (src, offset, length, buffer); } #else ret = gst_file_src_create_read (src, offset, length, buffer); #endif return ret; }
static void gst_file_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstFileSrc *src; g_return_if_fail (GST_IS_FILE_SRC (object)); src = GST_FILE_SRC (object); switch (prop_id) { case ARG_LOCATION: gst_file_src_set_location (src, g_value_get_string (value)); break; case ARG_MMAPSIZE: if ((src->mapsize % src->pagesize) == 0) { src->mapsize = g_value_get_ulong (value); } else { GST_INFO_OBJECT (src, "invalid mapsize, must be a multiple of pagesize, which is %d", src->pagesize); } break; case ARG_TOUCH: src->touch = g_value_get_boolean (value); break; case ARG_SEQUENTIAL: src->sequential = g_value_get_boolean (value); break; case ARG_USEMMAP: src->use_mmap = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/* 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; } }
/* open the file, 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->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; /* 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, lseek failed: %s", g_strerror (errno)); src->seekable = FALSE; } else { res = lseek (src->fd, 0, SEEK_SET); if (res < 0) { /* We really don't like not being able to go back to 0 */ src->seekable = FALSE; goto lseek_wonky; } src->seekable = TRUE; } } /* 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; gst_base_src_set_dynamic_size (basesrc, src->seekable); return TRUE; /* ERROR */ no_filename: { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (_("No file name specified for reading.")), (NULL)); goto error_exit; } 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; } goto error_exit; } no_stat: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("Could not get info on \"%s\"."), src->filename), (NULL)); goto error_close; } was_directory: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("\"%s\" is a directory."), src->filename), (NULL)); goto error_close; } was_socket: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("File \"%s\" is a socket."), src->filename), (NULL)); goto error_close; } lseek_wonky: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not seek back to zero after seek test in file \"%s\"", src->filename)); goto error_close; } error_close: close (src->fd); error_exit: return FALSE; }