Add wininetsrc for basic http/ftp support on windows (#520897).
authorOle André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
Thu, 10 Apr 2008 15:29:44 +0000 (15:29 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Thu, 10 Apr 2008 15:29:44 +0000 (15:29 +0000)
Original commit message from CVS:
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).

ChangeLog
configure.ac
sys/Makefile.am
sys/wininet/Makefile.am [new file with mode: 0644]
sys/wininet/gstwininetsrc.c [new file with mode: 0644]
sys/wininet/gstwininetsrc.h [new file with mode: 0644]

index dd8e46c..dca7c84 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index a64d0fa..81722a0 100644 (file)
@@ -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
index 55a0137..95ba873 100644 (file)
@@ -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 (file)
index 0000000..bbd6453
--- /dev/null
@@ -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 (file)
index 0000000..ea194d3
--- /dev/null
@@ -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 (file)
index 0000000..d2a6918
--- /dev/null
@@ -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__ */
+