gst_plugins_base/ext/gnomevfs/gstgnomevfssrc.c
branchRCL_3
changeset 30 7e817e7e631c
parent 0 0e761a78d257
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
       
     1 /* GStreamer
       
     2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
       
     3  *                    2000 Wim Taymans <wtay@chello.be>
       
     4  *                    2001 Bastien Nocera <hadess@hadess.net>
       
     5  *                    2002 Kristian Rietveld <kris@gtk.org>
       
     6  *                    2002,2003 Colin Walters <walters@gnu.org>
       
     7  *
       
     8  * gnomevfssrc.c:
       
     9  *
       
    10  * This library is free software; you can redistribute it and/or
       
    11  * modify it under the terms of the GNU Library General Public
       
    12  * License as published by the Free Software Foundation; either
       
    13  * version 2 of the License, or (at your option) any later version.
       
    14  *
       
    15  * This library is distributed in the hope that it will be useful,
       
    16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    18  * Library General Public License for more details.
       
    19  *
       
    20  * You should have received a copy of the GNU Library General Public
       
    21  * License along with this library; if not, write to the
       
    22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    23  * Boston, MA 02111-1307, USA.
       
    24  */
       
    25 
       
    26 /**
       
    27  * SECTION:element-gnomevfssrc
       
    28  * @short_description: Read from any GnomeVFS-supported location
       
    29  * @see_also: #GstFileSrc, #GstGnomeVFSSink
       
    30  *
       
    31  * <refsect2>
       
    32  * <para>
       
    33  * This plugin reads data from a local or remote location specified
       
    34  * by an URI. This location can be specified using any protocol supported by
       
    35  * the GnomeVFS library. Common protocols are 'file', 'http', 'ftp', or 'smb'.
       
    36  * </para>
       
    37  * <para>
       
    38  * In case the element-gnomevfssrc::iradio-mode property is set and the
       
    39  * location is a http resource, gnomevfssrc will send special icecast http
       
    40  * headers to the server to request additional icecast metainformation. If
       
    41  * the server is not an icecast server, it will display the same behaviour
       
    42  * as if the element-gnomevfssrc::iradio-mode property was not set. However,
       
    43  * if the server is in fact an icecast server, gnomevfssrc will output
       
    44  * data with a media type of application/x-icy, in which case you will
       
    45  * need to use the #ICYDemux element as follow-up element to extract
       
    46  * the icecast meta data and to determine the underlying media type.
       
    47  * </para>
       
    48  * <para>
       
    49  * Example pipeline:
       
    50  * <programlisting>
       
    51  * gst-launch -v gnomevfssrc location=file:///home/joe/foo.xyz ! fakesink
       
    52  * </programlisting>
       
    53  * The above pipeline will simply read a local file and do nothing with the
       
    54  * data read. Instead of gnomevfssrc, we could just as well have used the
       
    55  * filesrc element here.
       
    56  * </para>
       
    57  * <para>
       
    58  * Another example pipeline:
       
    59  * <programlisting>
       
    60  * gst-launch -v gnomevfssrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz
       
    61  * </programlisting>
       
    62  * The above pipeline will copy a file from a remote host to the local file
       
    63  * system using the Samba protocol.
       
    64  * </para>
       
    65  * <para>
       
    66  * Yet another example pipeline:
       
    67  * <programlisting>
       
    68  * gst-launch -v gnomevfssrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink
       
    69  * </programlisting>
       
    70  * The above pipeline will read and decode and play an mp3 file from a
       
    71  * web server using the http protocol.
       
    72  * </para>
       
    73  * </refsect2>
       
    74  *
       
    75  */
       
    76 
       
    77 
       
    78 #define BROKEN_SIG 1
       
    79 /*#undef BROKEN_SIG */
       
    80 
       
    81 #ifdef HAVE_CONFIG_H
       
    82 #include "config.h"
       
    83 #endif
       
    84 
       
    85 #include "gst/gst-i18n-plugin.h"
       
    86 
       
    87 #include "gstgnomevfssrc.h"
       
    88 
       
    89 #include <stdlib.h>
       
    90 #include <sys/types.h>
       
    91 #include <sys/socket.h>
       
    92 #include <sys/time.h>
       
    93 #include <netinet/in.h>
       
    94 #include <arpa/inet.h>
       
    95 #include <netdb.h>
       
    96 #include <sys/stat.h>
       
    97 #include <fcntl.h>
       
    98 #include <unistd.h>
       
    99 #include <sys/mman.h>
       
   100 #include <errno.h>
       
   101 #include <string.h>
       
   102 
       
   103 #include <gst/gst.h>
       
   104 #include <gst/tag/tag.h>
       
   105 
       
   106 /* gnome-vfs.h doesn't include the following header, which we need: */
       
   107 #include <libgnomevfs/gnome-vfs-standard-callbacks.h>
       
   108 
       
   109 GST_DEBUG_CATEGORY_STATIC (gnomevfssrc_debug);
       
   110 #define GST_CAT_DEFAULT gnomevfssrc_debug
       
   111 
       
   112 static const GstElementDetails gst_gnome_vfs_src_details =
       
   113 GST_ELEMENT_DETAILS ("GnomeVFS Source",
       
   114     "Source/File",
       
   115     "Read from any GnomeVFS-supported file",
       
   116     "Bastien Nocera <hadess@hadess.net>\n"
       
   117     "Ronald S. Bultje <rbultje@ronald.bitfreak.net>");
       
   118 
       
   119 static GStaticMutex count_lock = G_STATIC_MUTEX_INIT;
       
   120 static gint ref_count = 0;
       
   121 static gboolean vfs_owner = FALSE;
       
   122 
       
   123 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
       
   124     GST_PAD_SRC,
       
   125     GST_PAD_ALWAYS,
       
   126     GST_STATIC_CAPS_ANY);
       
   127 
       
   128 enum
       
   129 {
       
   130   ARG_0,
       
   131   ARG_HANDLE,
       
   132   ARG_LOCATION,
       
   133   ARG_IRADIO_MODE,
       
   134   ARG_IRADIO_NAME,
       
   135   ARG_IRADIO_GENRE,
       
   136   ARG_IRADIO_URL,
       
   137   ARG_IRADIO_TITLE
       
   138 };
       
   139 
       
   140 static void gst_gnome_vfs_src_base_init (gpointer g_class);
       
   141 static void gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass);
       
   142 static void gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc);
       
   143 static void gst_gnome_vfs_src_finalize (GObject * object);
       
   144 static void gst_gnome_vfs_src_uri_handler_init (gpointer g_iface,
       
   145     gpointer iface_data);
       
   146 
       
   147 static void gst_gnome_vfs_src_set_property (GObject * object, guint prop_id,
       
   148     const GValue * value, GParamSpec * pspec);
       
   149 static void gst_gnome_vfs_src_get_property (GObject * object, guint prop_id,
       
   150     GValue * value, GParamSpec * pspec);
       
   151 
       
   152 static gboolean gst_gnome_vfs_src_stop (GstBaseSrc * src);
       
   153 static gboolean gst_gnome_vfs_src_start (GstBaseSrc * src);
       
   154 static gboolean gst_gnome_vfs_src_is_seekable (GstBaseSrc * src);
       
   155 static gboolean gst_gnome_vfs_src_check_get_range (GstBaseSrc * src);
       
   156 static gboolean gst_gnome_vfs_src_get_size (GstBaseSrc * src, guint64 * size);
       
   157 static GstFlowReturn gst_gnome_vfs_src_create (GstBaseSrc * basesrc,
       
   158     guint64 offset, guint size, GstBuffer ** buffer);
       
   159 
       
   160 static GstElementClass *parent_class = NULL;
       
   161 
       
   162 GType
       
   163 gst_gnome_vfs_src_get_type (void)
       
   164 {
       
   165   static GType gnomevfssrc_type = 0;
       
   166 
       
   167   if (!gnomevfssrc_type) {
       
   168     static const GTypeInfo gnomevfssrc_info = {
       
   169       sizeof (GstGnomeVFSSrcClass),
       
   170       gst_gnome_vfs_src_base_init,
       
   171       NULL,
       
   172       (GClassInitFunc) gst_gnome_vfs_src_class_init,
       
   173       NULL,
       
   174       NULL,
       
   175       sizeof (GstGnomeVFSSrc),
       
   176       0,
       
   177       (GInstanceInitFunc) gst_gnome_vfs_src_init,
       
   178     };
       
   179     static const GInterfaceInfo urihandler_info = {
       
   180       gst_gnome_vfs_src_uri_handler_init,
       
   181       NULL,
       
   182       NULL
       
   183     };
       
   184 
       
   185     gnomevfssrc_type =
       
   186         g_type_register_static (GST_TYPE_BASE_SRC,
       
   187         "GstGnomeVFSSrc", &gnomevfssrc_info, 0);
       
   188     g_type_add_interface_static (gnomevfssrc_type, GST_TYPE_URI_HANDLER,
       
   189         &urihandler_info);
       
   190   }
       
   191   return gnomevfssrc_type;
       
   192 }
       
   193 
       
   194 static void
       
   195 gst_gnome_vfs_src_base_init (gpointer g_class)
       
   196 {
       
   197   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   198 
       
   199   gst_element_class_add_pad_template (element_class,
       
   200       gst_static_pad_template_get (&srctemplate));
       
   201   gst_element_class_set_details (element_class, &gst_gnome_vfs_src_details);
       
   202 
       
   203   GST_DEBUG_CATEGORY_INIT (gnomevfssrc_debug, "gnomevfssrc", 0,
       
   204       "Gnome-VFS Source");
       
   205 }
       
   206 
       
   207 static void
       
   208 gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass)
       
   209 {
       
   210   GObjectClass *gobject_class;
       
   211   GstElementClass *gstelement_class;
       
   212   GstBaseSrcClass *gstbasesrc_class;
       
   213 
       
   214   gobject_class = G_OBJECT_CLASS (klass);
       
   215   gstelement_class = GST_ELEMENT_CLASS (klass);
       
   216   gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
       
   217 
       
   218   parent_class = g_type_class_peek_parent (klass);
       
   219 
       
   220   gobject_class->finalize = gst_gnome_vfs_src_finalize;
       
   221   gobject_class->set_property = gst_gnome_vfs_src_set_property;
       
   222   gobject_class->get_property = gst_gnome_vfs_src_get_property;
       
   223 
       
   224   /* properties */
       
   225   gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
       
   226       "location", ARG_LOCATION, G_PARAM_READWRITE, NULL);
       
   227   g_object_class_install_property (gobject_class,
       
   228       ARG_HANDLE,
       
   229       g_param_spec_boxed ("handle",
       
   230           "GnomeVFSHandle", "Handle for GnomeVFS",
       
   231           GST_TYPE_GNOME_VFS_HANDLE, G_PARAM_READWRITE));
       
   232 
       
   233   /* icecast stuff */
       
   234   g_object_class_install_property (gobject_class,
       
   235       ARG_IRADIO_MODE,
       
   236       g_param_spec_boolean ("iradio-mode",
       
   237           "iradio-mode",
       
   238           "Enable internet radio mode (extraction of shoutcast/icecast metadata)",
       
   239           FALSE, G_PARAM_READWRITE));
       
   240   g_object_class_install_property (gobject_class,
       
   241       ARG_IRADIO_NAME,
       
   242       g_param_spec_string ("iradio-name",
       
   243           "iradio-name", "Name of the stream", NULL, G_PARAM_READABLE));
       
   244   g_object_class_install_property (gobject_class,
       
   245       ARG_IRADIO_GENRE,
       
   246       g_param_spec_string ("iradio-genre",
       
   247           "iradio-genre", "Genre of the stream", NULL, G_PARAM_READABLE));
       
   248   g_object_class_install_property (gobject_class,
       
   249       ARG_IRADIO_URL,
       
   250       g_param_spec_string ("iradio-url",
       
   251           "iradio-url",
       
   252           "Homepage URL for radio stream", NULL, G_PARAM_READABLE));
       
   253   g_object_class_install_property (gobject_class,
       
   254       ARG_IRADIO_TITLE,
       
   255       g_param_spec_string ("iradio-title",
       
   256           "iradio-title",
       
   257           "Name of currently playing song", NULL, G_PARAM_READABLE));
       
   258 
       
   259   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_start);
       
   260   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_stop);
       
   261   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_get_size);
       
   262   gstbasesrc_class->is_seekable =
       
   263       GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_is_seekable);
       
   264   gstbasesrc_class->check_get_range =
       
   265       GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_check_get_range);
       
   266   gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_create);
       
   267 }
       
   268 
       
   269 static void
       
   270 gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc)
       
   271 {
       
   272   gnomevfssrc->uri = NULL;
       
   273   gnomevfssrc->uri_name = NULL;
       
   274   gnomevfssrc->handle = NULL;
       
   275   gnomevfssrc->curoffset = 0;
       
   276   gnomevfssrc->seekable = FALSE;
       
   277 
       
   278   gnomevfssrc->icy_caps = NULL;
       
   279   gnomevfssrc->iradio_mode = FALSE;
       
   280   gnomevfssrc->http_callbacks_pushed = FALSE;
       
   281   gnomevfssrc->iradio_name = NULL;
       
   282   gnomevfssrc->iradio_genre = NULL;
       
   283   gnomevfssrc->iradio_url = NULL;
       
   284   gnomevfssrc->iradio_title = NULL;
       
   285 
       
   286   g_static_mutex_lock (&count_lock);
       
   287   if (ref_count == 0) {
       
   288     /* gnome vfs engine init */
       
   289     if (gnome_vfs_initialized () == FALSE) {
       
   290       gnome_vfs_init ();
       
   291       vfs_owner = TRUE;
       
   292     }
       
   293   }
       
   294   ref_count++;
       
   295   g_static_mutex_unlock (&count_lock);
       
   296 }
       
   297 
       
   298 static void
       
   299 gst_gnome_vfs_src_finalize (GObject * object)
       
   300 {
       
   301   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (object);
       
   302 
       
   303   g_static_mutex_lock (&count_lock);
       
   304   ref_count--;
       
   305   if (ref_count == 0 && vfs_owner) {
       
   306     if (gnome_vfs_initialized () == TRUE) {
       
   307       gnome_vfs_shutdown ();
       
   308     }
       
   309   }
       
   310   g_static_mutex_unlock (&count_lock);
       
   311 
       
   312   if (src->uri) {
       
   313     gnome_vfs_uri_unref (src->uri);
       
   314     src->uri = NULL;
       
   315   }
       
   316 
       
   317   g_free (src->uri_name);
       
   318   src->uri_name = NULL;
       
   319 
       
   320   g_free (src->iradio_name);
       
   321   src->iradio_name = NULL;
       
   322 
       
   323   g_free (src->iradio_genre);
       
   324   src->iradio_genre = NULL;
       
   325 
       
   326   g_free (src->iradio_url);
       
   327   src->iradio_url = NULL;
       
   328 
       
   329   g_free (src->iradio_title);
       
   330   src->iradio_title = NULL;
       
   331 
       
   332   if (src->icy_caps) {
       
   333     gst_caps_unref (src->icy_caps);
       
   334     src->icy_caps = NULL;
       
   335   }
       
   336 
       
   337   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   338 }
       
   339 
       
   340 /*
       
   341  * URI interface support.
       
   342  */
       
   343 
       
   344 static GstURIType
       
   345 gst_gnome_vfs_src_uri_get_type (void)
       
   346 {
       
   347   return GST_URI_SRC;
       
   348 }
       
   349 
       
   350 static gchar **
       
   351 gst_gnome_vfs_src_uri_get_protocols (void)
       
   352 {
       
   353   static gchar **protocols = NULL;
       
   354 
       
   355   if (!protocols)
       
   356     protocols = gst_gnomevfs_get_supported_uris ();
       
   357 
       
   358   return protocols;
       
   359 }
       
   360 
       
   361 static const gchar *
       
   362 gst_gnome_vfs_src_uri_get_uri (GstURIHandler * handler)
       
   363 {
       
   364   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler);
       
   365 
       
   366   return src->uri_name;
       
   367 }
       
   368 
       
   369 static gboolean
       
   370 gst_gnome_vfs_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
       
   371 {
       
   372   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler);
       
   373 
       
   374   if (GST_STATE (src) == GST_STATE_PLAYING ||
       
   375       GST_STATE (src) == GST_STATE_PAUSED)
       
   376     return FALSE;
       
   377 
       
   378   g_object_set (G_OBJECT (src), "location", uri, NULL);
       
   379 
       
   380   return TRUE;
       
   381 }
       
   382 
       
   383 static void
       
   384 gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
       
   385 {
       
   386   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
       
   387 
       
   388   iface->get_type = gst_gnome_vfs_src_uri_get_type;
       
   389   iface->get_protocols = gst_gnome_vfs_src_uri_get_protocols;
       
   390   iface->get_uri = gst_gnome_vfs_src_uri_get_uri;
       
   391   iface->set_uri = gst_gnome_vfs_src_uri_set_uri;
       
   392 }
       
   393 
       
   394 static void
       
   395 gst_gnome_vfs_src_set_property (GObject * object, guint prop_id,
       
   396     const GValue * value, GParamSpec * pspec)
       
   397 {
       
   398   GstGnomeVFSSrc *src;
       
   399 
       
   400   src = GST_GNOME_VFS_SRC (object);
       
   401 
       
   402   switch (prop_id) {
       
   403     case ARG_LOCATION:{
       
   404       const gchar *new_location;
       
   405 
       
   406       /* the element must be stopped or paused in order to do this */
       
   407       if (GST_STATE (src) == GST_STATE_PLAYING ||
       
   408           GST_STATE (src) == GST_STATE_PAUSED)
       
   409         break;
       
   410 
       
   411       if (src->uri) {
       
   412         gnome_vfs_uri_unref (src->uri);
       
   413         src->uri = NULL;
       
   414       }
       
   415       if (src->uri_name) {
       
   416         g_free (src->uri_name);
       
   417         src->uri_name = NULL;
       
   418       }
       
   419 
       
   420       new_location = g_value_get_string (value);
       
   421       if (new_location) {
       
   422         src->uri_name = gst_gnome_vfs_location_to_uri_string (new_location);
       
   423         src->uri = gnome_vfs_uri_new (src->uri_name);
       
   424       }
       
   425       break;
       
   426     }
       
   427     case ARG_HANDLE:
       
   428       if (GST_STATE (src) == GST_STATE_NULL ||
       
   429           GST_STATE (src) == GST_STATE_READY) {
       
   430         if (src->uri) {
       
   431           gnome_vfs_uri_unref (src->uri);
       
   432           src->uri = NULL;
       
   433         }
       
   434         if (src->uri_name) {
       
   435           g_free (src->uri_name);
       
   436           src->uri_name = NULL;
       
   437         }
       
   438         src->handle = g_value_get_boxed (value);
       
   439       }
       
   440       break;
       
   441     case ARG_IRADIO_MODE:
       
   442       src->iradio_mode = g_value_get_boolean (value);
       
   443       break;
       
   444     default:
       
   445       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   446       break;
       
   447   }
       
   448 }
       
   449 
       
   450 static void
       
   451 gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, GValue * value,
       
   452     GParamSpec * pspec)
       
   453 {
       
   454   GstGnomeVFSSrc *src;
       
   455 
       
   456   src = GST_GNOME_VFS_SRC (object);
       
   457 
       
   458   switch (prop_id) {
       
   459     case ARG_LOCATION:
       
   460       g_value_set_string (value, src->uri_name);
       
   461       break;
       
   462     case ARG_HANDLE:
       
   463       g_value_set_boxed (value, src->handle);
       
   464       break;
       
   465     case ARG_IRADIO_MODE:
       
   466       g_value_set_boolean (value, src->iradio_mode);
       
   467       break;
       
   468     case ARG_IRADIO_NAME:
       
   469       g_value_set_string (value, src->iradio_name);
       
   470       break;
       
   471     case ARG_IRADIO_GENRE:
       
   472       g_value_set_string (value, src->iradio_genre);
       
   473       break;
       
   474     case ARG_IRADIO_URL:
       
   475       g_value_set_string (value, src->iradio_url);
       
   476       break;
       
   477     case ARG_IRADIO_TITLE:
       
   478       g_value_set_string (value, src->iradio_title);
       
   479       break;
       
   480     default:
       
   481       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   482       break;
       
   483   }
       
   484 }
       
   485 
       
   486 static char *
       
   487 gst_gnome_vfs_src_unicodify (const char *str)
       
   488 {
       
   489   const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING",
       
   490     "GST_TAG_ENCODING", NULL
       
   491   };
       
   492 
       
   493   return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
       
   494 }
       
   495 
       
   496 static void
       
   497 gst_gnome_vfs_src_send_additional_headers_callback (gconstpointer in,
       
   498     gsize in_size, gpointer out, gsize out_size, gpointer callback_data)
       
   499 {
       
   500   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data);
       
   501   GnomeVFSModuleCallbackAdditionalHeadersOut *out_args =
       
   502       (GnomeVFSModuleCallbackAdditionalHeadersOut *) out;
       
   503 
       
   504   if (!src->iradio_mode)
       
   505     return;
       
   506   GST_DEBUG_OBJECT (src, "sending headers\n");
       
   507 
       
   508   out_args->headers = g_list_append (out_args->headers,
       
   509       g_strdup ("icy-metadata:1\r\n"));
       
   510 }
       
   511 
       
   512 static void
       
   513 gst_gnome_vfs_src_received_headers_callback (gconstpointer in,
       
   514     gsize in_size, gpointer out, gsize out_size, gpointer callback_data)
       
   515 {
       
   516   GList *i;
       
   517   gint icy_metaint;
       
   518   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data);
       
   519   GnomeVFSModuleCallbackReceivedHeadersIn *in_args =
       
   520       (GnomeVFSModuleCallbackReceivedHeadersIn *) in;
       
   521 
       
   522   /* This is only used for internet radio stuff right now */
       
   523   if (!src->iradio_mode)
       
   524     return;
       
   525 
       
   526   for (i = in_args->headers; i; i = i->next) {
       
   527     char *data = (char *) i->data;
       
   528     char *key = data;
       
   529     char *value = strchr (data, ':');
       
   530 
       
   531     if (!value)
       
   532       continue;
       
   533 
       
   534     value++;
       
   535     g_strstrip (value);
       
   536     if (!strlen (value))
       
   537       continue;
       
   538 
       
   539     /* Icecast stuff */
       
   540     if (strncmp (data, "icy-metaint:", 12) == 0) {      /* ugh */
       
   541       if (sscanf (data + 12, "%d", &icy_metaint) == 1) {
       
   542         if (icy_metaint > 0)
       
   543           src->icy_caps = gst_caps_new_simple ("application/x-icy",
       
   544               "metadata-interval", G_TYPE_INT, icy_metaint, NULL);
       
   545       }
       
   546       continue;
       
   547     }
       
   548 
       
   549     if (!strncmp (data, "icy-", 4))
       
   550       key = data + 4;
       
   551     else
       
   552       continue;
       
   553 
       
   554     GST_DEBUG_OBJECT (src, "key: %s", key);
       
   555     if (!strncmp (key, "name", 4)) {
       
   556       g_free (src->iradio_name);
       
   557       src->iradio_name = gst_gnome_vfs_src_unicodify (value);
       
   558       if (src->iradio_name)
       
   559         g_object_notify (G_OBJECT (src), "iradio-name");
       
   560     } else if (!strncmp (key, "genre", 5)) {
       
   561       g_free (src->iradio_genre);
       
   562       src->iradio_genre = gst_gnome_vfs_src_unicodify (value);
       
   563       if (src->iradio_genre)
       
   564         g_object_notify (G_OBJECT (src), "iradio-genre");
       
   565     } else if (!strncmp (key, "url", 3)) {
       
   566       g_free (src->iradio_url);
       
   567       src->iradio_url = gst_gnome_vfs_src_unicodify (value);
       
   568       if (src->iradio_url)
       
   569         g_object_notify (G_OBJECT (src), "iradio-url");
       
   570     }
       
   571   }
       
   572 }
       
   573 
       
   574 static void
       
   575 gst_gnome_vfs_src_push_callbacks (GstGnomeVFSSrc * src)
       
   576 {
       
   577   if (src->http_callbacks_pushed)
       
   578     return;
       
   579 
       
   580   GST_DEBUG_OBJECT (src, "pushing callbacks");
       
   581   gnome_vfs_module_callback_push
       
   582       (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS,
       
   583       gst_gnome_vfs_src_send_additional_headers_callback, src, NULL);
       
   584   gnome_vfs_module_callback_push
       
   585       (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS,
       
   586       gst_gnome_vfs_src_received_headers_callback, src, NULL);
       
   587 
       
   588   src->http_callbacks_pushed = TRUE;
       
   589 }
       
   590 
       
   591 static void
       
   592 gst_gnome_vfs_src_pop_callbacks (GstGnomeVFSSrc * src)
       
   593 {
       
   594   if (!src->http_callbacks_pushed)
       
   595     return;
       
   596 
       
   597   GST_DEBUG_OBJECT (src, "popping callbacks");
       
   598   gnome_vfs_module_callback_pop
       
   599       (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS);
       
   600   gnome_vfs_module_callback_pop
       
   601       (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS);
       
   602 
       
   603   src->http_callbacks_pushed = FALSE;
       
   604 }
       
   605 
       
   606 /*
       
   607  * Read a new buffer from src->reqoffset, takes care of events
       
   608  * and seeking and such.
       
   609  */
       
   610 static GstFlowReturn
       
   611 gst_gnome_vfs_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
       
   612     GstBuffer ** buffer)
       
   613 {
       
   614   GnomeVFSResult res;
       
   615   GstBuffer *buf;
       
   616   GnomeVFSFileSize readbytes;
       
   617   guint8 *data;
       
   618   GstGnomeVFSSrc *src;
       
   619 
       
   620   src = GST_GNOME_VFS_SRC (basesrc);
       
   621 
       
   622   GST_DEBUG ("now at %llu, reading from %lld, size %u", src->curoffset, offset,
       
   623       size);
       
   624 
       
   625   /* seek if required */
       
   626   if (G_UNLIKELY (src->curoffset != offset)) {
       
   627     GST_DEBUG ("need to seek");
       
   628     if (src->seekable) {
       
   629       GST_DEBUG ("seeking to %lld", offset);
       
   630       res = gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_START, offset);
       
   631       if (res != GNOME_VFS_OK)
       
   632         goto seek_failed;
       
   633       src->curoffset = offset;
       
   634     } else {
       
   635       goto cannot_seek;
       
   636     }
       
   637   }
       
   638 
       
   639   buf = gst_buffer_new_and_alloc (size);
       
   640 
       
   641   if (src->icy_caps)
       
   642     gst_buffer_set_caps (buf, src->icy_caps);
       
   643 
       
   644   data = GST_BUFFER_DATA (buf);
       
   645   GST_BUFFER_OFFSET (buf) = src->curoffset;
       
   646 
       
   647   res = gnome_vfs_read (src->handle, data, size, &readbytes);
       
   648 
       
   649   if (G_UNLIKELY (res == GNOME_VFS_ERROR_EOF || (res == GNOME_VFS_OK
       
   650               && readbytes == 0)))
       
   651     goto eos;
       
   652 
       
   653   GST_BUFFER_SIZE (buf) = readbytes;
       
   654 
       
   655   if (G_UNLIKELY (res != GNOME_VFS_OK))
       
   656     goto read_failed;
       
   657 
       
   658   src->curoffset += readbytes;
       
   659 
       
   660   /* we're done, return the buffer */
       
   661   *buffer = buf;
       
   662 
       
   663   return GST_FLOW_OK;
       
   664 
       
   665 seek_failed:
       
   666   {
       
   667     GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
       
   668         ("Failed to seek to requested position %" G_GINT64_FORMAT ": %s",
       
   669             offset, gnome_vfs_result_to_string (res)));
       
   670     return GST_FLOW_ERROR;
       
   671   }
       
   672 cannot_seek:
       
   673   {
       
   674     GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
       
   675         ("Requested seek from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT
       
   676             " on non-seekable stream", src->curoffset, offset));
       
   677     return GST_FLOW_ERROR;
       
   678   }
       
   679 read_failed:
       
   680   {
       
   681     gst_buffer_unref (buf);
       
   682     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
       
   683         ("Failed to read data: %s", gnome_vfs_result_to_string (res)));
       
   684     return GST_FLOW_ERROR;
       
   685   }
       
   686 eos:
       
   687   {
       
   688     gst_buffer_unref (buf);
       
   689     GST_DEBUG_OBJECT (src, "Reading data gave EOS");
       
   690     return GST_FLOW_UNEXPECTED;
       
   691   }
       
   692 }
       
   693 
       
   694 static gboolean
       
   695 gst_gnome_vfs_src_is_seekable (GstBaseSrc * basesrc)
       
   696 {
       
   697   GstGnomeVFSSrc *src;
       
   698 
       
   699   src = GST_GNOME_VFS_SRC (basesrc);
       
   700 
       
   701   return src->seekable;
       
   702 }
       
   703 
       
   704 static gboolean
       
   705 gst_gnome_vfs_src_check_get_range (GstBaseSrc * basesrc)
       
   706 {
       
   707   GstGnomeVFSSrc *src;
       
   708   const gchar *protocol;
       
   709 
       
   710   src = GST_GNOME_VFS_SRC (basesrc);
       
   711 
       
   712   if (src->uri == NULL) {
       
   713     GST_WARNING_OBJECT (src, "no URI set yet");
       
   714     return FALSE;
       
   715   }
       
   716 
       
   717   if (gnome_vfs_uri_is_local (src->uri)) {
       
   718     GST_LOG_OBJECT (src, "local URI (%s), assuming random access is possible",
       
   719         GST_STR_NULL (src->uri_name));
       
   720     return TRUE;
       
   721   }
       
   722 
       
   723   /* blacklist certain protocols we know won't work getrange-based */
       
   724   protocol = gnome_vfs_uri_get_scheme (src->uri);
       
   725   if (protocol == NULL)
       
   726     goto undecided;
       
   727 
       
   728   if (strcmp (protocol, "http") == 0 || strcmp (protocol, "https") == 0) {
       
   729     GST_LOG_OBJECT (src, "blacklisted protocol '%s', no random access possible"
       
   730         " (URI=%s)", protocol, GST_STR_NULL (src->uri_name));
       
   731     return FALSE;
       
   732   }
       
   733 
       
   734   /* fall through to undecided */
       
   735 
       
   736 undecided:
       
   737   {
       
   738     /* don't know what to do, let the basesrc class decide for us */
       
   739     GST_LOG_OBJECT (src, "undecided about URI '%s', let base class handle it",
       
   740         GST_STR_NULL (src->uri_name));
       
   741 
       
   742     if (GST_BASE_SRC_CLASS (parent_class)->check_get_range)
       
   743       return GST_BASE_SRC_CLASS (parent_class)->check_get_range (basesrc);
       
   744 
       
   745     return FALSE;
       
   746   }
       
   747 }
       
   748 
       
   749 static gboolean
       
   750 gst_gnome_vfs_src_get_size (GstBaseSrc * basesrc, guint64 * size)
       
   751 {
       
   752   GstGnomeVFSSrc *src;
       
   753   GnomeVFSFileInfo *info;
       
   754   GnomeVFSFileInfoOptions options;
       
   755   GnomeVFSResult res;
       
   756 
       
   757   src = GST_GNOME_VFS_SRC (basesrc);
       
   758 
       
   759   *size = -1;
       
   760   info = gnome_vfs_file_info_new ();
       
   761   options = GNOME_VFS_FILE_INFO_DEFAULT | GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
       
   762   res = gnome_vfs_get_file_info_from_handle (src->handle, info, options);
       
   763   if (res == GNOME_VFS_OK) {
       
   764     if ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) {
       
   765       *size = info->size;
       
   766       GST_DEBUG_OBJECT (src, "from handle: %" G_GUINT64_FORMAT " bytes", *size);
       
   767     } else if (src->own_handle && gnome_vfs_uri_is_local (src->uri)) {
       
   768       GST_DEBUG_OBJECT (src,
       
   769           "file size not known, file local, trying fallback");
       
   770       res = gnome_vfs_get_file_info_uri (src->uri, info, options);
       
   771       if (res == GNOME_VFS_OK &&
       
   772           (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) {
       
   773         *size = info->size;
       
   774         GST_DEBUG_OBJECT (src, "from uri: %" G_GUINT64_FORMAT " bytes", *size);
       
   775       }
       
   776     }
       
   777   } else {
       
   778     GST_WARNING_OBJECT (src, "getting info failed: %s",
       
   779         gnome_vfs_result_to_string (res));
       
   780   }
       
   781   gnome_vfs_file_info_unref (info);
       
   782 
       
   783   if (*size == (GnomeVFSFileSize) - 1)
       
   784     return FALSE;
       
   785 
       
   786   GST_DEBUG_OBJECT (src, "return size %" G_GUINT64_FORMAT, *size);
       
   787 
       
   788   return TRUE;
       
   789 }
       
   790 
       
   791 /* open the file, do stuff necessary to go to PAUSED state */
       
   792 static gboolean
       
   793 gst_gnome_vfs_src_start (GstBaseSrc * basesrc)
       
   794 {
       
   795   GnomeVFSResult res;
       
   796   GstGnomeVFSSrc *src;
       
   797 
       
   798   src = GST_GNOME_VFS_SRC (basesrc);
       
   799 
       
   800   gst_gnome_vfs_src_push_callbacks (src);
       
   801 
       
   802   if (src->uri != NULL) {
       
   803     GnomeVFSOpenMode mode = GNOME_VFS_OPEN_READ;
       
   804 
       
   805     /* this can block... */
       
   806     res = gnome_vfs_open_uri (&src->handle, src->uri, mode);
       
   807     if (res != GNOME_VFS_OK)
       
   808       goto open_failed;
       
   809     src->own_handle = TRUE;
       
   810   } else if (!src->handle) {
       
   811     goto no_filename;
       
   812   } else {
       
   813     src->own_handle = FALSE;
       
   814   }
       
   815 
       
   816   if (gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_CURRENT, 0) == GNOME_VFS_OK) {
       
   817     src->seekable = TRUE;
       
   818   } else {
       
   819     src->seekable = FALSE;
       
   820   }
       
   821 
       
   822   return TRUE;
       
   823 
       
   824   /* ERRORS */
       
   825 open_failed:
       
   826   {
       
   827     gchar *filename = gnome_vfs_uri_to_string (src->uri,
       
   828         GNOME_VFS_URI_HIDE_PASSWORD);
       
   829 
       
   830     gst_gnome_vfs_src_pop_callbacks (src);
       
   831 
       
   832     if (res == GNOME_VFS_ERROR_NOT_FOUND ||
       
   833         res == GNOME_VFS_ERROR_HOST_NOT_FOUND ||
       
   834         res == GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE) {
       
   835       GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
       
   836           ("Could not open vfs file \"%s\" for reading: %s (%d)",
       
   837               filename, gnome_vfs_result_to_string (res), res));
       
   838     } else {
       
   839       GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
       
   840           ("Could not open vfs file \"%s\" for reading: %s (%d)",
       
   841               filename, gnome_vfs_result_to_string (res), res));
       
   842     }
       
   843     g_free (filename);
       
   844     return FALSE;
       
   845   }
       
   846 no_filename:
       
   847   {
       
   848     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given"));
       
   849     return FALSE;
       
   850   }
       
   851 }
       
   852 
       
   853 static gboolean
       
   854 gst_gnome_vfs_src_stop (GstBaseSrc * basesrc)
       
   855 {
       
   856   GstGnomeVFSSrc *src;
       
   857 
       
   858   src = GST_GNOME_VFS_SRC (basesrc);
       
   859 
       
   860   gst_gnome_vfs_src_pop_callbacks (src);
       
   861 
       
   862   if (src->own_handle) {
       
   863     GnomeVFSResult res;
       
   864 
       
   865     res = gnome_vfs_close (src->handle);
       
   866     if (res != GNOME_VFS_OK) {
       
   867       GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
       
   868           ("Could not close vfs handle: %s", gnome_vfs_result_to_string (res)));
       
   869     }
       
   870     src->handle = NULL;
       
   871   }
       
   872   src->curoffset = 0;
       
   873 
       
   874   if (src->icy_caps) {
       
   875     gst_caps_unref (src->icy_caps);
       
   876     src->icy_caps = NULL;
       
   877   }
       
   878 
       
   879   return TRUE;
       
   880 }