From 493ba84f4cd8b12a08a98d5432029cd547c2cfc4 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 23 Oct 2003 01:27:07 +0000 Subject: [PATCH] initial checkin of typefind rewrite - doesn't work yet, but you may want to fix it :) - it compiles though Original commit message from CVS: initial checkin of typefind rewrite - doesn't work yet, but you may want to fix it :) - it compiles though --- configure.ac | 6 +- gst/Makefile.am | 20 +- gst/autoplug/gstspideridentity.c | 102 ++-- gst/elements/Makefile.am | 49 +- gst/elements/gstbufferstore.c | 427 +++++++++++++ gst/elements/gstbufferstore.h | 73 +++ gst/elements/gstelements.c | 43 +- gst/elements/gsttypefind.c | 563 ++++++++++++++++++ gst/elements/gsttypefind.h | 78 +++ gst/elements/gsttypefindelement.c | 563 ++++++++++++++++++ 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/gstmarshal.list | 2 + gst/gstpad.c | 1 - gst/gstpluginfeature.c | 24 +- gst/gstpluginfeature.h | 1 + gst/gsttypefind.c | 435 ++++++++------ gst/gsttypefind.h | 115 +++- gst/gstutils.c | 6 +- gst/registries/gstxmlregistry.c | 82 ++- gst/types/.gitignore | 7 - gst/types/Makefile.am | 8 - gst/types/gsttypes.c | 55 -- libs/gst/Makefile.am | 4 +- libs/gst/bytestream/Makefile.am | 10 + .../gst/bytestream/bytestream.c | 18 +- .../gst/bytestream/bytestream.h | 0 plugins/elements/Makefile.am | 49 +- plugins/elements/gstbufferstore.c | 427 +++++++++++++ plugins/elements/gstbufferstore.h | 73 +++ plugins/elements/gstelements.c | 43 +- plugins/elements/gsttypefind.c | 563 ++++++++++++++++++ plugins/elements/gsttypefind.h | 78 +++ plugins/elements/gsttypefindelement.c | 563 ++++++++++++++++++ plugins/elements/gsttypefindelement.h | 78 +++ tools/gst-inspect.c | 59 +- tools/gst-register.c | 2 +- tools/gst-xmlinspect.c | 55 +- 44 files changed, 4253 insertions(+), 624 deletions(-) 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 delete mode 100644 gst/types/.gitignore delete mode 100644 gst/types/Makefile.am delete mode 100644 gst/types/gsttypes.c 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/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..474dae5eb6 100644 --- a/gst/autoplug/gstspideridentity.c +++ b/gst/autoplug/gstspideridentity.c @@ -410,82 +410,96 @@ 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)) { + return GST_BUFFER_DATA (find->buffer) + offset - buffer_offset; + } else { + return NULL; + } +} +void spider_find_suggest (gpointer data, guint probability, GstCaps *caps) +{ + SpiderTypeFind *find = (SpiderTypeFind *) data; + + 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; + GList *type_list = NULL; + GstTypeFind gst_find; + SpiderTypeFind find; 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); } - + /* 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 (); + type_list = gst_type_find_factory_get_list (); + find.buffer = GST_BUFFER (data); + find.best_probability = 0; + find.caps = NULL; + gst_find.data = &find; + gst_find.peek = spider_find_peek; + gst_find.suggest = spider_find_suggest; while (type_list) { - GstType *type = (GstType *) type_list->data; - GSList *factories = type->factories; + GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (type_list->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); - } + 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; type_list = g_list_next (type_list); } + if (find.best_probability > 0) + goto plug; gst_element_error(GST_ELEMENT(ident), "Could not find media type", NULL); - buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS)); + find.buffer = GST_BUFFER (gst_event_new (GST_EVENT_EOS)); end: - /* remove loop function */ gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction) GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop)); /* push the buffer */ - gst_spider_identity_chain (ident->sink, buf); - - /* bytestream no longer needed */ - gst_bytestream_destroy (bs); + gst_spider_identity_chain (ident->sink, find.buffer); return; plug: - gst_caps_debug (caps, "spider starting caps"); - gst_caps_sink (caps); + GST_INFO ("typefind function found caps"); + g_assert (gst_pad_try_set_caps (ident->src, find.caps) > 0); + gst_caps_debug (find.caps, "spider starting caps"); + gst_caps_unref (find.caps); 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..ec2324b759 --- /dev/null +++ b/gst/elements/gstbufferstore.c @@ -0,0 +1,427 @@ +/* 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 }; + +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) +{ + 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 */ + 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_data_ref (GST_DATA (buffer)); + 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)); + /* 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_OFFSET (current)) { + g_assert (needed_size == GST_BUFFER_OFFSET (current)); /* we have no overlapping data */ + buffer = NULL; + } 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); + buffer = sub; + } else { + gst_data_ref (GST_DATA (buffer)); + } + /* replace current buffer with new one */ + 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)); + buffer = sub; + } else { + gst_data_ref (GST_DATA (buffer)); + } + store->buffers = g_list_insert_before (store->buffers, walk, buffer); + buffer = NULL; + break; + } + } + } + if (buffer) { + gst_data_ref (GST_DATA (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); + + 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 && + 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)); + 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 && + cur_offset + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) { + GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and size %u available, aborting", + offset, size); + gst_data_unref (GST_DATA (ret)); + ret = NULL; + break; + } + 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); + } + } + + 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 && + 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..b78f404018 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", 0, "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/gsttypefind.c b/gst/elements/gsttypefind.c new file mode 100644 index 0000000000..31f1be0b9a --- /dev/null +++ b/gst/elements/gsttypefind.c @@ -0,0 +1,563 @@ +/* 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. + */ + +/* 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 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); + + 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_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); + + 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); + + typefind_class->have_type = gst_type_find_element_have_type; +} +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_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); + + 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 void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + + typefind->mode = MODE_TYPEFIND; +} +static void +stop_typefinding (GstTypeFindElement *typefind, gboolean push_cached_buffers) +{ + /* stop all typefinding and set mode back to normal */ + g_assert (typefind->possibilities == NULL); + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + guint size = gst_buffer_store_get_size (typefind->store, 0); + GstBuffer *buffer = gst_buffer_store_get_buffer (typefind->store, 0, size); + if (buffer) { + gst_pad_push (typefind->sink, GST_DATA (buffer)); + } else { + size = 0; + } + gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET, size)); + } + gst_buffer_store_clear (typefind->store); +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + 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 guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length ()", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + if (!typefind->stream_length_available) + return 0; + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (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) + return 0; + } + + return entry->self->stream_length; +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_DEBUG_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) { + 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_DEBUG_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 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" */ + const GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = -1; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_DEBUG_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 = -1; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == -1) { + 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_LOG_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], 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, TRUE); + } 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 */ + GstEvent *event = gst_event_new_seek (entry->requested_offset < 0 ? + GST_SEEK_METHOD_SET : GST_SEEK_METHOD_END, entry->requested_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + break; + } else { + /* impossible to seek */ + 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) { + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + GST_LOG_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], entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind, TRUE); + } + } + 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, FALSE); + 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..31f1be0b9a --- /dev/null +++ b/gst/elements/gsttypefindelement.c @@ -0,0 +1,563 @@ +/* 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. + */ + +/* 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 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); + + 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_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); + + 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); + + typefind_class->have_type = gst_type_find_element_have_type; +} +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_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); + + 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 void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + + typefind->mode = MODE_TYPEFIND; +} +static void +stop_typefinding (GstTypeFindElement *typefind, gboolean push_cached_buffers) +{ + /* stop all typefinding and set mode back to normal */ + g_assert (typefind->possibilities == NULL); + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + guint size = gst_buffer_store_get_size (typefind->store, 0); + GstBuffer *buffer = gst_buffer_store_get_buffer (typefind->store, 0, size); + if (buffer) { + gst_pad_push (typefind->sink, GST_DATA (buffer)); + } else { + size = 0; + } + gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET, size)); + } + gst_buffer_store_clear (typefind->store); +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + 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 guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length ()", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + if (!typefind->stream_length_available) + return 0; + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (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) + return 0; + } + + return entry->self->stream_length; +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_DEBUG_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) { + 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_DEBUG_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 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" */ + const GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = -1; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_DEBUG_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 = -1; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == -1) { + 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_LOG_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], 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, TRUE); + } 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 */ + GstEvent *event = gst_event_new_seek (entry->requested_offset < 0 ? + GST_SEEK_METHOD_SET : GST_SEEK_METHOD_END, entry->requested_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + break; + } else { + /* impossible to seek */ + 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) { + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + GST_LOG_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], entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind, TRUE); + } + } + 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, FALSE); + 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/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..a1d0cac5b3 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" 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..6c16a2154c 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,304 @@ * 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 -); - -/* TypeFind signals and args */ +/* GstTypeFind flags */ enum { - HAVE_TYPE, - LAST_SIGNAL + FLAG_OFFSET = (1 << 0), + FLAG_SIZE = (1 << 1), + FLAG_CAPS = (1 << 2), + FLAG_EXTENSIONS = (1 << 3) }; -enum { - ARG_0, - ARG_CAPS, -}; +static void gst_type_find_factory_class_init (gpointer g_class, + gpointer class_data); +static void gst_type_find_factory_init (GTypeInstance * instance, + gpointer g_class); +static void gst_type_find_factory_dispose (GObject * object); +static void gst_type_find_factory_unload_thyself (GstPluginFeature * feature); -static void gst_type_find_class_init (GstTypeFindClass *klass); -static void gst_type_find_init (GstTypeFind *typefind); +static void gst_type_find_load_plugin (GstTypeFind * find, + gpointer data); -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. + * A typefind function not suggesting Caps during one call is thought + * to have no chance to identify the data. If there is even the slightest + * possibility that this data may still be identified by your function once + * more data is available you must call this function - preferrably with a + * probability of 0. + */ +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..19a93171d5 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 *info, gpointer data); + +enum { + GST_TYPE_FIND_NO_IDEA = 0, + 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/gst/types/.gitignore b/gst/types/.gitignore deleted file mode 100644 index 08f5ed37d8..0000000000 --- a/gst/types/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -Makefile -Makefile.in -*.o -*.lo -*.la -.deps -.libs diff --git a/gst/types/Makefile.am b/gst/types/Makefile.am deleted file mode 100644 index 5f26e166cc..0000000000 --- a/gst/types/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ - -plugin_LTLIBRARIES = libgsttypes.la - -libgsttypes_la_SOURCES = gsttypes.c -libgsttypes_la_CFLAGS = $(GST_CFLAGS) -libgsttypes_la_LIBADD = -libgsttypes_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/gst/types/gsttypes.c b/gst/types/gsttypes.c deleted file mode 100644 index 45ab1baa21..0000000000 --- a/gst/types/gsttypes.c +++ /dev/null @@ -1,55 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#include -#include - -GstTypeDefinition _definitions[] = { - { "gsttypes_audio/raw", "audio/raw", ".raw", NULL }, - { "gsttypes_video/raw", "video/raw", ".raw", NULL }, - { NULL, NULL, NULL, NULL }, -}; - - -static gboolean -plugin_init (GModule *module, GstPlugin *plugin) -{ - gint i = 0; - - while (_definitions[i].name) { - GstTypeFactory *factory; - - factory = gst_type_factory_new (&_definitions[i]); - gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); - GST_DEBUG ( "added factory #%d '%s'", i, _definitions[i].name); - i++; - } - - /*gst_info ("gsttypes: loaded %d standard types\n",i);*/ - - return TRUE; -} - -GstPluginDesc plugin_desc = { - GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gsttypes", - plugin_init -}; 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..06e1259d9f 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 @@ -744,3 +746,17 @@ gst_bytestream_print_status (GstByteStream * bs) GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf)); } } + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + 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..ec2324b759 --- /dev/null +++ b/plugins/elements/gstbufferstore.c @@ -0,0 +1,427 @@ +/* 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 }; + +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) +{ + 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 */ + 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_data_ref (GST_DATA (buffer)); + 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)); + /* 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_OFFSET (current)) { + g_assert (needed_size == GST_BUFFER_OFFSET (current)); /* we have no overlapping data */ + buffer = NULL; + } 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); + buffer = sub; + } else { + gst_data_ref (GST_DATA (buffer)); + } + /* replace current buffer with new one */ + 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)); + buffer = sub; + } else { + gst_data_ref (GST_DATA (buffer)); + } + store->buffers = g_list_insert_before (store->buffers, walk, buffer); + buffer = NULL; + break; + } + } + } + if (buffer) { + gst_data_ref (GST_DATA (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); + + 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 && + 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)); + 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 && + cur_offset + GST_BUFFER_SIZE (current) != GST_BUFFER_OFFSET (walk->data))) { + GST_DEBUG_OBJECT (store, "not all data for offset %"G_GUINT64_FORMAT" and size %u available, aborting", + offset, size); + gst_data_unref (GST_DATA (ret)); + ret = NULL; + break; + } + 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); + } + } + + 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 && + 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..b78f404018 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", 0, "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/gsttypefind.c b/plugins/elements/gsttypefind.c new file mode 100644 index 0000000000..31f1be0b9a --- /dev/null +++ b/plugins/elements/gsttypefind.c @@ -0,0 +1,563 @@ +/* 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. + */ + +/* 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 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); + + 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_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); + + 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); + + typefind_class->have_type = gst_type_find_element_have_type; +} +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_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); + + 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 void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + + typefind->mode = MODE_TYPEFIND; +} +static void +stop_typefinding (GstTypeFindElement *typefind, gboolean push_cached_buffers) +{ + /* stop all typefinding and set mode back to normal */ + g_assert (typefind->possibilities == NULL); + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + guint size = gst_buffer_store_get_size (typefind->store, 0); + GstBuffer *buffer = gst_buffer_store_get_buffer (typefind->store, 0, size); + if (buffer) { + gst_pad_push (typefind->sink, GST_DATA (buffer)); + } else { + size = 0; + } + gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET, size)); + } + gst_buffer_store_clear (typefind->store); +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + 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 guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length ()", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + if (!typefind->stream_length_available) + return 0; + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (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) + return 0; + } + + return entry->self->stream_length; +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_DEBUG_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) { + 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_DEBUG_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 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" */ + const GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = -1; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_DEBUG_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 = -1; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == -1) { + 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_LOG_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], 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, TRUE); + } 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 */ + GstEvent *event = gst_event_new_seek (entry->requested_offset < 0 ? + GST_SEEK_METHOD_SET : GST_SEEK_METHOD_END, entry->requested_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + break; + } else { + /* impossible to seek */ + 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) { + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + GST_LOG_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], entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind, TRUE); + } + } + 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, FALSE); + 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..31f1be0b9a --- /dev/null +++ b/plugins/elements/gsttypefindelement.c @@ -0,0 +1,563 @@ +/* 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. + */ + +/* 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 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); + + 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_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); + + 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); + + typefind_class->have_type = gst_type_find_element_have_type; +} +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_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); + + 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 void +start_typefinding (GstTypeFindElement *typefind) +{ + g_assert (typefind->caps == NULL); + + typefind->mode = MODE_TYPEFIND; +} +static void +stop_typefinding (GstTypeFindElement *typefind, gboolean push_cached_buffers) +{ + /* stop all typefinding and set mode back to normal */ + g_assert (typefind->possibilities == NULL); + + typefind->mode = MODE_NORMAL; + + if (push_cached_buffers) { + guint size = gst_buffer_store_get_size (typefind->store, 0); + GstBuffer *buffer = gst_buffer_store_get_buffer (typefind->store, 0, size); + if (buffer) { + gst_pad_push (typefind->sink, GST_DATA (buffer)); + } else { + size = 0; + } + gst_pad_send_event (GST_PAD_PEER (typefind->sink), + gst_event_new_seek (GST_SEEK_METHOD_SET, size)); + } + gst_buffer_store_clear (typefind->store); +} +static void +gst_type_find_element_handle_event (GstPad *pad, GstEvent *event) +{ + 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 guint64 +find_element_get_length (gpointer data) +{ + TypeFindEntry *entry = (TypeFindEntry *) data; + GstTypeFindElement *typefind = entry->self; + GstFormat format = GST_FORMAT_BYTES; + + GST_DEBUG_OBJECT (entry->self, "'%s' called get_length ()", + GST_PLUGIN_FEATURE_NAME (entry->factory)); + if (!typefind->stream_length_available) + return 0; + if (entry->self->stream_length == 0) { + typefind->stream_length_available = gst_pad_query (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) + return 0; + } + + return entry->self->stream_length; +} +static guint8 * +find_peek (gpointer data, gint64 offset, guint size) +{ + GstBuffer *buf; + TypeFindEntry *entry = (TypeFindEntry *) data; + + GST_DEBUG_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) { + 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_DEBUG_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 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" */ + const GList *all_factories = gst_type_find_factory_get_list (); + GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", + g_list_length ((GList *) all_factories)); + + while (all_factories) { + entry = g_new0 (TypeFindEntry, 1); + + entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data); + entry->self = typefind; + entry->probability = -1; + typefind->possibilities = g_list_prepend (typefind->possibilities, entry); + all_factories = g_list_next (all_factories); + } + } + /* call every typefind function once */ + walk = entries = typefind->possibilities; + GST_DEBUG_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 = -1; + entry->requested_offset = 0; + entry->requested_size = 0; + gst_type_find_factory_call_function (entry->factory, &find); + free_entry_buffers (entry); + if (entry->probability == -1) { + 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_LOG_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], 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, TRUE); + } 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 */ + GstEvent *event = gst_event_new_seek (entry->requested_offset < 0 ? + GST_SEEK_METHOD_SET : GST_SEEK_METHOD_END, entry->requested_offset); + if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) { + /* done seeking */ + break; + } else { + /* impossible to seek */ + 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) { + free_entry (entry); + typefind->possibilities = g_list_delete_link (typefind->possibilities, cur); + } + } + if (g_list_next (typefind->possibilities) == NULL) { + entry = (TypeFindEntry *) typefind->possibilities->data; + GST_LOG_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], entry->probability, entry->caps); + free_entry (entry); + g_list_free (typefind->possibilities); + typefind->possibilities = NULL; + stop_typefinding (typefind, TRUE); + } + } + 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, FALSE); + 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-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