LCOV - code coverage report
Current view: top level - plugins/elements - gstfilesrc.c (source / functions) Hit Total Coverage
Test: GStreamer 0.10.32.1 Lines: 195 378 51.6 %
Date: 2011-03-25 Functions: 23 31 74.2 %
Branches: 72 305 23.6 %

           Branch data     Line data    Source code
       1                 :            : /* GStreamer
       2                 :            :  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
       3                 :            :  *               2000,2005 Wim Taymans <wim@fluendo.com>
       4                 :            :  *
       5                 :            :  * gstfilesrc.c:
       6                 :            :  *
       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.
      11                 :            :  *
      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.
      16                 :            :  *
      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.
      21                 :            :  */
      22                 :            : /**
      23                 :            :  * SECTION:element-filesrc
      24                 :            :  * @see_also: #GstFileSrc
      25                 :            :  *
      26                 :            :  * Read data from a file in the local file system.
      27                 :            :  *
      28                 :            :  * <refsect2>
      29                 :            :  * <title>Example launch line</title>
      30                 :            :  * |[
      31                 :            :  * gst-launch filesrc location=song.ogg ! decodebin2 ! autoaudiosink
      32                 :            :  * ]| Play a song.ogg from local dir.
      33                 :            :  * </refsect2>
      34                 :            :  */
      35                 :            : 
      36                 :            : #ifdef HAVE_CONFIG_H
      37                 :            : #  include "config.h"
      38                 :            : #endif
      39                 :            : 
      40                 :            : #include <gst/gst.h>
      41                 :            : #include "gstfilesrc.h"
      42                 :            : 
      43                 :            : #include <stdio.h>
      44                 :            : #include <sys/types.h>
      45                 :            : #ifdef G_OS_WIN32
      46                 :            : #include <io.h>                 /* lseek, open, close, read */
      47                 :            : /* On win32, stat* default to 32 bit; we need the 64-bit
      48                 :            :  * variants, so explicitly define it that way. */
      49                 :            : #define stat __stat64
      50                 :            : #define fstat _fstat64
      51                 :            : #undef lseek
      52                 :            : #define lseek _lseeki64
      53                 :            : #undef off_t
      54                 :            : #define off_t guint64
      55                 :            : /* Prevent stat.h from defining the stat* functions as
      56                 :            :  * _stat*, since we're explicitly overriding that */
      57                 :            : #undef _INC_STAT_INL
      58                 :            : #endif
      59                 :            : #include <sys/stat.h>
      60                 :            : #include <fcntl.h>
      61                 :            : 
      62                 :            : #ifdef HAVE_UNISTD_H
      63                 :            : #  include <unistd.h>
      64                 :            : #endif
      65                 :            : 
      66                 :            : #ifdef HAVE_MMAP
      67                 :            : # include <sys/mman.h>
      68                 :            : #endif
      69                 :            : 
      70                 :            : #include <errno.h>
      71                 :            : #include <string.h>
      72                 :            : 
      73                 :            : #include "../../gst/gst-i18n-lib.h"
      74                 :            : 
      75                 :            : static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
      76                 :            :     GST_PAD_SRC,
      77                 :            :     GST_PAD_ALWAYS,
      78                 :            :     GST_STATIC_CAPS_ANY);
      79                 :            : 
      80                 :            : /* FIXME we should be using glib for this */
      81                 :            : #ifndef S_ISREG
      82                 :            : #define S_ISREG(mode) ((mode)&_S_IFREG)
      83                 :            : #endif
      84                 :            : #ifndef S_ISDIR
      85                 :            : #define S_ISDIR(mode) ((mode)&_S_IFDIR)
      86                 :            : #endif
      87                 :            : #ifndef S_ISSOCK
      88                 :            : #define S_ISSOCK(x) (0)
      89                 :            : #endif
      90                 :            : #ifndef O_BINARY
      91                 :            : #define O_BINARY (0)
      92                 :            : #endif
      93                 :            : 
      94                 :            : /* Copy of glib's g_open due to win32 libc/cross-DLL brokenness: we can't
      95                 :            :  * use the 'file descriptor' opened in glib (and returned from this function)
      96                 :            :  * in this library, as they may have unrelated C runtimes. */
      97                 :            : static int
      98                 :          4 : gst_open (const gchar * filename, int flags, int mode)
      99                 :            : {
     100                 :            : #ifdef G_OS_WIN32
     101                 :            :   wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
     102                 :            :   int retval;
     103                 :            :   int save_errno;
     104                 :            : 
     105                 :            :   if (wfilename == NULL) {
     106                 :            :     errno = EINVAL;
     107                 :            :     return -1;
     108                 :            :   }
     109                 :            : 
     110                 :            :   retval = _wopen (wfilename, flags, mode);
     111                 :            :   save_errno = errno;
     112                 :            : 
     113                 :            :   g_free (wfilename);
     114                 :            : 
     115                 :            :   errno = save_errno;
     116                 :            :   return retval;
     117                 :            : #else
     118                 :          4 :   return open (filename, flags, mode);
     119                 :            : #endif
     120                 :            : }
     121                 :            : 
     122                 :            : 
     123                 :            : /**********************************************************************
     124                 :            :  * GStreamer Default File Source
     125                 :            :  * Theory of Operation
     126                 :            :  *
     127                 :            :  * Update: see GstFileSrc:use-mmap property documentation below
     128                 :            :  *         for why use of mmap() is disabled by default.
     129                 :            :  *
     130                 :            :  * This source uses mmap(2) to efficiently load data from a file.
     131                 :            :  * To do this without seriously polluting the applications' memory
     132                 :            :  * space, it must do so in smaller chunks, say 1-4MB at a time.
     133                 :            :  * Buffers are then subdivided from these mmap'd chunks, to directly
     134                 :            :  * make use of the mmap.
     135                 :            :  *
     136                 :            :  * To handle refcounting so that the mmap can be freed at the appropriate
     137                 :            :  * time, a buffer will be created for each mmap'd region, and all new
     138                 :            :  * buffers will be sub-buffers of this top-level buffer.  As they are
     139                 :            :  * freed, the refcount goes down on the mmap'd buffer and its free()
     140                 :            :  * function is called, which will call munmap(2) on itself.
     141                 :            :  *
     142                 :            :  * If a buffer happens to cross the boundaries of an mmap'd region, we
     143                 :            :  * have to decide whether it's more efficient to copy the data into a
     144                 :            :  * new buffer, or mmap() just that buffer.  There will have to be a
     145                 :            :  * breakpoint size to determine which will be done.  The mmap() size
     146                 :            :  * has a lot to do with this as well, because you end up in double-
     147                 :            :  * jeopardy: the larger the outgoing buffer, the more data to copy when
     148                 :            :  * it overlaps, *and* the more frequently you'll have buffers that *do*
     149                 :            :  * overlap.
     150                 :            :  *
     151                 :            :  * Seeking is another tricky aspect to do efficiently.  The initial
     152                 :            :  * implementation of this source won't make use of these features, however.
     153                 :            :  * The issue is that if an application seeks backwards in a file, *and*
     154                 :            :  * that region of the file is covered by an mmap that hasn't been fully
     155                 :            :  * deallocated, we really should re-use it.  But keeping track of these
     156                 :            :  * regions is tricky because we have to lock the structure that holds
     157                 :            :  * them.  We need to settle on a locking primitive (GMutex seems to be
     158                 :            :  * a really good option...), then we can do that.
     159                 :            :  */
     160                 :            : 
     161                 :            : 
     162                 :            : GST_DEBUG_CATEGORY_STATIC (gst_file_src_debug);
     163                 :            : #define GST_CAT_DEFAULT gst_file_src_debug
     164                 :            : 
     165                 :            : /* FileSrc signals and args */
     166                 :            : enum
     167                 :            : {
     168                 :            :   /* FILL ME */
     169                 :            :   LAST_SIGNAL
     170                 :            : };
     171                 :            : 
     172                 :            : #define DEFAULT_BLOCKSIZE       4*1024
     173                 :            : #define DEFAULT_MMAPSIZE        4*1024*1024
     174                 :            : #define DEFAULT_TOUCH           TRUE
     175                 :            : #define DEFAULT_USEMMAP         FALSE
     176                 :            : #define DEFAULT_SEQUENTIAL      FALSE
     177                 :            : 
     178                 :            : enum
     179                 :            : {
     180                 :            :   ARG_0,
     181                 :            :   ARG_LOCATION,
     182                 :            :   ARG_FD,
     183                 :            :   ARG_MMAPSIZE,
     184                 :            :   ARG_SEQUENTIAL,
     185                 :            :   ARG_TOUCH,
     186                 :            :   ARG_USEMMAP
     187                 :            : };
     188                 :            : 
     189                 :            : static void gst_file_src_finalize (GObject * object);
     190                 :            : 
     191                 :            : static void gst_file_src_set_property (GObject * object, guint prop_id,
     192                 :            :     const GValue * value, GParamSpec * pspec);
     193                 :            : static void gst_file_src_get_property (GObject * object, guint prop_id,
     194                 :            :     GValue * value, GParamSpec * pspec);
     195                 :            : 
     196                 :            : static gboolean gst_file_src_start (GstBaseSrc * basesrc);
     197                 :            : static gboolean gst_file_src_stop (GstBaseSrc * basesrc);
     198                 :            : 
     199                 :            : static gboolean gst_file_src_is_seekable (GstBaseSrc * src);
     200                 :            : static gboolean gst_file_src_get_size (GstBaseSrc * src, guint64 * size);
     201                 :            : static GstFlowReturn gst_file_src_create (GstBaseSrc * src, guint64 offset,
     202                 :            :     guint length, GstBuffer ** buffer);
     203                 :            : static gboolean gst_file_src_query (GstBaseSrc * src, GstQuery * query);
     204                 :            : 
     205                 :            : static void gst_file_src_uri_handler_init (gpointer g_iface,
     206                 :            :     gpointer iface_data);
     207                 :            : 
     208                 :            : static void
     209                 :        153 : _do_init (GType filesrc_type)
     210                 :            : {
     211                 :            :   static const GInterfaceInfo urihandler_info = {
     212                 :            :     gst_file_src_uri_handler_init,
     213                 :            :     NULL,
     214                 :            :     NULL
     215                 :            :   };
     216                 :            : 
     217                 :        153 :   g_type_add_interface_static (filesrc_type, GST_TYPE_URI_HANDLER,
     218                 :            :       &urihandler_info);
     219         [ +  - ]:        153 :   GST_DEBUG_CATEGORY_INIT (gst_file_src_debug, "filesrc", 0, "filesrc element");
     220                 :        153 : }
     221                 :            : 
     222         [ +  + ]:        330 : GST_BOILERPLATE_FULL (GstFileSrc, gst_file_src, GstBaseSrc, GST_TYPE_BASE_SRC,
     223                 :        330 :     _do_init);
     224                 :            : 
     225                 :            : static void
     226                 :         21 : gst_file_src_base_init (gpointer g_class)
     227                 :            : {
     228                 :         21 :   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
     229                 :            : 
     230                 :         21 :   gst_element_class_set_details_simple (gstelement_class,
     231                 :            :       "File Source",
     232                 :            :       "Source/File",
     233                 :            :       "Read from arbitrary point in a file",
     234                 :            :       "Erik Walthinsen <omega@cse.ogi.edu>");
     235                 :         21 :   gst_element_class_add_pad_template (gstelement_class,
     236                 :            :       gst_static_pad_template_get (&srctemplate));
     237                 :         21 : }
     238                 :            : 
     239                 :            : static void
     240                 :         21 : gst_file_src_class_init (GstFileSrcClass * klass)
     241                 :            : {
     242                 :            :   GObjectClass *gobject_class;
     243                 :            :   GstBaseSrcClass *gstbasesrc_class;
     244                 :            : 
     245                 :         21 :   gobject_class = G_OBJECT_CLASS (klass);
     246                 :         21 :   gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
     247                 :            : 
     248                 :         21 :   gobject_class->set_property = gst_file_src_set_property;
     249                 :         21 :   gobject_class->get_property = gst_file_src_get_property;
     250                 :            : 
     251                 :         21 :   g_object_class_install_property (gobject_class, ARG_FD,
     252                 :            :       g_param_spec_int ("fd", "File-descriptor",
     253                 :            :           "File-descriptor for the file being mmap()d", 0, G_MAXINT, 0,
     254                 :            :           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
     255                 :         21 :   g_object_class_install_property (gobject_class, ARG_LOCATION,
     256                 :            :       g_param_spec_string ("location", "File Location",
     257                 :            :           "Location of the file to read", NULL,
     258                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
     259                 :            :           GST_PARAM_MUTABLE_READY));
     260                 :         21 :   g_object_class_install_property (gobject_class, ARG_MMAPSIZE,
     261                 :            :       g_param_spec_ulong ("mmapsize", "mmap() Block Size",
     262                 :            :           "Size in bytes of mmap()d regions", 0, G_MAXULONG, DEFAULT_MMAPSIZE,
     263                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
     264                 :            :           GST_PARAM_MUTABLE_PLAYING));
     265                 :         21 :   g_object_class_install_property (gobject_class, ARG_TOUCH,
     266                 :            :       g_param_spec_boolean ("touch", "Touch mapped region read data",
     267                 :            :           "Touch mmapped data regions to force them to be read from disk",
     268                 :            :           DEFAULT_TOUCH,
     269                 :            :           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
     270                 :            :           GST_PARAM_MUTABLE_PLAYING));
     271                 :            :   /**
     272                 :            :    * GstFileSrc:use-mmap
     273                 :            :    *
     274                 :            :    * Whether to use mmap(). Set to TRUE to force use of mmap() instead of
     275                 :            :    * read() for reading data.
     276                 :            :    *
     277                 :            :    * Use of mmap() is disabled by default since with mmap() there are a
     278                 :            :    * number of occasions where the process/application will be notified of
     279                 :            :    * read errors via a SIGBUS signal from the kernel, which will lead to
     280                 :            :    * the application being killed if not handled by the application. This
     281                 :            :    * is something that is difficult to work around for a library like
     282                 :            :    * GStreamer, hence use of mmap() is disabled by default. Said errors
     283                 :            :    * can occur for example when an external device (e.g. an external hard
     284                 :            :    * drive or a portable music player) are unplugged while in use, or when
     285                 :            :    * a CD/DVD medium cannot be be read because the medium is scratched or
     286                 :            :    * otherwise damaged.
     287                 :            :    *
     288                 :            :    **/
     289                 :         21 :   g_object_class_install_property (gobject_class, ARG_USEMMAP,
     290                 :            :       g_param_spec_boolean ("use-mmap", "Use mmap to read data",
     291                 :            :           "Whether to use mmap() instead of read()",
     292                 :            :           DEFAULT_USEMMAP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
     293                 :            :           GST_PARAM_MUTABLE_READY));
     294                 :         21 :   g_object_class_install_property (gobject_class, ARG_SEQUENTIAL,
     295                 :            :       g_param_spec_boolean ("sequential", "Optimise for sequential mmap access",
     296                 :            :           "Whether to use madvise to hint to the kernel that access to "
     297                 :            :           "mmap pages will be sequential",
     298                 :            :           DEFAULT_SEQUENTIAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
     299                 :            :           GST_PARAM_MUTABLE_PLAYING));
     300                 :            : 
     301                 :         21 :   gobject_class->finalize = gst_file_src_finalize;
     302                 :            : 
     303                 :         21 :   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_file_src_start);
     304                 :         21 :   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_file_src_stop);
     305                 :         21 :   gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_file_src_is_seekable);
     306                 :         21 :   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_file_src_get_size);
     307                 :         21 :   gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_file_src_create);
     308                 :         21 :   gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_file_src_query);
     309                 :            : 
     310                 :            :   if (sizeof (off_t) < 8) {
     311                 :            :     GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT "!",
     312                 :            :         sizeof (off_t));
     313                 :            :   }
     314                 :         21 : }
     315                 :            : 
     316                 :            : static void
     317                 :         23 : gst_file_src_init (GstFileSrc * src, GstFileSrcClass * g_class)
     318                 :            : {
     319                 :            : #ifdef HAVE_MMAP
     320                 :         23 :   src->pagesize = getpagesize ();
     321                 :            : #endif
     322                 :            : 
     323                 :         23 :   src->filename = NULL;
     324                 :         23 :   src->fd = 0;
     325                 :         23 :   src->uri = NULL;
     326                 :            : 
     327                 :         23 :   src->touch = DEFAULT_TOUCH;
     328                 :            : 
     329                 :         23 :   src->mapbuf = NULL;
     330                 :         23 :   src->mapsize = DEFAULT_MMAPSIZE;      /* default is 4MB */
     331                 :         23 :   src->use_mmap = DEFAULT_USEMMAP;
     332                 :         23 :   src->sequential = DEFAULT_SEQUENTIAL;
     333                 :            : 
     334                 :         23 :   src->is_regular = FALSE;
     335                 :         23 : }
     336                 :            : 
     337                 :            : static void
     338                 :         23 : gst_file_src_finalize (GObject * object)
     339                 :            : {
     340                 :            :   GstFileSrc *src;
     341                 :            : 
     342                 :         23 :   src = GST_FILE_SRC (object);
     343                 :            : 
     344                 :         23 :   g_free (src->filename);
     345                 :         23 :   g_free (src->uri);
     346                 :            : 
     347                 :         23 :   G_OBJECT_CLASS (parent_class)->finalize (object);
     348                 :         23 : }
     349                 :            : 
     350                 :            : static gboolean
     351                 :         32 : gst_file_src_set_location (GstFileSrc * src, const gchar * location)
     352                 :            : {
     353                 :            :   GstState state;
     354                 :            : 
     355                 :            :   /* the element must be stopped in order to do this */
     356                 :         32 :   GST_OBJECT_LOCK (src);
     357                 :         32 :   state = GST_STATE (src);
     358 [ +  + ][ -  + ]:         32 :   if (state != GST_STATE_READY && state != GST_STATE_NULL)
     359                 :          0 :     goto wrong_state;
     360                 :         32 :   GST_OBJECT_UNLOCK (src);
     361                 :            : 
     362                 :         32 :   g_free (src->filename);
     363                 :         32 :   g_free (src->uri);
     364                 :            : 
     365                 :            :   /* clear the filename if we get a NULL (is that possible?) */
     366         [ +  + ]:         32 :   if (location == NULL) {
     367                 :          1 :     src->filename = NULL;
     368                 :          1 :     src->uri = NULL;
     369                 :            :   } else {
     370                 :            :     /* we store the filename as received by the application. On Windows this
     371                 :            :      * should be UTF8 */
     372                 :         31 :     src->filename = g_strdup (location);
     373                 :         31 :     src->uri = gst_filename_to_uri (location, NULL);
     374         [ -  + ]:         31 :     GST_INFO ("filename : %s", src->filename);
     375         [ -  + ]:         31 :     GST_INFO ("uri      : %s", src->uri);
     376                 :            :   }
     377                 :         32 :   g_object_notify (G_OBJECT (src), "location");
     378                 :         32 :   gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri);
     379                 :            : 
     380                 :         32 :   return TRUE;
     381                 :            : 
     382                 :            :   /* ERROR */
     383                 :            : wrong_state:
     384                 :            :   {
     385                 :          0 :     g_warning ("Changing the `location' property on filesrc when a file is "
     386                 :            :         "open is not supported.");
     387                 :          0 :     GST_OBJECT_UNLOCK (src);
     388                 :         32 :     return FALSE;
     389                 :            :   }
     390                 :            : }
     391                 :            : 
     392                 :            : static void
     393                 :         27 : gst_file_src_set_property (GObject * object, guint prop_id,
     394                 :            :     const GValue * value, GParamSpec * pspec)
     395                 :            : {
     396                 :            :   GstFileSrc *src;
     397                 :            : 
     398 [ -  + ][ +  - ]:         54 :   g_return_if_fail (GST_IS_FILE_SRC (object));
         [ +  - ][ -  + ]
     399                 :            : 
     400                 :         27 :   src = GST_FILE_SRC (object);
     401                 :            : 
     402   [ +  -  -  -  :         27 :   switch (prop_id) {
                   -  - ]
     403                 :            :     case ARG_LOCATION:
     404                 :         27 :       gst_file_src_set_location (src, g_value_get_string (value));
     405                 :         27 :       break;
     406                 :            :     case ARG_MMAPSIZE:
     407         [ #  # ]:          0 :       if ((src->mapsize % src->pagesize) == 0) {
     408                 :          0 :         src->mapsize = g_value_get_ulong (value);
     409                 :            :       } else {
     410         [ #  # ]:          0 :         GST_INFO_OBJECT (src,
     411                 :            :             "invalid mapsize, must be a multiple of pagesize, which is %d",
     412                 :            :             src->pagesize);
     413                 :            :       }
     414                 :          0 :       break;
     415                 :            :     case ARG_TOUCH:
     416                 :          0 :       src->touch = g_value_get_boolean (value);
     417                 :          0 :       break;
     418                 :            :     case ARG_SEQUENTIAL:
     419                 :          0 :       src->sequential = g_value_get_boolean (value);
     420                 :          0 :       break;
     421                 :            :     case ARG_USEMMAP:
     422                 :          0 :       src->use_mmap = g_value_get_boolean (value);
     423                 :          0 :       break;
     424                 :            :     default:
     425                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     426                 :          0 :       break;
     427                 :            :   }
     428                 :            : }
     429                 :            : 
     430                 :            : static void
     431                 :          6 : gst_file_src_get_property (GObject * object, guint prop_id, GValue * value,
     432                 :            :     GParamSpec * pspec)
     433                 :            : {
     434                 :            :   GstFileSrc *src;
     435                 :            : 
     436 [ -  + ][ +  - ]:         12 :   g_return_if_fail (GST_IS_FILE_SRC (object));
         [ +  - ][ -  + ]
     437                 :            : 
     438                 :          6 :   src = GST_FILE_SRC (object);
     439                 :            : 
     440   [ +  -  -  -  :          6 :   switch (prop_id) {
                -  -  - ]
     441                 :            :     case ARG_LOCATION:
     442                 :          6 :       g_value_set_string (value, src->filename);
     443                 :          6 :       break;
     444                 :            :     case ARG_FD:
     445                 :          0 :       g_value_set_int (value, src->fd);
     446                 :          0 :       break;
     447                 :            :     case ARG_MMAPSIZE:
     448                 :          0 :       g_value_set_ulong (value, src->mapsize);
     449                 :          0 :       break;
     450                 :            :     case ARG_TOUCH:
     451                 :          0 :       g_value_set_boolean (value, src->touch);
     452                 :          0 :       break;
     453                 :            :     case ARG_SEQUENTIAL:
     454                 :          0 :       g_value_set_boolean (value, src->sequential);
     455                 :          0 :       break;
     456                 :            :     case ARG_USEMMAP:
     457                 :          0 :       g_value_set_boolean (value, src->use_mmap);
     458                 :          0 :       break;
     459                 :            :     default:
     460                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     461                 :          0 :       break;
     462                 :            :   }
     463                 :            : }
     464                 :            : 
     465                 :            : /***
     466                 :            :  * mmap code below
     467                 :            :  */
     468                 :            : 
     469                 :            : #ifdef HAVE_MMAP
     470                 :            : 
     471                 :            : /* GstMmapBuffer */
     472                 :            : 
     473                 :            : typedef struct _GstMmapBuffer GstMmapBuffer;
     474                 :            : typedef struct _GstMmapBufferClass GstMmapBufferClass;
     475                 :            : 
     476                 :            : #define GST_TYPE_MMAP_BUFFER                         (gst_mmap_buffer_get_type())
     477                 :            : 
     478                 :            : #define GST_IS_MMAP_BUFFER(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MMAP_BUFFER))
     479                 :            : #define GST_IS_MMAP_BUFFER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MMAP_BUFFER))
     480                 :            : #define GST_MMAP_BUFFER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass))
     481                 :            : #define GST_MMAP_BUFFER(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MMAP_BUFFER, GstMmapBuffer))
     482                 :            : #define GST_MMAP_BUFFER_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MMAP_BUFFER, GstMmapBufferClass))
     483                 :            : 
     484                 :            : 
     485                 :            : 
     486                 :            : struct _GstMmapBuffer
     487                 :            : {
     488                 :            :   GstBuffer buffer;
     489                 :            : 
     490                 :            :   GstFileSrc *filesrc;
     491                 :            : };
     492                 :            : 
     493                 :            : struct _GstMmapBufferClass
     494                 :            : {
     495                 :            :   GstBufferClass buffer_class;
     496                 :            : };
     497                 :            : 
     498                 :            : static void gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer);
     499                 :            : 
     500                 :            : GType gst_mmap_buffer_get_type (void);
     501                 :            : 
     502         [ #  # ]:          0 : G_DEFINE_TYPE (GstMmapBuffer, gst_mmap_buffer, GST_TYPE_BUFFER);
     503                 :            : 
     504                 :            : static void
     505                 :          0 : gst_mmap_buffer_class_init (GstMmapBufferClass * g_class)
     506                 :            : {
     507                 :          0 :   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
     508                 :            : 
     509                 :          0 :   mini_object_class->finalize =
     510                 :            :       (GstMiniObjectFinalizeFunction) gst_mmap_buffer_finalize;
     511                 :          0 : }
     512                 :            : 
     513                 :            : static void
     514                 :          0 : gst_mmap_buffer_init (GstMmapBuffer * buf)
     515                 :            : {
     516                 :          0 :   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
     517                 :            :   /* before we re-enable this flag, we probably need to fix _copy()
     518                 :            :    * _make_writable(), etc. in GstMiniObject/GstBuffer as well */
     519                 :            :   /* GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_ORIGINAL); */
     520                 :          0 : }
     521                 :            : 
     522                 :            : static void
     523                 :          0 : gst_mmap_buffer_finalize (GstMmapBuffer * mmap_buffer)
     524                 :            : {
     525                 :            :   guint size;
     526                 :            :   gpointer data;
     527                 :            :   guint64 offset;
     528                 :            :   GstFileSrc *src;
     529                 :          0 :   GstBuffer *buffer = GST_BUFFER (mmap_buffer);
     530                 :            : 
     531                 :            :   /* get info */
     532                 :          0 :   size = GST_BUFFER_SIZE (buffer);
     533                 :          0 :   offset = GST_BUFFER_OFFSET (buffer);
     534                 :          0 :   data = GST_BUFFER_DATA (buffer);
     535                 :          0 :   src = mmap_buffer->filesrc;
     536                 :            : 
     537         [ #  # ]:          0 :   GST_LOG ("freeing mmap()d buffer at %" G_GUINT64_FORMAT "+%u", offset, size);
     538                 :            : 
     539                 :            : #ifdef MADV_DONTNEED
     540                 :            :   /* madvise to tell the kernel what to do with it */
     541         [ #  # ]:          0 :   if (madvise (data, size, MADV_DONTNEED) < 0) {
     542         [ #  # ]:          0 :     GST_WARNING_OBJECT (src, "warning: madvise failed: %s", g_strerror (errno));
     543                 :            :   }
     544                 :            : #endif
     545                 :            : 
     546                 :            :   /* now unmap the memory */
     547         [ #  # ]:          0 :   if (munmap (data, size) < 0) {
     548         [ #  # ]:          0 :     GST_WARNING_OBJECT (src, "warning: munmap failed: %s", g_strerror (errno));
     549                 :            :   }
     550                 :            : 
     551                 :            :   /* cast to unsigned long, since there's no gportable way to print
     552                 :            :    * guint64 as hex */
     553         [ #  # ]:          0 :   GST_LOG ("unmapped region %08lx+%08lx at %p",
     554                 :            :       (gulong) offset, (gulong) size, data);
     555                 :            : 
     556                 :          0 :   GST_MINI_OBJECT_CLASS (gst_mmap_buffer_parent_class)->finalize
     557                 :          0 :       (GST_MINI_OBJECT (mmap_buffer));
     558                 :          0 : }
     559                 :            : 
     560                 :            : static GstBuffer *
     561                 :          0 : gst_file_src_map_region (GstFileSrc * src, off_t offset, gsize size,
     562                 :            :     gboolean testonly)
     563                 :            : {
     564                 :            :   GstBuffer *buf;
     565                 :            :   void *mmapregion;
     566                 :            : 
     567         [ #  # ]:          0 :   g_return_val_if_fail (offset >= 0, NULL);
     568                 :            : 
     569                 :            :   /* FIXME ? use goffset and friends if we require glib >= 2.20 */
     570         [ #  # ]:          0 :   GST_LOG_OBJECT (src, "mapping region %08" G_GINT64_MODIFIER "x+%08lx "
     571                 :            :       "from file into memory", (gint64) offset, (gulong) size);
     572                 :            : 
     573                 :          0 :   mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset);
     574                 :            : 
     575 [ #  # ][ #  # ]:          0 :   if (mmapregion == NULL || mmapregion == MAP_FAILED)
     576                 :            :     goto mmap_failed;
     577                 :            : 
     578         [ #  # ]:          0 :   GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p",
     579                 :            :       (gulong) offset, (gulong) size, mmapregion);
     580                 :            : 
     581                 :            :   /* time to allocate a new mapbuf */
     582                 :          0 :   buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_MMAP_BUFFER);
     583                 :            :   /* mmap() the data into this new buffer */
     584                 :          0 :   GST_BUFFER_DATA (buf) = mmapregion;
     585                 :          0 :   GST_MMAP_BUFFER (buf)->filesrc = src;
     586                 :            : 
     587                 :            : #ifdef MADV_SEQUENTIAL
     588         [ #  # ]:          0 :   if (src->sequential) {
     589                 :            :     /* madvise to tell the kernel what to do with it */
     590         [ #  # ]:          0 :     if (madvise (mmapregion, size, MADV_SEQUENTIAL) < 0) {
     591         [ #  # ]:          0 :       GST_WARNING_OBJECT (src, "warning: madvise failed: %s",
     592                 :            :           g_strerror (errno));
     593                 :            :     }
     594                 :            :   }
     595                 :            : #endif
     596                 :            : 
     597                 :            :   /* fill in the rest of the fields */
     598                 :          0 :   GST_BUFFER_SIZE (buf) = size;
     599                 :          0 :   GST_BUFFER_OFFSET (buf) = offset;
     600                 :          0 :   GST_BUFFER_OFFSET_END (buf) = offset + size;
     601                 :          0 :   GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
     602                 :            : 
     603                 :          0 :   return buf;
     604                 :            : 
     605                 :            :   /* ERROR */
     606                 :            : mmap_failed:
     607                 :            :   {
     608         [ #  # ]:          0 :     if (!testonly) {
     609 [ #  # ][ #  # ]:          0 :       GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
         [ #  # ][ #  # ]
     610                 :            :           ("mmap (0x%08lx, %d, 0x%" G_GINT64_MODIFIER "x) failed: %s",
     611                 :            :               (gulong) size, src->fd, (guint64) offset, g_strerror (errno)));
     612                 :            :     }
     613                 :          0 :     return NULL;
     614                 :            :   }
     615                 :            : }
     616                 :            : 
     617                 :            : static GstBuffer *
     618                 :          0 : gst_file_src_map_small_region (GstFileSrc * src, off_t offset, gsize size)
     619                 :            : {
     620                 :            :   GstBuffer *ret;
     621                 :            :   off_t mod;
     622                 :            :   guint pagesize;
     623                 :            : 
     624         [ #  # ]:          0 :   GST_LOG_OBJECT (src,
     625                 :            :       "attempting to map a small buffer at %" G_GUINT64_FORMAT "+%d",
     626                 :            :       (guint64) offset, (gint) size);
     627                 :            : 
     628                 :          0 :   pagesize = src->pagesize;
     629                 :            : 
     630                 :          0 :   mod = offset % pagesize;
     631                 :            : 
     632                 :            :   /* if the offset starts at a non-page boundary, we have to special case */
     633         [ #  # ]:          0 :   if (mod != 0) {
     634                 :            :     gsize mapsize;
     635                 :            :     off_t mapbase;
     636                 :            :     GstBuffer *map;
     637                 :            : 
     638                 :          0 :     mapbase = offset - mod;
     639                 :          0 :     mapsize = ((size + mod + pagesize - 1) / pagesize) * pagesize;
     640                 :            : 
     641         [ #  # ]:          0 :     GST_LOG_OBJECT (src,
     642                 :            :         "not on page boundaries, resizing to map to %" G_GUINT64_FORMAT "+%d",
     643                 :            :         (guint64) mapbase, (gint) mapsize);
     644                 :            : 
     645                 :          0 :     map = gst_file_src_map_region (src, mapbase, mapsize, FALSE);
     646         [ #  # ]:          0 :     if (map == NULL)
     647                 :          0 :       return NULL;
     648                 :            : 
     649                 :          0 :     ret = gst_buffer_create_sub (map, offset - mapbase, size);
     650                 :          0 :     GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET (map) + offset - mapbase;
     651                 :            : 
     652                 :          0 :     gst_buffer_unref (map);
     653                 :            :   } else {
     654                 :          0 :     ret = gst_file_src_map_region (src, offset, size, FALSE);
     655                 :            :   }
     656                 :            : 
     657                 :          0 :   return ret;
     658                 :            : }
     659                 :            : 
     660                 :            : static GstFlowReturn
     661                 :          0 : gst_file_src_create_mmap (GstFileSrc * src, guint64 offset, guint length,
     662                 :            :     GstBuffer ** buffer)
     663                 :            : {
     664                 :          0 :   GstBuffer *buf = NULL;
     665                 :            :   gsize readsize, mapsize;
     666                 :            :   off_t readend, mapstart, mapend;
     667                 :            :   int i;
     668                 :            : 
     669                 :            :   /* calculate end pointers so we don't have to do so repeatedly later */
     670                 :          0 :   readsize = length;
     671                 :          0 :   readend = offset + readsize;  /* note this is the byte *after* the read */
     672                 :            : 
     673                 :          0 :   mapstart = GST_BUFFER_OFFSET (src->mapbuf);
     674                 :          0 :   mapsize = GST_BUFFER_SIZE (src->mapbuf);
     675                 :          0 :   mapend = mapstart + mapsize;  /* note this is the byte *after* the map */
     676                 :            : 
     677         [ #  # ]:          0 :   GST_LOG ("attempting to read %08lx, %08lx, %08lx, %08lx",
     678                 :            :       (unsigned long) readsize, (unsigned long) readend,
     679                 :            :       (unsigned long) mapstart, (unsigned long) mapend);
     680                 :            : 
     681                 :            :   /* if the start is past the mapstart */
     682         [ #  # ]:          0 :   if (offset >= mapstart) {
     683                 :            :     /* if the end is before the mapend, the buffer is in current mmap region... */
     684                 :            :     /* ('cause by definition if readend is in the buffer, so's readstart) */
     685         [ #  # ]:          0 :     if (readend <= mapend) {
     686         [ #  # ]:          0 :       GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u lives in "
     687                 :            :           "current mapbuf %u+%u, creating subbuffer of mapbuf",
     688                 :            :           offset, (guint) readsize, (guint) mapstart, (guint) mapsize);
     689                 :          0 :       buf = gst_buffer_create_sub (src->mapbuf, offset - mapstart, readsize);
     690                 :          0 :       GST_BUFFER_OFFSET (buf) = offset;
     691                 :            : 
     692                 :            :       /* if the start actually is within the current mmap region, map an overlap buffer */
     693         [ #  # ]:          0 :     } else if (offset < mapend) {
     694         [ #  # ]:          0 :       GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%u starts in "
     695                 :            :           "mapbuf %u+%u but ends outside, creating new mmap",
     696                 :            :           offset, (guint) readsize, (guint) mapstart, (guint) mapsize);
     697                 :          0 :       buf = gst_file_src_map_small_region (src, offset, readsize);
     698         [ #  # ]:          0 :       if (buf == NULL)
     699                 :          0 :         goto could_not_mmap;
     700                 :            :     }
     701                 :            : 
     702                 :            :     /* the only other option is that buffer is totally outside, which means we search for it */
     703                 :            : 
     704                 :            :     /* now we can assume that the start is *before* the current mmap region */
     705                 :            :     /* if the readend is past mapstart, we have two options */
     706         [ #  # ]:          0 :   } else if (readend >= mapstart) {
     707                 :            :     /* either the read buffer overlaps the start of the mmap region */
     708                 :            :     /* or the read buffer fully contains the current mmap region    */
     709                 :            :     /* either way, it's really not relevant, we just create a new region anyway */
     710         [ #  # ]:          0 :     GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d starts before "
     711                 :            :         "mapbuf %d+%d, but overlaps it", (guint64) offset, (gint) readsize,
     712                 :            :         (gint) mapstart, (gint) mapsize);
     713                 :          0 :     buf = gst_file_src_map_small_region (src, offset, readsize);
     714         [ #  # ]:          0 :     if (buf == NULL)
     715                 :          0 :       goto could_not_mmap;
     716                 :            :   }
     717                 :            : 
     718                 :            :   /* then deal with the case where the read buffer is totally outside */
     719         [ #  # ]:          0 :   if (buf == NULL) {
     720                 :            :     /* first check to see if there's a map that covers the right region already */
     721         [ #  # ]:          0 :     GST_LOG_OBJECT (src, "searching for mapbuf to cover %" G_GUINT64_FORMAT
     722                 :            :         "+%d", offset, (int) readsize);
     723                 :            : 
     724                 :            :     /* if the read buffer crosses a mmap region boundary, create a one-off region */
     725         [ #  # ]:          0 :     if ((offset / src->mapsize) != (readend / src->mapsize)) {
     726         [ #  # ]:          0 :       GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d crosses a "
     727                 :            :           "%d-byte boundary, creating a one-off", offset, (int) readsize,
     728                 :            :           (int) src->mapsize);
     729                 :          0 :       buf = gst_file_src_map_small_region (src, offset, readsize);
     730         [ #  # ]:          0 :       if (buf == NULL)
     731                 :          0 :         goto could_not_mmap;
     732                 :            : 
     733                 :            :       /* otherwise we will create a new mmap region and set it to the default */
     734                 :            :     } else {
     735                 :            :       gsize mapsize;
     736                 :            : 
     737                 :          0 :       off_t nextmap = offset - (offset % src->mapsize);
     738                 :            : 
     739         [ #  # ]:          0 :       GST_LOG_OBJECT (src, "read buf %" G_GUINT64_FORMAT "+%d in new mapbuf "
     740                 :            :           "at %" G_GUINT64_FORMAT "+%d, mapping and subbuffering",
     741                 :            :           offset, (gint) readsize, (guint64) nextmap, (gint) src->mapsize);
     742                 :            :       /* first, we're done with the old mapbuf */
     743                 :          0 :       gst_buffer_unref (src->mapbuf);
     744                 :          0 :       mapsize = src->mapsize;
     745                 :            : 
     746                 :            :       /* double the mapsize as long as the readsize is smaller */
     747         [ #  # ]:          0 :       while (readsize + offset > nextmap + mapsize) {
     748         [ #  # ]:          0 :         GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d",
     749                 :            :             (guint) readsize, (gint) mapsize);
     750                 :          0 :         mapsize <<= 1;
     751                 :            :       }
     752                 :            :       /* create a new one */
     753                 :          0 :       src->mapbuf = gst_file_src_map_region (src, nextmap, mapsize, FALSE);
     754         [ #  # ]:          0 :       if (src->mapbuf == NULL)
     755                 :          0 :         goto could_not_mmap;
     756                 :            : 
     757                 :            :       /* subbuffer it */
     758                 :          0 :       buf = gst_buffer_create_sub (src->mapbuf, offset - nextmap, readsize);
     759                 :          0 :       GST_BUFFER_OFFSET (buf) =
     760                 :          0 :           GST_BUFFER_OFFSET (src->mapbuf) + offset - nextmap;
     761                 :            :     }
     762                 :            :   }
     763                 :            : 
     764                 :            :   /* if we need to touch the buffer (to bring it into memory), do so */
     765         [ #  # ]:          0 :   if (src->touch) {
     766                 :          0 :     volatile guchar *p = GST_BUFFER_DATA (buf);
     767                 :            : 
     768                 :            :     /* read first byte of each page */
     769         [ #  # ]:          0 :     for (i = 0; i < GST_BUFFER_SIZE (buf); i += src->pagesize)
     770                 :          0 :       (void) p[i];
     771                 :            :   }
     772                 :            : 
     773                 :            :   /* we're done, return the buffer */
     774                 :          0 :   *buffer = buf;
     775                 :            : 
     776                 :          0 :   return GST_FLOW_OK;
     777                 :            : 
     778                 :            :   /* ERROR */
     779                 :            : could_not_mmap:
     780                 :            :   {
     781                 :          0 :     return GST_FLOW_ERROR;
     782                 :            :   }
     783                 :            : }
     784                 :            : #endif
     785                 :            : 
     786                 :            : /***
     787                 :            :  * read code below
     788                 :            :  * that is to say, you shouldn't read the code below, but the code that reads
     789                 :            :  * stuff is below.  Well, you shouldn't not read the code below, feel free
     790                 :            :  * to read it of course.  It's just that "read code below" is a pretty crappy
     791                 :            :  * documentation string because it sounds like we're expecting you to read
     792                 :            :  * the code to understand what it does, which, while true, is really not
     793                 :            :  * the sort of attitude we want to be advertising.  No sir.
     794                 :            :  *
     795                 :            :  */
     796                 :            : 
     797                 :            : static GstFlowReturn
     798                 :         15 : gst_file_src_create_read (GstFileSrc * src, guint64 offset, guint length,
     799                 :            :     GstBuffer ** buffer)
     800                 :            : {
     801                 :            :   int ret;
     802                 :            :   GstBuffer *buf;
     803                 :            : 
     804         [ +  + ]:         15 :   if (G_UNLIKELY (src->read_position != offset)) {
     805                 :            :     off_t res;
     806                 :            : 
     807                 :         12 :     res = lseek (src->fd, offset, SEEK_SET);
     808 [ +  - ][ +  - ]:         12 :     if (G_UNLIKELY (res < 0 || res != offset))
     809                 :            :       goto seek_failed;
     810                 :            : 
     811                 :         12 :     src->read_position = offset;
     812                 :            :   }
     813                 :            : 
     814                 :         15 :   buf = gst_buffer_try_new_and_alloc (length);
     815 [ -  + ][ #  # ]:         15 :   if (G_UNLIKELY (buf == NULL && length > 0)) {
     816         [ #  # ]:          0 :     GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", length);
     817                 :          0 :     return GST_FLOW_ERROR;
     818                 :            :   }
     819                 :            : 
     820                 :            :   /* No need to read anything if length is 0 */
     821         [ +  + ]:         15 :   if (length > 0) {
     822         [ -  + ]:         14 :     GST_LOG_OBJECT (src, "Reading %d bytes at offset 0x%" G_GINT64_MODIFIER "x",
     823                 :            :         length, offset);
     824                 :         14 :     ret = read (src->fd, GST_BUFFER_DATA (buf), length);
     825         [ -  + ]:         14 :     if (G_UNLIKELY (ret < 0))
     826                 :          0 :       goto could_not_read;
     827                 :            : 
     828                 :            :     /* seekable regular files should have given us what we expected */
     829 [ -  + ][ #  # ]:         14 :     if (G_UNLIKELY ((guint) ret < length && src->seekable))
     830                 :          0 :       goto unexpected_eos;
     831                 :            : 
     832                 :            :     /* other files should eos if they read 0 and more was requested */
     833 [ -  + ][ #  # ]:         14 :     if (G_UNLIKELY (ret == 0 && length > 0))
     834                 :          0 :       goto eos;
     835                 :            : 
     836                 :         14 :     length = ret;
     837                 :         14 :     GST_BUFFER_SIZE (buf) = length;
     838                 :         14 :     GST_BUFFER_OFFSET (buf) = offset;
     839                 :         14 :     GST_BUFFER_OFFSET_END (buf) = offset + length;
     840                 :            : 
     841                 :         14 :     src->read_position += length;
     842                 :            :   }
     843                 :            : 
     844                 :         15 :   *buffer = buf;
     845                 :            : 
     846                 :         15 :   return GST_FLOW_OK;
     847                 :            : 
     848                 :            :   /* ERROR */
     849                 :            : seek_failed:
     850                 :            :   {
     851 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
         [ #  # ][ #  # ]
     852                 :          0 :     return GST_FLOW_ERROR;
     853                 :            :   }
     854                 :            : could_not_read:
     855                 :            :   {
     856 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
         [ #  # ][ #  # ]
     857                 :          0 :     gst_buffer_unref (buf);
     858                 :          0 :     return GST_FLOW_ERROR;
     859                 :            :   }
     860                 :            : unexpected_eos:
     861                 :            :   {
     862 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
         [ #  # ][ #  # ]
     863                 :            :         ("unexpected end of file."));
     864                 :          0 :     gst_buffer_unref (buf);
     865                 :          0 :     return GST_FLOW_ERROR;
     866                 :            :   }
     867                 :            : eos:
     868                 :            :   {
     869         [ #  # ]:          0 :     GST_DEBUG ("non-regular file hits EOS");
     870                 :          0 :     gst_buffer_unref (buf);
     871                 :         15 :     return GST_FLOW_UNEXPECTED;
     872                 :            :   }
     873                 :            : }
     874                 :            : 
     875                 :            : static GstFlowReturn
     876                 :         15 : gst_file_src_create (GstBaseSrc * basesrc, guint64 offset, guint length,
     877                 :            :     GstBuffer ** buffer)
     878                 :            : {
     879                 :            :   GstFileSrc *src;
     880                 :            :   GstFlowReturn ret;
     881                 :            : 
     882                 :         15 :   src = GST_FILE_SRC_CAST (basesrc);
     883                 :            : 
     884                 :            : #ifdef HAVE_MMAP
     885         [ -  + ]:         15 :   if (src->using_mmap) {
     886                 :          0 :     ret = gst_file_src_create_mmap (src, offset, length, buffer);
     887                 :            :   } else {
     888                 :         15 :     ret = gst_file_src_create_read (src, offset, length, buffer);
     889                 :            :   }
     890                 :            : #else
     891                 :            :   ret = gst_file_src_create_read (src, offset, length, buffer);
     892                 :            : #endif
     893                 :            : 
     894                 :         15 :   return ret;
     895                 :            : }
     896                 :            : 
     897                 :            : static gboolean
     898                 :         16 : gst_file_src_query (GstBaseSrc * basesrc, GstQuery * query)
     899                 :            : {
     900                 :         16 :   gboolean ret = FALSE;
     901                 :         16 :   GstFileSrc *src = GST_FILE_SRC (basesrc);
     902                 :            : 
     903         [ +  + ]:         16 :   switch (GST_QUERY_TYPE (query)) {
     904                 :            :     case GST_QUERY_URI:
     905                 :         14 :       gst_query_set_uri (query, src->uri);
     906                 :         14 :       ret = TRUE;
     907                 :         14 :       break;
     908                 :            :     default:
     909                 :          2 :       ret = FALSE;
     910                 :          2 :       break;
     911                 :            :   }
     912                 :            : 
     913         [ +  + ]:         16 :   if (!ret)
     914                 :          2 :     ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
     915                 :            : 
     916                 :         16 :   return ret;
     917                 :            : }
     918                 :            : 
     919                 :            : static gboolean
     920                 :          5 : gst_file_src_is_seekable (GstBaseSrc * basesrc)
     921                 :            : {
     922                 :          5 :   GstFileSrc *src = GST_FILE_SRC (basesrc);
     923                 :            : 
     924                 :          5 :   return src->seekable;
     925                 :            : }
     926                 :            : 
     927                 :            : static gboolean
     928                 :         11 : gst_file_src_get_size (GstBaseSrc * basesrc, guint64 * size)
     929                 :            : {
     930                 :            :   struct stat stat_results;
     931                 :            :   GstFileSrc *src;
     932                 :            : 
     933                 :         11 :   src = GST_FILE_SRC (basesrc);
     934                 :            : 
     935         [ -  + ]:         11 :   if (!src->seekable) {
     936                 :            :     /* If it isn't seekable, we won't know the length (but fstat will still
     937                 :            :      * succeed, and wrongly say our length is zero. */
     938                 :          0 :     return FALSE;
     939                 :            :   }
     940                 :            : 
     941         [ -  + ]:         11 :   if (fstat (src->fd, &stat_results) < 0)
     942                 :          0 :     goto could_not_stat;
     943                 :            : 
     944                 :         11 :   *size = stat_results.st_size;
     945                 :            : 
     946                 :         11 :   return TRUE;
     947                 :            : 
     948                 :            :   /* ERROR */
     949                 :            : could_not_stat:
     950                 :            :   {
     951                 :         11 :     return FALSE;
     952                 :            :   }
     953                 :            : }
     954                 :            : 
     955                 :            : /* open the file and mmap it, necessary to go to READY state */
     956                 :            : static gboolean
     957                 :         22 : gst_file_src_start (GstBaseSrc * basesrc)
     958                 :            : {
     959                 :         22 :   GstFileSrc *src = GST_FILE_SRC (basesrc);
     960                 :            :   struct stat stat_results;
     961                 :            : 
     962 [ +  + ][ +  - ]:         22 :   if (src->filename == NULL || src->filename[0] == '\0')
     963                 :            :     goto no_filename;
     964                 :            : 
     965         [ -  + ]:          4 :   GST_INFO_OBJECT (src, "opening file %s", src->filename);
     966                 :            : 
     967                 :            :   /* open the file */
     968                 :          4 :   src->fd = gst_open (src->filename, O_RDONLY | O_BINARY, 0);
     969                 :            : 
     970         [ +  + ]:          4 :   if (src->fd < 0)
     971                 :          1 :     goto open_failed;
     972                 :            : 
     973                 :            :   /* check if it is a regular file, otherwise bail out */
     974         [ -  + ]:          3 :   if (fstat (src->fd, &stat_results) < 0)
     975                 :          0 :     goto no_stat;
     976                 :            : 
     977         [ -  + ]:          3 :   if (S_ISDIR (stat_results.st_mode))
     978                 :          0 :     goto was_directory;
     979                 :            : 
     980         [ -  + ]:          3 :   if (S_ISSOCK (stat_results.st_mode))
     981                 :          0 :     goto was_socket;
     982                 :            : 
     983                 :          3 :   src->using_mmap = FALSE;
     984                 :          3 :   src->read_position = 0;
     985                 :            : 
     986                 :            :   /* record if it's a regular (hence seekable and lengthable) file */
     987         [ +  - ]:          3 :   if (S_ISREG (stat_results.st_mode))
     988                 :          3 :     src->is_regular = TRUE;
     989                 :            : 
     990                 :            : #ifdef HAVE_MMAP
     991         [ -  + ]:          3 :   if (src->use_mmap) {
     992                 :            :     /* FIXME: maybe we should only try to mmap if it's a regular file */
     993                 :            :     /* allocate the first mmap'd region if it's a regular file ? */
     994                 :          0 :     src->mapbuf = gst_file_src_map_region (src, 0, src->mapsize, TRUE);
     995         [ #  # ]:          0 :     if (src->mapbuf != NULL) {
     996         [ #  # ]:          0 :       GST_DEBUG_OBJECT (src, "using mmap for file");
     997                 :          0 :       src->using_mmap = TRUE;
     998                 :          0 :       src->seekable = TRUE;
     999                 :            :     }
    1000                 :            :   }
    1001         [ +  - ]:          3 :   if (src->mapbuf == NULL)
    1002                 :            : #endif
    1003                 :            :   {
    1004                 :            :     /* If not in mmap mode, we need to check if the underlying file is
    1005                 :            :      * seekable. */
    1006                 :          3 :     off_t res = lseek (src->fd, 0, SEEK_END);
    1007                 :            : 
    1008         [ -  + ]:          3 :     if (res < 0) {
    1009         [ #  # ]:          0 :       GST_LOG_OBJECT (src, "disabling seeking, not in mmap mode and lseek "
    1010                 :            :           "failed: %s", g_strerror (errno));
    1011                 :          0 :       src->seekable = FALSE;
    1012                 :            :     } else {
    1013                 :          3 :       src->seekable = TRUE;
    1014                 :            :     }
    1015                 :          3 :     lseek (src->fd, 0, SEEK_SET);
    1016                 :            :   }
    1017                 :            : 
    1018                 :            :   /* We can only really do seeking on regular files - for other file types, we
    1019                 :            :    * don't know their length, so seeking isn't useful/meaningful */
    1020 [ +  - ][ +  - ]:          3 :   src->seekable = src->seekable && src->is_regular;
    1021                 :            : 
    1022                 :          3 :   return TRUE;
    1023                 :            : 
    1024                 :            :   /* ERROR */
    1025                 :            : no_filename:
    1026                 :            :   {
    1027 [ +  - ][ -  + ]:         18 :     GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
         [ -  + ][ #  # ]
    1028                 :            :         (_("No file name specified for reading.")), (NULL));
    1029                 :         18 :     return FALSE;
    1030                 :            :   }
    1031                 :            : open_failed:
    1032                 :            :   {
    1033         [ +  - ]:          1 :     switch (errno) {
    1034                 :            :       case ENOENT:
    1035 [ -  + ][ #  # ]:          1 :         GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
         [ +  - ][ -  + ]
    1036                 :            :             ("No such file \"%s\"", src->filename));
    1037                 :          1 :         break;
    1038                 :            :       default:
    1039 [ #  # ][ #  # ]:          0 :         GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1040                 :            :             (_("Could not open file \"%s\" for reading."), src->filename),
    1041                 :            :             GST_ERROR_SYSTEM);
    1042                 :          0 :         break;
    1043                 :            :     }
    1044                 :          1 :     return FALSE;
    1045                 :            :   }
    1046                 :            : no_stat:
    1047                 :            :   {
    1048 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1049                 :            :         (_("Could not get info on \"%s\"."), src->filename), (NULL));
    1050                 :          0 :     close (src->fd);
    1051                 :          0 :     return FALSE;
    1052                 :            :   }
    1053                 :            : was_directory:
    1054                 :            :   {
    1055 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1056                 :            :         (_("\"%s\" is a directory."), src->filename), (NULL));
    1057                 :          0 :     close (src->fd);
    1058                 :          0 :     return FALSE;
    1059                 :            :   }
    1060                 :            : was_socket:
    1061                 :            :   {
    1062 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
         [ #  # ][ #  # ]
    1063                 :            :         (_("File \"%s\" is a socket."), src->filename), (NULL));
    1064                 :          0 :     close (src->fd);
    1065                 :         22 :     return FALSE;
    1066                 :            :   }
    1067                 :            : }
    1068                 :            : 
    1069                 :            : /* unmap and close the file */
    1070                 :            : static gboolean
    1071                 :          3 : gst_file_src_stop (GstBaseSrc * basesrc)
    1072                 :            : {
    1073                 :          3 :   GstFileSrc *src = GST_FILE_SRC (basesrc);
    1074                 :            : 
    1075                 :            :   /* close the file */
    1076                 :          3 :   close (src->fd);
    1077                 :            : 
    1078                 :            :   /* zero out a lot of our state */
    1079                 :          3 :   src->fd = 0;
    1080                 :          3 :   src->is_regular = FALSE;
    1081                 :            : 
    1082         [ -  + ]:          3 :   if (src->mapbuf) {
    1083                 :          0 :     gst_buffer_unref (src->mapbuf);
    1084                 :          0 :     src->mapbuf = NULL;
    1085                 :            :   }
    1086                 :            : 
    1087                 :          3 :   return TRUE;
    1088                 :            : }
    1089                 :            : 
    1090                 :            : /*** GSTURIHANDLER INTERFACE *************************************************/
    1091                 :            : 
    1092                 :            : static GstURIType
    1093                 :          4 : gst_file_src_uri_get_type (void)
    1094                 :            : {
    1095                 :          4 :   return GST_URI_SRC;
    1096                 :            : }
    1097                 :            : 
    1098                 :            : static gchar **
    1099                 :          4 : gst_file_src_uri_get_protocols (void)
    1100                 :            : {
    1101                 :            :   static gchar *protocols[] = { (char *) "file", NULL };
    1102                 :            : 
    1103                 :          4 :   return protocols;
    1104                 :            : }
    1105                 :            : 
    1106                 :            : static const gchar *
    1107                 :          4 : gst_file_src_uri_get_uri (GstURIHandler * handler)
    1108                 :            : {
    1109                 :          4 :   GstFileSrc *src = GST_FILE_SRC (handler);
    1110                 :            : 
    1111                 :          4 :   return src->uri;
    1112                 :            : }
    1113                 :            : 
    1114                 :            : static gboolean
    1115                 :          6 : gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
    1116                 :            : {
    1117                 :          6 :   gchar *location, *hostname = NULL;
    1118                 :          6 :   gboolean ret = FALSE;
    1119                 :          6 :   GstFileSrc *src = GST_FILE_SRC (handler);
    1120                 :          6 :   GError *error = NULL;
    1121                 :            : 
    1122         [ -  + ]:          6 :   if (strcmp (uri, "file://") == 0) {
    1123                 :            :     /* Special case for "file://" as this is used by some applications
    1124                 :            :      *  to test with gst_element_make_from_uri if there's an element
    1125                 :            :      *  that supports the URI protocol. */
    1126                 :          0 :     gst_file_src_set_location (src, NULL);
    1127                 :          0 :     return TRUE;
    1128                 :            :   }
    1129                 :            : 
    1130                 :          6 :   location = g_filename_from_uri (uri, &hostname, &error);
    1131                 :            : 
    1132 [ +  - ][ -  + ]:          6 :   if (!location || error) {
    1133         [ #  # ]:          0 :     if (error) {
    1134         [ #  # ]:          0 :       GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc: %s", uri,
    1135                 :            :           error->message);
    1136                 :          0 :       g_error_free (error);
    1137                 :            :     } else {
    1138         [ #  # ]:          0 :       GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc", uri);
    1139                 :            :     }
    1140                 :          0 :     goto beach;
    1141                 :            :   }
    1142                 :            : 
    1143 [ +  + ][ +  + ]:          6 :   if ((hostname) && (strcmp (hostname, "localhost"))) {
    1144                 :            :     /* Only 'localhost' is permitted */
    1145         [ -  + ]:          1 :     GST_WARNING_OBJECT (src, "Invalid hostname '%s' for filesrc", hostname);
    1146                 :          1 :     goto beach;
    1147                 :            :   }
    1148                 :            : #ifdef G_OS_WIN32
    1149                 :            :   /* Unfortunately, g_filename_from_uri() doesn't handle some UNC paths
    1150                 :            :    * correctly on windows, it leaves them with an extra backslash
    1151                 :            :    * at the start if they're of the mozilla-style file://///host/path/file 
    1152                 :            :    * form. Correct this.
    1153                 :            :    */
    1154                 :            :   if (location[0] == '\\' && location[1] == '\\' && location[2] == '\\')
    1155                 :            :     g_memmove (location, location + 1, strlen (location + 1) + 1);
    1156                 :            : #endif
    1157                 :            : 
    1158                 :          5 :   ret = gst_file_src_set_location (src, location);
    1159                 :            : 
    1160                 :            : beach:
    1161         [ +  - ]:          6 :   if (location)
    1162                 :          6 :     g_free (location);
    1163         [ +  + ]:          6 :   if (hostname)
    1164                 :          2 :     g_free (hostname);
    1165                 :            : 
    1166                 :          6 :   return ret;
    1167                 :            : }
    1168                 :            : 
    1169                 :            : static void
    1170                 :         21 : gst_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
    1171                 :            : {
    1172                 :         21 :   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
    1173                 :            : 
    1174                 :         21 :   iface->get_type = gst_file_src_uri_get_type;
    1175                 :         21 :   iface->get_protocols = gst_file_src_uri_get_protocols;
    1176                 :         21 :   iface->get_uri = gst_file_src_uri_get_uri;
    1177                 :         21 :   iface->set_uri = gst_file_src_uri_set_uri;
    1178                 :         21 : }

Generated by: LCOV version 1.9