merge TYPEFIND branch. Major changes:

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
This commit is contained in:
Benjamin Otte 2003-10-28 20:25:30 +00:00
parent 208c0e3089
commit 3235f1d4c0
54 changed files with 5090 additions and 913 deletions

View 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

View 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;

View 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

View 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

View file

@ -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:

View file

@ -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:

View file

@ -1,23 +1,114 @@
<!-- ##### 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>
<!-- ##### VARIABLE gst_type_find_details ##### -->
<!-- ##### 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>
@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:

View file

@ -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:

View 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 \

View 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;
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));
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 {
gst_type_find_factory_call_function (factory, &gst_find);
if (find.best_probability >= GST_TYPE_FIND_MAXIMUM)
goto plug;
walk = g_list_next (walk);
}
}
factories = g_slist_next (factories);
}
type_list = g_list_next (type_list);
}
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;
}

View 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 \
gstfakesink.h \
gstfilesink.h \
gstfdsrc.h \
gstmultidisksrc.h \
gstfdsink.h \
gstpipefilter.h \
gsttee.h \
gstaggregator.h \
gstbufferstore.h \
gstfakesink.h \
gstfakesrc.h \
gstfdsink.h \
gstfdsrc.h \
gstfilesink.h \
gstfilesrc.h \
gstidentity.h \
gstmd5sink.h \
gstmultidisksrc.h \
gstpipefilter.h \
gstshaper.h \
gststatistics.h \
gstfilesrc.h \
gstmd5sink.h
gsttee.h \
gsttypefindelement.h

View file

@ -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;
}

View file

@ -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__ */

View file

@ -27,20 +27,21 @@
#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_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_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_identity_debug, "identity", 0, "identity 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;
}

View 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;
}
}

View 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,7 +343,7 @@ 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_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 */
@ -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;

669
gst/elements/gsttypefind.c Normal file
View file

@ -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);
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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 ();

View file

@ -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>

View 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;

View 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;
}

View 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);
caps->id = g_quark_from_string (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;
}
/**
* 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")) {

View 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);

View 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@

View 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);

View file

@ -9,4 +9,6 @@ VOID:OBJECT,POINTER
VOID:OBJECT,STRING
VOID:INT,INT
VOID:INT64
VOID:UINT,BOXED
BOOLEAN:VOID
BOOLEAN:POINTER

View 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) {

View 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);
}
}

View 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

View 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
@ -20,234 +19,291 @@
* Boston, MA 02111-1307, USA.
*/
#include "gst_private.h"
#include "gsttype.h"
#include "gstinfo.h"
#include "gsttypefind.h"
#include "gstregistrypool.h"
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",
};
GST_DEBUG_CATEGORY_STATIC (gst_type_find_debug);
#define GST_CAT_DEFAULT gst_type_find_debug
/* generic templates */
GST_PAD_TEMPLATE_FACTORY (type_find_sink_factory,
"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
NULL
);
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);
/* TypeFind signals and args */
enum {
HAVE_TYPE,
LAST_SIGNAL
};
static void gst_type_find_factory_unload_thyself (GstPluginFeature * feature);
enum {
ARG_0,
ARG_CAPS,
};
static void gst_type_find_load_plugin (GstTypeFind * find,
gpointer data);
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;
GstPluginFeatureClass *gstpluginfeature_class = GST_PLUGIN_FEATURE_CLASS (g_class);
GObjectClass *object_class = G_OBJECT_CLASS (g_class);
gobject_class = (GObjectClass*)klass;
gstelement_class = (GstElementClass*)klass;
parent_class = g_type_class_peek_parent (g_class);
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
object_class->dispose = gst_type_find_factory_dispose;
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);
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);
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (instance);
gst_element_set_loop_function (GST_ELEMENT (typefind),
gst_type_find_loopfunc);
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;
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (object);
g_return_if_fail (GST_IS_TYPE_FIND (object));
typefind = GST_TYPE_FIND (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_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
gst_type_find_factory_unload_thyself (GstPluginFeature *feature)
{
GstTypeFind *typefind;
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
g_return_if_fail (GST_IS_TYPE_FIND (object));
typefind = GST_TYPE_FIND (object);
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;
factory->function = gst_type_find_load_plugin;
factory->user_data = factory;
}
}
static void
gst_type_find_loopfunc (GstElement *element)
gst_type_find_load_plugin (GstTypeFind *find, gpointer data)
{
GstTypeFind *typefind;
const GList *type_list;
GstType *type;
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (data);
typefind = GST_TYPE_FIND (element);
GST_DEBUG_OBJECT (factory, "need to load typefind function %s", GST_PLUGIN_FEATURE_NAME (factory));
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");
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_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);
}
/* if we get here, nothing worked... :'(. */
gst_element_error (GST_ELEMENT (typefind),
"media type could not be detected");
}
static GstElementStateReturn
gst_type_find_change_state (GstElement *element)
/**
* 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)
{
GstTypeFind *typefind;
GstElementStateReturn ret;
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 (element);
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 (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;
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);
/* 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)
{
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);
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
if (factory->extensions)
g_strfreev (factory->extensions);
return ret;
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));
}
/*** 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)
{
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);
}

View 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
@ -24,51 +23,103 @@
#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 (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))
#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))
typedef struct _GstTypeFind GstTypeFind;
typedef struct _GstTypeFindClass GstTypeFindClass;
typedef struct _GstTypeFindFactory GstTypeFindFactory;
typedef struct _GstTypeFindFactoryClass GstTypeFindFactoryClass;
typedef void (* GstTypeFindFunction) (GstTypeFind *find, gpointer data);
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;
struct _GstTypeFind {
GstElement element;
/* private to the caller of the typefind function */
guint8 * (* peek) (gpointer data,
gint64 offset,
guint size);
void (* suggest) (gpointer data,
guint probability,
GstCaps * caps);
GstPad *sinkpad;
GstByteStream *bs;
gpointer data;
GstCaps *caps;
/* optional */
guint64 (* get_length) (gpointer data);
/* <private> */
GST_STRUCT_PADDING
};
struct _GstTypeFindFactory {
GstPluginFeature feature;
/* <private> */
GstTypeFindFunction function;
gchar ** extensions;
GstCaps * caps; /* FIXME: not yet saved in registry */
gpointer user_data;
GST_OBJECT_PADDING
};
struct _GstTypeFindClass {
GstElementClass parent_class;
/* signals */
void (*have_type) (GstElement *element,
GstCaps *caps);
struct _GstTypeFindFactoryClass {
GstPluginFeatureClass parent;
/* <private> */
GST_CLASS_PADDING
};
GType gst_type_find_get_type (void);
/* 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);
/* registration interface */
void gst_type_find_factory_register (GstPlugin * plugin,
const gchar * name,
guint rank,
GstTypeFindFunction func,
gchar ** extensions,
GstCaps * possible_caps,
gpointer data);
/* typefinding interface */
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 */
#endif /* __GST_TYPE_FIND_H__ */

View 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);

View 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,
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)) {

View file

@ -1,3 +1,3 @@
SUBDIRS = control getbits
SUBDIRS = control getbits bytestream
DIST_SUBDIRS = control getbits
DIST_SUBDIRS = control getbits bytestream

View file

@ -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)

View file

@ -28,7 +28,9 @@
#include <stdlib.h>
#include <gst/gstinfo.h>
#include <gst/gstbytestream.h>
#include <gst/gstplugin.h>
#include <gst/gstversion.h>
#include "bytestream.h"
GST_DEBUG_CATEGORY_STATIC(debug_bs);
#define GST_CAT_DEFAULT debug_bs
@ -76,8 +78,6 @@ 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);
@ -744,3 +744,19 @@ gst_bytestream_print_status (GstByteStream * bs)
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
};

View 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 \
gstfakesink.h \
gstfilesink.h \
gstfdsrc.h \
gstmultidisksrc.h \
gstfdsink.h \
gstpipefilter.h \
gsttee.h \
gstaggregator.h \
gstbufferstore.h \
gstfakesink.h \
gstfakesrc.h \
gstfdsink.h \
gstfdsrc.h \
gstfilesink.h \
gstfilesrc.h \
gstidentity.h \
gstmd5sink.h \
gstmultidisksrc.h \
gstpipefilter.h \
gstshaper.h \
gststatistics.h \
gstfilesrc.h \
gstmd5sink.h
gsttee.h \
gsttypefindelement.h

View file

@ -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;
}

View file

@ -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__ */

View file

@ -27,20 +27,21 @@
#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_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_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_identity_debug, "identity", 0, "identity 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;
}

View 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;
}
}

View 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,7 +343,7 @@ 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_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 */
@ -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;

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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__ */

View 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;
else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
GstTypeFindFactory *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));
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;
else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
GstTypeFindFactory *factory;
factory = GST_TYPE_FACTORY (feature);
g_print (" %s: %s\n", factory->mime, factory->exts);
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

View 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);
}

View file

@ -12,26 +12,27 @@
**/
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);
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){
if (!gst_bin_iterate (GST_BIN (pipeline)))
break;
}
}
if (!FOUND) {
g_print ("No type found\n");
return 1;
g_print ("%s - No type found\n", argv[i]);
}
i++;
}
g_object_unref (pipeline);
return 0;
}

View 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;
else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
GstTypeFindFactory *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));
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;
else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
GstTypeFindFactory *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));
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