gstreamer/ext/gnomevfs/gstgnomevfssrc.c
Sebastian Dröge 49deb0c05d Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory u...
Original commit message from CVS:
* configure.ac:
* ext/alsa/gstalsamixerelement.c:
(gst_alsa_mixer_element_class_init):
* ext/alsa/gstalsasink.c: (gst_alsasink_class_init):
* ext/alsa/gstalsasrc.c: (gst_alsasrc_class_init):
* ext/cdparanoia/gstcdparanoiasrc.c:
(gst_cd_paranoia_src_class_init):
* ext/gio/gstgiosink.c: (gst_gio_sink_class_init):
* ext/gio/gstgiosrc.c: (gst_gio_src_class_init):
* ext/gio/gstgiostreamsink.c: (gst_gio_stream_sink_class_init):
* ext/gio/gstgiostreamsrc.c: (gst_gio_stream_src_class_init):
* ext/gnomevfs/gstgnomevfssink.c: (gst_gnome_vfs_sink_class_init):
* ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_class_init):
* ext/ogg/gstoggmux.c: (gst_ogg_mux_class_init):
* ext/pango/gsttextoverlay.c: (gst_text_overlay_class_init):
* ext/pango/gsttextrender.c: (gst_text_render_class_init):
* ext/theora/theoradec.c: (gst_theora_dec_class_init):
* ext/theora/theoraenc.c: (gst_theora_enc_class_init):
* ext/theora/theoraparse.c: (gst_theora_parse_class_init):
* ext/vorbis/vorbisenc.c: (gst_vorbis_enc_class_init):
* gst-libs/gst/audio/gstaudiofiltertemplate.c:
(gst_audio_filter_template_class_init):
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_class_init):
* gst-libs/gst/audio/gstbaseaudiosrc.c:
(gst_base_audio_src_class_init):
* gst-libs/gst/cdda/gstcddabasesrc.c:
(gst_cdda_base_src_class_init):
* gst-libs/gst/interfaces/mixertrack.c:
(gst_mixer_track_class_init):
* gst-libs/gst/rtp/gstbasertpdepayload.c:
(gst_base_rtp_depayload_class_init):
* gst-libs/gst/rtp/gstbasertppayload.c:
(gst_basertppayload_class_init):
* gst/audioconvert/gstaudioconvert.c:
(gst_audio_convert_class_init):
* gst/audiorate/gstaudiorate.c: (gst_audio_rate_class_init):
* gst/audioresample/gstaudioresample.c:
(gst_audioresample_class_init):
* gst/audiotestsrc/gstaudiotestsrc.c:
(gst_audio_test_src_class_init):
* gst/gdp/gstgdppay.c: (gst_gdp_pay_class_init):
* gst/playback/gstdecodebin2.c: (gst_decode_bin_class_init):
* gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init),
(preroll_unlinked):
* gst/playback/gstplaybin.c: (gst_play_bin_class_init):
* gst/playback/gstplaybin2.c: (gst_play_bin_class_init):
* gst/playback/gstplaysink.c: (gst_play_sink_class_init):
* gst/playback/gstqueue2.c: (gst_queue_class_init):
* gst/playback/gststreaminfo.c: (gst_stream_info_class_init):
* gst/playback/gststreamselector.c: (gst_selector_pad_class_init),
(gst_stream_selector_class_init):
* gst/playback/gsturidecodebin.c: (gst_uri_decode_bin_class_init):
* gst/subparse/gstsubparse.c: (gst_sub_parse_class_init):
* gst/tcp/gstmultifdsink.c: (gst_multi_fd_sink_class_init):
* gst/tcp/gsttcpclientsink.c: (gst_tcp_client_sink_class_init):
* gst/tcp/gsttcpclientsrc.c: (gst_tcp_client_src_class_init):
* gst/tcp/gsttcpserversink.c: (gst_tcp_server_sink_class_init):
* gst/tcp/gsttcpserversrc.c: (gst_tcp_server_src_class_init):
* gst/videorate/gstvideorate.c: (gst_video_rate_class_init):
* gst/videoscale/gstvideoscale.c: (gst_video_scale_class_init):
* gst/videotestsrc/gstvideotestsrc.c:
(gst_video_test_src_class_init):
* gst/volume/gstvolume.c: (gst_volume_class_init):
* sys/v4l/gstv4lelement.c: (gst_v4lelement_class_init):
* sys/v4l/gstv4lmjpegsink.c: (gst_v4lmjpegsink_class_init):
* sys/v4l/gstv4lmjpegsrc.c: (gst_v4lmjpegsrc_class_init):
* sys/v4l/gstv4lsrc.c: (gst_v4lsrc_class_init):
* sys/ximage/ximagesink.c: (gst_ximagesink_class_init):
* sys/xvimage/xvimagesink.c: (gst_xvimagesink_class_init):
Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use
static strings (i.e. all). This gives us less memory usage,
fewer allocations and thus less memory defragmentation. Depend
on core CVS for this. Fixes bug .
2008-03-22 15:00:53 +00:00

880 lines
25 KiB
C

/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2001 Bastien Nocera <hadess@hadess.net>
* 2002 Kristian Rietveld <kris@gtk.org>
* 2002,2003 Colin Walters <walters@gnu.org>
*
* gnomevfssrc.c:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-gnomevfssrc
* @short_description: Read from any GnomeVFS-supported location
* @see_also: #GstFileSrc, #GstGnomeVFSSink
*
* <refsect2>
* <para>
* This plugin reads data from a local or remote location specified
* by an URI. This location can be specified using any protocol supported by
* the GnomeVFS library. Common protocols are 'file', 'http', 'ftp', or 'smb'.
* </para>
* <para>
* In case the element-gnomevfssrc::iradio-mode property is set and the
* location is a http resource, gnomevfssrc will send special icecast http
* headers to the server to request additional icecast metainformation. If
* the server is not an icecast server, it will display the same behaviour
* as if the element-gnomevfssrc::iradio-mode property was not set. However,
* if the server is in fact an icecast server, gnomevfssrc will output
* data with a media type of application/x-icy, in which case you will
* need to use the #ICYDemux element as follow-up element to extract
* the icecast meta data and to determine the underlying media type.
* </para>
* <para>
* Example pipeline:
* <programlisting>
* gst-launch -v gnomevfssrc location=file:///home/joe/foo.xyz ! fakesink
* </programlisting>
* The above pipeline will simply read a local file and do nothing with the
* data read. Instead of gnomevfssrc, we could just as well have used the
* filesrc element here.
* </para>
* <para>
* Another example pipeline:
* <programlisting>
* gst-launch -v gnomevfssrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz
* </programlisting>
* The above pipeline will copy a file from a remote host to the local file
* system using the Samba protocol.
* </para>
* <para>
* Yet another example pipeline:
* <programlisting>
* gst-launch -v gnomevfssrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink
* </programlisting>
* The above pipeline will read and decode and play an mp3 file from a
* web server using the http protocol.
* </para>
* </refsect2>
*
*/
#define BROKEN_SIG 1
/*#undef BROKEN_SIG */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gst/gst-i18n-plugin.h"
#include "gstgnomevfssrc.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <gst/gst.h>
#include <gst/tag/tag.h>
/* gnome-vfs.h doesn't include the following header, which we need: */
#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
GST_DEBUG_CATEGORY_STATIC (gnomevfssrc_debug);
#define GST_CAT_DEFAULT gnomevfssrc_debug
static const GstElementDetails gst_gnome_vfs_src_details =
GST_ELEMENT_DETAILS ("GnomeVFS Source",
"Source/File",
"Read from any GnomeVFS-supported file",
"Bastien Nocera <hadess@hadess.net>\n"
"Ronald S. Bultje <rbultje@ronald.bitfreak.net>");
static GStaticMutex count_lock = G_STATIC_MUTEX_INIT;
static gint ref_count = 0;
static gboolean vfs_owner = FALSE;
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
enum
{
ARG_0,
ARG_HANDLE,
ARG_LOCATION,
ARG_IRADIO_MODE,
ARG_IRADIO_NAME,
ARG_IRADIO_GENRE,
ARG_IRADIO_URL,
ARG_IRADIO_TITLE
};
static void gst_gnome_vfs_src_base_init (gpointer g_class);
static void gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass);
static void gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc);
static void gst_gnome_vfs_src_finalize (GObject * object);
static void gst_gnome_vfs_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static void gst_gnome_vfs_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_gnome_vfs_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_gnome_vfs_src_stop (GstBaseSrc * src);
static gboolean gst_gnome_vfs_src_start (GstBaseSrc * src);
static gboolean gst_gnome_vfs_src_is_seekable (GstBaseSrc * src);
static gboolean gst_gnome_vfs_src_check_get_range (GstBaseSrc * src);
static gboolean gst_gnome_vfs_src_get_size (GstBaseSrc * src, guint64 * size);
static GstFlowReturn gst_gnome_vfs_src_create (GstBaseSrc * basesrc,
guint64 offset, guint size, GstBuffer ** buffer);
static GstElementClass *parent_class = NULL;
GType
gst_gnome_vfs_src_get_type (void)
{
static GType gnomevfssrc_type = 0;
if (!gnomevfssrc_type) {
static const GTypeInfo gnomevfssrc_info = {
sizeof (GstGnomeVFSSrcClass),
gst_gnome_vfs_src_base_init,
NULL,
(GClassInitFunc) gst_gnome_vfs_src_class_init,
NULL,
NULL,
sizeof (GstGnomeVFSSrc),
0,
(GInstanceInitFunc) gst_gnome_vfs_src_init,
};
static const GInterfaceInfo urihandler_info = {
gst_gnome_vfs_src_uri_handler_init,
NULL,
NULL
};
gnomevfssrc_type =
g_type_register_static (GST_TYPE_BASE_SRC,
"GstGnomeVFSSrc", &gnomevfssrc_info, 0);
g_type_add_interface_static (gnomevfssrc_type, GST_TYPE_URI_HANDLER,
&urihandler_info);
}
return gnomevfssrc_type;
}
static void
gst_gnome_vfs_src_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&srctemplate));
gst_element_class_set_details (element_class, &gst_gnome_vfs_src_details);
GST_DEBUG_CATEGORY_INIT (gnomevfssrc_debug, "gnomevfssrc", 0,
"Gnome-VFS Source");
}
static void
gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSrcClass *gstbasesrc_class;
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_gnome_vfs_src_finalize;
gobject_class->set_property = gst_gnome_vfs_src_set_property;
gobject_class->get_property = gst_gnome_vfs_src_get_property;
/* properties */
gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
"location", ARG_LOCATION, G_PARAM_READWRITE, NULL);
g_object_class_install_property (gobject_class,
ARG_HANDLE,
g_param_spec_boxed ("handle",
"GnomeVFSHandle", "Handle for GnomeVFS",
GST_TYPE_GNOME_VFS_HANDLE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/* icecast stuff */
g_object_class_install_property (gobject_class,
ARG_IRADIO_MODE,
g_param_spec_boolean ("iradio-mode",
"iradio-mode",
"Enable internet radio mode (extraction of shoutcast/icecast metadata)",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
ARG_IRADIO_NAME,
g_param_spec_string ("iradio-name",
"iradio-name", "Name of the stream", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_IRADIO_GENRE,
g_param_spec_string ("iradio-genre", "iradio-genre",
"Genre of the stream", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_IRADIO_URL,
g_param_spec_string ("iradio-url", "iradio-url",
"Homepage URL for radio stream", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_IRADIO_TITLE,
g_param_spec_string ("iradio-title", "iradio-title",
"Name of currently playing song", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_stop);
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_get_size);
gstbasesrc_class->is_seekable =
GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_is_seekable);
gstbasesrc_class->check_get_range =
GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_check_get_range);
gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_create);
}
static void
gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc)
{
gnomevfssrc->uri = NULL;
gnomevfssrc->uri_name = NULL;
gnomevfssrc->handle = NULL;
gnomevfssrc->curoffset = 0;
gnomevfssrc->seekable = FALSE;
gnomevfssrc->icy_caps = NULL;
gnomevfssrc->iradio_mode = FALSE;
gnomevfssrc->http_callbacks_pushed = FALSE;
gnomevfssrc->iradio_name = NULL;
gnomevfssrc->iradio_genre = NULL;
gnomevfssrc->iradio_url = NULL;
gnomevfssrc->iradio_title = NULL;
g_static_mutex_lock (&count_lock);
if (ref_count == 0) {
/* gnome vfs engine init */
if (gnome_vfs_initialized () == FALSE) {
gnome_vfs_init ();
vfs_owner = TRUE;
}
}
ref_count++;
g_static_mutex_unlock (&count_lock);
}
static void
gst_gnome_vfs_src_finalize (GObject * object)
{
GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (object);
g_static_mutex_lock (&count_lock);
ref_count--;
if (ref_count == 0 && vfs_owner) {
if (gnome_vfs_initialized () == TRUE) {
gnome_vfs_shutdown ();
}
}
g_static_mutex_unlock (&count_lock);
if (src->uri) {
gnome_vfs_uri_unref (src->uri);
src->uri = NULL;
}
g_free (src->uri_name);
src->uri_name = NULL;
g_free (src->iradio_name);
src->iradio_name = NULL;
g_free (src->iradio_genre);
src->iradio_genre = NULL;
g_free (src->iradio_url);
src->iradio_url = NULL;
g_free (src->iradio_title);
src->iradio_title = NULL;
if (src->icy_caps) {
gst_caps_unref (src->icy_caps);
src->icy_caps = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/*
* URI interface support.
*/
static GstURIType
gst_gnome_vfs_src_uri_get_type (void)
{
return GST_URI_SRC;
}
static gchar **
gst_gnome_vfs_src_uri_get_protocols (void)
{
static gchar **protocols = NULL;
if (!protocols)
protocols = gst_gnomevfs_get_supported_uris ();
return protocols;
}
static const gchar *
gst_gnome_vfs_src_uri_get_uri (GstURIHandler * handler)
{
GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler);
return src->uri_name;
}
static gboolean
gst_gnome_vfs_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
{
GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler);
if (GST_STATE (src) == GST_STATE_PLAYING ||
GST_STATE (src) == GST_STATE_PAUSED)
return FALSE;
g_object_set (G_OBJECT (src), "location", uri, NULL);
return TRUE;
}
static void
gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
iface->get_type = gst_gnome_vfs_src_uri_get_type;
iface->get_protocols = gst_gnome_vfs_src_uri_get_protocols;
iface->get_uri = gst_gnome_vfs_src_uri_get_uri;
iface->set_uri = gst_gnome_vfs_src_uri_set_uri;
}
static void
gst_gnome_vfs_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstGnomeVFSSrc *src;
src = GST_GNOME_VFS_SRC (object);
switch (prop_id) {
case ARG_LOCATION:{
const gchar *new_location;
/* the element must be stopped or paused in order to do this */
if (GST_STATE (src) == GST_STATE_PLAYING ||
GST_STATE (src) == GST_STATE_PAUSED)
break;
if (src->uri) {
gnome_vfs_uri_unref (src->uri);
src->uri = NULL;
}
if (src->uri_name) {
g_free (src->uri_name);
src->uri_name = NULL;
}
new_location = g_value_get_string (value);
if (new_location) {
src->uri_name = gst_gnome_vfs_location_to_uri_string (new_location);
src->uri = gnome_vfs_uri_new (src->uri_name);
}
break;
}
case ARG_HANDLE:
if (GST_STATE (src) == GST_STATE_NULL ||
GST_STATE (src) == GST_STATE_READY) {
if (src->uri) {
gnome_vfs_uri_unref (src->uri);
src->uri = NULL;
}
if (src->uri_name) {
g_free (src->uri_name);
src->uri_name = NULL;
}
src->handle = g_value_get_boxed (value);
}
break;
case ARG_IRADIO_MODE:
src->iradio_mode = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstGnomeVFSSrc *src;
src = GST_GNOME_VFS_SRC (object);
switch (prop_id) {
case ARG_LOCATION:
g_value_set_string (value, src->uri_name);
break;
case ARG_HANDLE:
g_value_set_boxed (value, src->handle);
break;
case ARG_IRADIO_MODE:
g_value_set_boolean (value, src->iradio_mode);
break;
case ARG_IRADIO_NAME:
g_value_set_string (value, src->iradio_name);
break;
case ARG_IRADIO_GENRE:
g_value_set_string (value, src->iradio_genre);
break;
case ARG_IRADIO_URL:
g_value_set_string (value, src->iradio_url);
break;
case ARG_IRADIO_TITLE:
g_value_set_string (value, src->iradio_title);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static char *
gst_gnome_vfs_src_unicodify (const char *str)
{
const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING",
"GST_TAG_ENCODING", NULL
};
return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
}
static void
gst_gnome_vfs_src_send_additional_headers_callback (gconstpointer in,
gsize in_size, gpointer out, gsize out_size, gpointer callback_data)
{
GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data);
GnomeVFSModuleCallbackAdditionalHeadersOut *out_args =
(GnomeVFSModuleCallbackAdditionalHeadersOut *) out;
if (!src->iradio_mode)
return;
GST_DEBUG_OBJECT (src, "sending headers\n");
out_args->headers = g_list_append (out_args->headers,
g_strdup ("icy-metadata:1\r\n"));
}
static void
gst_gnome_vfs_src_received_headers_callback (gconstpointer in,
gsize in_size, gpointer out, gsize out_size, gpointer callback_data)
{
GList *i;
gint icy_metaint;
GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data);
GnomeVFSModuleCallbackReceivedHeadersIn *in_args =
(GnomeVFSModuleCallbackReceivedHeadersIn *) in;
/* This is only used for internet radio stuff right now */
if (!src->iradio_mode)
return;
for (i = in_args->headers; i; i = i->next) {
char *data = (char *) i->data;
char *key = data;
char *value = strchr (data, ':');
if (!value)
continue;
value++;
g_strstrip (value);
if (!strlen (value))
continue;
/* Icecast stuff */
if (strncmp (data, "icy-metaint:", 12) == 0) { /* ugh */
if (sscanf (data + 12, "%d", &icy_metaint) == 1) {
if (icy_metaint > 0)
src->icy_caps = gst_caps_new_simple ("application/x-icy",
"metadata-interval", G_TYPE_INT, icy_metaint, NULL);
}
continue;
}
if (!strncmp (data, "icy-", 4))
key = data + 4;
else
continue;
GST_DEBUG_OBJECT (src, "key: %s", key);
if (!strncmp (key, "name", 4)) {
g_free (src->iradio_name);
src->iradio_name = gst_gnome_vfs_src_unicodify (value);
if (src->iradio_name)
g_object_notify (G_OBJECT (src), "iradio-name");
} else if (!strncmp (key, "genre", 5)) {
g_free (src->iradio_genre);
src->iradio_genre = gst_gnome_vfs_src_unicodify (value);
if (src->iradio_genre)
g_object_notify (G_OBJECT (src), "iradio-genre");
} else if (!strncmp (key, "url", 3)) {
g_free (src->iradio_url);
src->iradio_url = gst_gnome_vfs_src_unicodify (value);
if (src->iradio_url)
g_object_notify (G_OBJECT (src), "iradio-url");
}
}
}
static void
gst_gnome_vfs_src_push_callbacks (GstGnomeVFSSrc * src)
{
if (src->http_callbacks_pushed)
return;
GST_DEBUG_OBJECT (src, "pushing callbacks");
gnome_vfs_module_callback_push
(GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS,
gst_gnome_vfs_src_send_additional_headers_callback, src, NULL);
gnome_vfs_module_callback_push
(GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS,
gst_gnome_vfs_src_received_headers_callback, src, NULL);
src->http_callbacks_pushed = TRUE;
}
static void
gst_gnome_vfs_src_pop_callbacks (GstGnomeVFSSrc * src)
{
if (!src->http_callbacks_pushed)
return;
GST_DEBUG_OBJECT (src, "popping callbacks");
gnome_vfs_module_callback_pop
(GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS);
gnome_vfs_module_callback_pop
(GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS);
src->http_callbacks_pushed = FALSE;
}
/*
* Read a new buffer from src->reqoffset, takes care of events
* and seeking and such.
*/
static GstFlowReturn
gst_gnome_vfs_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
GstBuffer ** buffer)
{
GnomeVFSResult res;
GstBuffer *buf;
GnomeVFSFileSize readbytes;
guint8 *data;
GstGnomeVFSSrc *src;
src = GST_GNOME_VFS_SRC (basesrc);
GST_DEBUG ("now at %llu, reading from %lld, size %u", src->curoffset, offset,
size);
/* seek if required */
if (G_UNLIKELY (src->curoffset != offset)) {
GST_DEBUG ("need to seek");
if (src->seekable) {
GST_DEBUG ("seeking to %lld", offset);
res = gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_START, offset);
if (res != GNOME_VFS_OK)
goto seek_failed;
src->curoffset = offset;
} else {
goto cannot_seek;
}
}
buf = gst_buffer_new_and_alloc (size);
if (src->icy_caps)
gst_buffer_set_caps (buf, src->icy_caps);
data = GST_BUFFER_DATA (buf);
GST_BUFFER_OFFSET (buf) = src->curoffset;
res = gnome_vfs_read (src->handle, data, size, &readbytes);
if (G_UNLIKELY (res == GNOME_VFS_ERROR_EOF || (res == GNOME_VFS_OK
&& readbytes == 0)))
goto eos;
GST_BUFFER_SIZE (buf) = readbytes;
if (G_UNLIKELY (res != GNOME_VFS_OK))
goto read_failed;
src->curoffset += readbytes;
/* we're done, return the buffer */
*buffer = buf;
return GST_FLOW_OK;
seek_failed:
{
GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
("Failed to seek to requested position %" G_GINT64_FORMAT ": %s",
offset, gnome_vfs_result_to_string (res)));
return GST_FLOW_ERROR;
}
cannot_seek:
{
GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
("Requested seek from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT
" on non-seekable stream", src->curoffset, offset));
return GST_FLOW_ERROR;
}
read_failed:
{
gst_buffer_unref (buf);
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
("Failed to read data: %s", gnome_vfs_result_to_string (res)));
return GST_FLOW_ERROR;
}
eos:
{
gst_buffer_unref (buf);
GST_DEBUG_OBJECT (src, "Reading data gave EOS");
return GST_FLOW_UNEXPECTED;
}
}
static gboolean
gst_gnome_vfs_src_is_seekable (GstBaseSrc * basesrc)
{
GstGnomeVFSSrc *src;
src = GST_GNOME_VFS_SRC (basesrc);
return src->seekable;
}
static gboolean
gst_gnome_vfs_src_check_get_range (GstBaseSrc * basesrc)
{
GstGnomeVFSSrc *src;
const gchar *protocol;
src = GST_GNOME_VFS_SRC (basesrc);
if (src->uri == NULL) {
GST_WARNING_OBJECT (src, "no URI set yet");
return FALSE;
}
if (gnome_vfs_uri_is_local (src->uri)) {
GST_LOG_OBJECT (src, "local URI (%s), assuming random access is possible",
GST_STR_NULL (src->uri_name));
return TRUE;
}
/* blacklist certain protocols we know won't work getrange-based */
protocol = gnome_vfs_uri_get_scheme (src->uri);
if (protocol == NULL)
goto undecided;
if (strcmp (protocol, "http") == 0 || strcmp (protocol, "https") == 0) {
GST_LOG_OBJECT (src, "blacklisted protocol '%s', no random access possible"
" (URI=%s)", protocol, GST_STR_NULL (src->uri_name));
return FALSE;
}
/* fall through to undecided */
undecided:
{
/* don't know what to do, let the basesrc class decide for us */
GST_LOG_OBJECT (src, "undecided about URI '%s', let base class handle it",
GST_STR_NULL (src->uri_name));
if (GST_BASE_SRC_CLASS (parent_class)->check_get_range)
return GST_BASE_SRC_CLASS (parent_class)->check_get_range (basesrc);
return FALSE;
}
}
static gboolean
gst_gnome_vfs_src_get_size (GstBaseSrc * basesrc, guint64 * size)
{
GstGnomeVFSSrc *src;
GnomeVFSFileInfo *info;
GnomeVFSFileInfoOptions options;
GnomeVFSResult res;
src = GST_GNOME_VFS_SRC (basesrc);
*size = -1;
info = gnome_vfs_file_info_new ();
options = GNOME_VFS_FILE_INFO_DEFAULT | GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
res = gnome_vfs_get_file_info_from_handle (src->handle, info, options);
if (res == GNOME_VFS_OK) {
if ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) {
*size = info->size;
GST_DEBUG_OBJECT (src, "from handle: %" G_GUINT64_FORMAT " bytes", *size);
} else if (src->own_handle && gnome_vfs_uri_is_local (src->uri)) {
GST_DEBUG_OBJECT (src,
"file size not known, file local, trying fallback");
res = gnome_vfs_get_file_info_uri (src->uri, info, options);
if (res == GNOME_VFS_OK &&
(info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) {
*size = info->size;
GST_DEBUG_OBJECT (src, "from uri: %" G_GUINT64_FORMAT " bytes", *size);
}
}
} else {
GST_WARNING_OBJECT (src, "getting info failed: %s",
gnome_vfs_result_to_string (res));
}
gnome_vfs_file_info_unref (info);
if (*size == (GnomeVFSFileSize) - 1)
return FALSE;
GST_DEBUG_OBJECT (src, "return size %" G_GUINT64_FORMAT, *size);
return TRUE;
}
/* open the file, do stuff necessary to go to PAUSED state */
static gboolean
gst_gnome_vfs_src_start (GstBaseSrc * basesrc)
{
GnomeVFSResult res;
GstGnomeVFSSrc *src;
src = GST_GNOME_VFS_SRC (basesrc);
gst_gnome_vfs_src_push_callbacks (src);
if (src->uri != NULL) {
GnomeVFSOpenMode mode = GNOME_VFS_OPEN_READ;
/* this can block... */
res = gnome_vfs_open_uri (&src->handle, src->uri, mode);
if (res != GNOME_VFS_OK)
goto open_failed;
src->own_handle = TRUE;
} else if (!src->handle) {
goto no_filename;
} else {
src->own_handle = FALSE;
}
if (gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_CURRENT, 0) == GNOME_VFS_OK) {
src->seekable = TRUE;
} else {
src->seekable = FALSE;
}
return TRUE;
/* ERRORS */
open_failed:
{
gchar *filename = gnome_vfs_uri_to_string (src->uri,
GNOME_VFS_URI_HIDE_PASSWORD);
gst_gnome_vfs_src_pop_callbacks (src);
if (res == GNOME_VFS_ERROR_NOT_FOUND ||
res == GNOME_VFS_ERROR_HOST_NOT_FOUND ||
res == GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE) {
GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
("Could not open vfs file \"%s\" for reading: %s (%d)",
filename, gnome_vfs_result_to_string (res), res));
} else {
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
("Could not open vfs file \"%s\" for reading: %s (%d)",
filename, gnome_vfs_result_to_string (res), res));
}
g_free (filename);
return FALSE;
}
no_filename:
{
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given"));
return FALSE;
}
}
static gboolean
gst_gnome_vfs_src_stop (GstBaseSrc * basesrc)
{
GstGnomeVFSSrc *src;
src = GST_GNOME_VFS_SRC (basesrc);
gst_gnome_vfs_src_pop_callbacks (src);
if (src->own_handle) {
GnomeVFSResult res;
res = gnome_vfs_close (src->handle);
if (res != GNOME_VFS_OK) {
GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
("Could not close vfs handle: %s", gnome_vfs_result_to_string (res)));
}
src->handle = NULL;
}
src->curoffset = 0;
if (src->icy_caps) {
gst_caps_unref (src->icy_caps);
src->icy_caps = NULL;
}
return TRUE;
}