ext/gio/gstgio.h: Add macro to check if a stream supports seeking.
authorSebastian Dröge <slomo@circular-chaos.org>
Wed, 7 Nov 2007 15:18:54 +0000 (15:18 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Wed, 7 Nov 2007 15:18:54 +0000 (15:18 +0000)
Original commit message from CVS:
* ext/gio/gstgio.h:
Add macro to check if a stream supports seeking.
* ext/gio/Makefile.am:
* ext/gio/gstgiobasesink.c: (gst_gio_base_sink_base_init),
(gst_gio_base_sink_class_init), (gst_gio_base_sink_init),
(gst_gio_base_sink_finalize), (gst_gio_base_sink_start),
(gst_gio_base_sink_stop), (gst_gio_base_sink_unlock),
(gst_gio_base_sink_unlock_stop), (gst_gio_base_sink_event),
(gst_gio_base_sink_render), (gst_gio_base_sink_query),
(gst_gio_base_sink_set_stream):
* ext/gio/gstgiobasesink.h:
* ext/gio/gstgiobasesrc.c: (gst_gio_base_src_base_init),
(gst_gio_base_src_class_init), (gst_gio_base_src_init),
(gst_gio_base_src_finalize), (gst_gio_base_src_start),
(gst_gio_base_src_stop), (gst_gio_base_src_get_size),
(gst_gio_base_src_is_seekable), (gst_gio_base_src_unlock),
(gst_gio_base_src_unlock_stop), (gst_gio_base_src_check_get_range),
(gst_gio_base_src_create), (gst_gio_base_src_set_stream):
* ext/gio/gstgiobasesrc.h:
Refactor common GIO functions to GstGioBaseSink and GstGioBaseSrc
base classes that only require a GInputStream or GOutputStream to
work.
* ext/gio/gstgiosink.c: (gst_gio_sink_base_init),
(gst_gio_sink_class_init), (gst_gio_sink_init),
(gst_gio_sink_finalize), (gst_gio_sink_start):
* ext/gio/gstgiosink.h:
* ext/gio/gstgiosrc.c: (gst_gio_src_base_init),
(gst_gio_src_class_init), (gst_gio_src_init),
(gst_gio_src_finalize), (gst_gio_src_start):
* ext/gio/gstgiosrc.h:
Use the newly created base classes here.
* ext/gio/gstgio.c: (plugin_init):
* ext/gio/gstgiostreamsink.c: (gst_gio_stream_sink_base_init),
(gst_gio_stream_sink_class_init), (gst_gio_stream_sink_init),
(gst_gio_stream_sink_finalize), (gst_gio_stream_sink_set_property),
(gst_gio_stream_sink_get_property):
* ext/gio/gstgiostreamsink.h:
* ext/gio/gstgiostreamsrc.c: (gst_gio_stream_src_base_init),
(gst_gio_stream_src_class_init), (gst_gio_stream_src_init),
(gst_gio_stream_src_finalize), (gst_gio_stream_src_set_property),
(gst_gio_stream_src_get_property):
* ext/gio/gstgiostreamsrc.h:
Implement GstGioStreamSink and GstGioStreamSrc that have a property
to set the GInputStream/GOutputStream that should be used.
* tests/check/Makefile.am:
* tests/check/pipelines/.cvsignore:
* tests/check/pipelines/gio.c: (message_handler), (GST_START_TEST),
(gio_testsuite), (main):
Add unit test for giostreamsrc and giostreamsink.

16 files changed:
ext/gio/Makefile.am
ext/gio/gstgio.c
ext/gio/gstgio.h
ext/gio/gstgiobasesink.c [new file with mode: 0644]
ext/gio/gstgiobasesink.h [new file with mode: 0644]
ext/gio/gstgiobasesrc.c [new file with mode: 0644]
ext/gio/gstgiobasesrc.h [new file with mode: 0644]
ext/gio/gstgiosink.c
ext/gio/gstgiosink.h
ext/gio/gstgiosrc.c
ext/gio/gstgiosrc.h
ext/gio/gstgiostreamsink.c [new file with mode: 0644]
ext/gio/gstgiostreamsink.h [new file with mode: 0644]
ext/gio/gstgiostreamsrc.c [new file with mode: 0644]
ext/gio/gstgiostreamsrc.h [new file with mode: 0644]
tests/check/pipelines/gio.c [new file with mode: 0644]

index 15d5744..4fd7f55 100644 (file)
@@ -3,11 +3,26 @@
 plugin_LTLIBRARIES = libgstgio.la
 
 # sources used to compile this plug-in
-libgstgio_la_SOURCES = gstgio.c gstgiosink.c gstgiosrc.c
+libgstgio_la_SOURCES = \
+               gstgio.c \
+               gstgiobasesink.c \
+               gstgiobasesrc.c \
+               gstgiosink.c \
+               gstgiosrc.c \
+               gstgiostreamsink.c \
+               gstgiostreamsrc.c
 
 libgstgio_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GIO_CFLAGS)
 libgstgio_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GIO_LIBS)
 libgstgio_la_LDFLAGS = $(GST_GIO_LDFLAGS)
 
 # headers we need but don't want installed
-noinst_HEADERS = gstgio.h gstgiosink.h gstgiosrc.h
+noinst_HEADERS = \
+               gstgio.h \
+               gstgiobasesink.h \
+               gstgiobasesrc.h \
+               gstgiosink.h \
+               gstgiosrc.h \
+               gstgiostreamsink.h \
+               gstgiostreamsrc.h
+
index 978394a..1e8b423 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -25,6 +26,8 @@
 #include "gstgio.h"
 #include "gstgiosink.h"
 #include "gstgiosrc.h"
+#include "gstgiostreamsink.h"
+#include "gstgiostreamsrc.h"
 
 #include <gio/gvfs.h>
 
@@ -199,6 +202,12 @@ plugin_init (GstPlugin * plugin)
   ret &= gst_element_register (plugin, "giosrc", GST_RANK_MARGINAL,
       GST_TYPE_GIO_SRC);
 
+  ret &= gst_element_register (plugin, "giostreamsink", GST_RANK_NONE,
+      GST_TYPE_GIO_STREAM_SINK);
+
+  ret &= gst_element_register (plugin, "giostreamsrc", GST_RANK_NONE,
+      GST_TYPE_GIO_STREAM_SRC);
+
   return ret;
 }
 
index ec6b635..0546a26 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -18,8 +19,8 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __GSTGIO_H__
-#define __GSTGIO_H__
+#ifndef __GST_GIO_H__
+#define __GST_GIO_H__
 
 #include <gio/gioerror.h>
 #include <gio/gseekable.h>
@@ -29,6 +30,8 @@ G_BEGIN_DECLS
 
 #define GST_GIO_ERROR_MATCHES(err, code) g_error_matches (err, G_IO_ERROR, G_IO_ERROR_##code)
 
+#define GST_GIO_STREAM_IS_SEEKABLE(stream) (G_IS_SEEKABLE (stream) && g_seekable_can_seek (G_SEEKABLE (stream)))
+
 gboolean gst_gio_error (gpointer element, const gchar *func_name,
     GError **err, GstFlowReturn *ret);
 GstFlowReturn gst_gio_seek (gpointer element, GSeekable *stream, guint64 offset,
@@ -37,4 +40,4 @@ void gst_gio_uri_handler_do_init (GType type);
 
 G_END_DECLS
 
-#endif /* __GSTGIO_H__ */
+#endif /* __GST_GIO_H__ */
diff --git a/ext/gio/gstgiobasesink.c b/ext/gio/gstgiobasesink.c
new file mode 100644 (file)
index 0000000..1885b46
--- /dev/null
@@ -0,0 +1,334 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstgiobasesink.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_gio_base_sink_debug);
+#define GST_CAT_DEFAULT gst_gio_base_sink_debug
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+GST_BOILERPLATE (GstGioBaseSink, gst_gio_base_sink, GstBaseSink,
+    GST_TYPE_BASE_SINK);
+
+static void gst_gio_base_sink_finalize (GObject * object);
+static gboolean gst_gio_base_sink_start (GstBaseSink * base_sink);
+static gboolean gst_gio_base_sink_stop (GstBaseSink * base_sink);
+static gboolean gst_gio_base_sink_unlock (GstBaseSink * base_sink);
+static gboolean gst_gio_base_sink_unlock_stop (GstBaseSink * base_sink);
+static gboolean gst_gio_base_sink_event (GstBaseSink * base_sink,
+    GstEvent * event);
+static GstFlowReturn gst_gio_base_sink_render (GstBaseSink * base_sink,
+    GstBuffer * buffer);
+static gboolean gst_gio_base_sink_query (GstPad * pad, GstQuery * query);
+
+static void
+gst_gio_base_sink_base_init (gpointer gclass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+  GST_DEBUG_CATEGORY_INIT (gst_gio_base_sink_debug, "gio_base_sink", 0,
+      "GIO base sink");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_factory));
+}
+
+static void
+gst_gio_base_sink_class_init (GstGioBaseSinkClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSinkClass *gstbasesink_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesink_class = (GstBaseSinkClass *) klass;
+
+  gobject_class->finalize = gst_gio_base_sink_finalize;
+
+  gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_gio_base_sink_start);
+  gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_gio_base_sink_stop);
+  gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_base_sink_unlock);
+  gstbasesink_class->unlock_stop =
+      GST_DEBUG_FUNCPTR (gst_gio_base_sink_unlock_stop);
+  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_gio_base_sink_event);
+  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_gio_base_sink_render);
+}
+
+static void
+gst_gio_base_sink_init (GstGioBaseSink * sink, GstGioBaseSinkClass * gclass)
+{
+  gst_pad_set_query_function (GST_BASE_SINK_PAD (sink),
+      GST_DEBUG_FUNCPTR (gst_gio_base_sink_query));
+
+  gst_base_sink_set_sync (GST_BASE_SINK (sink), FALSE);
+
+  sink->cancel = g_cancellable_new ();
+}
+
+static void
+gst_gio_base_sink_finalize (GObject * object)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (object);
+
+  if (sink->cancel) {
+    g_object_unref (sink->cancel);
+    sink->cancel = NULL;
+  }
+
+  if (sink->stream) {
+    g_object_unref (sink->stream);
+    sink->stream = NULL;
+  }
+
+  GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static gboolean
+gst_gio_base_sink_start (GstBaseSink * base_sink)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink);
+
+  if (!G_IS_OUTPUT_STREAM (sink->stream)) {
+    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
+        ("No stream given yet"));
+    return FALSE;
+  }
+
+  sink->position = 0;
+
+  GST_DEBUG_OBJECT (sink, "started stream");
+
+  return TRUE;
+}
+
+static gboolean
+gst_gio_base_sink_stop (GstBaseSink * base_sink)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink);
+  gboolean success = TRUE;
+  GError *err = NULL;
+
+  if (G_IS_OUTPUT_STREAM (sink->stream)) {
+    /* FIXME: In case that the call below would block, there is no one to
+     * trigger the cancellation! */
+
+    success = g_output_stream_close (sink->stream, sink->cancel, &err);
+
+    if (success) {
+      GST_DEBUG_OBJECT (sink, "closed stream");
+    } else if (!gst_gio_error (sink, "g_output_stream_close", &err, NULL)) {
+      GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, (NULL),
+          ("g_output_stream_close failed: %s", err->message));
+      g_clear_error (&err);
+    }
+
+    g_object_unref (sink->stream);
+    sink->stream = NULL;
+  }
+
+  return success;
+}
+
+static gboolean
+gst_gio_base_sink_unlock (GstBaseSink * base_sink)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink);
+
+  GST_LOG_OBJECT (sink, "triggering cancellation");
+
+  g_cancellable_cancel (sink->cancel);
+
+  return TRUE;
+}
+
+static gboolean
+gst_gio_base_sink_unlock_stop (GstBaseSink * base_sink)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink);
+
+  GST_LOG_OBJECT (sink, "resetting cancellable");
+
+  g_cancellable_reset (sink->cancel);
+
+  return TRUE;
+}
+
+static gboolean
+gst_gio_base_sink_event (GstBaseSink * base_sink, GstEvent * event)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink);
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  if (sink->stream == NULL)
+    return TRUE;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    {
+      GstFormat format;
+      gint64 offset;
+
+      gst_event_parse_new_segment (event, NULL, NULL, &format, &offset, NULL,
+          NULL);
+
+      if (format != GST_FORMAT_BYTES) {
+        GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format",
+            gst_format_get_name (format));
+        break;
+      }
+
+      if (GST_GIO_STREAM_IS_SEEKABLE (sink->stream)) {
+        ret = gst_gio_seek (sink, G_SEEKABLE (sink->stream), offset,
+            sink->cancel);
+        if (ret == GST_FLOW_OK)
+          sink->position = offset;
+      } else {
+        ret = GST_FLOW_NOT_SUPPORTED;
+      }
+    }
+      break;
+
+    case GST_EVENT_EOS:
+    case GST_EVENT_FLUSH_START:
+    {
+      gboolean success;
+      GError *err = NULL;
+
+      success = g_output_stream_flush (sink->stream, sink->cancel, &err);
+
+      if (!success && !gst_gio_error (sink, "g_output_stream_flush", &err,
+              &ret)) {
+        GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
+            ("flush failed: %s", err->message));
+        g_clear_error (&err);
+      }
+    }
+      break;
+
+    default:
+      break;
+  }
+
+  return (ret == GST_FLOW_OK);
+}
+
+static GstFlowReturn
+gst_gio_base_sink_render (GstBaseSink * base_sink, GstBuffer * buffer)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (base_sink);
+  gssize written;
+  gboolean success;
+  GError *err = NULL;
+
+  GST_LOG_OBJECT (sink, "writing %u bytes to offset %" G_GUINT64_FORMAT,
+      GST_BUFFER_SIZE (buffer), sink->position);
+
+  written = g_output_stream_write (sink->stream,
+      GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), sink->cancel, &err);
+
+  success = (written >= 0);
+
+  if (G_UNLIKELY (success && written < GST_BUFFER_SIZE (buffer))) {
+    /* FIXME: Can this happen?  Should we handle it gracefully?  gnomevfssink
+     * doesn't... */
+    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
+        ("Could not write to stream: (short write, only %"
+            G_GUINT64_FORMAT " bytes of %d bytes written)",
+            written, GST_BUFFER_SIZE (buffer)));
+    return GST_FLOW_ERROR;
+  }
+
+  if (success) {
+    sink->position += written;
+    return GST_FLOW_OK;
+
+  } else {
+    GstFlowReturn ret;
+
+    if (!gst_gio_error (sink, "g_output_stream_write", &err, &ret)) {
+      GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
+          ("Could not write to stream: %s", err->message));
+      g_clear_error (&err);
+    }
+
+    return ret;
+  }
+}
+
+static gboolean
+gst_gio_base_sink_query (GstPad * pad, GstQuery * query)
+{
+  GstGioBaseSink *sink = GST_GIO_BASE_SINK (GST_PAD_PARENT (pad));
+  GstFormat format;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_POSITION:
+      gst_query_parse_position (query, &format, NULL);
+      switch (format) {
+        case GST_FORMAT_BYTES:
+        case GST_FORMAT_DEFAULT:
+          gst_query_set_position (query, GST_FORMAT_BYTES, sink->position);
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    case GST_QUERY_FORMATS:
+      gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
+      return TRUE;
+    default:
+      return gst_pad_query_default (pad, query);
+  }
+}
+
+void
+gst_gio_base_sink_set_stream (GstGioBaseSink * sink, GOutputStream * stream)
+{
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+  g_return_if_fail ((GST_STATE (sink) != GST_STATE_PLAYING &&
+          GST_STATE (sink) != GST_STATE_PAUSED));
+
+  if (sink->stream) {
+    GError *err = NULL;
+    gboolean success = g_output_stream_close (sink->stream, sink->cancel, &err);
+
+    if (success) {
+      GST_DEBUG_OBJECT (sink, "closed stream");
+    } else if (!gst_gio_error (sink, "g_output_stream_close", &err, NULL)) {
+      GST_WARNING_OBJECT (sink, "g_output_stream_close failed: %s",
+          err->message);
+      g_clear_error (&err);
+    }
+
+    g_object_unref (sink->stream);
+    sink->stream = NULL;
+  }
+
+  sink->stream = stream;
+}
diff --git a/ext/gio/gstgiobasesink.h b/ext/gio/gstgiobasesink.h
new file mode 100644 (file)
index 0000000..834b058
--- /dev/null
@@ -0,0 +1,66 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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_GIO_BASE_SINK_H__
+#define __GST_GIO_BASE_SINK_H__
+
+#include "gstgio.h"
+
+#include <gio/gfile.h>
+#include <gst/base/gstbasesink.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_GIO_BASE_SINK \
+  (gst_gio_base_sink_get_type())
+#define GST_GIO_BASE_SINK(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_BASE_SINK,GstGioBaseSink))
+#define GST_GIO_BASE_SINK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_BASE_SINK,GstGioBaseSinkClass))
+#define GST_IS_GIO_BASE_SINK(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_BASE_SINK))
+#define GST_IS_GIO_BASE_SINK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_BASE_SINK))
+
+typedef struct _GstGioBaseSink      GstGioBaseSink;
+typedef struct _GstGioBaseSinkClass GstGioBaseSinkClass;
+
+struct _GstGioBaseSink
+{
+  GstBaseSink sink;
+
+  GCancellable *cancel;
+  guint64 position;
+  GOutputStream *stream;
+};
+
+struct _GstGioBaseSinkClass 
+{
+  GstBaseSinkClass parent_class;
+};
+
+GType gst_gio_base_sink_get_type (void);
+
+void gst_gio_base_sink_set_stream (GstGioBaseSink *sink, GOutputStream *stream);
+
+G_END_DECLS
+
+#endif /* __GST_GIO_BASE_SINK_H__ */
diff --git a/ext/gio/gstgiobasesrc.c b/ext/gio/gstgiobasesrc.c
new file mode 100644 (file)
index 0000000..857b2a9
--- /dev/null
@@ -0,0 +1,329 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstgiobasesrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_gio_base_src_debug);
+#define GST_CAT_DEFAULT gst_gio_base_src_debug
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+GST_BOILERPLATE (GstGioBaseSrc, gst_gio_base_src, GstBaseSrc,
+    GST_TYPE_BASE_SRC);
+
+static void gst_gio_base_src_finalize (GObject * object);
+static gboolean gst_gio_base_src_start (GstBaseSrc * base_src);
+static gboolean gst_gio_base_src_stop (GstBaseSrc * base_src);
+static gboolean gst_gio_base_src_get_size (GstBaseSrc * base_src,
+    guint64 * size);
+static gboolean gst_gio_base_src_is_seekable (GstBaseSrc * base_src);
+static gboolean gst_gio_base_src_unlock (GstBaseSrc * base_src);
+static gboolean gst_gio_base_src_unlock_stop (GstBaseSrc * base_src);
+static gboolean gst_gio_base_src_check_get_range (GstBaseSrc * base_src);
+static GstFlowReturn gst_gio_base_src_create (GstBaseSrc * base_src,
+    guint64 offset, guint size, GstBuffer ** buf);
+
+static void
+gst_gio_base_src_base_init (gpointer gclass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+  GST_DEBUG_CATEGORY_INIT (gst_gio_base_src_debug, "gio_base_src", 0,
+      "GIO base source");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_factory));
+}
+
+static void
+gst_gio_base_src_class_init (GstGioBaseSrcClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSrcClass *gstbasesrc_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesrc_class = (GstBaseSrcClass *) klass;
+
+  gobject_class->finalize = gst_gio_base_src_finalize;
+
+  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gio_base_src_start);
+  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gio_base_src_stop);
+  gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gio_base_src_get_size);
+  gstbasesrc_class->is_seekable =
+      GST_DEBUG_FUNCPTR (gst_gio_base_src_is_seekable);
+  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_base_src_unlock);
+  gstbasesrc_class->unlock_stop =
+      GST_DEBUG_FUNCPTR (gst_gio_base_src_unlock_stop);
+  gstbasesrc_class->check_get_range =
+      GST_DEBUG_FUNCPTR (gst_gio_base_src_check_get_range);
+  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gio_base_src_create);
+}
+
+static void
+gst_gio_base_src_init (GstGioBaseSrc * src, GstGioBaseSrcClass * gclass)
+{
+  src->cancel = g_cancellable_new ();
+}
+
+static void
+gst_gio_base_src_finalize (GObject * object)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (object);
+
+  if (src->cancel) {
+    g_object_unref (src->cancel);
+    src->cancel = NULL;
+  }
+
+  if (src->stream) {
+    g_object_unref (src->stream);
+    src->stream = NULL;
+  }
+
+  GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static gboolean
+gst_gio_base_src_start (GstBaseSrc * base_src)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
+  if (!G_IS_INPUT_STREAM (src->stream)) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+        ("No stream given yet"));
+    return FALSE;
+  }
+
+  src->position = 0;
+
+  GST_DEBUG_OBJECT (src, "started stream");
+
+  return TRUE;
+}
+
+static gboolean
+gst_gio_base_src_stop (GstBaseSrc * base_src)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+  gboolean success = TRUE;
+  GError *err = NULL;
+
+  if (src->stream != NULL) {
+    /* FIXME: In case that the call below would block, there is no one to
+     * trigger the cancellation! */
+
+    success = g_input_stream_close (src->stream, src->cancel, &err);
+
+    if (!success && !gst_gio_error (src, "g_input_stream_close", &err, NULL)) {
+      GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
+          ("g_input_stream_close failed: %s", err->message));
+      g_clear_error (&err);
+    }
+
+    g_object_unref (src->stream);
+    src->stream = NULL;
+  }
+
+  GST_DEBUG_OBJECT (src, "closed stream");
+
+  return success;
+}
+
+static gboolean
+gst_gio_base_src_get_size (GstBaseSrc * base_src, guint64 * size)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
+  if (G_IS_FILE_INPUT_STREAM (src->stream)) {
+    GFileInfo *info;
+    GError *err = NULL;
+
+    info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (src->stream),
+        G_FILE_ATTRIBUTE_STD_SIZE, src->cancel, &err);
+
+    if (info != NULL) {
+      *size = g_file_info_get_size (info);
+      g_object_unref (info);
+      GST_DEBUG_OBJECT (src, "found size: %" G_GUINT64_FORMAT, *size);
+      return TRUE;
+    }
+
+    if (!gst_gio_error (src, "g_file_input_stream_query_info", &err, NULL)) {
+
+      if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED))
+        GST_DEBUG_OBJECT (src, "size information not available");
+      else
+        GST_WARNING_OBJECT (src, "size information retrieval failed: %s",
+            err->message);
+
+      g_clear_error (&err);
+    }
+  } else if (G_IS_MEMORY_INPUT_STREAM (src->stream)) {
+    gsize data_size;
+
+    data_size =
+        g_memory_input_stream_get_data_size (G_MEMORY_INPUT_STREAM (src->
+            stream));
+
+    if (data_size != -1) {
+      *size = data_size;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+static gboolean
+gst_gio_base_src_is_seekable (GstBaseSrc * base_src)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+  gboolean seekable;
+
+  seekable = GST_GIO_STREAM_IS_SEEKABLE (src->stream);
+
+  GST_DEBUG_OBJECT (src, "can seek: %d", seekable);
+
+  return seekable;
+}
+
+static gboolean
+gst_gio_base_src_unlock (GstBaseSrc * base_src)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
+  GST_LOG_OBJECT (src, "triggering cancellation");
+
+  g_cancellable_cancel (src->cancel);
+
+  return TRUE;
+}
+
+static gboolean
+gst_gio_base_src_unlock_stop (GstBaseSrc * base_src)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+
+  GST_LOG_OBJECT (src, "resetting cancellable");
+
+  g_cancellable_reset (src->cancel);
+
+  return TRUE;
+}
+
+static gboolean
+gst_gio_base_src_check_get_range (GstBaseSrc * base_src)
+{
+  /* FIXME: Implement dry-run variant using guesswork like gnomevfssrc? */
+
+  return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS,
+      check_get_range, (base_src), FALSE);
+}
+
+static GstFlowReturn
+gst_gio_base_src_create (GstBaseSrc * base_src, guint64 offset, guint size,
+    GstBuffer ** buf_return)
+{
+  GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src);
+  GstBuffer *buf;
+  gssize read;
+  gboolean success, eos;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GError *err = NULL;
+
+  if (G_UNLIKELY (offset != src->position)) {
+    if (!GST_GIO_STREAM_IS_SEEKABLE (src->stream))
+      return GST_FLOW_NOT_SUPPORTED;
+
+    ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel);
+
+    if (ret == GST_FLOW_OK)
+      src->position = offset;
+    return ret;
+  }
+
+  buf = gst_buffer_new_and_alloc (size);
+
+  GST_LOG_OBJECT (src, "reading %u bytes from offset %" G_GUINT64_FORMAT,
+      size, offset);
+
+  read =
+      g_input_stream_read (G_INPUT_STREAM (src->stream), GST_BUFFER_DATA (buf),
+      size, src->cancel, &err);
+
+  success = (read >= 0);
+  eos = (size > 0 && read == 0);
+
+  if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) {
+    GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
+        ("Could not read from stream: %s", err->message));
+    g_clear_error (&err);
+  }
+
+  if (success && !eos) {
+    src->position += read;
+    GST_BUFFER_OFFSET (buf) = offset;
+    GST_BUFFER_SIZE (buf) = read;
+    *buf_return = buf;
+  } else {
+    /* !success || eos */
+    gst_buffer_unref (buf);
+  }
+
+  if (eos)
+    ret = GST_FLOW_UNEXPECTED;
+
+  return ret;
+}
+
+void
+gst_gio_base_src_set_stream (GstGioBaseSrc * src, GInputStream * stream)
+{
+  g_return_if_fail (G_IS_INPUT_STREAM (stream));
+  g_return_if_fail ((GST_STATE (src) != GST_STATE_PLAYING &&
+          GST_STATE (src) != GST_STATE_PAUSED));
+
+  if (src->stream) {
+    gboolean success;
+    GError *err = NULL;
+
+    success = g_input_stream_close (src->stream, src->cancel, &err);
+
+    if (!success && !gst_gio_error (src, "g_input_stream_close", &err, NULL)) {
+      GST_WARNING_OBJECT (src, "g_input_stream_close failed: %s", err->message);
+      g_clear_error (&err);
+    }
+
+    g_object_unref (src->stream);
+    src->stream = NULL;
+  }
+
+  src->stream = stream;
+}
diff --git a/ext/gio/gstgiobasesrc.h b/ext/gio/gstgiobasesrc.h
new file mode 100644 (file)
index 0000000..0d0fdb6
--- /dev/null
@@ -0,0 +1,68 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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_GIO_BASE_SRC_H__
+#define __GST_GIO_BASE_SRC_H__
+
+#include "gstgio.h"
+
+#include <gio/gfile.h>
+#include <gio/gfileinputstream.h>
+#include <gio/gmemoryinputstream.h>
+#include <gst/base/gstbasesrc.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_GIO_BASE_SRC \
+  (gst_gio_base_src_get_type())
+#define GST_GIO_BASE_SRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_BASE_SRC,GstGioBaseSrc))
+#define GST_GIO_BASE_SRC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_BASE_SRC,GstGioBaseSrcClass))
+#define GST_IS_GIO_BASE_SRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_BASE_SRC))
+#define GST_IS_GIO_BASE_SRC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_BASE_SRC))
+
+typedef struct _GstGioBaseSrc      GstGioBaseSrc;
+typedef struct _GstGioBaseSrcClass GstGioBaseSrcClass;
+
+struct _GstGioBaseSrc
+{
+  GstBaseSrc src;
+  
+  GCancellable *cancel;
+  guint64 position;
+  GInputStream *stream;
+};
+
+struct _GstGioBaseSrcClass 
+{
+  GstBaseSrcClass parent_class;
+};
+
+GType gst_gio_base_src_get_type (void);
+
+void gst_gio_base_src_set_stream (GstGioBaseSrc *src, GInputStream *stream);
+
+G_END_DECLS
+
+#endif /* __GST_GIO_BASE_SRC_H__ */
index 8929bd9..ad0433b 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -52,13 +53,8 @@ enum
   ARG_LOCATION
 };
 
-static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
-    GST_PAD_SINK,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS_ANY);
-
-GST_BOILERPLATE_FULL (GstGioSink, gst_gio_sink, GstBaseSink, GST_TYPE_BASE_SINK,
-    gst_gio_uri_handler_do_init);
+GST_BOILERPLATE_FULL (GstGioSink, gst_gio_sink, GstGioBaseSink,
+    GST_TYPE_GIO_BASE_SINK, gst_gio_uri_handler_do_init);
 
 static void gst_gio_sink_finalize (GObject * object);
 static void gst_gio_sink_set_property (GObject * object, guint prop_id,
@@ -66,13 +62,6 @@ static void gst_gio_sink_set_property (GObject * object, guint prop_id,
 static void gst_gio_sink_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 static gboolean gst_gio_sink_start (GstBaseSink * base_sink);
-static gboolean gst_gio_sink_stop (GstBaseSink * base_sink);
-static gboolean gst_gio_sink_unlock (GstBaseSink * base_sink);
-static gboolean gst_gio_sink_unlock_stop (GstBaseSink * base_sink);
-static gboolean gst_gio_sink_event (GstBaseSink * base_sink, GstEvent * event);
-static GstFlowReturn gst_gio_sink_render (GstBaseSink * base_sink,
-    GstBuffer * buffer);
-static gboolean gst_gio_sink_query (GstPad * pad, GstQuery * query);
 
 static void
 gst_gio_sink_base_init (gpointer gclass)
@@ -81,14 +70,13 @@ gst_gio_sink_base_init (gpointer gclass)
     "GIO sink",
     "Sink/File",
     "Write to any GIO-supported location",
-    "Ren\xc3\xa9 Stadler <mail@renestadler.de>"
+    "Ren\xc3\xa9 Stadler <mail@renestadler.de>, "
+        "Sebastian Dröge <slomo@circular-chaos.org>"
   };
   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
 
-  GST_DEBUG_CATEGORY_INIT (gst_gio_sink_debug, "giosink", 0, "GIO source");
+  GST_DEBUG_CATEGORY_INIT (gst_gio_sink_debug, "gio_sink", 0, "GIO sink");
 
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&sink_factory));
   gst_element_class_set_details (element_class, &element_details);
 }
 
@@ -112,22 +100,11 @@ gst_gio_sink_class_init (GstGioSinkClass * klass)
           NULL, G_PARAM_READWRITE));
 
   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_gio_sink_start);
-  gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_gio_sink_stop);
-  gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_sink_unlock);
-  gstbasesink_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_gio_sink_unlock_stop);
-  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_gio_sink_event);
-  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_gio_sink_render);
 }
 
 static void
 gst_gio_sink_init (GstGioSink * sink, GstGioSinkClass * gclass)
 {
-  gst_pad_set_query_function (GST_BASE_SINK_PAD (sink),
-      GST_DEBUG_FUNCPTR (gst_gio_sink_query));
-
-  GST_BASE_SINK (sink)->sync = FALSE;
-
-  sink->cancel = g_cancellable_new ();
 }
 
 static void
@@ -135,16 +112,6 @@ gst_gio_sink_finalize (GObject * object)
 {
   GstGioSink *sink = GST_GIO_SINK (object);
 
-  if (sink->cancel) {
-    g_object_unref (sink->cancel);
-    sink->cancel = NULL;
-  }
-
-  if (sink->stream) {
-    g_object_unref (sink->stream);
-    sink->stream = NULL;
-  }
-
   if (sink->location) {
     g_free (sink->location);
     sink->location = NULL;
@@ -195,6 +162,8 @@ gst_gio_sink_start (GstBaseSink * base_sink)
 {
   GstGioSink *sink = GST_GIO_SINK (base_sink);
   GFile *file;
+  GOutputStream *stream;
+  GCancellable *cancel = GST_GIO_BASE_SINK (sink)->cancel;
   gboolean success;
   GError *err = NULL;
 
@@ -212,9 +181,10 @@ gst_gio_sink_start (GstBaseSink * base_sink)
     return FALSE;
   }
 
-  sink->stream =
-      g_file_create (file, G_FILE_CREATE_FLAGS_NONE, sink->cancel, &err);
-  success = (sink->stream != NULL);
+  stream =
+      G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_FLAGS_NONE, cancel,
+          &err));
+  success = (stream != NULL);
 
   g_object_unref (file);
 
@@ -238,187 +208,9 @@ gst_gio_sink_start (GstBaseSink * base_sink)
   if (!success)
     return FALSE;
 
-  sink->position = 0;
-
   GST_DEBUG_OBJECT (sink, "opened location %s", sink->location);
 
-  return TRUE;
-}
-
-static gboolean
-gst_gio_sink_stop (GstBaseSink * base_sink)
-{
-  GstGioSink *sink = GST_GIO_SINK (base_sink);
-  gboolean success = TRUE;
-  GError *err = NULL;
-
-  if (sink->stream != NULL) {
-    /* FIXME: In case that the call below would block, there is no one to
-     * trigger the cancellation! */
+  gst_gio_base_sink_set_stream (GST_GIO_BASE_SINK (sink), stream);
 
-    success = g_output_stream_close (G_OUTPUT_STREAM (sink->stream),
-        sink->cancel, &err);
-
-    if (success) {
-      GST_DEBUG_OBJECT (sink, "closed location %s", sink->location);
-    } else if (!gst_gio_error (sink, "g_output_stream_close", &err, NULL)) {
-      GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, (NULL),
-          ("g_output_stream_close failed: %s", err->message));
-      g_clear_error (&err);
-    }
-
-    g_object_unref (sink->stream);
-    sink->stream = NULL;
-  }
-
-  return success;
-}
-
-static gboolean
-gst_gio_sink_unlock (GstBaseSink * base_sink)
-{
-  GstGioSink *sink = GST_GIO_SINK (base_sink);
-
-  GST_LOG_OBJECT (sink, "triggering cancellation");
-
-  g_cancellable_cancel (sink->cancel);
-
-  return TRUE;
-}
-
-static gboolean
-gst_gio_sink_unlock_stop (GstBaseSink * base_sink)
-{
-  GstGioSink *sink = GST_GIO_SINK (base_sink);
-
-  GST_LOG_OBJECT (sink, "resetting cancellable");
-
-  g_cancellable_reset (sink->cancel);
-
-  return TRUE;
-}
-
-static gboolean
-gst_gio_sink_event (GstBaseSink * base_sink, GstEvent * event)
-{
-  GstGioSink *sink = GST_GIO_SINK (base_sink);
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  if (sink->stream == NULL)
-    return TRUE;
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_NEWSEGMENT:
-    {
-      GstFormat format;
-      gint64 offset;
-
-      gst_event_parse_new_segment (event, NULL, NULL, &format, &offset, NULL,
-          NULL);
-
-      if (format != GST_FORMAT_BYTES) {
-        GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format",
-            gst_format_get_name (format));
-        break;
-      }
-
-      ret = gst_gio_seek (sink, G_SEEKABLE (sink->stream), offset,
-          sink->cancel);
-
-      if (ret == GST_FLOW_OK)
-        sink->position = offset;
-    }
-      break;
-
-    case GST_EVENT_EOS:
-    case GST_EVENT_FLUSH_START:
-    {
-      gboolean success;
-      GError *err = NULL;
-
-      success = g_output_stream_flush (G_OUTPUT_STREAM (sink->stream),
-          sink->cancel, &err);
-
-      if (!success && !gst_gio_error (sink, "g_output_stream_flush", &err,
-              &ret)) {
-        GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
-            ("flush failed: %s", err->message));
-        g_clear_error (&err);
-      }
-    }
-      break;
-
-    default:
-      break;
-  }
-
-  return (ret == GST_FLOW_OK);
-}
-
-static GstFlowReturn
-gst_gio_sink_render (GstBaseSink * base_sink, GstBuffer * buffer)
-{
-  GstGioSink *sink = GST_GIO_SINK (base_sink);
-  gssize written;
-  gboolean success;
-  GError *err = NULL;
-
-  GST_LOG_OBJECT (sink, "writing %u bytes to offset %" G_GUINT64_FORMAT,
-      GST_BUFFER_SIZE (buffer), sink->position);
-
-  written = g_output_stream_write (G_OUTPUT_STREAM (sink->stream),
-      GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), sink->cancel, &err);
-
-  success = (written >= 0);
-
-  if (G_UNLIKELY (success && written < GST_BUFFER_SIZE (buffer))) {
-    /* FIXME: Can this happen?  Should we handle it gracefully?  gnomevfssink
-     * doesn't... */
-    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
-        ("Could not write to location %s: (short write, only %"
-            G_GUINT64_FORMAT " bytes of %d bytes written)",
-            sink->location, written, GST_BUFFER_SIZE (buffer)));
-    return GST_FLOW_ERROR;
-  }
-
-  if (success) {
-    sink->position += written;
-    return GST_FLOW_OK;
-
-  } else {
-    GstFlowReturn ret;
-
-    if (!gst_gio_error (sink, "g_output_stream_write", &err, &ret)) {
-      GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
-          ("Could not write to location %s: %s", sink->location, err->message));
-      g_clear_error (&err);
-    }
-
-    return ret;
-  }
-}
-
-static gboolean
-gst_gio_sink_query (GstPad * pad, GstQuery * query)
-{
-  GstGioSink *sink = GST_GIO_SINK (GST_PAD_PARENT (pad));
-  GstFormat format;
-
-  switch (GST_QUERY_TYPE (query)) {
-    case GST_QUERY_POSITION:
-      gst_query_parse_position (query, &format, NULL);
-      switch (format) {
-        case GST_FORMAT_BYTES:
-        case GST_FORMAT_DEFAULT:
-          gst_query_set_position (query, GST_FORMAT_BYTES, sink->position);
-          return TRUE;
-        default:
-          return FALSE;
-      }
-    case GST_QUERY_FORMATS:
-      gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
-      return TRUE;
-    default:
-      return gst_pad_query_default (pad, query);
-  }
+  return GST_BASE_SINK_CLASS (parent_class)->start (base_sink);
 }
index a9bdbed..88f220b 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __GSTGIOSINK_H__
-#define __GSTGIOSINK_H__
+#ifndef __GST_GIO_SINK_H__
+#define __GST_GIO_SINK_H__
 
 #include "gstgio.h"
+#include "gstgiobasesink.h"
 
 #include <gio/gfile.h>
 #include <gst/base/gstbasesink.h>
@@ -49,22 +51,19 @@ typedef struct _GstGioSinkClass GstGioSinkClass;
  */
 struct _GstGioSink
 {
-  GstBaseSink sink;
+  GstGioBaseSink sink;
 
   /*< private >*/
-  GCancellable *cancel;
   gchar *location;
-  guint64 position;
-  GFileOutputStream *stream;
 };
 
 struct _GstGioSinkClass 
 {
-  GstBaseSinkClass parent_class;
+  GstGioBaseSinkClass parent_class;
 };
 
 GType gst_gio_sink_get_type (void);
 
 G_END_DECLS
 
-#endif /* __GSTGIOSINK_H__ */
+#endif /* __GST_GIO_SINK_H__ */
index e984e14..ee336f0 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -46,13 +47,8 @@ enum
   ARG_LOCATION
 };
 
-static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
-    GST_PAD_SRC,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS_ANY);
-
-GST_BOILERPLATE_FULL (GstGioSrc, gst_gio_src, GstBaseSrc, GST_TYPE_BASE_SRC,
-    gst_gio_uri_handler_do_init);
+GST_BOILERPLATE_FULL (GstGioSrc, gst_gio_src, GstGioBaseSrc,
+    GST_TYPE_GIO_BASE_SRC, gst_gio_uri_handler_do_init);
 
 static void gst_gio_src_finalize (GObject * object);
 static void gst_gio_src_set_property (GObject * object, guint prop_id,
@@ -60,14 +56,6 @@ static void gst_gio_src_set_property (GObject * object, guint prop_id,
 static void gst_gio_src_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 static gboolean gst_gio_src_start (GstBaseSrc * base_src);
-static gboolean gst_gio_src_stop (GstBaseSrc * base_src);
-static gboolean gst_gio_src_get_size (GstBaseSrc * base_src, guint64 * size);
-static gboolean gst_gio_src_is_seekable (GstBaseSrc * base_src);
-static gboolean gst_gio_src_unlock (GstBaseSrc * base_src);
-static gboolean gst_gio_src_unlock_stop (GstBaseSrc * base_src);
-static gboolean gst_gio_src_check_get_range (GstBaseSrc * base_src);
-static GstFlowReturn gst_gio_src_create (GstBaseSrc * base_src, guint64 offset,
-    guint size, GstBuffer ** buf);
 
 static void
 gst_gio_src_base_init (gpointer gclass)
@@ -76,14 +64,13 @@ gst_gio_src_base_init (gpointer gclass)
     "GIO source",
     "Source/File",
     "Read from any GIO-supported location",
-    "Ren\xc3\xa9 Stadler <mail@renestadler.de>"
+    "Ren\xc3\xa9 Stadler <mail@renestadler.de>, "
+        "Sebastian Dröge <slomo@circular-chaos.org>"
   };
   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
 
-  GST_DEBUG_CATEGORY_INIT (gst_gio_src_debug, "giosrc", 0, "GIO source");
+  GST_DEBUG_CATEGORY_INIT (gst_gio_src_debug, "gio_src", 0, "GIO source");
 
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&src_factory));
   gst_element_class_set_details (element_class, &element_details);
 }
 
@@ -107,20 +94,11 @@ gst_gio_src_class_init (GstGioSrcClass * klass)
           NULL, G_PARAM_READWRITE));
 
   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gio_src_start);
-  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gio_src_stop);
-  gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gio_src_get_size);
-  gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_gio_src_is_seekable);
-  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_gio_src_unlock);
-  gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_gio_src_unlock_stop);
-  gstbasesrc_class->check_get_range =
-      GST_DEBUG_FUNCPTR (gst_gio_src_check_get_range);
-  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gio_src_create);
 }
 
 static void
 gst_gio_src_init (GstGioSrc * src, GstGioSrcClass * gclass)
 {
-  src->cancel = g_cancellable_new ();
 }
 
 static void
@@ -128,16 +106,6 @@ gst_gio_src_finalize (GObject * object)
 {
   GstGioSrc *src = GST_GIO_SRC (object);
 
-  if (src->cancel) {
-    g_object_unref (src->cancel);
-    src->cancel = NULL;
-  }
-
-  if (src->stream) {
-    g_object_unref (src->stream);
-    src->stream = NULL;
-  }
-
   if (src->location) {
     g_free (src->location);
     src->location = NULL;
@@ -189,6 +157,8 @@ gst_gio_src_start (GstBaseSrc * base_src)
   GstGioSrc *src = GST_GIO_SRC (base_src);
   GFile *file;
   GError *err = NULL;
+  GInputStream *stream;
+  GCancellable *cancel = GST_GIO_BASE_SRC (src)->cancel;
 
   if (src->location == NULL) {
     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No location given"));
@@ -203,11 +173,11 @@ gst_gio_src_start (GstBaseSrc * base_src)
     return FALSE;
   }
 
-  src->stream = g_file_read (file, src->cancel, &err);
+  stream = G_INPUT_STREAM (g_file_read (file, cancel, &err));
 
   g_object_unref (file);
 
-  if (src->stream == NULL && !gst_gio_error (src, "g_file_read", &err, NULL)) {
+  if (stream == NULL && !gst_gio_error (src, "g_file_read", &err, NULL)) {
 
     if (GST_GIO_ERROR_MATCHES (err, NOT_FOUND))
       GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
@@ -222,174 +192,13 @@ gst_gio_src_start (GstBaseSrc * base_src)
 
     return FALSE;
 
-  } else if (src->stream == NULL) {
+  } else if (stream == NULL) {
     return FALSE;
   }
 
-  src->position = 0;
+  gst_gio_base_src_set_stream (GST_GIO_BASE_SRC (src), stream);
 
   GST_DEBUG_OBJECT (src, "opened location %s", src->location);
 
-  return TRUE;
-}
-
-static gboolean
-gst_gio_src_stop (GstBaseSrc * base_src)
-{
-  GstGioSrc *src = GST_GIO_SRC (base_src);
-  gboolean success = TRUE;
-  GError *err = NULL;
-
-  if (src->stream != NULL) {
-    /* FIXME: In case that the call below would block, there is no one to
-     * trigger the cancellation! */
-
-    success = g_input_stream_close (G_INPUT_STREAM (src->stream), src->cancel,
-        &err);
-
-    if (!success && !gst_gio_error (src, "g_input_stream_close", &err, NULL)) {
-      GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
-          ("g_input_stream_close failed: %s", err->message));
-      g_clear_error (&err);
-    }
-
-    g_object_unref (src->stream);
-    src->stream = NULL;
-  }
-
-  GST_DEBUG_OBJECT (src, "closed location %s", src->location);
-
-  return success;
-}
-
-static gboolean
-gst_gio_src_get_size (GstBaseSrc * base_src, guint64 * size)
-{
-  GstGioSrc *src = GST_GIO_SRC (base_src);
-  GFileInfo *info;
-  GError *err = NULL;
-
-  info = g_file_input_stream_query_info (src->stream,
-      G_FILE_ATTRIBUTE_STD_SIZE, src->cancel, &err);
-
-  if (info != NULL) {
-    *size = g_file_info_get_size (info);
-    g_object_unref (info);
-    GST_DEBUG_OBJECT (src, "found size: %" G_GUINT64_FORMAT, *size);
-    return TRUE;
-  }
-
-  if (!gst_gio_error (src, "g_file_input_stream_query_info", &err, NULL)) {
-
-    if (GST_GIO_ERROR_MATCHES (err, NOT_SUPPORTED))
-      GST_DEBUG_OBJECT (src, "size information not available");
-    else
-      GST_WARNING_OBJECT (src, "size information retrieval failed: %s",
-          err->message);
-
-    g_clear_error (&err);
-  }
-
-  return FALSE;
-}
-
-static gboolean
-gst_gio_src_is_seekable (GstBaseSrc * base_src)
-{
-  GstGioSrc *src = GST_GIO_SRC (base_src);
-  gboolean seekable;
-
-  seekable = g_seekable_can_seek (G_SEEKABLE (src->stream));
-
-  GST_DEBUG_OBJECT (src, "can seek: %d", seekable);
-
-  return seekable;
-}
-
-static gboolean
-gst_gio_src_unlock (GstBaseSrc * base_src)
-{
-  GstGioSrc *src = GST_GIO_SRC (base_src);
-
-  GST_LOG_OBJECT (src, "triggering cancellation");
-
-  g_cancellable_cancel (src->cancel);
-
-  return TRUE;
-}
-
-static gboolean
-gst_gio_src_unlock_stop (GstBaseSrc * base_src)
-{
-  GstGioSrc *src = GST_GIO_SRC (base_src);
-
-  GST_LOG_OBJECT (src, "resetting cancellable");
-
-  g_cancellable_reset (src->cancel);
-
-  return TRUE;
-}
-
-static gboolean
-gst_gio_src_check_get_range (GstBaseSrc * base_src)
-{
-  /* FIXME: Implement dry-run variant using guesswork like gnomevfssrc? */
-
-  return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SRC_CLASS,
-      check_get_range, (base_src), FALSE);
-}
-
-static GstFlowReturn
-gst_gio_src_create (GstBaseSrc * base_src, guint64 offset, guint size,
-    GstBuffer ** buf_return)
-{
-  GstGioSrc *src = GST_GIO_SRC (base_src);
-  GstBuffer *buf;
-  gssize read;
-  gboolean success, eos;
-  GstFlowReturn ret = GST_FLOW_OK;
-  GError *err = NULL;
-
-  if (G_UNLIKELY (offset != src->position)) {
-
-    ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel);
-
-    if (ret == GST_FLOW_OK)
-      src->position = offset;
-    else
-      return ret;
-  }
-
-  buf = gst_buffer_new_and_alloc (size);
-
-  GST_LOG_OBJECT (src, "reading %u bytes from offset %" G_GUINT64_FORMAT,
-      size, offset);
-
-  read =
-      g_input_stream_read (G_INPUT_STREAM (src->stream), GST_BUFFER_DATA (buf),
-      size, src->cancel, &err);
-
-  success = (read >= 0);
-  eos = (size > 0 && read == 0);
-
-  if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) {
-    GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
-        ("Could not read from location %s: %s", src->location, err->message));
-    g_clear_error (&err);
-  }
-
-  if (success && !eos) {
-    src->position += read;
-    GST_BUFFER_OFFSET (buf) = offset;
-    GST_BUFFER_SIZE (buf) = read;
-    *buf_return = buf;
-  } else {
-    /* !success || eos */
-    gst_buffer_unref (buf);
-  }
-
-  if (eos)
-    ret = GST_FLOW_UNEXPECTED;
-
-  return ret;
+  return GST_BASE_SRC_CLASS (parent_class)->start (base_src);
 }
index 7fcfaae..967137a 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  *
  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __GSTGIOSRC_H__
-#define __GSTGIOSRC_H__
+#ifndef __GST_GIO_SRC_H__
+#define __GST_GIO_SRC_H__
 
 #include "gstgio.h"
+#include "gstgiobasesrc.h"
 
 #include <gio/gfile.h>
 #include <gst/base/gstbasesrc.h>
@@ -49,22 +51,19 @@ typedef struct _GstGioSrcClass GstGioSrcClass;
  */
 struct _GstGioSrc
 {
-  GstBaseSrc src;
+  GstGioBaseSrc src;
   
   /*< private >*/
-  GCancellable *cancel;
   gchar *location;
-  guint64 position;
-  GFileInputStream *stream;
 };
 
 struct _GstGioSrcClass 
 {
-  GstBaseSrcClass parent_class;
+  GstGioBaseSrcClass parent_class;
 };
 
 GType gst_gio_src_get_type (void);
 
 G_END_DECLS
 
-#endif /* __GSTGIOSRC_H__ */
+#endif /* __GST_GIO_SRC_H__ */
diff --git a/ext/gio/gstgiostreamsink.c b/ext/gio/gstgiostreamsink.c
new file mode 100644 (file)
index 0000000..f27d299
--- /dev/null
@@ -0,0 +1,155 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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-giostreamsink
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * <para>
+ * <programlisting>
+ * gst-launch audiotestsrc num-buffers=100 ! flacenc ! giosink location=file:///home/foo/bar.flac
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstgiostreamsink.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_gio_stream_sink_debug);
+#define GST_CAT_DEFAULT gst_gio_stream_sink_debug
+
+/* Filter signals and args */
+enum
+{
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  ARG_STREAM
+};
+
+GST_BOILERPLATE (GstGioStreamSink, gst_gio_stream_sink, GstGioBaseSink,
+    GST_TYPE_GIO_BASE_SINK);
+
+static void gst_gio_stream_sink_finalize (GObject * object);
+static void gst_gio_stream_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_gio_stream_sink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static void
+gst_gio_stream_sink_base_init (gpointer gclass)
+{
+  static GstElementDetails element_details = {
+    "GIO stream sink",
+    "Sink",
+    "Write to any GIO stream",
+    "Sebastian Dröge <slomo@circular-chaos.org>"
+  };
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+  GST_DEBUG_CATEGORY_INIT (gst_gio_stream_sink_debug, "gio_stream_sink", 0,
+      "GIO stream sink");
+
+  gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+gst_gio_stream_sink_class_init (GstGioStreamSinkClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSinkClass *gstbasesink_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesink_class = (GstBaseSinkClass *) klass;
+
+  gobject_class->finalize = gst_gio_stream_sink_finalize;
+  gobject_class->set_property = gst_gio_stream_sink_set_property;
+  gobject_class->get_property = gst_gio_stream_sink_get_property;
+
+  g_object_class_install_property (gobject_class, ARG_STREAM,
+      g_param_spec_object ("stream", "Stream", "Stream to write to",
+          G_TYPE_OUTPUT_STREAM, G_PARAM_READWRITE));
+}
+
+static void
+gst_gio_stream_sink_init (GstGioStreamSink * sink,
+    GstGioStreamSinkClass * gclass)
+{
+}
+
+static void
+gst_gio_stream_sink_finalize (GObject * object)
+{
+  GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static void
+gst_gio_stream_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstGioStreamSink *sink = GST_GIO_STREAM_SINK (object);
+
+  switch (prop_id) {
+    case ARG_STREAM:{
+      GObject *stream;
+
+      if (GST_STATE (sink) == GST_STATE_PLAYING ||
+          GST_STATE (sink) == GST_STATE_PAUSED)
+        break;
+
+      stream = g_value_dup_object (value);
+      if (G_IS_OUTPUT_STREAM (stream))
+        gst_gio_base_sink_set_stream (GST_GIO_BASE_SINK (sink),
+            G_OUTPUT_STREAM (stream));
+
+      break;
+    }
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_gio_stream_sink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstGioStreamSink *sink = GST_GIO_STREAM_SINK (object);
+
+  switch (prop_id) {
+    case ARG_STREAM:
+      g_value_set_object (value, GST_GIO_BASE_SINK (sink)->stream);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
diff --git a/ext/gio/gstgiostreamsink.h b/ext/gio/gstgiostreamsink.h
new file mode 100644 (file)
index 0000000..47b7809
--- /dev/null
@@ -0,0 +1,66 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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_GIO_STREAM_SINK_H__
+#define __GST_GIO_STREAM_SINK_H__
+
+#include "gstgio.h"
+#include "gstgiobasesink.h"
+
+#include <gio/gfile.h>
+#include <gst/base/gstbasesink.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_GIO_STREAM_SINK \
+  (gst_gio_stream_sink_get_type())
+#define GST_GIO_STREAM_SINK(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_STREAM_SINK,GstGioStreamSink))
+#define GST_GIO_STREAM_SINK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_STREAM_SINK,GstGioStreamSinkClass))
+#define GST_IS_GIO_STREAM_SINK(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_STREAM_SINK))
+#define GST_IS_GIO_STREAM_SINK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_STREAM_SINK))
+
+typedef struct _GstGioStreamSink      GstGioStreamSink;
+typedef struct _GstGioStreamSinkClass GstGioStreamSinkClass;
+
+/**
+ * GstGioStreamSink:
+ *
+ * Opaque data structure.
+ */
+struct _GstGioStreamSink
+{
+  GstGioBaseSink sink;
+};
+
+struct _GstGioStreamSinkClass 
+{
+  GstGioBaseSinkClass parent_class;
+};
+
+GType gst_gio_stream_sink_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_GIO_STREAM_SINK_H__ */
diff --git a/ext/gio/gstgiostreamsrc.c b/ext/gio/gstgiostreamsrc.c
new file mode 100644 (file)
index 0000000..3683c98
--- /dev/null
@@ -0,0 +1,148 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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-giostreamsrc
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * <para>
+ * <programlisting>
+ * gst-launch giosrc location=file:///home/foo/bar.ext ! fakesink
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstgiostreamsrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_gio_stream_src_debug);
+#define GST_CAT_DEFAULT gst_gio_stream_src_debug
+
+enum
+{
+  ARG_0,
+  ARG_STREAM
+};
+
+GST_BOILERPLATE (GstGioStreamSrc, gst_gio_stream_src, GstGioBaseSrc,
+    GST_TYPE_GIO_BASE_SRC);
+
+static void gst_gio_stream_src_finalize (GObject * object);
+static void gst_gio_stream_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_gio_stream_src_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static void
+gst_gio_stream_src_base_init (gpointer gclass)
+{
+  static GstElementDetails element_details = {
+    "GIO stream source",
+    "Source",
+    "Read from any GIO stream",
+    "Sebastian Dröge <slomo@circular-chaos.org>"
+  };
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+  GST_DEBUG_CATEGORY_INIT (gst_gio_stream_src_debug, "gio_stream_src", 0,
+      "GIO source");
+
+  gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+gst_gio_stream_src_class_init (GstGioStreamSrcClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSrcClass *gstbasesrc_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesrc_class = (GstBaseSrcClass *) klass;
+
+  gobject_class->finalize = gst_gio_stream_src_finalize;
+  gobject_class->set_property = gst_gio_stream_src_set_property;
+  gobject_class->get_property = gst_gio_stream_src_get_property;
+
+  g_object_class_install_property (gobject_class, ARG_STREAM,
+      g_param_spec_object ("stream", "Stream", "Stream to read from",
+          G_TYPE_INPUT_STREAM, G_PARAM_READWRITE));
+}
+
+static void
+gst_gio_stream_src_init (GstGioStreamSrc * src, GstGioStreamSrcClass * gclass)
+{
+}
+
+static void
+gst_gio_stream_src_finalize (GObject * object)
+{
+  GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static void
+gst_gio_stream_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstGioStreamSrc *src = GST_GIO_STREAM_SRC (object);
+
+  switch (prop_id) {
+    case ARG_STREAM:{
+      GObject *stream;
+
+      if (GST_STATE (src) == GST_STATE_PLAYING ||
+          GST_STATE (src) == GST_STATE_PAUSED)
+        break;
+
+      stream = g_value_dup_object (value);
+      if (G_IS_INPUT_STREAM (stream))
+        gst_gio_base_src_set_stream (GST_GIO_BASE_SRC (src),
+            G_INPUT_STREAM (stream));
+
+      break;
+    }
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_gio_stream_src_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstGioStreamSrc *src = GST_GIO_STREAM_SRC (object);
+
+  switch (prop_id) {
+    case ARG_STREAM:
+      g_value_set_object (value, GST_GIO_BASE_SRC (src)->stream);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
diff --git a/ext/gio/gstgiostreamsrc.h b/ext/gio/gstgiostreamsrc.h
new file mode 100644 (file)
index 0000000..0321b6f
--- /dev/null
@@ -0,0 +1,66 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ * 
+ * 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_GIO_STREAM_SRC_H__
+#define __GST_GIO_STREAM_SRC_H__
+
+#include "gstgio.h"
+#include "gstgiobasesrc.h"
+
+#include <gio/gfile.h>
+#include <gst/base/gstbasesrc.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_GIO_STREAM_SRC \
+  (gst_gio_stream_src_get_type())
+#define GST_GIO_STREAM_SRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GIO_STREAM_SRC,GstGioStreamSrc))
+#define GST_GIO_STREAM_SRC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GIO_STREAM_SRC,GstGioStreamSrcClass))
+#define GST_IS_GIO_STREAM_SRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GIO_STREAM_SRC))
+#define GST_IS_GIO_STREAM_SRC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GIO_STREAM_SRC))
+
+typedef struct _GstGioStreamSrc      GstGioStreamSrc;
+typedef struct _GstGioStreamSrcClass GstGioStreamSrcClass;
+
+/**
+ * GstGioStreamSrc:
+ *
+ * Opaque data structure.
+ */
+struct _GstGioStreamSrc
+{
+  GstGioBaseSrc src;
+};
+
+struct _GstGioStreamSrcClass 
+{
+  GstGioBaseSrcClass parent_class;
+};
+
+GType gst_gio_stream_src_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_GIO_STREAM_SRC_H__ */
diff --git a/tests/check/pipelines/gio.c b/tests/check/pipelines/gio.c
new file mode 100644 (file)
index 0000000..7597123
--- /dev/null
@@ -0,0 +1,160 @@
+/* GStreamer
+ *
+ * unit test for GIO
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/check/gstbufferstraw.h>
+#include <gio/gmemoryinputstream.h>
+#include <gio/gmemoryoutputstream.h>
+
+static gboolean got_eos = FALSE;
+
+static gboolean
+message_handler (GstBus * bus, GstMessage * msg, gpointer data)
+{
+  GMainLoop *loop = (GMainLoop *) data;
+
+  switch (GST_MESSAGE_TYPE (msg)) {
+    case GST_MESSAGE_EOS:
+      got_eos = TRUE;
+      g_main_loop_quit (loop);
+      break;
+    case GST_MESSAGE_ERROR:{
+      gchar *debug;
+      GError *err;
+
+      gst_message_parse_error (msg, &err, &debug);
+      g_free (debug);
+
+      /* Will abort the check */
+      g_warning ("Error: %s\n", err->message);
+      g_error_free (err);
+
+      g_main_loop_quit (loop);
+      break;
+    }
+    default:
+      break;
+  }
+
+  return TRUE;
+}
+
+GST_START_TEST (test_memory_stream)
+{
+  GMainLoop *loop;
+  GstElement *bin;
+  GstElement *src, *sink;
+  GstBus *bus;
+
+  GMemoryInputStream *input;
+  GMemoryOutputStream *output;
+
+  guint8 *in_data;
+  GByteArray *out_data;
+  gint i;
+
+  got_eos = FALSE;
+
+  in_data = g_new (guint8, 512);
+  for (i = 0; i < 512; i++)
+    in_data[i] = i % 256;
+
+  input =
+      G_MEMORY_INPUT_STREAM (g_memory_input_stream_from_data (in_data, 512));
+  g_memory_input_stream_set_free_data (input, TRUE);
+
+  out_data = g_byte_array_new ();
+  output = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (out_data));
+  g_memory_output_stream_set_free_on_close (output, FALSE);
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  bin = gst_pipeline_new ("bin");
+
+  src = gst_element_factory_make ("giostreamsrc", "src");
+  fail_unless (src != NULL);
+  g_object_set (G_OBJECT (src), "stream", input, NULL);
+
+  sink = gst_element_factory_make ("giostreamsink", "sink");
+  fail_unless (sink != NULL);
+  g_object_set (G_OBJECT (sink), "stream", output, NULL);
+
+  gst_bin_add_many (GST_BIN (bin), src, sink, NULL);
+
+  fail_unless (gst_element_link_many (src, sink, NULL));
+
+  bus = gst_element_get_bus (bin);
+  gst_bus_add_watch (bus, message_handler, loop);
+  gst_object_unref (bus);
+
+  gst_element_set_state (bin, GST_STATE_PLAYING);
+
+  g_main_loop_run (loop);
+
+  gst_element_set_state (bin, GST_STATE_NULL);
+  gst_object_unref (bin);
+
+  fail_unless (got_eos);
+
+  for (i = 0; i < 512; i++) {
+    fail_unless_equals_int (in_data[i], out_data->data[i]);
+  }
+
+  g_byte_array_free (out_data, TRUE);
+
+  g_object_unref (input);
+  g_object_unref (output);
+
+  g_main_loop_unref (loop);
+
+}
+
+GST_END_TEST;
+
+Suite *
+gio_testsuite (void)
+{
+  Suite *s = suite_create ("gio");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_memory_stream);
+
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = gio_testsuite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}