check/gst/gstplugin.c: More testing
[platform/upstream/gstreamer.git] / plugins / elements / gsttypefindelement.c
1 /* GStreamer
2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3  *
4  * gsttypefindelement.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 #if 0
119 static const GstEventMask *gst_type_find_element_src_event_mask (GstPad * pad);
120 #endif
121
122 static gboolean gst_type_find_element_src_event (GstPad * pad,
123     GstEvent * event);
124 static gboolean gst_type_find_handle_src_query (GstPad * pad, GstQuery * query);
125
126 static gboolean gst_type_find_element_handle_event (GstPad * pad,
127     GstEvent * event);
128 static GstFlowReturn gst_type_find_element_chain (GstPad * sinkpad,
129     GstBuffer * buffer);
130 static GstFlowReturn gst_type_find_element_getrange (GstPad * srcpad,
131     guint64 offset, guint length, GstBuffer ** buffer);
132 static gboolean gst_type_find_element_checkgetrange (GstPad * srcpad);
133
134 static GstStateChangeReturn
135 gst_type_find_element_change_state (GstElement * element,
136     GstStateChange transition);
137 static gboolean gst_type_find_element_activate (GstPad * pad);
138 static gboolean
139 gst_type_find_element_activate_src_pull (GstPad * pad, gboolean active);
140
141 static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
142
143 static void
144 gst_type_find_element_have_type (GstTypeFindElement * typefind,
145     guint probability, const GstCaps * caps)
146 {
147   g_assert (typefind->caps == NULL);
148   g_assert (caps != NULL);
149
150   GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT, caps);
151   typefind->caps = gst_caps_copy (caps);
152   gst_pad_set_caps (typefind->src, (GstCaps *) caps);
153 }
154
155 static void
156 gst_type_find_element_base_init (gpointer g_class)
157 {
158   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
159
160   gst_element_class_add_pad_template (gstelement_class,
161       gst_static_pad_template_get (&type_find_element_src_template));
162   gst_element_class_add_pad_template (gstelement_class,
163       gst_static_pad_template_get (&type_find_element_sink_template));
164   gst_element_class_set_details (gstelement_class,
165       &gst_type_find_element_details);
166 }
167 static void
168 gst_type_find_element_class_init (GstTypeFindElementClass * typefind_class)
169 {
170   GObjectClass *gobject_class = G_OBJECT_CLASS (typefind_class);
171   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (typefind_class);
172
173   gobject_class->set_property =
174       GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
175   gobject_class->get_property =
176       GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
177   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
178
179   typefind_class->have_type = gst_type_find_element_have_type;
180
181   g_object_class_install_property (gobject_class, ARG_CAPS,
182       g_param_spec_boxed ("caps", _("caps"),
183           _("detected capabilities in stream"), gst_caps_get_type (),
184           G_PARAM_READABLE));
185   g_object_class_install_property (gobject_class, ARG_MINIMUM,
186       g_param_spec_uint ("minimum", _("minimum"),
187           "minimum probability required to accept caps", GST_TYPE_FIND_MINIMUM,
188           GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
189   g_object_class_install_property (gobject_class, ARG_MINIMUM,
190       g_param_spec_uint ("maximum", _("maximum"),
191           "probability to stop typefinding", GST_TYPE_FIND_MINIMUM,
192           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 (typefind_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 | G_SIGNAL_TYPE_STATIC_SCOPE);
199
200   gstelement_class->change_state =
201       GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
202 }
203 static void
204 gst_type_find_element_init (GstTypeFindElement * typefind,
205     GstTypeFindElementClass * g_class)
206 {
207   /* sinkpad */
208   typefind->sink =
209       gst_pad_new_from_template (gst_static_pad_template_get
210       (&type_find_element_sink_template), "sink");
211   gst_pad_set_activate_function (typefind->sink,
212       gst_type_find_element_activate);
213   gst_pad_set_chain_function (typefind->sink, gst_type_find_element_chain);
214   gst_pad_set_event_function (typefind->sink,
215       gst_type_find_element_handle_event);
216   gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
217   /* srcpad */
218   typefind->src =
219       gst_pad_new_from_template (gst_static_pad_template_get
220       (&type_find_element_src_template), "src");
221   gst_pad_set_activatepull_function (typefind->src,
222       gst_type_find_element_activate_src_pull);
223   gst_pad_set_checkgetrange_function (typefind->src,
224       gst_type_find_element_checkgetrange);
225   gst_pad_set_getrange_function (typefind->src, gst_type_find_element_getrange);
226   gst_pad_set_event_function (typefind->src, gst_type_find_element_src_event);
227   gst_pad_set_query_function (typefind->src,
228       GST_DEBUG_FUNCPTR (gst_type_find_handle_src_query));
229   gst_pad_use_fixed_caps (typefind->src);
230   gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
231
232   typefind->mode = MODE_TYPEFIND;
233   typefind->caps = NULL;
234   typefind->min_probability = 1;
235   typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
236
237   typefind->store = NULL;
238 }
239 static void
240 gst_type_find_element_dispose (GObject * object)
241 {
242   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
243
244   G_OBJECT_CLASS (parent_class)->dispose (object);
245
246   if (typefind->store) {
247     gst_buffer_unref (typefind->store);
248     typefind->store = NULL;
249   }
250 }
251 static void
252 gst_type_find_element_set_property (GObject * object, guint prop_id,
253     const GValue * value, GParamSpec * pspec)
254 {
255   GstTypeFindElement *typefind;
256
257   g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
258
259   typefind = GST_TYPE_FIND_ELEMENT (object);
260
261   switch (prop_id) {
262     case ARG_MINIMUM:
263       typefind->min_probability = g_value_get_uint (value);
264       g_object_notify (object, "minimum");
265       break;
266     case ARG_MAXIMUM:
267       typefind->max_probability = g_value_get_uint (value);
268       g_object_notify (object, "maximum");
269       break;
270     default:
271       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272       break;
273   }
274 }
275 static void
276 gst_type_find_element_get_property (GObject * object, guint prop_id,
277     GValue * value, GParamSpec * pspec)
278 {
279   GstTypeFindElement *typefind;
280
281   g_return_if_fail (GST_IS_TYPE_FIND_ELEMENT (object));
282
283   typefind = GST_TYPE_FIND_ELEMENT (object);
284
285   switch (prop_id) {
286     case ARG_CAPS:
287       g_value_set_boxed (value, typefind->caps);
288       break;
289     case ARG_MINIMUM:
290       g_value_set_uint (value, typefind->min_probability);
291       break;
292     case ARG_MAXIMUM:
293       g_value_set_uint (value, typefind->max_probability);
294       break;
295     default:
296       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
297       break;
298   }
299 }
300
301 static gboolean
302 gst_type_find_handle_src_query (GstPad * pad, GstQuery * query)
303 {
304   GstTypeFindElement *typefind;
305   gboolean res;
306
307   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
308
309   res = gst_pad_query (GST_PAD_PEER (typefind->sink), query);
310   if (!res)
311     return FALSE;
312
313   switch (GST_QUERY_TYPE (query)) {
314     case GST_QUERY_POSITION:
315     {
316       gint64 peer_pos, peer_total;
317       GstFormat format;
318
319       if (typefind->store == NULL)
320         return TRUE;
321
322       gst_query_parse_position (query, &format, &peer_pos, &peer_total);
323
324       /* FIXME: this code assumes that there's no discont in the queue */
325       switch (format) {
326         case GST_FORMAT_BYTES:
327           peer_pos -= typefind->store->size;
328           break;
329         default:
330           /* FIXME */
331           break;
332       }
333       gst_query_set_position (query, format, peer_pos, peer_total);
334       break;
335     }
336     default:
337       break;
338   }
339
340   return TRUE;
341 }
342
343 #if 0
344 static const GstEventMask *
345 gst_type_find_element_src_event_mask (GstPad * pad)
346 {
347   static const GstEventMask mask[] = {
348     {GST_EVENT_SEEK,
349         GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END |
350           GST_SEEK_FLAG_FLUSH},
351     /* add more if you want, event masks suck and need to die anyway */
352     {0,}
353   };
354
355   return mask;
356 }
357 #endif
358
359 static gboolean
360 gst_type_find_element_src_event (GstPad * pad, GstEvent * event)
361 {
362   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
363
364   if (typefind->mode != MODE_NORMAL) {
365     /* need to do more? */
366     gst_mini_object_unref (GST_MINI_OBJECT (event));
367     return FALSE;
368   }
369   return gst_pad_event_default (pad, event);
370 }
371 typedef struct
372 {
373   GstTypeFindFactory *factory;
374   gint probability;
375   GstCaps *caps;
376   guint requested_size;
377   GstTypeFindElement *self;
378 }
379 TypeFindEntry;
380
381 static inline TypeFindEntry *
382 new_entry (void)
383 {
384   return g_new0 (TypeFindEntry, 1);
385 }
386 static void
387 free_entry (TypeFindEntry * entry)
388 {
389   if (entry->caps)
390     gst_caps_unref (entry->caps);
391   g_free (entry);
392 }
393 static void
394 start_typefinding (GstTypeFindElement * typefind)
395 {
396   g_assert (typefind->possibilities == NULL);
397
398   GST_DEBUG_OBJECT (typefind, "starting typefinding");
399   gst_pad_set_caps (typefind->src, NULL);
400   if (typefind->caps) {
401     gst_caps_replace (&typefind->caps, NULL);
402   }
403   typefind->mode = MODE_TYPEFIND;
404   typefind->stream_length_available = TRUE;
405   typefind->stream_length = 0;
406 }
407 static void
408 stop_typefinding (GstTypeFindElement * typefind)
409 {
410   GstState state;
411   gboolean push_cached_buffers;
412
413   gst_element_get_state (GST_ELEMENT (typefind), &state, NULL, NULL);
414
415   push_cached_buffers = (state >= GST_STATE_PAUSED);
416
417   GST_DEBUG_OBJECT (typefind, "stopping typefinding%s",
418       push_cached_buffers ? " and pushing cached buffers" : "");
419   if (typefind->possibilities != NULL) {
420     /* this should only happen on PAUSED => READY or EOS */
421     GST_LOG_OBJECT (typefind, "freeing remaining %u typefind functions",
422         g_list_length (typefind->possibilities));
423     g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
424     g_list_free (typefind->possibilities);
425     typefind->possibilities = NULL;
426   }
427   //typefind->mode = MODE_TRANSITION;
428
429   if (typefind->store) {
430     if (!push_cached_buffers) {
431       gst_buffer_unref (typefind->store);
432     } else {
433       typefind->mode = MODE_NORMAL;
434       gst_buffer_set_caps (typefind->store, typefind->caps);
435       gst_pad_push (typefind->src, typefind->store);
436     }
437     typefind->store = NULL;
438   }
439 }
440
441 static guint64
442 find_element_get_length (gpointer data)
443 {
444   TypeFindEntry *entry = (TypeFindEntry *) data;
445   GstTypeFindElement *typefind = entry->self;
446   GstFormat format = GST_FORMAT_BYTES;
447
448   if (!typefind->stream_length_available) {
449     GST_LOG_OBJECT (entry->self,
450         "'%s' called get_length () but we know it's not available",
451         GST_PLUGIN_FEATURE_NAME (entry->factory));
452     return 0;
453   }
454   if (entry->self->stream_length == 0) {
455     if (!gst_pad_query_position (GST_PAD_PEER (entry->self->sink), &format,
456             NULL, (gint64 *) & entry->self->stream_length))
457       goto no_length;
458
459     if (format != GST_FORMAT_BYTES) {
460       typefind->stream_length_available = FALSE;
461       entry->self->stream_length = 0;
462     } else {
463       GST_DEBUG_OBJECT (entry->self,
464           "'%s' called get_length () and it's %" G_GUINT64_FORMAT " bytes",
465           GST_PLUGIN_FEATURE_NAME (entry->factory), entry->self->stream_length);
466     }
467   }
468   return entry->self->stream_length;
469
470 no_length:
471   {
472     typefind->stream_length_available = FALSE;
473     GST_DEBUG_OBJECT (entry->self,
474         "'%s' called get_length () but it's not available",
475         GST_PLUGIN_FEATURE_NAME (entry->factory));
476     return 0;
477   }
478 }
479
480 static gboolean
481 gst_type_find_element_handle_event (GstPad * pad, GstEvent * event)
482 {
483   gboolean res = FALSE;
484   TypeFindEntry *entry;
485   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
486
487   GST_DEBUG_OBJECT (typefind, "got event %d in mode %d", GST_EVENT_TYPE (event),
488       typefind->mode);
489
490   switch (typefind->mode) {
491     case MODE_TYPEFIND:
492       switch (GST_EVENT_TYPE (event)) {
493         case GST_EVENT_EOS:
494           /* this should only happen when we got all available data */
495           entry =
496               (TypeFindEntry *) typefind->possibilities ? typefind->
497               possibilities->data : NULL;
498           if (entry && entry->probability >= typefind->min_probability) {
499             GST_INFO_OBJECT (typefind,
500                 "'%s' is the best typefind left after we got all data, using it now (probability %u)",
501                 GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
502             g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
503                 0, entry->probability, entry->caps);
504             stop_typefinding (typefind);
505             gst_buffer_set_caps (typefind->store, typefind->caps);
506             gst_pad_push (typefind->src, typefind->store);
507             typefind->store = NULL;
508             res = gst_pad_event_default (pad, event);
509           } else {
510             res = gst_pad_event_default (pad, event);
511             GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL),
512                 (NULL));
513             stop_typefinding (typefind);
514           }
515           break;
516         default:
517           gst_mini_object_unref (GST_MINI_OBJECT (event));
518           res = TRUE;
519           break;
520       }
521       break;
522     case MODE_NORMAL:
523       if (FALSE) {              // GST_EVENT_TYPE (event) == GST_EVENT_DISCONTINUOUS) {
524         start_typefinding (typefind);
525         gst_event_unref (event);
526         res = TRUE;
527       } else {
528         res = gst_pad_event_default (pad, event);
529       }
530       break;
531     default:
532       g_assert_not_reached ();
533   }
534   return res;
535 }
536 static guint8 *
537 find_peek (gpointer data, gint64 offset, guint size)
538 {
539   TypeFindEntry *entry = (TypeFindEntry *) data;
540
541   GST_LOG_OBJECT (entry->self, "'%s' called peek (%" G_GINT64_FORMAT ", %u)",
542       GST_PLUGIN_FEATURE_NAME (entry->factory), offset, size);
543   if (offset != 0)
544     return NULL;
545
546   if (size <= entry->self->store->size) {
547     return GST_BUFFER_DATA (entry->self->store);
548   } else {
549     entry->requested_size = size;
550
551     GST_LOG_OBJECT (entry->self,
552         "setting requested peek (%" G_GINT64_FORMAT ", %u) on '%s'", offset,
553         size, GST_PLUGIN_FEATURE_NAME (entry->factory));
554     return NULL;
555   }
556 }
557 static void
558 find_suggest (gpointer data, guint probability, const GstCaps * caps)
559 {
560   TypeFindEntry *entry = (TypeFindEntry *) data;
561
562   GST_LOG_OBJECT (entry->self, "'%s' called suggest (%u, %" GST_PTR_FORMAT ")",
563       GST_PLUGIN_FEATURE_NAME (entry->factory), probability, caps);
564   if (((gint) probability) > entry->probability) {
565     entry->probability = probability;
566     gst_caps_replace (&entry->caps, (GstCaps *) caps);
567   }
568 }
569
570 #if 0
571 static gint
572 compare_type_find_entry (gconstpointer a, gconstpointer b)
573 {
574   TypeFindEntry *one = (TypeFindEntry *) a;
575   TypeFindEntry *two = (TypeFindEntry *) b;
576
577   if (one->probability == two->probability) {
578     /* FIXME: can be improved by analyzing requests */
579     return 0;
580   } else {
581     return two->probability - one->probability;
582   }
583 }
584 #endif
585
586 static gint
587 compare_type_find_factory (gconstpointer fac1, gconstpointer fac2)
588 {
589   return GST_PLUGIN_FEATURE (fac1)->rank - GST_PLUGIN_FEATURE (fac2)->rank;
590 }
591
592 static GstFlowReturn
593 gst_type_find_element_chain (GstPad * pad, GstBuffer * buffer)
594 {
595   GstTypeFindElement *typefind;
596   GList *entries;
597   TypeFindEntry *entry;
598   GList *walk;
599   GstFlowReturn res = GST_FLOW_OK;
600   GstTypeFind find = { find_peek, find_suggest, NULL, find_element_get_length };
601
602   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
603
604   switch (typefind->mode) {
605     case MODE_NORMAL:
606       gst_buffer_set_caps (buffer, typefind->caps);
607       return gst_pad_push (typefind->src, buffer);
608     case MODE_TYPEFIND:{
609       gboolean done = TRUE;
610
611       if (typefind->store)
612         typefind->store = gst_buffer_join (typefind->store, buffer);
613       else
614         typefind->store = buffer;
615
616       if (typefind->possibilities == NULL) {
617         /* not yet started, get all typefinding functions into our "queue" */
618         GList *all_factories = gst_type_find_factory_get_list ();
619
620         GST_INFO_OBJECT (typefind, "starting with %u typefinding functions",
621             g_list_length (all_factories));
622
623         all_factories = g_list_sort (all_factories, compare_type_find_factory);
624         walk = all_factories;
625         while (all_factories) {
626           entry = new_entry ();
627
628           entry->factory = GST_TYPE_FIND_FACTORY (all_factories->data);
629           entry->self = typefind;
630           entry->probability = 0;
631           typefind->possibilities =
632               g_list_prepend (typefind->possibilities, entry);
633           all_factories = g_list_next (all_factories);
634         }
635         gst_plugin_feature_list_free (all_factories);
636       }
637
638       /* call every typefind function once */
639       walk = entries = typefind->possibilities;
640       GST_INFO_OBJECT (typefind, "iterating %u typefinding functions",
641           g_list_length (entries));
642       typefind->possibilities = NULL;
643       while (walk) {
644         find.data = entry = (TypeFindEntry *) walk->data;
645         walk = g_list_next (walk);
646         if (entry->probability == 0) {
647           entry->requested_size = 0;
648           gst_type_find_factory_call_function (entry->factory, &find);
649         } else {
650           typefind->possibilities =
651               g_list_prepend (typefind->possibilities, entry);
652           continue;
653         }
654         if (entry->probability == 0 && entry->requested_size == 0) {
655           GST_DEBUG_OBJECT (typefind,
656               "'%s' was removed - no chance of being the right plugin",
657               GST_PLUGIN_FEATURE_NAME (entry->factory));
658           free_entry (entry);
659         } else if (entry->probability >= typefind->max_probability) {
660           /* wooha, got caps */
661           GstCaps *found_caps = entry->caps;
662           guint probability = entry->probability;
663
664           GST_INFO_OBJECT (typefind,
665               "'%s' returned %u/%u probability, using it NOW",
666               GST_PLUGIN_FEATURE_NAME (entry->factory), probability,
667               typefind->max_probability);
668           while (walk) {
669             free_entry ((TypeFindEntry *) walk->data);
670             walk = g_list_next (walk);
671           }
672           walk = typefind->possibilities;
673           while (walk) {
674             free_entry (walk->data);
675             walk = g_list_next (walk);
676           }
677           g_list_free (typefind->possibilities);
678           typefind->possibilities = NULL;
679           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
680               probability, found_caps);
681           free_entry (entry);
682         } else {
683           typefind->possibilities =
684               g_list_prepend (typefind->possibilities, entry);
685           if (entry->requested_size != 0)
686             done = FALSE;
687         }
688       }
689       g_list_free (entries);
690
691       /* we may now already have caps or we might be left without functions to try */
692       if (typefind->caps) {
693         stop_typefinding (typefind);
694       } else if (typefind->possibilities == NULL) {
695         GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
696         stop_typefinding (typefind);
697         return GST_FLOW_ERROR;
698       } else if (done) {
699         TypeFindEntry *best = NULL;
700
701         walk = typefind->possibilities;
702         while (walk) {
703           entry = (TypeFindEntry *) typefind->possibilities->data;
704           if ((!best || entry->probability > best->probability) &&
705               entry->probability >= typefind->min_probability) {
706             best = entry;
707           }
708           walk = g_list_next (walk);
709         }
710
711         if (best) {
712           GST_INFO_OBJECT (typefind,
713               "'%s' is the only typefind left, using it now (probability %u)",
714               GST_PLUGIN_FEATURE_NAME (entry->factory), entry->probability);
715           g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
716               0, entry->probability, entry->caps);
717           g_list_foreach (typefind->possibilities, (GFunc) free_entry, NULL);
718           g_list_free (typefind->possibilities);
719           typefind->possibilities = NULL;
720           stop_typefinding (typefind);
721         } else {
722           GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
723           stop_typefinding (typefind);
724           return GST_FLOW_ERROR;
725         }
726       }
727       break;
728     }
729     default:
730       g_assert_not_reached ();
731       return GST_FLOW_ERROR;
732   }
733
734   return res;
735 }
736
737 static gboolean
738 gst_type_find_element_checkgetrange (GstPad * srcpad)
739 {
740   GstTypeFindElement *typefind;
741
742   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));
743
744   return gst_pad_check_pull_range (typefind->sink);
745 }
746
747 static GstFlowReturn
748 gst_type_find_element_getrange (GstPad * srcpad,
749     guint64 offset, guint length, GstBuffer ** buffer)
750 {
751   GstTypeFindElement *typefind;
752   GstFlowReturn ret;
753
754   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));
755
756   ret = gst_pad_pull_range (typefind->sink, offset, length, buffer);
757
758   if (ret == GST_FLOW_OK && buffer && *buffer)
759     gst_buffer_set_caps (*buffer, typefind->caps);
760
761   return ret;
762 }
763
764 static gboolean
765 gst_type_find_element_activate_src_pull (GstPad * pad, gboolean active)
766 {
767   GstTypeFindElement *typefind;
768
769   typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));
770
771   return gst_pad_activate_pull (typefind->sink, active);
772 }
773
774 static gboolean
775 gst_type_find_element_activate (GstPad * pad)
776 {
777   GstCaps *found_caps = NULL;
778   GstTypeFindElement *typefind;
779
780   typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));
781
782   /* 1. try to activate in pull mode. if not, switch to push and succeed.
783      2. try to pull type find.
784      3. deactivate pull mode.
785      4. src pad might have been activated push by the state change. deactivate.
786      5. if we didn't find any caps, fail.
787      6. emit have-type; maybe the app connected the source pad to something.
788      7. if the sink pad is activated, we are in pull mode. succeed.
789      otherwise activate both pads in push mode and succeed.
790    */
791
792   /* 1 */
793   if (!gst_pad_activate_pull (pad, TRUE)) {
794     start_typefinding (typefind);
795     return gst_pad_activate_push (pad, TRUE);
796   }
797
798   /* 2 */
799   {
800     GstPad *peer;
801
802     peer = gst_pad_get_peer (pad);
803     if (peer) {
804       gint64 size;
805       GstFormat format = GST_FORMAT_BYTES;
806
807       gst_pad_query_position (peer, &format, NULL, &size);
808       found_caps = gst_type_find_helper (peer, (guint64) size);
809       gst_object_unref (peer);
810     }
811   }
812
813   /* 3 */
814   gst_pad_activate_pull (pad, FALSE);
815
816   /* 4 */
817   gst_pad_activate_push (typefind->src, FALSE);
818
819   /* 5 */
820   if (!found_caps)
821     return FALSE;
822
823   /* 6 */
824   g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
825       0, 100, found_caps);
826   typefind->mode = MODE_NORMAL;
827   /* FIXME see if I can unref the caps here */
828
829   /* 7 */
830   if (gst_pad_is_active (pad))
831     return TRUE;
832   else {
833     gboolean ret;
834
835     ret = gst_pad_activate_push (typefind->src, TRUE);
836     ret &= gst_pad_activate_push (pad, TRUE);
837     return ret;
838   }
839 }
840
841 static GstStateChangeReturn
842 gst_type_find_element_change_state (GstElement * element,
843     GstStateChange transition)
844 {
845   GstStateChangeReturn ret;
846   GstTypeFindElement *typefind;
847
848   typefind = GST_TYPE_FIND_ELEMENT (element);
849
850
851   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
852
853   switch (transition) {
854     case GST_STATE_CHANGE_PAUSED_TO_READY:
855       gst_caps_replace (&typefind->caps, NULL);
856       break;
857     default:
858       break;
859   }
860
861   return ret;
862 }