gstreamer_core/plugins/elements/gstfilesrc.c
changeset 8 4a7fac7dd34a
parent 0 0e761a78d257
child 30 7e817e7e631c
--- a/gstreamer_core/plugins/elements/gstfilesrc.c	Fri Mar 19 09:35:09 2010 +0200
+++ b/gstreamer_core/plugins/elements/gstfilesrc.c	Fri Apr 16 15:15:52 2010 +0300
@@ -21,7 +21,6 @@
  */
 /**
  * SECTION:element-filesrc
- * @short_description: read from arbitrary point in a file
  * @see_also: #GstFileSrc
  *
  * Read data from a file in the local file system.
@@ -30,23 +29,29 @@
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
-#ifdef __SYMBIAN32__
-#include <gst_global.h>
-#endif
 
 #include <gst/gst.h>
 #include "gstfilesrc.h"
 
 #include <stdio.h>
 #include <sys/types.h>
+#ifdef G_OS_WIN32
+#include <io.h>                 /* lseek, open, close, read */
+/* On win32, stat* default to 32 bit; we need the 64-bit
+ * variants, so explicitly define it that way. */
+#define stat __stat64
+#define fstat _fstat64
+#undef lseek
+#define lseek _lseeki64
+#undef off_t
+#define off_t guint64
+/* Prevent stat.h from defining the stat* functions as
+ * _stat*, since we're explicitly overriding that */
+#undef _INC_STAT_INL
+#endif
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#ifdef __SYMBIAN32__
-#include <glib_global.h>
-#include <gobject_global.h>
-#endif
-
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
@@ -55,15 +60,10 @@
 # include <sys/mman.h>
 #endif
 
-#ifdef HAVE_WIN32
-#  include <io.h>               /* lseek, open, close, read */
-#endif
-
 #include <errno.h>
 #include <string.h>
 
 #include "../../gst/gst-i18n-lib.h"
-#include <gstelement.h>
 
 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -84,6 +84,38 @@
 #define O_BINARY (0)
 #endif
 
+/* Copy of glib's g_open due to win32 libc/cross-DLL brokenness: we can't
+ * use the 'file descriptor' opened in glib (and returned from this function)
+ * in this library, as they may have unrelated C runtimes. */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+int
+gst_open (const gchar * filename, int flags, int mode)
+{
+#ifdef G_OS_WIN32
+  wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+  int retval;
+  int save_errno;
+
+  if (wfilename == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  retval = _wopen (wfilename, flags, mode);
+  save_errno = errno;
+
+  g_free (wfilename);
+
+  errno = save_errno;
+  return retval;
+#else
+  return open (filename, flags, mode);
+#endif
+}
+
 
 /**********************************************************************
  * GStreamer Default File Source
@@ -165,6 +197,7 @@
 static gboolean gst_file_src_get_size (GstBaseSrc * src, guint64 * size);
 static GstFlowReturn gst_file_src_create (GstBaseSrc * src, guint64 offset,
     guint length, GstBuffer ** buffer);
+static gboolean gst_file_src_query (GstBaseSrc * src, GstQuery * query);
 
 static void gst_file_src_uri_handler_init (gpointer g_iface,
     gpointer iface_data);
@@ -204,11 +237,9 @@
 gst_file_src_class_init (GstFileSrcClass * klass)
 {
   GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
   GstBaseSrcClass *gstbasesrc_class;
 
   gobject_class = G_OBJECT_CLASS (klass);
-  gstelement_class = GST_ELEMENT_CLASS (klass);
   gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
 
   gobject_class->set_property = gst_file_src_set_property;
@@ -217,18 +248,23 @@
   g_object_class_install_property (gobject_class, ARG_FD,
       g_param_spec_int ("fd", "File-descriptor",
           "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0,
-          G_PARAM_READABLE));
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, ARG_LOCATION,
       g_param_spec_string ("location", "File Location",
-          "Location of the file to read", NULL, G_PARAM_READWRITE));
+          "Location of the file to read", NULL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_READY));
   g_object_class_install_property (gobject_class, ARG_MMAPSIZE,
       g_param_spec_ulong ("mmapsize", "mmap() Block Size",
           "Size in bytes of mmap()d regions", 0, G_MAXULONG, DEFAULT_MMAPSIZE,
-          G_PARAM_READWRITE));
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_PLAYING));
   g_object_class_install_property (gobject_class, ARG_TOUCH,
       g_param_spec_boolean ("touch", "Touch mapped region read data",
           "Touch mmapped data regions to force them to be read from disk",
-          DEFAULT_TOUCH, G_PARAM_READWRITE));
+          DEFAULT_TOUCH,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_PLAYING));
   /**
    * GstFileSrc:use-mmap
    *
@@ -250,12 +286,14 @@
   g_object_class_install_property (gobject_class, ARG_USEMMAP,
       g_param_spec_boolean ("use-mmap", "Use mmap to read data",
           "Whether to use mmap() instead of read()",
-          DEFAULT_USEMMAP, G_PARAM_READWRITE));
+          DEFAULT_USEMMAP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_READY));
   g_object_class_install_property (gobject_class, ARG_SEQUENTIAL,
       g_param_spec_boolean ("sequential", "Optimise for sequential mmap access",
           "Whether to use madvise to hint to the kernel that access to "
           "mmap pages will be sequential",
-          DEFAULT_SEQUENTIAL, G_PARAM_READWRITE));
+          DEFAULT_SEQUENTIAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_PLAYING));
 
   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_file_src_finalize);
 
@@ -264,6 +302,7 @@
   gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_file_src_is_seekable);
   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_file_src_get_size);
   gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_file_src_create);
+  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_file_src_query);
 
   if (sizeof (off_t) < 8) {
     GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT "!",
@@ -325,6 +364,8 @@
     src->filename = NULL;
     src->uri = NULL;
   } else {
+    /* we store the filename as received by the application. On Windoes this
+     * should be UTF8 */
     src->filename = g_strdup (location);
     src->uri = gst_uri_construct ("file", src->filename);
   }
@@ -336,7 +377,8 @@
   /* ERROR */
 wrong_state:
   {
-    GST_DEBUG_OBJECT (src, "setting location in wrong state");
+    g_warning ("Changing the `location' property on filesink when a file is "
+        "open is not supported.");
     GST_OBJECT_UNLOCK (src);
     return FALSE;
   }
@@ -534,8 +576,8 @@
   GST_LOG ("unmapped region %08lx+%08lx at %p",
       (gulong) offset, (gulong) size, data);
 
-  GST_MINI_OBJECT_CLASS (mmap_buffer_parent_class)->
-      finalize (GST_MINI_OBJECT (mmap_buffer));
+  GST_MINI_OBJECT_CLASS (mmap_buffer_parent_class)->finalize (GST_MINI_OBJECT
+      (mmap_buffer));
 }
 
 static GstBuffer *
@@ -567,12 +609,11 @@
 #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 */
@@ -792,9 +833,14 @@
     src->read_position = offset;
   }
 
-  buf = gst_buffer_new_and_alloc (length);
+  buf = gst_buffer_try_new_and_alloc (length);
+  if (G_UNLIKELY (buf == NULL && length > 0)) {
+    GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", length);
+    return GST_FLOW_ERROR;
+  }
 
-  GST_LOG_OBJECT (src, "Reading %d bytes", length);
+  GST_LOG_OBJECT (src, "Reading %d bytes at offset 0x%" G_GINT64_MODIFIER "x",
+      length, offset);
   ret = read (src->fd, GST_BUFFER_DATA (buf), length);
   if (G_UNLIKELY (ret < 0))
     goto could_not_read;
@@ -869,6 +915,28 @@
 }
 
 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 gboolean
 gst_file_src_is_seekable (GstBaseSrc * basesrc)
 {
   GstFileSrc *src = GST_FILE_SRC (basesrc);
@@ -917,7 +985,8 @@
   GST_INFO_OBJECT (src, "opening file %s", src->filename);
 
   /* open the file */
-  src->fd = open (src->filename, O_RDONLY | O_BINARY);
+  src->fd = gst_open (src->filename, O_RDONLY | O_BINARY, 0);
+
   if (src->fd < 0)
     goto open_failed;
 
@@ -1039,15 +1108,17 @@
 }
 
 /*** GSTURIHANDLER INTERFACE *************************************************/
+
 #ifdef __SYMBIAN32__
 GstURIType
 #else
-static guint
+static GstURIType
 #endif 
 gst_file_src_uri_get_type (void)
 {
   return GST_URI_SRC;
 }
+
 static gchar **
 gst_file_src_uri_get_protocols (void)
 {
@@ -1055,6 +1126,7 @@
 
   return protocols;
 }
+
 static const gchar *
 gst_file_src_uri_get_uri (GstURIHandler * handler)
 {
@@ -1066,47 +1138,54 @@
 static gboolean
 gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 {
-  gchar *protocol, *location;
-  gboolean ret;
+  gchar *location, *hostname = NULL;
+  gboolean ret = FALSE;
   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);
+  GError *error = NULL;
 
-  /* 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) {
+  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);
+  }
+
+  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 (!location)
-    return FALSE;
-  if (!g_path_is_absolute (location)) {
-    g_free (location);
-    return FALSE;
+  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);
-  g_free (location);
+
+beach:
+  if (location)
+    g_free (location);
+  if (hostname)
+    g_free (hostname);
 
   return ret;
 }