merge TYPEFIND branch. Major changes:
authorBenjamin Otte <otte@gnome.org>
Tue, 28 Oct 2003 20:25:30 +0000 (20:25 +0000)
committerBenjamin Otte <otte@gnome.org>
Tue, 28 Oct 2003 20:25:30 +0000 (20:25 +0000)
Original commit message from CVS:
merge TYPEFIND branch. Major changes:
- totally reworked type(find) system
- bytestream is out of the core again
- typefind element is now part of gstelements

56 files changed:
configure.ac
docs/gst/gstreamer-docs.sgml
docs/gst/gstreamer-sections.txt
docs/gst/gstreamer.types
docs/gst/tmpl/gsttype.sgml [deleted file]
docs/gst/tmpl/gsttypefactory.sgml [deleted file]
docs/gst/tmpl/gsttypefind.sgml
docs/gst/tmpl/gsttypefindfactory.sgml [new file with mode: 0644]
gst/Makefile.am
gst/autoplug/gstspideridentity.c
gst/elements/Makefile.am
gst/elements/gstbufferstore.c [new file with mode: 0644]
gst/elements/gstbufferstore.h [new file with mode: 0644]
gst/elements/gstelements.c
gst/elements/gstfakesrc.c
gst/elements/gstfilesrc.c
gst/elements/gsttypefind.c [new file with mode: 0644]
gst/elements/gsttypefind.h [new file with mode: 0644]
gst/elements/gsttypefindelement.c [new file with mode: 0644]
gst/elements/gsttypefindelement.h [new file with mode: 0644]
gst/gst.c
gst/gst.h
gst/gst_private.h
gst/gstbuffer.c
gst/gstbytestream.c [deleted file]
gst/gstbytestream.h [deleted file]
gst/gstcaps.c
gst/gstcaps.h
gst/gstconfig.h.in
gst/gstelement.c
gst/gstmarshal.list
gst/gstpad.c
gst/gstpluginfeature.c
gst/gstpluginfeature.h
gst/gsttypefind.c
gst/gsttypefind.h
gst/gstutils.c
gst/registries/gstxmlregistry.c
libs/gst/Makefile.am
libs/gst/bytestream/Makefile.am [new file with mode: 0644]
libs/gst/bytestream/bytestream.c [new file with mode: 0644]
libs/gst/bytestream/bytestream.h [new file with mode: 0644]
plugins/elements/Makefile.am
plugins/elements/gstbufferstore.c [new file with mode: 0644]
plugins/elements/gstbufferstore.h [new file with mode: 0644]
plugins/elements/gstelements.c
plugins/elements/gstfakesrc.c
plugins/elements/gstfilesrc.c
plugins/elements/gsttypefind.c [new file with mode: 0644]
plugins/elements/gsttypefind.h [new file with mode: 0644]
plugins/elements/gsttypefindelement.c [new file with mode: 0644]
plugins/elements/gsttypefindelement.h [new file with mode: 0644]
tools/gst-inspect.c
tools/gst-register.c
tools/gst-typefind.c
tools/gst-xmlinspect.c

index 7a9f6a62f37f2441af2b28b7618c387d1b708ae9..cffdc8ef9075aa731ed3d68c6a0c428156b0a906 100644 (file)
@@ -372,8 +372,6 @@ GST_SUBSYSTEM_DISABLE(GST_DEBUG,[debugging subsystem])
 
 translit(dnm, m, l) AM_CONDITIONAL(GST_DISABLE_LOADSAVE, true)
 GST_SUBSYSTEM_DISABLE(LOADSAVE,[pipeline XML load/save])
-translit(dnm, m, l) AM_CONDITIONAL(GST_DISABLE_TYPEFIND, true)
-GST_SUBSYSTEM_DISABLE(TYPEFIND,[typefind plugin],)
 translit(dnm, m, l) AM_CONDITIONAL(GST_DISABLE_AUTOPLUG, true)
 GST_SUBSYSTEM_DISABLE(AUTOPLUG,[autoplugger subsystem])
 translit(dnm, m, l) AM_CONDITIONAL(GST_DISABLE_PARSE, true)
@@ -562,12 +560,12 @@ gst/indexers/Makefile
 gst/elements/Makefile
 gst/parse/Makefile
 gst/schedulers/Makefile
-gst/types/Makefile
 gst/registries/Makefile
 libs/Makefile
 libs/gst/Makefile
-libs/gst/getbits/Makefile
+libs/gst/bytestream/Makefile
 libs/gst/control/Makefile
+libs/gst/getbits/Makefile
 libs/ext/Makefile
 po/Makefile.in
 tests/Makefile
index c3e6a2a3886a53aab2d2770fd5e2a5608a50f8fc..9cc12a4462eb552c782179e7f64ffd0407b39e8c 100644 (file)
@@ -29,8 +29,8 @@
 <!ENTITY GstRegistryPool SYSTEM "xml/gstregistrypool.xml">
 <!ENTITY GstScheduler SYSTEM "xml/gstscheduler.xml">
 <!ENTITY GstTrace SYSTEM "xml/gsttrace.xml">
-<!ENTITY GstType SYSTEM "xml/gsttype.xml">
-<!ENTITY GstTypeFactory SYSTEM "xml/gsttype.xml">
+<!ENTITY GstTypeFind SYSTEM "xml/gsttypefind.xml">
+<!ENTITY GstTypeFindFactory SYSTEM "xml/gsttypefindfactory.xml">
 <!ENTITY GstCaps SYSTEM "xml/gstcaps.xml">
 <!ENTITY GstProps SYSTEM "xml/gstprops.xml">
 <!ENTITY GstClock SYSTEM "xml/gstclock.xml">
@@ -40,7 +40,6 @@
 <!ENTITY GstXML SYSTEM "xml/gstxml.xml">
 <!-- these are elements without API docs
 <!ENTITY GstQueue SYSTEM "xml/gstqueue.xml">
-<!ENTITY GstTypeFind SYSTEM "xml/gsttypefind.xml">
 -->
 <!ENTITY GstIndex SYSTEM "xml/gstindex.xml">
 <!ENTITY cothreads SYSTEM "xml/cothreads.xml">
@@ -118,9 +117,8 @@ with some more specialized elements.</para>
     &GstScheduler;
     &GstSystemClock;
     &GstThread;
-    &GstType;
-<!-- no API docs
-    &GstTypeFind; -->
+    &GstTypeFind;
+    &GstTypeFindFactory;
     &GstUri;
     &GstUtils;
     &GstXML;
index 04a0f5b9bdcaf47fd84be86ae5c7264478df86a0..f641d0a2ef3977b9476f1b642072bf5285bf94bf 100644 (file)
@@ -1254,38 +1254,33 @@ gst_alloc_trace_flags_get_type
 </SECTION>
 
 <SECTION>
-<FILE>gsttype</FILE>
-<TITLE>GstType</TITLE>
-GstType
-gst_type_register
-gst_type_find_by_mime
-gst_type_find_by_ext
-gst_type_find_by_id
-gst_type_get_list
-<SUBSECTION Standard>
+<FILE>gsttypefind</FILE>
+<TITLE>Writing typefind functions</TITLE>
+GstTypeFind
+GstTypeFindFunction
+gst_type_find_peek
+gst_type_find_suggest
+gst_type_find_get_length
+gst_type_find_factory_register
 </SECTION>
 
 <SECTION>
-<FILE>gsttypefactory</FILE>
-<TITLE>GstTypeFactory</TITLE>
-GstTypeFactory
-GstTypeDefinition
-GstTypeFindFunc
-gst_type_factory_new
-gst_type_factory_find
+<FILE>gsttypefindfactory</FILE>
+<TITLE>GstTypeFindFactory</TITLE>
+GstTypeFindFactory
+GstTypeFindFactoryClass
+gst_type_find_factory_get_list
+gst_type_find_factory_get_extensions
+gst_type_find_factory_get_caps
+gst_type_find_factory_call_function
 <SUBSECTION Standard>
-GstTypeFind
-GstTypeFindClass
-GST_TYPE_FACTORY
-GST_IS_TYPE_FACTORY
-GST_TYPE_TYPE_FACTORY
-gst_type_factory_get_type
-GST_TYPE_FACTORY_CLASS
-GST_IS_TYPE_FACTORY_CLASS
-GST_IS_TYPE_FIND
-GST_IS_TYPE_FIND_CLASS
-GST_TYPE_FACTORY_GET_CLASS
-GstTypeFactoryClass
+GST_TYPE_FIND_FACTORY
+GST_IS_TYPE_FIND_FACTORY
+GST_TYPE_TYPE_FIND_FACTORY
+gst_type_find_factory_get_type
+GST_TYPE_FIND_FACTORY_CLASS
+GST_IS_TYPE_FIND_FACTORY_CLASS
+GST_TYPE_FIND_FACTORY_GET_CLASS
 </SECTION>
 
 <SECTION>
@@ -1325,8 +1320,6 @@ gst_caps_replace_sink
 gst_caps_sink
 gst_caps_set_name
 gst_caps_get_name
-gst_caps_set_type_id
-gst_caps_get_type_id
 gst_caps_set_mime
 gst_caps_get_mime
 gst_caps_set_props
index bca68fcce37adcd61f8e136c7a12bf82bdbf284c..d561d86f1e36b266807aa98757568ddacc3c8479 100644 (file)
@@ -13,7 +13,7 @@ gst_thread_get_type
 gst_plugin_feature_get_type
 gst_autoplug_get_type
 gst_autoplug_factory_get_type
-gst_type_factory_get_type
+gst_type_find_factory_get_type
 gst_element_factory_get_type
 gst_scheduler_factory_get_type
 gst_scheduler_get_type
diff --git a/docs/gst/tmpl/gsttype.sgml b/docs/gst/tmpl/gsttype.sgml
deleted file mode 100644 (file)
index fbd9b8c..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-GstType
-
-<!-- ##### SECTION Short_Description ##### -->
-Identifies the data
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-GstTypes exist to try to make sure data eveyrone is talking about the
-right kind of data.  They aid quite a bit in autoplugging, in fact make it
-possible.  Each well-formed type includes a function (typefind) that will
-take one or more buffers and determine whether or not it is indeed a
-stream of that type, and possible a metadata to go with it.  It may
-provide related metadata structure IDs (and must if it provides metadata
-from the typefind function).
-</para>
-
-<para>
-Because multiple elements and plugins are very likely to be using the same
-types, the process of creating/finding types is designed to be done with a
-single function call.  All operations on GstTypes occur via their guint16
-ID numbers, with the GstType structure "private" to the GST library.  A
-plugin wishing to use a give type would contain a static struct of type
-GstTypeFactory, which lists the MIME type, possible extensions (which may
-overlap the mime-types file), a typefind function, and any other cruft I
-decide to add.
-</para>
-
-<para>
-A plugin init function would take this typefactory and hand it to the
-gst_type_new() (FIXME: badly named) function, which would first search for
-that same MIME type in the current list.  If it found one, it would
-compare the two to see if the new one is "better".  Better is defined as
-having more extentions (to be merged) or a typefind function verses none.
-</para>
-
-<para>
-The point of returning an existing MIME type is a result of the goal of
-unifying types enough to guarantee that, for instance, all MP3 decoders
-will work interchangably.  If MP3 decoder A says "MIME type 'audio/mpeg'
-with extensions 'mpeg3'" and decoder B says "MIME type 'audio/mpeg' with
-extensions 'mp3'", we don't want to have two types defined, possibly with
-two typefind functions. If we did, it's not obvious which of the two would
-be tried first (luck) and if both would really identify streams as mp3
-correctly in all cases.  And whichever wins, we're stuck using the
-associated decoder to play that stream.  We lose the choice between any
-valid mp3 decoder, and thus the whole point of the type system.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-#GstTypeFactory
-</para>
-
-<!-- ##### STRUCT GstType ##### -->
-<para>
-A type.
-</para>
-
-@id: 
-@mime: 
-@exts: 
-@factories: 
-
-<!-- ##### FUNCTION gst_type_register ##### -->
-<para>
-
-</para>
-
-@factory: 
-@Returns: 
-
-
-<!-- ##### FUNCTION gst_type_find_by_mime ##### -->
-<para>
-
-</para>
-
-@mime: 
-@Returns: 
-
-
-<!-- ##### FUNCTION gst_type_find_by_ext ##### -->
-<para>
-
-</para>
-
-@ext: 
-@Returns: 
-
-
-<!-- ##### FUNCTION gst_type_find_by_id ##### -->
-<para>
-
-</para>
-
-@id: 
-@Returns: 
-
-
-<!-- ##### FUNCTION gst_type_get_list ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
diff --git a/docs/gst/tmpl/gsttypefactory.sgml b/docs/gst/tmpl/gsttypefactory.sgml
deleted file mode 100644 (file)
index 5b65c24..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-GstTypeFactory
-
-<!-- ##### SECTION Short_Description ##### -->
-Add types to plugins.
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-A GstTypeFactory is used to add a new type and a typedetection function 
-to a plugin. Typefactories are named so they can be found with
-gst_type_factory_find().
-</para>
-<para>
-gst_type_factory_new() is used to create a new typefactory from the given
-#GstTypeDefinition. A typefactory is added to a #GstPlugin with 
-gst_plugin_add_feature() as shown in the example:
-<programlisting>
-  static GstCaps*
-  avi_type_find (GstBuffer *buf, gpointer private)
-  {
-    gchar *data = GST_BUFFER_DATA (buf);
-                     
-    if (strncmp (&amp;data[0], "RIFF", 4)) return NULL;
-    if (strncmp (&amp;data[8], "AVI ", 4)) return NULL;
-                             
-    return gst_caps_new ("avi_type_find","video/avi", NULL);
-  }
-
-  /* typedefinition for 'avi' */
-  static GstTypeDefinition avidefinition = {
-    "avidecoder_video/avi",   /* the name of this definition */
-    "video/avi",              /* the mime type */
-    ".avi",                   /* the file extensions */
-    avi_type_find,             /* a pointer to a GstTypeFindFunc function */
-  };
-
-  static gboolean
-  plugin_init (GModule *module, GstPlugin *plugin)
-  {
-    GstTypeFactory *type;
-    ...
-    type = gst_type_factory_new (&amp;avidefinition);
-    gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
-    ...
-  }
-</programlisting>
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-#GstPluginFeature, #GstPlugin
-</para>
-
-<!-- ##### STRUCT GstTypeFactory ##### -->
-<para>
-The struct with the typefactory information.
-</para>
-
-
-<!-- ##### STRUCT GstTypeDefinition ##### -->
-<para>
-The typedefinition structure.
-</para>
-
-@name: The name of this factory
-@mime: The mime type of the new type.
-@exts: The extensions of this type.
-@typefindfunc: An optional typefind function.
-
-<!-- ##### USER_FUNCTION GstTypeFindFunc ##### -->
-<para>
-This is the function that will be called when a typefind has to be
-performed by a plugin.
-</para>
-
-@bs: 
-@priv: private; don't touch
-@Returns: A #GstCaps structure describing the type or NULL if the
-         type was not recognized by this function;
-<!-- # Unused Parameters # -->
-@buf: the buffer with media on which to perform the typefind
-
-
-<!-- ##### FUNCTION gst_type_factory_new ##### -->
-<para>
-
-</para>
-
-@definition: 
-@Returns: 
-
-
-<!-- ##### FUNCTION gst_type_factory_find ##### -->
-<para>
-
-</para>
-
-@name: 
-@Returns: 
-
-
index ad3f5ae74f5fd4079deba746835ef976b3bfb86d..10ae8f64bdf6b2aa62d70e5d887339b4ab9ad7fb 100644 (file)
 <!-- ##### SECTION Title ##### -->
-GstTypeFind
+Writing typefind functions
 
 <!-- ##### SECTION Short_Description ##### -->
-Detect the mime type of a media stream
+Using the type finding subsystem from plugins
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
-This element can be added to the pipeline and will notify the listener of
-the detected mime type of the stream. It is used in autoplugging.
+The typefinding subsystem in GStreamer is used to find the matching #GstCaps
+for an unidentified stream. This works similar to the unix <emphasis>file
+</emphasis> command.
+</para>
+<para>
+There are a number of elements which output unidentified data streams, such as
+a file source. Some other elements such as an autoplugger require a proper 
+identification of the data, so they can create the right pipeline. To find the
+right type, they use typefinding elements, the most notable being the typefind
+element. These elements take a list of all registered typefind functions and
+try them on the data to see if any of these functions can identify the data.
+</para>
+<para>
+The functions in this section provide the simple framework for writing these
+typefind functions. The job of a typefind function is to identify the type of
+the data good enough so that plugins using this type can understand them while
+make sure no other type is misidentified.
+<example>
+<title>a typefind function for Ogg data</title>
+<programlisting>
+static void
+ogg_type_find (GstTypeFind *tf, gpointer unused)
+{
+  guint8 *data = gst_type_find_peek (tf, 0, 4);
+  
+  if (data &amp;&amp; memcmp (data, "OggS", 4) == 0) {
+    gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, 
+           gst_caps_new ("ogg_type_find", "application/ogg", NULL));
+  }
+};
+</programlisting>
+</example>
 </para>
 
 <!-- ##### SECTION See_Also ##### -->
 <para>
+<link linkend="GstTypeFindFactory">GstTypeFactory - querying registered typefind 
+functions</link>
+</para>
+
+<!-- ##### STRUCT GstTypeFind ##### -->
+<para>
+This structure is filled by the caller of the typefind function. Typefind 
+functions must treat this as an opaque structure.
+</para>
 
+@peek: function called to get data. See gst_type_find_peek()
+@suggest: function called to suggest a caps. See gst_type_find_suggest()
+@data: caller defined data, that is passed when calling the functions
+@get_length: function called to query the length of the stream. See 
+gst_type_find_get_length(). Providing this function is optional.
+
+<!-- ##### USER_FUNCTION GstTypeFindFunction ##### -->
+<para>
+This is the prototype for a typefind function.
 </para>
 
-<!-- ##### VARIABLE gst_type_find_details ##### -->
+@find: The #GstTypeFind data
+@data: the user defined data that was provided on 
+gst_type_find_factory_register()
+
+
+<!-- ##### FUNCTION gst_type_find_peek ##### -->
 <para>
 
 </para>
 
+@find: 
+@offset: 
+@size: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gst_type_find_suggest ##### -->
+<para>
+
+</para>
+
+@find: 
+@probability: 
+@caps: 
+
+
+<!-- ##### FUNCTION gst_type_find_get_length ##### -->
+<para>
+
+</para>
+
+@find: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gst_type_find_factory_register ##### -->
+<para>
+
+</para>
+
+@plugin: 
+@name: 
+@rank: 
+@func: 
+@extensions: 
+@possible_caps: 
+@data: 
+
 
diff --git a/docs/gst/tmpl/gsttypefindfactory.sgml b/docs/gst/tmpl/gsttypefindfactory.sgml
new file mode 100644 (file)
index 0000000..118cf5a
--- /dev/null
@@ -0,0 +1,117 @@
+<!-- ##### SECTION Title ##### -->
+GstTypeFindFactory
+
+<!-- ##### SECTION Short_Description ##### -->
+information about registered typefind functions
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+These functions allow querying informations about registered typefind 
+functions. How to create and register these functions is described in
+the section <link linkend="gstreamer-Writing-typefind-functions">
+"Writing typefind functions"</link>.
+</para>
+<example>
+<title>how to write a simple typefinder</title>
+<programlisting>
+/* FIXME: compile this? ;) */
+typedef struct {
+  guint8 *data;
+  guint size;
+  guint probability;
+  GstCaps *data;
+} MyTypeFind;
+static void
+my_peek (gpointer data, gint64 offset, guint size)
+{
+  MyTypeFind *find = (MyTypeFind *) data;
+  if (offset &gt;= 0 &amp;&amp; offset + size &lt;= find->size) {
+    return find->data + offset;
+  }
+  return NULL;
+}
+static void
+my_suggest (gpointer data, guint probability, GstCaps *caps)
+{
+  MyTypeFind *find = (MyTypeFind *) data;
+  if (probability &gt; find->probability) {
+    find->probability = probability;
+    gst_caps_replace (&amp;find->caps, caps);
+  }
+}
+static GstCaps *
+find_type (guint8 *data, guint size)
+{
+  GList *walk, *type_list;
+  MyTypeFind find = {data, size, 0, NULL};
+  GstTypeFind gst_find = {my_peek, my_suggest, &amp;find, };
+  
+  walk = type_list = gst_type_find_factory_get_list ();
+  while (walk) {
+    GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data);
+    walk = g_list_next (walk)
+    gst_type_find_factory_call_function (factory, &amp;gst_find);
+  }
+  g_list_free (type_list);
+  return find.caps;
+};
+</programlisting>
+</example>
+<para>
+The above example shows how to write a very simple typefinder that identifies
+the given data. You can get quite a bit more complicated than that though.
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+<link linkend="gstreamer-Writing-typefind-functions">Writing typefind functions</link>
+</para>
+
+<!-- ##### STRUCT GstTypeFindFactory ##### -->
+<para>
+Object that stores information about a typefind function
+</para>
+
+
+<!-- ##### STRUCT GstTypeFindFactoryClass ##### -->
+<para>
+Class belonging to #GstTypeFindFactory.
+</para>
+
+@parent: 
+
+<!-- ##### FUNCTION gst_type_find_factory_get_list ##### -->
+<para>
+
+</para>
+
+@Returns: 
+
+
+<!-- ##### FUNCTION gst_type_find_factory_get_extensions ##### -->
+<para>
+
+</para>
+
+@factory: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gst_type_find_factory_get_caps ##### -->
+<para>
+
+</para>
+
+@factory: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gst_type_find_factory_call_function ##### -->
+<para>
+
+</para>
+
+@factory: 
+@find: 
+
+
index f5082115e91d1dd4d54b153235b897089ca14805..b2f44a6d774014159f54c52635b5141aef99d1e4 100644 (file)
@@ -15,12 +15,6 @@ else
 GST_LOADSAVE_SRC = gstxml.c
 endif
 
-if GST_DISABLE_TYPEFIND
-GST_TYPEFIND_SRC =
-else
-GST_TYPEFIND_SRC = gsttypefind.c
-endif
-
 if GST_DISABLE_AUTOPLUG
 GST_AUTOPLUG_SRC =
 GST_AUTOPLUG_DIRS =
@@ -81,10 +75,10 @@ else
 GST_URI_SRC = gsturi.c
 endif
 
-EXTRA_libgstreamer_@GST_MAJORMINOR@_la_SOURCES = gstcpuid_i386.s gstmarshal.list gstxml.c gsttypefind.c gstparse.c gstautoplug.c gsttrace.c
+EXTRA_libgstreamer_@GST_MAJORMINOR@_la_SOURCES = gstcpuid_i386.s gstmarshal.list gstxml.c gstparse.c gstautoplug.c gsttrace.c
 
-SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . $(GST_AUTOPLUG_DIRS) elements schedulers types $(GST_INDEX_DIRS) 
-DIST_SUBDIRS = autoplug elements parse registries schedulers types indexers
+SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . $(GST_AUTOPLUG_DIRS) elements schedulers $(GST_INDEX_DIRS) 
+DIST_SUBDIRS = autoplug elements parse registries schedulers indexers
 
 libgstreamer_@GST_MAJORMINOR@_la_SOURCES =             \
        gst.c                   \
@@ -96,7 +90,6 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES =            \
        gstbin.c                \
        gstbuffer.c             \
        gstbufferpool-default.c \
-       gstbytestream.c         \
        gstcaps.c               \
        gstclock.c              \
        gstcpu.c                \
@@ -124,8 +117,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES =          \
        gstthreaddummy.c        \
        $(GST_TRACE_SRC)        \
        gsttrashstack.c         \
-       gsttype.c               \
-       $(GST_TYPEFIND_SRC)     \
+       gsttypefind.c           \
        $(GST_URI_SRC)          \
        gsturitype.c            \
        gstutils.c              \
@@ -153,12 +145,10 @@ gst_headers =                     \
        gst.h                   \
        gstatomic.h             \
        gstobject.h             \
-       gsttypes.h              \
        gstautoplug.h           \
        gstbin.h                \
        gstbuffer.h             \
        gstbufferpool-default.h \
-       gstbytestream.h         \
        gstcaps.h               \
        gstclock.h              \
        gstcompat.h             \
@@ -187,8 +177,8 @@ gst_headers =                       \
        gstthread.h             \
        gsttrace.h              \
        gsttrashstack.h         \
-       gsttype.h               \
        gsttypefind.h           \
+       gsttypes.h              \
        gsturi.h                \
        gsturitype.h            \
        gstutils.h              \
index 110e97b4e0cd5142c72daaa008a69dae90093ddb..15007e4028f92f4d6d577522519f3cb23287f217 100644 (file)
@@ -410,82 +410,105 @@ gst_spider_identity_src_loop (GstSpiderIdentity *ident)
 }
 /* This loop function is only needed when typefinding.
  */
+typedef struct {
+  GstBuffer *buffer;
+  guint best_probability;
+  GstCaps *caps;
+} SpiderTypeFind;
+guint8 *
+spider_find_peek (gpointer data, gint64 offset, guint size)
+{
+  SpiderTypeFind *find = (SpiderTypeFind *) data;
+  gint64 buffer_offset = GST_BUFFER_OFFSET_IS_VALID (find->buffer) ? 
+                        GST_BUFFER_OFFSET (find->buffer) : 0;
+  
+  if (offset >= buffer_offset && offset + size <= buffer_offset + GST_BUFFER_SIZE (find->buffer)) {
+    GST_LOG ("peek %"G_GINT64_FORMAT", %u successful", offset, size);
+    return GST_BUFFER_DATA (find->buffer) + offset - buffer_offset;
+  } else {
+    GST_LOG ("peek %"G_GINT64_FORMAT", %u failed", offset, size);
+    return NULL;
+  }
+}
+static void
+spider_find_suggest (gpointer data, guint probability, GstCaps *caps)
+{
+  SpiderTypeFind *find = (SpiderTypeFind *) data;
+  G_GNUC_UNUSED gchar *caps_str;
+
+  caps_str = gst_caps_to_string (caps);
+  GST_INFO ("suggest %u, %s", probability, caps_str);
+  g_free (caps_str);
+  if (probability > find->best_probability) {
+    gst_caps_replace (&find->caps, caps);
+    find->best_probability = probability;
+  }
+}
 static void
 gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity *ident)
 {
-  GstBuffer *buf = NULL;
-  GList *type_list;
-  GstCaps *caps;
-  GstByteStream *bs;
+  GstData *data;
+  GstTypeFind gst_find;
+  SpiderTypeFind find;
+  GList *walk, *type_list = NULL;
 
   g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
 
-  /* 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;
+  data = gst_pad_pull (ident->sink);
+  while (!GST_IS_BUFFER (data)) {
+    gst_spider_identity_chain (ident->sink, GST_BUFFER (data));
+    data = gst_pad_pull (ident->sink);
   }
-
+  
+  find.buffer = GST_BUFFER (data);
   /* maybe there are already valid caps now? */
-  if ((caps = gst_pad_get_caps (ident->sink)) != NULL) {
+  if ((find.caps = gst_pad_get_caps (ident->sink)) != NULL) {
+    gst_caps_ref (find.caps); /* it's unrefed later below */
     goto plug;
   }
   
   /* now do the actual typefinding with the supplied buffer */
-  type_list = (GList *) gst_type_get_list ();
+  walk = type_list = gst_type_find_factory_get_list ();
     
-  while (type_list) {
-    GstType *type = (GstType *) type_list->data;
-    GSList *factories = type->factories;
-
-    while (factories) {
-      GstTypeFactory *factory = GST_TYPE_FACTORY (factories->data);
-      GstTypeFindFunc typefindfunc = (GstTypeFindFunc)factory->typefindfunc;
-
-      GST_DEBUG ("trying typefind function %s", GST_PLUGIN_FEATURE_NAME (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");
-         gst_caps_sink (caps);
-        } else {
-          goto plug;
-       }
-      }
-      factories = g_slist_next (factories);
-    }
-    type_list = g_list_next (type_list);
+  find.best_probability = 0;
+  find.caps = NULL;
+  gst_find.data = &find;
+  gst_find.peek = spider_find_peek;
+  gst_find.suggest = spider_find_suggest;
+  while (walk) {
+    GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data);
+
+    GST_DEBUG ("trying typefind function %s", GST_PLUGIN_FEATURE_NAME (factory));
+    gst_type_find_factory_call_function (factory, &gst_find);
+    if (find.best_probability >= GST_TYPE_FIND_MAXIMUM)
+      goto plug;
+    walk = g_list_next (walk);
   }
+  if (find.best_probability > 0)
+    goto plug;
   gst_element_error(GST_ELEMENT(ident), "Could not find media type", NULL);
-  buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
+  find.buffer = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
 
 end:
-
   /* remove loop function */
   gst_element_set_loop_function (GST_ELEMENT (ident), 
                                 (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
   
   /* push the buffer */
-  gst_spider_identity_chain (ident->sink, buf);
-
-  /* bytestream no longer needed */
-  gst_bytestream_destroy (bs);
+  gst_spider_identity_chain (ident->sink, find.buffer);
   
   return;
 
 plug:
-  gst_caps_debug (caps, "spider starting caps");
-  gst_caps_sink (caps);
+  GST_INFO ("typefind function found caps"); 
+  g_assert (gst_pad_try_set_caps (ident->src, find.caps) > 0);
+  gst_caps_debug (find.caps, "spider starting caps");
+  gst_caps_unref (find.caps);
+  if (type_list)
+    g_list_free (type_list);
 
   gst_spider_identity_plug (ident);
 
-  gst_bytestream_read (bs, &buf, bs->listavail);
-
   goto end;
 }
 
index 00bd244086c06d405f489b21d796209d2afb7508..0d6a67d050307ed3b9bd323c0699807caaa2ad36 100644 (file)
@@ -1,40 +1,51 @@
+# FIXME:
+# need to get gstbufferstore.[ch] into its own lib, preferrably 
+# libs/gst/buifferstore
+# This requires building libs/gst before this dir, which we currently don't
+# do.
+
 plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@
 
 plugin_LTLIBRARIES = libgstelements.la
 
 libgstelements_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
 libgstelements_la_SOURCES =    \
+       gstaggregator.c         \
+       gstbufferstore.c        \
        gstelements.c           \
-       gstfakesrc.c            \
-       gstidentity.c           \
        gstfakesink.c           \
-       gstfilesrc.c            \
+       gstfakesrc.c            \
        gstfilesink.c           \
-       gstfdsrc.c              \
+       gstfilesrc.c            \
        gstfdsink.c             \
+       gstfdsrc.c              \
+       gstidentity.c           \
+       gstmd5sink.c            \
        gstmultidisksrc.c       \
        gstpipefilter.c         \
-       gsttee.c                \
-       gstaggregator.c         \
        gstshaper.c             \
        gststatistics.c         \
-       gstmd5sink.c
+       gsttee.c                \
+       gsttypefindelement.c
+
 libgstelements_la_CFLAGS = $(GST_CFLAGS)
 libgstelements_la_LIBADD =
 libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS =               \
-       gstfakesrc.h            \
-       gstidentity.h           \
+       gstaggregator.h         \
+       gstbufferstore.h        \
        gstfakesink.h           \
-       gstfilesink.h           \
+       gstfakesrc.h            \
+       gstfdsink.h             \
        gstfdsrc.h              \
+       gstfilesink.h           \
+       gstfilesrc.h            \
+       gstidentity.h           \
+       gstmd5sink.h            \
        gstmultidisksrc.h       \
-       gstfdsink.h             \
        gstpipefilter.h         \
-       gsttee.h                \
-       gstaggregator.h         \
        gstshaper.h             \
-       gststatistics.h         \
-       gstfilesrc.h            \
-       gstmd5sink.h
+       gststatistics.h         \
+       gsttee.h                \
+       gsttypefindelement.h
diff --git a/gst/elements/gstbufferstore.c b/gst/elements/gstbufferstore.c
new file mode 100644 (file)
index 0000000..d7dc0b3
--- /dev/null
@@ -0,0 +1,465 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gstbufferstore.c: keep an easily accessible list of all buffers
+ *
+ * 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 "gstbufferstore.h"
+#include <string.h>
+
+GST_DEBUG_CATEGORY (gst_buffer_store_debug);
+#define GST_CAT_DEFAULT gst_buffer_store_debug
+
+enum {
+  CLEARED,
+  BUFFER_ADDED,
+  LAST_SIGNAL
+};
+enum {
+  ARG_0
+};
+
+
+static void    gst_buffer_store_class_init     (gpointer               g_class,
+                                                 gpointer              class_data);
+static void    gst_buffer_store_init           (GTypeInstance *        instance,
+                                                gpointer               g_class);
+static void    gst_buffer_store_dispose        (GObject *              object);
+
+static gboolean        gst_buffer_store_add_buffer_func (GstBufferStore *      store, 
+                                                GstBuffer *            buffer);
+static void    gst_buffer_store_cleared_func   (GstBufferStore *       store);
+  
+static GObjectClass *parent_class = NULL;
+static guint gst_buffer_store_signals[LAST_SIGNAL] = { 0 };
+
+G_GNUC_UNUSED static void
+debug_buffers (GstBufferStore *store)
+{
+  GList *walk = store->buffers;
+  
+  g_printerr ("BUFFERS in store:\n");
+  while (walk) {
+    g_print ("%15"G_GUINT64_FORMAT" - %7u\n", GST_BUFFER_OFFSET (walk->data), GST_BUFFER_SIZE (walk->data));
+    walk = g_list_next (walk);
+  }
+  g_printerr ("\n");
+}
+GType
+gst_buffer_store_get_type (void)
+{
+  static GType store_type = 0;
+
+  if (!store_type) {
+    static const GTypeInfo store_info = {
+      sizeof (GstBufferStoreClass),
+      NULL,
+      NULL,
+      gst_buffer_store_class_init,
+      NULL,
+      NULL,
+      sizeof (GstBufferStore),
+      0,
+      gst_buffer_store_init,
+      NULL
+    };
+    store_type = g_type_register_static (G_TYPE_OBJECT,
+                                           "GstBufferStore",
+                                           &store_info, 0);
+  
+    /* FIXME: better description anyone? */
+    GST_DEBUG_CATEGORY_INIT (gst_buffer_store_debug, "bufferstore", 0, "store all data");
+  }
+  return store_type;
+}
+static gboolean
+continue_accu (GSignalInvocationHint *ihint, GValue *return_accu, 
+              const GValue *handler_return, gpointer data)
+{
+  gboolean do_continue = g_value_get_boolean (handler_return);
+  g_value_set_boolean (return_accu, do_continue);
+
+  return do_continue;
+}
+static void
+gst_buffer_store_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstBufferStoreClass *store_class;
+
+  gobject_class = G_OBJECT_CLASS (g_class);
+  store_class = GST_BUFFER_STORE_CLASS (g_class);
+
+  parent_class = g_type_class_peek_parent (g_class);
+
+  gobject_class->dispose = gst_buffer_store_dispose;
+  
+  gst_buffer_store_signals[CLEARED] = g_signal_new ("cleared", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstBufferStoreClass, cleared), NULL, NULL,
+          gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
+  gst_buffer_store_signals[BUFFER_ADDED] = g_signal_new ("buffer-added", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstBufferStoreClass, buffer_added), continue_accu, NULL,
+          gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+
+  store_class->cleared = gst_buffer_store_cleared_func;
+  store_class->buffer_added = gst_buffer_store_add_buffer_func;
+}
+static void
+gst_buffer_store_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstBufferStore *store = GST_BUFFER_STORE (instance);
+  
+  store->buffers = NULL;
+}
+static void
+gst_buffer_store_dispose (GObject *object)
+{
+  GstBufferStore *store = GST_BUFFER_STORE (object);
+
+  gst_buffer_store_clear (store);
+
+  parent_class->dispose (object);
+}
+static gboolean
+gst_buffer_store_add_buffer_func (GstBufferStore *store, GstBuffer *buffer)
+{
+  g_assert (buffer != NULL);
+  
+  if (!GST_BUFFER_OFFSET_IS_VALID (buffer) &&
+      store->buffers &&
+      GST_BUFFER_OFFSET_IS_VALID (store->buffers->data)) {
+    /* we assumed valid offsets, but suddenly they are not anymore */
+    GST_DEBUG_OBJECT (store, "attempting to add buffer %p with invalid offset to store with valid offset, abort",
+           buffer);
+    return FALSE;
+  } else if (!store->buffers || !GST_BUFFER_OFFSET_IS_VALID (store->buffers->data)) {
+    /* the starting buffer had an invalid offset, in that case we assume continuous buffers */
+    GST_LOG_OBJECT (store, "adding buffer %p with invalid offset and size %u",
+           buffer, GST_BUFFER_SIZE (buffer));
+    gst_data_ref (GST_DATA (buffer));
+    store->buffers = g_list_append (store->buffers, buffer);
+    return TRUE;
+  } else {
+    /* both list and buffer have valid offsets, we can really go wild */
+    GList *walk, *current_list = NULL;
+    GstBuffer *current;
+    
+    g_assert (GST_BUFFER_OFFSET_IS_VALID (buffer));
+    GST_LOG_OBJECT (store, "attempting to add buffer %p with offset %"G_GUINT64_FORMAT" and size %u",
+           buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+    /* we keep a sorted list of non-overlapping buffers */
+    walk = store->buffers;
+    while (walk) {
+      current = GST_BUFFER (walk->data);
+      current_list = walk;
+      walk = g_list_next (walk);
+      if (GST_BUFFER_OFFSET (current) < GST_BUFFER_OFFSET (buffer)) {
+       continue;
+      } else if (GST_BUFFER_OFFSET (current) == GST_BUFFER_OFFSET (buffer)) {
+       guint needed_size;
+       if (walk) {
+         needed_size = MIN (GST_BUFFER_SIZE (buffer), 
+                 GST_BUFFER_OFFSET (walk->data) - GST_BUFFER_OFFSET (current));
+       } else {
+         needed_size = GST_BUFFER_SIZE (buffer);
+       }
+       if (needed_size <= GST_BUFFER_SIZE (current)) {
+         buffer = NULL;
+         break;
+       } else {
+         if (needed_size < GST_BUFFER_SIZE (buffer)) {
+           /* need to create subbuffer to not have overlapping data */
+           GstBuffer *sub = gst_buffer_create_sub (buffer, 0, needed_size);
+           g_assert (sub);
+           buffer = sub;
+         } else {
+           gst_data_ref (GST_DATA (buffer));
+         }
+         /* replace current buffer with new one */
+         GST_INFO_OBJECT (store, "replacing buffer %p with buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
+                          current_list->data, buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+         gst_data_unref (GST_DATA (current_list->data));
+         current_list->data = buffer;
+         buffer = NULL;
+         break;
+       }
+      } else if (GST_BUFFER_OFFSET (current) > GST_BUFFER_OFFSET (buffer)) {
+       GList *previous = g_list_previous (current_list);
+       guint64 start_offset = previous ? 
+               GST_BUFFER_OFFSET (previous->data) + GST_BUFFER_SIZE (previous->data) : 0;
+
+       if (start_offset == GST_BUFFER_OFFSET (current)) {
+         buffer = NULL;
+         break;
+       } else {
+         /* we have data to insert */
+         if (start_offset > GST_BUFFER_OFFSET (buffer) ||
+             GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer) > GST_BUFFER_OFFSET (current)) {
+           /* need a subbuffer */
+           start_offset = GST_BUFFER_OFFSET (buffer) > start_offset ? 0 : 
+                          start_offset - GST_BUFFER_OFFSET (buffer);
+           GstBuffer* sub = gst_buffer_create_sub (buffer, start_offset,
+                   MIN (GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (current) - start_offset - GST_BUFFER_OFFSET (buffer)));
+           g_assert (sub);
+           GST_BUFFER_OFFSET (sub) = start_offset + GST_BUFFER_OFFSET (buffer);
+           buffer = sub;
+         } else {
+           gst_data_ref (GST_DATA (buffer));
+         }
+         GST_INFO_OBJECT (store, "adding buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
+                          buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+         store->buffers = g_list_insert_before (store->buffers, current_list, buffer);
+         buffer = NULL;
+         break;
+       }
+      }
+    }
+    if (buffer) {
+      gst_data_ref (GST_DATA (buffer));
+      GST_INFO_OBJECT (store, "adding buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
+                      buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+      if (current_list) {
+       g_list_append (current_list, buffer);
+      } else {
+       g_assert (store->buffers == NULL);
+       store->buffers = g_list_prepend (NULL, buffer);
+      }
+    }
+    return TRUE;
+  }
+}
+static void
+gst_buffer_store_cleared_func (GstBufferStore *store)
+{
+  g_list_foreach (store->buffers, (GFunc) gst_data_unref, NULL);
+  g_list_free (store->buffers);
+  store->buffers = NULL;
+}
+/**
+ * gst_buffer_store_new:
+ *
+ * Creates a new bufferstore.
+ *
+ * Returns: the new bufferstore.
+ */
+GstBufferStore *
+gst_buffer_store_new (void)
+{
+  return GST_BUFFER_STORE (g_object_new (GST_TYPE_BUFFER_STORE, NULL));
+}
+/**
+ * gst_buffer_store_clear:
+ * @store: a bufferstore
+ *
+ * Clears the buffer store. All buffers are removed and the buffer store
+ * behaves like it was just created.
+ */
+/* FIXME: call this function _reset ? */
+void
+gst_buffer_store_clear (GstBufferStore *store)
+{
+  g_return_if_fail (GST_IS_BUFFER_STORE (store));
+  
+  g_signal_emit (store, gst_buffer_store_signals [CLEARED], 0, NULL);
+}
+/**
+ * gst_buffer_store_add_buffer:
+ * @store: a bufferstore
+ * @buffer: the buffer to add
+ *
+ * Adds a buffer to the buffer store. 
+ *
+ * Returns: TRUE, if the buffer was added, FALSE if an error occured.
+ */
+gboolean
+gst_buffer_store_add_buffer (GstBufferStore *store, GstBuffer *buffer)
+{
+  gboolean ret;
+  
+  g_return_val_if_fail (GST_IS_BUFFER_STORE (store), FALSE);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
+
+  if (store->buffers &&
+      GST_BUFFER_OFFSET_IS_VALID (store->buffers->data) &&
+      !GST_BUFFER_OFFSET_IS_VALID (buffer))
+    return FALSE;
+  
+  g_signal_emit (store, gst_buffer_store_signals [BUFFER_ADDED], 0, buffer, &ret);
+  
+  return ret;
+}
+/**
+ * gst_buffer_store_get_buffer:
+ * @store: a bufferstore
+ * @offset: starting offset of returned buffer
+ * @size: size of returned buffer
+ *
+ * Returns a buffer that corresponds to the given area of data. If part of the
+ * data is not available inside the store, NULL is returned. You have to unref
+ * the buffer after use.
+ *
+ * Returns: a buffer with the requested data or NULL if the data was not 
+ *          available.
+ */
+GstBuffer *
+gst_buffer_store_get_buffer (GstBufferStore *store, guint64 offset, guint size)
+{
+  GstBuffer *current;
+  GList *walk;
+  guint8 *data;
+  guint tmp;
+  guint64 cur_offset;
+  gboolean have_offset;
+  GstBuffer *ret = NULL;
+
+  g_return_val_if_fail (GST_IS_BUFFER_STORE (store), NULL);
+
+  walk = store->buffers;
+  if (!walk)
+    return NULL;
+  if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) {
+    have_offset = TRUE;
+  } else {
+    have_offset = FALSE;
+    cur_offset = 0;
+  }
+  while (walk) {
+    current = GST_BUFFER (walk->data);
+    if (have_offset) {
+      cur_offset = GST_BUFFER_OFFSET (current);
+    }
+    walk = g_list_next (walk);
+    if (cur_offset > offset) {
+      /* #include <windows.h>
+         do_nothing_loop (); */
+    } else if (cur_offset == offset &&
+       GST_BUFFER_SIZE (current) == size) {
+      GST_LOG_OBJECT (store, "found matching buffer %p for offset %"G_GUINT64_FORMAT" and size %u",
+                     current, offset, size);
+      ret = current;
+      gst_data_ref (GST_DATA (ret));
+      GST_LOG_OBJECT (store, "refcount %d",
+                     GST_DATA_REFCOUNT_VALUE(ret));
+      break;
+    } else if (cur_offset + GST_BUFFER_SIZE (current) > offset) {
+      if (cur_offset + GST_BUFFER_SIZE (current) >= offset + size) {
+       ret = gst_buffer_create_sub (current, offset - cur_offset, size);
+       GST_LOG_OBJECT (store, "created subbuffer %p from buffer %p for offset %llu and size %u",
+                       ret, current,  offset, size);
+       break;
+      }
+      /* uh, the requested data spans some buffers */
+      ret = gst_buffer_new_and_alloc (size);
+      GST_LOG_OBJECT (store, "created buffer %p for offset %"G_GUINT64_FORMAT
+                     " and size %u, will fill with data now",
+                     ret, offset, size);
+      data = GST_BUFFER_DATA (ret);
+      tmp = GST_BUFFER_SIZE (current) - offset + cur_offset;
+      memcpy (data, GST_BUFFER_DATA (current) + offset - cur_offset, tmp);
+      data += tmp;
+      size -= tmp;
+      while (size) {
+       if (walk == NULL || 
+           (have_offset && 
+            GST_BUFFER_OFFSET (current) + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) {
+         GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and remaining size %u available, aborting",
+                           offset, size);
+         gst_data_unref (GST_DATA (ret));
+         ret = NULL;
+         goto out;
+       }
+       current = GST_BUFFER (walk->data);
+       walk = g_list_next (walk);
+       tmp = MIN (GST_BUFFER_SIZE (current), size);
+       memcpy (data, GST_BUFFER_DATA (current), tmp);
+       size -= tmp;
+      }
+    }
+    if (!have_offset) {
+      cur_offset += GST_BUFFER_SIZE (current);
+    }
+  }
+out:
+  
+  return ret;
+}
+/**
+ * gst_buffer_store_get_size:
+ * @store: a bufferstore
+ * @offset: desired offset
+ *
+ * Calculates the number of bytes available starting from offset. This allows
+ * to query a buffer with the returned size.
+ *
+ * Returns: the number of continuous bytes in the bufferstore starting at
+ *          offset.
+ */
+guint
+gst_buffer_store_get_size (GstBufferStore *store, guint64 offset)
+{
+  GList *walk;
+  guint64 cur_offset;
+  gboolean have_offset;
+  gboolean counting = FALSE;
+  GstBuffer *current;
+  guint ret = 0;
+
+  g_return_val_if_fail (GST_IS_BUFFER_STORE (store), 0);
+
+  walk = store->buffers;
+  if (!walk)
+    return 0;
+  if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) {
+    have_offset = TRUE;
+  } else {
+    have_offset = FALSE;
+    cur_offset = 0;
+  }
+  while (walk) {
+    if (have_offset && counting && 
+       cur_offset + GST_BUFFER_SIZE (current) !=  GST_BUFFER_OFFSET (walk->data)) {
+      break;
+    }
+    current = GST_BUFFER (walk->data);
+    if (have_offset) {
+      cur_offset = GST_BUFFER_OFFSET (current);
+    }
+    walk = g_list_next (walk);
+    if (counting) {
+      ret += GST_BUFFER_SIZE (current);
+    } else {
+      if (cur_offset > offset)
+       return 0;
+      if (cur_offset + GST_BUFFER_SIZE (current) > offset) {
+       /* we have at least some bytes */
+       ret = cur_offset + GST_BUFFER_SIZE (current) - offset;
+       counting = TRUE;
+      }
+    }
+    if (!have_offset) {
+      cur_offset += GST_BUFFER_SIZE (current);
+    }
+  }
+  
+  return ret;
+}
diff --git a/gst/elements/gstbufferstore.h b/gst/elements/gstbufferstore.h
new file mode 100644 (file)
index 0000000..15fff8e
--- /dev/null
@@ -0,0 +1,73 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: keep an easily accessible list of all buffers
+ *
+ * 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_BUFFER_STORE_H__
+#define __GST_BUFFER_STORE_H__
+
+#include <gst/gstbuffer.h>
+#include <gst/gstinfo.h>
+#include <gst/gstmarshal.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_BUFFER_STORE          (gst_buffer_store_get_type ())
+#define GST_BUFFER_STORE(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_BUFFER_STORE, GstBufferStore))
+#define GST_IS_BUFFER_STORE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_BUFFER_STORE))
+#define GST_BUFFER_STORE_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BUFFER_STORE, GstBufferStoreClass))
+#define GST_IS_BUFFER_STORE_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_BUFFER_STORE))
+#define GST_BUFFER_STORE_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BUFFER_STORE, GstBufferStoreClass))
+
+typedef struct _GstBufferStore                 GstBufferStore;
+typedef struct _GstBufferStoreClass    GstBufferStoreClass;
+
+struct _GstBufferStore {
+  GObject              object;
+
+  GList *              buffers;
+};
+
+struct _GstBufferStoreClass {
+  GObjectClass         parent_class;
+
+  /* signals */
+  void                 (* cleared)                     (GstBufferStore *       store);
+  gboolean             (* buffer_added)                (GstBufferStore *       store,
+                                                        GstBuffer *            buffer);
+};
+
+GType                  gst_buffer_store_get_type       (void);
+
+GstBufferStore *       gst_buffer_store_new            (void);
+void                   gst_buffer_store_clear          (GstBufferStore *       store);
+
+gboolean               gst_buffer_store_add_buffer     (GstBufferStore *       store,
+                                                        GstBuffer *            buffer);
+
+GstBuffer *            gst_buffer_store_get_buffer     (GstBufferStore *       store,
+                                                        guint64                offset,
+                                                        guint                  size);
+guint                  gst_buffer_store_get_size       (GstBufferStore *       store,
+                                                        guint64                offset);
+
+G_END_DECLS
+
+#endif /* __GST_BUFFER_STORE_H__ */
index 78413f2161a5c7ea3ff68318a8ba5720992565a4..7b769a05148db7a4d9ee008ebfd208e3e54dc063 100644 (file)
 
 #include <gst/gst.h>
 
-#include "gstfilesrc.h"
-#include "gstfilesink.h"
-#include "gstidentity.h"
+#include "gstaggregator.h"
 #include "gstfakesink.h"
 #include "gstfakesrc.h"
 #include "gstfdsink.h"
 #include "gstfdsrc.h"
+#include "gstfilesink.h"
+#include "gstfilesrc.h"
+#include "gstidentity.h"
+#include "gstmd5sink.h"
 #include "gstmultidisksrc.h"
 #include "gstpipefilter.h"
-#include "gsttee.h"
-#include "gstaggregator.h"
 #include "gstshaper.h"
 #include "gststatistics.h"
-#include "gstmd5sink.h"
+#include "gsttee.h"
+#include "gsttypefindelement.h"
 
 
 struct _elements_entry {
@@ -55,20 +56,21 @@ extern GType gst_filesrc_get_type(void);
 extern GstElementDetails gst_filesrc_details;
 
 static struct _elements_entry _elements[] = {
+  { "aggregator",   gst_aggregator_get_type,   &gst_aggregator_details,        gst_aggregator_factory_init },
   { "fakesrc",             gst_fakesrc_get_type,       &gst_fakesrc_details,           gst_fakesrc_factory_init },
   { "fakesink",     gst_fakesink_get_type,     &gst_fakesink_details,          gst_fakesink_factory_init },
+  { "fdsink",       gst_fdsink_get_type,       &gst_fdsink_details,            NULL },
+  { "fdsrc",       gst_fdsrc_get_type,         &gst_fdsrc_details,             NULL },
   { "filesrc",             gst_filesrc_get_type,       &gst_filesrc_details,           NULL },
   { "filesink",            gst_filesink_get_type,      &gst_filesink_details,          NULL },
   { "identity",     gst_identity_get_type,     &gst_identity_details,          NULL },
-  { "fdsink",       gst_fdsink_get_type,       &gst_fdsink_details,            NULL },
-  { "fdsrc",       gst_fdsrc_get_type,         &gst_fdsrc_details,             NULL },
+  { "md5sink",      gst_md5sink_get_type,      &gst_md5sink_details,           gst_md5sink_factory_init },
   { "multidisksrc", gst_multidisksrc_get_type, &gst_multidisksrc_details,      NULL },
   { "pipefilter",   gst_pipefilter_get_type,   &gst_pipefilter_details,        NULL },
-  { "tee",                 gst_tee_get_type,           &gst_tee_details,               gst_tee_factory_init },
-  { "aggregator",   gst_aggregator_get_type,   &gst_aggregator_details,        gst_aggregator_factory_init },
   { "shaper",       gst_shaper_get_type,       &gst_shaper_details,            gst_shaper_factory_init },
   { "statistics",   gst_statistics_get_type,   &gst_statistics_details,        NULL },
-  { "md5sink",      gst_md5sink_get_type,      &gst_md5sink_details,           gst_md5sink_factory_init },
+  { "tee",                 gst_tee_get_type,           &gst_tee_details,               gst_tee_factory_init },
+  { "typefind",     gst_type_find_element_get_type, &gst_type_find_element_details,    NULL },
   { NULL, 0 },
 };
 
@@ -80,20 +82,21 @@ plugin_init (GModule *module, GstPlugin *plugin)
 
   gst_plugin_set_longname (plugin, "Standard GST Elements");
 
-  GST_DEBUG_CATEGORY_INIT (gst_fakesrc_debug,  "fakesrc",      0,      "fakesrc element");
+  GST_DEBUG_CATEGORY_INIT (gst_aggregator_debug, "aggregator", 0,      "aggregator element");
   GST_DEBUG_CATEGORY_INIT (gst_fakesink_debug, "fakesink",     0,      "fakesink element");
+  GST_DEBUG_CATEGORY_INIT (gst_fakesrc_debug,  "fakesrc",      0,      "fakesrc element");
+  GST_DEBUG_CATEGORY_INIT (gst_fdsink_debug,   "fdsink",       0,      "fdsink element");
+  GST_DEBUG_CATEGORY_INIT (gst_fdsrc_debug,    "fdsrc",        0,      "fdsrc element");
+  GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink",     0,      "filesink element");
   GST_DEBUG_CATEGORY_INIT (gst_filesrc_debug,  "filesrc",      0,      "filesrc element");
-  GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "fakesink",     0,      "filesink element");
   GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity",     0,      "identity element");
-  GST_DEBUG_CATEGORY_INIT (gst_fdsrc_debug,    "fdsrc",        0,      "fdsrc element");
-  GST_DEBUG_CATEGORY_INIT (gst_fdsink_debug,   "fdsink",       0,      "fdsink element");
+  GST_DEBUG_CATEGORY_INIT (gst_md5sink_debug,  "md5sink",      0,      "md5sink element");
   GST_DEBUG_CATEGORY_INIT (gst_multidisksrc_debug, "multidisksrc", 0,  "multidisksrc element");
   GST_DEBUG_CATEGORY_INIT (gst_pipefilter_debug, "pipefilter", 0,      "pipefilter element");
-  GST_DEBUG_CATEGORY_INIT (gst_tee_debug,      "tee",          0,      "tee element");
-  GST_DEBUG_CATEGORY_INIT (gst_aggregator_debug, "aggregator", 0,      "aggregator element");
   GST_DEBUG_CATEGORY_INIT (gst_shaper_debug,   "shaper",       0,      "shaper element");
   GST_DEBUG_CATEGORY_INIT (gst_statistics_debug, "statistics", 0,      "statistics element");
-  GST_DEBUG_CATEGORY_INIT (gst_md5sink_debug,  "md5sink",      0,      "md5sink element");
+  GST_DEBUG_CATEGORY_INIT (gst_tee_debug,      "tee",          0,      "tee element");
+  GST_DEBUG_CATEGORY_INIT (gst_type_find_element_debug,        "typefind", GST_DEBUG_BG_YELLOW | GST_DEBUG_FG_GREEN, "typefind element");
 
   while (_elements[i].name) {  
     factory = gst_element_factory_new (_elements[i].name,
@@ -112,12 +115,10 @@ plugin_init (GModule *module, GstPlugin *plugin)
       _elements[i].factoryinit (factory);
     }
 /*      g_print("added factory '%s'\n",_elements[i].name); */
-
     i++;
   }
 
 /*  INFO (GST_INFO_PLUGIN_LOAD,"gstelements: loaded %d standard elements", i);*/
-
   return TRUE;
 }
 
index e992ee50f994a8e9bdd9a7ed290cb539db87de9c..14287e347ef3cd8b786de107c61b92c7c2344a99 100644 (file)
@@ -487,7 +487,7 @@ gst_fakesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
           src->pool = gst_buffer_pool_get_default (src->sizemax, 10);
       } else {
         if (src->pool) {
-          gst_buffer_pool_free (src->pool);
+          gst_buffer_pool_unref (src->pool);
           src->pool = NULL;
         }
       }
index f0de20d0a249bdca4775d272be5dabe03905ac1e..d3288d6b368456606f950fe83214ce04d2e6a20c 100644 (file)
@@ -88,18 +88,6 @@ GstElementDetails gst_filesrc_details = {
 #define DEFAULT_BLOCKSIZE      4*1024
 #define DEFAULT_MMAPSIZE       4*1024*1024
 
-#ifdef G_HAVE_ISO_VARARGS
-
-/* #define fs_print(...) g_print(__VA_ARGS__)  */
-#define fs_print(...)
-
-#elif defined(G_HAVE_GNUC_VARARGS)
-
-/* #define fs_print(format,args...) g_print(format, ## args)  */
-#define fs_print(format,args...)
-
-#endif
-
 /* FileSrc signals and args */
 enum {
   /* FILL ME */
@@ -305,7 +293,7 @@ gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
         src->mapsize = g_value_get_ulong (value);
         g_object_notify (G_OBJECT (src), "mmapsize");
       } else {
-        GST_INFO ( "invalid mapsize, must a multiple of pagesize, which is %d", 
+        GST_INFO_OBJECT (src, "invalid mapsize, must a multiple of pagesize, which is %d", 
                  src->pagesize);
       }
       break;
@@ -355,8 +343,8 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf)
 {
   GstFileSrc *src = GST_FILESRC (GST_BUFFER_POOL_PRIVATE (buf));
 
-  fs_print ("freeing mmap()d buffer at %d+%d\n", 
-           GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf));
+  GST_LOG_OBJECT (src, "freeing mmap()d buffer at %"G_GUINT64_FORMAT"+%u", 
+                 GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf));
 
   /* remove the buffer from the list of available mmap'd regions */
   g_mutex_lock (src->map_regions_lock);
@@ -375,13 +363,14 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf)
   munmap (GST_BUFFER_DATA (buf), GST_BUFFER_MAXSIZE (buf));
   /* cast to unsigned long, since there's no gportable way to print
    * guint64 as hex */
-  GST_DEBUG ( "unmapped region %08lx+%08lx at %p", 
+  GST_LOG_OBJECT (src, "unmapped region %08lx+%08lx at %p", 
                  (unsigned long) GST_BUFFER_OFFSET (buf),
                  (unsigned long) GST_BUFFER_MAXSIZE (buf), 
                  GST_BUFFER_DATA (buf));
 
   GST_BUFFER_DATA (buf) = NULL;
 
+  g_object_unref (src);
   gst_buffer_default_free (buf);
 }
 
@@ -394,7 +383,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
 
   g_return_val_if_fail (offset >= 0, NULL);
 
-  fs_print  ("mapping region %08llx+%08lx from file into memory\n",offset,(unsigned long)size);
+  GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory",offset,(unsigned long)size);
   mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset);
 
   if (mmapregion == NULL) {
@@ -402,11 +391,11 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
     return NULL;
   }
   else if (mmapregion == MAP_FAILED) {
-    GST_DEBUG ("mmap (0x%08lx, %d, 0x%llx) : %s",
+    GST_WARNING_OBJECT (src, "mmap (0x%08lx, %d, 0x%llx) failed: %s",
             (unsigned long)size, src->fd, offset, strerror (errno));
     return NULL;
   }
-  GST_DEBUG ( "mapped region %08lx+%08lx from file into memory at %p", 
+  GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p", 
                  (unsigned long)offset, (unsigned long)size, mmapregion);
 
   /* time to allocate a new mapbuf */
@@ -426,6 +415,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
   GST_BUFFER_OFFSET (buf) = offset;
   GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
   GST_BUFFER_POOL_PRIVATE (buf) = src;
+  g_object_ref (src);
   GST_BUFFER_FREE_FUNC (buf) = (GstDataFreeFunction) gst_filesrc_free_parent_mmap;
 
   g_mutex_lock (src->map_regions_lock);
@@ -522,7 +512,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
     /* if the end is before the mapend, the buffer is in current mmap region... */
     /* ('cause by definition if readend is in the buffer, so's readstart) */
     if (readend <= mapend) {
-      fs_print ("read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf\n",
+      GST_LOG_OBJECT (src, "read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf",
              src->curoffset, readsize, mapstart, mapsize);
       buf = gst_buffer_create_sub (src->mapbuf, src->curoffset - mapstart,
                                    readsize);
@@ -530,8 +520,8 @@ gst_filesrc_get_mmap (GstFileSrc *src)
 
     /* if the start actually is within the current mmap region, map an overlap buffer */
     } else if (src->curoffset < mapend) {
-      fs_print ("read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap\n",
-             src->curoffset, readsize, mapstart, mapsize);
+      GST_LOG_OBJECT (src, "read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap",
+             (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize);
       buf = gst_filesrc_map_small_region (src, src->curoffset, readsize);
       if (buf == NULL)
         return NULL;
@@ -545,8 +535,8 @@ gst_filesrc_get_mmap (GstFileSrc *src)
     /* either the read buffer overlaps the start of the mmap region */
     /* or the read buffer fully contains the current mmap region    */
     /* either way, it's really not relevant, we just create a new region anyway*/
-    fs_print ("read buf %llu+%d starts before mapbuf %d+%d, but overlaps it\n",
-             src->curoffset,readsize, mapstart, mapsize);
+    GST_LOG_OBJECT (src, "read buf %llu+%d starts before mapbuf %d+%d, but overlaps it",
+             (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize);
     buf = gst_filesrc_map_small_region (src, src->curoffset, readsize);
     if (buf == NULL)
       return NULL;
@@ -555,7 +545,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
   /* then deal with the case where the read buffer is totally outside */
   if (buf == NULL) {
     /* first check to see if there's a map that covers the right region already */
-    fs_print ("searching for mapbuf to cover %llu+%d\n",src->curoffset,readsize);
+    GST_LOG_OBJECT (src, "searching for mapbuf to cover %llu+%d",src->curoffset,readsize);
     region.offset = src->curoffset;
     region.size = readsize;
     map = g_tree_search (src->map_regions,
@@ -564,7 +554,8 @@ gst_filesrc_get_mmap (GstFileSrc *src)
 
     /* if we found an exact match, subbuffer it */
     if (map != NULL) {
-      fs_print ("found mapbuf at %d+%d, creating subbuffer\n",GST_BUFFER_OFFSET(map),GST_BUFFER_SIZE(map));
+      GST_LOG_OBJECT (src, "found mapbuf at %"G_GUINT64_FORMAT"+%u, creating subbuffer",
+                     GST_BUFFER_OFFSET (map), GST_BUFFER_SIZE (map));
       buf = gst_buffer_create_sub (map, src->curoffset - GST_BUFFER_OFFSET(map), readsize);
       GST_BUFFER_OFFSET (buf) = src->curoffset;
 
@@ -572,7 +563,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
     } else {
       /* if the read buffer crosses a mmap region boundary, create a one-off region */
       if ((src->curoffset / src->mapsize) != (readend / src->mapsize)) {
-        fs_print ("read buf %llu+%d crosses a %d-byte boundary, creating a one-off\n",
+        GST_LOG_OBJECT (src, "read buf %llu+%d crosses a %d-byte boundary, creating a one-off",
                src->curoffset,readsize,src->mapsize);
         buf = gst_filesrc_map_small_region (src, src->curoffset, readsize);
        if (buf == NULL)
@@ -583,7 +574,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
        size_t mapsize;
 
         off_t nextmap = src->curoffset - (src->curoffset % src->mapsize);
-        fs_print ("read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering\n",
+        GST_LOG_OBJECT (src, "read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering",
                src->curoffset, readsize, nextmap, src->mapsize);
         /* first, we're done with the old mapbuf */
         gst_buffer_unref(src->mapbuf);
@@ -591,7 +582,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
 
        /* double the mapsize as long as the readsize is smaller */
        while (readsize - (src->curoffset - nextmap) > mapsize) {
-          fs_print ("readsize smaller then mapsize %08x %d\n", readsize, mapsize);
+          GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d", readsize, mapsize);
           mapsize <<=1;
        }
         /* create a new one */
@@ -664,7 +655,7 @@ gst_filesrc_get (GstPad *pad)
     GstEvent *event;
 
     src->seek_happened = FALSE;
-    GST_DEBUG ("filesrc sending discont");
+    GST_DEBUG_OBJECT (src, "sending discont");
     event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset, NULL);
     src->need_flush = FALSE;
     return GST_DATA (event);
@@ -672,13 +663,13 @@ gst_filesrc_get (GstPad *pad)
   /* check for flush */
   if (src->need_flush) {
     src->need_flush = FALSE;
-    GST_DEBUG ("filesrc sending flush");
+    GST_DEBUG_OBJECT (src, "sending flush");
     return GST_DATA (gst_event_new_flush ());
   }
 
   /* check for EOF */
   if (src->curoffset == src->filelen) {
-    GST_DEBUG ("filesrc eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT,
+    GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT,
                src->curoffset, src->filelen);
     gst_element_set_eos (GST_ELEMENT (src));
     return GST_DATA (gst_event_new (GST_EVENT_EOS));
@@ -697,7 +688,7 @@ gst_filesrc_open_file (GstFileSrc *src)
 {
   g_return_val_if_fail (!GST_FLAG_IS_SET (src ,GST_FILESRC_OPEN), FALSE);
 
-  GST_DEBUG ( "opening file %s",src->filename);
+  GST_INFO_OBJECT (src, "opening file %s",src->filename);
 
   /* open the file */
   src->fd = open (src->filename, O_RDONLY);
@@ -829,7 +820,7 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event)
 {
   GstFileSrc *src = GST_FILESRC (GST_PAD_PARENT (pad));
 
-  GST_DEBUG ( "event %d", GST_EVENT_TYPE (event));
+  GST_DEBUG_OBJECT (src, "event %d", GST_EVENT_TYPE (event));
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_SEEK:
@@ -847,19 +838,19 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event)
           if (offset > src->filelen) 
            goto error;
           src->curoffset = offset;
-          GST_DEBUG ( "seek set pending to %" G_GINT64_FORMAT, src->curoffset);
+          GST_DEBUG_OBJECT (src, "seek set pending to %" G_GINT64_FORMAT, src->curoffset);
          break;
         case GST_SEEK_METHOD_CUR:
           if (offset + src->curoffset > src->filelen) 
            goto error;
           src->curoffset += offset;
-          GST_DEBUG ( "seek cur pending to %" G_GINT64_FORMAT, src->curoffset);
+          GST_DEBUG_OBJECT (src, "seek cur pending to %" G_GINT64_FORMAT, src->curoffset);
          break;
         case GST_SEEK_METHOD_END:
           if (ABS (offset) > src->filelen) 
            goto error;
           src->curoffset = src->filelen - ABS (offset);
-          GST_DEBUG ( "seek end pending to %" G_GINT64_FORMAT, src->curoffset);
+          GST_DEBUG_OBJECT (src, "seek end pending to %" G_GINT64_FORMAT, src->curoffset);
          break;
        default:
           goto error;
diff --git a/gst/elements/gsttypefind.c b/gst/elements/gsttypefind.c
new file mode 100644 (file)
index 0000000..bd5e3d3
--- /dev/null
@@ -0,0 +1,669 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.c: element that detects type of stream
+ *
+ * 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.
+ */
+
+/* FIXME: need a better solution for non-seekable streams */
+
+/* way of operation:
+ * 1) get a list of all typefind functions sorted best to worst
+ * 2) if all elements have been called with all requested data goto 8
+ * 3) call all functions once with all available data
+ * 4) if a function returns a value >= ARG_MAXIMUM goto 8
+ * 5) all functions with a result > ARG_MINIMUM or functions that did not get
+ *    all requested data (where peek returned NULL) stay in list
+ * 6) seek to requested offset of best function that still has open data 
+ *    requests
+ * 7) goto 2
+ * 8) take best available result and use its caps
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gsttypefindelement.h"
+#include "gst/gst_private.h"
+
+#include <gst/gsttypefind.h>
+
+GST_DEBUG_CATEGORY (gst_type_find_element_debug);
+#define GST_CAT_DEFAULT gst_type_find_element_debug
+
+GstElementDetails gst_type_find_element_details = {
+  "TypeFind",
+  "Generic",
+  "LGPL",
+  "Finds the media type of a stream",
+  VERSION,
+  "Benjamin Otte <in7y118@public.uni-hamburg.de>",
+  "(C) 2003",
+};
+
+/* generic templates */
+GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory,
+  "sink",
+  GST_PAD_SINK,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory,
+  "src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+
+/* TypeFind signals and args */
+enum {
+  HAVE_TYPE,
+  LAST_SIGNAL
+};
+enum {
+  ARG_0,
+  ARG_CAPS,
+  ARG_MINIMUM,
+  ARG_MAXIMUM
+};
+enum {
+  MODE_NORMAL, /* act as identity */
+  MODE_TYPEFIND, /* do typefinding */
+};
+
+
+static void    gst_type_find_element_class_init        (gpointer       g_class,
+                                                        gpointer       class_data);
+static void    gst_type_find_element_init              (GTypeInstance *instance,
+                                                        gpointer       g_class);
+static void    gst_type_find_element_dispose           (GObject *      object);
+static void    gst_type_find_element_set_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        const GValue * value, 
+                                                        GParamSpec *   pspec);
+static void    gst_type_find_element_get_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        GValue *       value, 
+                                                        GParamSpec *   pspec);
+
+static const GstEventMask *
+                gst_type_find_element_src_event_mask    (GstPad *      pad);
+static gboolean        gst_type_find_element_src_event         (GstPad *       pad,
+                                                        GstEvent *     event);
+
+static void    gst_type_find_element_chain             (GstPad *       sinkpad,
+                                                        GstData *      data);
+static GstElementStateReturn
+               gst_type_find_element_change_state      (GstElement *   element);
+
+static GstElementClass *parent_class = NULL;
+static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_type_find_element_get_type (void)
+{
+  static GType typefind_type = 0;
+
+  if (!typefind_type) {
+    static const GTypeInfo typefind_info = {
+      sizeof (GstTypeFindElementClass),
+      NULL,
+      NULL,
+      gst_type_find_element_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTypeFindElement),
+      0,
+      gst_type_find_element_init,
+      NULL
+    };
+    typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
+                                           "GstTypeFindElement",
+                                           &typefind_info, 0);
+  }
+  return typefind_type;
+}
+static void
+gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps)
+{
+  gchar *caps_str;
+  
+  g_assert (typefind->caps == NULL);
+  g_assert (caps != NULL);
+
+  caps_str = gst_caps_to_string (caps);
+  GST_INFO_OBJECT (typefind, "found caps %s", caps_str);
+  g_free (caps_str);
+  gst_caps_replace (&typefind->caps, caps);
+  if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) {
+    gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad");
+  }
+}
+static void
+gst_type_find_element_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstTypeFindElementClass *typefind_class;
+
+  gobject_class = G_OBJECT_CLASS (g_class);
+  gstelement_class = GST_ELEMENT_CLASS (g_class);
+  typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class);
+
+  parent_class = g_type_class_peek_parent (g_class);
+
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
+  gobject_class->dispose      =        GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
+
+  typefind_class->have_type = gst_type_find_element_have_type;
+
+  g_object_class_install_property (gobject_class, ARG_CAPS,
+         g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"),
+         GST_TYPE_CAPS, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE));
+
+  gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
+          gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
+          G_TYPE_UINT, GST_TYPE_CAPS);
+
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
+}
+static void
+gst_type_find_element_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance);
+  
+  /* sinkpad */
+  typefind->sink = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink");
+  gst_pad_set_chain_function (typefind->sink,
+                             gst_type_find_element_chain);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
+  /* srcpad */
+  typefind->src = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src");
+  gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
+  gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
+
+  typefind->caps = NULL;
+  typefind->min_probability = 1;
+  typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
+
+  typefind->store = gst_buffer_store_new ();
+
+  GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE);
+}
+static void
+gst_type_find_element_dispose (GObject *object)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+
+  if (typefind->store) {
+    g_object_unref (typefind->store);
+    typefind->store = NULL;
+  }
+}
+static void
+gst_type_find_element_set_property (GObject *object, guint prop_id, 
+                                   const GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_MINIMUM:
+      typefind->min_probability = g_value_get_uint (value);
+      g_object_notify (object, "minimum");
+      break;
+    case ARG_MAXIMUM:
+      typefind->max_probability = g_value_get_uint (value);
+      g_object_notify (object, "maximum");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static void
+gst_type_find_element_get_property (GObject *object, guint prop_id, 
+                                   GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_CAPS:
+      g_value_set_boxed (value, typefind->caps);
+      break;
+    case ARG_MINIMUM:
+      g_value_set_uint (value, typefind->min_probability);
+      break;
+    case ARG_MAXIMUM:
+      g_value_set_uint (value, typefind->max_probability);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static const GstEventMask *
+gst_type_find_element_src_event_mask (GstPad *pad)
+{
+  static const GstEventMask mask[] = {
+    { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
+    /* add more if you want, event masks suck and need to die anyway */
+    { 0, }
+  };
+  
+  return mask;
+}
+static gboolean        
+gst_type_find_element_src_event (GstPad *pad, GstEvent *event)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    gst_data_unref (GST_DATA (event));
+    return FALSE;
+  }
+  return gst_pad_event_default (pad, event);
+}
+typedef struct {
+  GstTypeFindFactory * factory;
+  gint                 probability;
+  GstCaps *            caps;
+  gint64               requested_offset;
+  guint                        requested_size;
+
+  GList *              buffers;
+  GstTypeFindElement * self;
+} TypeFindEntry;
+static void
+free_entry_buffers (TypeFindEntry *entry)
+{
+  g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL);
+  g_list_free (entry->buffers);
+  entry->buffers = NULL;
+}
+static void
+free_entry (TypeFindEntry *entry)
+{
+  free_entry_buffers (entry);
+  
+  if (entry->caps)
+    gst_caps_unref (entry->caps);
+  g_free (entry);
+}
+static void
+start_typefinding (GstTypeFindElement *typefind)
+{
+  g_assert (typefind->caps == NULL);
+  g_assert (typefind->possibilities == NULL);
+  
+  GST_DEBUG_OBJECT (typefind, "starting typefinding");
+  typefind->mode = MODE_TYPEFIND; 
+  typefind->stream_length_available = TRUE; 
+  typefind->stream_length = 0; 
+}
+static void
+stop_typefinding (GstTypeFindElement *typefind)
+{
+  /* stop all typefinding and set mode back to normal */
+  gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING;
+  
+  GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : "");
+  if (typefind->possibilities != NULL) {
+    /* this should only happen on PAUSED => READY or EOS */
+    GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities));
+    g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
+    g_list_free (typefind->possibilities);
+    typefind->possibilities = NULL;
+  }
+
+  typefind->mode = MODE_NORMAL;
+
+  if (push_cached_buffers) {
+    GstBuffer *buffer;
+    guint size = gst_buffer_store_get_size (typefind->store, 0);
+    if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) {
+      gst_pad_push (typefind->src, GST_DATA (buffer));
+    } else {
+      size = 0;
+    }
+    GST_LOG_OBJECT (typefind, "seeking back to current position %u", size);
+    if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), 
+                            gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) {
+      GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size);
+    }
+  }
+  gst_buffer_store_clear (typefind->store);
+}
+static guint64
+find_element_get_length (gpointer data)
+{
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  GstTypeFindElement *typefind = entry->self;
+  GstFormat format = GST_FORMAT_BYTES;
+  
+  if (!typefind->stream_length_available) {
+    GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", 
+           GST_PLUGIN_FEATURE_NAME (entry->factory));
+    return 0;
+  }
+  if (entry->self->stream_length == 0) {
+    typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, 
+           &format, &entry->self->stream_length);
+    if (format != GST_FORMAT_BYTES)
+      typefind->stream_length_available = FALSE;
+    if (!typefind->stream_length_available) {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory));
+      return 0;
+    } else {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
+    }
+  }
+  
+  return entry->self->stream_length;
+}
+static void
+gst_type_find_element_handle_event (GstPad *pad, GstEvent *event)
+{
+  TypeFindEntry *entry;
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* this should only happen when we got all available data */
+      entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL;
+      if (entry && entry->probability >= typefind->min_probability) {
+       GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", 
+               GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+       g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+      }
+      stop_typefinding (typefind);
+      gst_pad_event_default (pad, event);
+      break;
+    default:
+      gst_data_unref (GST_DATA (event));
+      break;
+    }
+  } else {
+    gst_pad_event_default (pad, event);
+  }
+}
+static guint8 *
+find_peek (gpointer data, gint64 offset, guint size)
+{
+  GstBuffer *buf;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
+  if (offset >= 0) {
+    buf = gst_buffer_store_get_buffer (entry->self->store, offset, size);
+  } else {
+    /* FIXME: can we do this easily without querying length? */
+    guint64 length = find_element_get_length (data);
+
+    if (length == 0) {
+      buf = NULL;
+    } else {
+      buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size);
+    }
+  }
+
+  if (buf) {
+    entry->buffers = g_list_prepend (entry->buffers, buf);
+    return GST_BUFFER_DATA (buf);
+  } else {
+    if (entry->requested_size == 0) {
+      GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", 
+             offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory));
+      entry->requested_offset = offset;
+      entry->requested_size = size;
+    }
+    return NULL;
+  }
+}
+static void
+find_suggest (gpointer data, guint probability, GstCaps *caps)
+{
+  gchar *str;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  str = gst_caps_to_string (caps);
+  GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str);
+  g_free (str);
+  if (((gint) probability) > entry->probability) {
+    entry->probability = probability;
+    gst_caps_replace (&entry->caps, caps);
+  }
+}
+static gint
+compare_type_find_entry (gconstpointer a, gconstpointer b)
+{
+  TypeFindEntry *one = (TypeFindEntry *) a;
+  TypeFindEntry *two = (TypeFindEntry *) b;
+
+  if (one->probability == two->probability) {
+    /* FIXME: can be improved by analyzing requests */
+    return 0;
+  } else {
+    return two->probability - one->probability;
+  }
+}
+static gint
+compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
+{
+  return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
+}
+static void
+gst_type_find_element_chain (GstPad *pad, GstData *data)
+{
+  GstTypeFindElement *typefind;
+  GList *entries;
+  TypeFindEntry *entry;
+  GList *walk;
+  GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length };
+
+  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+  if (GST_IS_EVENT (data)) {
+    gst_type_find_element_handle_event (pad, GST_EVENT (data));
+    return;
+  }
+  switch (typefind->mode) {
+    case MODE_NORMAL:
+      gst_pad_push (typefind->src, data);
+      return;
+    case MODE_TYPEFIND: {
+      gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data));
+      gst_data_unref (data);
+      if (typefind->possibilities == NULL) {
+       /* not yet started, get all typefinding functions into our "queue" */
+       GList *all_factories = gst_type_find_factory_get_list ();
+       GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", 
+                        g_list_length ((GList *) all_factories));
+       
+       all_factories = g_list_sort (all_factories, compare_type_find_factory);
+       walk = all_factories;
+       while (all_factories) {
+         entry = g_new0 (TypeFindEntry, 1);
+         
+         entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
+         entry->self = typefind;
+         entry->probability = 0;
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+         all_factories = g_list_next (all_factories);
+       }
+       g_list_free (all_factories);
+      }
+      /* call every typefind function once */
+      walk = entries = typefind->possibilities;
+      GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries));
+      typefind->possibilities = NULL;
+      while (walk) {
+       find.data = entry = (TypeFindEntry *) walk->data;
+       walk = g_list_next (walk);
+       entry->probability = 0;
+       entry->requested_offset = 0;
+       entry->requested_size = 0;
+       gst_type_find_factory_call_function (entry->factory, &find);
+       free_entry_buffers (entry);
+       if (entry->probability == 0 && entry->requested_size == 0) {
+         GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory));
+         free_entry (entry);
+       } else if (entry->probability >= typefind->max_probability) {
+         /* wooha, got caps */
+         GstCaps *found_caps = entry->caps;
+         guint probability = entry->probability;
+         
+         gst_caps_ref (found_caps);
+         GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability);
+         while (walk) {
+           free_entry ((TypeFindEntry *) walk->data);
+           walk = g_list_next (walk);
+         }
+         walk = typefind->possibilities;
+         while (walk) {
+           free_entry (walk->data);
+           walk = g_list_next (walk);
+         }
+         typefind->possibilities = NULL;
+         g_list_free (typefind->possibilities); 
+         g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps);
+         gst_caps_unref (found_caps);
+       } else {
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+       }
+      }
+      g_list_free (entries);
+      /* we may now already have caps or we might be left without functions to try */
+      if (typefind->caps) {
+       stop_typefinding (typefind);
+      } else if (typefind->possibilities == NULL) {
+       gst_element_error (GST_ELEMENT (typefind), "media type could not be detected");
+      } else {
+       /* set up typefind element for next iteration */
+       typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry);
+       
+       walk = typefind->possibilities;
+       while (walk) {
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size > 0) {
+           /* FIXME: need heuristic to find out if we should seek */
+           gint64 seek_offset;
+           GstEvent *event;
+
+           seek_offset = entry->requested_offset > 0 ? entry->requested_offset : 
+                         find_element_get_length (entry) + entry->requested_offset;
+           seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset);
+           event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset);
+           if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) {
+             /* done seeking */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             break;
+           } else if (entry->requested_offset < 0) {
+             /* impossible to seek */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             entry->requested_size = 0;
+             entry->requested_offset = 0;
+           }
+         }
+       }
+       /* throw out all entries that can't get more data */
+       walk = g_list_next (typefind->possibilities);
+       while (walk) {
+         GList *cur = walk;
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size == 0) {
+           GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory));
+           free_entry (entry);
+           typefind->possibilities = g_list_delete_link (typefind->possibilities, cur);
+         }
+       }
+       if (g_list_next (typefind->possibilities) == NULL) {
+         entry = (TypeFindEntry *) typefind->possibilities->data;
+         if (entry->probability > typefind->min_probability) {
+           GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+           free_entry (entry);
+           g_list_free (typefind->possibilities);
+           typefind->possibilities = NULL;
+           stop_typefinding (typefind);
+         }
+       }
+      }
+      break;
+    }
+    default:
+      g_assert_not_reached ();
+      return;
+  }
+}
+static GstElementStateReturn
+gst_type_find_element_change_state (GstElement *element)
+{
+  GstTypeFindElement *typefind;
+
+  typefind = GST_TYPE_FIND_ELEMENT (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_READY_TO_PAUSED:
+      start_typefinding (typefind);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      stop_typefinding (typefind);
+      gst_caps_replace (&typefind->caps, NULL);
+      break;
+    default:
+      break;
+  }
+  
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+}
diff --git a/gst/elements/gsttypefind.h b/gst/elements/gsttypefind.h
new file mode 100644 (file)
index 0000000..4b5e0cc
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: element that detects type of stream
+ *
+ * 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_TYPE_FIND_ELEMENT_H__
+#define __GST_TYPE_FIND_ELEMENT_H__
+
+#include <gst/gstinfo.h>
+#include <gst/gstelement.h>
+/* #include <gst/gstbufferstore.h> */
+#include "gstbufferstore.h"
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug);
+
+extern GstElementDetails gst_type_find_element_details;
+
+#define GST_TYPE_TYPE_FIND_ELEMENT             (gst_type_find_element_get_type ())
+#define GST_TYPE_FIND_ELEMENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement))
+#define GST_IS_TYPE_FIND_ELEMENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+
+typedef struct _GstTypeFindElement             GstTypeFindElement;
+typedef struct _GstTypeFindElementClass        GstTypeFindElementClass;
+
+struct _GstTypeFindElement {
+  GstElement           element;
+
+  GstPad *             sink;
+  GstPad *             src;
+
+  guint                        min_probability;
+  guint                        max_probability;
+  GstCaps *            caps;
+
+  guint                        mode;
+  GstBufferStore *     store;
+  guint64              stream_length;
+  gboolean             stream_length_available;
+  
+  GList *              possibilities;
+};
+
+struct _GstTypeFindElementClass {
+  GstElementClass      parent_class;
+
+  /* signals */
+  void                         (*have_type)    (GstTypeFindElement *element,
+                                        guint          probability,
+                                        GstCaps *      caps);
+};
+
+GType gst_type_find_element_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_TYPE_FIND_ELEMENT_H__ */
diff --git a/gst/elements/gsttypefindelement.c b/gst/elements/gsttypefindelement.c
new file mode 100644 (file)
index 0000000..bd5e3d3
--- /dev/null
@@ -0,0 +1,669 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.c: element that detects type of stream
+ *
+ * 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.
+ */
+
+/* FIXME: need a better solution for non-seekable streams */
+
+/* way of operation:
+ * 1) get a list of all typefind functions sorted best to worst
+ * 2) if all elements have been called with all requested data goto 8
+ * 3) call all functions once with all available data
+ * 4) if a function returns a value >= ARG_MAXIMUM goto 8
+ * 5) all functions with a result > ARG_MINIMUM or functions that did not get
+ *    all requested data (where peek returned NULL) stay in list
+ * 6) seek to requested offset of best function that still has open data 
+ *    requests
+ * 7) goto 2
+ * 8) take best available result and use its caps
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gsttypefindelement.h"
+#include "gst/gst_private.h"
+
+#include <gst/gsttypefind.h>
+
+GST_DEBUG_CATEGORY (gst_type_find_element_debug);
+#define GST_CAT_DEFAULT gst_type_find_element_debug
+
+GstElementDetails gst_type_find_element_details = {
+  "TypeFind",
+  "Generic",
+  "LGPL",
+  "Finds the media type of a stream",
+  VERSION,
+  "Benjamin Otte <in7y118@public.uni-hamburg.de>",
+  "(C) 2003",
+};
+
+/* generic templates */
+GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory,
+  "sink",
+  GST_PAD_SINK,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory,
+  "src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+
+/* TypeFind signals and args */
+enum {
+  HAVE_TYPE,
+  LAST_SIGNAL
+};
+enum {
+  ARG_0,
+  ARG_CAPS,
+  ARG_MINIMUM,
+  ARG_MAXIMUM
+};
+enum {
+  MODE_NORMAL, /* act as identity */
+  MODE_TYPEFIND, /* do typefinding */
+};
+
+
+static void    gst_type_find_element_class_init        (gpointer       g_class,
+                                                        gpointer       class_data);
+static void    gst_type_find_element_init              (GTypeInstance *instance,
+                                                        gpointer       g_class);
+static void    gst_type_find_element_dispose           (GObject *      object);
+static void    gst_type_find_element_set_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        const GValue * value, 
+                                                        GParamSpec *   pspec);
+static void    gst_type_find_element_get_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        GValue *       value, 
+                                                        GParamSpec *   pspec);
+
+static const GstEventMask *
+                gst_type_find_element_src_event_mask    (GstPad *      pad);
+static gboolean        gst_type_find_element_src_event         (GstPad *       pad,
+                                                        GstEvent *     event);
+
+static void    gst_type_find_element_chain             (GstPad *       sinkpad,
+                                                        GstData *      data);
+static GstElementStateReturn
+               gst_type_find_element_change_state      (GstElement *   element);
+
+static GstElementClass *parent_class = NULL;
+static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_type_find_element_get_type (void)
+{
+  static GType typefind_type = 0;
+
+  if (!typefind_type) {
+    static const GTypeInfo typefind_info = {
+      sizeof (GstTypeFindElementClass),
+      NULL,
+      NULL,
+      gst_type_find_element_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTypeFindElement),
+      0,
+      gst_type_find_element_init,
+      NULL
+    };
+    typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
+                                           "GstTypeFindElement",
+                                           &typefind_info, 0);
+  }
+  return typefind_type;
+}
+static void
+gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps)
+{
+  gchar *caps_str;
+  
+  g_assert (typefind->caps == NULL);
+  g_assert (caps != NULL);
+
+  caps_str = gst_caps_to_string (caps);
+  GST_INFO_OBJECT (typefind, "found caps %s", caps_str);
+  g_free (caps_str);
+  gst_caps_replace (&typefind->caps, caps);
+  if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) {
+    gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad");
+  }
+}
+static void
+gst_type_find_element_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstTypeFindElementClass *typefind_class;
+
+  gobject_class = G_OBJECT_CLASS (g_class);
+  gstelement_class = GST_ELEMENT_CLASS (g_class);
+  typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class);
+
+  parent_class = g_type_class_peek_parent (g_class);
+
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
+  gobject_class->dispose      =        GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
+
+  typefind_class->have_type = gst_type_find_element_have_type;
+
+  g_object_class_install_property (gobject_class, ARG_CAPS,
+         g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"),
+         GST_TYPE_CAPS, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE));
+
+  gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
+          gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
+          G_TYPE_UINT, GST_TYPE_CAPS);
+
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
+}
+static void
+gst_type_find_element_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance);
+  
+  /* sinkpad */
+  typefind->sink = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink");
+  gst_pad_set_chain_function (typefind->sink,
+                             gst_type_find_element_chain);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
+  /* srcpad */
+  typefind->src = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src");
+  gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
+  gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
+
+  typefind->caps = NULL;
+  typefind->min_probability = 1;
+  typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
+
+  typefind->store = gst_buffer_store_new ();
+
+  GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE);
+}
+static void
+gst_type_find_element_dispose (GObject *object)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+
+  if (typefind->store) {
+    g_object_unref (typefind->store);
+    typefind->store = NULL;
+  }
+}
+static void
+gst_type_find_element_set_property (GObject *object, guint prop_id, 
+                                   const GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_MINIMUM:
+      typefind->min_probability = g_value_get_uint (value);
+      g_object_notify (object, "minimum");
+      break;
+    case ARG_MAXIMUM:
+      typefind->max_probability = g_value_get_uint (value);
+      g_object_notify (object, "maximum");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static void
+gst_type_find_element_get_property (GObject *object, guint prop_id, 
+                                   GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_CAPS:
+      g_value_set_boxed (value, typefind->caps);
+      break;
+    case ARG_MINIMUM:
+      g_value_set_uint (value, typefind->min_probability);
+      break;
+    case ARG_MAXIMUM:
+      g_value_set_uint (value, typefind->max_probability);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static const GstEventMask *
+gst_type_find_element_src_event_mask (GstPad *pad)
+{
+  static const GstEventMask mask[] = {
+    { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
+    /* add more if you want, event masks suck and need to die anyway */
+    { 0, }
+  };
+  
+  return mask;
+}
+static gboolean        
+gst_type_find_element_src_event (GstPad *pad, GstEvent *event)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    gst_data_unref (GST_DATA (event));
+    return FALSE;
+  }
+  return gst_pad_event_default (pad, event);
+}
+typedef struct {
+  GstTypeFindFactory * factory;
+  gint                 probability;
+  GstCaps *            caps;
+  gint64               requested_offset;
+  guint                        requested_size;
+
+  GList *              buffers;
+  GstTypeFindElement * self;
+} TypeFindEntry;
+static void
+free_entry_buffers (TypeFindEntry *entry)
+{
+  g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL);
+  g_list_free (entry->buffers);
+  entry->buffers = NULL;
+}
+static void
+free_entry (TypeFindEntry *entry)
+{
+  free_entry_buffers (entry);
+  
+  if (entry->caps)
+    gst_caps_unref (entry->caps);
+  g_free (entry);
+}
+static void
+start_typefinding (GstTypeFindElement *typefind)
+{
+  g_assert (typefind->caps == NULL);
+  g_assert (typefind->possibilities == NULL);
+  
+  GST_DEBUG_OBJECT (typefind, "starting typefinding");
+  typefind->mode = MODE_TYPEFIND; 
+  typefind->stream_length_available = TRUE; 
+  typefind->stream_length = 0; 
+}
+static void
+stop_typefinding (GstTypeFindElement *typefind)
+{
+  /* stop all typefinding and set mode back to normal */
+  gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING;
+  
+  GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : "");
+  if (typefind->possibilities != NULL) {
+    /* this should only happen on PAUSED => READY or EOS */
+    GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities));
+    g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
+    g_list_free (typefind->possibilities);
+    typefind->possibilities = NULL;
+  }
+
+  typefind->mode = MODE_NORMAL;
+
+  if (push_cached_buffers) {
+    GstBuffer *buffer;
+    guint size = gst_buffer_store_get_size (typefind->store, 0);
+    if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) {
+      gst_pad_push (typefind->src, GST_DATA (buffer));
+    } else {
+      size = 0;
+    }
+    GST_LOG_OBJECT (typefind, "seeking back to current position %u", size);
+    if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), 
+                            gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) {
+      GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size);
+    }
+  }
+  gst_buffer_store_clear (typefind->store);
+}
+static guint64
+find_element_get_length (gpointer data)
+{
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  GstTypeFindElement *typefind = entry->self;
+  GstFormat format = GST_FORMAT_BYTES;
+  
+  if (!typefind->stream_length_available) {
+    GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", 
+           GST_PLUGIN_FEATURE_NAME (entry->factory));
+    return 0;
+  }
+  if (entry->self->stream_length == 0) {
+    typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, 
+           &format, &entry->self->stream_length);
+    if (format != GST_FORMAT_BYTES)
+      typefind->stream_length_available = FALSE;
+    if (!typefind->stream_length_available) {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory));
+      return 0;
+    } else {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
+    }
+  }
+  
+  return entry->self->stream_length;
+}
+static void
+gst_type_find_element_handle_event (GstPad *pad, GstEvent *event)
+{
+  TypeFindEntry *entry;
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* this should only happen when we got all available data */
+      entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL;
+      if (entry && entry->probability >= typefind->min_probability) {
+       GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", 
+               GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+       g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+      }
+      stop_typefinding (typefind);
+      gst_pad_event_default (pad, event);
+      break;
+    default:
+      gst_data_unref (GST_DATA (event));
+      break;
+    }
+  } else {
+    gst_pad_event_default (pad, event);
+  }
+}
+static guint8 *
+find_peek (gpointer data, gint64 offset, guint size)
+{
+  GstBuffer *buf;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
+  if (offset >= 0) {
+    buf = gst_buffer_store_get_buffer (entry->self->store, offset, size);
+  } else {
+    /* FIXME: can we do this easily without querying length? */
+    guint64 length = find_element_get_length (data);
+
+    if (length == 0) {
+      buf = NULL;
+    } else {
+      buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size);
+    }
+  }
+
+  if (buf) {
+    entry->buffers = g_list_prepend (entry->buffers, buf);
+    return GST_BUFFER_DATA (buf);
+  } else {
+    if (entry->requested_size == 0) {
+      GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", 
+             offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory));
+      entry->requested_offset = offset;
+      entry->requested_size = size;
+    }
+    return NULL;
+  }
+}
+static void
+find_suggest (gpointer data, guint probability, GstCaps *caps)
+{
+  gchar *str;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  str = gst_caps_to_string (caps);
+  GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str);
+  g_free (str);
+  if (((gint) probability) > entry->probability) {
+    entry->probability = probability;
+    gst_caps_replace (&entry->caps, caps);
+  }
+}
+static gint
+compare_type_find_entry (gconstpointer a, gconstpointer b)
+{
+  TypeFindEntry *one = (TypeFindEntry *) a;
+  TypeFindEntry *two = (TypeFindEntry *) b;
+
+  if (one->probability == two->probability) {
+    /* FIXME: can be improved by analyzing requests */
+    return 0;
+  } else {
+    return two->probability - one->probability;
+  }
+}
+static gint
+compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
+{
+  return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
+}
+static void
+gst_type_find_element_chain (GstPad *pad, GstData *data)
+{
+  GstTypeFindElement *typefind;
+  GList *entries;
+  TypeFindEntry *entry;
+  GList *walk;
+  GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length };
+
+  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+  if (GST_IS_EVENT (data)) {
+    gst_type_find_element_handle_event (pad, GST_EVENT (data));
+    return;
+  }
+  switch (typefind->mode) {
+    case MODE_NORMAL:
+      gst_pad_push (typefind->src, data);
+      return;
+    case MODE_TYPEFIND: {
+      gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data));
+      gst_data_unref (data);
+      if (typefind->possibilities == NULL) {
+       /* not yet started, get all typefinding functions into our "queue" */
+       GList *all_factories = gst_type_find_factory_get_list ();
+       GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", 
+                        g_list_length ((GList *) all_factories));
+       
+       all_factories = g_list_sort (all_factories, compare_type_find_factory);
+       walk = all_factories;
+       while (all_factories) {
+         entry = g_new0 (TypeFindEntry, 1);
+         
+         entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
+         entry->self = typefind;
+         entry->probability = 0;
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+         all_factories = g_list_next (all_factories);
+       }
+       g_list_free (all_factories);
+      }
+      /* call every typefind function once */
+      walk = entries = typefind->possibilities;
+      GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries));
+      typefind->possibilities = NULL;
+      while (walk) {
+       find.data = entry = (TypeFindEntry *) walk->data;
+       walk = g_list_next (walk);
+       entry->probability = 0;
+       entry->requested_offset = 0;
+       entry->requested_size = 0;
+       gst_type_find_factory_call_function (entry->factory, &find);
+       free_entry_buffers (entry);
+       if (entry->probability == 0 && entry->requested_size == 0) {
+         GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory));
+         free_entry (entry);
+       } else if (entry->probability >= typefind->max_probability) {
+         /* wooha, got caps */
+         GstCaps *found_caps = entry->caps;
+         guint probability = entry->probability;
+         
+         gst_caps_ref (found_caps);
+         GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability);
+         while (walk) {
+           free_entry ((TypeFindEntry *) walk->data);
+           walk = g_list_next (walk);
+         }
+         walk = typefind->possibilities;
+         while (walk) {
+           free_entry (walk->data);
+           walk = g_list_next (walk);
+         }
+         typefind->possibilities = NULL;
+         g_list_free (typefind->possibilities); 
+         g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps);
+         gst_caps_unref (found_caps);
+       } else {
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+       }
+      }
+      g_list_free (entries);
+      /* we may now already have caps or we might be left without functions to try */
+      if (typefind->caps) {
+       stop_typefinding (typefind);
+      } else if (typefind->possibilities == NULL) {
+       gst_element_error (GST_ELEMENT (typefind), "media type could not be detected");
+      } else {
+       /* set up typefind element for next iteration */
+       typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry);
+       
+       walk = typefind->possibilities;
+       while (walk) {
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size > 0) {
+           /* FIXME: need heuristic to find out if we should seek */
+           gint64 seek_offset;
+           GstEvent *event;
+
+           seek_offset = entry->requested_offset > 0 ? entry->requested_offset : 
+                         find_element_get_length (entry) + entry->requested_offset;
+           seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset);
+           event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset);
+           if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) {
+             /* done seeking */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             break;
+           } else if (entry->requested_offset < 0) {
+             /* impossible to seek */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             entry->requested_size = 0;
+             entry->requested_offset = 0;
+           }
+         }
+       }
+       /* throw out all entries that can't get more data */
+       walk = g_list_next (typefind->possibilities);
+       while (walk) {
+         GList *cur = walk;
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size == 0) {
+           GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory));
+           free_entry (entry);
+           typefind->possibilities = g_list_delete_link (typefind->possibilities, cur);
+         }
+       }
+       if (g_list_next (typefind->possibilities) == NULL) {
+         entry = (TypeFindEntry *) typefind->possibilities->data;
+         if (entry->probability > typefind->min_probability) {
+           GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+           free_entry (entry);
+           g_list_free (typefind->possibilities);
+           typefind->possibilities = NULL;
+           stop_typefinding (typefind);
+         }
+       }
+      }
+      break;
+    }
+    default:
+      g_assert_not_reached ();
+      return;
+  }
+}
+static GstElementStateReturn
+gst_type_find_element_change_state (GstElement *element)
+{
+  GstTypeFindElement *typefind;
+
+  typefind = GST_TYPE_FIND_ELEMENT (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_READY_TO_PAUSED:
+      start_typefinding (typefind);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      stop_typefinding (typefind);
+      gst_caps_replace (&typefind->caps, NULL);
+      break;
+    default:
+      break;
+  }
+  
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+}
diff --git a/gst/elements/gsttypefindelement.h b/gst/elements/gsttypefindelement.h
new file mode 100644 (file)
index 0000000..4b5e0cc
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: element that detects type of stream
+ *
+ * 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_TYPE_FIND_ELEMENT_H__
+#define __GST_TYPE_FIND_ELEMENT_H__
+
+#include <gst/gstinfo.h>
+#include <gst/gstelement.h>
+/* #include <gst/gstbufferstore.h> */
+#include "gstbufferstore.h"
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug);
+
+extern GstElementDetails gst_type_find_element_details;
+
+#define GST_TYPE_TYPE_FIND_ELEMENT             (gst_type_find_element_get_type ())
+#define GST_TYPE_FIND_ELEMENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement))
+#define GST_IS_TYPE_FIND_ELEMENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+
+typedef struct _GstTypeFindElement             GstTypeFindElement;
+typedef struct _GstTypeFindElementClass        GstTypeFindElementClass;
+
+struct _GstTypeFindElement {
+  GstElement           element;
+
+  GstPad *             sink;
+  GstPad *             src;
+
+  guint                        min_probability;
+  guint                        max_probability;
+  GstCaps *            caps;
+
+  guint                        mode;
+  GstBufferStore *     store;
+  guint64              stream_length;
+  gboolean             stream_length_available;
+  
+  GList *              possibilities;
+};
+
+struct _GstTypeFindElementClass {
+  GstElementClass      parent_class;
+
+  /* signals */
+  void                         (*have_type)    (GstTypeFindElement *element,
+                                        guint          probability,
+                                        GstCaps *      caps);
+};
+
+GType gst_type_find_element_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_TYPE_FIND_ELEMENT_H__ */
index 733c436d5b9343219145dae065fcf54afb9a7d01..f06b11c6ed9e0e6da5485eeb93a306d6cb932459 100644 (file)
--- a/gst/gst.c
+++ b/gst/gst.c
@@ -27,9 +27,6 @@
 
 #include "gst.h"
 #include "gstqueue.h"
-#ifndef GST_DISABLE_TYPEFIND
-#include "gsttypefind.h"
-#endif /* GST_DISABLE_TYPEFIND */
 #ifndef GST_DISABLE_REGISTRY
 #include "registries/gstxmlregistry.h"
 #endif /* GST_DISABLE_REGISTRY */
@@ -461,10 +458,6 @@ gst_register_core_elements (GModule *module, GstPlugin *plugin)
   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
   factory = gst_element_factory_new ("queue", gst_queue_get_type (), &gst_queue_details);
   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-#ifndef GST_DISABLE_TYPEFIND
-  factory = gst_element_factory_new ("typefind", gst_type_find_get_type (), &gst_type_find_details);
-  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-#endif /* GST_DISABLE_TYPEFIND */
 
   return TRUE;
 }
@@ -510,8 +503,8 @@ init_post (void)
   gst_ghost_pad_get_type ();
   gst_element_factory_get_type ();
   gst_element_get_type ();
-  gst_type_factory_get_type ();
   gst_scheduler_factory_get_type ();
+  gst_type_find_factory_get_type ();
   gst_bin_get_type ();
 #ifndef GST_DISABLE_AUTOPLUG
   gst_autoplug_factory_get_type ();
index 476b693b2fe745c57ff8c043be77481af5db3cd2..e7fd46d17a37f27a5dcf30713f00d57ea579564c 100644 (file)
--- a/gst/gst.h
+++ b/gst/gst.h
@@ -41,7 +41,7 @@
 #include <gst/gstindex.h>
 #include <gst/gstpipeline.h>
 #include <gst/gstthread.h>
-#include <gst/gsttype.h>
+#include <gst/gsttypefind.h>
 #include <gst/gstautoplug.h>
 #include <gst/gstcaps.h>
 #include <gst/gstprops.h>
index 1c20374ab6d851a874c6e16cfcd2c3fa37d23691..caab5b03811153d2f2f2289f7cfaa30941b52137 100644 (file)
@@ -78,7 +78,6 @@ extern GstDebugCategory *GST_CAT_PLUGIN_LOADING;
 extern GstDebugCategory *GST_CAT_PLUGIN_INFO;
 extern GstDebugCategory *GST_CAT_PROPERTIES;
 extern GstDebugCategory *GST_CAT_THREAD;
-extern GstDebugCategory *GST_CAT_TYPES;
 extern GstDebugCategory *GST_CAT_XML;
 extern GstDebugCategory *GST_CAT_NEGOTIATION;
 extern GstDebugCategory *GST_CAT_REFCOUNTING;
index c7a0f16e1dc2959dce3324729dd09cb517c4ff17..6df3cf8cb8aacecd3c8236551164560c7c341916 100644 (file)
@@ -301,8 +301,6 @@ gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size)
   }
   /* ref the real parent */
   gst_data_ref (GST_DATA (parent));
-  /* make sure nobody overwrites data in the parent */
-  GST_DATA_FLAG_SET (parent, GST_DATA_READONLY);
 
   /* create the new buffer */
   buffer = gst_mem_chunk_alloc (chunk);
@@ -337,9 +335,12 @@ gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size)
     GST_BUFFER_TIMESTAMP (buffer)    = GST_CLOCK_TIME_NONE;
     GST_BUFFER_OFFSET (buffer)       = GST_BUFFER_OFFSET_NONE;
   }
-
   GST_BUFFER_DURATION (buffer)     = GST_CLOCK_TIME_NONE;
 
+  /* make sure nobody overwrites data as it would overwrite in the parent.
+   * data in parent cannot be overwritten because we hold a ref */
+  GST_DATA_FLAG_SET (parent, GST_DATA_READONLY);
+
   return buffer;
 }
 
diff --git a/gst/gstbytestream.c b/gst/gstbytestream.c
deleted file mode 100644 (file)
index 849838d..0000000
+++ /dev/null
@@ -1,746 +0,0 @@
-/* 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_BUFFER (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));
-  }
-}
diff --git a/gst/gstbytestream.h b/gst/gstbytestream.h
deleted file mode 100644 (file)
index 82ac0ec..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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;
-
-  GST_OBJECT_PADDING
-};
-
-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__ */
index a6d70381bfc1af4dbb63c79d0bd2a147bed27882..7d7b4560031df94b960fc303a81bcf1d9550d7c0 100644 (file)
@@ -23,7 +23,6 @@
 #include "gst_private.h"
 
 #include "gstcaps.h"
-#include "gsttype.h"
 #include "gstmemchunk.h"
 #include "gstinfo.h"
 
@@ -192,30 +191,6 @@ gst_caps_get_type (void)
 {
   return _gst_caps_type;
 }
-
-static guint16
-get_type_for_mime (const gchar *mime)
-{
-  guint16 typeid;
-
-  typeid = gst_type_find_by_mime (mime);
-  if (typeid == 0) {
-     GstTypeDefinition definition;
-     GstTypeFactory *factory;
-
-     definition.name = "capstype";
-     definition.mime = g_strdup (mime);
-     definition.exts = NULL;
-     definition.typefindfunc = NULL;
-
-     factory = gst_type_factory_new (&definition);
-     typeid = gst_type_register (factory);
-
-     g_free (definition.mime);
-  }
-  return typeid;
-}
-
 /**
  * gst_caps_new:
  * @name: the name of this capability
@@ -231,9 +206,8 @@ gst_caps_new (const gchar *name, const gchar *mime, GstProps *props)
 {
   g_return_val_if_fail (mime != NULL, NULL);
 
-  return gst_caps_new_id (name, get_type_for_mime (mime), props);
+  return gst_caps_new_id (name, g_quark_from_string (mime), props);
 }
-
 /**
  * gst_caps_new_id:
  * @name: the name of this capability
@@ -245,7 +219,7 @@ gst_caps_new (const gchar *name, const gchar *mime, GstProps *props)
  * Returns: a new capability
  */
 GstCaps*
-gst_caps_new_id (const gchar *name, const guint16 id, GstProps *props)
+gst_caps_new_id (const gchar *name, const GQuark id, GstProps *props)
 {
   GstCaps *caps;
 
@@ -584,7 +558,6 @@ gst_caps_set_name (GstCaps *caps, const gchar *name)
   g_free (caps->name);
   caps->name = g_strdup (name);
 }
-
 /**
  * gst_caps_get_mime:
  * @caps: the caps to get the mime type from
@@ -596,18 +569,10 @@ gst_caps_set_name (GstCaps *caps, const gchar *name)
 const gchar*
 gst_caps_get_mime (GstCaps *caps)
 {
-  GstType *type;
-
   g_return_val_if_fail (caps != NULL, NULL);
 
-  type = gst_type_find_by_id (caps->id);
-
-  if (type)
-    return type->mime;
-  else
-    return "unknown/unknown";
+  return g_quark_to_string (caps->id);
 }
-
 /**
  * gst_caps_set_mime:
  * @caps: the caps to set the mime type to
@@ -621,40 +586,8 @@ gst_caps_set_mime (GstCaps *caps, const gchar *mime)
   g_return_if_fail (caps != NULL);
   g_return_if_fail (mime != NULL);
 
-  caps->id = get_type_for_mime (mime);
-}
-
-/**
- * gst_caps_get_type_id:
- * @caps: the caps to get the type id from
- *
- * Get the type id of the caps.
- *
- * Returns: the type id of the caps
- */
-guint16
-gst_caps_get_type_id (GstCaps *caps)
-{
-  g_return_val_if_fail (caps != NULL, 0);
-
-  return caps->id;
-}
-
-/**
- * gst_caps_set_type_id:
- * @caps: the caps to set the type id to
- * @type_id: the type id to set
- *
- * Set the type id of the caps.
- */
-void
-gst_caps_set_type_id (GstCaps *caps, guint16 type_id)
-{
-  g_return_if_fail (caps != NULL);
-
-  caps->id = type_id;
+  caps->id = g_quark_from_string (mime);
 }
-
 /**
  * gst_caps_set_props:
  * @caps: the caps to attach the properties to
@@ -880,8 +813,8 @@ gst_caps_check_compatibility_func (GstCaps *fromcaps, GstCaps *tocaps)
 {
   if (fromcaps->id != tocaps->id) {
     GST_CAT_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)",
-              gst_type_find_by_id (fromcaps->id)->mime
-              gst_type_find_by_id (tocaps->id)->mime);
+              gst_caps_get_mime (fromcaps)
+              gst_caps_get_mime (tocaps));
     return FALSE;
   }
 
@@ -963,8 +896,8 @@ gst_caps_intersect_func (GstCaps *caps1, GstCaps *caps2)
 
   if (caps1->id != caps2->id) {
     GST_CAT_DEBUG (GST_CAT_CAPS, "mime types differ (%s to %s)",
-              gst_type_find_by_id (caps1->id)->mime
-              gst_type_find_by_id (caps2->id)->mime);
+              gst_caps_get_mime (caps1)
+              gst_caps_get_mime (caps2));
     return NULL;
   }
 
@@ -1127,7 +1060,7 @@ gst_caps_save_thyself (GstCaps *caps, xmlNodePtr parent)
     subtree = xmlNewChild (parent, NULL, "capscomp", NULL);
 
     xmlNewChild (subtree, NULL, "name", caps->name);
-    xmlNewChild (subtree, NULL, "type", gst_type_find_by_id (caps->id)->mime);
+    xmlNewChild (subtree, NULL, "type", gst_caps_get_mime (caps));
     if (caps->properties) {
       subsubtree = xmlNewChild (subtree, NULL, "properties", NULL);
 
@@ -1175,7 +1108,7 @@ gst_caps_load_thyself (xmlNodePtr parent)
         }
         if (!strcmp (subfield->name, "type")) {
           content = xmlNodeGetContent (subfield);
-          caps->id = get_type_for_mime (content);
+          caps->id = g_quark_from_string (content);
           g_free (content);
         }
         else if (!strcmp (subfield->name, "properties")) {
index d781247ef87fe7a297ed5fe9816d7051ec032194..fa2c24959536d7db7dcfcdd53f09a10eabcf9ab6 100644 (file)
@@ -63,7 +63,7 @@ typedef enum {
 struct _GstCaps {
   /* --- public --- */
   gchar        *name;                  /* the name of this caps */
-  guint16       id;                    /* type id (major type) representing 
+  GQuark        id;                    /* type id (major type) representing 
                                           the mime type, it's stored as a GQuark 
                                           for speed/space reasons */
 
@@ -126,7 +126,7 @@ void                _gst_caps_initialize                    (void);
 /* creating new caps */
 GType          gst_caps_get_type                       (void);
 GstCaps*       gst_caps_new                            (const gchar *name, const gchar *mime, GstProps *props);
-GstCaps*       gst_caps_new_id                         (const gchar *name, const guint16 id, GstProps *props);
+GstCaps*       gst_caps_new_id                         (const gchar *name, const GQuark id, GstProps *props);
 GstCaps*       gst_caps_get_any                        (void);
 /* replace pointer to caps, doing proper refcounting */
 void           gst_caps_replace                        (GstCaps **oldcaps, GstCaps *newcaps);
@@ -151,9 +151,6 @@ void                gst_caps_set_name                       (GstCaps *caps, const gchar *name);
 const gchar*   gst_caps_get_mime                       (GstCaps *caps);
 void           gst_caps_set_mime                       (GstCaps *caps, const gchar *mime);
 
-guint16                gst_caps_get_type_id                    (GstCaps *caps);
-void           gst_caps_set_type_id                    (GstCaps *caps, guint16 type_id);
-
 GstCaps*       gst_caps_set_props                      (GstCaps *caps, GstProps *props);
 GstProps*      gst_caps_get_props                      (GstCaps *caps);
 
index 2792821695c14354293acc09b8754dd6be1ee414..24c6e0a4276d99b1e82461a1e32d42aee7a61f7a 100644 (file)
@@ -10,7 +10,6 @@
 #define GST_DISABLE_LOADSAVE_REGISTRY 1
 #define GST_DISABLE_GST_DEBUG 1
 #define GST_DISABLE_LOADSAVE 1
-#define GST_DISABLE_TYPEFIND 1
 #define GST_DISABLE_AUTOPLUG 1
 #define GST_DISABLE_PARSE 1
 #define GST_DISABLE_TRACE 1
@@ -31,9 +30,6 @@
 /* DOES NOT WORK */
 @GST_DISABLE_LOADSAVE_DEFINE@
 
-/* DOES NOT WORK */
-@GST_DISABLE_TYPEFIND_DEFINE@
-
 /* DOES NOT WORK */
 @GST_DISABLE_AUTOPLUG_DEFINE@
 
index bc215b04fbc6d1cf1ffb6ad617f8efeff7fa35a1..6795cfe10e12fd8f8a7b7cedae7329bb56cd833e 100644 (file)
@@ -2545,7 +2545,7 @@ gst_element_dispose (GObject *object)
   GList *pads;
   GstPad *pad;
 
-  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
+  GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
 
   gst_element_set_state (element, GST_STATE_NULL);
 
index 26b543f8505e9c89a6bdbf77b4eae2603c921a1f..0340c85f760c305160e6beb37640d0d830698d6c 100644 (file)
@@ -9,4 +9,6 @@ VOID:OBJECT,POINTER
 VOID:OBJECT,STRING
 VOID:INT,INT
 VOID:INT64
+VOID:UINT,BOXED
 BOOLEAN:VOID
+BOOLEAN:POINTER
index 4387e307fa05374839efeab2c68a02d9749ba224..e0f1a7c78c90c0e89613b60418db763bdeb21c79 100644 (file)
@@ -25,7 +25,6 @@
 #include "gstpad.h"
 #include "gstutils.h"
 #include "gstelement.h"
-#include "gsttype.h"
 #include "gstbin.h"
 #include "gstscheduler.h"
 #include "gstevent.h"
@@ -2311,14 +2310,19 @@ gst_pad_push (GstPad *pad, GstData *data)
 {
   GstRealPad *peer;
 
-  g_assert (GST_IS_PAD (pad));
-  GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, pad, "pushing");
-
+  g_return_if_fail (GST_IS_PAD (pad));
   g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC);
 
   if (!gst_probe_dispatcher_dispatch (&(GST_REAL_PAD (pad)->probedisp), &data))
     return;
+  
+  if (!GST_PAD_IS_LINKED (pad)) {
+    GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, pad, "not pushing data %p as pad is unconnected", data);
+    gst_data_unref (data);
+    return;
+  }      
 
+  GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, pad, "pushing");
   peer = GST_RPAD_PEER (pad);
 
   if (!peer) {
index abc047302087756da67f452600d2d717e00cfd40..494075cd26fdfa506d11bf53db441e711de63c3e 100644 (file)
@@ -27,6 +27,8 @@
 #include "gstregistry.h"
 #include "gstinfo.h"
 
+#include <string.h>
+
 static void            gst_plugin_feature_class_init           (GstPluginFeatureClass *klass);
 static void            gst_plugin_feature_init                 (GstPluginFeature *feature);
 
@@ -136,7 +138,6 @@ gst_plugin_feature_type_name_filter (GstPluginFeature *feature,
   return ((data->type == 0    || data->type == G_OBJECT_TYPE (feature)) &&
           (data->name == NULL || !strcmp (data->name, GST_PLUGIN_FEATURE_NAME (feature))));
 }
-
 /**
  * gst_plugin_feature_set_rank:
  * @feature: feature to rank
@@ -153,4 +154,25 @@ gst_plugin_feature_set_rank (GstPluginFeature *feature, guint16 rank)
 
   feature->rank = rank;
 }
+/**
+ * gst_plugin_feature_set_rank:
+ * @feature: a feature
+ * @name: the name to set
+ *
+ * Sets the name of a plugin feature. The name uniquely identifies a feature
+ * within all features of the same type. Renaming a plugin feature is not 
+ * allowed.
+ */
+void
+gst_plugin_feature_set_name (GstPluginFeature *feature, const gchar *name)
+{
+  g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature));
+  g_return_if_fail (name != NULL);
+
+  if (feature->name) {
+    g_return_if_fail (strcmp (feature->name, name) == 0);
+  } else {
+    feature->name = g_strdup (name);
+  }
+}
 
index 23f614a608081ab0be37958ce7bdd0b1028e0dea..a543f669af65658d2db30931edeccbec3b57c5c1 100644 (file)
@@ -80,6 +80,7 @@ gboolean      gst_plugin_feature_type_name_filter     (GstPluginFeature *feature,
                                                         GstTypeNameData *data);
 
 void           gst_plugin_feature_set_rank             (GstPluginFeature *feature, guint16 rank);
+void           gst_plugin_feature_set_name             (GstPluginFeature *feature, const gchar *name);
 
 G_END_DECLS
 
index 19a2f100440fad2b2f18f97e470a4ef665bf5f26..77677d362add2a5db44484111764489ea4d06afa 100644 (file)
@@ -1,8 +1,7 @@
 /* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- *                    2000 Wim Taymans <wtay@chello.be>
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
  *
- * gsttypefind.c: 
+ * gsttypefind.h: typefinding subsystem
  *
  * 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.
  */
 
-
-#include "gst_private.h"
-#include "gsttype.h"
 #include "gstinfo.h"
 #include "gsttypefind.h"
+#include "gstregistrypool.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_type_find_debug);
+#define GST_CAT_DEFAULT gst_type_find_debug
+
+static void            gst_type_find_factory_class_init        (gpointer               g_class,
+                                                                gpointer               class_data);
+static void            gst_type_find_factory_init              (GTypeInstance *        instance,
+                                                                gpointer               g_class);
+static void            gst_type_find_factory_dispose           (GObject *              object);
+
+static void            gst_type_find_factory_unload_thyself    (GstPluginFeature *     feature);
+
+static void            gst_type_find_load_plugin               (GstTypeFind *          find,
+                                                                gpointer               data);
 
-GstElementDetails gst_type_find_details = {
-  "TypeFind",
-  "Generic",
-  "LGPL",
-  "Finds the media type of a stream",
-  VERSION,
-  "Erik Walthinsen <omega@cse.ogi.edu>,"
-  "Wim Taymans <wim.taymans@chello.be>",
-  "(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 {
-  HAVE_TYPE,
-  LAST_SIGNAL
-};
-
-enum {
-  ARG_0,
-  ARG_CAPS,
-};
-
-
-static void    gst_type_find_class_init        (GstTypeFindClass *klass);
-static void    gst_type_find_init              (GstTypeFind *typefind);
-
-static void    gst_type_find_set_property      (GObject *object, guint prop_id,
-                                                const GValue *value, 
-                                                GParamSpec *pspec);
-static void    gst_type_find_get_property      (GObject *object, guint prop_id,
-                                                GValue *value, 
-                                                GParamSpec *pspec);
-
-static void    gst_type_find_loopfunc          (GstElement *element);
-static GstElementStateReturn
-               gst_type_find_change_state      (GstElement *element);
-
-static GstElementClass *parent_class = NULL;
-static guint gst_type_find_signals[LAST_SIGNAL] = { 0 };
+static GstPluginFeatureClass *parent_class = NULL;
 
 GType
-gst_type_find_get_type (void)
+gst_type_find_factory_get_type (void)
 {
   static GType typefind_type = 0;
-
+    
   if (!typefind_type) {
     static const GTypeInfo typefind_info = {
-      sizeof(GstTypeFindClass),
+      sizeof (GstTypeFindFactoryClass),
       NULL,
       NULL,
-      (GClassInitFunc)gst_type_find_class_init,
+      gst_type_find_factory_class_init,
       NULL,
       NULL,
-      sizeof(GstTypeFind),
+      sizeof (GstTypeFindFactory),
       0,
-      (GInstanceInitFunc)gst_type_find_init,
+      gst_type_find_factory_init,
       NULL
     };
-    typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
-                                           "GstTypeFind",
+    typefind_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE,
+                                           "GstTypeFindFactory",
                                            &typefind_info, 0);
+    GST_DEBUG_CATEGORY_INIT (gst_type_find_debug, "GST_TYPEFIND", 
+                            GST_DEBUG_FG_GREEN, "typefinding subsystem");
   }
+
   return typefind_type;
 }
-
 static void
-gst_type_find_class_init (GstTypeFindClass *klass)
+gst_type_find_factory_class_init (gpointer g_class, gpointer class_data)
 {
-  GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
-
-  gobject_class = (GObjectClass*)klass;
-  gstelement_class = (GstElementClass*)klass;
-
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
-
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CAPS,
-    g_param_spec_pointer ("caps", "Caps", "Found capabilities", G_PARAM_READABLE));
-
-  gst_type_find_signals[HAVE_TYPE] =
-      g_signal_new ("have_type", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GstTypeFindClass, have_type), NULL, NULL,
-                     g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
-                     G_TYPE_POINTER);
-
-  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_set_property);
-  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_get_property);
-
-  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_change_state);
+  GstPluginFeatureClass *gstpluginfeature_class = GST_PLUGIN_FEATURE_CLASS (g_class);
+  GObjectClass *object_class = G_OBJECT_CLASS (g_class);
+    
+  parent_class = g_type_class_peek_parent (g_class);
+  
+  object_class->dispose = gst_type_find_factory_dispose;
+  
+  gstpluginfeature_class->unload_thyself = GST_DEBUG_FUNCPTR (gst_type_find_factory_unload_thyself);
 }
-
 static void
-gst_type_find_init (GstTypeFind *typefind)
+gst_type_find_factory_init (GTypeInstance *instance, gpointer g_class)
 {
-  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_element_set_loop_function (GST_ELEMENT (typefind),
-                                gst_type_find_loopfunc);
+  GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (instance);
 
-  typefind->caps = NULL;
+  factory->user_data = factory;
+  factory->function = gst_type_find_load_plugin;
 }
-
 static void
-gst_type_find_set_property (GObject *object, guint prop_id, 
-                           const GValue *value, GParamSpec *pspec)
+gst_type_find_factory_dispose (GObject *object)
 {
-  GstTypeFind *typefind;
-
-  g_return_if_fail (GST_IS_TYPE_FIND (object));
-
-  typefind = GST_TYPE_FIND (object);
+  GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (object);
 
-  switch (prop_id) {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+  if (factory->caps) {
+    gst_caps_unref (factory->caps);
+    factory->caps = NULL;
+  }
+  if (factory->extensions) {
+    g_strfreev (factory->extensions);
+    factory->extensions = NULL;
   }
 }
+static void
+gst_type_find_factory_unload_thyself (GstPluginFeature *feature)
+{
+  GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
 
+  factory->function = gst_type_find_load_plugin;
+  factory->user_data = factory;
+}
 static void
-gst_type_find_get_property (GObject *object, guint prop_id, 
-                           GValue *value, GParamSpec *pspec)
+gst_type_find_load_plugin (GstTypeFind *find, gpointer data)
 {
-  GstTypeFind *typefind;
+  GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (data);
 
-  g_return_if_fail (GST_IS_TYPE_FIND (object));
+  GST_DEBUG_OBJECT (factory, "need to load typefind function %s",  GST_PLUGIN_FEATURE_NAME (factory));
+  
+  if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
+    if (factory->function == gst_type_find_load_plugin) {
+      /* looks like we didn't get a real typefind function */
+      g_warning ("could not load valid typefind function for feature '%s'\n", GST_PLUGIN_FEATURE_NAME (factory));
+    } else {
+      g_assert (factory->function);
+      gst_type_find_factory_call_function (factory, find);
+    }
+  }
+}
+/**
+ * gst_type_find_factory_get_list:
+ *
+ * Gets the list of all registered typefind factories. You must free the
+ * list using g_list_free.
+ * 
+ * Returns: the list of all registered typefind factories
+ */
+GList *
+gst_type_find_factory_get_list (void)
+{
+  return gst_registry_pool_feature_list (GST_TYPE_TYPE_FIND_FACTORY);
+}
+/**
+ * gst_type_find_factory_get_caps:
+ * @factory: a factory
+ * 
+ * Gets the caps associated with a typefind factory.
+ *
+ * Returns: the #GstCaps associated with this factory
+ */
+GstCaps *
+gst_type_find_factory_get_caps (const GstTypeFindFactory *factory)
+{
+  g_return_val_if_fail (GST_IS_TYPE_FIND_FACTORY (factory), NULL);
 
-  typefind = GST_TYPE_FIND (object);
+  return factory->caps;
+}
+/**
+ * gst_type_find_factory_get_extensions:
+ * @factory: a factory
+ * 
+ * Gets the extensions associated with a typefind factory. The returned
+ * array should not be changed. If you need to change stuff in it, you should
+ * copy it using g_stdupv().
+ *
+ * Returns: a NULL-terminated array of extensions associated with this factory
+ */
+gchar **
+gst_type_find_factory_get_extensions (const GstTypeFindFactory *factory)
+{
+  g_return_val_if_fail (GST_IS_TYPE_FIND_FACTORY (factory), NULL);
 
-  switch (prop_id) {
-    case ARG_CAPS:
-      g_value_set_pointer (value, typefind->caps);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
+  return factory->extensions;
 }
+/**
+ * gst_type_find_factory_call_function:
+ * @factory: a factory
+ * @find: a properly setup #GstTypeFind entry. The get_data and suggest_type
+ *        members must be set.
+ * 
+ * Calls the typefinding function associated with this factory.
+ */
+void
+gst_type_find_factory_call_function (const GstTypeFindFactory *factory, GstTypeFind *find)
+{
+  g_return_if_fail (GST_IS_TYPE_FIND_FACTORY (factory));
+  g_return_if_fail (find != NULL);
+  g_return_if_fail (find->peek != NULL);
+  g_return_if_fail (find->suggest != NULL);
 
-static void
-gst_type_find_loopfunc (GstElement *element)
+  /* should never happen */
+  g_assert (factory->function != NULL);
+
+  factory->function (find, factory->user_data);
+}
+/**
+ * gst_type_find_factory_register:
+ * @plugin: the GstPlugin to register with
+ * @name: the name for registering
+ * @rank: rank (or importance) of this typefind function
+ * @func: the function to use for typefinding
+ * @extensions: optional extensions that could belong to this type
+ * @possible_caps: optionally the caps that could be returned when typefinding succeeds
+ * @data: optional user data. This user data must be available until the plugin 
+ *       is unloaded.
+ *
+ * Registers a new typefind function to be used for typefinding. After 
+ * registering this function will be available for typefinding.
+ * This function is typically called during an element's plugin initialization.
+ *
+ * Returns: TRUE on success, FALSE otherwise
+ */
+void
+gst_type_find_factory_register (GstPlugin *plugin, const gchar *name, guint rank,
+                               GstTypeFindFunction func, gchar **extensions, GstCaps *possible_caps,
+                               gpointer data)
 {
-  GstTypeFind *typefind;
-  const GList *type_list;
-  GstType *type;
-
-  typefind = GST_TYPE_FIND (element);
-
-  GST_DEBUG ("Started typefinding loop in '%s'",
-            GST_OBJECT_NAME (typefind));
-
-  type_list = gst_type_get_list ();
-
-  while (type_list) {
-    GSList *factories;
-    type = (GstType *) type_list->data;
-
-    factories = type->factories;
-
-    while (factories) {
-      GstTypeFactory *factory = GST_TYPE_FACTORY (factories->data);
-      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 (typefind->bs, factory))) {
-        GST_CAT_DEBUG (GST_CAT_TYPES, "found type: %d \"%s\" \"%s\"", 
-                  caps->id, type->mime, gst_caps_get_name (caps));
-       gst_caps_replace (&typefind->caps, caps);
-
-       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));
-        return;
-      }
-      factories = g_slist_next (factories);
-    }
-    type_list = g_list_next (type_list);
+  GstTypeFindFactory *factory;
+  
+  g_return_if_fail (plugin != NULL);
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (func != NULL);
+
+  GST_INFO ("registering typefind function for %s", name);
+  factory = GST_TYPE_FIND_FACTORY (gst_registry_pool_find_feature (name, GST_TYPE_TYPE_FIND_FACTORY));
+  if (!factory) {
+    factory = g_object_new (GST_TYPE_TYPE_FIND_FACTORY, NULL);
+    GST_DEBUG_OBJECT (factory, "using new typefind factory for %s", name);
+    g_assert (GST_IS_TYPE_FIND_FACTORY (factory));
+    gst_plugin_feature_set_name (GST_PLUGIN_FEATURE (factory), name);
+  } else {
+    GST_DEBUG_OBJECT (factory, "using old typefind factory for %s", name);
   }
 
-  /* if we get here, nothing worked... :'(. */
-  gst_element_error (GST_ELEMENT (typefind), 
-                    "media type could not be detected");
+  gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
+  if (factory->extensions)
+    g_strfreev (factory->extensions);
+
+  factory->extensions = g_strdupv (extensions);
+  gst_caps_replace (&factory->caps, possible_caps);
+  factory->function = func;
+  factory->user_data = data;
+
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
 }
 
-static GstElementStateReturn
-gst_type_find_change_state (GstElement *element)
+/*** typefind function interface **********************************************/
+
+/**
+ * gst_type_find_peek:
+ * @find: the find object the function was called with
+ * @offset: the offset
+ * @size: the number of bytes to return
+ *
+ * Returns size bytes of the stream to identify beginning at offset. If offset 
+ * is a positive number, the offset is relative to the beginning of the stream,
+ * if offset is a negative number the offset is relative to the end of the 
+ * stream. The returned memory is valid until the typefinding function returns
+ * and must not be freed.
+ * If NULL is returned, that data is not available.
+ *
+ * Returns: the requested data or NULL if that data is not available.
+ */
+guint8 *
+gst_type_find_peek (GstTypeFind *find, gint64 offset, guint size)
 {
-  GstTypeFind *typefind;
-  GstElementStateReturn ret;
-
-  typefind = GST_TYPE_FIND (element);
-
-  switch (GST_STATE_TRANSITION (element)) {
-    case GST_STATE_READY_TO_PAUSED:
-      typefind->bs = gst_bytestream_new (typefind->sinkpad);
-      break;
-    case GST_STATE_PAUSED_TO_READY:
-      gst_bytestream_destroy (typefind->bs);
-      gst_caps_replace (&typefind->caps, NULL);
-      break;
-    default:
-      break;
-  }
-  
-  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
-  
-  return ret;
+  g_return_val_if_fail (find->peek != NULL, NULL);
+
+  return find->peek (find->data, offset, size);
 }
+/**
+ * gst_type_find_suggest:
+ * @find: the find object the function was called with
+ * @probability: the probability in percent that the suggestion is right
+ * @caps: the fixed caps to suggest
+ *
+ * If a typefind function calls this function it suggests the caps with the
+ * given probability. A typefind function may supply different suggestions
+ * in one call.
+ * It is up to the caller of the typefind function to interpret these values.
+ */
+void
+gst_type_find_suggest (GstTypeFind *find, guint probability, GstCaps *caps)
+{
+  g_return_if_fail (find->suggest != NULL);
+  g_return_if_fail (probability <= 100);
+  g_return_if_fail (caps != NULL);
+  g_return_if_fail (GST_CAPS_IS_FIXED (caps));
+
+  gst_caps_ref (caps);
+  gst_caps_sink (caps);
+  find->suggest (find->data, probability, caps);
+  gst_caps_unref (caps);
+}
+/**
+ * gst_type_find_get_length:
+ * @find: the find object the function was called with
+ *
+ * Get the length of the data stream.
+ * 
+ * Returns: the length of the data stream or 0 if it is not available.
+ */
+guint64
+gst_type_find_get_length (GstTypeFind *find)
+{
+  if (find->get_length == NULL)
+    return 0;
+
+  return find->get_length(find->data);
+}
+
index 0085eb063073d9a423ef22d3ae64bdbf2c6bbf4a..e80dc2d3e3bc7870af5ea095c4f50219fc5a11b4 100644 (file)
@@ -1,8 +1,7 @@
 /* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- *                    2000 Wim Taymans <wtay@chello.be>
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
  *
- * gsttypefind.h: 
+ * gsttypefind.h: typefinding subsystem
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #ifndef __GST_TYPE_FIND_H__
 #define __GST_TYPE_FIND_H__
 
-#ifndef GST_DISABLE_TYPE_FIND
-
-#include <gst/gstelement.h>
-#include <gst/gstbytestream.h>
+#include <gst/gstbuffer.h>
+#include <gst/gstcaps.h>
+#include <gst/gstplugin.h>
+#include <gst/gstpluginfeature.h>
+#include <gst/gsttypes.h>
 
 G_BEGIN_DECLS
 
-extern GstElementDetails gst_type_find_details;
+#define GST_TYPE_TYPE_FIND_FACTORY                 (gst_type_find_factory_get_type())
+#define GST_TYPE_FIND_FACTORY(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactory))
+#define GST_IS_TYPE_FIND_FACTORY(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_FACTORY))
+#define GST_TYPE_FIND_FACTORY_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactoryClass))
+#define GST_IS_TYPE_FIND_FACTORY_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_FACTORY))
+#define GST_TYPE_FIND_FACTORY_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactoryClass))
 
-#define GST_TYPE_TYPE_FIND             (gst_type_find_get_type ())
-#define GST_TYPE_FIND(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND, GstTypeFind))
-#define GST_IS_TYPE_FIND(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND))
-#define GST_TYPE_FIND_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND, GstTypeFindClass))
-#define GST_IS_TYPE_FIND_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND))
-#define GST_TYPE_FIND_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND, GstTypeFindClass))
+typedef struct _GstTypeFind GstTypeFind;
+typedef struct _GstTypeFindFactory GstTypeFindFactory;
+typedef struct _GstTypeFindFactoryClass GstTypeFindFactoryClass;
 
-typedef struct _GstTypeFind            GstTypeFind;
-typedef struct _GstTypeFindClass       GstTypeFindClass;
+typedef void (* GstTypeFindFunction) (GstTypeFind *find, gpointer data);
 
-struct _GstTypeFind {
-  GstElement    element;
+enum {
+  GST_TYPE_FIND_MINIMUM = 1,
+  GST_TYPE_FIND_POSSIBLE = 50,
+  GST_TYPE_FIND_LIKELY = 80,
+  GST_TYPE_FIND_NEARLY_CERTAIN = 99,
+  GST_TYPE_FIND_MAXIMUM = 100,
+} GstTypeFindProbability;
 
-  GstPad       *sinkpad;
-  GstByteStream *bs;
+struct _GstTypeFind {
+  /* private to the caller of the typefind function */
+  guint8 *             (* peek)                        (gpointer               data,
+                                                        gint64                 offset,
+                                                        guint                  size);
+  void                 (* suggest)                     (gpointer               data,
+                                                        guint                  probability,
+                                                        GstCaps *              caps);
+  
+  gpointer                     data;
+  
+  /* optional */
+  guint64              (* get_length)                  (gpointer               data);
+
+  /* <private> */
+  GST_STRUCT_PADDING
+};
 
-  GstCaps      *caps;
+struct _GstTypeFindFactory {
+  GstPluginFeature             feature;
+  /* <private> */
 
+  GstTypeFindFunction          function;
+  gchar **                     extensions;
+  GstCaps *                    caps; /* FIXME: not yet saved in registry */
+  
+  gpointer                     user_data;
+    
   GST_OBJECT_PADDING
 };
+                                                                                                                                                                         
+struct _GstTypeFindFactoryClass {
+  GstPluginFeatureClass                parent;
+  /* <private> */
+    
+  GST_CLASS_PADDING
+};
 
-struct _GstTypeFindClass {
-  GstElementClass      parent_class;
+/* typefind function interface */
+guint8 *       gst_type_find_peek                      (GstTypeFind *          find,
+                                                        gint64                 offset,
+                                                        guint                  size);
+void           gst_type_find_suggest                   (GstTypeFind *          find,
+                                                        guint                  probability,
+                                                        GstCaps *              caps);
+guint64                gst_type_find_get_length                (GstTypeFind *          find);
 
-  /* signals */
-  void                         (*have_type)    (GstElement *element,
-                                        GstCaps    *caps);
+/* registration interface */
+void           gst_type_find_factory_register          (GstPlugin *            plugin,
+                                                        const gchar *          name, 
+                                                        guint                  rank,
+                                                        GstTypeFindFunction    func,
+                                                        gchar **               extensions,
+                                                        GstCaps *              possible_caps,
+                                                        gpointer               data); 
 
-  GST_CLASS_PADDING
-};
+/* typefinding interface */
 
-GType gst_type_find_get_type (void);
+GType           gst_type_find_factory_get_type         (void);
+    
+GList *                gst_type_find_factory_get_list          (void);
 
+gchar **       gst_type_find_factory_get_extensions    (const GstTypeFindFactory *factory);
+GstCaps *      gst_type_find_factory_get_caps          (const GstTypeFindFactory *factory);
+void           gst_type_find_factory_call_function     (const GstTypeFindFactory *factory,
+                                                        GstTypeFind *find);
 
-G_END_DECLS
 
-#endif /* GST_DISABLE_TYPE_FIND */
+G_END_DECLS
 
 #endif /* __GST_TYPE_FIND_H__ */
index 3a7d0b883763b12d4fb8e15bc35c2e301d6203c6..6dd2e2d201a75a9e1fe3a0854af0813ea676e179 100644 (file)
@@ -265,7 +265,6 @@ gst_util_set_object_arg (GObject * object, const gchar * name, const gchar * val
  */
 
 #include "gstpad.h"
-#include "gsttype.h"
 #include "gstprops.h"
 
 static void
@@ -399,14 +398,11 @@ gst_print_pad_caps (GString * buf, gint indent, GstPad * pad)
     gint capx = 0;
 
     while (caps) {
-      GstType *type;
-
       string_append_indent (buf, indent);
       g_string_append_printf (buf, "Cap[%d]: %s\n", capx++, caps->name);
 
-      type = gst_type_find_by_id (caps->id);
       string_append_indent (buf, indent + 2);
-      g_string_append_printf (buf, "MIME type: %s\n", type->mime ? type->mime : "unknown/unknown");
+      g_string_append_printf (buf, "MIME type: %s\n", gst_caps_get_mime (caps));
 
       if (caps->properties)
        gst_print_props (buf, indent + 4, caps->properties->properties, TRUE);
index 19f2576e1d68041da16495b0b9ad15d07a476bc5..ca6b9f76da234bb0a8ff118902292b5e498200b0 100644 (file)
@@ -34,7 +34,7 @@
 
 #include <gst/gst_private.h>
 #include <gst/gstelement.h>
-#include <gst/gsttype.h>
+#include <gst/gsttypefind.h>
 #include <gst/gstscheduler.h>
 #include <gst/gstautoplug.h>
 #include <gst/gsturi.h>
@@ -712,19 +712,42 @@ gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gcha
 }
 
 static gboolean
-gst_xml_registry_parse_type_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
-                                     gsize text_len, GstXMLRegistry *registry, GError **error)
+gst_xml_registry_parse_type_find_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
+                                         gsize text_len, GstXMLRegistry *registry, GError **error)
 {
-  GstTypeFactory *factory = GST_TYPE_FACTORY (registry->current_feature);
+  GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (registry->current_feature);
 
   if (!strcmp (tag, "name")) {
     registry->current_feature->name = g_strndup (text, text_len);
   }
-  else if (!strcmp (tag, "mime")) {
-    factory->mime = g_strndup (text, text_len);
+  else if (!strcmp(tag, "rank")) {
+    glong rank;
+    gchar *ret;
+     rank = strtol (text, &ret, 0);
+    if (ret == text + text_len) {
+     gst_element_factory_set_rank (factory, rank);
+    }
   }
-  else if (!strcmp(tag, "extensions")) {
-    factory->exts = g_strndup (text, text_len);
+  /* FIXME!!
+  else if (!strcmp (tag, "caps")) {
+    factory->caps = g_strndup (text, text_len);
+  }*/
+  else if (!strcmp(tag, "extension")) {
+    gchar **new;
+    gchar **old = factory->extensions;
+    gint i = 0;
+    
+    /* expensive, but cycles are cheap... */
+    if (old)
+      while (old[i]) i++;
+    new = g_new0 (gchar *, i + 2);
+    new[i] = g_strndup (text, text_len);
+    while (i > 0) {
+      i--;
+      new[i] = old[i];
+    }
+    g_free (old);
+    factory->extensions = new;
   }
 
   return TRUE;
@@ -905,8 +928,8 @@ gst_xml_registry_start_element (GMarkupParseContext *context,
            xmlregistry->parser = gst_xml_registry_parse_element_factory;
            break;
          }
-         else if (GST_IS_TYPE_FACTORY (feature)) {
-           xmlregistry->parser = gst_xml_registry_parse_type_factory;
+         else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+           xmlregistry->parser = gst_xml_registry_parse_type_find_factory;
          }
          else if (GST_IS_SCHEDULER_FACTORY (feature)) {
            xmlregistry->parser = gst_xml_registry_parse_scheduler_factory;
@@ -1087,20 +1110,11 @@ gst_xml_registry_end_element (GMarkupParseContext *context,
       break;
     case GST_XML_REGISTRY_FEATURE:
       if (!strcmp (element_name, "feature")) {
-       if (GST_IS_TYPE_FACTORY (xmlregistry->current_feature)) {
-          GstTypeFactory *factory = GST_TYPE_FACTORY (xmlregistry->current_feature);
-         gst_type_register (factory);
-       }
        xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
        xmlregistry->parser = gst_xml_registry_parse_plugin;
        gst_plugin_add_feature (xmlregistry->current_plugin, xmlregistry->current_feature);
        xmlregistry->current_feature = NULL;
       }
-      else if (!strcmp (element_name, "typefind")) {
-        GstTypeFactory *factory = GST_TYPE_FACTORY (xmlregistry->current_feature);
-
-        factory->typefindfunc = gst_type_type_find_dummy;
-      }
       break;
     case GST_XML_REGISTRY_PADTEMPLATE:
       if (!strcmp (element_name, "padtemplate")) {
@@ -1270,12 +1284,19 @@ gst_xml_registry_paths_text (GMarkupParseContext *context, const gchar *text,
 #define PUT_ESCAPED(tag,value)                                         \
 G_STMT_START{                                                  \
   const gchar *toconv = value;                                 \
-  if (value) {                                                 \
+  if (toconv) {                                                        \
     gchar *v = g_markup_escape_text (toconv, strlen (toconv)); \
     CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, v, tag);                        \
     g_free (v);                                                        \
   }                                                            \
 }G_STMT_END
+#define PUT_ESCAPED_INT(tag,value)                             \
+G_STMT_START{                                                  \
+  gchar *save = g_strdup_printf ("%ld", (glong) value);                \
+  CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, save, tag);               \
+  g_free (save);                                               \
+}G_STMT_END
+
 
 static gboolean
 gst_xml_registry_save_props_func (GstPropsEntry *entry, 
@@ -1381,7 +1402,7 @@ gst_xml_registry_save_caps (GstXMLRegistry *xmlregistry, GstCaps *caps)
   while (caps) {
     CLASS (xmlregistry)->save_func (xmlregistry, "<capscomp>\n");
     PUT_ESCAPED ("name", caps->name);
-    PUT_ESCAPED ("type", gst_type_find_by_id (caps->id)->mime);
+    PUT_ESCAPED ("type", gst_caps_get_mime (caps));
 
     if (caps->properties) {
       CLASS (xmlregistry)->save_func (xmlregistry, "<properties>\n");
@@ -1460,13 +1481,18 @@ gst_xml_registry_save_feature (GstXMLRegistry *xmlregistry, GstPluginFeature *fe
       templates = g_list_next (templates);
     }
   }
-  else if (GST_IS_TYPE_FACTORY (feature)) {
-    GstTypeFactory *factory = GST_TYPE_FACTORY (feature);
-
-    PUT_ESCAPED ("mime", factory->mime);
-    PUT_ESCAPED ("extensions", factory->exts);
-    if (factory->typefindfunc) {
-      CLASS (xmlregistry)->save_func (xmlregistry, "<typefind/>\n");
+  else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+    GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
+    gint i = 0;
+    /* FIXME 
+    if (factory->caps) {
+      CLASS (xmlregistry)->save_func (xmlregistry, "<caps>\n");
+      gst_xml_registry_save_caps (xmlregistry, factory->caps);
+      CLASS (xmlregistry)->save_func (xmlregistry, "</caps>\n");
+    } */
+    while (factory->extensions[i]) {
+      PUT_ESCAPED ("extension", factory->extensions[i]);
+      i++;
     }
   }
   else if (GST_IS_SCHEDULER_FACTORY (feature)) {
index 97d3e58050067a85420089792851fb42c29242fb..3bd8a76a39a262f0512384d8481d1d65b4772630 100644 (file)
@@ -1,3 +1,3 @@
-SUBDIRS = control getbits
+SUBDIRS = control getbits bytestream
 
-DIST_SUBDIRS = control getbits
+DIST_SUBDIRS = control getbits bytestream
diff --git a/libs/gst/bytestream/Makefile.am b/libs/gst/bytestream/Makefile.am
new file mode 100644 (file)
index 0000000..b8f8f4d
--- /dev/null
@@ -0,0 +1,10 @@
+librarydir = $(libdir)/gstreamer-@GST_MAJORMINOR@
+
+library_LTLIBRARIES = libgstbytestream.la
+
+libgstbytestreamincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst
+libgstbytestreaminclude_HEADERS = bytestream.h
+
+libgstbytestream_la_SOURCES = bytestream.c 
+libgstbytestream_la_CFLAGS = $(GST_CFLAGS)
+libgstbytestream_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
diff --git a/libs/gst/bytestream/bytestream.c b/libs/gst/bytestream/bytestream.c
new file mode 100644 (file)
index 0000000..8476806
--- /dev/null
@@ -0,0 +1,762 @@
+/* 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/gstplugin.h>
+#include <gst/gstversion.h>
+#include "bytestream.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);
+
+  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_BUFFER (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));
+  }
+}
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (debug_bs, "bytestream", 0, "bytestream library");
+
+  gst_plugin_set_longname (plugin, "GstByteStream: a byte-oriented layer on top of buffer-passing");
+  return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "gstbytestream",
+  plugin_init
+};
diff --git a/libs/gst/bytestream/bytestream.h b/libs/gst/bytestream/bytestream.h
new file mode 100644 (file)
index 0000000..82ac0ec
--- /dev/null
@@ -0,0 +1,74 @@
+/* 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;
+
+  GST_OBJECT_PADDING
+};
+
+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__ */
index 00bd244086c06d405f489b21d796209d2afb7508..0d6a67d050307ed3b9bd323c0699807caaa2ad36 100644 (file)
@@ -1,40 +1,51 @@
+# FIXME:
+# need to get gstbufferstore.[ch] into its own lib, preferrably 
+# libs/gst/buifferstore
+# This requires building libs/gst before this dir, which we currently don't
+# do.
+
 plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@
 
 plugin_LTLIBRARIES = libgstelements.la
 
 libgstelements_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
 libgstelements_la_SOURCES =    \
+       gstaggregator.c         \
+       gstbufferstore.c        \
        gstelements.c           \
-       gstfakesrc.c            \
-       gstidentity.c           \
        gstfakesink.c           \
-       gstfilesrc.c            \
+       gstfakesrc.c            \
        gstfilesink.c           \
-       gstfdsrc.c              \
+       gstfilesrc.c            \
        gstfdsink.c             \
+       gstfdsrc.c              \
+       gstidentity.c           \
+       gstmd5sink.c            \
        gstmultidisksrc.c       \
        gstpipefilter.c         \
-       gsttee.c                \
-       gstaggregator.c         \
        gstshaper.c             \
        gststatistics.c         \
-       gstmd5sink.c
+       gsttee.c                \
+       gsttypefindelement.c
+
 libgstelements_la_CFLAGS = $(GST_CFLAGS)
 libgstelements_la_LIBADD =
 libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS =               \
-       gstfakesrc.h            \
-       gstidentity.h           \
+       gstaggregator.h         \
+       gstbufferstore.h        \
        gstfakesink.h           \
-       gstfilesink.h           \
+       gstfakesrc.h            \
+       gstfdsink.h             \
        gstfdsrc.h              \
+       gstfilesink.h           \
+       gstfilesrc.h            \
+       gstidentity.h           \
+       gstmd5sink.h            \
        gstmultidisksrc.h       \
-       gstfdsink.h             \
        gstpipefilter.h         \
-       gsttee.h                \
-       gstaggregator.h         \
        gstshaper.h             \
-       gststatistics.h         \
-       gstfilesrc.h            \
-       gstmd5sink.h
+       gststatistics.h         \
+       gsttee.h                \
+       gsttypefindelement.h
diff --git a/plugins/elements/gstbufferstore.c b/plugins/elements/gstbufferstore.c
new file mode 100644 (file)
index 0000000..d7dc0b3
--- /dev/null
@@ -0,0 +1,465 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gstbufferstore.c: keep an easily accessible list of all buffers
+ *
+ * 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 "gstbufferstore.h"
+#include <string.h>
+
+GST_DEBUG_CATEGORY (gst_buffer_store_debug);
+#define GST_CAT_DEFAULT gst_buffer_store_debug
+
+enum {
+  CLEARED,
+  BUFFER_ADDED,
+  LAST_SIGNAL
+};
+enum {
+  ARG_0
+};
+
+
+static void    gst_buffer_store_class_init     (gpointer               g_class,
+                                                 gpointer              class_data);
+static void    gst_buffer_store_init           (GTypeInstance *        instance,
+                                                gpointer               g_class);
+static void    gst_buffer_store_dispose        (GObject *              object);
+
+static gboolean        gst_buffer_store_add_buffer_func (GstBufferStore *      store, 
+                                                GstBuffer *            buffer);
+static void    gst_buffer_store_cleared_func   (GstBufferStore *       store);
+  
+static GObjectClass *parent_class = NULL;
+static guint gst_buffer_store_signals[LAST_SIGNAL] = { 0 };
+
+G_GNUC_UNUSED static void
+debug_buffers (GstBufferStore *store)
+{
+  GList *walk = store->buffers;
+  
+  g_printerr ("BUFFERS in store:\n");
+  while (walk) {
+    g_print ("%15"G_GUINT64_FORMAT" - %7u\n", GST_BUFFER_OFFSET (walk->data), GST_BUFFER_SIZE (walk->data));
+    walk = g_list_next (walk);
+  }
+  g_printerr ("\n");
+}
+GType
+gst_buffer_store_get_type (void)
+{
+  static GType store_type = 0;
+
+  if (!store_type) {
+    static const GTypeInfo store_info = {
+      sizeof (GstBufferStoreClass),
+      NULL,
+      NULL,
+      gst_buffer_store_class_init,
+      NULL,
+      NULL,
+      sizeof (GstBufferStore),
+      0,
+      gst_buffer_store_init,
+      NULL
+    };
+    store_type = g_type_register_static (G_TYPE_OBJECT,
+                                           "GstBufferStore",
+                                           &store_info, 0);
+  
+    /* FIXME: better description anyone? */
+    GST_DEBUG_CATEGORY_INIT (gst_buffer_store_debug, "bufferstore", 0, "store all data");
+  }
+  return store_type;
+}
+static gboolean
+continue_accu (GSignalInvocationHint *ihint, GValue *return_accu, 
+              const GValue *handler_return, gpointer data)
+{
+  gboolean do_continue = g_value_get_boolean (handler_return);
+  g_value_set_boolean (return_accu, do_continue);
+
+  return do_continue;
+}
+static void
+gst_buffer_store_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstBufferStoreClass *store_class;
+
+  gobject_class = G_OBJECT_CLASS (g_class);
+  store_class = GST_BUFFER_STORE_CLASS (g_class);
+
+  parent_class = g_type_class_peek_parent (g_class);
+
+  gobject_class->dispose = gst_buffer_store_dispose;
+  
+  gst_buffer_store_signals[CLEARED] = g_signal_new ("cleared", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstBufferStoreClass, cleared), NULL, NULL,
+          gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
+  gst_buffer_store_signals[BUFFER_ADDED] = g_signal_new ("buffer-added", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstBufferStoreClass, buffer_added), continue_accu, NULL,
+          gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+
+  store_class->cleared = gst_buffer_store_cleared_func;
+  store_class->buffer_added = gst_buffer_store_add_buffer_func;
+}
+static void
+gst_buffer_store_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstBufferStore *store = GST_BUFFER_STORE (instance);
+  
+  store->buffers = NULL;
+}
+static void
+gst_buffer_store_dispose (GObject *object)
+{
+  GstBufferStore *store = GST_BUFFER_STORE (object);
+
+  gst_buffer_store_clear (store);
+
+  parent_class->dispose (object);
+}
+static gboolean
+gst_buffer_store_add_buffer_func (GstBufferStore *store, GstBuffer *buffer)
+{
+  g_assert (buffer != NULL);
+  
+  if (!GST_BUFFER_OFFSET_IS_VALID (buffer) &&
+      store->buffers &&
+      GST_BUFFER_OFFSET_IS_VALID (store->buffers->data)) {
+    /* we assumed valid offsets, but suddenly they are not anymore */
+    GST_DEBUG_OBJECT (store, "attempting to add buffer %p with invalid offset to store with valid offset, abort",
+           buffer);
+    return FALSE;
+  } else if (!store->buffers || !GST_BUFFER_OFFSET_IS_VALID (store->buffers->data)) {
+    /* the starting buffer had an invalid offset, in that case we assume continuous buffers */
+    GST_LOG_OBJECT (store, "adding buffer %p with invalid offset and size %u",
+           buffer, GST_BUFFER_SIZE (buffer));
+    gst_data_ref (GST_DATA (buffer));
+    store->buffers = g_list_append (store->buffers, buffer);
+    return TRUE;
+  } else {
+    /* both list and buffer have valid offsets, we can really go wild */
+    GList *walk, *current_list = NULL;
+    GstBuffer *current;
+    
+    g_assert (GST_BUFFER_OFFSET_IS_VALID (buffer));
+    GST_LOG_OBJECT (store, "attempting to add buffer %p with offset %"G_GUINT64_FORMAT" and size %u",
+           buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+    /* we keep a sorted list of non-overlapping buffers */
+    walk = store->buffers;
+    while (walk) {
+      current = GST_BUFFER (walk->data);
+      current_list = walk;
+      walk = g_list_next (walk);
+      if (GST_BUFFER_OFFSET (current) < GST_BUFFER_OFFSET (buffer)) {
+       continue;
+      } else if (GST_BUFFER_OFFSET (current) == GST_BUFFER_OFFSET (buffer)) {
+       guint needed_size;
+       if (walk) {
+         needed_size = MIN (GST_BUFFER_SIZE (buffer), 
+                 GST_BUFFER_OFFSET (walk->data) - GST_BUFFER_OFFSET (current));
+       } else {
+         needed_size = GST_BUFFER_SIZE (buffer);
+       }
+       if (needed_size <= GST_BUFFER_SIZE (current)) {
+         buffer = NULL;
+         break;
+       } else {
+         if (needed_size < GST_BUFFER_SIZE (buffer)) {
+           /* need to create subbuffer to not have overlapping data */
+           GstBuffer *sub = gst_buffer_create_sub (buffer, 0, needed_size);
+           g_assert (sub);
+           buffer = sub;
+         } else {
+           gst_data_ref (GST_DATA (buffer));
+         }
+         /* replace current buffer with new one */
+         GST_INFO_OBJECT (store, "replacing buffer %p with buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
+                          current_list->data, buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+         gst_data_unref (GST_DATA (current_list->data));
+         current_list->data = buffer;
+         buffer = NULL;
+         break;
+       }
+      } else if (GST_BUFFER_OFFSET (current) > GST_BUFFER_OFFSET (buffer)) {
+       GList *previous = g_list_previous (current_list);
+       guint64 start_offset = previous ? 
+               GST_BUFFER_OFFSET (previous->data) + GST_BUFFER_SIZE (previous->data) : 0;
+
+       if (start_offset == GST_BUFFER_OFFSET (current)) {
+         buffer = NULL;
+         break;
+       } else {
+         /* we have data to insert */
+         if (start_offset > GST_BUFFER_OFFSET (buffer) ||
+             GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer) > GST_BUFFER_OFFSET (current)) {
+           /* need a subbuffer */
+           start_offset = GST_BUFFER_OFFSET (buffer) > start_offset ? 0 : 
+                          start_offset - GST_BUFFER_OFFSET (buffer);
+           GstBuffer* sub = gst_buffer_create_sub (buffer, start_offset,
+                   MIN (GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (current) - start_offset - GST_BUFFER_OFFSET (buffer)));
+           g_assert (sub);
+           GST_BUFFER_OFFSET (sub) = start_offset + GST_BUFFER_OFFSET (buffer);
+           buffer = sub;
+         } else {
+           gst_data_ref (GST_DATA (buffer));
+         }
+         GST_INFO_OBJECT (store, "adding buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
+                          buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+         store->buffers = g_list_insert_before (store->buffers, current_list, buffer);
+         buffer = NULL;
+         break;
+       }
+      }
+    }
+    if (buffer) {
+      gst_data_ref (GST_DATA (buffer));
+      GST_INFO_OBJECT (store, "adding buffer %p with offset %"G_GINT64_FORMAT" and size %u", 
+                      buffer, GST_BUFFER_OFFSET (buffer), GST_BUFFER_SIZE (buffer));
+      if (current_list) {
+       g_list_append (current_list, buffer);
+      } else {
+       g_assert (store->buffers == NULL);
+       store->buffers = g_list_prepend (NULL, buffer);
+      }
+    }
+    return TRUE;
+  }
+}
+static void
+gst_buffer_store_cleared_func (GstBufferStore *store)
+{
+  g_list_foreach (store->buffers, (GFunc) gst_data_unref, NULL);
+  g_list_free (store->buffers);
+  store->buffers = NULL;
+}
+/**
+ * gst_buffer_store_new:
+ *
+ * Creates a new bufferstore.
+ *
+ * Returns: the new bufferstore.
+ */
+GstBufferStore *
+gst_buffer_store_new (void)
+{
+  return GST_BUFFER_STORE (g_object_new (GST_TYPE_BUFFER_STORE, NULL));
+}
+/**
+ * gst_buffer_store_clear:
+ * @store: a bufferstore
+ *
+ * Clears the buffer store. All buffers are removed and the buffer store
+ * behaves like it was just created.
+ */
+/* FIXME: call this function _reset ? */
+void
+gst_buffer_store_clear (GstBufferStore *store)
+{
+  g_return_if_fail (GST_IS_BUFFER_STORE (store));
+  
+  g_signal_emit (store, gst_buffer_store_signals [CLEARED], 0, NULL);
+}
+/**
+ * gst_buffer_store_add_buffer:
+ * @store: a bufferstore
+ * @buffer: the buffer to add
+ *
+ * Adds a buffer to the buffer store. 
+ *
+ * Returns: TRUE, if the buffer was added, FALSE if an error occured.
+ */
+gboolean
+gst_buffer_store_add_buffer (GstBufferStore *store, GstBuffer *buffer)
+{
+  gboolean ret;
+  
+  g_return_val_if_fail (GST_IS_BUFFER_STORE (store), FALSE);
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
+
+  if (store->buffers &&
+      GST_BUFFER_OFFSET_IS_VALID (store->buffers->data) &&
+      !GST_BUFFER_OFFSET_IS_VALID (buffer))
+    return FALSE;
+  
+  g_signal_emit (store, gst_buffer_store_signals [BUFFER_ADDED], 0, buffer, &ret);
+  
+  return ret;
+}
+/**
+ * gst_buffer_store_get_buffer:
+ * @store: a bufferstore
+ * @offset: starting offset of returned buffer
+ * @size: size of returned buffer
+ *
+ * Returns a buffer that corresponds to the given area of data. If part of the
+ * data is not available inside the store, NULL is returned. You have to unref
+ * the buffer after use.
+ *
+ * Returns: a buffer with the requested data or NULL if the data was not 
+ *          available.
+ */
+GstBuffer *
+gst_buffer_store_get_buffer (GstBufferStore *store, guint64 offset, guint size)
+{
+  GstBuffer *current;
+  GList *walk;
+  guint8 *data;
+  guint tmp;
+  guint64 cur_offset;
+  gboolean have_offset;
+  GstBuffer *ret = NULL;
+
+  g_return_val_if_fail (GST_IS_BUFFER_STORE (store), NULL);
+
+  walk = store->buffers;
+  if (!walk)
+    return NULL;
+  if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) {
+    have_offset = TRUE;
+  } else {
+    have_offset = FALSE;
+    cur_offset = 0;
+  }
+  while (walk) {
+    current = GST_BUFFER (walk->data);
+    if (have_offset) {
+      cur_offset = GST_BUFFER_OFFSET (current);
+    }
+    walk = g_list_next (walk);
+    if (cur_offset > offset) {
+      /* #include <windows.h>
+         do_nothing_loop (); */
+    } else if (cur_offset == offset &&
+       GST_BUFFER_SIZE (current) == size) {
+      GST_LOG_OBJECT (store, "found matching buffer %p for offset %"G_GUINT64_FORMAT" and size %u",
+                     current, offset, size);
+      ret = current;
+      gst_data_ref (GST_DATA (ret));
+      GST_LOG_OBJECT (store, "refcount %d",
+                     GST_DATA_REFCOUNT_VALUE(ret));
+      break;
+    } else if (cur_offset + GST_BUFFER_SIZE (current) > offset) {
+      if (cur_offset + GST_BUFFER_SIZE (current) >= offset + size) {
+       ret = gst_buffer_create_sub (current, offset - cur_offset, size);
+       GST_LOG_OBJECT (store, "created subbuffer %p from buffer %p for offset %llu and size %u",
+                       ret, current,  offset, size);
+       break;
+      }
+      /* uh, the requested data spans some buffers */
+      ret = gst_buffer_new_and_alloc (size);
+      GST_LOG_OBJECT (store, "created buffer %p for offset %"G_GUINT64_FORMAT
+                     " and size %u, will fill with data now",
+                     ret, offset, size);
+      data = GST_BUFFER_DATA (ret);
+      tmp = GST_BUFFER_SIZE (current) - offset + cur_offset;
+      memcpy (data, GST_BUFFER_DATA (current) + offset - cur_offset, tmp);
+      data += tmp;
+      size -= tmp;
+      while (size) {
+       if (walk == NULL || 
+           (have_offset && 
+            GST_BUFFER_OFFSET (current) + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) {
+         GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and remaining size %u available, aborting",
+                           offset, size);
+         gst_data_unref (GST_DATA (ret));
+         ret = NULL;
+         goto out;
+       }
+       current = GST_BUFFER (walk->data);
+       walk = g_list_next (walk);
+       tmp = MIN (GST_BUFFER_SIZE (current), size);
+       memcpy (data, GST_BUFFER_DATA (current), tmp);
+       size -= tmp;
+      }
+    }
+    if (!have_offset) {
+      cur_offset += GST_BUFFER_SIZE (current);
+    }
+  }
+out:
+  
+  return ret;
+}
+/**
+ * gst_buffer_store_get_size:
+ * @store: a bufferstore
+ * @offset: desired offset
+ *
+ * Calculates the number of bytes available starting from offset. This allows
+ * to query a buffer with the returned size.
+ *
+ * Returns: the number of continuous bytes in the bufferstore starting at
+ *          offset.
+ */
+guint
+gst_buffer_store_get_size (GstBufferStore *store, guint64 offset)
+{
+  GList *walk;
+  guint64 cur_offset;
+  gboolean have_offset;
+  gboolean counting = FALSE;
+  GstBuffer *current;
+  guint ret = 0;
+
+  g_return_val_if_fail (GST_IS_BUFFER_STORE (store), 0);
+
+  walk = store->buffers;
+  if (!walk)
+    return 0;
+  if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) {
+    have_offset = TRUE;
+  } else {
+    have_offset = FALSE;
+    cur_offset = 0;
+  }
+  while (walk) {
+    if (have_offset && counting && 
+       cur_offset + GST_BUFFER_SIZE (current) !=  GST_BUFFER_OFFSET (walk->data)) {
+      break;
+    }
+    current = GST_BUFFER (walk->data);
+    if (have_offset) {
+      cur_offset = GST_BUFFER_OFFSET (current);
+    }
+    walk = g_list_next (walk);
+    if (counting) {
+      ret += GST_BUFFER_SIZE (current);
+    } else {
+      if (cur_offset > offset)
+       return 0;
+      if (cur_offset + GST_BUFFER_SIZE (current) > offset) {
+       /* we have at least some bytes */
+       ret = cur_offset + GST_BUFFER_SIZE (current) - offset;
+       counting = TRUE;
+      }
+    }
+    if (!have_offset) {
+      cur_offset += GST_BUFFER_SIZE (current);
+    }
+  }
+  
+  return ret;
+}
diff --git a/plugins/elements/gstbufferstore.h b/plugins/elements/gstbufferstore.h
new file mode 100644 (file)
index 0000000..15fff8e
--- /dev/null
@@ -0,0 +1,73 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: keep an easily accessible list of all buffers
+ *
+ * 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_BUFFER_STORE_H__
+#define __GST_BUFFER_STORE_H__
+
+#include <gst/gstbuffer.h>
+#include <gst/gstinfo.h>
+#include <gst/gstmarshal.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_BUFFER_STORE          (gst_buffer_store_get_type ())
+#define GST_BUFFER_STORE(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_BUFFER_STORE, GstBufferStore))
+#define GST_IS_BUFFER_STORE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_BUFFER_STORE))
+#define GST_BUFFER_STORE_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BUFFER_STORE, GstBufferStoreClass))
+#define GST_IS_BUFFER_STORE_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_BUFFER_STORE))
+#define GST_BUFFER_STORE_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BUFFER_STORE, GstBufferStoreClass))
+
+typedef struct _GstBufferStore                 GstBufferStore;
+typedef struct _GstBufferStoreClass    GstBufferStoreClass;
+
+struct _GstBufferStore {
+  GObject              object;
+
+  GList *              buffers;
+};
+
+struct _GstBufferStoreClass {
+  GObjectClass         parent_class;
+
+  /* signals */
+  void                 (* cleared)                     (GstBufferStore *       store);
+  gboolean             (* buffer_added)                (GstBufferStore *       store,
+                                                        GstBuffer *            buffer);
+};
+
+GType                  gst_buffer_store_get_type       (void);
+
+GstBufferStore *       gst_buffer_store_new            (void);
+void                   gst_buffer_store_clear          (GstBufferStore *       store);
+
+gboolean               gst_buffer_store_add_buffer     (GstBufferStore *       store,
+                                                        GstBuffer *            buffer);
+
+GstBuffer *            gst_buffer_store_get_buffer     (GstBufferStore *       store,
+                                                        guint64                offset,
+                                                        guint                  size);
+guint                  gst_buffer_store_get_size       (GstBufferStore *       store,
+                                                        guint64                offset);
+
+G_END_DECLS
+
+#endif /* __GST_BUFFER_STORE_H__ */
index 78413f2161a5c7ea3ff68318a8ba5720992565a4..7b769a05148db7a4d9ee008ebfd208e3e54dc063 100644 (file)
 
 #include <gst/gst.h>
 
-#include "gstfilesrc.h"
-#include "gstfilesink.h"
-#include "gstidentity.h"
+#include "gstaggregator.h"
 #include "gstfakesink.h"
 #include "gstfakesrc.h"
 #include "gstfdsink.h"
 #include "gstfdsrc.h"
+#include "gstfilesink.h"
+#include "gstfilesrc.h"
+#include "gstidentity.h"
+#include "gstmd5sink.h"
 #include "gstmultidisksrc.h"
 #include "gstpipefilter.h"
-#include "gsttee.h"
-#include "gstaggregator.h"
 #include "gstshaper.h"
 #include "gststatistics.h"
-#include "gstmd5sink.h"
+#include "gsttee.h"
+#include "gsttypefindelement.h"
 
 
 struct _elements_entry {
@@ -55,20 +56,21 @@ extern GType gst_filesrc_get_type(void);
 extern GstElementDetails gst_filesrc_details;
 
 static struct _elements_entry _elements[] = {
+  { "aggregator",   gst_aggregator_get_type,   &gst_aggregator_details,        gst_aggregator_factory_init },
   { "fakesrc",             gst_fakesrc_get_type,       &gst_fakesrc_details,           gst_fakesrc_factory_init },
   { "fakesink",     gst_fakesink_get_type,     &gst_fakesink_details,          gst_fakesink_factory_init },
+  { "fdsink",       gst_fdsink_get_type,       &gst_fdsink_details,            NULL },
+  { "fdsrc",       gst_fdsrc_get_type,         &gst_fdsrc_details,             NULL },
   { "filesrc",             gst_filesrc_get_type,       &gst_filesrc_details,           NULL },
   { "filesink",            gst_filesink_get_type,      &gst_filesink_details,          NULL },
   { "identity",     gst_identity_get_type,     &gst_identity_details,          NULL },
-  { "fdsink",       gst_fdsink_get_type,       &gst_fdsink_details,            NULL },
-  { "fdsrc",       gst_fdsrc_get_type,         &gst_fdsrc_details,             NULL },
+  { "md5sink",      gst_md5sink_get_type,      &gst_md5sink_details,           gst_md5sink_factory_init },
   { "multidisksrc", gst_multidisksrc_get_type, &gst_multidisksrc_details,      NULL },
   { "pipefilter",   gst_pipefilter_get_type,   &gst_pipefilter_details,        NULL },
-  { "tee",                 gst_tee_get_type,           &gst_tee_details,               gst_tee_factory_init },
-  { "aggregator",   gst_aggregator_get_type,   &gst_aggregator_details,        gst_aggregator_factory_init },
   { "shaper",       gst_shaper_get_type,       &gst_shaper_details,            gst_shaper_factory_init },
   { "statistics",   gst_statistics_get_type,   &gst_statistics_details,        NULL },
-  { "md5sink",      gst_md5sink_get_type,      &gst_md5sink_details,           gst_md5sink_factory_init },
+  { "tee",                 gst_tee_get_type,           &gst_tee_details,               gst_tee_factory_init },
+  { "typefind",     gst_type_find_element_get_type, &gst_type_find_element_details,    NULL },
   { NULL, 0 },
 };
 
@@ -80,20 +82,21 @@ plugin_init (GModule *module, GstPlugin *plugin)
 
   gst_plugin_set_longname (plugin, "Standard GST Elements");
 
-  GST_DEBUG_CATEGORY_INIT (gst_fakesrc_debug,  "fakesrc",      0,      "fakesrc element");
+  GST_DEBUG_CATEGORY_INIT (gst_aggregator_debug, "aggregator", 0,      "aggregator element");
   GST_DEBUG_CATEGORY_INIT (gst_fakesink_debug, "fakesink",     0,      "fakesink element");
+  GST_DEBUG_CATEGORY_INIT (gst_fakesrc_debug,  "fakesrc",      0,      "fakesrc element");
+  GST_DEBUG_CATEGORY_INIT (gst_fdsink_debug,   "fdsink",       0,      "fdsink element");
+  GST_DEBUG_CATEGORY_INIT (gst_fdsrc_debug,    "fdsrc",        0,      "fdsrc element");
+  GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink",     0,      "filesink element");
   GST_DEBUG_CATEGORY_INIT (gst_filesrc_debug,  "filesrc",      0,      "filesrc element");
-  GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "fakesink",     0,      "filesink element");
   GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity",     0,      "identity element");
-  GST_DEBUG_CATEGORY_INIT (gst_fdsrc_debug,    "fdsrc",        0,      "fdsrc element");
-  GST_DEBUG_CATEGORY_INIT (gst_fdsink_debug,   "fdsink",       0,      "fdsink element");
+  GST_DEBUG_CATEGORY_INIT (gst_md5sink_debug,  "md5sink",      0,      "md5sink element");
   GST_DEBUG_CATEGORY_INIT (gst_multidisksrc_debug, "multidisksrc", 0,  "multidisksrc element");
   GST_DEBUG_CATEGORY_INIT (gst_pipefilter_debug, "pipefilter", 0,      "pipefilter element");
-  GST_DEBUG_CATEGORY_INIT (gst_tee_debug,      "tee",          0,      "tee element");
-  GST_DEBUG_CATEGORY_INIT (gst_aggregator_debug, "aggregator", 0,      "aggregator element");
   GST_DEBUG_CATEGORY_INIT (gst_shaper_debug,   "shaper",       0,      "shaper element");
   GST_DEBUG_CATEGORY_INIT (gst_statistics_debug, "statistics", 0,      "statistics element");
-  GST_DEBUG_CATEGORY_INIT (gst_md5sink_debug,  "md5sink",      0,      "md5sink element");
+  GST_DEBUG_CATEGORY_INIT (gst_tee_debug,      "tee",          0,      "tee element");
+  GST_DEBUG_CATEGORY_INIT (gst_type_find_element_debug,        "typefind", GST_DEBUG_BG_YELLOW | GST_DEBUG_FG_GREEN, "typefind element");
 
   while (_elements[i].name) {  
     factory = gst_element_factory_new (_elements[i].name,
@@ -112,12 +115,10 @@ plugin_init (GModule *module, GstPlugin *plugin)
       _elements[i].factoryinit (factory);
     }
 /*      g_print("added factory '%s'\n",_elements[i].name); */
-
     i++;
   }
 
 /*  INFO (GST_INFO_PLUGIN_LOAD,"gstelements: loaded %d standard elements", i);*/
-
   return TRUE;
 }
 
index e992ee50f994a8e9bdd9a7ed290cb539db87de9c..14287e347ef3cd8b786de107c61b92c7c2344a99 100644 (file)
@@ -487,7 +487,7 @@ gst_fakesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
           src->pool = gst_buffer_pool_get_default (src->sizemax, 10);
       } else {
         if (src->pool) {
-          gst_buffer_pool_free (src->pool);
+          gst_buffer_pool_unref (src->pool);
           src->pool = NULL;
         }
       }
index f0de20d0a249bdca4775d272be5dabe03905ac1e..d3288d6b368456606f950fe83214ce04d2e6a20c 100644 (file)
@@ -88,18 +88,6 @@ GstElementDetails gst_filesrc_details = {
 #define DEFAULT_BLOCKSIZE      4*1024
 #define DEFAULT_MMAPSIZE       4*1024*1024
 
-#ifdef G_HAVE_ISO_VARARGS
-
-/* #define fs_print(...) g_print(__VA_ARGS__)  */
-#define fs_print(...)
-
-#elif defined(G_HAVE_GNUC_VARARGS)
-
-/* #define fs_print(format,args...) g_print(format, ## args)  */
-#define fs_print(format,args...)
-
-#endif
-
 /* FileSrc signals and args */
 enum {
   /* FILL ME */
@@ -305,7 +293,7 @@ gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, G
         src->mapsize = g_value_get_ulong (value);
         g_object_notify (G_OBJECT (src), "mmapsize");
       } else {
-        GST_INFO ( "invalid mapsize, must a multiple of pagesize, which is %d", 
+        GST_INFO_OBJECT (src, "invalid mapsize, must a multiple of pagesize, which is %d", 
                  src->pagesize);
       }
       break;
@@ -355,8 +343,8 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf)
 {
   GstFileSrc *src = GST_FILESRC (GST_BUFFER_POOL_PRIVATE (buf));
 
-  fs_print ("freeing mmap()d buffer at %d+%d\n", 
-           GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf));
+  GST_LOG_OBJECT (src, "freeing mmap()d buffer at %"G_GUINT64_FORMAT"+%u", 
+                 GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf));
 
   /* remove the buffer from the list of available mmap'd regions */
   g_mutex_lock (src->map_regions_lock);
@@ -375,13 +363,14 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf)
   munmap (GST_BUFFER_DATA (buf), GST_BUFFER_MAXSIZE (buf));
   /* cast to unsigned long, since there's no gportable way to print
    * guint64 as hex */
-  GST_DEBUG ( "unmapped region %08lx+%08lx at %p", 
+  GST_LOG_OBJECT (src, "unmapped region %08lx+%08lx at %p", 
                  (unsigned long) GST_BUFFER_OFFSET (buf),
                  (unsigned long) GST_BUFFER_MAXSIZE (buf), 
                  GST_BUFFER_DATA (buf));
 
   GST_BUFFER_DATA (buf) = NULL;
 
+  g_object_unref (src);
   gst_buffer_default_free (buf);
 }
 
@@ -394,7 +383,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
 
   g_return_val_if_fail (offset >= 0, NULL);
 
-  fs_print  ("mapping region %08llx+%08lx from file into memory\n",offset,(unsigned long)size);
+  GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory",offset,(unsigned long)size);
   mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset);
 
   if (mmapregion == NULL) {
@@ -402,11 +391,11 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
     return NULL;
   }
   else if (mmapregion == MAP_FAILED) {
-    GST_DEBUG ("mmap (0x%08lx, %d, 0x%llx) : %s",
+    GST_WARNING_OBJECT (src, "mmap (0x%08lx, %d, 0x%llx) failed: %s",
             (unsigned long)size, src->fd, offset, strerror (errno));
     return NULL;
   }
-  GST_DEBUG ( "mapped region %08lx+%08lx from file into memory at %p", 
+  GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p", 
                  (unsigned long)offset, (unsigned long)size, mmapregion);
 
   /* time to allocate a new mapbuf */
@@ -426,6 +415,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size)
   GST_BUFFER_OFFSET (buf) = offset;
   GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
   GST_BUFFER_POOL_PRIVATE (buf) = src;
+  g_object_ref (src);
   GST_BUFFER_FREE_FUNC (buf) = (GstDataFreeFunction) gst_filesrc_free_parent_mmap;
 
   g_mutex_lock (src->map_regions_lock);
@@ -522,7 +512,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
     /* if the end is before the mapend, the buffer is in current mmap region... */
     /* ('cause by definition if readend is in the buffer, so's readstart) */
     if (readend <= mapend) {
-      fs_print ("read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf\n",
+      GST_LOG_OBJECT (src, "read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf",
              src->curoffset, readsize, mapstart, mapsize);
       buf = gst_buffer_create_sub (src->mapbuf, src->curoffset - mapstart,
                                    readsize);
@@ -530,8 +520,8 @@ gst_filesrc_get_mmap (GstFileSrc *src)
 
     /* if the start actually is within the current mmap region, map an overlap buffer */
     } else if (src->curoffset < mapend) {
-      fs_print ("read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap\n",
-             src->curoffset, readsize, mapstart, mapsize);
+      GST_LOG_OBJECT (src, "read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap",
+             (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize);
       buf = gst_filesrc_map_small_region (src, src->curoffset, readsize);
       if (buf == NULL)
         return NULL;
@@ -545,8 +535,8 @@ gst_filesrc_get_mmap (GstFileSrc *src)
     /* either the read buffer overlaps the start of the mmap region */
     /* or the read buffer fully contains the current mmap region    */
     /* either way, it's really not relevant, we just create a new region anyway*/
-    fs_print ("read buf %llu+%d starts before mapbuf %d+%d, but overlaps it\n",
-             src->curoffset,readsize, mapstart, mapsize);
+    GST_LOG_OBJECT (src, "read buf %llu+%d starts before mapbuf %d+%d, but overlaps it",
+             (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize);
     buf = gst_filesrc_map_small_region (src, src->curoffset, readsize);
     if (buf == NULL)
       return NULL;
@@ -555,7 +545,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
   /* then deal with the case where the read buffer is totally outside */
   if (buf == NULL) {
     /* first check to see if there's a map that covers the right region already */
-    fs_print ("searching for mapbuf to cover %llu+%d\n",src->curoffset,readsize);
+    GST_LOG_OBJECT (src, "searching for mapbuf to cover %llu+%d",src->curoffset,readsize);
     region.offset = src->curoffset;
     region.size = readsize;
     map = g_tree_search (src->map_regions,
@@ -564,7 +554,8 @@ gst_filesrc_get_mmap (GstFileSrc *src)
 
     /* if we found an exact match, subbuffer it */
     if (map != NULL) {
-      fs_print ("found mapbuf at %d+%d, creating subbuffer\n",GST_BUFFER_OFFSET(map),GST_BUFFER_SIZE(map));
+      GST_LOG_OBJECT (src, "found mapbuf at %"G_GUINT64_FORMAT"+%u, creating subbuffer",
+                     GST_BUFFER_OFFSET (map), GST_BUFFER_SIZE (map));
       buf = gst_buffer_create_sub (map, src->curoffset - GST_BUFFER_OFFSET(map), readsize);
       GST_BUFFER_OFFSET (buf) = src->curoffset;
 
@@ -572,7 +563,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
     } else {
       /* if the read buffer crosses a mmap region boundary, create a one-off region */
       if ((src->curoffset / src->mapsize) != (readend / src->mapsize)) {
-        fs_print ("read buf %llu+%d crosses a %d-byte boundary, creating a one-off\n",
+        GST_LOG_OBJECT (src, "read buf %llu+%d crosses a %d-byte boundary, creating a one-off",
                src->curoffset,readsize,src->mapsize);
         buf = gst_filesrc_map_small_region (src, src->curoffset, readsize);
        if (buf == NULL)
@@ -583,7 +574,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
        size_t mapsize;
 
         off_t nextmap = src->curoffset - (src->curoffset % src->mapsize);
-        fs_print ("read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering\n",
+        GST_LOG_OBJECT (src, "read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering",
                src->curoffset, readsize, nextmap, src->mapsize);
         /* first, we're done with the old mapbuf */
         gst_buffer_unref(src->mapbuf);
@@ -591,7 +582,7 @@ gst_filesrc_get_mmap (GstFileSrc *src)
 
        /* double the mapsize as long as the readsize is smaller */
        while (readsize - (src->curoffset - nextmap) > mapsize) {
-          fs_print ("readsize smaller then mapsize %08x %d\n", readsize, mapsize);
+          GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d", readsize, mapsize);
           mapsize <<=1;
        }
         /* create a new one */
@@ -664,7 +655,7 @@ gst_filesrc_get (GstPad *pad)
     GstEvent *event;
 
     src->seek_happened = FALSE;
-    GST_DEBUG ("filesrc sending discont");
+    GST_DEBUG_OBJECT (src, "sending discont");
     event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset, NULL);
     src->need_flush = FALSE;
     return GST_DATA (event);
@@ -672,13 +663,13 @@ gst_filesrc_get (GstPad *pad)
   /* check for flush */
   if (src->need_flush) {
     src->need_flush = FALSE;
-    GST_DEBUG ("filesrc sending flush");
+    GST_DEBUG_OBJECT (src, "sending flush");
     return GST_DATA (gst_event_new_flush ());
   }
 
   /* check for EOF */
   if (src->curoffset == src->filelen) {
-    GST_DEBUG ("filesrc eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT,
+    GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT,
                src->curoffset, src->filelen);
     gst_element_set_eos (GST_ELEMENT (src));
     return GST_DATA (gst_event_new (GST_EVENT_EOS));
@@ -697,7 +688,7 @@ gst_filesrc_open_file (GstFileSrc *src)
 {
   g_return_val_if_fail (!GST_FLAG_IS_SET (src ,GST_FILESRC_OPEN), FALSE);
 
-  GST_DEBUG ( "opening file %s",src->filename);
+  GST_INFO_OBJECT (src, "opening file %s",src->filename);
 
   /* open the file */
   src->fd = open (src->filename, O_RDONLY);
@@ -829,7 +820,7 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event)
 {
   GstFileSrc *src = GST_FILESRC (GST_PAD_PARENT (pad));
 
-  GST_DEBUG ( "event %d", GST_EVENT_TYPE (event));
+  GST_DEBUG_OBJECT (src, "event %d", GST_EVENT_TYPE (event));
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_SEEK:
@@ -847,19 +838,19 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event)
           if (offset > src->filelen) 
            goto error;
           src->curoffset = offset;
-          GST_DEBUG ( "seek set pending to %" G_GINT64_FORMAT, src->curoffset);
+          GST_DEBUG_OBJECT (src, "seek set pending to %" G_GINT64_FORMAT, src->curoffset);
          break;
         case GST_SEEK_METHOD_CUR:
           if (offset + src->curoffset > src->filelen) 
            goto error;
           src->curoffset += offset;
-          GST_DEBUG ( "seek cur pending to %" G_GINT64_FORMAT, src->curoffset);
+          GST_DEBUG_OBJECT (src, "seek cur pending to %" G_GINT64_FORMAT, src->curoffset);
          break;
         case GST_SEEK_METHOD_END:
           if (ABS (offset) > src->filelen) 
            goto error;
           src->curoffset = src->filelen - ABS (offset);
-          GST_DEBUG ( "seek end pending to %" G_GINT64_FORMAT, src->curoffset);
+          GST_DEBUG_OBJECT (src, "seek end pending to %" G_GINT64_FORMAT, src->curoffset);
          break;
        default:
           goto error;
diff --git a/plugins/elements/gsttypefind.c b/plugins/elements/gsttypefind.c
new file mode 100644 (file)
index 0000000..bd5e3d3
--- /dev/null
@@ -0,0 +1,669 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.c: element that detects type of stream
+ *
+ * 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.
+ */
+
+/* FIXME: need a better solution for non-seekable streams */
+
+/* way of operation:
+ * 1) get a list of all typefind functions sorted best to worst
+ * 2) if all elements have been called with all requested data goto 8
+ * 3) call all functions once with all available data
+ * 4) if a function returns a value >= ARG_MAXIMUM goto 8
+ * 5) all functions with a result > ARG_MINIMUM or functions that did not get
+ *    all requested data (where peek returned NULL) stay in list
+ * 6) seek to requested offset of best function that still has open data 
+ *    requests
+ * 7) goto 2
+ * 8) take best available result and use its caps
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gsttypefindelement.h"
+#include "gst/gst_private.h"
+
+#include <gst/gsttypefind.h>
+
+GST_DEBUG_CATEGORY (gst_type_find_element_debug);
+#define GST_CAT_DEFAULT gst_type_find_element_debug
+
+GstElementDetails gst_type_find_element_details = {
+  "TypeFind",
+  "Generic",
+  "LGPL",
+  "Finds the media type of a stream",
+  VERSION,
+  "Benjamin Otte <in7y118@public.uni-hamburg.de>",
+  "(C) 2003",
+};
+
+/* generic templates */
+GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory,
+  "sink",
+  GST_PAD_SINK,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory,
+  "src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+
+/* TypeFind signals and args */
+enum {
+  HAVE_TYPE,
+  LAST_SIGNAL
+};
+enum {
+  ARG_0,
+  ARG_CAPS,
+  ARG_MINIMUM,
+  ARG_MAXIMUM
+};
+enum {
+  MODE_NORMAL, /* act as identity */
+  MODE_TYPEFIND, /* do typefinding */
+};
+
+
+static void    gst_type_find_element_class_init        (gpointer       g_class,
+                                                        gpointer       class_data);
+static void    gst_type_find_element_init              (GTypeInstance *instance,
+                                                        gpointer       g_class);
+static void    gst_type_find_element_dispose           (GObject *      object);
+static void    gst_type_find_element_set_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        const GValue * value, 
+                                                        GParamSpec *   pspec);
+static void    gst_type_find_element_get_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        GValue *       value, 
+                                                        GParamSpec *   pspec);
+
+static const GstEventMask *
+                gst_type_find_element_src_event_mask    (GstPad *      pad);
+static gboolean        gst_type_find_element_src_event         (GstPad *       pad,
+                                                        GstEvent *     event);
+
+static void    gst_type_find_element_chain             (GstPad *       sinkpad,
+                                                        GstData *      data);
+static GstElementStateReturn
+               gst_type_find_element_change_state      (GstElement *   element);
+
+static GstElementClass *parent_class = NULL;
+static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_type_find_element_get_type (void)
+{
+  static GType typefind_type = 0;
+
+  if (!typefind_type) {
+    static const GTypeInfo typefind_info = {
+      sizeof (GstTypeFindElementClass),
+      NULL,
+      NULL,
+      gst_type_find_element_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTypeFindElement),
+      0,
+      gst_type_find_element_init,
+      NULL
+    };
+    typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
+                                           "GstTypeFindElement",
+                                           &typefind_info, 0);
+  }
+  return typefind_type;
+}
+static void
+gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps)
+{
+  gchar *caps_str;
+  
+  g_assert (typefind->caps == NULL);
+  g_assert (caps != NULL);
+
+  caps_str = gst_caps_to_string (caps);
+  GST_INFO_OBJECT (typefind, "found caps %s", caps_str);
+  g_free (caps_str);
+  gst_caps_replace (&typefind->caps, caps);
+  if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) {
+    gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad");
+  }
+}
+static void
+gst_type_find_element_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstTypeFindElementClass *typefind_class;
+
+  gobject_class = G_OBJECT_CLASS (g_class);
+  gstelement_class = GST_ELEMENT_CLASS (g_class);
+  typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class);
+
+  parent_class = g_type_class_peek_parent (g_class);
+
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
+  gobject_class->dispose      =        GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
+
+  typefind_class->have_type = gst_type_find_element_have_type;
+
+  g_object_class_install_property (gobject_class, ARG_CAPS,
+         g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"),
+         GST_TYPE_CAPS, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE));
+
+  gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
+          gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
+          G_TYPE_UINT, GST_TYPE_CAPS);
+
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
+}
+static void
+gst_type_find_element_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance);
+  
+  /* sinkpad */
+  typefind->sink = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink");
+  gst_pad_set_chain_function (typefind->sink,
+                             gst_type_find_element_chain);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
+  /* srcpad */
+  typefind->src = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src");
+  gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
+  gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
+
+  typefind->caps = NULL;
+  typefind->min_probability = 1;
+  typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
+
+  typefind->store = gst_buffer_store_new ();
+
+  GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE);
+}
+static void
+gst_type_find_element_dispose (GObject *object)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+
+  if (typefind->store) {
+    g_object_unref (typefind->store);
+    typefind->store = NULL;
+  }
+}
+static void
+gst_type_find_element_set_property (GObject *object, guint prop_id, 
+                                   const GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_MINIMUM:
+      typefind->min_probability = g_value_get_uint (value);
+      g_object_notify (object, "minimum");
+      break;
+    case ARG_MAXIMUM:
+      typefind->max_probability = g_value_get_uint (value);
+      g_object_notify (object, "maximum");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static void
+gst_type_find_element_get_property (GObject *object, guint prop_id, 
+                                   GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_CAPS:
+      g_value_set_boxed (value, typefind->caps);
+      break;
+    case ARG_MINIMUM:
+      g_value_set_uint (value, typefind->min_probability);
+      break;
+    case ARG_MAXIMUM:
+      g_value_set_uint (value, typefind->max_probability);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static const GstEventMask *
+gst_type_find_element_src_event_mask (GstPad *pad)
+{
+  static const GstEventMask mask[] = {
+    { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
+    /* add more if you want, event masks suck and need to die anyway */
+    { 0, }
+  };
+  
+  return mask;
+}
+static gboolean        
+gst_type_find_element_src_event (GstPad *pad, GstEvent *event)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    gst_data_unref (GST_DATA (event));
+    return FALSE;
+  }
+  return gst_pad_event_default (pad, event);
+}
+typedef struct {
+  GstTypeFindFactory * factory;
+  gint                 probability;
+  GstCaps *            caps;
+  gint64               requested_offset;
+  guint                        requested_size;
+
+  GList *              buffers;
+  GstTypeFindElement * self;
+} TypeFindEntry;
+static void
+free_entry_buffers (TypeFindEntry *entry)
+{
+  g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL);
+  g_list_free (entry->buffers);
+  entry->buffers = NULL;
+}
+static void
+free_entry (TypeFindEntry *entry)
+{
+  free_entry_buffers (entry);
+  
+  if (entry->caps)
+    gst_caps_unref (entry->caps);
+  g_free (entry);
+}
+static void
+start_typefinding (GstTypeFindElement *typefind)
+{
+  g_assert (typefind->caps == NULL);
+  g_assert (typefind->possibilities == NULL);
+  
+  GST_DEBUG_OBJECT (typefind, "starting typefinding");
+  typefind->mode = MODE_TYPEFIND; 
+  typefind->stream_length_available = TRUE; 
+  typefind->stream_length = 0; 
+}
+static void
+stop_typefinding (GstTypeFindElement *typefind)
+{
+  /* stop all typefinding and set mode back to normal */
+  gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING;
+  
+  GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : "");
+  if (typefind->possibilities != NULL) {
+    /* this should only happen on PAUSED => READY or EOS */
+    GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities));
+    g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
+    g_list_free (typefind->possibilities);
+    typefind->possibilities = NULL;
+  }
+
+  typefind->mode = MODE_NORMAL;
+
+  if (push_cached_buffers) {
+    GstBuffer *buffer;
+    guint size = gst_buffer_store_get_size (typefind->store, 0);
+    if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) {
+      gst_pad_push (typefind->src, GST_DATA (buffer));
+    } else {
+      size = 0;
+    }
+    GST_LOG_OBJECT (typefind, "seeking back to current position %u", size);
+    if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), 
+                            gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) {
+      GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size);
+    }
+  }
+  gst_buffer_store_clear (typefind->store);
+}
+static guint64
+find_element_get_length (gpointer data)
+{
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  GstTypeFindElement *typefind = entry->self;
+  GstFormat format = GST_FORMAT_BYTES;
+  
+  if (!typefind->stream_length_available) {
+    GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", 
+           GST_PLUGIN_FEATURE_NAME (entry->factory));
+    return 0;
+  }
+  if (entry->self->stream_length == 0) {
+    typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, 
+           &format, &entry->self->stream_length);
+    if (format != GST_FORMAT_BYTES)
+      typefind->stream_length_available = FALSE;
+    if (!typefind->stream_length_available) {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory));
+      return 0;
+    } else {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
+    }
+  }
+  
+  return entry->self->stream_length;
+}
+static void
+gst_type_find_element_handle_event (GstPad *pad, GstEvent *event)
+{
+  TypeFindEntry *entry;
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* this should only happen when we got all available data */
+      entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL;
+      if (entry && entry->probability >= typefind->min_probability) {
+       GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", 
+               GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+       g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+      }
+      stop_typefinding (typefind);
+      gst_pad_event_default (pad, event);
+      break;
+    default:
+      gst_data_unref (GST_DATA (event));
+      break;
+    }
+  } else {
+    gst_pad_event_default (pad, event);
+  }
+}
+static guint8 *
+find_peek (gpointer data, gint64 offset, guint size)
+{
+  GstBuffer *buf;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
+  if (offset >= 0) {
+    buf = gst_buffer_store_get_buffer (entry->self->store, offset, size);
+  } else {
+    /* FIXME: can we do this easily without querying length? */
+    guint64 length = find_element_get_length (data);
+
+    if (length == 0) {
+      buf = NULL;
+    } else {
+      buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size);
+    }
+  }
+
+  if (buf) {
+    entry->buffers = g_list_prepend (entry->buffers, buf);
+    return GST_BUFFER_DATA (buf);
+  } else {
+    if (entry->requested_size == 0) {
+      GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", 
+             offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory));
+      entry->requested_offset = offset;
+      entry->requested_size = size;
+    }
+    return NULL;
+  }
+}
+static void
+find_suggest (gpointer data, guint probability, GstCaps *caps)
+{
+  gchar *str;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  str = gst_caps_to_string (caps);
+  GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str);
+  g_free (str);
+  if (((gint) probability) > entry->probability) {
+    entry->probability = probability;
+    gst_caps_replace (&entry->caps, caps);
+  }
+}
+static gint
+compare_type_find_entry (gconstpointer a, gconstpointer b)
+{
+  TypeFindEntry *one = (TypeFindEntry *) a;
+  TypeFindEntry *two = (TypeFindEntry *) b;
+
+  if (one->probability == two->probability) {
+    /* FIXME: can be improved by analyzing requests */
+    return 0;
+  } else {
+    return two->probability - one->probability;
+  }
+}
+static gint
+compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
+{
+  return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
+}
+static void
+gst_type_find_element_chain (GstPad *pad, GstData *data)
+{
+  GstTypeFindElement *typefind;
+  GList *entries;
+  TypeFindEntry *entry;
+  GList *walk;
+  GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length };
+
+  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+  if (GST_IS_EVENT (data)) {
+    gst_type_find_element_handle_event (pad, GST_EVENT (data));
+    return;
+  }
+  switch (typefind->mode) {
+    case MODE_NORMAL:
+      gst_pad_push (typefind->src, data);
+      return;
+    case MODE_TYPEFIND: {
+      gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data));
+      gst_data_unref (data);
+      if (typefind->possibilities == NULL) {
+       /* not yet started, get all typefinding functions into our "queue" */
+       GList *all_factories = gst_type_find_factory_get_list ();
+       GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", 
+                        g_list_length ((GList *) all_factories));
+       
+       all_factories = g_list_sort (all_factories, compare_type_find_factory);
+       walk = all_factories;
+       while (all_factories) {
+         entry = g_new0 (TypeFindEntry, 1);
+         
+         entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
+         entry->self = typefind;
+         entry->probability = 0;
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+         all_factories = g_list_next (all_factories);
+       }
+       g_list_free (all_factories);
+      }
+      /* call every typefind function once */
+      walk = entries = typefind->possibilities;
+      GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries));
+      typefind->possibilities = NULL;
+      while (walk) {
+       find.data = entry = (TypeFindEntry *) walk->data;
+       walk = g_list_next (walk);
+       entry->probability = 0;
+       entry->requested_offset = 0;
+       entry->requested_size = 0;
+       gst_type_find_factory_call_function (entry->factory, &find);
+       free_entry_buffers (entry);
+       if (entry->probability == 0 && entry->requested_size == 0) {
+         GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory));
+         free_entry (entry);
+       } else if (entry->probability >= typefind->max_probability) {
+         /* wooha, got caps */
+         GstCaps *found_caps = entry->caps;
+         guint probability = entry->probability;
+         
+         gst_caps_ref (found_caps);
+         GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability);
+         while (walk) {
+           free_entry ((TypeFindEntry *) walk->data);
+           walk = g_list_next (walk);
+         }
+         walk = typefind->possibilities;
+         while (walk) {
+           free_entry (walk->data);
+           walk = g_list_next (walk);
+         }
+         typefind->possibilities = NULL;
+         g_list_free (typefind->possibilities); 
+         g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps);
+         gst_caps_unref (found_caps);
+       } else {
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+       }
+      }
+      g_list_free (entries);
+      /* we may now already have caps or we might be left without functions to try */
+      if (typefind->caps) {
+       stop_typefinding (typefind);
+      } else if (typefind->possibilities == NULL) {
+       gst_element_error (GST_ELEMENT (typefind), "media type could not be detected");
+      } else {
+       /* set up typefind element for next iteration */
+       typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry);
+       
+       walk = typefind->possibilities;
+       while (walk) {
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size > 0) {
+           /* FIXME: need heuristic to find out if we should seek */
+           gint64 seek_offset;
+           GstEvent *event;
+
+           seek_offset = entry->requested_offset > 0 ? entry->requested_offset : 
+                         find_element_get_length (entry) + entry->requested_offset;
+           seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset);
+           event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset);
+           if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) {
+             /* done seeking */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             break;
+           } else if (entry->requested_offset < 0) {
+             /* impossible to seek */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             entry->requested_size = 0;
+             entry->requested_offset = 0;
+           }
+         }
+       }
+       /* throw out all entries that can't get more data */
+       walk = g_list_next (typefind->possibilities);
+       while (walk) {
+         GList *cur = walk;
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size == 0) {
+           GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory));
+           free_entry (entry);
+           typefind->possibilities = g_list_delete_link (typefind->possibilities, cur);
+         }
+       }
+       if (g_list_next (typefind->possibilities) == NULL) {
+         entry = (TypeFindEntry *) typefind->possibilities->data;
+         if (entry->probability > typefind->min_probability) {
+           GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+           free_entry (entry);
+           g_list_free (typefind->possibilities);
+           typefind->possibilities = NULL;
+           stop_typefinding (typefind);
+         }
+       }
+      }
+      break;
+    }
+    default:
+      g_assert_not_reached ();
+      return;
+  }
+}
+static GstElementStateReturn
+gst_type_find_element_change_state (GstElement *element)
+{
+  GstTypeFindElement *typefind;
+
+  typefind = GST_TYPE_FIND_ELEMENT (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_READY_TO_PAUSED:
+      start_typefinding (typefind);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      stop_typefinding (typefind);
+      gst_caps_replace (&typefind->caps, NULL);
+      break;
+    default:
+      break;
+  }
+  
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+}
diff --git a/plugins/elements/gsttypefind.h b/plugins/elements/gsttypefind.h
new file mode 100644 (file)
index 0000000..4b5e0cc
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: element that detects type of stream
+ *
+ * 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_TYPE_FIND_ELEMENT_H__
+#define __GST_TYPE_FIND_ELEMENT_H__
+
+#include <gst/gstinfo.h>
+#include <gst/gstelement.h>
+/* #include <gst/gstbufferstore.h> */
+#include "gstbufferstore.h"
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug);
+
+extern GstElementDetails gst_type_find_element_details;
+
+#define GST_TYPE_TYPE_FIND_ELEMENT             (gst_type_find_element_get_type ())
+#define GST_TYPE_FIND_ELEMENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement))
+#define GST_IS_TYPE_FIND_ELEMENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+
+typedef struct _GstTypeFindElement             GstTypeFindElement;
+typedef struct _GstTypeFindElementClass        GstTypeFindElementClass;
+
+struct _GstTypeFindElement {
+  GstElement           element;
+
+  GstPad *             sink;
+  GstPad *             src;
+
+  guint                        min_probability;
+  guint                        max_probability;
+  GstCaps *            caps;
+
+  guint                        mode;
+  GstBufferStore *     store;
+  guint64              stream_length;
+  gboolean             stream_length_available;
+  
+  GList *              possibilities;
+};
+
+struct _GstTypeFindElementClass {
+  GstElementClass      parent_class;
+
+  /* signals */
+  void                         (*have_type)    (GstTypeFindElement *element,
+                                        guint          probability,
+                                        GstCaps *      caps);
+};
+
+GType gst_type_find_element_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_TYPE_FIND_ELEMENT_H__ */
diff --git a/plugins/elements/gsttypefindelement.c b/plugins/elements/gsttypefindelement.c
new file mode 100644 (file)
index 0000000..bd5e3d3
--- /dev/null
@@ -0,0 +1,669 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.c: element that detects type of stream
+ *
+ * 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.
+ */
+
+/* FIXME: need a better solution for non-seekable streams */
+
+/* way of operation:
+ * 1) get a list of all typefind functions sorted best to worst
+ * 2) if all elements have been called with all requested data goto 8
+ * 3) call all functions once with all available data
+ * 4) if a function returns a value >= ARG_MAXIMUM goto 8
+ * 5) all functions with a result > ARG_MINIMUM or functions that did not get
+ *    all requested data (where peek returned NULL) stay in list
+ * 6) seek to requested offset of best function that still has open data 
+ *    requests
+ * 7) goto 2
+ * 8) take best available result and use its caps
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gsttypefindelement.h"
+#include "gst/gst_private.h"
+
+#include <gst/gsttypefind.h>
+
+GST_DEBUG_CATEGORY (gst_type_find_element_debug);
+#define GST_CAT_DEFAULT gst_type_find_element_debug
+
+GstElementDetails gst_type_find_element_details = {
+  "TypeFind",
+  "Generic",
+  "LGPL",
+  "Finds the media type of a stream",
+  VERSION,
+  "Benjamin Otte <in7y118@public.uni-hamburg.de>",
+  "(C) 2003",
+};
+
+/* generic templates */
+GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory,
+  "sink",
+  GST_PAD_SINK,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory,
+  "src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_CAPS_ANY
+);
+
+/* TypeFind signals and args */
+enum {
+  HAVE_TYPE,
+  LAST_SIGNAL
+};
+enum {
+  ARG_0,
+  ARG_CAPS,
+  ARG_MINIMUM,
+  ARG_MAXIMUM
+};
+enum {
+  MODE_NORMAL, /* act as identity */
+  MODE_TYPEFIND, /* do typefinding */
+};
+
+
+static void    gst_type_find_element_class_init        (gpointer       g_class,
+                                                        gpointer       class_data);
+static void    gst_type_find_element_init              (GTypeInstance *instance,
+                                                        gpointer       g_class);
+static void    gst_type_find_element_dispose           (GObject *      object);
+static void    gst_type_find_element_set_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        const GValue * value, 
+                                                        GParamSpec *   pspec);
+static void    gst_type_find_element_get_property      (GObject *      object, 
+                                                        guint          prop_id,
+                                                        GValue *       value, 
+                                                        GParamSpec *   pspec);
+
+static const GstEventMask *
+                gst_type_find_element_src_event_mask    (GstPad *      pad);
+static gboolean        gst_type_find_element_src_event         (GstPad *       pad,
+                                                        GstEvent *     event);
+
+static void    gst_type_find_element_chain             (GstPad *       sinkpad,
+                                                        GstData *      data);
+static GstElementStateReturn
+               gst_type_find_element_change_state      (GstElement *   element);
+
+static GstElementClass *parent_class = NULL;
+static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_type_find_element_get_type (void)
+{
+  static GType typefind_type = 0;
+
+  if (!typefind_type) {
+    static const GTypeInfo typefind_info = {
+      sizeof (GstTypeFindElementClass),
+      NULL,
+      NULL,
+      gst_type_find_element_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTypeFindElement),
+      0,
+      gst_type_find_element_init,
+      NULL
+    };
+    typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
+                                           "GstTypeFindElement",
+                                           &typefind_info, 0);
+  }
+  return typefind_type;
+}
+static void
+gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps)
+{
+  gchar *caps_str;
+  
+  g_assert (typefind->caps == NULL);
+  g_assert (caps != NULL);
+
+  caps_str = gst_caps_to_string (caps);
+  GST_INFO_OBJECT (typefind, "found caps %s", caps_str);
+  g_free (caps_str);
+  gst_caps_replace (&typefind->caps, caps);
+  if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) {
+    gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad");
+  }
+}
+static void
+gst_type_find_element_class_init (gpointer g_class, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstTypeFindElementClass *typefind_class;
+
+  gobject_class = G_OBJECT_CLASS (g_class);
+  gstelement_class = GST_ELEMENT_CLASS (g_class);
+  typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class);
+
+  parent_class = g_type_class_peek_parent (g_class);
+
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
+  gobject_class->dispose      =        GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
+
+  typefind_class->have_type = gst_type_find_element_have_type;
+
+  g_object_class_install_property (gobject_class, ARG_CAPS,
+         g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"),
+         GST_TYPE_CAPS, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_MINIMUM,
+         g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding",
+         GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE));
+
+  gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", 
+         G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
+          G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
+          gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
+          G_TYPE_UINT, GST_TYPE_CAPS);
+
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
+}
+static void
+gst_type_find_element_init (GTypeInstance *instance, gpointer g_class)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance);
+  
+  /* sinkpad */
+  typefind->sink = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink");
+  gst_pad_set_chain_function (typefind->sink,
+                             gst_type_find_element_chain);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
+  /* srcpad */
+  typefind->src = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src");
+  gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
+  gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask);
+  gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
+
+  typefind->caps = NULL;
+  typefind->min_probability = 1;
+  typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
+
+  typefind->store = gst_buffer_store_new ();
+
+  GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE);
+}
+static void
+gst_type_find_element_dispose (GObject *object)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+
+  if (typefind->store) {
+    g_object_unref (typefind->store);
+    typefind->store = NULL;
+  }
+}
+static void
+gst_type_find_element_set_property (GObject *object, guint prop_id, 
+                                   const GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_MINIMUM:
+      typefind->min_probability = g_value_get_uint (value);
+      g_object_notify (object, "minimum");
+      break;
+    case ARG_MAXIMUM:
+      typefind->max_probability = g_value_get_uint (value);
+      g_object_notify (object, "maximum");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static void
+gst_type_find_element_get_property (GObject *object, guint prop_id, 
+                                   GValue *value, GParamSpec *pspec)
+{
+  GstTypeFindElement *typefind;
+
+  g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
+
+  typefind = GST_TYPE_FIND_ELEMENT (object);
+
+  switch (prop_id) {
+    case ARG_CAPS:
+      g_value_set_boxed (value, typefind->caps);
+      break;
+    case ARG_MINIMUM:
+      g_value_set_uint (value, typefind->min_probability);
+      break;
+    case ARG_MAXIMUM:
+      g_value_set_uint (value, typefind->max_probability);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static const GstEventMask *
+gst_type_find_element_src_event_mask (GstPad *pad)
+{
+  static const GstEventMask mask[] = {
+    { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
+    /* add more if you want, event masks suck and need to die anyway */
+    { 0, }
+  };
+  
+  return mask;
+}
+static gboolean        
+gst_type_find_element_src_event (GstPad *pad, GstEvent *event)
+{
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    gst_data_unref (GST_DATA (event));
+    return FALSE;
+  }
+  return gst_pad_event_default (pad, event);
+}
+typedef struct {
+  GstTypeFindFactory * factory;
+  gint                 probability;
+  GstCaps *            caps;
+  gint64               requested_offset;
+  guint                        requested_size;
+
+  GList *              buffers;
+  GstTypeFindElement * self;
+} TypeFindEntry;
+static void
+free_entry_buffers (TypeFindEntry *entry)
+{
+  g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL);
+  g_list_free (entry->buffers);
+  entry->buffers = NULL;
+}
+static void
+free_entry (TypeFindEntry *entry)
+{
+  free_entry_buffers (entry);
+  
+  if (entry->caps)
+    gst_caps_unref (entry->caps);
+  g_free (entry);
+}
+static void
+start_typefinding (GstTypeFindElement *typefind)
+{
+  g_assert (typefind->caps == NULL);
+  g_assert (typefind->possibilities == NULL);
+  
+  GST_DEBUG_OBJECT (typefind, "starting typefinding");
+  typefind->mode = MODE_TYPEFIND; 
+  typefind->stream_length_available = TRUE; 
+  typefind->stream_length = 0; 
+}
+static void
+stop_typefinding (GstTypeFindElement *typefind)
+{
+  /* stop all typefinding and set mode back to normal */
+  gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING;
+  
+  GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : "");
+  if (typefind->possibilities != NULL) {
+    /* this should only happen on PAUSED => READY or EOS */
+    GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities));
+    g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
+    g_list_free (typefind->possibilities);
+    typefind->possibilities = NULL;
+  }
+
+  typefind->mode = MODE_NORMAL;
+
+  if (push_cached_buffers) {
+    GstBuffer *buffer;
+    guint size = gst_buffer_store_get_size (typefind->store, 0);
+    if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) {
+      gst_pad_push (typefind->src, GST_DATA (buffer));
+    } else {
+      size = 0;
+    }
+    GST_LOG_OBJECT (typefind, "seeking back to current position %u", size);
+    if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), 
+                            gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) {
+      GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size);
+    }
+  }
+  gst_buffer_store_clear (typefind->store);
+}
+static guint64
+find_element_get_length (gpointer data)
+{
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  GstTypeFindElement *typefind = entry->self;
+  GstFormat format = GST_FORMAT_BYTES;
+  
+  if (!typefind->stream_length_available) {
+    GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", 
+           GST_PLUGIN_FEATURE_NAME (entry->factory));
+    return 0;
+  }
+  if (entry->self->stream_length == 0) {
+    typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, 
+           &format, &entry->self->stream_length);
+    if (format != GST_FORMAT_BYTES)
+      typefind->stream_length_available = FALSE;
+    if (!typefind->stream_length_available) {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory));
+      return 0;
+    } else {
+      GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", 
+             GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
+    }
+  }
+  
+  return entry->self->stream_length;
+}
+static void
+gst_type_find_element_handle_event (GstPad *pad, GstEvent *event)
+{
+  TypeFindEntry *entry;
+  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+
+  if (typefind->mode == MODE_TYPEFIND) {
+    /* need to do more? */
+    switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* this should only happen when we got all available data */
+      entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL;
+      if (entry && entry->probability >= typefind->min_probability) {
+       GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", 
+               GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+       g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+      }
+      stop_typefinding (typefind);
+      gst_pad_event_default (pad, event);
+      break;
+    default:
+      gst_data_unref (GST_DATA (event));
+      break;
+    }
+  } else {
+    gst_pad_event_default (pad, event);
+  }
+}
+static guint8 *
+find_peek (gpointer data, gint64 offset, guint size)
+{
+  GstBuffer *buf;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
+  if (offset >= 0) {
+    buf = gst_buffer_store_get_buffer (entry->self->store, offset, size);
+  } else {
+    /* FIXME: can we do this easily without querying length? */
+    guint64 length = find_element_get_length (data);
+
+    if (length == 0) {
+      buf = NULL;
+    } else {
+      buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size);
+    }
+  }
+
+  if (buf) {
+    entry->buffers = g_list_prepend (entry->buffers, buf);
+    return GST_BUFFER_DATA (buf);
+  } else {
+    if (entry->requested_size == 0) {
+      GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", 
+             offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory));
+      entry->requested_offset = offset;
+      entry->requested_size = size;
+    }
+    return NULL;
+  }
+}
+static void
+find_suggest (gpointer data, guint probability, GstCaps *caps)
+{
+  gchar *str;
+  TypeFindEntry *entry = (TypeFindEntry *) data;
+  
+  str = gst_caps_to_string (caps);
+  GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", 
+         GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str);
+  g_free (str);
+  if (((gint) probability) > entry->probability) {
+    entry->probability = probability;
+    gst_caps_replace (&entry->caps, caps);
+  }
+}
+static gint
+compare_type_find_entry (gconstpointer a, gconstpointer b)
+{
+  TypeFindEntry *one = (TypeFindEntry *) a;
+  TypeFindEntry *two = (TypeFindEntry *) b;
+
+  if (one->probability == two->probability) {
+    /* FIXME: can be improved by analyzing requests */
+    return 0;
+  } else {
+    return two->probability - one->probability;
+  }
+}
+static gint
+compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
+{
+  return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
+}
+static void
+gst_type_find_element_chain (GstPad *pad, GstData *data)
+{
+  GstTypeFindElement *typefind;
+  GList *entries;
+  TypeFindEntry *entry;
+  GList *walk;
+  GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length };
+
+  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
+  if (GST_IS_EVENT (data)) {
+    gst_type_find_element_handle_event (pad, GST_EVENT (data));
+    return;
+  }
+  switch (typefind->mode) {
+    case MODE_NORMAL:
+      gst_pad_push (typefind->src, data);
+      return;
+    case MODE_TYPEFIND: {
+      gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data));
+      gst_data_unref (data);
+      if (typefind->possibilities == NULL) {
+       /* not yet started, get all typefinding functions into our "queue" */
+       GList *all_factories = gst_type_find_factory_get_list ();
+       GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", 
+                        g_list_length ((GList *) all_factories));
+       
+       all_factories = g_list_sort (all_factories, compare_type_find_factory);
+       walk = all_factories;
+       while (all_factories) {
+         entry = g_new0 (TypeFindEntry, 1);
+         
+         entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
+         entry->self = typefind;
+         entry->probability = 0;
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+         all_factories = g_list_next (all_factories);
+       }
+       g_list_free (all_factories);
+      }
+      /* call every typefind function once */
+      walk = entries = typefind->possibilities;
+      GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries));
+      typefind->possibilities = NULL;
+      while (walk) {
+       find.data = entry = (TypeFindEntry *) walk->data;
+       walk = g_list_next (walk);
+       entry->probability = 0;
+       entry->requested_offset = 0;
+       entry->requested_size = 0;
+       gst_type_find_factory_call_function (entry->factory, &find);
+       free_entry_buffers (entry);
+       if (entry->probability == 0 && entry->requested_size == 0) {
+         GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory));
+         free_entry (entry);
+       } else if (entry->probability >= typefind->max_probability) {
+         /* wooha, got caps */
+         GstCaps *found_caps = entry->caps;
+         guint probability = entry->probability;
+         
+         gst_caps_ref (found_caps);
+         GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", 
+                 GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability);
+         while (walk) {
+           free_entry ((TypeFindEntry *) walk->data);
+           walk = g_list_next (walk);
+         }
+         walk = typefind->possibilities;
+         while (walk) {
+           free_entry (walk->data);
+           walk = g_list_next (walk);
+         }
+         typefind->possibilities = NULL;
+         g_list_free (typefind->possibilities); 
+         g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps);
+         gst_caps_unref (found_caps);
+       } else {
+         typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
+       }
+      }
+      g_list_free (entries);
+      /* we may now already have caps or we might be left without functions to try */
+      if (typefind->caps) {
+       stop_typefinding (typefind);
+      } else if (typefind->possibilities == NULL) {
+       gst_element_error (GST_ELEMENT (typefind), "media type could not be detected");
+      } else {
+       /* set up typefind element for next iteration */
+       typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry);
+       
+       walk = typefind->possibilities;
+       while (walk) {
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size > 0) {
+           /* FIXME: need heuristic to find out if we should seek */
+           gint64 seek_offset;
+           GstEvent *event;
+
+           seek_offset = entry->requested_offset > 0 ? entry->requested_offset : 
+                         find_element_get_length (entry) + entry->requested_offset;
+           seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset);
+           event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset);
+           if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) {
+             /* done seeking */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             break;
+           } else if (entry->requested_offset < 0) {
+             /* impossible to seek */
+             GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, 
+                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
+             entry->requested_size = 0;
+             entry->requested_offset = 0;
+           }
+         }
+       }
+       /* throw out all entries that can't get more data */
+       walk = g_list_next (typefind->possibilities);
+       while (walk) {
+         GList *cur = walk;
+         entry = (TypeFindEntry *) walk->data;
+         walk = g_list_next (walk);
+         if (entry->requested_size == 0) {
+           GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory));
+           free_entry (entry);
+           typefind->possibilities = g_list_delete_link (typefind->possibilities, cur);
+         }
+       }
+       if (g_list_next (typefind->possibilities) == NULL) {
+         entry = (TypeFindEntry *) typefind->possibilities->data;
+         if (entry->probability > typefind->min_probability) {
+           GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", 
+                   GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
+           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
+           free_entry (entry);
+           g_list_free (typefind->possibilities);
+           typefind->possibilities = NULL;
+           stop_typefinding (typefind);
+         }
+       }
+      }
+      break;
+    }
+    default:
+      g_assert_not_reached ();
+      return;
+  }
+}
+static GstElementStateReturn
+gst_type_find_element_change_state (GstElement *element)
+{
+  GstTypeFindElement *typefind;
+
+  typefind = GST_TYPE_FIND_ELEMENT (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_READY_TO_PAUSED:
+      start_typefinding (typefind);
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      stop_typefinding (typefind);
+      gst_caps_replace (&typefind->caps, NULL);
+      break;
+    default:
+      break;
+  }
+  
+  return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+}
diff --git a/plugins/elements/gsttypefindelement.h b/plugins/elements/gsttypefindelement.h
new file mode 100644 (file)
index 0000000..4b5e0cc
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttypefind.h: element that detects type of stream
+ *
+ * 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_TYPE_FIND_ELEMENT_H__
+#define __GST_TYPE_FIND_ELEMENT_H__
+
+#include <gst/gstinfo.h>
+#include <gst/gstelement.h>
+/* #include <gst/gstbufferstore.h> */
+#include "gstbufferstore.h"
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug);
+
+extern GstElementDetails gst_type_find_element_details;
+
+#define GST_TYPE_TYPE_FIND_ELEMENT             (gst_type_find_element_get_type ())
+#define GST_TYPE_FIND_ELEMENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement))
+#define GST_IS_TYPE_FIND_ELEMENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT))
+#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass))
+
+typedef struct _GstTypeFindElement             GstTypeFindElement;
+typedef struct _GstTypeFindElementClass        GstTypeFindElementClass;
+
+struct _GstTypeFindElement {
+  GstElement           element;
+
+  GstPad *             sink;
+  GstPad *             src;
+
+  guint                        min_probability;
+  guint                        max_probability;
+  GstCaps *            caps;
+
+  guint                        mode;
+  GstBufferStore *     store;
+  guint64              stream_length;
+  gboolean             stream_length_available;
+  
+  GList *              possibilities;
+};
+
+struct _GstTypeFindElementClass {
+  GstElementClass      parent_class;
+
+  /* signals */
+  void                         (*have_type)    (GstTypeFindElement *element,
+                                        guint          probability,
+                                        GstCaps *      caps);
+};
+
+GType gst_type_find_element_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_TYPE_FIND_ELEMENT_H__ */
index c92f72bf74834c984af7d3a25dc7cca6b51bd191..f2dbdfb5cd33c605d2d3019577b7aee48bc2eb87 100644 (file)
@@ -112,18 +112,11 @@ print_props (GstProps *properties, const gchar *pfx)
 }
 
 static void
-print_caps (const GstCaps *caps, const gchar *pfx)
+print_caps (GstCaps *caps, const gchar *pfx)
 {
   while (caps) {
-    GstType *type;
-
     g_print ("%s'%s': (%sfixed)\n", pfx, caps->name, (GST_CAPS_IS_FIXED (caps) ? "" : "NOT "));
-
-    type = gst_type_find_by_id (caps->id);
-    if (type)
-      g_print ("%s  MIME type: '%s':\n", pfx, type->mime);
-    else
-      g_print ("%s  MIME type: 'unknown/unknown':\n", pfx);
+    g_print ("%s  MIME type: '%s':\n", pfx, gst_caps_get_mime (caps));
 
     if (caps->properties) {
       gchar *prefix = g_strdup_printf ("%s  ", pfx);
@@ -863,20 +856,19 @@ print_element_list (void)
                 GST_PLUGIN_FEATURE_NAME (factory), factory->longdesc);
       }
 #endif
-      else if (GST_IS_TYPE_FACTORY (feature)) {
-        GstTypeFactory *factory;
-
-        factory = GST_TYPE_FACTORY (feature);
-        if (factory->exts)
-          g_print ("%s type:  %s: %s\n", plugin->name,
-                  factory->mime, factory->exts);
-        else
-          g_print ("%s type:  %s: N/A\n", plugin->name,
-                  factory->mime);
-
-        if (factory->typefindfunc)
-          g_print ("      Has typefind function: %s\n",
-                  GST_DEBUG_FUNCPTR_NAME (factory->typefindfunc));
+      else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+        GstTypeFindFactory *factory;
+
+        factory = GST_TYPE_FIND_FACTORY (feature);
+        if (factory->extensions) {
+         guint i;
+          g_print ("%s type: ", plugin->name);
+         while (factory->extensions[i]) {
+           g_print ("%s%s", i > 0 ? ", " : "", factory->extensions[i]);
+           i++;
+         }
+       } else
+          g_print ("%s type: N/A\n", plugin->name);
       }
       else if (GST_IS_SCHEDULER_FACTORY (feature)) {
         GstSchedulerFactory *factory;
@@ -957,15 +949,20 @@ print_plugin_info (GstPlugin *plugin)
       num_indexes++;
     }
 #endif
-    else if (GST_IS_TYPE_FACTORY (feature)) {
-      GstTypeFactory *factory;
-
-      factory = GST_TYPE_FACTORY (feature);
-      g_print ("  %s: %s\n", factory->mime, factory->exts);
+    else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+      GstTypeFindFactory *factory;
+
+      factory = GST_TYPE_FIND_FACTORY (feature);
+      if (factory->extensions) {
+       guint i;
+       g_print ("%s type: ", plugin->name);
+       while (factory->extensions[i]) {
+         g_print ("%s%s", i > 0 ? ", " : "", factory->extensions[i]);
+         i++;
+       }
+      } else
+       g_print ("%s type: N/A\n", plugin->name);
 
-      if (factory->typefindfunc)
-        g_print ("      Has typefind function: %s\n",
-                GST_DEBUG_FUNCPTR_NAME (factory->typefindfunc));
       num_types++;
     }
     else if (GST_IS_SCHEDULER_FACTORY (feature)) {
@@ -1066,9 +1063,9 @@ main (int argc, char *argv[])
          }
 #endif
          feature = gst_registry_pool_find_feature (argv[1],
-                                                   GST_TYPE_TYPE_FACTORY);
+                                                   GST_TYPE_TYPE_FIND_FACTORY);
          if (feature) {
-           g_print ("%s: an type\n", argv[1]);
+           g_print ("%s: a typefind function\n", argv[1]);
            return 0;
          }
 #ifndef GST_DISABLE_URI
index e3828a4fdc5bbce45dd29d05611b9638aa80c2fb..a58401599670cb8cf5bd2adf0d2b07aeb7a19826 100644 (file)
@@ -132,7 +132,7 @@ int main (int argc,char *argv[])
 
     dir_list = gst_registry_get_path_list(registry);
     for(iter = dir_list; iter; iter = iter->next) {
-      dir = g_build_filename((const char *)iter->data, "register-scripts");
+      dir = g_build_filename((const char *)iter->data, "register-scripts", NULL);
       spawn_all_in_dir(dir);
       g_free(dir);
     }
index fd65713622629fa0b081eb9ba92b32ddbd08e7fe..fc0551ac4761109d3dd074ecf2baaf32cacce185 100644 (file)
  **/
 
 gboolean FOUND = FALSE;
-int iterations;
-int max_iterations = 100;
+gchar *filename = NULL;
 
 void
-gst_caps_print (GstCaps *caps)
+gst_caps_print (const char *filename, GstCaps *caps)
 {
-  g_print ("%s\n", gst_caps_to_string (caps));
+  gchar *caps_str = gst_caps_to_string (caps);
+  g_print ("%s - %s\n", filename, caps_str);
+  g_free (caps_str);
 }
 
 void
-have_type_handler (GstElement *typefind, gpointer data)
+have_type_handler (GstElement *typefind, guint probability, GstCaps *caps, gpointer unused)
 {
-  GstCaps *caps = (GstCaps *) data;
-  gst_caps_print (caps);
+  gst_caps_print (filename, caps);
   FOUND = TRUE;
 }
 
 int
 main (int argc, char *argv[])
 {
+  guint i = 1;
   GstElement *pipeline;
   GstElement *source, *typefind;
 
@@ -41,12 +42,12 @@ main (int argc, char *argv[])
 
   if (argc < 2) { 
     g_print ("Please give a filename to typefind\n\n");
-    exit (1);
+    return 1;
   }
+  
   pipeline = gst_pipeline_new (NULL);
   source = gst_element_factory_make ("filesrc", "source");
   g_assert (GST_IS_ELEMENT (source));
-  g_object_set (source, "location", argv[1], NULL);
   typefind = gst_element_factory_make ("typefind", "typefind");
   g_assert (GST_IS_ELEMENT (typefind));
   gst_bin_add_many (GST_BIN (pipeline), source, typefind, NULL);
@@ -54,19 +55,23 @@ main (int argc, char *argv[])
   g_signal_connect (G_OBJECT (typefind), "have-type", 
                    G_CALLBACK (have_type_handler), NULL);
 
-  /* set to play */
-  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+  while (i < argc) {
+    FOUND = FALSE;
+    gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+    filename = argv[i];
+    g_object_set (source, "location", filename, NULL);
+    /* set to play */
+    gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
 
-  while (!FOUND){
-    gst_bin_iterate (GST_BIN (pipeline));
-    iterations++;
-    if(iterations >= max_iterations){
-      break;
+    while (!FOUND) { 
+      if (!gst_bin_iterate (GST_BIN (pipeline)))
+       break;
     }
+    if (!FOUND) {
+      g_print ("%s - No type found\n", argv[i]);
+    }
+    i++;
   }
-  if (!FOUND) {
-    g_print ("No type found\n");
-    return 1;
-  }
+  g_object_unref (pipeline);
   return 0;
 }
index f6620e8ae5b3d804ae423d5ef7697dae5b7c9170..81052e891ab412b894b279df5ea4ba2a000b8701 100644 (file)
@@ -159,7 +159,7 @@ print_props (GstProps *properties, gint pfx)
 }
 
 static void
-print_caps (const GstCaps *caps, gint pfx)
+print_caps (GstCaps *caps, gint pfx)
 {
   if (!caps)
     return;
@@ -167,16 +167,10 @@ print_caps (const GstCaps *caps, gint pfx)
   PUT_START_TAG (pfx, "capscomp");
 
   while (caps) {
-    GstType *type;
-
     PUT_START_TAG (pfx + 1, "caps");
     PUT_ESCAPED (pfx + 2, "name", caps->name);
 
-    type = gst_type_find_by_id (caps->id);
-    if (type)
-      PUT_ESCAPED (pfx + 2, "type", type->mime);
-    else
-      PUT_ESCAPED (pfx + 2, "type", "unkown/unknown");
+    PUT_ESCAPED (pfx + 2, "type", gst_caps_get_mime (caps));
 
     if (caps->properties) {
       print_props(caps->properties, pfx + 2);
@@ -855,16 +849,19 @@ print_element_list (void)
                 GST_PLUGIN_FEATURE_NAME (factory), factory->longdesc);
       }
 #endif
-      else if (GST_IS_TYPE_FACTORY (feature)) {
-        GstTypeFactory *factory;
-
-        factory = GST_TYPE_FACTORY (feature);
-        g_print ("%s type:  %s: %s\n", plugin->name,
-                factory->mime, factory->exts);
-
-        if (factory->typefindfunc)
-          g_print ("      Has typefind function: %s\n",
-                  GST_DEBUG_FUNCPTR_NAME (factory->typefindfunc));
+      else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+        GstTypeFindFactory *factory;
+
+       factory = GST_TYPE_FIND_FACTORY (feature);
+        if (factory->extensions) {
+         guint i;
+         g_print ("%s type: ", plugin->name);
+         while (factory->extensions[i]) {
+           g_print ("%s%s", i > 0 ? ", " : "", factory->extensions[i]);
+           i++;
+         }
+       } else
+         g_print ("%s type: N/A\n", plugin->name);
       }
       else if (GST_IS_SCHEDULER_FACTORY (feature)) {
         GstSchedulerFactory *factory;
@@ -945,15 +942,19 @@ print_plugin_info (GstPlugin *plugin)
       num_indexes++;
     }
 #endif
-    else if (GST_IS_TYPE_FACTORY (feature)) {
-      GstTypeFactory *factory;
-
-      factory = GST_TYPE_FACTORY (feature);
-      g_print ("  %s: %s\n", factory->mime, factory->exts);
-
-      if (factory->typefindfunc)
-        g_print ("      Has typefind function: %s\n",
-                GST_DEBUG_FUNCPTR_NAME (factory->typefindfunc));
+    else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
+      GstTypeFindFactory *factory;
+
+      factory = GST_TYPE_FIND_FACTORY (feature);
+      if (factory->extensions) {
+       guint i;
+       g_print ("%s type: ", plugin->name);
+       while (factory->extensions[i]) {
+         g_print ("%s%s", i > 0 ? ", " : "", factory->extensions[i]);
+         i++;
+       }
+      } else
+       g_print ("%s type: N/A\n", plugin->name);
       num_types++;
     }
     else if (GST_IS_SCHEDULER_FACTORY (feature)) {
@@ -1059,9 +1060,9 @@ main (int argc, char *argv[])
          }
 #endif
          feature = gst_registry_pool_find_feature (argv[1],
-                                                   GST_TYPE_TYPE_FACTORY);
+                                                   GST_TYPE_TYPE_FIND_FACTORY);
          if (feature) {
-           g_print ("%s: an type\n", argv[1]);
+           g_print ("%s: a type find function\n", argv[1]);
            return 0;
          }
 #ifndef GST_DISABLE_URI