diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | sys/Makefile.am | 10 | ||||
-rw-r--r-- | sys/wininet/Makefile.am | 10 | ||||
-rw-r--r-- | sys/wininet/gstwininetsrc.c | 453 | ||||
-rw-r--r-- | sys/wininet/gstwininetsrc.h | 69 |
6 files changed, 561 insertions, 2 deletions
@@ -1,5 +1,16 @@ 2008-04-10 Tim-Philipp Müller <tim at centricular dot net> + Patch by: Ole André Vadla Ravnås <ole.andre.ravnas at tandberg com> + + * configure.ac: + * sys/Makefile.am: + * sys/wininet/Makefile.am: + * sys/wininet/gstwininetsrc.c: + * sys/wininet/gstwininetsrc.h: + Add wininetsrc for basic http/ftp support on windows (#520897). + +2008-04-10 Tim-Philipp Müller <tim at centricular dot net> + * gst/nsf/nsf.h: Change prototype of process function here too to avoid 'incompatible assignment' warnings. diff --git a/configure.ac b/configure.ac index a64d0fae..81722a00 100644 --- a/configure.ac +++ b/configure.ac @@ -1039,6 +1039,14 @@ AG_GST_CHECK_FEATURE(OSS4, [Open Sound System 4], oss4, [ [HAVE_OSS4="yes"], [HAVE_OSS4="no"]) ]) +dnl *** wininet *** +translit(dnm, m, l) AM_CONDITIONAL(USE_WININET, true) +AG_GST_CHECK_FEATURE(WININET, [Windows internet library], wininet, [ + AC_MSG_CHECKING([Checking for windows internet support]) + AC_CHECK_HEADERS([windows.h wininet.h], + [HAVE_WININET="yes"], [HAVE_WININET="no"]) +]) + else dnl not building plugins with external dependencies, @@ -1085,6 +1093,7 @@ AM_CONDITIONAL(USE_TIMIDITY, false) AM_CONDITIONAL(USE_X264, false) AM_CONDITIONAL(USE_XVID, false) AM_CONDITIONAL(USE_WILDMIDI, false) +AM_CONDITIONAL(USE_WININET, false) fi dnl of EXT plugins @@ -1194,6 +1203,7 @@ sys/fbdev/Makefile sys/oss4/Makefile sys/qtwrapper/Makefile sys/vcd/Makefile +sys/wininet/Makefile examples/Makefile examples/app/Makefile examples/directfb/Makefile diff --git a/sys/Makefile.am b/sys/Makefile.am index 55a01374..95ba8739 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -46,7 +46,13 @@ else QT_DIR= endif -SUBDIRS = $(FBDEV_DIR) $(DVB_DIR) $(VCD_DIR) $(QT_DIR) $(OSS4_DIR) +if USE_WININET +WININET_DIR=wininet +else +WININET_DIR= +endif + +SUBDIRS = $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(QT_DIR) $(VCD_DIR) $(WININET) -DIST_SUBDIRS = dvb fbdev dshowdecwrapper dshowsrcwrapper oss4 qtwrapper vcd +DIST_SUBDIRS = dvb fbdev dshowdecwrapper dshowsrcwrapper oss4 qtwrapper vcd wininet diff --git a/sys/wininet/Makefile.am b/sys/wininet/Makefile.am new file mode 100644 index 00000000..bbd6453d --- /dev/null +++ b/sys/wininet/Makefile.am @@ -0,0 +1,10 @@ +plugin_LTLIBRARIES = libgstwininet.la + +libgstwininet_la_SOURCES = gstwininetsrc.c + +libgstwininet_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstwininet_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) -lwininet +libgstwininet_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstwininetsrc.h + diff --git a/sys/wininet/gstwininetsrc.c b/sys/wininet/gstwininetsrc.c new file mode 100644 index 00000000..ea194d30 --- /dev/null +++ b/sys/wininet/gstwininetsrc.c @@ -0,0 +1,453 @@ +/* GStreamer Windows network source + * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> + * + * 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-wininetsrc + * + * <refsect2> + * <title>Example launch line</title> + * <para> + * <programlisting> + * gst-launch-0.10 -v wininetsrc location="http://71.83.57.210:9000" ! application/x-icy,metadata-interval=0 ! icydemux ! mad ! audioconvert ! directsoundsink + * </programlisting> + * </para> + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gstwininetsrc.h" + +#include <string.h> + +#define DEFAULT_LOCATION "http://localhost/" +#define DEFAULT_POLL_MODE FALSE +#define DEFAULT_IRADIO_MODE FALSE + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_POLL_MODE, + PROP_IRADIO_MODE +}; + +GST_DEBUG_CATEGORY_STATIC (gst_win_inet_src_debug); +#define GST_CAT_DEFAULT gst_win_inet_src_debug + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static void gst_win_inet_src_init_interfaces (GType type); +static void gst_win_inet_src_uri_handler_init (gpointer g_iface, + gpointer iface_data); + +static void gst_win_inet_src_dispose (GObject * object); +static void gst_win_inet_src_finalize (GObject * object); +static void gst_win_inet_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_win_inet_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static gboolean gst_win_inet_src_start (GstBaseSrc * basesrc); +static gboolean gst_win_inet_src_stop (GstBaseSrc * basesrc); + +static GstFlowReturn gst_win_inet_src_create (GstPushSrc * pushsrc, + GstBuffer ** buffer); + +static void gst_win_inet_src_reset (GstWinInetSrc * self); + +GST_BOILERPLATE_FULL (GstWinInetSrc, gst_win_inet_src, GstPushSrc, + GST_TYPE_PUSH_SRC, gst_win_inet_src_init_interfaces); + +static void +gst_win_inet_src_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details_simple (element_class, + "Windows Network Source", "Source/Network", + "Receive data as a client over the network via HTTP or FTP", + "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>"); +} + +static void +gst_win_inet_src_class_init (GstWinInetSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); + GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); + + gobject_class->dispose = gst_win_inet_src_dispose; + gobject_class->finalize = gst_win_inet_src_finalize; + gobject_class->get_property = gst_win_inet_src_get_property; + gobject_class->set_property = gst_win_inet_src_set_property; + + gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_win_inet_src_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_win_inet_src_stop); + gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_win_inet_src_create); + + g_object_class_install_property (gobject_class, + PROP_LOCATION, g_param_spec_string ("location", "Location", + "Location to read from", DEFAULT_LOCATION, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, + PROP_POLL_MODE, g_param_spec_boolean ("poll-mode", "poll-mode", + "Enable poll mode (keep re-issuing request)", + DEFAULT_POLL_MODE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, + PROP_IRADIO_MODE, g_param_spec_boolean ("iradio-mode", "iradio-mode", + "Enable Internet radio mode " + "(extraction of shoutcast/icecast metadata)", + DEFAULT_IRADIO_MODE, G_PARAM_READWRITE)); +} + +static void +gst_win_inet_src_init_interfaces (GType type) +{ + static const GInterfaceInfo uri_handler_info = { + gst_win_inet_src_uri_handler_init, + NULL, + NULL + }; + + g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &uri_handler_info); + + GST_DEBUG_CATEGORY_INIT (gst_win_inet_src_debug, "wininetsrc", + 0, "Wininet source"); +} + +static void +gst_win_inet_src_init (GstWinInetSrc * self, GstWinInetSrcClass * gclass) +{ + self->location = g_strdup (DEFAULT_LOCATION); + self->poll_mode = DEFAULT_POLL_MODE; + self->iradio_mode = DEFAULT_IRADIO_MODE; + + self->inet = NULL; + self->url = NULL; + self->cur_offset = 0; + self->icy_caps = NULL; +} + +static void +gst_win_inet_src_dispose (GObject * object) +{ + GstWinInetSrc *self = GST_WIN_INET_SRC (object); + + gst_win_inet_src_reset (self); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_win_inet_src_finalize (GObject * object) +{ + GstWinInetSrc *self = GST_WIN_INET_SRC (object); + + g_free (self->location); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_win_inet_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWinInetSrc *self = GST_WIN_INET_SRC (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, self->location); + break; + + case PROP_POLL_MODE: + g_value_set_boolean (value, self->poll_mode); + break; + + case PROP_IRADIO_MODE: + g_value_set_boolean (value, self->iradio_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_win_inet_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstWinInetSrc *self = GST_WIN_INET_SRC (object); + + switch (prop_id) { + case PROP_LOCATION: + if (GST_STATE (self) == GST_STATE_PLAYING || + GST_STATE (self) == GST_STATE_PAUSED) { + GST_WARNING_OBJECT (self, "element must be in stopped or paused state " + "in order to change location"); + break; + } + + g_free (self->location); + self->location = g_value_dup_string (value); + break; + + case PROP_POLL_MODE: + self->poll_mode = g_value_get_boolean (value); + break; + + case PROP_IRADIO_MODE: + self->iradio_mode = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_win_inet_src_reset (GstWinInetSrc * self) +{ + if (self->url != NULL) { + InternetCloseHandle (self->url); + self->url = NULL; + } + + if (self->inet != NULL) { + InternetCloseHandle (self->inet); + self->inet = NULL; + } + + if (self->icy_caps != NULL) { + gst_caps_unref (self->icy_caps); + self->icy_caps = NULL; + } + + self->cur_offset = 0; +} + +static gboolean +gst_win_inet_src_get_header_value_as_int (GstWinInetSrc * self, + const gchar * header_name, gint * header_value, gboolean log_failure) +{ + gchar buf[16] = { 0, }; + DWORD buf_size = sizeof (buf); + gint *value = (gint *) buf; + + strcpy (buf, header_name); + + if (!HttpQueryInfo (self->url, HTTP_QUERY_CUSTOM | HTTP_QUERY_FLAG_NUMBER, + buf, &buf_size, NULL)) { + if (log_failure) { + DWORD error_code = GetLastError (); + const gchar *error_str = "unknown error"; + + if (error_code == ERROR_HTTP_HEADER_NOT_FOUND) + error_str = "ERROR_HTTP_HEADER_NOT_FOUND"; + + GST_WARNING_OBJECT (self, "HttpQueryInfo for header '%s' failed: %s " + "(0x%08x)", header_name, error_str, error_code); + } + + return FALSE; + } + + *header_value = *value; + return TRUE; +} + +static gboolean +gst_win_inet_src_open (GstWinInetSrc * self) +{ + const gchar *extra_headers = NULL; + + gst_win_inet_src_reset (self); + + self->inet = InternetOpen (NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + if (self->inet == NULL) + goto error; + + if (self->iradio_mode) + extra_headers = "Icy-MetaData:1"; /* exactly as sent by WinAmp, no space */ + + self->url = InternetOpenUrl (self->inet, self->location, extra_headers, + (extra_headers != NULL) ? -1 : 0, INTERNET_FLAG_NO_UI, (DWORD_PTR) self); + if (self->url == NULL) + goto error; + + if (self->iradio_mode) { + gint value; + + if (gst_win_inet_src_get_header_value_as_int (self, "icy-metaint", &value, + TRUE)) { + self->icy_caps = gst_caps_new_simple ("application/x-icy", + "metadata-interval", G_TYPE_INT, value, NULL); + } + } + + return TRUE; + +error: + GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, (NULL), + ("Could not open location \"%s\" for reading: 0x%08x", + self->location, GetLastError ())); + gst_win_inet_src_reset (self); + + return FALSE; +} + +static gboolean +gst_win_inet_src_start (GstBaseSrc * basesrc) +{ + GstWinInetSrc *self = GST_WIN_INET_SRC (basesrc); + + return gst_win_inet_src_open (self); +} + +static gboolean +gst_win_inet_src_stop (GstBaseSrc * basesrc) +{ + gst_win_inet_src_reset (GST_WIN_INET_SRC (basesrc)); + + return TRUE; +} + +static GstFlowReturn +gst_win_inet_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer) +{ + GstWinInetSrc *self = GST_WIN_INET_SRC (pushsrc); + GstBaseSrc *basesrc = GST_BASE_SRC (pushsrc); + GstBuffer *buf = NULL; + GstFlowReturn ret = GST_FLOW_OK; + DWORD bytes_read = 0; + + do { + GstCaps *caps = GST_PAD_CAPS (GST_BASE_SRC_PAD (self)); + + if (self->icy_caps != NULL) + caps = self->icy_caps; + + ret = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (basesrc), + self->cur_offset, basesrc->blocksize, caps, &buf); + + if (G_LIKELY (ret == GST_FLOW_OK)) { + if (InternetReadFile (self->url, GST_BUFFER_DATA (buf), + basesrc->blocksize, &bytes_read)) { + if (bytes_read == 0) { + if (self->poll_mode) { + if (gst_win_inet_src_open (self)) { + gst_buffer_unref (buf); + buf = NULL; + } else { + ret = GST_FLOW_ERROR; + } + } else { + GST_ERROR_OBJECT (self, "short read (eof?)"); + ret = GST_FLOW_UNEXPECTED; + } + } + } else { + GST_ERROR_OBJECT (self, "InternetReadFile failed: 0x%08x", + GetLastError ()); + + ret = GST_FLOW_ERROR; + } + } + } + while (bytes_read == 0 && ret == GST_FLOW_OK); + + if (ret == GST_FLOW_OK) { + GST_BUFFER_SIZE (buf) = bytes_read; + self->cur_offset += bytes_read; + + *buffer = buf; + } else { + if (buf != NULL) + gst_buffer_unref (buf); + } + + return ret; +} + +static GstURIType +gst_win_inet_src_uri_get_type (void) +{ + return GST_URI_SRC; +} + +static gchar ** +gst_win_inet_src_uri_get_protocols (void) +{ + static gchar *protocols[] = { "http", "https", "ftp", NULL }; + + return protocols; +} + +static G_CONST_RETURN gchar * +gst_win_inet_src_uri_get_uri (GstURIHandler * handler) +{ + GstWinInetSrc *src = GST_WIN_INET_SRC (handler); + + return src->location; +} + +static gboolean +gst_win_inet_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) +{ + GstWinInetSrc *src = GST_WIN_INET_SRC (handler); + + g_free (src->location); + src->location = g_strdup (uri); + return TRUE; +} + +static void +gst_win_inet_src_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_win_inet_src_uri_get_type; + iface->get_protocols = gst_win_inet_src_uri_get_protocols; + iface->get_uri = gst_win_inet_src_uri_get_uri; + iface->set_uri = gst_win_inet_src_uri_set_uri; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "wininetsrc", + GST_RANK_NONE, GST_TYPE_WIN_INET_SRC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "wininet", + "Windows network plugins", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/sys/wininet/gstwininetsrc.h b/sys/wininet/gstwininetsrc.h new file mode 100644 index 00000000..d2a69183 --- /dev/null +++ b/sys/wininet/gstwininetsrc.h @@ -0,0 +1,69 @@ +/* GStreamer Windows network source + * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> + * + * 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. + */ + +#ifndef __GST_WIN_INET_SRC_H__ +#define __GST_WIN_INET_SRC_H__ + +#include <windows.h> +#include <wininet.h> +#include <gst/base/gstpushsrc.h> + +G_BEGIN_DECLS + +#define GST_TYPE_WIN_INET_SRC \ + (gst_win_inet_src_get_type ()) +#define GST_WIN_INET_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WIN_INET_SRC, GstWinInetSrc)) +#define GST_WIN_INET_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WIN_INET_SRC, GstWinInetSrcClass)) +#define GST_IS_WIN_INET_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WIN_INET_SRC)) +#define GST_IS_WIN_INET_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WIN_INET_SRC)) + +typedef struct _GstWinInetSrc GstWinInetSrc; +typedef struct _GstWinInetSrcClass GstWinInetSrcClass; + +struct _GstWinInetSrc +{ + GstPushSrc push_src; + + /* property storage */ + gchar * location; + gboolean poll_mode; + gboolean iradio_mode; + + /* state */ + HINTERNET inet; + HINTERNET url; + guint64 cur_offset; + GstCaps * icy_caps; +}; + +struct _GstWinInetSrcClass +{ + GstPushSrcClass parent_class; +}; + +GType gst_win_inet_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_WIN_INET_SRC_H__ */ + |