a0e9ae9308a2fa30e5441a96500db96d07c5acf6
[platform/upstream/gstreamer.git] / gst / elements / gsttypefindelement.c
1 /* GStreamer
2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3  *
4  * gsttypefind.c: element that detects type of stream
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /* FIXME: need a better solution for non-seekable streams */
23
24 /* way of operation:
25  * 1) get a list of all typefind functions sorted best to worst
26  * 2) if all elements have been called with all requested data goto 8
27  * 3) call all functions once with all available data
28  * 4) if a function returns a value >= ARG_MAXIMUM goto 8
29  * 5) all functions with a result > ARG_MINIMUM or functions that did not get
30  *    all requested data (where peek returned NULL) stay in list
31  * 6) seek to requested offset of best function that still has open data 
32  *    requests
33  * 7) goto 2
34  * 8) take best available result and use its caps
35  *
36  * The element has two scheduling modes:
37  *
38  * 1) chain based, it will collect buffers and run the typefind function on
39  *    the buffer until something is found.
40  * 2) getrange based, it will proxy the getrange function to the sinkpad. It
41  *    is assumed that the peer element is happy with whatever format we 
42  *    eventually read.
43  *
44  * When the element has no connected srcpad, and the sinkpad can operate in
45  * getrange based mode, the element starts its own task to figure out the 
46  * type of the stream.
47  * 
48  */
49
50 #ifdef HAVE_CONFIG_H
51 #  include "config.h"
52 #endif
53
54 #include "gsttypefindelement.h"
55 #include "gst/gst_private.h"
56 #include "gst/gst-i18n-lib.h"
57 #include "gst/base/gsttypefindhelper.h"
58
59 #include <gst/gsttypefind.h>
60 #include <gst/gstutils.h>
61 #include <gst/gsterror.h>
62
63 GST_DEBUG_CATEGORY_STATIC (gst_type_find_element_debug);
64 #define GST_CAT_DEFAULT gst_type_find_element_debug
65
66 GstElementDetails gst_type_find_element_details =
67 GST_ELEMENT_DETAILS ("TypeFind",
68     "Generic",
69     "Finds the media type of a stream",
70     "Benjamin Otte <in7y118@public.uni-hamburg.de>");
71
72 /* generic templates */
73 GstStaticPadTemplate type_find_element_sink_template =
74 GST_STATIC_PAD_TEMPLATE ("sink",
75     GST_PAD_SINK,
76     GST_PAD_ALWAYS,
77     GST_STATIC_CAPS_ANY);
78
79 GstStaticPadTemplate type_find_element_src_template =
80 GST_STATIC_PAD_TEMPLATE ("src",
81     GST_PAD_SRC,
82     GST_PAD_ALWAYS,
83     GST_STATIC_CAPS_ANY);
84
85 /* TypeFind signals and args */
86 enum
87 {
88   HAVE_TYPE,
89   LAST_SIGNAL
90 };
91 enum
92 {
93   ARG_0,
94   ARG_CAPS,
95   ARG_MINIMUM,
96   ARG_MAXIMUM
97 };
98 enum
99 {
100   MODE_NORMAL,                  /* act as identity */
101   MODE_TYPEFIND                 /* do typefinding */
102 };
103
104
105 #define _do_init(bla) \
106     GST_DEBUG_CATEGORY_INIT (gst_type_find_element_debug, "typefind",           \
107         GST_DEBUG_BG_YELLOW | GST_DEBUG_FG_GREEN, "type finding element");
108
109 GST_BOILERPLATE_FULL (GstTypeFindElement, gst_type_find_element, GstElement,
110     GST_TYPE_ELEMENT, _do_init);
111
112 static void gst_type_find_element_dispose (GObject * object);
113 static void gst_type_find_element_set_property (GObject * object,
114     guint prop_id, const GValue * value, GParamSpec * pspec);
115 static void gst_type_find_element_get_property (GObject * object,
116     guint prop_id, GValue * value, GParamSpec * pspec);
117
118 static const GstEventMask *gst_type_find_element_src_event_mask (GstPad * pad);
119 static gboolean gst_type_find_element_src_event (GstPad * pad,
120     GstEvent * event);
121 static gboolean gst_type_find_handle_src_query (GstPad * pad,
122     GstQueryType type, GstFormat * fmt, gint64 * value);
123 static GstFlowReturn push_buffer_store (GstTypeFindElement * typefind);
124
125 static gboolean gst_type_find_element_handle_event (GstPad * pad,
126     GstEvent * event);
127 static GstFlowReturn gst_type_find_element_chain (GstPad * sinkpad,
128     GstBuffer * buffer);
129 static GstFlowReturn gst_type_find_element_getrange (GstPad * srcpad,
130     guint64 offset, guint length, GstBuffer ** buffer);
131 static gboolean gst_type_find_element_checkgetrange (GstPad * srcpad);
132 static GstElementStateReturn
133 gst_type_find_element_change_state (GstElement * element);
134 static gboolean
135 gst_type_find_element_activate (GstPad * pad, GstActivateMode mode);
136
137 static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
138
139 static void
140 gst_type_find_element_have_type (GstTypeFindElement * typefind,
141     guint probability, const GstCaps * caps)
142 {
143   g_assert (typefind->caps == NULL);
144   g_assert (caps != NULL);
145
146   GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT, caps);
147   typefind->caps = gst_caps_copy (caps);
148   gst_pad_set_caps (typefind->src, (GstCaps *) caps);
149 }
150
151 static void
152 gst_type_find_element_base_init (gpointer g_class)
153 {
154   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
155
156   gst_element_class_add_pad_template (gstelement_class,
157       gst_static_pad_template_get (&type_find_element_src_template));
158   gst_element_class_add_pad_template (gstelement_class,
159       gst_static_pad_template_get (&type_find_element_sink_template));
160   gst_element_class_set_details (gstelement_class,
161       &gst_type_find_element_details);
162 }
163 static void
164 gst_type_find_element_class_init (GstTypeFindElementClass * typefind_class)
165 {
166   GObjectClass *gobject_class = G_OBJECT_CLASS (typefind_class);
167   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (typefind_class);
168
169   gobject_class->set_property =
170       GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
171   gobject_class->get_property =
172       GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
173   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
174
175   typefind_class->have_type = gst_type_find_element_have_type;
176
177   g_object_class_install_property (gobject_class, ARG_CAPS,
178       g_param_spec_boxed ("caps", _("caps"),
179           _("detected capabilities in stream"), gst_caps_get_type (),
180           G_PARAM_READABLE));
181   g_object_class_install_property (gobject_class, ARG_MINIMUM,
182       g_param_spec_uint ("minimum", _("minimum"),
183           "minimum probability required to accept caps", GST_TYPE_FIND_MINIMUM,
184           GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
185   g_object_class_install_property (gobject_class, ARG_MINIMUM,
186       g_param_spec_uint ("maximum", _("maximum"),
187           "probability to stop typefinding", GST_TYPE_FIND_MINIMUM,
188           GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE));
189
190   gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type",
191       G_TYPE_FROM_CLASS (typefind_class), G_SIGNAL_RUN_LAST,
192       G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
193       gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
194       G_TYPE_UINT, GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE);
195
196   gstelement_class->change_state =
197       GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
198 }
199 static void
200 gst_type_find_element_init (GstTypeFindElement * typefind)
201 {
202   /* sinkpad */
203   typefind->sink =
204       gst_pad_new_from_template (gst_static_pad_template_get
205       (&type_find_element_sink_template), "sink");
206   gst_pad_set_activate_function (typefind->sink,
207       gst_type_find_element_activate);
208   gst_pad_set_chain_function (typefind->sink, gst_type_find_element_chain);
209   gst_pad_set_event_function (typefind->sink,
210       gst_type_find_element_handle_event);
211   gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
212   /* srcpad */
213   typefind->src =
214       gst_pad_new_from_template (gst_static_pad_template_get
215       (&type_find_element_src_template), "src");
216   gst_pad_set_activate_function (typefind->src, gst_type_find_element_activate);
217   gst_pad_set_checkgetrange_function (typefind->src,
218       gst_type_find_element_checkgetrange);
219   gst_pad_set_getrange_function (typefind->src, gst_type_find_element_getrange);
220   gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
221   gst_pad_set_event_mask_function (typefind->src,
222       gst_type_find_element_src_event_mask);
223   gst_pad_set_query_function (typefind->src,
224       GST_DEBUG_FUNCPTR (gst_type_find_handle_src_query));
225   gst_pad_use_fixed_caps (typefind->src);
226   gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
227
228   typefind->caps = NULL;
229   typefind->min_probability = 1;
230   typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
231
232   typefind->store = gst_buffer_store_new ();
233 }
234 static void
235 gst_type_find_element_dispose (GObject * object)
236 {
237   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
238
239   G_OBJECT_CLASS (parent_class)->dispose (object);
240
241   if (typefind->store) {
242     g_object_unref (typefind->store);
243     typefind->store = NULL;
244   }
245 }
246 static void
247 gst_type_find_element_set_property (GObject * object, guint prop_id,
248     const GValue * value, GParamSpec * pspec)
249 {
250   GstTypeFindElement *typefind;
251
252   g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
253
254   typefind = GST_TYPE_FIND_ELEMENT (object);
255
256   switch (prop_id) {
257     case ARG_MINIMUM:
258       typefind->min_probability = g_value_get_uint (value);
259       g_object_notify (object, "minimum");
260       break;
261     case ARG_MAXIMUM:
262       typefind->max_probability = g_value_get_uint (value);
263       g_object_notify (object, "maximum");
264       break;
265     default:
266       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267       break;
268   }
269 }
270 static void
271 gst_type_find_element_get_property (GObject * object, guint prop_id,
272     GValue * value, GParamSpec * pspec)
273 {
274   GstTypeFindElement *typefind;
275
276   g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
277
278   typefind = GST_TYPE_FIND_ELEMENT (object);
279
280   switch (prop_id) {
281     case ARG_CAPS:
282       g_value_set_boxed (value, typefind->caps);
283       break;
284     case ARG_MINIMUM:
285       g_value_set_uint (value, typefind->min_probability);
286       break;
287     case ARG_MAXIMUM:
288       g_value_set_uint (value, typefind->max_probability);
289       break;
290     default:
291       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292       break;
293   }
294 }
295
296 static gboolean
297 gst_type_find_handle_src_query (GstPad * pad,
298     GstQueryType type, GstFormat * fmt, gint64 * value)
299 {
300   GstTypeFindElement *typefind =
301       GST_TYPE_FIND_ELEMENT (gst_pad_get_parent (pad));
302   gboolean res;
303
304   res = gst_pad_query (GST_PAD_PEER (typefind->sink), type, fmt, value);
305   if (!res)
306     return FALSE;
307
308   if (type == GST_QUERY_POSITION && typefind->store != NULL) {
309     /* FIXME: this code assumes that there's no discont in the queue */
310     switch (*fmt) {
311       case GST_FORMAT_BYTES:
312         *value -= gst_buffer_store_get_size (typefind->store, 0);
313         break;
314       default:
315         /* FIXME */
316         break;
317     }
318   }
319
320   return TRUE;
321 }
322
323 static const GstEventMask *
324 gst_type_find_element_src_event_mask (GstPad * pad)
325 {
326   static const GstEventMask mask[] = {
327     {GST_EVENT_SEEK,
328         GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END |
329           GST_SEEK_FLAG_FLUSH},
330     /* add more if you want, event masks suck and need to die anyway */
331     {0,}
332   };
333
334   return mask;
335 }
336
337 static gboolean
338 gst_type_find_element_src_event (GstPad * pad, GstEvent * event)
339 {
340   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
341
342   if (typefind->mode != MODE_NORMAL) {
343     /* need to do more? */
344     gst_data_unref (GST_DATA (event));
345     return FALSE;
346   }
347   return gst_pad_event_default (pad, event);
348 }
349 typedef struct
350 {
351   GstTypeFindFactory *factory;
352   gint probability;
353   GstCaps *caps;
354   gint64 requested_offset;
355   guint requested_size;
356
357   GList *buffers;
358   GstTypeFindElement *self;
359 }
360 TypeFindEntry;
361
362 static inline TypeFindEntry *
363 new_entry (void)
364 {
365   return g_new0 (TypeFindEntry, 1);
366 }
367 static void
368 free_entry_buffers (TypeFindEntry * entry)
369 {
370   g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL);
371   g_list_free (entry->buffers);
372   entry->buffers = NULL;
373 }
374 static void
375 free_entry (TypeFindEntry * entry)
376 {
377   free_entry_buffers (entry);
378
379   if (entry->caps)
380     gst_caps_unref (entry->caps);
381   g_free (entry);
382 }
383 static void
384 start_typefinding (GstTypeFindElement * typefind)
385 {
386   g_assert (typefind->possibilities == NULL);
387
388   GST_DEBUG_OBJECT (typefind, "starting typefinding");
389   gst_pad_set_caps (typefind->src, NULL);
390   if (typefind->caps) {
391     gst_caps_replace (&typefind->caps, NULL);
392   }
393   typefind->mode = MODE_TYPEFIND;
394   typefind->stream_length_available = TRUE;
395   typefind->stream_length = 0;
396 }
397 static void
398 stop_typefinding (GstTypeFindElement * typefind)
399 {
400   GstElementState state;
401
402   /* stop all typefinding and set mode back to normal */
403   gboolean push_cached_buffers;
404
405   gst_element_get_state (GST_ELEMENT (typefind), &state, NULL, NULL);
406
407   push_cached_buffers = (state >= GST_STATE_PAUSED);
408
409   GST_DEBUG_OBJECT (typefind, "stopping typefinding%s",
410       push_cached_buffers ? " and pushing cached buffers" : "");
411   if (typefind->possibilities != NULL) {
412     /* this should only happen on PAUSED => READY or EOS */
413     GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions",
414         g_list_length (typefind->possibilities));
415     g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
416     g_list_free (typefind->possibilities);
417     typefind->possibilities = NULL;
418   }
419   //typefind->mode = MODE_TRANSITION;
420
421   if (!push_cached_buffers) {
422     gst_buffer_store_clear (typefind->store);
423   } else {
424     typefind->mode = MODE_NORMAL;
425     /* push out our queued buffers here */
426     push_buffer_store (typefind);
427   }
428 }
429
430 static GstFlowReturn
431 push_buffer_store (GstTypeFindElement * typefind)
432 {
433   guint size = gst_buffer_store_get_size (typefind->store, 0);
434   GstBuffer *buffer;
435   GstFlowReturn ret;
436
437   if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) {
438     GST_DEBUG_OBJECT (typefind, "pushing cached data (%u bytes)", size);
439     ret = gst_pad_push (typefind->src, buffer);
440   } else {
441     size = 0;
442     ret = GST_FLOW_ERROR;
443   }
444
445   gst_buffer_store_clear (typefind->store);
446
447   return ret;
448 }
449
450 static guint64
451 find_element_get_length (gpointer data)
452 {
453   TypeFindEntry *entry = (TypeFindEntry *) data;
454   GstTypeFindElement *typefind = entry->self;
455   GstFormat format = GST_FORMAT_BYTES;
456
457   if (!typefind->stream_length_available) {
458     GST_LOG_OBJECT (entry->self,
459         "'%s' called get_length () but we know it's not available",
460         GST_PLUGIN_FEATURE_NAME (entry->factory));
461     return 0;
462   }
463   if (entry->self->stream_length == 0) {
464     typefind->stream_length_available =
465         gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL,
466         &format, &entry->self->stream_length);
467     if (format != GST_FORMAT_BYTES)
468       typefind->stream_length_available = FALSE;
469     if (!typefind->stream_length_available) {
470       GST_DEBUG_OBJECT (entry->self,
471           "'%s' called get_length () but it's not available",
472           GST_PLUGIN_FEATURE_NAME (entry->factory));
473       return 0;
474     } else {
475       GST_DEBUG_OBJECT (entry->self,
476           "'%s' called get_length () and it's %" G_GUINT64_FORMAT " bytes",
477           GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
478     }
479   }
480
481   return entry->self->stream_length;
482 }
483
484 static gboolean
485 gst_type_find_element_handle_event (GstPad * pad, GstEvent * event)
486 {
487   gboolean res = FALSE;
488   TypeFindEntry *entry;
489   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
490
491   GST_DEBUG_OBJECT (typefind, "got event %d in mode %d", GST_EVENT_TYPE (event),
492       typefind->mode);
493
494   switch (typefind->mode) {
495     case MODE_TYPEFIND:
496       switch (GST_EVENT_TYPE (event)) {
497         case GST_EVENT_EOS:
498           /* this should only happen when we got all available data */
499           entry =
500               (TypeFindEntry *) typefind->possibilities ? typefind->
501               possibilities->data : NULL;
502           if (entry && entry->probability >= typefind->min_probability) {
503             GST_INFO_OBJECT (typefind,
504                 "'%s' is the best typefind left after we got all data, using it now (probability %u)",
505                 GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
506             g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
507                 0, entry->probability, entry->caps);
508             stop_typefinding (typefind);
509             push_buffer_store (typefind);
510             res = gst_pad_event_default (pad, event);
511           } else {
512             res = gst_pad_event_default (pad, event);
513             GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL),
514                 (NULL));
515             stop_typefinding (typefind);
516           }
517           break;
518         default:
519           gst_data_unref (GST_DATA (event));
520           res = TRUE;
521           break;
522       }
523       break;
524     case MODE_NORMAL:
525       if (GST_EVENT_TYPE (event) == GST_EVENT_DISCONTINUOUS) {
526         start_typefinding (typefind);
527         gst_event_unref (event);
528         res = TRUE;
529       } else {
530         res = gst_pad_event_default (pad, event);
531       }
532       break;
533     default:
534       g_assert_not_reached ();
535   }
536   return res;
537 }
538 static guint8 *
539 find_peek (gpointer data, gint64 offset, guint size)
540 {
541   GstBuffer *buf;
542   TypeFindEntry *entry = (TypeFindEntry *) data;
543
544   GST_LOG_OBJECT (entry->self, "'%s' called peek (%" G_GINT64_FORMAT ", %u)",
545       GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
546   if (offset >= 0) {
547     buf = gst_buffer_store_get_buffer (entry->self->store, offset, size);
548   } else {
549     /* FIXME: can we do this easily without querying length? */
550     guint64 length = find_element_get_length (data);
551
552     if (length == 0) {
553       buf = NULL;
554     } else {
555       buf =
556           gst_buffer_store_get_buffer (entry->self->store, length + offset,
557           size);
558     }
559   }
560
561   if (buf) {
562     entry->buffers = g_list_prepend (entry->buffers, buf);
563     return GST_BUFFER_DATA (buf);
564   } else {
565     if (entry->requested_size == 0) {
566       GST_LOG_OBJECT (entry->self,
567           "setting requested peek (%" G_GINT64_FORMAT ", %u) on '%s'", offset,
568           size, GST_PLUGIN_FEATURE_NAME (entry->factory));
569       entry->requested_offset = offset;
570       entry->requested_size = size;
571     }
572     return NULL;
573   }
574 }
575 static void
576 find_suggest (gpointer data, guint probability, const GstCaps * caps)
577 {
578   TypeFindEntry *entry = (TypeFindEntry *) data;
579
580   GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %" GST_PTR_FORMAT ")",
581       GST_PLUGIN_FEATURE_NAME (entry->factory), probability, caps);
582   if (((gint) probability) > entry->probability) {
583     entry->probability = probability;
584     gst_caps_replace (&entry->caps, gst_caps_copy (caps));
585   }
586 }
587
588 static gint
589 compare_type_find_entry (gconstpointer a, gconstpointer b)
590 {
591   TypeFindEntry *one = (TypeFindEntry *) a;
592   TypeFindEntry *two = (TypeFindEntry *) b;
593
594   if (one->probability == two->probability) {
595     /* FIXME: can be improved by analyzing requests */
596     return 0;
597   } else {
598     return two->probability - one->probability;
599   }
600 }
601
602 static gint
603 compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
604 {
605   return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
606 }
607
608 static GstFlowReturn
609 gst_type_find_element_chain (GstPad * pad, GstBuffer * buffer)
610 {
611   GstTypeFindElement *typefind;
612   GList *entries;
613   TypeFindEntry *entry;
614   GList *walk;
615   GstFlowReturn res = GST_FLOW_OK;
616   GstTypeFind find = { find_peek, find_suggest, NULL, find_element_get_length };
617
618   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
619
620   switch (typefind->mode) {
621     case MODE_NORMAL:
622       return gst_pad_push (typefind->src, buffer);
623     case MODE_TYPEFIND:{
624       guint64 current_offset;
625
626       gst_buffer_store_add_buffer (typefind->store, buffer);
627       current_offset = GST_BUFFER_OFFSET_IS_VALID (buffer) ?
628           GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer) :
629           gst_buffer_store_get_size (typefind->store, 0);
630       gst_buffer_unref (buffer);
631
632       if (typefind->possibilities == NULL) {
633         /* not yet started, get all typefinding functions into our "queue" */
634         GList *all_factories = gst_type_find_factory_get_list ();
635
636         GST_INFO_OBJECT (typefind, "starting with %u typefinding functions",
637             g_list_length ((GList *) all_factories));
638
639         all_factories = g_list_sort (all_factories, compare_type_find_factory);
640         walk = all_factories;
641         while (all_factories) {
642           entry = new_entry ();
643
644           entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
645           entry->self = typefind;
646           entry->probability = 0;
647           typefind->possibilities =
648               g_list_prepend (typefind->possibilities, entry);
649           all_factories = g_list_next (all_factories);
650         }
651         g_list_free (all_factories);
652       }
653       /* call every typefind function once */
654       walk = entries = typefind->possibilities;
655       GST_INFO_OBJECT (typefind, "iterating %u typefinding functions",
656           g_list_length (entries));
657       typefind->possibilities = NULL;
658       while (walk) {
659         find.data = entry = (TypeFindEntry *) walk->data;
660         walk = g_list_next (walk);
661         entry->probability = 0;
662         entry->requested_offset = 0;
663         entry->requested_size = 0;
664         gst_type_find_factory_call_function (entry->factory, &find);
665         free_entry_buffers (entry);
666         if (entry->probability == 0 && entry->requested_size == 0) {
667           GST_DEBUG_OBJECT (typefind,
668               "'%s' was removed - no chance of being the right plugin",
669               GST_PLUGIN_FEATURE_NAME (entry->factory));
670           free_entry (entry);
671         } else if (entry->probability >= typefind->max_probability) {
672           /* wooha, got caps */
673           GstCaps *found_caps = entry->caps;
674           guint probability = entry->probability;
675
676           GST_INFO_OBJECT (typefind,
677               "'%s' returned %u/%u probability, using it NOW",
678               GST_PLUGIN_FEATURE_NAME (entry->factory), probability,
679               typefind->max_probability);
680           while (walk) {
681             free_entry ((TypeFindEntry *) walk->data);
682             walk = g_list_next (walk);
683           }
684           walk = typefind->possibilities;
685           while (walk) {
686             free_entry (walk->data);
687             walk = g_list_next (walk);
688           }
689           typefind->possibilities = NULL;
690           g_list_free (typefind->possibilities);
691           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
692               probability, found_caps);
693           free_entry (entry);
694         } else {
695           typefind->possibilities =
696               g_list_prepend (typefind->possibilities, entry);
697         }
698       }
699       g_list_free (entries);
700       /* we may now already have caps or we might be left without functions to try */
701       if (typefind->caps) {
702         stop_typefinding (typefind);
703       } else if (typefind->possibilities == NULL) {
704       error:
705         GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
706         stop_typefinding (typefind);
707       } else {
708         /* set up typefind element for next iteration */
709         typefind->possibilities =
710             g_list_sort (typefind->possibilities, compare_type_find_entry);
711
712         /* look for typefind functions that require data without seeking */
713         for (walk = typefind->possibilities; walk; walk = g_list_next (walk)) {
714           entry = (TypeFindEntry *) walk->data;
715           if (entry->requested_offset <= current_offset &&
716               entry->requested_offset + entry->requested_size > current_offset)
717             break;
718         }
719         if (!walk) {
720           /* find out if we should seek */
721         restart:
722           for (walk = typefind->possibilities; walk; walk = g_list_next (walk)) {
723             entry = (TypeFindEntry *) walk->data;
724             if (entry->requested_size > 0) {
725               /* FIXME: need heuristic to find out if we should seek */
726               gint64 seek_offset;
727               GstEvent *event;
728
729               seek_offset =
730                   entry->requested_offset >
731                   0 ? entry->
732                   requested_offset : find_element_get_length (entry) +
733                   entry->requested_offset;
734               seek_offset +=
735                   gst_buffer_store_get_size (typefind->store, seek_offset);
736               event =
737                   gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET,
738                   seek_offset);
739               if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) {
740                 /* done seeking */
741                 GST_DEBUG_OBJECT (typefind,
742                     "'%s' was reset - seeked to %" G_GINT64_FORMAT,
743                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
744                 break;
745               } else if (entry->requested_offset < 0) {
746                 /* impossible to seek */
747                 GST_DEBUG_OBJECT (typefind,
748                     "'%s' was reset - couldn't seek to %" G_GINT64_FORMAT,
749                     GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
750                 if (entry->probability == 0) {
751                   free_entry (entry);
752                   typefind->possibilities =
753                       g_list_delete_link (typefind->possibilities, walk);
754                   /* FIXME: too many gotos */
755                   if (!typefind->possibilities)
756                     goto error;
757                   /* we modified the list, let's restart */
758                   goto restart;
759                 } else {
760                   entry->requested_size = 0;
761                   entry->requested_offset = 0;
762                 }
763               }
764             }
765           }
766         }
767         /* throw out all entries that can't get more data */
768         walk = g_list_next (typefind->possibilities);
769         while (walk) {
770           GList *cur = walk;
771
772           entry = (TypeFindEntry *) walk->data;
773           walk = g_list_next (walk);
774           if (entry->requested_size == 0) {
775             GST_DEBUG_OBJECT (typefind,
776                 "'%s' was removed - higher possibilities available",
777                 GST_PLUGIN_FEATURE_NAME (entry->factory));
778             free_entry (entry);
779             typefind->possibilities =
780                 g_list_delete_link (typefind->possibilities, cur);
781           }
782         }
783         if (g_list_next (typefind->possibilities) == NULL) {
784           entry = (TypeFindEntry *) typefind->possibilities->data;
785           if (entry->probability > typefind->min_probability) {
786             GST_INFO_OBJECT (typefind,
787                 "'%s' is the only typefind left, using it now (probability %u)",
788                 GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
789             g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
790                 0, entry->probability, entry->caps);
791             free_entry (entry);
792             g_list_free (typefind->possibilities);
793             typefind->possibilities = NULL;
794             stop_typefinding (typefind);
795           }
796         }
797       }
798       break;
799     }
800     default:
801       g_assert_not_reached ();
802       return GST_FLOW_ERROR;
803   }
804
805   return res;
806 }
807
808 static gboolean
809 gst_type_find_element_checkgetrange (GstPad * srcpad)
810 {
811   GstTypeFindElement *typefind;
812
813   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));
814
815   return gst_pad_check_pull_range (typefind->sink);
816 }
817
818 static GstFlowReturn
819 gst_type_find_element_getrange (GstPad * srcpad,
820     guint64 offset, guint length, GstBuffer ** buffer)
821 {
822   GstTypeFindElement *typefind;
823
824   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));
825
826   return gst_pad_pull_range (typefind->sink, offset, length, buffer);
827 }
828
829 static gboolean
830 do_typefind (GstTypeFindElement * typefind)
831 {
832   GstCaps *caps;
833   GstPad *peer;
834
835   peer = gst_pad_get_peer (typefind->sink);
836   if (peer) {
837     gst_pad_peer_set_active (typefind->sink, GST_ACTIVATE_PULL);
838
839     caps = gst_type_find_helper (peer, 0);
840     gst_pad_set_caps (typefind->src, caps);
841
842     if (caps) {
843       g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
844           0, 100, caps);
845     }
846
847     gst_object_unref (GST_OBJECT (peer));
848   }
849
850   return TRUE;
851 }
852
853 static gboolean
854 gst_type_find_element_activate (GstPad * pad, GstActivateMode mode)
855 {
856   gboolean result;
857   GstTypeFindElement *typefind;
858
859   typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));
860
861   switch (mode) {
862     case GST_ACTIVATE_PUSH:
863     case GST_ACTIVATE_PULL:
864       result = TRUE;
865       break;
866     default:
867       result = TRUE;
868       break;
869   }
870
871   return result;
872 }
873
874 static GstElementStateReturn
875 gst_type_find_element_change_state (GstElement * element)
876 {
877   GstElementState transition;
878   GstElementStateReturn ret;
879   GstTypeFindElement *typefind;
880
881   typefind = GST_TYPE_FIND_ELEMENT (element);
882
883   transition = GST_STATE_TRANSITION (element);
884   switch (transition) {
885     case GST_STATE_READY_TO_PAUSED:
886       do_typefind (typefind);
887
888       //start_typefinding (typefind);
889       break;
890     default:
891       break;
892   }
893
894   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
895
896   switch (transition) {
897     case GST_STATE_PAUSED_TO_READY:
898       //stop_typefinding (typefind);
899       gst_caps_replace (&typefind->caps, NULL);
900       break;
901     default:
902       break;
903   }
904
905   return ret;
906 }