? gst/gstbytestream.c
? gst/gstbytestream.h
Index: docs/random/interfaces
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/docs/random/interfaces,v
retrieving revision 1.6
diff -u -r1.6 interfaces
--- docs/random/interfaces	13 Sep 2003 10:17:49 -0000	1.6
+++ docs/random/interfaces	29 Sep 2003 08:58:22 -0000
@@ -210,17 +210,6 @@
 So the application would just tread it with the known macro, and
 everything would look extremely simple to the end user.
 
-Also some convenience function to set the element:
-
-void
-gst_interface_set_element (GstInterface *iface,
-			   GstElement   *element)
-{
-  g_return_if_fail (GST_IS_INTERFACE (iface));
-
-  iface->element = element;
-}
-
 4a) mixer
 ---------
 A mixer is a way of controlling volume and input/output channels.
@@ -317,7 +306,7 @@
 			     XID                xid);
 } GstOverlayClass;
 
-That's all!
+That's all! It would look similar for FB & co.
 
 4c) user input
 --------------
@@ -335,8 +324,9 @@
 
 5) Status of this document
 ==========================
-This is a proposal, nothing more. Nothing is implemented. Target
-release is 0.8.0 or any 0.7.x version.
+The interfaces are implemented, more (for metadata, framebuffer-
+overlay, video balancing (brightness), user input etc. are all
+pending.
 
 6) Copyright and blabla
 =======================
Index: gst/Makefile.am
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/Makefile.am,v
retrieving revision 1.138
diff -u -r1.138 Makefile.am
--- gst/Makefile.am	13 Sep 2003 01:10:11 -0000	1.138
+++ gst/Makefile.am	29 Sep 2003 08:58:22 -0000
@@ -16,13 +16,13 @@
 endif
 
 if GST_DISABLE_TYPEFIND
-GST_TYPEFIND_SRC = 
+GST_TYPEFIND_SRC =
 else
 GST_TYPEFIND_SRC = gsttypefind.c
 endif
 
 if GST_DISABLE_AUTOPLUG
-GST_AUTOPLUG_SRC = 
+GST_AUTOPLUG_SRC =
 GST_AUTOPLUG_DIRS =
 else
 GST_AUTOPLUG_SRC = gstautoplug.c
@@ -91,11 +91,12 @@
 	gstmarshal.c		\
 	$(GST_ENUMTYPES_SRC)	\
 	gstobject.c		\
-	$(GST_AUTOPLUG_SRC)	\
 	gstatomic.c		\
+	$(GST_AUTOPLUG_SRC)	\
 	gstbin.c		\
 	gstbuffer.c		\
 	gstbufferpool-default.c	\
+	gstbytestream.c		\
 	gstcaps.c		\
 	gstclock.c		\
 	gstcpu.c		\
@@ -157,6 +158,7 @@
 	gstbin.h		\
 	gstbuffer.h		\
 	gstbufferpool-default.h \
+	gstbytestream.h		\
 	gstcaps.h		\
 	gstclock.h		\
 	gstcompat.h		\
Index: gst/gsttype.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gsttype.c,v
retrieving revision 1.42
diff -u -r1.42 gsttype.c
--- gst/gsttype.c	29 Jun 2003 14:05:43 -0000	1.42
+++ gst/gsttype.c	29 Sep 2003 08:58:22 -0000
@@ -29,6 +29,7 @@
 
 #include "gst_private.h"
 
+#include "gstbytestream.h"
 #include "gsttype.h"
 #include "gstregistrypool.h"
 #include "gstobject.h"
@@ -42,9 +43,7 @@
 static void 		gst_type_factory_class_init 	(GstTypeFactoryClass *klass);
 static void 		gst_type_factory_init 		(GstTypeFactory *factory);
 
-static GstCaps*		gst_type_type_find_dummy		(GstBuffer *buffer, gpointer priv);
-
-static void 		gst_type_factory_unload_thyself 	(GstPluginFeature *feature);
+static void 		gst_type_factory_unload_thyself	(GstPluginFeature *feature);
 
 static GstPluginFeatureClass *parent_class = NULL;
 /* static guint gst_type_factory_signals[LAST_SIGNAL] = { 0 }; */
@@ -315,8 +314,8 @@
     factory->typefindfunc = gst_type_type_find_dummy;
 }
 
-static GstCaps*
-gst_type_type_find_dummy (GstBuffer *buffer, gpointer priv)
+GstCaps*
+gst_type_type_find_dummy (GstByteStream *bs, gpointer priv)
 {
   GstCaps *res = NULL;
   GstTypeFactory *factory = (GstTypeFactory *)priv;
@@ -329,7 +328,7 @@
       g_warning ("could not load valid typefind function for %s\n", factory->mime);
     }
     else if (factory->typefindfunc) {
-      res = factory->typefindfunc (buffer, factory);
+      res = factory->typefindfunc (bs, priv);
     }
   }
 
Index: gst/gsttype.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gsttype.h,v
retrieving revision 1.18
diff -u -r1.18 gsttype.h
--- gst/gsttype.h	19 Dec 2002 21:31:02 -0000	1.18
+++ gst/gsttype.h	29 Sep 2003 08:58:22 -0000
@@ -24,14 +24,14 @@
 #ifndef __GST_TYPE_H__
 #define __GST_TYPE_H__
 
-#include <gst/gstbuffer.h>
 #include <gst/gstcaps.h>
 #include <gst/gstpluginfeature.h>
+#include <gst/gstbytestream.h>
 
 G_BEGIN_DECLS
 
 /* type of function used to check a stream for equality with type */
-typedef GstCaps*	(*GstTypeFindFunc) 	(GstBuffer *buf, gpointer priv);
+typedef GstCaps*	(*GstTypeFindFunc) 	(GstByteStream *bs, gpointer priv);
 
 typedef struct _GstType GstType;
 typedef struct _GstTypeDefinition GstTypeDefinition;
@@ -74,7 +74,7 @@
 struct _GstTypeFactoryClass {
   GstPluginFeatureClass parent;
 
-  gpointer		 dummy[8];
+  gpointer		dummy[8];
 };
 
 
@@ -97,6 +97,9 @@
 
 /* get the list of registered types (returns list of GstType!) */
 const GList*		gst_type_get_list		(void);
+
+/* dummy typefind function */
+GstCaps*		gst_type_type_find_dummy	(GstByteStream *bs, gpointer priv);
 
 G_END_DECLS
 
Index: gst/gsttypefind.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gsttypefind.c,v
retrieving revision 1.36
diff -u -r1.36 gsttypefind.c
--- gst/gsttypefind.c	16 Sep 2003 09:57:30 -0000	1.36
+++ gst/gsttypefind.c	29 Sep 2003 08:58:22 -0000
@@ -26,8 +26,6 @@
 #include "gstinfo.h"
 #include "gsttypefind.h"
 
-#define DEFAULT_MAX_BUFFERS	1
-
 GstElementDetails gst_type_find_details = {
   "TypeFind",
   "Generic",
@@ -39,6 +37,13 @@
   "(C) 1999",
 };
 
+/* generic templates */
+GST_PAD_TEMPLATE_FACTORY (type_find_sink_factory,
+  "sink",
+  GST_PAD_SINK,
+  GST_PAD_ALWAYS,
+  NULL
+);
 
 /* TypeFind signals and args */
 enum {
@@ -49,7 +54,6 @@
 enum {
   ARG_0,
   ARG_CAPS,
-  ARG_MAX_BUFFERS,
 };
 
 
@@ -63,7 +67,7 @@
 						 GValue *value, 
 						 GParamSpec *pspec);
 
-static void	gst_type_find_chain		(GstPad *pad, GstBuffer *buf);
+static void	gst_type_find_loopfunc		(GstElement *element);
 static GstElementStateReturn
 		gst_type_find_change_state 	(GstElement *element);
 
@@ -88,7 +92,9 @@
       (GInstanceInitFunc)gst_type_find_init,
       NULL
     };
-    typefind_type = g_type_register_static (GST_TYPE_ELEMENT, "GstTypeFind", &typefind_info, 0);
+    typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
+					    "GstTypeFind",
+					    &typefind_info, 0);
   }
   return typefind_type;
 }
@@ -106,11 +112,6 @@
 
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CAPS,
     g_param_spec_pointer ("caps", "Caps", "Found capabilities", G_PARAM_READABLE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BUFFERS,
-    g_param_spec_int ("max_buffers", 
-	    	      "Max Buffers", 
-		      "Maximal amount of buffers before giving en error (0 == unlimited)", 
-		      0, G_MAXINT, DEFAULT_MAX_BUFFERS, G_PARAM_READWRITE));
 
   gst_type_find_signals[HAVE_TYPE] =
       g_signal_new ("have_type", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
@@ -127,12 +128,13 @@
 static void
 gst_type_find_init (GstTypeFind *typefind)
 {
-  typefind->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+  typefind->sinkpad = gst_pad_new_from_template (
+		GST_PAD_TEMPLATE_GET (type_find_sink_factory), "sink");
   gst_element_add_pad (GST_ELEMENT (typefind), typefind->sinkpad);
-  gst_pad_set_chain_function (typefind->sinkpad, gst_type_find_chain);
 
-  typefind->num_buffer = 0;
-  typefind->max_buffers = DEFAULT_MAX_BUFFERS;
+  gst_element_set_loop_function (GST_ELEMENT (typefind),
+				 gst_type_find_loopfunc);
+
   typefind->caps = NULL;
 }
 
@@ -147,9 +149,6 @@
   typefind = GST_TYPE_FIND (object);
 
   switch (prop_id) {
-    case ARG_MAX_BUFFERS:
-      typefind->max_buffers = g_value_get_int (value);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -170,9 +169,6 @@
     case ARG_CAPS:
       g_value_set_pointer (value, typefind->caps);
       break;
-    case ARG_MAX_BUFFERS:
-      g_value_set_int (value, typefind->max_buffers);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -180,18 +176,16 @@
 }
 
 static void
-gst_type_find_chain (GstPad *pad, GstBuffer *buf)
+gst_type_find_loopfunc (GstElement *element)
 {
   GstTypeFind *typefind;
   const GList *type_list;
   GstType *type;
 
-  g_return_if_fail (GST_IS_PAD (pad));
-
-  typefind = GST_TYPE_FIND (GST_OBJECT_PARENT (pad));
+  typefind = GST_TYPE_FIND (element);
 
-  GST_DEBUG ("got buffer of %d bytes in '%s'",
-        GST_BUFFER_SIZE (buf), GST_OBJECT_NAME (typefind));
+  GST_DEBUG ("Started typefinding loop in '%s'",
+	     GST_OBJECT_NAME (typefind));
 
   type_list = gst_type_get_list ();
 
@@ -203,42 +197,34 @@
 
     while (factories) {
       GstTypeFactory *factory = GST_TYPE_FACTORY (factories->data);
-      GstTypeFindFunc typefindfunc = (GstTypeFindFunc)factory->typefindfunc;
+      GstTypeFindFunc typefindfunc = (GstTypeFindFunc) factory->typefindfunc;
       GstCaps *caps;
 
       GST_CAT_DEBUG (GST_CAT_TYPES, "try type (%p) :%d \"%s\" %p", 
 		 factory, type->id, type->mime, typefindfunc);
-      if (typefindfunc && (caps = typefindfunc (buf, factory))) {
+      if (typefindfunc && (caps = typefindfunc (typefind->bs, factory))) {
         GST_CAT_DEBUG (GST_CAT_TYPES, "found type: %d \"%s\" \"%s\"", 
 		   caps->id, type->mime, gst_caps_get_name (caps));
-	typefind->caps = caps;
+	gst_caps_replace (&typefind->caps, caps);
 
-	if (gst_pad_try_set_caps (pad, caps) <= 0) {
+	if (gst_pad_try_set_caps (typefind->sinkpad, caps) <= 0) {
           g_warning ("typefind: found type but peer didn't accept it");
 	}
 
-	{
-	  gst_object_ref (GST_OBJECT (typefind));
-          g_signal_emit (G_OBJECT (typefind), gst_type_find_signals[HAVE_TYPE], 0,
-	                      typefind->caps);
-	  gst_object_unref (GST_OBJECT (typefind));
-          goto end;
-	}
+	gst_object_ref (GST_OBJECT (typefind));
+	g_signal_emit (G_OBJECT (typefind), gst_type_find_signals[HAVE_TYPE],
+		       0, typefind->caps);
+	gst_object_unref (GST_OBJECT (typefind));
+        return;
       }
       factories = g_slist_next (factories);
     }
     type_list = g_list_next (type_list);
   }
 
-  typefind->num_buffer++;
-
-end:
-  gst_buffer_unref (buf);
-
-  if (typefind->max_buffers && typefind->num_buffer >= typefind->max_buffers) {
-    gst_element_error (GST_ELEMENT (typefind), 
-		    "typefind could not determine type after %d buffers", typefind->num_buffer);
-  }
+  /* if we get here, nothing worked... :'(. */
+  gst_element_error (GST_ELEMENT (typefind), 
+		     "media type could not be detected");
 }
 
 static GstElementStateReturn
@@ -250,17 +236,14 @@
   typefind = GST_TYPE_FIND (element);
 
   switch (GST_STATE_TRANSITION (element)) {
-    case GST_STATE_NULL_TO_READY:
-      break;
     case GST_STATE_READY_TO_PAUSED:
-      typefind->num_buffer = 0;
-      gst_caps_unref (typefind->caps);
-      typefind->caps = NULL;
+      typefind->bs = gst_bytestream_new (typefind->sinkpad);
       break;
-    case GST_STATE_PAUSED_TO_PLAYING:
-    case GST_STATE_PLAYING_TO_PAUSED:
     case GST_STATE_PAUSED_TO_READY:
-    case GST_STATE_READY_TO_NULL:
+      gst_bytestream_destroy (typefind->bs);
+      gst_caps_replace (&typefind->caps, NULL);
+      break;
+    default:
       break;
   }
   
Index: gst/gsttypefind.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gsttypefind.h,v
retrieving revision 1.12
diff -u -r1.12 gsttypefind.h
--- gst/gsttypefind.h	26 May 2003 06:15:27 -0000	1.12
+++ gst/gsttypefind.h	29 Sep 2003 08:58:22 -0000
@@ -27,6 +27,7 @@
 #ifndef GST_DISABLE_TYPE_FIND
 
 #include <gst/gstelement.h>
+#include <gst/gstbytestream.h>
 
 G_BEGIN_DECLS
 
@@ -46,11 +47,9 @@
   GstElement 	 element;
 
   GstPad 	*sinkpad;
+  GstByteStream *bs;
 
   GstCaps 	*caps;
-
-  gint 		 num_buffer;
-  gint 		 max_buffers;
 };
 
 struct _GstTypeFindClass {
Index: gst/autoplug/Makefile.am
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/autoplug/Makefile.am,v
retrieving revision 1.16
diff -u -r1.16 Makefile.am
--- gst/autoplug/Makefile.am	8 Dec 2002 14:39:35 -0000	1.16
+++ gst/autoplug/Makefile.am	29 Sep 2003 08:58:22 -0000
@@ -27,13 +27,15 @@
 libgstautoplugger_la_LIBADD =
 libgstautoplugger_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
-libgstspider_la_SOURCES = gstspider.c gstspideridentity.c gstsearchfuncs.c
+libgstspider_la_SOURCES = \
+	gstspider.c gstspideridentity.c \
+	gstsearchfuncs.c
 libgstspider_la_CFLAGS = $(GST_CFLAGS)
 libgstspider_la_LIBADD =
 libgstspider_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS = gststaticautoplug.h gststaticautoplugrender.h \
-	gstspider.h gstspideridentity.h gstsearchfuncs.h
+	gstspider.h gstspideridentity.h gstsearchfuncs.h 
 
 noinst_PROGRAMS = autoplugtest spidertest
 
Index: gst/autoplug/gstsearchfuncs.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/autoplug/gstsearchfuncs.c,v
retrieving revision 1.17
diff -u -r1.17 gstsearchfuncs.c
--- gst/autoplug/gstsearchfuncs.c	18 Aug 2003 21:14:16 -0000	1.17
+++ gst/autoplug/gstsearchfuncs.c	29 Sep 2003 08:58:22 -0000
@@ -250,11 +250,12 @@
   GList *ret = NULL;
   GstElementFactory *factory;
   GList *templs;
-  
+
   while (factories)
   {
     factory = (GstElementFactory *) factories->data;
     templs = factory->padtemplates;
+
     if (GST_PLUGIN_FEATURE (factory)->rank > 0){
       gboolean have_src = FALSE;
       gboolean have_sink = FALSE;
@@ -341,6 +342,7 @@
   
   GST_INFO ("attempting to autoplug via shortest path from %s to %s", 
 	    gst_caps_get_mime (srccaps), gst_caps_get_mime (sinkcaps));
+
   gst_caps_debug (srccaps, "source caps");
   gst_caps_debug (sinkcaps, "sink caps");
   /* wrap all factories as GstAutoplugNode 
Index: gst/autoplug/gstspideridentity.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/autoplug/gstspideridentity.c,v
retrieving revision 1.40
diff -u -r1.40 gstspideridentity.c
--- gst/autoplug/gstspideridentity.c	16 Sep 2003 09:57:30 -0000	1.40
+++ gst/autoplug/gstspideridentity.c	29 Sep 2003 08:58:22 -0000
@@ -413,61 +413,22 @@
 static void
 gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity *ident)
 {
-  GstBuffer *buf=NULL;
-  GstBuffer *typefindbuf = NULL;
-  gboolean getmorebuf = TRUE;
+  GstBuffer *buf = NULL;
   GList *type_list;
   GstCaps *caps;
+  GstByteStream *bs;
 
-  /* this should possibly be a property */
-  guint bufsizelimit = 4096;
-  
   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
 
-  while (getmorebuf){
-
-    /* check if our buffer is big enough to do a typefind */
-    if (typefindbuf && GST_BUFFER_SIZE(typefindbuf) >= bufsizelimit){
-      getmorebuf = FALSE;
-      break;
-    }
-    buf = gst_pad_pull (ident->sink);
-  
-    /* if it's an event... */
-    while (GST_IS_EVENT (buf)) {
-      switch (GST_EVENT_TYPE (GST_EVENT (buf))){
-      case GST_EVENT_EOS:
-        getmorebuf = FALSE;
-	/* FIXME Notify the srcs that EOS has happened */
-        gst_pad_event_default (ident->sink, GST_EVENT (buf));
-	break;
-      default:
-        gst_pad_event_default (ident->sink, GST_EVENT (buf));
-        buf = gst_pad_pull (ident->sink);
-        break;
-      }
-      /* handle DISCONT events, please */
-    }
-
-    typefindbuf = buf;
-    getmorebuf = FALSE;
-    /* FIXME merging doesn't work for some reason so 
-     * we'll just typefind with the first element
-    if (!typefindbuf){
-      typefindbuf = buf;
-      gst_buffer_ref(buf);
-    }
-    else {
-      GstBuffer *oldbuf = typefindbuf;
-      typefindbuf = gst_buffer_merge(typefindbuf, buf);
-      gst_buffer_unref(oldbuf);
-      gst_buffer_unref(buf);
-    }
-    */
-  }
-  
-  if (!typefindbuf){
+  /* get a bytestream object */
+  bs = gst_bytestream_new (ident->sink);
+  if (gst_bytestream_peek (bs, &buf, 1) != 1 || !buf) {
+    buf = NULL;
+    g_warning ("Failed to read fake buffer - serious idiocy going on here");
     goto end;
+  } else {
+    gst_buffer_unref (buf);
+    buf = NULL;
   }
 
   /* maybe there are already valid caps now? */
@@ -487,7 +448,7 @@
       GstTypeFindFunc typefindfunc = (GstTypeFindFunc)factory->typefindfunc;
 
       GST_DEBUG ("trying typefind function %s", GST_PLUGIN_FEATURE_NAME (factory));
-      if (typefindfunc && (caps = typefindfunc (buf, factory))) {
+      if (typefindfunc && (caps = typefindfunc (bs, factory))) {
         GST_INFO ("typefind function %s found caps", GST_PLUGIN_FEATURE_NAME (factory));
         if (gst_pad_try_set_caps (ident->src, caps) <= 0) {
           g_warning ("typefind: found type but peer didn't accept it");
@@ -501,7 +462,6 @@
     type_list = g_list_next (type_list);
   }
   gst_element_error(GST_ELEMENT(ident), "Could not find media type", NULL);
-  gst_buffer_unref(buf);
   buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
 
 end:
@@ -512,15 +472,19 @@
   
   /* push the buffer */
   gst_spider_identity_chain (ident->sink, buf);
+
+  /* bytestream no longer needed */
+  gst_bytestream_destroy (bs);
   
   return;
 
 plug:
-
   gst_caps_debug (caps, "spider starting caps");
   gst_caps_sink (caps);
 
-  gst_spider_identity_plug (ident);  
+  gst_spider_identity_plug (ident);
+
+  gst_bytestream_read (bs, &buf, bs->listavail);
 
   goto end;
 }
Index: gst/registries/gstxmlregistry.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/registries/gstxmlregistry.c,v
retrieving revision 1.32
diff -u -r1.32 gstxmlregistry.c
--- gst/registries/gstxmlregistry.c	27 Sep 2003 18:51:16 -0000	1.32
+++ gst/registries/gstxmlregistry.c	29 Sep 2003 08:58:23 -0000
@@ -711,29 +711,6 @@
   return TRUE;
 }
 
-static GstCaps*
-gst_type_type_find_dummy (GstBuffer *buffer, gpointer priv)
-{
-  GstTypeFactory *factory = (GstTypeFactory *)priv;
-
-  GST_CAT_DEBUG (GST_CAT_TYPES,"gsttype: need to load typefind function for %s", factory->mime);
-
-  if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
-    if (factory->typefindfunc) {
-      if (factory->typefindfunc == gst_type_type_find_dummy) {
-	GST_CAT_WARNING (GST_CAT_TYPES, "GstTypeFactory %s for mime %s exts %s does not install a valid typefindfunc",
-			 factory->feature.name, factory->mime, factory->exts);
-      } else {
-	GstCaps *res = factory->typefindfunc (buffer, factory);
-	if (res)
-	  return res;
-      }
-    }
-  }
-  return NULL;
-}
-
-
 static gboolean
 gst_xml_registry_parse_type_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
                                      gsize text_len, GstXMLRegistry *registry, GError **error)
Index: libs/gst/Makefile.am
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/libs/gst/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- libs/gst/Makefile.am	6 Aug 2003 01:06:30 -0000	1.3
+++ libs/gst/Makefile.am	29 Sep 2003 08:58:23 -0000
@@ -1,3 +1,3 @@
-SUBDIRS = bytestream control getbits
+SUBDIRS = control getbits
 
-DIST_SUBDIRS = bytestream control getbits
+DIST_SUBDIRS = control getbits
--- /dev/null	2003-01-30 11:24:37.000000000 +0100
+++ gst/gstbytestream.c	2003-09-27 18:44:03.000000000 +0200
@@ -0,0 +1,746 @@
+/* GStreamer
+ * Copyright (C) 2001 Erik Walthinsen <omega@temple-baptist.com>
+ *
+ * gstbytestream.c: adds a convenient bytestream based API to a pad.
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <gst/gstinfo.h>
+#include <gst/gstbytestream.h>
+
+GST_DEBUG_CATEGORY_STATIC(debug_bs);
+#define GST_CAT_DEFAULT debug_bs
+
+static guint8 *gst_bytestream_assemble (GstByteStream * bs, guint32 len);
+
+static inline void
+gst_bytestream_init (GstByteStream *bs)
+{
+  bs->event = NULL;
+  bs->buflist = NULL;
+  bs->headbufavail = 0;
+  bs->listavail = 0;
+  bs->assembled = NULL;
+  bs->offset = 0LL;
+  bs->in_seek = FALSE;
+}
+static inline void
+gst_bytestream_exit (GstByteStream *bs)
+{
+  GSList *walk;
+
+  if (bs->event)
+    gst_event_unref (bs->event);
+  
+  walk = bs->buflist;
+  while (walk) {
+    gst_buffer_unref (GST_BUFFER (walk->data));
+    walk = g_slist_next (walk);
+  }
+  g_slist_free (bs->buflist);
+
+  g_free (bs->assembled);
+}
+/**
+ * gst_bytestream_new:
+ * @pad: the pad to attach the bytestream to
+ *
+ * creates a bytestream from the given pad
+ *
+ * Returns: a new #GstByteStream object
+ */
+GstByteStream *
+gst_bytestream_new (GstPad * pad)
+{
+  GstByteStream *bs = g_new (GstByteStream, 1);
+
+  GST_DEBUG_CATEGORY_INIT (debug_bs, "bytestream", 0, "bytestream library");
+
+  bs->pad = pad;
+  gst_bytestream_init (bs);
+
+  return bs;
+}
+
+/**
+ * gst_bytestream_destroy:
+ * @bs: the bytestream object to destroy
+ *
+ * destroy the bytestream object and free its resources.
+ */
+void
+gst_bytestream_destroy (GstByteStream * bs)
+{
+  gst_bytestream_exit (bs);
+  g_free (bs);
+}
+void
+gst_bytestream_reset (GstByteStream *bs)
+{
+  /* free all data */
+  gst_bytestream_exit (bs);
+  /* reset data to clean state */
+  gst_bytestream_init (bs);
+}
+/* HOW THIS WORKS:
+ *
+ * The fundamental structure is a singly-linked list of buffers.  The
+ * buffer on the front is the oldest, and thus the first to read data
+ * from.  The number of bytes left to be read in this buffer is stored
+ * in bs->headbufavail.  The number of bytes available in the entire
+ * list (including the head buffer) is in bs->listavail.
+ *
+ * When a request is made for data (peek), _fill_bytes is called with
+ * the number of bytes needed, but only if the listavail indicates
+ * that there aren't already enough.  This calls _get_next_buf until
+ * the listavail is sufficient to satisfy the demand.
+ *
+ * _get_next_buf pulls a buffer from the pad the bytestream is attached
+ * to, and shoves it in the list.  There are actually two things it can
+ * do.  If there's already a buffer in the list, and the _is_span_fast()
+ * test returns true, it will merge it with that last buffer.  Otherwise
+ * it will simply tack it onto the end of the list.
+ *
+ * The _peek itself first checks the simple case of the request fitting
+ * within the head buffer, and if so creates a subbuffer and returns.
+ * Otherwise, it creates a new buffer and allocates space for the request
+ * and calls _assemble to fill it.  We know we have to copy because this
+ * case only happens when the _merge wasn't feasible during _get_next_buf.
+ *
+ * The _flush method repeatedly inspects the head buffer and flushes as
+ * much data from it as it needs to, up to the size of the buffer.  If
+ * the flush decimates the buffer, it's stripped, unref'd, and removed.
+ */
+
+
+/* get the next buffer
+ * if the buffer can be merged with the head buffer, do so
+ * else add it onto the head of the 
+ */
+static gboolean
+gst_bytestream_get_next_buf (GstByteStream *bs)
+{
+  GstBuffer *nextbuf, *lastbuf, *headbuf;
+  GSList *end;
+
+  /* if there is an event pending, return FALSE */
+  if (bs->event)
+    return FALSE;
+
+  GST_DEBUG ("get_next_buf: pulling buffer");
+  nextbuf = gst_pad_pull (bs->pad);
+
+  if (!nextbuf)
+    return FALSE;
+
+  if (GST_IS_EVENT (nextbuf)) {
+    bs->event = GST_EVENT (nextbuf);
+    return FALSE;
+  }
+
+  if (GST_BUFFER_TIMESTAMP_IS_VALID (nextbuf))
+    bs->last_ts = GST_BUFFER_TIMESTAMP (nextbuf);
+
+  GST_DEBUG ("get_next_buf: got buffer of %d bytes", GST_BUFFER_SIZE (nextbuf));
+
+  /* first see if there are any buffers in the list at all */
+  if (bs->buflist) {
+    GST_DEBUG ("gst_next_buf: there is at least one buffer in the list");
+    /* now find the end of the list */
+    end = g_slist_last (bs->buflist);
+    /* get the buffer that's there */
+    lastbuf = GST_BUFFER (end->data);
+
+    /* see if we can marge cheaply */
+    if (gst_buffer_is_span_fast (lastbuf, nextbuf)) {
+      GST_DEBUG ("get_next_buf: merging new buffer with last buf on list");
+      /* it is, let's merge them (this is really an append, but...) */
+      end->data = gst_buffer_merge (lastbuf, nextbuf);
+      /* add to the length of the list */
+      bs->listavail += GST_BUFFER_SIZE (nextbuf);
+
+      /* have to check to see if we merged with the head buffer */
+      if (end == bs->buflist) {
+	bs->headbufavail += GST_BUFFER_SIZE (nextbuf);
+      }
+
+      gst_buffer_unref (lastbuf);
+      gst_buffer_unref (nextbuf);
+
+      /* if we can't, we just append this buffer */
+    }
+    else {
+      GST_DEBUG ("get_next_buf: adding new buffer to the end of the list");
+      end = g_slist_append (end, nextbuf);
+      /* also need to increment length of list and buffer count */
+      bs->listavail += GST_BUFFER_SIZE (nextbuf);
+    }
+
+    /* if there are no buffers in the list */
+  }
+  else {
+    GST_DEBUG ("get_next_buf: buflist is empty, adding new buffer to list");
+    /* put this on the end of the list */
+    bs->buflist = g_slist_append (bs->buflist, nextbuf);
+    /* and increment the number of bytes in the list */
+    bs->listavail = GST_BUFFER_SIZE (nextbuf);
+    /* set the head buffer avail to the size */
+    bs->headbufavail = GST_BUFFER_SIZE (nextbuf);
+  }
+
+  /* a zero offset is a indication that we might need to set the timestamp */ 
+  if (bs->offset == 0LL){
+    headbuf = GST_BUFFER (bs->buflist->data);
+    bs->offset = GST_BUFFER_OFFSET(headbuf);
+  }
+  
+  return TRUE;
+}
+
+static gboolean
+gst_bytestream_fill_bytes (GstByteStream *bs, guint32 len)
+{
+  /* as long as we don't have enough, we get more buffers */
+  while (bs->listavail < len) {
+    GST_DEBUG ("fill_bytes: there are %d bytes in the list, we need %d", bs->listavail, len);
+    if (!gst_bytestream_get_next_buf (bs))
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+ * gst_bytestream_peek:
+ * @bs: the bytestream to peek
+ * @buf: pointer to a variable that can hold a buffer pointer.
+ * @len: the number of bytes to peek
+ *
+ * Peeks len bytes into the bytestream, the result is returned as
+ * a #GstBuffer. Unref the buffer after usage.
+ * This function can return less bytes than requested. In that case,
+ * an event might have happened which you can retrieve with
+ * gst_bytestream_get_status().
+ *
+ * Returns: The number of bytes successfully peeked.
+ */
+guint32
+gst_bytestream_peek (GstByteStream *bs, GstBuffer **buf, guint32 len)
+{
+  GstBuffer *headbuf, *retbuf = NULL;
+
+  g_return_val_if_fail (bs != NULL, 0);
+  g_return_val_if_fail (len > 0, 0);
+
+  GST_DEBUG ("peek: asking for %d bytes", len);
+
+  /* make sure we have enough */
+  GST_DEBUG ("peek: there are %d bytes in the list", bs->listavail);
+  if (len > bs->listavail) {
+    if (!gst_bytestream_fill_bytes (bs, len)) {
+      /* we must have an event coming up */
+      if (bs->listavail > 0) {
+        /* we have some data left, len will be shrunk to the amount of data available */
+        len = bs->listavail;
+      }
+      else {
+        /* there is no data */
+        *buf = retbuf;
+        return 0;
+      }
+    }
+    GST_DEBUG ("peek: there are now %d bytes in the list", bs->listavail);
+  }
+  gst_bytestream_print_status (bs);
+
+  /* extract the head buffer */
+  headbuf = GST_BUFFER (bs->buflist->data);
+
+  /* if the requested bytes are in the current buffer */
+  GST_DEBUG ("peek: headbufavail is %d", bs->headbufavail);
+  if (len <= bs->headbufavail) {
+    GST_DEBUG ("peek: there are enough bytes in headbuf (need %d, have %d)", len, bs->headbufavail);
+    /* create a sub-buffer of the headbuf */
+    retbuf = gst_buffer_create_sub (headbuf, GST_BUFFER_SIZE (headbuf) - bs->headbufavail, len);
+    GST_BUFFER_OFFSET (retbuf) = GST_BUFFER_OFFSET (headbuf) + GST_BUFFER_SIZE (headbuf) - bs->headbufavail;
+
+  }
+  /* otherwise we need to figure out how to assemble one */
+  else {
+    GST_DEBUG ("peek: current buffer is not big enough for len %d", len);
+
+    retbuf = gst_buffer_new ();
+    GST_BUFFER_SIZE (retbuf) = len;
+    GST_BUFFER_DATA (retbuf) = gst_bytestream_assemble (bs, len);
+    GST_BUFFER_TIMESTAMP (retbuf) = bs->last_ts;
+  }
+
+  *buf = retbuf;
+  return len;
+}
+
+/**
+ * gst_bytestream_peek_bytes:
+ * @bs: the bytestream to peek
+ * @data: pointer to a variable that can hold a guint8 pointer.
+ * @len: the number of bytes to peek
+ *
+ * Peeks len bytes into the bytestream, the result is returned as
+ * a pointer to a guint8*. The data pointed to be data should not
+ * be freed and will become invalid after performing the next bytestream
+ * operation.
+ * This function can return less bytes than requested. In that case,
+ * an event might have happened which you can retrieve with
+ * gst_bytestream_get_status().
+ *
+ * Returns: The number of bytes successfully peeked.
+ */
+guint32
+gst_bytestream_peek_bytes (GstByteStream *bs, guint8** data, guint32 len)
+{
+  GstBuffer *headbuf;
+
+  g_return_val_if_fail (bs != NULL, 0);
+  g_return_val_if_fail (len > 0, 0);
+
+  GST_DEBUG ("peek_bytes: asking for %d bytes", len);
+  if (bs->assembled) {
+    if (bs->assembled_len >= len) {
+      *data = bs->assembled;
+      return len;
+    }
+    g_free (bs->assembled);
+    bs->assembled = NULL;
+  }
+
+  /* make sure we have enough */
+  GST_DEBUG ("peek_bytes: there are %d bytes in the list", bs->listavail);
+  if (len > bs->listavail) {
+    if (!gst_bytestream_fill_bytes (bs, len)){
+      /* we must have an event coming up */
+      if (bs->listavail > 0){
+        /* we have some data left, len will be shrunk to the amount of data available */
+        len = bs->listavail;
+      }
+      else {
+        /* there is no data */
+        *data = NULL;
+        return 0;
+      }
+    }
+    GST_DEBUG ("peek_bytes: there are now %d bytes in the list", bs->listavail);
+  }
+  gst_bytestream_print_status (bs);
+
+  /* extract the head buffer */
+  headbuf = GST_BUFFER (bs->buflist->data);
+
+  /* if the requested bytes are in the current buffer */
+  GST_DEBUG ("peek_bytes: headbufavail is %d", bs->headbufavail);
+  if (len <= bs->headbufavail) {
+    GST_DEBUG ("peek_bytes: there are enough bytes in headbuf (need %d, have %d)", len, bs->headbufavail);
+    /* create a sub-buffer of the headbuf */
+    *data = GST_BUFFER_DATA (headbuf) + (GST_BUFFER_SIZE (headbuf) - bs->headbufavail);
+
+  }
+  /* otherwise we need to figure out how to assemble one */
+  else {
+    GST_DEBUG ("peek_bytes: current buffer is not big enough for len %d", len);
+
+    *data = gst_bytestream_assemble (bs, len);
+    bs->assembled = *data;
+    bs->assembled_len = len;
+  }
+
+  return len;
+}
+
+static guint8*
+gst_bytestream_assemble (GstByteStream *bs, guint32 len)
+{
+  guint8 *data = g_malloc (len);
+  GSList *walk;
+  guint32 copied = 0;
+  GstBuffer *buf;
+
+  /* copy the data from the curbuf */
+  buf = GST_BUFFER (bs->buflist->data);
+  GST_DEBUG ("assemble: copying %d bytes from curbuf at %d to *data", bs->headbufavail,
+	    GST_BUFFER_SIZE (buf) - bs->headbufavail);
+  memcpy (data, GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf) - bs->headbufavail, bs->headbufavail);
+  copied += bs->headbufavail;
+
+  /* asumption is made that the buffers all exist in the list */
+  walk = g_slist_next (bs->buflist);
+  while (copied < len) {
+    buf = GST_BUFFER (walk->data);
+    if (GST_BUFFER_SIZE (buf) < (len - copied)) {
+      GST_DEBUG ("assemble: copying %d bytes from buf to output offset %d", GST_BUFFER_SIZE (buf), copied);
+      memcpy (data + copied, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+      copied += GST_BUFFER_SIZE (buf);
+    }
+    else {
+      GST_DEBUG ("assemble: copying %d bytes from buf to output offset %d", len - copied, copied);
+      memcpy (data + copied, GST_BUFFER_DATA (buf), len - copied);
+      copied = len;
+    }
+    walk = g_slist_next (walk);
+  }
+
+  return data;
+}
+
+/**
+ * gst_bytestream_flush:
+ * @bs: the bytestream to flush
+ * @len: the number of bytes to flush
+ *
+ * Flush len bytes from the bytestream. 
+ * This function can return FALSE when the number of
+ * bytes could not be flushed due to an event. In that case,
+ * you can get the number of available bytes before the event
+ * with gst_bytestream_get_status().
+ *
+ * Returns: TRUE if the number of bytes could be flushed.
+ */
+gboolean
+gst_bytestream_flush (GstByteStream *bs, guint32 len)
+{
+  GST_DEBUG ("flush: flushing %d bytes", len);
+
+  if (len == 0)
+    return TRUE;
+
+  /* make sure we have enough */
+  GST_DEBUG ("flush: there are %d bytes in the list", bs->listavail);
+  if (len > bs->listavail) {
+    if (!gst_bytestream_fill_bytes (bs, len)) {
+      return FALSE;
+    }
+    GST_DEBUG ("flush: there are now %d bytes in the list", bs->listavail);
+  }
+
+  gst_bytestream_flush_fast (bs, len);
+
+  return TRUE;
+}
+
+/**
+ * gst_bytestream_flush_fast:
+ * @bs: the bytestream to flush
+ * @len: the number of bytes to flush
+ *
+ * Flushes len bytes from the bytestream. This function
+ * is faster than gst_bytestream_flush() but only works
+ * when you have recently peeked no less than len bytes
+ * with gst_bytestream_peek() or gst_bytestream_peek_bytes().
+ */
+void
+gst_bytestream_flush_fast (GstByteStream *bs, guint32 len)
+{
+  GstBuffer *headbuf;
+
+  if (len == 0)
+    return;
+		  
+  g_assert (len <= bs->listavail);
+
+  if (bs->assembled) {
+    g_free (bs->assembled);
+    bs->assembled = NULL;
+  }
+
+  /* update the byte offset */
+  bs->offset += len;
+
+  /* repeat until we've flushed enough data */
+  while (len > 0) {
+    headbuf = GST_BUFFER (bs->buflist->data);
+
+    GST_DEBUG ("flush: analyzing buffer that's %d bytes long, offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (headbuf),
+	      GST_BUFFER_OFFSET (headbuf));
+
+    /* if there's enough to complete the flush */
+    if (bs->headbufavail > len) {
+      /* just trim it off */
+      GST_DEBUG ("flush: trimming %d bytes off end of headbuf", len);
+      bs->headbufavail -= len;
+      bs->listavail -= len;
+      len = 0;
+
+      /* otherwise we have to trim the whole buffer */
+    }
+    else {
+      GST_DEBUG ("flush: removing head buffer completely");
+      /* remove it from the list */
+      bs->buflist = g_slist_delete_link (bs->buflist, bs->buflist);
+      /* trim it from the avail size */
+      bs->listavail -= bs->headbufavail;
+      /* record that we've trimmed this many bytes */
+      len -= bs->headbufavail;
+      /* unref it */
+      gst_buffer_unref (headbuf);
+
+      /* record the new headbufavail */
+      if (bs->buflist) {
+	bs->headbufavail = GST_BUFFER_SIZE (GST_BUFFER (bs->buflist->data));
+	GST_DEBUG ("flush: next headbuf is %d bytes", bs->headbufavail);
+      }
+      else {
+	GST_DEBUG ("flush: no more bytes at all");
+      }
+    }
+
+    GST_DEBUG ("flush: bottom of while(), len is now %d", len);
+  }
+}
+
+/**
+ * gst_bytestream_seek:
+ * @bs: the bytestream to seek
+ * @offset: the byte offset to seek to
+ * @method: the seek method.
+ *
+ * Perform a seek on the bytestream to the given offset.
+ * The method can be one of GST_SEEK_METHOD_CUR, GST_SEEK_METHOD_SET,
+ * GST_SEEK_METHOD_END.
+ * This seek will also flush any pending data in the bytestream or
+ * peer elements.
+ *
+ * Returns: TRUE when the seek succeeded.
+ */
+gboolean
+gst_bytestream_seek (GstByteStream *bs, gint64 offset, GstSeekType method)
+{
+  GstRealPad *peer;
+  
+  g_return_val_if_fail (bs != NULL, FALSE);
+  
+  peer = GST_RPAD_PEER (bs->pad);
+
+  GST_DEBUG ("bs: send event\n");
+  if (gst_pad_send_event (GST_PAD (peer), gst_event_new_seek (
+			  GST_FORMAT_BYTES | 
+			  (method & GST_SEEK_METHOD_MASK) | 
+			  GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 
+			  offset))) 
+  {
+    gst_bytestream_flush_fast (bs, bs->listavail);
+
+    /* we set the seek flag here. We cannot pull the pad here
+     * bacause a seek might occur outisde of the pads cothread context */
+    bs->in_seek = TRUE;
+    
+    return TRUE;
+  }
+  GST_DEBUG ("bs: send event failed\n");
+  return FALSE;
+}
+
+/**
+ * gst_bytestream_tell
+ * @bs: a bytestream
+ *
+ * Get the current byteoffset in the bytestream.
+ *
+ * Returns: the offset or -1 on error.
+ */
+guint64
+gst_bytestream_tell (GstByteStream *bs)
+{
+  GstFormat format;
+  gint64 value;
+  
+  g_return_val_if_fail (bs != NULL, -1);
+
+  format = GST_FORMAT_BYTES;
+
+  if (gst_pad_query (GST_PAD_PEER (bs->pad), GST_QUERY_POSITION, &format, &value)) {
+    return value - bs->listavail;
+  }
+  
+  return -1;
+}
+
+/**
+ * gst_bytestream_length
+ * @bs: a bytestream
+ *
+ * Get the total length of the bytestream.
+ *
+ * Returns: the total length or -1 on error.
+ */
+guint64
+gst_bytestream_length (GstByteStream *bs)
+{
+  GstFormat format;
+  gint64 value;
+  
+  g_return_val_if_fail (bs != NULL, -1);
+
+  format = GST_FORMAT_BYTES;
+
+  if (gst_pad_query (GST_PAD_PEER (bs->pad), GST_QUERY_TOTAL, &format, &value)) 
+    return value;
+  
+  return -1;
+}
+
+/**
+ * gst_bytestream_read:
+ * @bs: the bytestream to read
+ * @buf: pointer to a variable that can hold a buffer pointer.
+ * @len: the number of bytes to read
+ *
+ * Read len bytes from the bytestream, the result is returned as
+ * a #GstBuffer. Unref the buffer after usage.
+ * This function can return less bytes than requested. In that case,
+ * an event might have happened which you can retrieve with
+ * gst_bytestream_get_status().
+ *
+ * Returns: The number of bytes successfully read.
+ */
+guint32
+gst_bytestream_read (GstByteStream *bs, GstBuffer** buf, guint32 len)
+{
+  guint32 len_peeked;
+
+  g_return_val_if_fail (bs != NULL, -1);
+  
+  len_peeked = gst_bytestream_peek (bs, buf, len);
+  if (len_peeked == 0)
+    return 0;
+
+  gst_bytestream_flush_fast (bs, len_peeked);
+
+  return len_peeked;
+}
+
+/**
+ * gst_bytestream_size_hint
+ * @bs: a bytestream
+ * @size: the size to hint
+ *
+ * Give a hint that we are going to read chunks of the given size.
+ * Giving size hints to the peer element might improve performance
+ * since less buffers need to be merged.
+ *
+ * Returns: TRUE if the hint was accepted
+ */
+gboolean
+gst_bytestream_size_hint (GstByteStream *bs, guint32 size)
+{
+  GstEvent *event;
+
+  g_return_val_if_fail (bs != NULL, FALSE);
+
+  event = gst_event_new_size (GST_FORMAT_BYTES, size);
+
+  return gst_pad_send_event (GST_PAD_PEER (bs->pad), event);
+}
+
+/**
+ * gst_bytestream_get_status
+ * @bs: a bytestream
+ * @avail_out: total number of bytes buffered
+ * @event_out: an event
+ *
+ * When an event occurs, the bytestream operations return a value less
+ * than the requested length. You must retrieve the event using this API 
+ * before reading more bytes from the stream.
+ */
+void
+gst_bytestream_get_status (GstByteStream *bs,
+			   guint32 	 *avail_out,
+			   GstEvent 	**event_out)
+{
+  if (avail_out)
+    *avail_out = bs->listavail;
+
+  if (event_out) {
+    *event_out = bs->event;
+    bs->event = NULL;
+  }
+}
+
+/**
+ * gst_bytestream_get_timestamp
+ * @bs: a bytestream
+ *
+ * Get the timestamp of the first data in the bytestream.  If no data
+ * exists 1 byte is read to load a new buffer.
+ *
+ * This function will not check input buffer boundries.  It is  possible
+ * the next read could span two or more input buffers with different
+ * timestamps.
+ *
+ * Returns: a timestamp
+ */
+guint64
+gst_bytestream_get_timestamp (GstByteStream *bs)
+{
+  GstBuffer *headbuf;
+
+  g_return_val_if_fail (bs != NULL, 0);
+
+  GST_DEBUG ("get_timestamp: getting timestamp");
+
+  /* make sure we have a buffer */
+  if (bs->listavail == 0) {
+    GST_DEBUG ("gst_timestamp: fetching a buffer");
+    if (!gst_bytestream_fill_bytes (bs, 1))
+      return 0;
+  }
+
+  /* extract the head buffer */
+  headbuf = GST_BUFFER (bs->buflist->data);
+
+  return GST_BUFFER_TIMESTAMP (headbuf);
+}
+
+/**
+ * gst_bytestream_print_status
+ * @bs: a bytestream
+ *
+ * Print the current status of the bytestream object. mainly
+ * used for debugging purposes.
+ */
+void
+gst_bytestream_print_status (GstByteStream * bs)
+{
+  GSList *walk;
+  GstBuffer *buf;
+
+  GST_DEBUG ("STATUS: head buffer has %d bytes available", bs->headbufavail);
+  GST_DEBUG ("STATUS: list has %d bytes available", bs->listavail);
+  walk = bs->buflist;
+  while (walk) {
+    buf = GST_BUFFER (walk->data);
+    walk = g_slist_next (walk);
+
+    GST_DEBUG ("STATUS: buffer starts at %" G_GUINT64_FORMAT " and is %d bytes long", 
+	      GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf));
+  }
+}
--- /dev/null	2003-01-30 11:24:37.000000000 +0100
+++ gst/gstbytestream.h	2003-09-27 18:42:33.000000000 +0200
@@ -0,0 +1,72 @@
+/* GStreamer
+ * Copyright (C) 2001 Erik Walthinsen <omega@temple-baptist.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_BYTESTREAM_H__
+#define __GST_BYTESTREAM_H__
+
+#include <glib.h>
+#include <gst/gstpad.h>
+#include <gst/gstevent.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstByteStream GstByteStream;
+
+struct _GstByteStream {
+  GstPad 	*pad;
+
+  GstEvent 	*event;
+
+  GSList 	*buflist;
+  guint32 	 headbufavail;
+  guint32 	 listavail;
+
+  /* we keep state of assembled pieces */
+  guint8	*assembled;
+  guint32	 assembled_len; /* only valid when assembled != NULL */
+
+  /* this is needed for gst_bytestream_tell */
+  guint64	 offset;
+  guint64	 last_ts;
+
+  /* if we are in the seek state (waiting for DISCONT) */
+  gboolean	 in_seek;
+};
+
+GstByteStream*		gst_bytestream_new		(GstPad *pad);
+void			gst_bytestream_destroy		(GstByteStream *bs);
+
+void			gst_bytestream_reset		(GstByteStream *bs);
+guint32			gst_bytestream_read		(GstByteStream *bs, GstBuffer** buf, guint32 len);
+guint64			gst_bytestream_tell		(GstByteStream *bs);
+guint64			gst_bytestream_length		(GstByteStream *bs);
+gboolean		gst_bytestream_size_hint	(GstByteStream *bs, guint32 size);
+gboolean		gst_bytestream_seek		(GstByteStream *bs, gint64 offset, GstSeekType type);
+guint32			gst_bytestream_peek		(GstByteStream *bs, GstBuffer** buf, guint32 len);
+guint32			gst_bytestream_peek_bytes	(GstByteStream *bs, guint8** data, guint32 len);
+gboolean		gst_bytestream_flush		(GstByteStream *bs, guint32 len);
+void                    gst_bytestream_flush_fast       (GstByteStream *bs, guint32 len);
+void                    gst_bytestream_get_status	(GstByteStream *bs, guint32 *avail_out, GstEvent **event_out);
+guint64			gst_bytestream_get_timestamp	(GstByteStream *bs);
+
+void 			gst_bytestream_print_status	(GstByteStream *bs);
+
+G_END_DECLS
+
+#endif /* __GST_BYTESTREAM_H__ */
