From 3235f1d4c09e9298b843f87dfa2849bd1ea00db8 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 28 Oct 2003 20:25:30 +0000 Subject: [PATCH] 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 --- configure.ac | 6 +- docs/gst/gstreamer-docs.sgml | 10 +- docs/gst/gstreamer-sections.txt | 53 +- docs/gst/gstreamer.types | 2 +- docs/gst/tmpl/gsttype.sgml | 108 --- docs/gst/tmpl/gsttypefactory.sgml | 101 --- docs/gst/tmpl/gsttypefind.sgml | 103 ++- docs/gst/tmpl/gsttypefindfactory.sgml | 117 +++ gst/Makefile.am | 20 +- gst/autoplug/gstspideridentity.c | 115 +-- gst/elements/Makefile.am | 49 +- gst/elements/gstbufferstore.c | 465 ++++++++++++ gst/elements/gstbufferstore.h | 73 ++ gst/elements/gstelements.c | 43 +- gst/elements/gstfakesrc.c | 2 +- gst/elements/gstfilesrc.c | 65 +- gst/elements/gsttypefind.c | 669 ++++++++++++++++++ gst/elements/gsttypefind.h | 78 ++ gst/elements/gsttypefindelement.c | 669 ++++++++++++++++++ gst/elements/gsttypefindelement.h | 78 ++ gst/gst.c | 9 +- gst/gst.h | 2 +- gst/gst_private.h | 1 - gst/gstbuffer.c | 7 +- gst/gstcaps.c | 87 +-- gst/gstcaps.h | 7 +- gst/gstconfig.h.in | 4 - gst/gstelement.c | 2 +- gst/gstmarshal.list | 2 + gst/gstpad.c | 12 +- gst/gstpluginfeature.c | 24 +- gst/gstpluginfeature.h | 1 + gst/gsttypefind.c | 428 ++++++----- gst/gsttypefind.h | 115 ++- gst/gstutils.c | 6 +- gst/registries/gstxmlregistry.c | 82 ++- libs/gst/Makefile.am | 4 +- libs/gst/bytestream/Makefile.am | 10 + .../gst/bytestream/bytestream.c | 22 +- .../gst/bytestream/bytestream.h | 0 plugins/elements/Makefile.am | 49 +- plugins/elements/gstbufferstore.c | 465 ++++++++++++ plugins/elements/gstbufferstore.h | 73 ++ plugins/elements/gstelements.c | 43 +- plugins/elements/gstfakesrc.c | 2 +- plugins/elements/gstfilesrc.c | 65 +- plugins/elements/gsttypefind.c | 669 ++++++++++++++++++ plugins/elements/gsttypefind.h | 78 ++ plugins/elements/gsttypefindelement.c | 669 ++++++++++++++++++ plugins/elements/gsttypefindelement.h | 78 ++ tools/gst-inspect.c | 59 +- tools/gst-register.c | 2 +- tools/gst-typefind.c | 45 +- tools/gst-xmlinspect.c | 55 +- 54 files changed, 5090 insertions(+), 913 deletions(-) delete mode 100644 docs/gst/tmpl/gsttype.sgml delete mode 100644 docs/gst/tmpl/gsttypefactory.sgml create mode 100644 docs/gst/tmpl/gsttypefindfactory.sgml create mode 100644 gst/elements/gstbufferstore.c create mode 100644 gst/elements/gstbufferstore.h create mode 100644 gst/elements/gsttypefind.c create mode 100644 gst/elements/gsttypefind.h create mode 100644 gst/elements/gsttypefindelement.c create mode 100644 gst/elements/gsttypefindelement.h create mode 100644 libs/gst/bytestream/Makefile.am rename gst/gstbytestream.c => libs/gst/bytestream/bytestream.c (98%) rename gst/gstbytestream.h => libs/gst/bytestream/bytestream.h (100%) create mode 100644 plugins/elements/gstbufferstore.c create mode 100644 plugins/elements/gstbufferstore.h create mode 100644 plugins/elements/gsttypefind.c create mode 100644 plugins/elements/gsttypefind.h create mode 100644 plugins/elements/gsttypefindelement.c create mode 100644 plugins/elements/gsttypefindelement.h diff --git a/configure.ac b/configure.ac index 7a9f6a62f3..cffdc8ef90 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/docs/gst/gstreamer-docs.sgml b/docs/gst/gstreamer-docs.sgml index c3e6a2a388..9cc12a4462 100644 --- a/docs/gst/gstreamer-docs.sgml +++ b/docs/gst/gstreamer-docs.sgml @@ -29,8 +29,8 @@ - - + + @@ -40,7 +40,6 @@ @@ -118,9 +117,8 @@ with some more specialized elements. &GstScheduler; &GstSystemClock; &GstThread; - &GstType; - + &GstTypeFind; + &GstTypeFindFactory; &GstUri; &GstUtils; &GstXML; diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index 04a0f5b9bd..f641d0a2ef 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -1254,38 +1254,33 @@ gst_alloc_trace_flags_get_type
-gsttype -GstType -GstType -gst_type_register -gst_type_find_by_mime -gst_type_find_by_ext -gst_type_find_by_id -gst_type_get_list - +gsttypefind +Writing typefind functions +GstTypeFind +GstTypeFindFunction +gst_type_find_peek +gst_type_find_suggest +gst_type_find_get_length +gst_type_find_factory_register
-gsttypefactory -GstTypeFactory -GstTypeFactory -GstTypeDefinition -GstTypeFindFunc -gst_type_factory_new -gst_type_factory_find +gsttypefindfactory +GstTypeFindFactory +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 -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
@@ -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 diff --git a/docs/gst/gstreamer.types b/docs/gst/gstreamer.types index bca68fcce3..d561d86f1e 100644 --- a/docs/gst/gstreamer.types +++ b/docs/gst/gstreamer.types @@ -13,7 +13,7 @@ gst_thread_get_type gst_plugin_feature_get_type gst_autoplug_get_type gst_autoplug_factory_get_type -gst_type_factory_get_type +gst_type_find_factory_get_type gst_element_factory_get_type gst_scheduler_factory_get_type gst_scheduler_get_type diff --git a/docs/gst/tmpl/gsttype.sgml b/docs/gst/tmpl/gsttype.sgml deleted file mode 100644 index fbd9b8cbe8..0000000000 --- a/docs/gst/tmpl/gsttype.sgml +++ /dev/null @@ -1,108 +0,0 @@ - -GstType - - -Identifies the data - - - -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). - - - -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. - - - -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. - - - -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. - - - - -#GstTypeFactory - - - - -A type. - - -@id: -@mime: -@exts: -@factories: - - - - - - -@factory: -@Returns: - - - - - - - -@mime: -@Returns: - - - - - - - -@ext: -@Returns: - - - - - - - -@id: -@Returns: - - - - - - - -@Returns: - - diff --git a/docs/gst/tmpl/gsttypefactory.sgml b/docs/gst/tmpl/gsttypefactory.sgml deleted file mode 100644 index 5b65c24c35..0000000000 --- a/docs/gst/tmpl/gsttypefactory.sgml +++ /dev/null @@ -1,101 +0,0 @@ - -GstTypeFactory - - -Add types to plugins. - - - -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(). - - -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: - - static GstCaps* - avi_type_find (GstBuffer *buf, gpointer private) - { - gchar *data = GST_BUFFER_DATA (buf); - - if (strncmp (&data[0], "RIFF", 4)) return NULL; - if (strncmp (&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 (&avidefinition); - gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type)); - ... - } - - - - - -#GstPluginFeature, #GstPlugin - - - - -The struct with the typefactory information. - - - - - -The typedefinition structure. - - -@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. - - - -This is the function that will be called when a typefind has to be -performed by a plugin. - - -@bs: -@priv: private; don't touch -@Returns: A #GstCaps structure describing the type or NULL if the - type was not recognized by this function; - -@buf: the buffer with media on which to perform the typefind - - - - - - - -@definition: -@Returns: - - - - - - - -@name: -@Returns: - - diff --git a/docs/gst/tmpl/gsttypefind.sgml b/docs/gst/tmpl/gsttypefind.sgml index ad3f5ae74f..10ae8f64bd 100644 --- a/docs/gst/tmpl/gsttypefind.sgml +++ b/docs/gst/tmpl/gsttypefind.sgml @@ -1,23 +1,114 @@ -GstTypeFind +Writing typefind functions -Detect the mime type of a media stream +Using the type finding subsystem from plugins -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 file + command. + + +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. + + +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. + +a typefind function for Ogg data + +static void +ogg_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, "OggS", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, + gst_caps_new ("ogg_type_find", "application/ogg", NULL)); + } +}; + + - +GstTypeFactory - querying registered typefind +functions - + + +This structure is filled by the caller of the typefind function. Typefind +functions must treat this as an opaque structure. + + +@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. + + + +This is the prototype for a typefind function. + + +@find: The #GstTypeFind data +@data: the user defined data that was provided on +gst_type_find_factory_register() + + + +@find: +@offset: +@size: +@Returns: + + + + + + + +@find: +@probability: +@caps: + + + + + + + +@find: +@Returns: + + + + + + + +@plugin: +@name: +@rank: +@func: +@extensions: +@possible_caps: +@data: + diff --git a/docs/gst/tmpl/gsttypefindfactory.sgml b/docs/gst/tmpl/gsttypefindfactory.sgml new file mode 100644 index 0000000000..118cf5a508 --- /dev/null +++ b/docs/gst/tmpl/gsttypefindfactory.sgml @@ -0,0 +1,117 @@ + +GstTypeFindFactory + + +information about registered typefind functions + + + +These functions allow querying informations about registered typefind +functions. How to create and register these functions is described in +the section +"Writing typefind functions". + + +how to write a simple typefinder + +/* 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 >= 0 && offset + size <= find->size) { + return find->data + offset; + } + return NULL; +} +static void +my_suggest (gpointer data, guint probability, GstCaps *caps) +{ + MyTypeFind *find = (MyTypeFind *) data; + if (probability > find->probability) { + find->probability = probability; + gst_caps_replace (&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, &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, &gst_find); + } + g_list_free (type_list); + return find.caps; +}; + + + +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. + + + + +Writing typefind functions + + + + +Object that stores information about a typefind function + + + + + +Class belonging to #GstTypeFindFactory. + + +@parent: + + + + + + +@Returns: + + + + + + + +@factory: +@Returns: + + + + + + + +@factory: +@Returns: + + + + + + + +@factory: +@find: + + diff --git a/gst/Makefile.am b/gst/Makefile.am index f5082115e9..b2f44a6d77 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -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 \ diff --git a/gst/autoplug/gstspideridentity.c b/gst/autoplug/gstspideridentity.c index 110e97b4e0..15007e4028 100644 --- a/gst/autoplug/gstspideridentity.c +++ b/gst/autoplug/gstspideridentity.c @@ -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; + 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); - while (factories) { - GstTypeFactory *factory = GST_TYPE_FACTORY (factories->data); - GstTypeFindFunc typefindfunc = (GstTypeFindFunc)factory->typefindfunc; - - GST_DEBUG ("trying typefind function %s", GST_PLUGIN_FEATURE_NAME (factory)); - if (typefindfunc && (caps = typefindfunc (bs, factory))) { - GST_INFO ("typefind function %s found caps", GST_PLUGIN_FEATURE_NAME (factory)); - if (gst_pad_try_set_caps (ident->src, caps) <= 0) { - g_warning ("typefind: found type but peer didn't accept it"); - gst_caps_sink (caps); - } else { - goto plug; - } - } - factories = g_slist_next (factories); - } - type_list = g_list_next (type_list); + GST_DEBUG ("trying typefind function %s", GST_PLUGIN_FEATURE_NAME (factory)); + gst_type_find_factory_call_function (factory, &gst_find); + if (find.best_probability >= GST_TYPE_FIND_MAXIMUM) + goto plug; + walk = g_list_next (walk); } + if (find.best_probability > 0) + goto plug; gst_element_error(GST_ELEMENT(ident), "Could not find media type", NULL); - buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS)); + find.buffer = GST_BUFFER (gst_event_new (GST_EVENT_EOS)); end: - /* remove loop function */ gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop)); /* push the buffer */ - gst_spider_identity_chain (ident->sink, buf); - - /* bytestream no longer needed */ - gst_bytestream_destroy (bs); + gst_spider_identity_chain (ident->sink, find.buffer); return; plug: - gst_caps_debug (caps, "spider starting caps"); - gst_caps_sink (caps); + GST_INFO ("typefind function found caps"); + g_assert (gst_pad_try_set_caps (ident->src, find.caps) > 0); + gst_caps_debug (find.caps, "spider starting caps"); + gst_caps_unref (find.caps); + if (type_list) + g_list_free (type_list); gst_spider_identity_plug (ident); - gst_bytestream_read (bs, &buf, bs->listavail); - goto end; } diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index 00bd244086..0d6a67d050 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -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 \ - gstshaper.h \ - gststatistics.h \ + gstbufferstore.h \ + gstfakesink.h \ + gstfakesrc.h \ + gstfdsink.h \ + gstfdsrc.h \ + gstfilesink.h \ gstfilesrc.h \ - gstmd5sink.h + gstidentity.h \ + gstmd5sink.h \ + gstmultidisksrc.h \ + gstpipefilter.h \ + gstshaper.h \ + gststatistics.h \ + gsttee.h \ + gsttypefindelement.h diff --git a/gst/elements/gstbufferstore.c b/gst/elements/gstbufferstore.c new file mode 100644 index 0000000000..d7dc0b37e1 --- /dev/null +++ b/gst/elements/gstbufferstore.c @@ -0,0 +1,465 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 + +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 + do_nothing_loop (); */ + } else if (cur_offset == offset && + GST_BUFFER_SIZE (current) == size) { + GST_LOG_OBJECT (store, "found matching buffer %p for offset %"G_GUINT64_FORMAT" and size %u", + current, offset, size); + ret = current; + gst_data_ref (GST_DATA (ret)); + GST_LOG_OBJECT (store, "refcount %d", + GST_DATA_REFCOUNT_VALUE(ret)); + break; + } else if (cur_offset + GST_BUFFER_SIZE (current) > offset) { + if (cur_offset + GST_BUFFER_SIZE (current) >= offset + size) { + ret = gst_buffer_create_sub (current, offset - cur_offset, size); + GST_LOG_OBJECT (store, "created subbuffer %p from buffer %p for offset %llu and size %u", + ret, current, offset, size); + break; + } + /* uh, the requested data spans some buffers */ + ret = gst_buffer_new_and_alloc (size); + GST_LOG_OBJECT (store, "created buffer %p for offset %"G_GUINT64_FORMAT + " and size %u, will fill with data now", + ret, offset, size); + data = GST_BUFFER_DATA (ret); + tmp = GST_BUFFER_SIZE (current) - offset + cur_offset; + memcpy (data, GST_BUFFER_DATA (current) + offset - cur_offset, tmp); + data += tmp; + size -= tmp; + while (size) { + if (walk == NULL || + (have_offset && + GST_BUFFER_OFFSET (current) + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) { + GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and remaining size %u available, aborting", + offset, size); + gst_data_unref (GST_DATA (ret)); + ret = NULL; + goto out; + } + current = GST_BUFFER (walk->data); + walk = g_list_next (walk); + tmp = MIN (GST_BUFFER_SIZE (current), size); + memcpy (data, GST_BUFFER_DATA (current), tmp); + size -= tmp; + } + } + if (!have_offset) { + cur_offset += GST_BUFFER_SIZE (current); + } + } +out: + + return ret; +} +/** + * gst_buffer_store_get_size: + * @store: a bufferstore + * @offset: desired offset + * + * Calculates the number of bytes available starting from offset. This allows + * to query a buffer with the returned size. + * + * Returns: the number of continuous bytes in the bufferstore starting at + * offset. + */ +guint +gst_buffer_store_get_size (GstBufferStore *store, guint64 offset) +{ + GList *walk; + guint64 cur_offset; + gboolean have_offset; + gboolean counting = FALSE; + GstBuffer *current; + guint ret = 0; + + g_return_val_if_fail (GST_IS_BUFFER_STORE (store), 0); + + walk = store->buffers; + if (!walk) + return 0; + if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) { + have_offset = TRUE; + } else { + have_offset = FALSE; + cur_offset = 0; + } + while (walk) { + if (have_offset && counting && + cur_offset + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data)) { + break; + } + current = GST_BUFFER (walk->data); + if (have_offset) { + cur_offset = GST_BUFFER_OFFSET (current); + } + walk = g_list_next (walk); + if (counting) { + ret += GST_BUFFER_SIZE (current); + } else { + if (cur_offset > offset) + return 0; + if (cur_offset + GST_BUFFER_SIZE (current) > offset) { + /* we have at least some bytes */ + ret = cur_offset + GST_BUFFER_SIZE (current) - offset; + counting = TRUE; + } + } + if (!have_offset) { + cur_offset += GST_BUFFER_SIZE (current); + } + } + + return ret; +} diff --git a/gst/elements/gstbufferstore.h b/gst/elements/gstbufferstore.h new file mode 100644 index 0000000000..15fff8e6a1 --- /dev/null +++ b/gst/elements/gstbufferstore.h @@ -0,0 +1,73 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 +#include +#include + +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__ */ diff --git a/gst/elements/gstelements.c b/gst/elements/gstelements.c index 78413f2161..7b769a0514 100644 --- a/gst/elements/gstelements.c +++ b/gst/elements/gstelements.c @@ -27,20 +27,21 @@ #include -#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; } diff --git a/gst/elements/gstfakesrc.c b/gst/elements/gstfakesrc.c index e992ee50f9..14287e347e 100644 --- a/gst/elements/gstfakesrc.c +++ b/gst/elements/gstfakesrc.c @@ -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; } } diff --git a/gst/elements/gstfilesrc.c b/gst/elements/gstfilesrc.c index f0de20d0a2..d3288d6b36 100644 --- a/gst/elements/gstfilesrc.c +++ b/gst/elements/gstfilesrc.c @@ -88,18 +88,6 @@ GstElementDetails gst_filesrc_details = { #define DEFAULT_BLOCKSIZE 4*1024 #define DEFAULT_MMAPSIZE 4*1024*1024 -#ifdef G_HAVE_ISO_VARARGS - -/* #define fs_print(...) g_print(__VA_ARGS__) */ -#define fs_print(...) - -#elif defined(G_HAVE_GNUC_VARARGS) - -/* #define fs_print(format,args...) g_print(format, ## args) */ -#define fs_print(format,args...) - -#endif - /* FileSrc signals and args */ enum { /* FILL ME */ @@ -305,7 +293,7 @@ gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, G src->mapsize = g_value_get_ulong (value); g_object_notify (G_OBJECT (src), "mmapsize"); } else { - GST_INFO ( "invalid mapsize, must a multiple of pagesize, which is %d", + GST_INFO_OBJECT (src, "invalid mapsize, must a multiple of pagesize, which is %d", src->pagesize); } break; @@ -355,8 +343,8 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf) { GstFileSrc *src = GST_FILESRC (GST_BUFFER_POOL_PRIVATE (buf)); - fs_print ("freeing mmap()d buffer at %d+%d\n", - GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf)); + GST_LOG_OBJECT (src, "freeing mmap()d buffer at %"G_GUINT64_FORMAT"+%u", + GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf)); /* remove the buffer from the list of available mmap'd regions */ g_mutex_lock (src->map_regions_lock); @@ -375,13 +363,14 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf) munmap (GST_BUFFER_DATA (buf), GST_BUFFER_MAXSIZE (buf)); /* cast to unsigned long, since there's no gportable way to print * guint64 as hex */ - GST_DEBUG ( "unmapped region %08lx+%08lx at %p", + GST_LOG_OBJECT (src, "unmapped region %08lx+%08lx at %p", (unsigned long) GST_BUFFER_OFFSET (buf), (unsigned long) GST_BUFFER_MAXSIZE (buf), GST_BUFFER_DATA (buf)); GST_BUFFER_DATA (buf) = NULL; + g_object_unref (src); gst_buffer_default_free (buf); } @@ -394,7 +383,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size) g_return_val_if_fail (offset >= 0, NULL); - fs_print ("mapping region %08llx+%08lx from file into memory\n",offset,(unsigned long)size); + GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory",offset,(unsigned long)size); mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset); if (mmapregion == NULL) { @@ -402,11 +391,11 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size) return NULL; } else if (mmapregion == MAP_FAILED) { - GST_DEBUG ("mmap (0x%08lx, %d, 0x%llx) : %s", + GST_WARNING_OBJECT (src, "mmap (0x%08lx, %d, 0x%llx) failed: %s", (unsigned long)size, src->fd, offset, strerror (errno)); return NULL; } - GST_DEBUG ( "mapped region %08lx+%08lx from file into memory at %p", + GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p", (unsigned long)offset, (unsigned long)size, mmapregion); /* time to allocate a new mapbuf */ @@ -426,6 +415,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size) GST_BUFFER_OFFSET (buf) = offset; GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; GST_BUFFER_POOL_PRIVATE (buf) = src; + g_object_ref (src); GST_BUFFER_FREE_FUNC (buf) = (GstDataFreeFunction) gst_filesrc_free_parent_mmap; g_mutex_lock (src->map_regions_lock); @@ -522,7 +512,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* if the end is before the mapend, the buffer is in current mmap region... */ /* ('cause by definition if readend is in the buffer, so's readstart) */ if (readend <= mapend) { - fs_print ("read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf\n", + GST_LOG_OBJECT (src, "read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf", src->curoffset, readsize, mapstart, mapsize); buf = gst_buffer_create_sub (src->mapbuf, src->curoffset - mapstart, readsize); @@ -530,8 +520,8 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* if the start actually is within the current mmap region, map an overlap buffer */ } else if (src->curoffset < mapend) { - fs_print ("read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap\n", - src->curoffset, readsize, mapstart, mapsize); + GST_LOG_OBJECT (src, "read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap", + (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize); buf = gst_filesrc_map_small_region (src, src->curoffset, readsize); if (buf == NULL) return NULL; @@ -545,8 +535,8 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* either the read buffer overlaps the start of the mmap region */ /* or the read buffer fully contains the current mmap region */ /* either way, it's really not relevant, we just create a new region anyway*/ - fs_print ("read buf %llu+%d starts before mapbuf %d+%d, but overlaps it\n", - src->curoffset,readsize, mapstart, mapsize); + GST_LOG_OBJECT (src, "read buf %llu+%d starts before mapbuf %d+%d, but overlaps it", + (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize); buf = gst_filesrc_map_small_region (src, src->curoffset, readsize); if (buf == NULL) return NULL; @@ -555,7 +545,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* then deal with the case where the read buffer is totally outside */ if (buf == NULL) { /* first check to see if there's a map that covers the right region already */ - fs_print ("searching for mapbuf to cover %llu+%d\n",src->curoffset,readsize); + GST_LOG_OBJECT (src, "searching for mapbuf to cover %llu+%d",src->curoffset,readsize); region.offset = src->curoffset; region.size = readsize; map = g_tree_search (src->map_regions, @@ -564,7 +554,8 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* if we found an exact match, subbuffer it */ if (map != NULL) { - fs_print ("found mapbuf at %d+%d, creating subbuffer\n",GST_BUFFER_OFFSET(map),GST_BUFFER_SIZE(map)); + GST_LOG_OBJECT (src, "found mapbuf at %"G_GUINT64_FORMAT"+%u, creating subbuffer", + GST_BUFFER_OFFSET (map), GST_BUFFER_SIZE (map)); buf = gst_buffer_create_sub (map, src->curoffset - GST_BUFFER_OFFSET(map), readsize); GST_BUFFER_OFFSET (buf) = src->curoffset; @@ -572,7 +563,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) } else { /* if the read buffer crosses a mmap region boundary, create a one-off region */ if ((src->curoffset / src->mapsize) != (readend / src->mapsize)) { - fs_print ("read buf %llu+%d crosses a %d-byte boundary, creating a one-off\n", + GST_LOG_OBJECT (src, "read buf %llu+%d crosses a %d-byte boundary, creating a one-off", src->curoffset,readsize,src->mapsize); buf = gst_filesrc_map_small_region (src, src->curoffset, readsize); if (buf == NULL) @@ -583,7 +574,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) size_t mapsize; off_t nextmap = src->curoffset - (src->curoffset % src->mapsize); - fs_print ("read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering\n", + GST_LOG_OBJECT (src, "read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering", src->curoffset, readsize, nextmap, src->mapsize); /* first, we're done with the old mapbuf */ gst_buffer_unref(src->mapbuf); @@ -591,7 +582,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* double the mapsize as long as the readsize is smaller */ while (readsize - (src->curoffset - nextmap) > mapsize) { - fs_print ("readsize smaller then mapsize %08x %d\n", readsize, mapsize); + GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d", readsize, mapsize); mapsize <<=1; } /* create a new one */ @@ -664,7 +655,7 @@ gst_filesrc_get (GstPad *pad) GstEvent *event; src->seek_happened = FALSE; - GST_DEBUG ("filesrc sending discont"); + GST_DEBUG_OBJECT (src, "sending discont"); event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset, NULL); src->need_flush = FALSE; return GST_DATA (event); @@ -672,13 +663,13 @@ gst_filesrc_get (GstPad *pad) /* check for flush */ if (src->need_flush) { src->need_flush = FALSE; - GST_DEBUG ("filesrc sending flush"); + GST_DEBUG_OBJECT (src, "sending flush"); return GST_DATA (gst_event_new_flush ()); } /* check for EOF */ if (src->curoffset == src->filelen) { - GST_DEBUG ("filesrc eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT, + GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT, src->curoffset, src->filelen); gst_element_set_eos (GST_ELEMENT (src)); return GST_DATA (gst_event_new (GST_EVENT_EOS)); @@ -697,7 +688,7 @@ gst_filesrc_open_file (GstFileSrc *src) { g_return_val_if_fail (!GST_FLAG_IS_SET (src ,GST_FILESRC_OPEN), FALSE); - GST_DEBUG ( "opening file %s",src->filename); + GST_INFO_OBJECT (src, "opening file %s",src->filename); /* open the file */ src->fd = open (src->filename, O_RDONLY); @@ -829,7 +820,7 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event) { GstFileSrc *src = GST_FILESRC (GST_PAD_PARENT (pad)); - GST_DEBUG ( "event %d", GST_EVENT_TYPE (event)); + GST_DEBUG_OBJECT (src, "event %d", GST_EVENT_TYPE (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: @@ -847,19 +838,19 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event) if (offset > src->filelen) goto error; src->curoffset = offset; - GST_DEBUG ( "seek set pending to %" G_GINT64_FORMAT, src->curoffset); + GST_DEBUG_OBJECT (src, "seek set pending to %" G_GINT64_FORMAT, src->curoffset); break; case GST_SEEK_METHOD_CUR: if (offset + src->curoffset > src->filelen) goto error; src->curoffset += offset; - GST_DEBUG ( "seek cur pending to %" G_GINT64_FORMAT, src->curoffset); + GST_DEBUG_OBJECT (src, "seek cur pending to %" G_GINT64_FORMAT, src->curoffset); break; case GST_SEEK_METHOD_END: if (ABS (offset) > src->filelen) goto error; src->curoffset = src->filelen - ABS (offset); - GST_DEBUG ( "seek end pending to %" G_GINT64_FORMAT, src->curoffset); + GST_DEBUG_OBJECT (src, "seek end pending to %" G_GINT64_FORMAT, src->curoffset); break; default: goto error; diff --git a/gst/elements/gsttypefind.c b/gst/elements/gsttypefind.c new file mode 100644 index 0000000000..bd5e3d367b --- /dev/null +++ b/gst/elements/gsttypefind.c @@ -0,0 +1,669 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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_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 ", + "(C) 2003", +}; + +/* generic templates */ +GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); +GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); + +/* TypeFind signals and args */ +enum { + HAVE_TYPE, + LAST_SIGNAL +}; +enum { + ARG_0, + ARG_CAPS, + ARG_MINIMUM, + ARG_MAXIMUM +}; +enum { + MODE_NORMAL, /* act as identity */ + MODE_TYPEFIND, /* do typefinding */ +}; + + +static void gst_type_find_element_class_init (gpointer g_class, + gpointer class_data); +static void gst_type_find_element_init (GTypeInstance *instance, + gpointer g_class); +static void gst_type_find_element_dispose (GObject * object); +static void gst_type_find_element_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void gst_type_find_element_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +static const GstEventMask * + gst_type_find_element_src_event_mask (GstPad * pad); +static gboolean gst_type_find_element_src_event (GstPad * pad, + GstEvent * event); + +static void gst_type_find_element_chain (GstPad * sinkpad, + GstData * data); +static GstElementStateReturn + gst_type_find_element_change_state (GstElement * element); + +static GstElementClass *parent_class = NULL; +static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_type_find_element_get_type (void) +{ + static GType typefind_type = 0; + + if (!typefind_type) { + static const GTypeInfo typefind_info = { + sizeof (GstTypeFindElementClass), + NULL, + NULL, + gst_type_find_element_class_init, + NULL, + NULL, + sizeof (GstTypeFindElement), + 0, + gst_type_find_element_init, + NULL + }; + typefind_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstTypeFindElement", + &typefind_info, 0); + } + return typefind_type; +} +static void +gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps) +{ + gchar *caps_str; + + g_assert (typefind->caps == NULL); + g_assert (caps != NULL); + + caps_str = gst_caps_to_string (caps); + GST_INFO_OBJECT (typefind, "found caps %s", caps_str); + g_free (caps_str); + gst_caps_replace (&typefind->caps, caps); + if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) { + gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad"); + } +} +static void +gst_type_find_element_class_init (gpointer g_class, gpointer class_data) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstTypeFindElementClass *typefind_class; + + gobject_class = G_OBJECT_CLASS (g_class); + gstelement_class = GST_ELEMENT_CLASS (g_class); + typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class); + + parent_class = g_type_class_peek_parent (g_class); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property); + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose); + + typefind_class->have_type = gst_type_find_element_have_type; + + g_object_class_install_property (gobject_class, ARG_CAPS, + g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"), + GST_TYPE_CAPS, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE)); + + gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", + G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL, + gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2, + G_TYPE_UINT, GST_TYPE_CAPS); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state); +} +static void +gst_type_find_element_init (GTypeInstance *instance, gpointer g_class) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance); + + /* sinkpad */ + typefind->sink = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink"); + gst_pad_set_chain_function (typefind->sink, + gst_type_find_element_chain); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink); + /* srcpad */ + typefind->src = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src"); + gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event); + gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->src); + + typefind->caps = NULL; + typefind->min_probability = 1; + typefind->max_probability = GST_TYPE_FIND_MAXIMUM; + + typefind->store = gst_buffer_store_new (); + + GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE); +} +static void +gst_type_find_element_dispose (GObject *object) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object); + + G_OBJECT_CLASS (parent_class)->dispose (object); + + if (typefind->store) { + g_object_unref (typefind->store); + typefind->store = NULL; + } +} +static void +gst_type_find_element_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_MINIMUM: + typefind->min_probability = g_value_get_uint (value); + g_object_notify (object, "minimum"); + break; + case ARG_MAXIMUM: + typefind->max_probability = g_value_get_uint (value); + g_object_notify (object, "maximum"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static void +gst_type_find_element_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_CAPS: + g_value_set_boxed (value, typefind->caps); + break; + case ARG_MINIMUM: + g_value_set_uint (value, typefind->min_probability); + break; + case ARG_MAXIMUM: + g_value_set_uint (value, typefind->max_probability); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static const GstEventMask * +gst_type_find_element_src_event_mask (GstPad *pad) +{ + static const GstEventMask mask[] = { + { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH}, + /* add more if you want, event masks suck and need to die anyway */ + { 0, } + }; + + return mask; +} +static gboolean +gst_type_find_element_src_event (GstPad *pad, GstEvent *event) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + gst_data_unref (GST_DATA (event)); + return FALSE; + } + return gst_pad_event_default (pad, event); +} +typedef struct { + GstTypeFindFactory * factory; + gint probability; + GstCaps * caps; + gint64 requested_offset; + guint requested_size; + + GList * buffers; + GstTypeFindElement * self; +} TypeFindEntry; +static void +free_entry_buffers (TypeFindEntry *entry) +{ + g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL); + g_list_free (entry->buffers); + entry->buffers = NULL; +} +static void +free_entry (TypeFindEntry *entry) +{ + free_entry_buffers (entry); + + if (entry->caps) + gst_caps_unref (entry->caps); + g_free (entry); +} +static void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + g_assert (typefind->possibilities == NULL); + + GST_DEBUG_OBJECT (typefind, "starting typefinding"); + typefind->mode = MODE_TYPEFIND; + typefind->stream_length_available = TRUE; + typefind->stream_length = 0; +} +static void +stop_typefinding (GstTypeFindElement *typefind) +{ + /* stop all typefinding and set mode back to normal */ + gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING; + + GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : ""); + if (typefind->possibilities != NULL) { + /* this should only happen on PAUSED => READY or EOS */ + GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities)); + g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + } + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + GstBuffer *buffer; + guint size = gst_buffer_store_get_size (typefind->store, 0); + if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) { + gst_pad_push (typefind->src, GST_DATA (buffer)); + } else { + size = 0; + } + GST_LOG_OBJECT (typefind, "seeking back to current position %u", size); + if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) { + GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size); + } + } + gst_buffer_store_clear (typefind->store); +} +static guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + if (!typefind->stream_length_available) { + GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, + &format, &entry->self->stream_length); + if (format != GST_FORMAT_BYTES) + typefind->stream_length_available = FALSE; + if (!typefind->stream_length_available) { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } else { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length); + } + } + + return entry->self->stream_length; +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + TypeFindEntry *entry; + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* this should only happen when we got all available data */ + entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL; + if (entry && entry->probability >= typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + } + stop_typefinding (typefind); + gst_pad_event_default (pad, event); + break; + default: + gst_data_unref (GST_DATA (event)); + break; + } + } else { + gst_pad_event_default (pad, event); + } +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size); + if (offset >= 0) { + buf = gst_buffer_store_get_buffer (entry->self->store, offset, size); + } else { + /* FIXME: can we do this easily without querying length? */ + guint64 length = find_element_get_length (data); + + if (length == 0) { + buf = NULL; + } else { + buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size); + } + } + + if (buf) { + entry->buffers = g_list_prepend (entry->buffers, buf); + return GST_BUFFER_DATA (buf); + } else { + if (entry->requested_size == 0) { + GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", + offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory)); + entry->requested_offset = offset; + entry->requested_size = size; + } + return NULL; + } +} +static void +find_suggest (gpointer data, guint probability, GstCaps *caps) +{ + gchar *str; + TypeFindEntry *entry = (TypeFindEntry *) data; + + str = gst_caps_to_string (caps); + GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str); + g_free (str); + if (((gint) probability) > entry->probability) { + entry->probability = probability; + gst_caps_replace (&entry->caps, caps); + } +} +static gint +compare_type_find_entry (gconstpointer a, gconstpointer b) +{ + TypeFindEntry *one = (TypeFindEntry *) a; + TypeFindEntry *two = (TypeFindEntry *) b; + + if (one->probability == two->probability) { + /* FIXME: can be improved by analyzing requests */ + return 0; + } else { + return two->probability - one->probability; + } +} +static gint +compare_type_find_factory (gconstpointer fac1, gconstpointer fac2) +{ + return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank; +} +static void +gst_type_find_element_chain (GstPad *pad, GstData *data) +{ + GstTypeFindElement *typefind; + GList *entries; + TypeFindEntry *entry; + GList *walk; + GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length }; + + typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + if (GST_IS_EVENT (data)) { + gst_type_find_element_handle_event (pad, GST_EVENT (data)); + return; + } + switch (typefind->mode) { + case MODE_NORMAL: + gst_pad_push (typefind->src, data); + return; + case MODE_TYPEFIND: { + gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data)); + gst_data_unref (data); + if (typefind->possibilities == NULL) { + /* not yet started, get all typefinding functions into our "queue" */ + GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + all_factories = g_list_sort (all_factories, compare_type_find_factory); + walk = all_factories; + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = 0; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + g_list_free (all_factories); + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries)); + typefind->possibilities = NULL; + while (walk) { + find.data = entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + entry->probability = 0; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == 0 && entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + } else if (entry->probability >= typefind->max_probability) { + /* wooha, got caps */ + GstCaps *found_caps = entry->caps; + guint probability = entry->probability; + + gst_caps_ref (found_caps); + GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability); + while (walk) { + free_entry ((TypeFindEntry *) walk->data); + walk = g_list_next (walk); + } + walk = typefind->possibilities; + while (walk) { + free_entry (walk->data); + walk = g_list_next (walk); + } + typefind->possibilities = NULL; + g_list_free (typefind->possibilities); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps); + gst_caps_unref (found_caps); + } else { + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + } + } + g_list_free (entries); + /* we may now already have caps or we might be left without functions to try */ + if (typefind->caps) { + stop_typefinding (typefind); + } else if (typefind->possibilities == NULL) { + gst_element_error (GST_ELEMENT (typefind), "media type could not be detected"); + } else { + /* set up typefind element for next iteration */ + typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry); + + walk = typefind->possibilities; + while (walk) { + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size > 0) { + /* FIXME: need heuristic to find out if we should seek */ + gint64 seek_offset; + GstEvent *event; + + seek_offset = entry->requested_offset > 0 ? entry->requested_offset : + find_element_get_length (entry) + entry->requested_offset; + seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset); + event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + break; + } else if (entry->requested_offset < 0) { + /* impossible to seek */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + entry->requested_size = 0; + entry->requested_offset = 0; + } + } + } + /* throw out all entries that can't get more data */ + walk = g_list_next (typefind->possibilities); + while (walk) { + GList *cur = walk; + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + if (entry->probability > typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind); + } + } + } + break; + } + default: + g_assert_not_reached (); + return; + } +} +static GstElementStateReturn +gst_type_find_element_change_state (GstElement *element) +{ + GstTypeFindElement *typefind; + + typefind = GST_TYPE_FIND_ELEMENT (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + start_typefinding (typefind); + break; + case GST_STATE_PAUSED_TO_READY: + stop_typefinding (typefind); + gst_caps_replace (&typefind->caps, NULL); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element); +} diff --git a/gst/elements/gsttypefind.h b/gst/elements/gsttypefind.h new file mode 100644 index 0000000000..4b5e0cc40f --- /dev/null +++ b/gst/elements/gsttypefind.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 +#include +/* #include */ +#include "gstbufferstore.h" + +G_BEGIN_DECLS + +GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug); + +extern GstElementDetails gst_type_find_element_details; + +#define GST_TYPE_TYPE_FIND_ELEMENT (gst_type_find_element_get_type ()) +#define GST_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement)) +#define GST_IS_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) +#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) + +typedef struct _GstTypeFindElement GstTypeFindElement; +typedef struct _GstTypeFindElementClass GstTypeFindElementClass; + +struct _GstTypeFindElement { + GstElement element; + + GstPad * sink; + GstPad * src; + + guint min_probability; + guint max_probability; + GstCaps * caps; + + guint mode; + GstBufferStore * store; + guint64 stream_length; + gboolean stream_length_available; + + GList * possibilities; +}; + +struct _GstTypeFindElementClass { + GstElementClass parent_class; + + /* signals */ + void (*have_type) (GstTypeFindElement *element, + guint probability, + GstCaps * caps); +}; + +GType gst_type_find_element_get_type (void); + +G_END_DECLS + +#endif /* __GST_TYPE_FIND_ELEMENT_H__ */ diff --git a/gst/elements/gsttypefindelement.c b/gst/elements/gsttypefindelement.c new file mode 100644 index 0000000000..bd5e3d367b --- /dev/null +++ b/gst/elements/gsttypefindelement.c @@ -0,0 +1,669 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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_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 ", + "(C) 2003", +}; + +/* generic templates */ +GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); +GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); + +/* TypeFind signals and args */ +enum { + HAVE_TYPE, + LAST_SIGNAL +}; +enum { + ARG_0, + ARG_CAPS, + ARG_MINIMUM, + ARG_MAXIMUM +}; +enum { + MODE_NORMAL, /* act as identity */ + MODE_TYPEFIND, /* do typefinding */ +}; + + +static void gst_type_find_element_class_init (gpointer g_class, + gpointer class_data); +static void gst_type_find_element_init (GTypeInstance *instance, + gpointer g_class); +static void gst_type_find_element_dispose (GObject * object); +static void gst_type_find_element_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void gst_type_find_element_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +static const GstEventMask * + gst_type_find_element_src_event_mask (GstPad * pad); +static gboolean gst_type_find_element_src_event (GstPad * pad, + GstEvent * event); + +static void gst_type_find_element_chain (GstPad * sinkpad, + GstData * data); +static GstElementStateReturn + gst_type_find_element_change_state (GstElement * element); + +static GstElementClass *parent_class = NULL; +static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_type_find_element_get_type (void) +{ + static GType typefind_type = 0; + + if (!typefind_type) { + static const GTypeInfo typefind_info = { + sizeof (GstTypeFindElementClass), + NULL, + NULL, + gst_type_find_element_class_init, + NULL, + NULL, + sizeof (GstTypeFindElement), + 0, + gst_type_find_element_init, + NULL + }; + typefind_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstTypeFindElement", + &typefind_info, 0); + } + return typefind_type; +} +static void +gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps) +{ + gchar *caps_str; + + g_assert (typefind->caps == NULL); + g_assert (caps != NULL); + + caps_str = gst_caps_to_string (caps); + GST_INFO_OBJECT (typefind, "found caps %s", caps_str); + g_free (caps_str); + gst_caps_replace (&typefind->caps, caps); + if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) { + gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad"); + } +} +static void +gst_type_find_element_class_init (gpointer g_class, gpointer class_data) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstTypeFindElementClass *typefind_class; + + gobject_class = G_OBJECT_CLASS (g_class); + gstelement_class = GST_ELEMENT_CLASS (g_class); + typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class); + + parent_class = g_type_class_peek_parent (g_class); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property); + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose); + + typefind_class->have_type = gst_type_find_element_have_type; + + g_object_class_install_property (gobject_class, ARG_CAPS, + g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"), + GST_TYPE_CAPS, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE)); + + gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", + G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL, + gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2, + G_TYPE_UINT, GST_TYPE_CAPS); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state); +} +static void +gst_type_find_element_init (GTypeInstance *instance, gpointer g_class) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance); + + /* sinkpad */ + typefind->sink = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink"); + gst_pad_set_chain_function (typefind->sink, + gst_type_find_element_chain); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink); + /* srcpad */ + typefind->src = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src"); + gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event); + gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->src); + + typefind->caps = NULL; + typefind->min_probability = 1; + typefind->max_probability = GST_TYPE_FIND_MAXIMUM; + + typefind->store = gst_buffer_store_new (); + + GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE); +} +static void +gst_type_find_element_dispose (GObject *object) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object); + + G_OBJECT_CLASS (parent_class)->dispose (object); + + if (typefind->store) { + g_object_unref (typefind->store); + typefind->store = NULL; + } +} +static void +gst_type_find_element_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_MINIMUM: + typefind->min_probability = g_value_get_uint (value); + g_object_notify (object, "minimum"); + break; + case ARG_MAXIMUM: + typefind->max_probability = g_value_get_uint (value); + g_object_notify (object, "maximum"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static void +gst_type_find_element_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_CAPS: + g_value_set_boxed (value, typefind->caps); + break; + case ARG_MINIMUM: + g_value_set_uint (value, typefind->min_probability); + break; + case ARG_MAXIMUM: + g_value_set_uint (value, typefind->max_probability); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static const GstEventMask * +gst_type_find_element_src_event_mask (GstPad *pad) +{ + static const GstEventMask mask[] = { + { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH}, + /* add more if you want, event masks suck and need to die anyway */ + { 0, } + }; + + return mask; +} +static gboolean +gst_type_find_element_src_event (GstPad *pad, GstEvent *event) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + gst_data_unref (GST_DATA (event)); + return FALSE; + } + return gst_pad_event_default (pad, event); +} +typedef struct { + GstTypeFindFactory * factory; + gint probability; + GstCaps * caps; + gint64 requested_offset; + guint requested_size; + + GList * buffers; + GstTypeFindElement * self; +} TypeFindEntry; +static void +free_entry_buffers (TypeFindEntry *entry) +{ + g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL); + g_list_free (entry->buffers); + entry->buffers = NULL; +} +static void +free_entry (TypeFindEntry *entry) +{ + free_entry_buffers (entry); + + if (entry->caps) + gst_caps_unref (entry->caps); + g_free (entry); +} +static void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + g_assert (typefind->possibilities == NULL); + + GST_DEBUG_OBJECT (typefind, "starting typefinding"); + typefind->mode = MODE_TYPEFIND; + typefind->stream_length_available = TRUE; + typefind->stream_length = 0; +} +static void +stop_typefinding (GstTypeFindElement *typefind) +{ + /* stop all typefinding and set mode back to normal */ + gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING; + + GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : ""); + if (typefind->possibilities != NULL) { + /* this should only happen on PAUSED => READY or EOS */ + GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities)); + g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + } + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + GstBuffer *buffer; + guint size = gst_buffer_store_get_size (typefind->store, 0); + if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) { + gst_pad_push (typefind->src, GST_DATA (buffer)); + } else { + size = 0; + } + GST_LOG_OBJECT (typefind, "seeking back to current position %u", size); + if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) { + GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size); + } + } + gst_buffer_store_clear (typefind->store); +} +static guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + if (!typefind->stream_length_available) { + GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, + &format, &entry->self->stream_length); + if (format != GST_FORMAT_BYTES) + typefind->stream_length_available = FALSE; + if (!typefind->stream_length_available) { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } else { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length); + } + } + + return entry->self->stream_length; +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + TypeFindEntry *entry; + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* this should only happen when we got all available data */ + entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL; + if (entry && entry->probability >= typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + } + stop_typefinding (typefind); + gst_pad_event_default (pad, event); + break; + default: + gst_data_unref (GST_DATA (event)); + break; + } + } else { + gst_pad_event_default (pad, event); + } +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size); + if (offset >= 0) { + buf = gst_buffer_store_get_buffer (entry->self->store, offset, size); + } else { + /* FIXME: can we do this easily without querying length? */ + guint64 length = find_element_get_length (data); + + if (length == 0) { + buf = NULL; + } else { + buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size); + } + } + + if (buf) { + entry->buffers = g_list_prepend (entry->buffers, buf); + return GST_BUFFER_DATA (buf); + } else { + if (entry->requested_size == 0) { + GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", + offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory)); + entry->requested_offset = offset; + entry->requested_size = size; + } + return NULL; + } +} +static void +find_suggest (gpointer data, guint probability, GstCaps *caps) +{ + gchar *str; + TypeFindEntry *entry = (TypeFindEntry *) data; + + str = gst_caps_to_string (caps); + GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str); + g_free (str); + if (((gint) probability) > entry->probability) { + entry->probability = probability; + gst_caps_replace (&entry->caps, caps); + } +} +static gint +compare_type_find_entry (gconstpointer a, gconstpointer b) +{ + TypeFindEntry *one = (TypeFindEntry *) a; + TypeFindEntry *two = (TypeFindEntry *) b; + + if (one->probability == two->probability) { + /* FIXME: can be improved by analyzing requests */ + return 0; + } else { + return two->probability - one->probability; + } +} +static gint +compare_type_find_factory (gconstpointer fac1, gconstpointer fac2) +{ + return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank; +} +static void +gst_type_find_element_chain (GstPad *pad, GstData *data) +{ + GstTypeFindElement *typefind; + GList *entries; + TypeFindEntry *entry; + GList *walk; + GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length }; + + typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + if (GST_IS_EVENT (data)) { + gst_type_find_element_handle_event (pad, GST_EVENT (data)); + return; + } + switch (typefind->mode) { + case MODE_NORMAL: + gst_pad_push (typefind->src, data); + return; + case MODE_TYPEFIND: { + gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data)); + gst_data_unref (data); + if (typefind->possibilities == NULL) { + /* not yet started, get all typefinding functions into our "queue" */ + GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + all_factories = g_list_sort (all_factories, compare_type_find_factory); + walk = all_factories; + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = 0; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + g_list_free (all_factories); + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries)); + typefind->possibilities = NULL; + while (walk) { + find.data = entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + entry->probability = 0; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == 0 && entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + } else if (entry->probability >= typefind->max_probability) { + /* wooha, got caps */ + GstCaps *found_caps = entry->caps; + guint probability = entry->probability; + + gst_caps_ref (found_caps); + GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability); + while (walk) { + free_entry ((TypeFindEntry *) walk->data); + walk = g_list_next (walk); + } + walk = typefind->possibilities; + while (walk) { + free_entry (walk->data); + walk = g_list_next (walk); + } + typefind->possibilities = NULL; + g_list_free (typefind->possibilities); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps); + gst_caps_unref (found_caps); + } else { + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + } + } + g_list_free (entries); + /* we may now already have caps or we might be left without functions to try */ + if (typefind->caps) { + stop_typefinding (typefind); + } else if (typefind->possibilities == NULL) { + gst_element_error (GST_ELEMENT (typefind), "media type could not be detected"); + } else { + /* set up typefind element for next iteration */ + typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry); + + walk = typefind->possibilities; + while (walk) { + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size > 0) { + /* FIXME: need heuristic to find out if we should seek */ + gint64 seek_offset; + GstEvent *event; + + seek_offset = entry->requested_offset > 0 ? entry->requested_offset : + find_element_get_length (entry) + entry->requested_offset; + seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset); + event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + break; + } else if (entry->requested_offset < 0) { + /* impossible to seek */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + entry->requested_size = 0; + entry->requested_offset = 0; + } + } + } + /* throw out all entries that can't get more data */ + walk = g_list_next (typefind->possibilities); + while (walk) { + GList *cur = walk; + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + if (entry->probability > typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind); + } + } + } + break; + } + default: + g_assert_not_reached (); + return; + } +} +static GstElementStateReturn +gst_type_find_element_change_state (GstElement *element) +{ + GstTypeFindElement *typefind; + + typefind = GST_TYPE_FIND_ELEMENT (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + start_typefinding (typefind); + break; + case GST_STATE_PAUSED_TO_READY: + stop_typefinding (typefind); + gst_caps_replace (&typefind->caps, NULL); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element); +} diff --git a/gst/elements/gsttypefindelement.h b/gst/elements/gsttypefindelement.h new file mode 100644 index 0000000000..4b5e0cc40f --- /dev/null +++ b/gst/elements/gsttypefindelement.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 +#include +/* #include */ +#include "gstbufferstore.h" + +G_BEGIN_DECLS + +GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug); + +extern GstElementDetails gst_type_find_element_details; + +#define GST_TYPE_TYPE_FIND_ELEMENT (gst_type_find_element_get_type ()) +#define GST_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement)) +#define GST_IS_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) +#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) + +typedef struct _GstTypeFindElement GstTypeFindElement; +typedef struct _GstTypeFindElementClass GstTypeFindElementClass; + +struct _GstTypeFindElement { + GstElement element; + + GstPad * sink; + GstPad * src; + + guint min_probability; + guint max_probability; + GstCaps * caps; + + guint mode; + GstBufferStore * store; + guint64 stream_length; + gboolean stream_length_available; + + GList * possibilities; +}; + +struct _GstTypeFindElementClass { + GstElementClass parent_class; + + /* signals */ + void (*have_type) (GstTypeFindElement *element, + guint probability, + GstCaps * caps); +}; + +GType gst_type_find_element_get_type (void); + +G_END_DECLS + +#endif /* __GST_TYPE_FIND_ELEMENT_H__ */ diff --git a/gst/gst.c b/gst/gst.c index 733c436d5b..f06b11c6ed 100644 --- a/gst/gst.c +++ b/gst/gst.c @@ -27,9 +27,6 @@ #include "gst.h" #include "gstqueue.h" -#ifndef GST_DISABLE_TYPEFIND -#include "gsttypefind.h" -#endif /* GST_DISABLE_TYPEFIND */ #ifndef GST_DISABLE_REGISTRY #include "registries/gstxmlregistry.h" #endif /* GST_DISABLE_REGISTRY */ @@ -461,10 +458,6 @@ gst_register_core_elements (GModule *module, GstPlugin *plugin) gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); factory = gst_element_factory_new ("queue", gst_queue_get_type (), &gst_queue_details); gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); -#ifndef GST_DISABLE_TYPEFIND - factory = gst_element_factory_new ("typefind", gst_type_find_get_type (), &gst_type_find_details); - gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); -#endif /* GST_DISABLE_TYPEFIND */ return TRUE; } @@ -510,8 +503,8 @@ init_post (void) gst_ghost_pad_get_type (); gst_element_factory_get_type (); gst_element_get_type (); - gst_type_factory_get_type (); gst_scheduler_factory_get_type (); + gst_type_find_factory_get_type (); gst_bin_get_type (); #ifndef GST_DISABLE_AUTOPLUG gst_autoplug_factory_get_type (); diff --git a/gst/gst.h b/gst/gst.h index 476b693b2f..e7fd46d17a 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gst/gst_private.h b/gst/gst_private.h index 1c20374ab6..caab5b0381 100644 --- a/gst/gst_private.h +++ b/gst/gst_private.h @@ -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; diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index c7a0f16e1d..6df3cf8cb8 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -301,8 +301,6 @@ gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size) } /* ref the real parent */ gst_data_ref (GST_DATA (parent)); - /* make sure nobody overwrites data in the parent */ - GST_DATA_FLAG_SET (parent, GST_DATA_READONLY); /* create the new buffer */ buffer = gst_mem_chunk_alloc (chunk); @@ -337,9 +335,12 @@ gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size) GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE; } - GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; + /* make sure nobody overwrites data as it would overwrite in the parent. + * data in parent cannot be overwritten because we hold a ref */ + GST_DATA_FLAG_SET (parent, GST_DATA_READONLY); + return buffer; } diff --git a/gst/gstcaps.c b/gst/gstcaps.c index a6d70381bf..7d7b456003 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -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")) { diff --git a/gst/gstcaps.h b/gst/gstcaps.h index d781247ef8..fa2c249595 100644 --- a/gst/gstcaps.h +++ b/gst/gstcaps.h @@ -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); diff --git a/gst/gstconfig.h.in b/gst/gstconfig.h.in index 2792821695..24c6e0a427 100644 --- a/gst/gstconfig.h.in +++ b/gst/gstconfig.h.in @@ -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@ diff --git a/gst/gstelement.c b/gst/gstelement.c index bc215b04fb..6795cfe10e 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -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); diff --git a/gst/gstmarshal.list b/gst/gstmarshal.list index 26b543f850..0340c85f76 100644 --- a/gst/gstmarshal.list +++ b/gst/gstmarshal.list @@ -9,4 +9,6 @@ VOID:OBJECT,POINTER VOID:OBJECT,STRING VOID:INT,INT VOID:INT64 +VOID:UINT,BOXED BOOLEAN:VOID +BOOLEAN:POINTER diff --git a/gst/gstpad.c b/gst/gstpad.c index 4387e307fa..e0f1a7c78c 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -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) { diff --git a/gst/gstpluginfeature.c b/gst/gstpluginfeature.c index abc0473020..494075cd26 100644 --- a/gst/gstpluginfeature.c +++ b/gst/gstpluginfeature.c @@ -27,6 +27,8 @@ #include "gstregistry.h" #include "gstinfo.h" +#include + 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); + } +} diff --git a/gst/gstpluginfeature.h b/gst/gstpluginfeature.h index 23f614a608..a543f669af 100644 --- a/gst/gstpluginfeature.h +++ b/gst/gstpluginfeature.h @@ -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 diff --git a/gst/gsttypefind.c b/gst/gsttypefind.c index 19a2f10044..77677d362a 100644 --- a/gst/gsttypefind.c +++ b/gst/gsttypefind.c @@ -1,8 +1,7 @@ /* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * Copyright (C) 2003 Benjamin Otte * - * 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 ," - "Wim Taymans ", - "(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; - - gobject_class = (GObjectClass*)klass; - gstelement_class = (GstElementClass*)klass; - - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CAPS, - g_param_spec_pointer ("caps", "Caps", "Found capabilities", G_PARAM_READABLE)); - - gst_type_find_signals[HAVE_TYPE] = - g_signal_new ("have_type", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstTypeFindClass, have_type), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, - G_TYPE_POINTER); - - gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_get_property); - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_change_state); + GstPluginFeatureClass *gstpluginfeature_class = GST_PLUGIN_FEATURE_CLASS (g_class); + GObjectClass *object_class = G_OBJECT_CLASS (g_class); + + parent_class = g_type_class_peek_parent (g_class); + + object_class->dispose = gst_type_find_factory_dispose; + + gstpluginfeature_class->unload_thyself = GST_DEBUG_FUNCPTR (gst_type_find_factory_unload_thyself); } - static void -gst_type_find_init (GstTypeFind *typefind) +gst_type_find_factory_init (GTypeInstance *instance, gpointer g_class) { - typefind->sinkpad = gst_pad_new_from_template ( - GST_PAD_TEMPLATE_GET (type_find_sink_factory), "sink"); - gst_element_add_pad (GST_ELEMENT (typefind), typefind->sinkpad); + 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 ("Started typefinding loop in '%s'", - GST_OBJECT_NAME (typefind)); - - type_list = gst_type_get_list (); - - while (type_list) { - GSList *factories; - type = (GstType *) type_list->data; - - factories = type->factories; - - while (factories) { - GstTypeFactory *factory = GST_TYPE_FACTORY (factories->data); - GstTypeFindFunc typefindfunc = (GstTypeFindFunc) factory->typefindfunc; - GstCaps *caps; - - GST_CAT_DEBUG (GST_CAT_TYPES, "try type (%p) :%d \"%s\" %p", - factory, type->id, type->mime, typefindfunc); - if (typefindfunc && (caps = typefindfunc (typefind->bs, factory))) { - GST_CAT_DEBUG (GST_CAT_TYPES, "found type: %d \"%s\" \"%s\"", - caps->id, type->mime, gst_caps_get_name (caps)); - gst_caps_replace (&typefind->caps, caps); - - if (gst_pad_try_set_caps (typefind->sinkpad, caps) <= 0) { - g_warning ("typefind: found type but peer didn't accept it"); - } - - gst_object_ref (GST_OBJECT (typefind)); - g_signal_emit (G_OBJECT (typefind), gst_type_find_signals[HAVE_TYPE], - 0, typefind->caps); - gst_object_unref (GST_OBJECT (typefind)); - return; - } - factories = g_slist_next (factories); + GST_DEBUG_OBJECT (factory, "need to load typefind function %s", GST_PLUGIN_FEATURE_NAME (factory)); + + if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) { + if (factory->function == gst_type_find_load_plugin) { + /* looks like we didn't get a real typefind function */ + g_warning ("could not load valid typefind function for feature '%s'\n", GST_PLUGIN_FEATURE_NAME (factory)); + } else { + g_assert (factory->function); + gst_type_find_factory_call_function (factory, find); } - 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; - - typefind = GST_TYPE_FIND (element); - - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_READY_TO_PAUSED: - typefind->bs = gst_bytestream_new (typefind->sinkpad); - break; - case GST_STATE_PAUSED_TO_READY: - gst_bytestream_destroy (typefind->bs); - gst_caps_replace (&typefind->caps, NULL); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return ret; + 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); + + 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); + + 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); + } + + gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank); + if (factory->extensions) + g_strfreev (factory->extensions); + + factory->extensions = g_strdupv (extensions); + gst_caps_replace (&factory->caps, possible_caps); + factory->function = func; + factory->user_data = data; + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); +} + +/*** 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); +} + diff --git a/gst/gsttypefind.h b/gst/gsttypefind.h index 0085eb0630..e80dc2d3e3 100644 --- a/gst/gsttypefind.h +++ b/gst/gsttypefind.h @@ -1,8 +1,7 @@ /* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * Copyright (C) 2003 Benjamin Otte * - * 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 -#include +#include +#include +#include +#include +#include G_BEGIN_DECLS -extern GstElementDetails gst_type_find_details; +#define GST_TYPE_TYPE_FIND_FACTORY (gst_type_find_factory_get_type()) +#define GST_TYPE_FIND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactory)) +#define GST_IS_TYPE_FIND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_FACTORY)) +#define GST_TYPE_FIND_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactoryClass)) +#define GST_IS_TYPE_FIND_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_FACTORY)) +#define GST_TYPE_FIND_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_FACTORY, GstTypeFindFactoryClass)) -#define GST_TYPE_TYPE_FIND (gst_type_find_get_type ()) -#define GST_TYPE_FIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND, GstTypeFind)) -#define GST_IS_TYPE_FIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND)) -#define GST_TYPE_FIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND, GstTypeFindClass)) -#define GST_IS_TYPE_FIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND)) -#define GST_TYPE_FIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND, GstTypeFindClass)) +typedef struct _GstTypeFind GstTypeFind; +typedef struct _GstTypeFindFactory GstTypeFindFactory; +typedef struct _GstTypeFindFactoryClass GstTypeFindFactoryClass; -typedef struct _GstTypeFind GstTypeFind; -typedef struct _GstTypeFindClass GstTypeFindClass; +typedef void (* GstTypeFindFunction) (GstTypeFind *find, gpointer data); + +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); + + gpointer data; + + /* optional */ + guint64 (* get_length) (gpointer data); - GstPad *sinkpad; - GstByteStream *bs; - - GstCaps *caps; - - GST_OBJECT_PADDING + /* */ + GST_STRUCT_PADDING }; -struct _GstTypeFindClass { - GstElementClass parent_class; - - /* signals */ - void (*have_type) (GstElement *element, - GstCaps *caps); +struct _GstTypeFindFactory { + GstPluginFeature feature; + /* */ + GstTypeFindFunction function; + gchar ** extensions; + GstCaps * caps; /* FIXME: not yet saved in registry */ + + gpointer user_data; + + GST_OBJECT_PADDING +}; + +struct _GstTypeFindFactoryClass { + GstPluginFeatureClass parent; + /* */ + 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__ */ diff --git a/gst/gstutils.c b/gst/gstutils.c index 3a7d0b8837..6dd2e2d201 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -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); diff --git a/gst/registries/gstxmlregistry.c b/gst/registries/gstxmlregistry.c index 19f2576e1d..ca6b9f76da 100644 --- a/gst/registries/gstxmlregistry.c +++ b/gst/registries/gstxmlregistry.c @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include #include @@ -712,19 +712,42 @@ gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gcha } static gboolean -gst_xml_registry_parse_type_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text, - gsize text_len, GstXMLRegistry *registry, GError **error) +gst_xml_registry_parse_type_find_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text, + gsize text_len, GstXMLRegistry *registry, GError **error) { - GstTypeFactory *factory = GST_TYPE_FACTORY (registry->current_feature); + GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (registry->current_feature); if (!strcmp (tag, "name")) { registry->current_feature->name = g_strndup (text, text_len); } - else if (!strcmp (tag, "mime")) { - factory->mime = g_strndup (text, text_len); + else if (!strcmp(tag, "rank")) { + glong rank; + gchar *ret; + rank = strtol (text, &ret, 0); + if (ret == text + text_len) { + gst_element_factory_set_rank (factory, rank); + } } - else if (!strcmp(tag, "extensions")) { - factory->exts = g_strndup (text, text_len); + /* FIXME!! + else if (!strcmp (tag, "caps")) { + factory->caps = g_strndup (text, text_len); + }*/ + else if (!strcmp(tag, "extension")) { + gchar **new; + gchar **old = factory->extensions; + gint i = 0; + + /* expensive, but cycles are cheap... */ + if (old) + while (old[i]) i++; + new = g_new0 (gchar *, i + 2); + new[i] = g_strndup (text, text_len); + while (i > 0) { + i--; + new[i] = old[i]; + } + g_free (old); + factory->extensions = new; } return TRUE; @@ -905,8 +928,8 @@ gst_xml_registry_start_element (GMarkupParseContext *context, xmlregistry->parser = gst_xml_registry_parse_element_factory; break; } - else if (GST_IS_TYPE_FACTORY (feature)) { - xmlregistry->parser = gst_xml_registry_parse_type_factory; + else if (GST_IS_TYPE_FIND_FACTORY (feature)) { + xmlregistry->parser = gst_xml_registry_parse_type_find_factory; } else if (GST_IS_SCHEDULER_FACTORY (feature)) { xmlregistry->parser = gst_xml_registry_parse_scheduler_factory; @@ -1087,20 +1110,11 @@ gst_xml_registry_end_element (GMarkupParseContext *context, break; case GST_XML_REGISTRY_FEATURE: if (!strcmp (element_name, "feature")) { - if (GST_IS_TYPE_FACTORY (xmlregistry->current_feature)) { - GstTypeFactory *factory = GST_TYPE_FACTORY (xmlregistry->current_feature); - gst_type_register (factory); - } xmlregistry->state = GST_XML_REGISTRY_PLUGIN; xmlregistry->parser = gst_xml_registry_parse_plugin; gst_plugin_add_feature (xmlregistry->current_plugin, xmlregistry->current_feature); xmlregistry->current_feature = NULL; } - else if (!strcmp (element_name, "typefind")) { - GstTypeFactory *factory = GST_TYPE_FACTORY (xmlregistry->current_feature); - - factory->typefindfunc = gst_type_type_find_dummy; - } break; case GST_XML_REGISTRY_PADTEMPLATE: if (!strcmp (element_name, "padtemplate")) { @@ -1270,12 +1284,19 @@ gst_xml_registry_paths_text (GMarkupParseContext *context, const gchar *text, #define PUT_ESCAPED(tag,value) \ G_STMT_START{ \ const gchar *toconv = value; \ - if (value) { \ + if (toconv) { \ gchar *v = g_markup_escape_text (toconv, strlen (toconv)); \ CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s\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\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, "\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, "\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, "\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, "\n"); + gst_xml_registry_save_caps (xmlregistry, factory->caps); + CLASS (xmlregistry)->save_func (xmlregistry, "\n"); + } */ + while (factory->extensions[i]) { + PUT_ESCAPED ("extension", factory->extensions[i]); + i++; } } else if (GST_IS_SCHEDULER_FACTORY (feature)) { diff --git a/libs/gst/Makefile.am b/libs/gst/Makefile.am index 97d3e58050..3bd8a76a39 100644 --- a/libs/gst/Makefile.am +++ b/libs/gst/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = control getbits +SUBDIRS = control getbits bytestream -DIST_SUBDIRS = control getbits +DIST_SUBDIRS = control getbits bytestream diff --git a/libs/gst/bytestream/Makefile.am b/libs/gst/bytestream/Makefile.am new file mode 100644 index 0000000000..b8f8f4d771 --- /dev/null +++ b/libs/gst/bytestream/Makefile.am @@ -0,0 +1,10 @@ +librarydir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +library_LTLIBRARIES = libgstbytestream.la + +libgstbytestreamincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst +libgstbytestreaminclude_HEADERS = bytestream.h + +libgstbytestream_la_SOURCES = bytestream.c +libgstbytestream_la_CFLAGS = $(GST_CFLAGS) +libgstbytestream_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/gst/gstbytestream.c b/libs/gst/bytestream/bytestream.c similarity index 98% rename from gst/gstbytestream.c rename to libs/gst/bytestream/bytestream.c index 849838d86a..8476806d13 100644 --- a/gst/gstbytestream.c +++ b/libs/gst/bytestream/bytestream.c @@ -28,7 +28,9 @@ #include #include -#include +#include +#include +#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 +}; diff --git a/gst/gstbytestream.h b/libs/gst/bytestream/bytestream.h similarity index 100% rename from gst/gstbytestream.h rename to libs/gst/bytestream/bytestream.h diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index 00bd244086..0d6a67d050 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -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 \ - gstshaper.h \ - gststatistics.h \ + gstbufferstore.h \ + gstfakesink.h \ + gstfakesrc.h \ + gstfdsink.h \ + gstfdsrc.h \ + gstfilesink.h \ gstfilesrc.h \ - gstmd5sink.h + gstidentity.h \ + gstmd5sink.h \ + gstmultidisksrc.h \ + gstpipefilter.h \ + gstshaper.h \ + gststatistics.h \ + gsttee.h \ + gsttypefindelement.h diff --git a/plugins/elements/gstbufferstore.c b/plugins/elements/gstbufferstore.c new file mode 100644 index 0000000000..d7dc0b37e1 --- /dev/null +++ b/plugins/elements/gstbufferstore.c @@ -0,0 +1,465 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 + +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 + do_nothing_loop (); */ + } else if (cur_offset == offset && + GST_BUFFER_SIZE (current) == size) { + GST_LOG_OBJECT (store, "found matching buffer %p for offset %"G_GUINT64_FORMAT" and size %u", + current, offset, size); + ret = current; + gst_data_ref (GST_DATA (ret)); + GST_LOG_OBJECT (store, "refcount %d", + GST_DATA_REFCOUNT_VALUE(ret)); + break; + } else if (cur_offset + GST_BUFFER_SIZE (current) > offset) { + if (cur_offset + GST_BUFFER_SIZE (current) >= offset + size) { + ret = gst_buffer_create_sub (current, offset - cur_offset, size); + GST_LOG_OBJECT (store, "created subbuffer %p from buffer %p for offset %llu and size %u", + ret, current, offset, size); + break; + } + /* uh, the requested data spans some buffers */ + ret = gst_buffer_new_and_alloc (size); + GST_LOG_OBJECT (store, "created buffer %p for offset %"G_GUINT64_FORMAT + " and size %u, will fill with data now", + ret, offset, size); + data = GST_BUFFER_DATA (ret); + tmp = GST_BUFFER_SIZE (current) - offset + cur_offset; + memcpy (data, GST_BUFFER_DATA (current) + offset - cur_offset, tmp); + data += tmp; + size -= tmp; + while (size) { + if (walk == NULL || + (have_offset && + GST_BUFFER_OFFSET (current) + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) { + GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and remaining size %u available, aborting", + offset, size); + gst_data_unref (GST_DATA (ret)); + ret = NULL; + goto out; + } + current = GST_BUFFER (walk->data); + walk = g_list_next (walk); + tmp = MIN (GST_BUFFER_SIZE (current), size); + memcpy (data, GST_BUFFER_DATA (current), tmp); + size -= tmp; + } + } + if (!have_offset) { + cur_offset += GST_BUFFER_SIZE (current); + } + } +out: + + return ret; +} +/** + * gst_buffer_store_get_size: + * @store: a bufferstore + * @offset: desired offset + * + * Calculates the number of bytes available starting from offset. This allows + * to query a buffer with the returned size. + * + * Returns: the number of continuous bytes in the bufferstore starting at + * offset. + */ +guint +gst_buffer_store_get_size (GstBufferStore *store, guint64 offset) +{ + GList *walk; + guint64 cur_offset; + gboolean have_offset; + gboolean counting = FALSE; + GstBuffer *current; + guint ret = 0; + + g_return_val_if_fail (GST_IS_BUFFER_STORE (store), 0); + + walk = store->buffers; + if (!walk) + return 0; + if (GST_BUFFER_OFFSET_IS_VALID (walk->data)) { + have_offset = TRUE; + } else { + have_offset = FALSE; + cur_offset = 0; + } + while (walk) { + if (have_offset && counting && + cur_offset + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data)) { + break; + } + current = GST_BUFFER (walk->data); + if (have_offset) { + cur_offset = GST_BUFFER_OFFSET (current); + } + walk = g_list_next (walk); + if (counting) { + ret += GST_BUFFER_SIZE (current); + } else { + if (cur_offset > offset) + return 0; + if (cur_offset + GST_BUFFER_SIZE (current) > offset) { + /* we have at least some bytes */ + ret = cur_offset + GST_BUFFER_SIZE (current) - offset; + counting = TRUE; + } + } + if (!have_offset) { + cur_offset += GST_BUFFER_SIZE (current); + } + } + + return ret; +} diff --git a/plugins/elements/gstbufferstore.h b/plugins/elements/gstbufferstore.h new file mode 100644 index 0000000000..15fff8e6a1 --- /dev/null +++ b/plugins/elements/gstbufferstore.h @@ -0,0 +1,73 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 +#include +#include + +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__ */ diff --git a/plugins/elements/gstelements.c b/plugins/elements/gstelements.c index 78413f2161..7b769a0514 100644 --- a/plugins/elements/gstelements.c +++ b/plugins/elements/gstelements.c @@ -27,20 +27,21 @@ #include -#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; } diff --git a/plugins/elements/gstfakesrc.c b/plugins/elements/gstfakesrc.c index e992ee50f9..14287e347e 100644 --- a/plugins/elements/gstfakesrc.c +++ b/plugins/elements/gstfakesrc.c @@ -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; } } diff --git a/plugins/elements/gstfilesrc.c b/plugins/elements/gstfilesrc.c index f0de20d0a2..d3288d6b36 100644 --- a/plugins/elements/gstfilesrc.c +++ b/plugins/elements/gstfilesrc.c @@ -88,18 +88,6 @@ GstElementDetails gst_filesrc_details = { #define DEFAULT_BLOCKSIZE 4*1024 #define DEFAULT_MMAPSIZE 4*1024*1024 -#ifdef G_HAVE_ISO_VARARGS - -/* #define fs_print(...) g_print(__VA_ARGS__) */ -#define fs_print(...) - -#elif defined(G_HAVE_GNUC_VARARGS) - -/* #define fs_print(format,args...) g_print(format, ## args) */ -#define fs_print(format,args...) - -#endif - /* FileSrc signals and args */ enum { /* FILL ME */ @@ -305,7 +293,7 @@ gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, G src->mapsize = g_value_get_ulong (value); g_object_notify (G_OBJECT (src), "mmapsize"); } else { - GST_INFO ( "invalid mapsize, must a multiple of pagesize, which is %d", + GST_INFO_OBJECT (src, "invalid mapsize, must a multiple of pagesize, which is %d", src->pagesize); } break; @@ -355,8 +343,8 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf) { GstFileSrc *src = GST_FILESRC (GST_BUFFER_POOL_PRIVATE (buf)); - fs_print ("freeing mmap()d buffer at %d+%d\n", - GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf)); + GST_LOG_OBJECT (src, "freeing mmap()d buffer at %"G_GUINT64_FORMAT"+%u", + GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf)); /* remove the buffer from the list of available mmap'd regions */ g_mutex_lock (src->map_regions_lock); @@ -375,13 +363,14 @@ gst_filesrc_free_parent_mmap (GstBuffer *buf) munmap (GST_BUFFER_DATA (buf), GST_BUFFER_MAXSIZE (buf)); /* cast to unsigned long, since there's no gportable way to print * guint64 as hex */ - GST_DEBUG ( "unmapped region %08lx+%08lx at %p", + GST_LOG_OBJECT (src, "unmapped region %08lx+%08lx at %p", (unsigned long) GST_BUFFER_OFFSET (buf), (unsigned long) GST_BUFFER_MAXSIZE (buf), GST_BUFFER_DATA (buf)); GST_BUFFER_DATA (buf) = NULL; + g_object_unref (src); gst_buffer_default_free (buf); } @@ -394,7 +383,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size) g_return_val_if_fail (offset >= 0, NULL); - fs_print ("mapping region %08llx+%08lx from file into memory\n",offset,(unsigned long)size); + GST_LOG_OBJECT (src, "mapping region %08llx+%08lx from file into memory",offset,(unsigned long)size); mmapregion = mmap (NULL, size, PROT_READ, MAP_SHARED, src->fd, offset); if (mmapregion == NULL) { @@ -402,11 +391,11 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size) return NULL; } else if (mmapregion == MAP_FAILED) { - GST_DEBUG ("mmap (0x%08lx, %d, 0x%llx) : %s", + GST_WARNING_OBJECT (src, "mmap (0x%08lx, %d, 0x%llx) failed: %s", (unsigned long)size, src->fd, offset, strerror (errno)); return NULL; } - GST_DEBUG ( "mapped region %08lx+%08lx from file into memory at %p", + GST_LOG_OBJECT (src, "mapped region %08lx+%08lx from file into memory at %p", (unsigned long)offset, (unsigned long)size, mmapregion); /* time to allocate a new mapbuf */ @@ -426,6 +415,7 @@ gst_filesrc_map_region (GstFileSrc *src, off_t offset, size_t size) GST_BUFFER_OFFSET (buf) = offset; GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; GST_BUFFER_POOL_PRIVATE (buf) = src; + g_object_ref (src); GST_BUFFER_FREE_FUNC (buf) = (GstDataFreeFunction) gst_filesrc_free_parent_mmap; g_mutex_lock (src->map_regions_lock); @@ -522,7 +512,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* if the end is before the mapend, the buffer is in current mmap region... */ /* ('cause by definition if readend is in the buffer, so's readstart) */ if (readend <= mapend) { - fs_print ("read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf\n", + GST_LOG_OBJECT (src, "read buf %llu+%d lives in current mapbuf %lld+%d, creating subbuffer of mapbuf", src->curoffset, readsize, mapstart, mapsize); buf = gst_buffer_create_sub (src->mapbuf, src->curoffset - mapstart, readsize); @@ -530,8 +520,8 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* if the start actually is within the current mmap region, map an overlap buffer */ } else if (src->curoffset < mapend) { - fs_print ("read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap\n", - src->curoffset, readsize, mapstart, mapsize); + GST_LOG_OBJECT (src, "read buf %llu+%d starts in mapbuf %d+%d but ends outside, creating new mmap", + (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize); buf = gst_filesrc_map_small_region (src, src->curoffset, readsize); if (buf == NULL) return NULL; @@ -545,8 +535,8 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* either the read buffer overlaps the start of the mmap region */ /* or the read buffer fully contains the current mmap region */ /* either way, it's really not relevant, we just create a new region anyway*/ - fs_print ("read buf %llu+%d starts before mapbuf %d+%d, but overlaps it\n", - src->curoffset,readsize, mapstart, mapsize); + GST_LOG_OBJECT (src, "read buf %llu+%d starts before mapbuf %d+%d, but overlaps it", + (unsigned long long) src->curoffset, (gint) readsize, (gint) mapstart, (gint) mapsize); buf = gst_filesrc_map_small_region (src, src->curoffset, readsize); if (buf == NULL) return NULL; @@ -555,7 +545,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* then deal with the case where the read buffer is totally outside */ if (buf == NULL) { /* first check to see if there's a map that covers the right region already */ - fs_print ("searching for mapbuf to cover %llu+%d\n",src->curoffset,readsize); + GST_LOG_OBJECT (src, "searching for mapbuf to cover %llu+%d",src->curoffset,readsize); region.offset = src->curoffset; region.size = readsize; map = g_tree_search (src->map_regions, @@ -564,7 +554,8 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* if we found an exact match, subbuffer it */ if (map != NULL) { - fs_print ("found mapbuf at %d+%d, creating subbuffer\n",GST_BUFFER_OFFSET(map),GST_BUFFER_SIZE(map)); + GST_LOG_OBJECT (src, "found mapbuf at %"G_GUINT64_FORMAT"+%u, creating subbuffer", + GST_BUFFER_OFFSET (map), GST_BUFFER_SIZE (map)); buf = gst_buffer_create_sub (map, src->curoffset - GST_BUFFER_OFFSET(map), readsize); GST_BUFFER_OFFSET (buf) = src->curoffset; @@ -572,7 +563,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) } else { /* if the read buffer crosses a mmap region boundary, create a one-off region */ if ((src->curoffset / src->mapsize) != (readend / src->mapsize)) { - fs_print ("read buf %llu+%d crosses a %d-byte boundary, creating a one-off\n", + GST_LOG_OBJECT (src, "read buf %llu+%d crosses a %d-byte boundary, creating a one-off", src->curoffset,readsize,src->mapsize); buf = gst_filesrc_map_small_region (src, src->curoffset, readsize); if (buf == NULL) @@ -583,7 +574,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) size_t mapsize; off_t nextmap = src->curoffset - (src->curoffset % src->mapsize); - fs_print ("read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering\n", + GST_LOG_OBJECT (src, "read buf %llu+%d in new mapbuf at %llu+%d, mapping and subbuffering", src->curoffset, readsize, nextmap, src->mapsize); /* first, we're done with the old mapbuf */ gst_buffer_unref(src->mapbuf); @@ -591,7 +582,7 @@ gst_filesrc_get_mmap (GstFileSrc *src) /* double the mapsize as long as the readsize is smaller */ while (readsize - (src->curoffset - nextmap) > mapsize) { - fs_print ("readsize smaller then mapsize %08x %d\n", readsize, mapsize); + GST_LOG_OBJECT (src, "readsize smaller then mapsize %08x %d", readsize, mapsize); mapsize <<=1; } /* create a new one */ @@ -664,7 +655,7 @@ gst_filesrc_get (GstPad *pad) GstEvent *event; src->seek_happened = FALSE; - GST_DEBUG ("filesrc sending discont"); + GST_DEBUG_OBJECT (src, "sending discont"); event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset, NULL); src->need_flush = FALSE; return GST_DATA (event); @@ -672,13 +663,13 @@ gst_filesrc_get (GstPad *pad) /* check for flush */ if (src->need_flush) { src->need_flush = FALSE; - GST_DEBUG ("filesrc sending flush"); + GST_DEBUG_OBJECT (src, "sending flush"); return GST_DATA (gst_event_new_flush ()); } /* check for EOF */ if (src->curoffset == src->filelen) { - GST_DEBUG ("filesrc eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT, + GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT" %" G_GINT64_FORMAT, src->curoffset, src->filelen); gst_element_set_eos (GST_ELEMENT (src)); return GST_DATA (gst_event_new (GST_EVENT_EOS)); @@ -697,7 +688,7 @@ gst_filesrc_open_file (GstFileSrc *src) { g_return_val_if_fail (!GST_FLAG_IS_SET (src ,GST_FILESRC_OPEN), FALSE); - GST_DEBUG ( "opening file %s",src->filename); + GST_INFO_OBJECT (src, "opening file %s",src->filename); /* open the file */ src->fd = open (src->filename, O_RDONLY); @@ -829,7 +820,7 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event) { GstFileSrc *src = GST_FILESRC (GST_PAD_PARENT (pad)); - GST_DEBUG ( "event %d", GST_EVENT_TYPE (event)); + GST_DEBUG_OBJECT (src, "event %d", GST_EVENT_TYPE (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: @@ -847,19 +838,19 @@ gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event) if (offset > src->filelen) goto error; src->curoffset = offset; - GST_DEBUG ( "seek set pending to %" G_GINT64_FORMAT, src->curoffset); + GST_DEBUG_OBJECT (src, "seek set pending to %" G_GINT64_FORMAT, src->curoffset); break; case GST_SEEK_METHOD_CUR: if (offset + src->curoffset > src->filelen) goto error; src->curoffset += offset; - GST_DEBUG ( "seek cur pending to %" G_GINT64_FORMAT, src->curoffset); + GST_DEBUG_OBJECT (src, "seek cur pending to %" G_GINT64_FORMAT, src->curoffset); break; case GST_SEEK_METHOD_END: if (ABS (offset) > src->filelen) goto error; src->curoffset = src->filelen - ABS (offset); - GST_DEBUG ( "seek end pending to %" G_GINT64_FORMAT, src->curoffset); + GST_DEBUG_OBJECT (src, "seek end pending to %" G_GINT64_FORMAT, src->curoffset); break; default: goto error; diff --git a/plugins/elements/gsttypefind.c b/plugins/elements/gsttypefind.c new file mode 100644 index 0000000000..bd5e3d367b --- /dev/null +++ b/plugins/elements/gsttypefind.c @@ -0,0 +1,669 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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_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 ", + "(C) 2003", +}; + +/* generic templates */ +GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); +GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); + +/* TypeFind signals and args */ +enum { + HAVE_TYPE, + LAST_SIGNAL +}; +enum { + ARG_0, + ARG_CAPS, + ARG_MINIMUM, + ARG_MAXIMUM +}; +enum { + MODE_NORMAL, /* act as identity */ + MODE_TYPEFIND, /* do typefinding */ +}; + + +static void gst_type_find_element_class_init (gpointer g_class, + gpointer class_data); +static void gst_type_find_element_init (GTypeInstance *instance, + gpointer g_class); +static void gst_type_find_element_dispose (GObject * object); +static void gst_type_find_element_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void gst_type_find_element_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +static const GstEventMask * + gst_type_find_element_src_event_mask (GstPad * pad); +static gboolean gst_type_find_element_src_event (GstPad * pad, + GstEvent * event); + +static void gst_type_find_element_chain (GstPad * sinkpad, + GstData * data); +static GstElementStateReturn + gst_type_find_element_change_state (GstElement * element); + +static GstElementClass *parent_class = NULL; +static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_type_find_element_get_type (void) +{ + static GType typefind_type = 0; + + if (!typefind_type) { + static const GTypeInfo typefind_info = { + sizeof (GstTypeFindElementClass), + NULL, + NULL, + gst_type_find_element_class_init, + NULL, + NULL, + sizeof (GstTypeFindElement), + 0, + gst_type_find_element_init, + NULL + }; + typefind_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstTypeFindElement", + &typefind_info, 0); + } + return typefind_type; +} +static void +gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps) +{ + gchar *caps_str; + + g_assert (typefind->caps == NULL); + g_assert (caps != NULL); + + caps_str = gst_caps_to_string (caps); + GST_INFO_OBJECT (typefind, "found caps %s", caps_str); + g_free (caps_str); + gst_caps_replace (&typefind->caps, caps); + if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) { + gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad"); + } +} +static void +gst_type_find_element_class_init (gpointer g_class, gpointer class_data) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstTypeFindElementClass *typefind_class; + + gobject_class = G_OBJECT_CLASS (g_class); + gstelement_class = GST_ELEMENT_CLASS (g_class); + typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class); + + parent_class = g_type_class_peek_parent (g_class); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property); + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose); + + typefind_class->have_type = gst_type_find_element_have_type; + + g_object_class_install_property (gobject_class, ARG_CAPS, + g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"), + GST_TYPE_CAPS, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE)); + + gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", + G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL, + gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2, + G_TYPE_UINT, GST_TYPE_CAPS); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state); +} +static void +gst_type_find_element_init (GTypeInstance *instance, gpointer g_class) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance); + + /* sinkpad */ + typefind->sink = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink"); + gst_pad_set_chain_function (typefind->sink, + gst_type_find_element_chain); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink); + /* srcpad */ + typefind->src = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src"); + gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event); + gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->src); + + typefind->caps = NULL; + typefind->min_probability = 1; + typefind->max_probability = GST_TYPE_FIND_MAXIMUM; + + typefind->store = gst_buffer_store_new (); + + GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE); +} +static void +gst_type_find_element_dispose (GObject *object) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object); + + G_OBJECT_CLASS (parent_class)->dispose (object); + + if (typefind->store) { + g_object_unref (typefind->store); + typefind->store = NULL; + } +} +static void +gst_type_find_element_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_MINIMUM: + typefind->min_probability = g_value_get_uint (value); + g_object_notify (object, "minimum"); + break; + case ARG_MAXIMUM: + typefind->max_probability = g_value_get_uint (value); + g_object_notify (object, "maximum"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static void +gst_type_find_element_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_CAPS: + g_value_set_boxed (value, typefind->caps); + break; + case ARG_MINIMUM: + g_value_set_uint (value, typefind->min_probability); + break; + case ARG_MAXIMUM: + g_value_set_uint (value, typefind->max_probability); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static const GstEventMask * +gst_type_find_element_src_event_mask (GstPad *pad) +{ + static const GstEventMask mask[] = { + { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH}, + /* add more if you want, event masks suck and need to die anyway */ + { 0, } + }; + + return mask; +} +static gboolean +gst_type_find_element_src_event (GstPad *pad, GstEvent *event) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + gst_data_unref (GST_DATA (event)); + return FALSE; + } + return gst_pad_event_default (pad, event); +} +typedef struct { + GstTypeFindFactory * factory; + gint probability; + GstCaps * caps; + gint64 requested_offset; + guint requested_size; + + GList * buffers; + GstTypeFindElement * self; +} TypeFindEntry; +static void +free_entry_buffers (TypeFindEntry *entry) +{ + g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL); + g_list_free (entry->buffers); + entry->buffers = NULL; +} +static void +free_entry (TypeFindEntry *entry) +{ + free_entry_buffers (entry); + + if (entry->caps) + gst_caps_unref (entry->caps); + g_free (entry); +} +static void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + g_assert (typefind->possibilities == NULL); + + GST_DEBUG_OBJECT (typefind, "starting typefinding"); + typefind->mode = MODE_TYPEFIND; + typefind->stream_length_available = TRUE; + typefind->stream_length = 0; +} +static void +stop_typefinding (GstTypeFindElement *typefind) +{ + /* stop all typefinding and set mode back to normal */ + gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING; + + GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : ""); + if (typefind->possibilities != NULL) { + /* this should only happen on PAUSED => READY or EOS */ + GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities)); + g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + } + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + GstBuffer *buffer; + guint size = gst_buffer_store_get_size (typefind->store, 0); + if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) { + gst_pad_push (typefind->src, GST_DATA (buffer)); + } else { + size = 0; + } + GST_LOG_OBJECT (typefind, "seeking back to current position %u", size); + if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) { + GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size); + } + } + gst_buffer_store_clear (typefind->store); +} +static guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + if (!typefind->stream_length_available) { + GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, + &format, &entry->self->stream_length); + if (format != GST_FORMAT_BYTES) + typefind->stream_length_available = FALSE; + if (!typefind->stream_length_available) { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } else { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length); + } + } + + return entry->self->stream_length; +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + TypeFindEntry *entry; + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* this should only happen when we got all available data */ + entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL; + if (entry && entry->probability >= typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + } + stop_typefinding (typefind); + gst_pad_event_default (pad, event); + break; + default: + gst_data_unref (GST_DATA (event)); + break; + } + } else { + gst_pad_event_default (pad, event); + } +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size); + if (offset >= 0) { + buf = gst_buffer_store_get_buffer (entry->self->store, offset, size); + } else { + /* FIXME: can we do this easily without querying length? */ + guint64 length = find_element_get_length (data); + + if (length == 0) { + buf = NULL; + } else { + buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size); + } + } + + if (buf) { + entry->buffers = g_list_prepend (entry->buffers, buf); + return GST_BUFFER_DATA (buf); + } else { + if (entry->requested_size == 0) { + GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", + offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory)); + entry->requested_offset = offset; + entry->requested_size = size; + } + return NULL; + } +} +static void +find_suggest (gpointer data, guint probability, GstCaps *caps) +{ + gchar *str; + TypeFindEntry *entry = (TypeFindEntry *) data; + + str = gst_caps_to_string (caps); + GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str); + g_free (str); + if (((gint) probability) > entry->probability) { + entry->probability = probability; + gst_caps_replace (&entry->caps, caps); + } +} +static gint +compare_type_find_entry (gconstpointer a, gconstpointer b) +{ + TypeFindEntry *one = (TypeFindEntry *) a; + TypeFindEntry *two = (TypeFindEntry *) b; + + if (one->probability == two->probability) { + /* FIXME: can be improved by analyzing requests */ + return 0; + } else { + return two->probability - one->probability; + } +} +static gint +compare_type_find_factory (gconstpointer fac1, gconstpointer fac2) +{ + return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank; +} +static void +gst_type_find_element_chain (GstPad *pad, GstData *data) +{ + GstTypeFindElement *typefind; + GList *entries; + TypeFindEntry *entry; + GList *walk; + GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length }; + + typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + if (GST_IS_EVENT (data)) { + gst_type_find_element_handle_event (pad, GST_EVENT (data)); + return; + } + switch (typefind->mode) { + case MODE_NORMAL: + gst_pad_push (typefind->src, data); + return; + case MODE_TYPEFIND: { + gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data)); + gst_data_unref (data); + if (typefind->possibilities == NULL) { + /* not yet started, get all typefinding functions into our "queue" */ + GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + all_factories = g_list_sort (all_factories, compare_type_find_factory); + walk = all_factories; + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = 0; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + g_list_free (all_factories); + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries)); + typefind->possibilities = NULL; + while (walk) { + find.data = entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + entry->probability = 0; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == 0 && entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + } else if (entry->probability >= typefind->max_probability) { + /* wooha, got caps */ + GstCaps *found_caps = entry->caps; + guint probability = entry->probability; + + gst_caps_ref (found_caps); + GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability); + while (walk) { + free_entry ((TypeFindEntry *) walk->data); + walk = g_list_next (walk); + } + walk = typefind->possibilities; + while (walk) { + free_entry (walk->data); + walk = g_list_next (walk); + } + typefind->possibilities = NULL; + g_list_free (typefind->possibilities); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps); + gst_caps_unref (found_caps); + } else { + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + } + } + g_list_free (entries); + /* we may now already have caps or we might be left without functions to try */ + if (typefind->caps) { + stop_typefinding (typefind); + } else if (typefind->possibilities == NULL) { + gst_element_error (GST_ELEMENT (typefind), "media type could not be detected"); + } else { + /* set up typefind element for next iteration */ + typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry); + + walk = typefind->possibilities; + while (walk) { + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size > 0) { + /* FIXME: need heuristic to find out if we should seek */ + gint64 seek_offset; + GstEvent *event; + + seek_offset = entry->requested_offset > 0 ? entry->requested_offset : + find_element_get_length (entry) + entry->requested_offset; + seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset); + event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + break; + } else if (entry->requested_offset < 0) { + /* impossible to seek */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + entry->requested_size = 0; + entry->requested_offset = 0; + } + } + } + /* throw out all entries that can't get more data */ + walk = g_list_next (typefind->possibilities); + while (walk) { + GList *cur = walk; + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + if (entry->probability > typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind); + } + } + } + break; + } + default: + g_assert_not_reached (); + return; + } +} +static GstElementStateReturn +gst_type_find_element_change_state (GstElement *element) +{ + GstTypeFindElement *typefind; + + typefind = GST_TYPE_FIND_ELEMENT (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + start_typefinding (typefind); + break; + case GST_STATE_PAUSED_TO_READY: + stop_typefinding (typefind); + gst_caps_replace (&typefind->caps, NULL); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element); +} diff --git a/plugins/elements/gsttypefind.h b/plugins/elements/gsttypefind.h new file mode 100644 index 0000000000..4b5e0cc40f --- /dev/null +++ b/plugins/elements/gsttypefind.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 +#include +/* #include */ +#include "gstbufferstore.h" + +G_BEGIN_DECLS + +GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug); + +extern GstElementDetails gst_type_find_element_details; + +#define GST_TYPE_TYPE_FIND_ELEMENT (gst_type_find_element_get_type ()) +#define GST_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement)) +#define GST_IS_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) +#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) + +typedef struct _GstTypeFindElement GstTypeFindElement; +typedef struct _GstTypeFindElementClass GstTypeFindElementClass; + +struct _GstTypeFindElement { + GstElement element; + + GstPad * sink; + GstPad * src; + + guint min_probability; + guint max_probability; + GstCaps * caps; + + guint mode; + GstBufferStore * store; + guint64 stream_length; + gboolean stream_length_available; + + GList * possibilities; +}; + +struct _GstTypeFindElementClass { + GstElementClass parent_class; + + /* signals */ + void (*have_type) (GstTypeFindElement *element, + guint probability, + GstCaps * caps); +}; + +GType gst_type_find_element_get_type (void); + +G_END_DECLS + +#endif /* __GST_TYPE_FIND_ELEMENT_H__ */ diff --git a/plugins/elements/gsttypefindelement.c b/plugins/elements/gsttypefindelement.c new file mode 100644 index 0000000000..bd5e3d367b --- /dev/null +++ b/plugins/elements/gsttypefindelement.c @@ -0,0 +1,669 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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_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 ", + "(C) 2003", +}; + +/* generic templates */ +GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); +GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_ANY +); + +/* TypeFind signals and args */ +enum { + HAVE_TYPE, + LAST_SIGNAL +}; +enum { + ARG_0, + ARG_CAPS, + ARG_MINIMUM, + ARG_MAXIMUM +}; +enum { + MODE_NORMAL, /* act as identity */ + MODE_TYPEFIND, /* do typefinding */ +}; + + +static void gst_type_find_element_class_init (gpointer g_class, + gpointer class_data); +static void gst_type_find_element_init (GTypeInstance *instance, + gpointer g_class); +static void gst_type_find_element_dispose (GObject * object); +static void gst_type_find_element_set_property (GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void gst_type_find_element_get_property (GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec); + +static const GstEventMask * + gst_type_find_element_src_event_mask (GstPad * pad); +static gboolean gst_type_find_element_src_event (GstPad * pad, + GstEvent * event); + +static void gst_type_find_element_chain (GstPad * sinkpad, + GstData * data); +static GstElementStateReturn + gst_type_find_element_change_state (GstElement * element); + +static GstElementClass *parent_class = NULL; +static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_type_find_element_get_type (void) +{ + static GType typefind_type = 0; + + if (!typefind_type) { + static const GTypeInfo typefind_info = { + sizeof (GstTypeFindElementClass), + NULL, + NULL, + gst_type_find_element_class_init, + NULL, + NULL, + sizeof (GstTypeFindElement), + 0, + gst_type_find_element_init, + NULL + }; + typefind_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstTypeFindElement", + &typefind_info, 0); + } + return typefind_type; +} +static void +gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps) +{ + gchar *caps_str; + + g_assert (typefind->caps == NULL); + g_assert (caps != NULL); + + caps_str = gst_caps_to_string (caps); + GST_INFO_OBJECT (typefind, "found caps %s", caps_str); + g_free (caps_str); + gst_caps_replace (&typefind->caps, caps); + if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) { + gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad"); + } +} +static void +gst_type_find_element_class_init (gpointer g_class, gpointer class_data) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstTypeFindElementClass *typefind_class; + + gobject_class = G_OBJECT_CLASS (g_class); + gstelement_class = GST_ELEMENT_CLASS (g_class); + typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class); + + parent_class = g_type_class_peek_parent (g_class); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property); + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose); + + typefind_class->have_type = gst_type_find_element_have_type; + + g_object_class_install_property (gobject_class, ARG_CAPS, + g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"), + GST_TYPE_CAPS, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, ARG_MINIMUM, + g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding", + GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE)); + + gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", + G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL, + gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2, + G_TYPE_UINT, GST_TYPE_CAPS); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state); +} +static void +gst_type_find_element_init (GTypeInstance *instance, gpointer g_class) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance); + + /* sinkpad */ + typefind->sink = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink"); + gst_pad_set_chain_function (typefind->sink, + gst_type_find_element_chain); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink); + /* srcpad */ + typefind->src = gst_pad_new_from_template ( + GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src"); + gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event); + gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask); + gst_element_add_pad (GST_ELEMENT (typefind), typefind->src); + + typefind->caps = NULL; + typefind->min_probability = 1; + typefind->max_probability = GST_TYPE_FIND_MAXIMUM; + + typefind->store = gst_buffer_store_new (); + + GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE); +} +static void +gst_type_find_element_dispose (GObject *object) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object); + + G_OBJECT_CLASS (parent_class)->dispose (object); + + if (typefind->store) { + g_object_unref (typefind->store); + typefind->store = NULL; + } +} +static void +gst_type_find_element_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_MINIMUM: + typefind->min_probability = g_value_get_uint (value); + g_object_notify (object, "minimum"); + break; + case ARG_MAXIMUM: + typefind->max_probability = g_value_get_uint (value); + g_object_notify (object, "maximum"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static void +gst_type_find_element_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstTypeFindElement *typefind; + + g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object)); + + typefind = GST_TYPE_FIND_ELEMENT (object); + + switch (prop_id) { + case ARG_CAPS: + g_value_set_boxed (value, typefind->caps); + break; + case ARG_MINIMUM: + g_value_set_uint (value, typefind->min_probability); + break; + case ARG_MAXIMUM: + g_value_set_uint (value, typefind->max_probability); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static const GstEventMask * +gst_type_find_element_src_event_mask (GstPad *pad) +{ + static const GstEventMask mask[] = { + { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH}, + /* add more if you want, event masks suck and need to die anyway */ + { 0, } + }; + + return mask; +} +static gboolean +gst_type_find_element_src_event (GstPad *pad, GstEvent *event) +{ + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + gst_data_unref (GST_DATA (event)); + return FALSE; + } + return gst_pad_event_default (pad, event); +} +typedef struct { + GstTypeFindFactory * factory; + gint probability; + GstCaps * caps; + gint64 requested_offset; + guint requested_size; + + GList * buffers; + GstTypeFindElement * self; +} TypeFindEntry; +static void +free_entry_buffers (TypeFindEntry *entry) +{ + g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL); + g_list_free (entry->buffers); + entry->buffers = NULL; +} +static void +free_entry (TypeFindEntry *entry) +{ + free_entry_buffers (entry); + + if (entry->caps) + gst_caps_unref (entry->caps); + g_free (entry); +} +static void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + g_assert (typefind->possibilities == NULL); + + GST_DEBUG_OBJECT (typefind, "starting typefinding"); + typefind->mode = MODE_TYPEFIND; + typefind->stream_length_available = TRUE; + typefind->stream_length = 0; +} +static void +stop_typefinding (GstTypeFindElement *typefind) +{ + /* stop all typefinding and set mode back to normal */ + gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING; + + GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : ""); + if (typefind->possibilities != NULL) { + /* this should only happen on PAUSED => READY or EOS */ + GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities)); + g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + } + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + GstBuffer *buffer; + guint size = gst_buffer_store_get_size (typefind->store, 0); + if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) { + gst_pad_push (typefind->src, GST_DATA (buffer)); + } else { + size = 0; + } + GST_LOG_OBJECT (typefind, "seeking back to current position %u", size); + if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) { + GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size); + } + } + gst_buffer_store_clear (typefind->store); +} +static guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + if (!typefind->stream_length_available) { + GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, + &format, &entry->self->stream_length); + if (format != GST_FORMAT_BYTES) + typefind->stream_length_available = FALSE; + if (!typefind->stream_length_available) { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + return 0; + } else { + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length); + } + } + + return entry->self->stream_length; +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + TypeFindEntry *entry; + GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + + if (typefind->mode == MODE_TYPEFIND) { + /* need to do more? */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* this should only happen when we got all available data */ + entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL; + if (entry && entry->probability >= typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + } + stop_typefinding (typefind); + gst_pad_event_default (pad, event); + break; + default: + gst_data_unref (GST_DATA (event)); + break; + } + } else { + gst_pad_event_default (pad, event); + } +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size); + if (offset >= 0) { + buf = gst_buffer_store_get_buffer (entry->self->store, offset, size); + } else { + /* FIXME: can we do this easily without querying length? */ + guint64 length = find_element_get_length (data); + + if (length == 0) { + buf = NULL; + } else { + buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size); + } + } + + if (buf) { + entry->buffers = g_list_prepend (entry->buffers, buf); + return GST_BUFFER_DATA (buf); + } else { + if (entry->requested_size == 0) { + GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", + offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory)); + entry->requested_offset = offset; + entry->requested_size = size; + } + return NULL; + } +} +static void +find_suggest (gpointer data, guint probability, GstCaps *caps) +{ + gchar *str; + TypeFindEntry *entry = (TypeFindEntry *) data; + + str = gst_caps_to_string (caps); + GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str); + g_free (str); + if (((gint) probability) > entry->probability) { + entry->probability = probability; + gst_caps_replace (&entry->caps, caps); + } +} +static gint +compare_type_find_entry (gconstpointer a, gconstpointer b) +{ + TypeFindEntry *one = (TypeFindEntry *) a; + TypeFindEntry *two = (TypeFindEntry *) b; + + if (one->probability == two->probability) { + /* FIXME: can be improved by analyzing requests */ + return 0; + } else { + return two->probability - one->probability; + } +} +static gint +compare_type_find_factory (gconstpointer fac1, gconstpointer fac2) +{ + return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank; +} +static void +gst_type_find_element_chain (GstPad *pad, GstData *data) +{ + GstTypeFindElement *typefind; + GList *entries; + TypeFindEntry *entry; + GList *walk; + GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length }; + + typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad)); + if (GST_IS_EVENT (data)) { + gst_type_find_element_handle_event (pad, GST_EVENT (data)); + return; + } + switch (typefind->mode) { + case MODE_NORMAL: + gst_pad_push (typefind->src, data); + return; + case MODE_TYPEFIND: { + gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data)); + gst_data_unref (data); + if (typefind->possibilities == NULL) { + /* not yet started, get all typefinding functions into our "queue" */ + GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + all_factories = g_list_sort (all_factories, compare_type_find_factory); + walk = all_factories; + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = 0; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + g_list_free (all_factories); + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries)); + typefind->possibilities = NULL; + while (walk) { + find.data = entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + entry->probability = 0; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == 0 && entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + } else if (entry->probability >= typefind->max_probability) { + /* wooha, got caps */ + GstCaps *found_caps = entry->caps; + guint probability = entry->probability; + + gst_caps_ref (found_caps); + GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", + GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability); + while (walk) { + free_entry ((TypeFindEntry *) walk->data); + walk = g_list_next (walk); + } + walk = typefind->possibilities; + while (walk) { + free_entry (walk->data); + walk = g_list_next (walk); + } + typefind->possibilities = NULL; + g_list_free (typefind->possibilities); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps); + gst_caps_unref (found_caps); + } else { + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + } + } + g_list_free (entries); + /* we may now already have caps or we might be left without functions to try */ + if (typefind->caps) { + stop_typefinding (typefind); + } else if (typefind->possibilities == NULL) { + gst_element_error (GST_ELEMENT (typefind), "media type could not be detected"); + } else { + /* set up typefind element for next iteration */ + typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry); + + walk = typefind->possibilities; + while (walk) { + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size > 0) { + /* FIXME: need heuristic to find out if we should seek */ + gint64 seek_offset; + GstEvent *event; + + seek_offset = entry->requested_offset > 0 ? entry->requested_offset : + find_element_get_length (entry) + entry->requested_offset; + seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset); + event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + break; + } else if (entry->requested_offset < 0) { + /* impossible to seek */ + GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, + GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset); + entry->requested_size = 0; + entry->requested_offset = 0; + } + } + } + /* throw out all entries that can't get more data */ + walk = g_list_next (typefind->possibilities); + while (walk) { + GList *cur = walk; + entry = (TypeFindEntry *) walk->data; + walk = g_list_next (walk); + if (entry->requested_size == 0) { + GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + if (entry->probability > typefind->min_probability) { + GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", + GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability); + g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind); + } + } + } + break; + } + default: + g_assert_not_reached (); + return; + } +} +static GstElementStateReturn +gst_type_find_element_change_state (GstElement *element) +{ + GstTypeFindElement *typefind; + + typefind = GST_TYPE_FIND_ELEMENT (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + start_typefinding (typefind); + break; + case GST_STATE_PAUSED_TO_READY: + stop_typefinding (typefind); + gst_caps_replace (&typefind->caps, NULL); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element); +} diff --git a/plugins/elements/gsttypefindelement.h b/plugins/elements/gsttypefindelement.h new file mode 100644 index 0000000000..4b5e0cc40f --- /dev/null +++ b/plugins/elements/gsttypefindelement.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * 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 +#include +/* #include */ +#include "gstbufferstore.h" + +G_BEGIN_DECLS + +GST_DEBUG_CATEGORY_EXTERN(gst_type_find_element_debug); + +extern GstElementDetails gst_type_find_element_details; + +#define GST_TYPE_TYPE_FIND_ELEMENT (gst_type_find_element_get_type ()) +#define GST_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElement)) +#define GST_IS_TYPE_FIND_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) +#define GST_IS_TYPE_FIND_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TYPE_FIND_ELEMENT)) +#define GST_TYPE_FIND_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TYPE_FIND_ELEMENT, GstTypeFindElementClass)) + +typedef struct _GstTypeFindElement GstTypeFindElement; +typedef struct _GstTypeFindElementClass GstTypeFindElementClass; + +struct _GstTypeFindElement { + GstElement element; + + GstPad * sink; + GstPad * src; + + guint min_probability; + guint max_probability; + GstCaps * caps; + + guint mode; + GstBufferStore * store; + guint64 stream_length; + gboolean stream_length_available; + + GList * possibilities; +}; + +struct _GstTypeFindElementClass { + GstElementClass parent_class; + + /* signals */ + void (*have_type) (GstTypeFindElement *element, + guint probability, + GstCaps * caps); +}; + +GType gst_type_find_element_get_type (void); + +G_END_DECLS + +#endif /* __GST_TYPE_FIND_ELEMENT_H__ */ diff --git a/tools/gst-inspect.c b/tools/gst-inspect.c index c92f72bf74..f2dbdfb5cd 100644 --- a/tools/gst-inspect.c +++ b/tools/gst-inspect.c @@ -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 diff --git a/tools/gst-register.c b/tools/gst-register.c index e3828a4fdc..a584015996 100644 --- a/tools/gst-register.c +++ b/tools/gst-register.c @@ -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); } diff --git a/tools/gst-typefind.c b/tools/gst-typefind.c index fd65713622..fc0551ac47 100644 --- a/tools/gst-typefind.c +++ b/tools/gst-typefind.c @@ -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); - /* set to play */ - gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); + while (i < argc) { + FOUND = FALSE; + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL); + filename = argv[i]; + g_object_set (source, "location", filename, NULL); + /* set to play */ + gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); - while (!FOUND){ - gst_bin_iterate (GST_BIN (pipeline)); - iterations++; - if(iterations >= max_iterations){ - break; + while (!FOUND) { + if (!gst_bin_iterate (GST_BIN (pipeline))) + break; } + if (!FOUND) { + g_print ("%s - No type found\n", argv[i]); + } + i++; } - if (!FOUND) { - g_print ("No type found\n"); - return 1; - } + g_object_unref (pipeline); return 0; } diff --git a/tools/gst-xmlinspect.c b/tools/gst-xmlinspect.c index f6620e8ae5..81052e891a 100644 --- a/tools/gst-xmlinspect.c +++ b/tools/gst-xmlinspect.c @@ -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