finish everything typefinding related (like seeking to correct offset) _before_ pushi...
[platform/upstream/gstreamer.git] / gst / elements / gsttypefind.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
37 #ifdef HAVE_CONFIG_H
38 #  include "config.h"
39 #endif
40
41 #include "gsttypefindelement.h"
42 #include "gst/gst_private.h"
43
44 #include <gst/gsttypefind.h>
45
46 GST_DEBUG_CATEGORY_STATIC (gst_type_find_element_debug);
47 #define GST_CAT_DEFAULT gst_type_find_element_debug
48
49 GstElementDetails gst_type_find_element_details = GST_ELEMENT_DETAILS (
50   "TypeFind",
51   "Generic",
52   "Finds the media type of a stream",
53   "Benjamin Otte <in7y118@public.uni-hamburg.de>"
54 );
55
56 /* generic templates */
57 GST_PAD_TEMPLATE_FACTORY (type_find_element_sink_factory,
58   "sink",
59   GST_PAD_SINK,
60   GST_PAD_ALWAYS,
61   GST_CAPS_ANY
62 );
63 GST_PAD_TEMPLATE_FACTORY (type_find_element_src_factory,
64   "src",
65   GST_PAD_SRC,
66   GST_PAD_ALWAYS,
67   GST_CAPS_ANY
68 );
69
70 /* TypeFind signals and args */
71 enum {
72   HAVE_TYPE,
73   LAST_SIGNAL
74 };
75 enum {
76   ARG_0,
77   ARG_CAPS,
78   ARG_MINIMUM,
79   ARG_MAXIMUM
80 };
81 enum {
82   MODE_NORMAL, /* act as identity */
83   MODE_TYPEFIND, /* do typefinding */
84 };
85
86
87 static void     gst_type_find_element_base_init         (gpointer g_class);
88 static void     gst_type_find_element_class_init        (gpointer       g_class,
89                                                          gpointer       class_data);
90 static void     gst_type_find_element_init              (GTypeInstance *instance,
91                                                          gpointer       g_class);
92 static void     gst_type_find_element_dispose           (GObject *      object);
93 static void     gst_type_find_element_set_property      (GObject *      object, 
94                                                          guint          prop_id,
95                                                          const GValue * value, 
96                                                          GParamSpec *   pspec);
97 static void     gst_type_find_element_get_property      (GObject *      object, 
98                                                          guint          prop_id,
99                                                          GValue *       value, 
100                                                          GParamSpec *   pspec);
101
102 static const GstEventMask *
103                 gst_type_find_element_src_event_mask    (GstPad *       pad);
104 static gboolean gst_type_find_element_src_event         (GstPad *       pad,
105                                                          GstEvent *     event);
106
107 static void     gst_type_find_element_chain             (GstPad *       sinkpad,
108                                                          GstData *      data);
109 static GstElementStateReturn
110                 gst_type_find_element_change_state      (GstElement *   element);
111
112 static GstElementClass *parent_class = NULL;
113 static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
114
115 GType
116 gst_type_find_element_get_type (void)
117 {
118   static GType typefind_type = 0;
119
120   if (!typefind_type) {
121     static const GTypeInfo typefind_info = {
122       sizeof (GstTypeFindElementClass),
123       gst_type_find_element_base_init,
124       NULL,
125       gst_type_find_element_class_init,
126       NULL,
127       NULL,
128       sizeof (GstTypeFindElement),
129       0,
130       gst_type_find_element_init,
131       NULL
132     };
133     typefind_type = g_type_register_static (GST_TYPE_ELEMENT,
134                                             "GstTypeFindElement",
135                                             &typefind_info, 0);
136
137     GST_DEBUG_CATEGORY_INIT (gst_type_find_element_debug, "typefind", 
138                              GST_DEBUG_BG_YELLOW | GST_DEBUG_FG_GREEN, "typefind element");
139   }
140   return typefind_type;
141 }
142 static void
143 gst_type_find_element_have_type (GstTypeFindElement *typefind, guint probability, GstCaps *caps)
144 {
145   gchar *caps_str;
146   
147   g_assert (typefind->caps == NULL);
148   g_assert (caps != NULL);
149
150   caps_str = gst_caps_to_string (caps);
151   GST_INFO_OBJECT (typefind, "found caps %s", caps_str);
152   g_free (caps_str);
153   gst_caps_replace (&typefind->caps, caps);
154   if (gst_pad_try_set_caps (typefind->src, caps) < GST_PAD_LINK_OK) {
155     gst_element_error (GST_ELEMENT (typefind), "could not set caps on source pad");
156   }
157 }
158 static void
159 gst_type_find_element_base_init (gpointer g_class)
160 {
161   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
162   
163   gst_element_class_set_details (gstelement_class, &gst_type_find_element_details);
164 }
165 static void
166 gst_type_find_element_class_init (gpointer g_class, gpointer class_data)
167 {
168   GObjectClass *gobject_class;
169   GstElementClass *gstelement_class;
170   GstTypeFindElementClass *typefind_class;
171
172   gobject_class = G_OBJECT_CLASS (g_class);
173   gstelement_class = GST_ELEMENT_CLASS (g_class);
174   typefind_class = GST_TYPE_FIND_ELEMENT_CLASS (g_class);
175
176   parent_class = g_type_class_peek_parent (g_class);
177
178   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
179   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
180   gobject_class->dispose      = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
181
182   typefind_class->have_type = gst_type_find_element_have_type;
183
184   g_object_class_install_property (gobject_class, ARG_CAPS,
185           g_param_spec_boxed ("caps", _("caps"), _("detected capabilities in stream"),
186           GST_TYPE_CAPS, G_PARAM_READABLE));
187   g_object_class_install_property (gobject_class, ARG_MINIMUM,
188           g_param_spec_uint ("minimum", _("minimum"), "minimum probability required to accept caps",
189           GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
190   g_object_class_install_property (gobject_class, ARG_MINIMUM,
191           g_param_spec_uint ("maximum", _("maximum"), "probability to stop typefinding",
192           GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, G_PARAM_READWRITE));
193
194   gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type", 
195           G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST,
196           G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
197           gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
198           G_TYPE_UINT, GST_TYPE_CAPS);
199
200   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
201 }
202 static void
203 gst_type_find_element_init (GTypeInstance *instance, gpointer g_class)
204 {
205   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (instance);
206   
207   /* sinkpad */
208   typefind->sink = gst_pad_new_from_template (
209                 GST_PAD_TEMPLATE_GET (type_find_element_sink_factory), "sink");
210   gst_pad_set_chain_function (typefind->sink,
211                               gst_type_find_element_chain);
212   gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
213   /* srcpad */
214   typefind->src = gst_pad_new_from_template (
215                 GST_PAD_TEMPLATE_GET (type_find_element_src_factory), "src");
216   gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
217   gst_pad_set_event_mask_function (typefind->src, gst_type_find_element_src_event_mask);
218   gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
219
220   typefind->caps = NULL;
221   typefind->min_probability = 1;
222   typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
223
224   typefind->store = gst_buffer_store_new ();
225
226   GST_FLAG_SET (typefind, GST_ELEMENT_EVENT_AWARE);
227 }
228 static void
229 gst_type_find_element_dispose (GObject *object)
230 {
231   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
232
233   G_OBJECT_CLASS (parent_class)->dispose (object);
234
235   if (typefind->store) {
236     g_object_unref (typefind->store);
237     typefind->store = NULL;
238   }
239 }
240 static void
241 gst_type_find_element_set_property (GObject *object, guint prop_id, 
242                                     const GValue *value, GParamSpec *pspec)
243 {
244   GstTypeFindElement *typefind;
245
246   g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
247
248   typefind = GST_TYPE_FIND_ELEMENT (object);
249
250   switch (prop_id) {
251     case ARG_MINIMUM:
252       typefind->min_probability = g_value_get_uint (value);
253       g_object_notify (object, "minimum");
254       break;
255     case ARG_MAXIMUM:
256       typefind->max_probability = g_value_get_uint (value);
257       g_object_notify (object, "maximum");
258       break;
259     default:
260       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
261       break;
262   }
263 }
264 static void
265 gst_type_find_element_get_property (GObject *object, guint prop_id, 
266                                     GValue *value, GParamSpec *pspec)
267 {
268   GstTypeFindElement *typefind;
269
270   g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
271
272   typefind = GST_TYPE_FIND_ELEMENT (object);
273
274   switch (prop_id) {
275     case ARG_CAPS:
276       g_value_set_boxed (value, typefind->caps);
277       break;
278     case ARG_MINIMUM:
279       g_value_set_uint (value, typefind->min_probability);
280       break;
281     case ARG_MAXIMUM:
282       g_value_set_uint (value, typefind->max_probability);
283       break;
284     default:
285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286       break;
287   }
288 }
289 static const GstEventMask *
290 gst_type_find_element_src_event_mask (GstPad *pad)
291 {
292   static const GstEventMask mask[] = {
293     { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
294     /* add more if you want, event masks suck and need to die anyway */
295     { 0, }
296   };
297   
298   return mask;
299 }
300 static gboolean 
301 gst_type_find_element_src_event (GstPad *pad, GstEvent *event)
302 {
303   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
304
305   if (typefind->mode == MODE_TYPEFIND) {
306     /* need to do more? */
307     gst_data_unref (GST_DATA (event));
308     return FALSE;
309   }
310   return gst_pad_event_default (pad, event);
311 }
312 typedef struct {
313   GstTypeFindFactory *  factory;
314   gint                  probability;
315   GstCaps *             caps;
316   gint64                requested_offset;
317   guint                 requested_size;
318
319   GList *               buffers;
320   GstTypeFindElement *  self;
321 } TypeFindEntry;
322 static void
323 free_entry_buffers (TypeFindEntry *entry)
324 {
325   g_list_foreach (entry->buffers, (GFunc) gst_data_unref, NULL);
326   g_list_free (entry->buffers);
327   entry->buffers = NULL;
328 }
329 static void
330 free_entry (TypeFindEntry *entry)
331 {
332   free_entry_buffers (entry);
333   
334   if (entry->caps)
335     gst_caps_unref (entry->caps);
336   g_free (entry);
337 }
338 static void
339 start_typefinding (GstTypeFindElement *typefind)
340 {
341   g_assert (typefind->caps == NULL);
342   g_assert (typefind->possibilities == NULL);
343   
344   GST_DEBUG_OBJECT (typefind, "starting typefinding");
345   typefind->mode = MODE_TYPEFIND; 
346   typefind->stream_length_available = TRUE; 
347   typefind->stream_length = 0; 
348 }
349 static void
350 stop_typefinding (GstTypeFindElement *typefind)
351 {
352   /* stop all typefinding and set mode back to normal */
353   gboolean push_cached_buffers = gst_element_get_state (GST_ELEMENT (typefind)) == GST_STATE_PLAYING;
354   
355   GST_DEBUG_OBJECT (typefind, "stopping typefinding%s", push_cached_buffers ? " and pushing cached buffers" : "");
356   if (typefind->possibilities != NULL) {
357     /* this should only happen on PAUSED => READY or EOS */
358     GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions", g_list_length (typefind->possibilities));
359     g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
360     g_list_free (typefind->possibilities);
361     typefind->possibilities = NULL;
362   }
363
364   typefind->mode = MODE_NORMAL;
365
366   if (push_cached_buffers) {
367     GstBuffer *buffer;
368     guint size = gst_buffer_store_get_size (typefind->store, 0);
369
370     GST_LOG_OBJECT (typefind, "seeking back to current position %u", size);
371     if (!gst_pad_send_event (GST_PAD_PEER (typefind->sink), 
372                              gst_event_new_seek (GST_SEEK_METHOD_SET | GST_FORMAT_BYTES, size))) {
373       GST_WARNING_OBJECT (typefind, "could not seek to required position %u, hope for the best", size);
374     }
375     if (size && (buffer = gst_buffer_store_get_buffer (typefind->store, 0, size))) {
376       gst_pad_push (typefind->src, GST_DATA (buffer));
377     } else {
378       size = 0;
379     }
380   }
381   gst_buffer_store_clear (typefind->store);
382 }
383 static guint64
384 find_element_get_length (gpointer data)
385 {
386   TypeFindEntry *entry = (TypeFindEntry *) data;
387   GstTypeFindElement *typefind = entry->self;
388   GstFormat format = GST_FORMAT_BYTES;
389   
390   if (!typefind->stream_length_available) {
391     GST_LOG_OBJECT (entry->self, "'%s' called get_length () but we know it's not available", 
392             GST_PLUGIN_FEATURE_NAME (entry->factory));
393     return 0;
394   }
395   if (entry->self->stream_length == 0) {
396     typefind->stream_length_available = gst_pad_query (GST_PAD_PEER (entry->self->sink), GST_QUERY_TOTAL, 
397             &format, &entry->self->stream_length);
398     if (format != GST_FORMAT_BYTES)
399       typefind->stream_length_available = FALSE;
400     if (!typefind->stream_length_available) {
401       GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () but it's not available", 
402               GST_PLUGIN_FEATURE_NAME (entry->factory));
403       return 0;
404     } else {
405       GST_DEBUG_OBJECT (entry->self, "'%s' called get_length () and it's %"G_GUINT64_FORMAT" bytes", 
406               GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
407     }
408   }
409   
410   return entry->self->stream_length;
411 }
412 static void
413 gst_type_find_element_handle_event (GstPad *pad, GstEvent *event)
414 {
415   TypeFindEntry *entry;
416   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
417
418   if (typefind->mode == MODE_TYPEFIND) {
419     /* need to do more? */
420     switch (GST_EVENT_TYPE (event)) {
421     case GST_EVENT_EOS:
422       /* this should only happen when we got all available data */
423       entry = (TypeFindEntry *) typefind->possibilities ? typefind->possibilities->data : NULL;
424       if (entry && entry->probability >= typefind->min_probability) {
425         GST_INFO_OBJECT (typefind, "'%s' is the best typefind left after we got all data, using it now (probability %u)", 
426                 GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
427         g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
428       }
429       stop_typefinding (typefind);
430       gst_pad_event_default (pad, event);
431       break;
432     default:
433       gst_data_unref (GST_DATA (event));
434       break;
435     }
436   } else {
437     gst_pad_event_default (pad, event);
438   }
439 }
440 static guint8 *
441 find_peek (gpointer data, gint64 offset, guint size)
442 {
443   GstBuffer *buf;
444   TypeFindEntry *entry = (TypeFindEntry *) data;
445   
446   GST_LOG_OBJECT (entry->self, "'%s' called peek (%"G_GINT64_FORMAT", %u)", 
447           GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
448   if (offset >= 0) {
449     buf = gst_buffer_store_get_buffer (entry->self->store, offset, size);
450   } else {
451     /* FIXME: can we do this easily without querying length? */
452     guint64 length = find_element_get_length (data);
453
454     if (length == 0) {
455       buf = NULL;
456     } else {
457       buf = gst_buffer_store_get_buffer (entry->self->store, length + offset, size);
458     }
459   }
460
461   if (buf) {
462     entry->buffers = g_list_prepend (entry->buffers, buf);
463     return GST_BUFFER_DATA (buf);
464   } else {
465     if (entry->requested_size == 0) {
466       GST_LOG_OBJECT (entry->self, "setting requested peek (%"G_GINT64_FORMAT", %u) on '%s'", 
467               offset, size, GST_PLUGIN_FEATURE_NAME (entry->factory));
468       entry->requested_offset = offset;
469       entry->requested_size = size;
470     }
471     return NULL;
472   }
473 }
474 static void
475 find_suggest (gpointer data, guint probability, GstCaps *caps)
476 {
477   gchar *str;
478   TypeFindEntry *entry = (TypeFindEntry *) data;
479   
480   str = gst_caps_to_string (caps);
481   GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %s)", 
482           GST_PLUGIN_FEATURE_NAME (entry->factory), probability, str);
483   g_free (str);
484   if (((gint) probability) > entry->probability) {
485     entry->probability = probability;
486     gst_caps_replace (&entry->caps, caps);
487   }
488 }
489 static gint
490 compare_type_find_entry (gconstpointer a, gconstpointer b)
491 {
492   TypeFindEntry *one = (TypeFindEntry *) a;
493   TypeFindEntry *two = (TypeFindEntry *) b;
494
495   if (one->probability == two->probability) {
496     /* FIXME: can be improved by analyzing requests */
497     return 0;
498   } else {
499     return two->probability - one->probability;
500   }
501 }
502 static gint
503 compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
504 {
505   return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
506 }
507 static void
508 gst_type_find_element_chain (GstPad *pad, GstData *data)
509 {
510   GstTypeFindElement *typefind;
511   GList *entries;
512   TypeFindEntry *entry;
513   GList *walk;
514   GstTypeFind find = {find_peek, find_suggest, NULL, find_element_get_length };
515
516   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
517   if (GST_IS_EVENT (data)) {
518     gst_type_find_element_handle_event (pad, GST_EVENT (data));
519     return;
520   }
521   switch (typefind->mode) {
522     case MODE_NORMAL:
523       gst_pad_push (typefind->src, data);
524       return;
525     case MODE_TYPEFIND: {
526       gst_buffer_store_add_buffer (typefind->store, GST_BUFFER (data));
527       gst_data_unref (data);
528       if (typefind->possibilities == NULL) {
529         /* not yet started, get all typefinding functions into our "queue" */
530         GList *all_factories = gst_type_find_factory_get_list ();
531         GST_INFO_OBJECT (typefind, "starting with %u typefinding functions", 
532                          g_list_length ((GList *) all_factories));
533         
534         all_factories = g_list_sort (all_factories, compare_type_find_factory);
535         walk = all_factories;
536         while (all_factories) {
537           entry = g_new0 (TypeFindEntry, 1);
538           
539           entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
540           entry->self = typefind;
541           entry->probability = 0;
542           typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
543           all_factories = g_list_next (all_factories);
544         }
545         g_list_free (all_factories);
546       }
547       /* call every typefind function once */
548       walk = entries = typefind->possibilities;
549       GST_INFO_OBJECT (typefind, "iterating %u typefinding functions", g_list_length (entries));
550       typefind->possibilities = NULL;
551       while (walk) {
552         find.data = entry = (TypeFindEntry *) walk->data;
553         walk = g_list_next (walk);
554         entry->probability = 0;
555         entry->requested_offset = 0;
556         entry->requested_size = 0;
557         gst_type_find_factory_call_function (entry->factory, &find);
558         free_entry_buffers (entry);
559         if (entry->probability == 0 && entry->requested_size == 0) {
560           GST_DEBUG_OBJECT (typefind, "'%s' was removed - no chance of being the right plugin", 
561                   GST_PLUGIN_FEATURE_NAME (entry->factory));
562           free_entry (entry);
563         } else if (entry->probability >= typefind->max_probability) {
564           /* wooha, got caps */
565           GstCaps *found_caps = entry->caps;
566           guint probability = entry->probability;
567           
568           gst_caps_ref (found_caps);
569           GST_INFO_OBJECT (typefind, "'%s' returned %u/%u probability, using it NOW", 
570                   GST_PLUGIN_FEATURE_NAME (entry->factory), probability, typefind->max_probability);
571           while (walk) {
572             free_entry ((TypeFindEntry *) walk->data);
573             walk = g_list_next (walk);
574           }
575           walk = typefind->possibilities;
576           while (walk) {
577             free_entry (walk->data);
578             walk = g_list_next (walk);
579           }
580           typefind->possibilities = NULL;
581           g_list_free (typefind->possibilities); 
582           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, probability, found_caps);
583           gst_caps_unref (found_caps);
584         } else {
585           typefind->possibilities = g_list_prepend (typefind->possibilities, entry);
586         }
587       }
588       g_list_free (entries);
589       /* we may now already have caps or we might be left without functions to try */
590       if (typefind->caps) {
591         stop_typefinding (typefind);
592       } else if (typefind->possibilities == NULL) {
593         gst_element_error (GST_ELEMENT (typefind), "media type could not be detected");
594       } else {
595         /* set up typefind element for next iteration */
596         typefind->possibilities = g_list_sort (typefind->possibilities, compare_type_find_entry);
597         
598         walk = typefind->possibilities;
599         while (walk) {
600           entry = (TypeFindEntry *) walk->data;
601           walk = g_list_next (walk);
602           if (entry->requested_size > 0) {
603             /* FIXME: need heuristic to find out if we should seek */
604             gint64 seek_offset;
605             GstEvent *event;
606
607             seek_offset = entry->requested_offset > 0 ? entry->requested_offset : 
608                           find_element_get_length (entry) + entry->requested_offset;
609             seek_offset += gst_buffer_store_get_size (typefind->store, seek_offset);
610             event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, seek_offset);
611             if (gst_pad_send_event (GST_PAD_PEER (typefind->sink), event)) {
612               /* done seeking */
613               GST_DEBUG_OBJECT (typefind, "'%s' was reset - seeked to %"G_GINT64_FORMAT, 
614                       GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
615               break;
616             } else if (entry->requested_offset < 0) {
617               /* impossible to seek */
618               GST_DEBUG_OBJECT (typefind, "'%s' was reset - couldn't seek to %"G_GINT64_FORMAT, 
619                       GST_PLUGIN_FEATURE_NAME (entry->factory), seek_offset);
620               entry->requested_size = 0;
621               entry->requested_offset = 0;
622             }
623           }
624         }
625         /* throw out all entries that can't get more data */
626         walk = g_list_next (typefind->possibilities);
627         while (walk) {
628           GList *cur = walk;
629           entry = (TypeFindEntry *) walk->data;
630           walk = g_list_next (walk);
631           if (entry->requested_size == 0) {
632             GST_DEBUG_OBJECT (typefind, "'%s' was removed - higher possibilities available", 
633                     GST_PLUGIN_FEATURE_NAME (entry->factory));
634             free_entry (entry);
635             typefind->possibilities = g_list_delete_link (typefind->possibilities, cur);
636           }
637         }
638         if (g_list_next (typefind->possibilities) == NULL) {
639           entry = (TypeFindEntry *) typefind->possibilities->data;
640           if (entry->probability > typefind->min_probability) {
641             GST_INFO_OBJECT (typefind, "'%s' is the only typefind left, using it now (probability %u)", 
642                     GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
643             g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0, entry->probability, entry->caps);
644             free_entry (entry);
645             g_list_free (typefind->possibilities);
646             typefind->possibilities = NULL;
647             stop_typefinding (typefind);
648           }
649         }
650       }
651       break;
652     }
653     default:
654       g_assert_not_reached ();
655       return;
656   }
657 }
658 static GstElementStateReturn
659 gst_type_find_element_change_state (GstElement *element)
660 {
661   GstTypeFindElement *typefind;
662
663   typefind = GST_TYPE_FIND_ELEMENT (element);
664
665   switch (GST_STATE_TRANSITION (element)) {
666     case GST_STATE_READY_TO_PAUSED:
667       start_typefinding (typefind);
668       break;
669     case GST_STATE_PAUSED_TO_READY:
670       stop_typefinding (typefind);
671       gst_caps_replace (&typefind->caps, NULL);
672       break;
673     default:
674       break;
675   }
676   
677   return GST_ELEMENT_CLASS (parent_class)->change_state (element);
678 }