2 * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
4 * gsttypefindelement.c: element that detects type of stream
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.
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.
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.
22 /* FIXME: need a better solution for non-seekable streams */
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
34 * 8) take best available result and use its caps
36 * The element has two scheduling modes:
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
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
54 #include "gsttypefindelement.h"
55 #include "gst/gst_private.h"
56 #include "gst/gst-i18n-lib.h"
57 #include "gst/base/gsttypefindhelper.h"
59 #include <gst/gsttypefind.h>
60 #include <gst/gstutils.h>
61 #include <gst/gsterror.h>
63 GST_DEBUG_CATEGORY_STATIC (gst_type_find_element_debug);
64 #define GST_CAT_DEFAULT gst_type_find_element_debug
66 static const GstElementDetails gst_type_find_element_details =
67 GST_ELEMENT_DETAILS ("TypeFind",
69 "Finds the media type of a stream",
70 "Benjamin Otte <in7y118@public.uni-hamburg.de>");
72 /* generic templates */
73 GstStaticPadTemplate type_find_element_sink_template =
74 GST_STATIC_PAD_TEMPLATE ("sink",
79 GstStaticPadTemplate type_find_element_src_template =
80 GST_STATIC_PAD_TEMPLATE ("src",
85 /* Require at least 2kB of data before we attempt typefinding in chain-mode.
86 * 128kB is massive overkill for the maximum, but doesn't do any harm */
87 #define TYPE_FIND_MIN_SIZE (2*1024)
88 #define TYPE_FIND_MAX_SIZE (128*1024)
90 /* TypeFind signals and args */
105 MODE_NORMAL, /* act as identity */
106 MODE_TYPEFIND, /* do typefinding */
107 MODE_ERROR /* had fatal error */
111 #define _do_init(bla) \
112 GST_DEBUG_CATEGORY_INIT (gst_type_find_element_debug, "typefind", \
113 GST_DEBUG_BG_YELLOW | GST_DEBUG_FG_GREEN, "type finding element");
115 GST_BOILERPLATE_FULL (GstTypeFindElement, gst_type_find_element, GstElement,
116 GST_TYPE_ELEMENT, _do_init);
118 static void gst_type_find_element_dispose (GObject * object);
119 static void gst_type_find_element_set_property (GObject * object,
120 guint prop_id, const GValue * value, GParamSpec * pspec);
121 static void gst_type_find_element_get_property (GObject * object,
122 guint prop_id, GValue * value, GParamSpec * pspec);
125 static const GstEventMask *gst_type_find_element_src_event_mask (GstPad * pad);
128 static gboolean gst_type_find_element_src_event (GstPad * pad,
130 static gboolean gst_type_find_handle_src_query (GstPad * pad, GstQuery * query);
132 static gboolean gst_type_find_element_handle_event (GstPad * pad,
134 static GstFlowReturn gst_type_find_element_chain (GstPad * sinkpad,
136 static GstFlowReturn gst_type_find_element_getrange (GstPad * srcpad,
137 guint64 offset, guint length, GstBuffer ** buffer);
138 static gboolean gst_type_find_element_checkgetrange (GstPad * srcpad);
140 static GstStateChangeReturn
141 gst_type_find_element_change_state (GstElement * element,
142 GstStateChange transition);
143 static gboolean gst_type_find_element_activate (GstPad * pad);
145 gst_type_find_element_activate_src_pull (GstPad * pad, gboolean active);
147 gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind);
149 gst_type_find_element_send_cached_events (GstTypeFindElement * typefind);
151 static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
154 gst_type_find_element_have_type (GstTypeFindElement * typefind,
155 guint probability, const GstCaps * caps)
157 g_assert (typefind->caps == NULL);
158 g_assert (caps != NULL);
160 GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT, caps);
161 typefind->caps = gst_caps_copy (caps);
162 gst_pad_set_caps (typefind->src, (GstCaps *) caps);
166 gst_type_find_element_base_init (gpointer g_class)
168 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
170 gst_element_class_add_pad_template (gstelement_class,
171 gst_static_pad_template_get (&type_find_element_src_template));
172 gst_element_class_add_pad_template (gstelement_class,
173 gst_static_pad_template_get (&type_find_element_sink_template));
174 gst_element_class_set_details (gstelement_class,
175 &gst_type_find_element_details);
178 gst_type_find_element_class_init (GstTypeFindElementClass * typefind_class)
180 GObjectClass *gobject_class = G_OBJECT_CLASS (typefind_class);
181 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (typefind_class);
183 gobject_class->set_property =
184 GST_DEBUG_FUNCPTR (gst_type_find_element_set_property);
185 gobject_class->get_property =
186 GST_DEBUG_FUNCPTR (gst_type_find_element_get_property);
187 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose);
189 typefind_class->have_type = gst_type_find_element_have_type;
191 g_object_class_install_property (gobject_class, ARG_CAPS,
192 g_param_spec_boxed ("caps", _("caps"),
193 _("detected capabilities in stream"), gst_caps_get_type (),
195 g_object_class_install_property (gobject_class, ARG_MINIMUM,
196 g_param_spec_uint ("minimum", _("minimum"),
197 "minimum probability required to accept caps", GST_TYPE_FIND_MINIMUM,
198 GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE));
199 g_object_class_install_property (gobject_class, ARG_MAXIMUM,
200 g_param_spec_uint ("maximum", _("maximum"),
201 "probability to stop typefinding (deprecated; non-functional)",
202 GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM,
205 * GstTypeFindElement::have-type:
206 * @typefind: the typefind instance
207 * @probability: the probability of the type found
208 * @caps: the caps of the type found
210 * This signal gets emitted when the type and its probability has
213 gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have_type",
214 G_TYPE_FROM_CLASS (typefind_class), G_SIGNAL_RUN_FIRST,
215 G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
216 gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
217 G_TYPE_UINT, GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE);
219 gstelement_class->change_state =
220 GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
223 gst_type_find_element_init (GstTypeFindElement * typefind,
224 GstTypeFindElementClass * g_class)
228 gst_pad_new_from_static_template (&type_find_element_sink_template,
231 gst_pad_set_activate_function (typefind->sink,
232 GST_DEBUG_FUNCPTR (gst_type_find_element_activate));
233 gst_pad_set_chain_function (typefind->sink,
234 GST_DEBUG_FUNCPTR (gst_type_find_element_chain));
235 gst_pad_set_event_function (typefind->sink,
236 GST_DEBUG_FUNCPTR (gst_type_find_element_handle_event));
237 gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
241 gst_pad_new_from_static_template (&type_find_element_src_template, "src");
243 gst_pad_set_activatepull_function (typefind->src,
244 GST_DEBUG_FUNCPTR (gst_type_find_element_activate_src_pull));
245 gst_pad_set_checkgetrange_function (typefind->src,
246 GST_DEBUG_FUNCPTR (gst_type_find_element_checkgetrange));
247 gst_pad_set_getrange_function (typefind->src,
248 GST_DEBUG_FUNCPTR (gst_type_find_element_getrange));
249 gst_pad_set_event_function (typefind->src,
250 GST_DEBUG_FUNCPTR (gst_type_find_element_src_event));
251 gst_pad_set_query_function (typefind->src,
252 GST_DEBUG_FUNCPTR (gst_type_find_handle_src_query));
253 gst_pad_use_fixed_caps (typefind->src);
254 gst_element_add_pad (GST_ELEMENT (typefind), typefind->src);
256 typefind->mode = MODE_TYPEFIND;
257 typefind->caps = NULL;
258 typefind->min_probability = 1;
259 typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
261 typefind->store = NULL;
264 gst_type_find_element_dispose (GObject * object)
266 GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);
268 G_OBJECT_CLASS (parent_class)->dispose (object);
270 if (typefind->store) {
271 gst_buffer_unref (typefind->store);
272 typefind->store = NULL;
276 gst_type_find_element_set_property (GObject * object, guint prop_id,
277 const GValue * value, GParamSpec * pspec)
279 GstTypeFindElement *typefind;
281 typefind = GST_TYPE_FIND_ELEMENT (object);
285 typefind->min_probability = g_value_get_uint (value);
288 typefind->max_probability = g_value_get_uint (value);
291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
296 gst_type_find_element_get_property (GObject * object, guint prop_id,
297 GValue * value, GParamSpec * pspec)
299 GstTypeFindElement *typefind;
301 typefind = GST_TYPE_FIND_ELEMENT (object);
305 g_value_set_boxed (value, typefind->caps);
308 g_value_set_uint (value, typefind->min_probability);
311 g_value_set_uint (value, typefind->max_probability);
314 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
320 gst_type_find_handle_src_query (GstPad * pad, GstQuery * query)
322 GstTypeFindElement *typefind;
323 gboolean res = FALSE;
326 typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
328 peer = gst_pad_get_peer (typefind->sink);
332 res = gst_pad_query (peer, query);
336 switch (GST_QUERY_TYPE (query)) {
337 case GST_QUERY_POSITION:
342 if (typefind->store == NULL)
345 gst_query_parse_position (query, &format, &peer_pos);
347 /* FIXME: this code assumes that there's no discont in the queue */
349 case GST_FORMAT_BYTES:
350 peer_pos -= typefind->store->size;
356 gst_query_set_position (query, format, peer_pos);
364 gst_object_unref (peer);
369 static const GstEventMask *
370 gst_type_find_element_src_event_mask (GstPad * pad)
372 static const GstEventMask mask[] = {
374 GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END |
375 GST_SEEK_FLAG_FLUSH},
376 /* add more if you want, event masks suck and need to die anyway */
385 gst_type_find_element_src_event (GstPad * pad, GstEvent * event)
387 GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
389 if (typefind->mode != MODE_NORMAL) {
390 /* need to do more? */
391 gst_mini_object_unref (GST_MINI_OBJECT (event));
394 return gst_pad_event_default (pad, event);
398 start_typefinding (GstTypeFindElement * typefind)
400 GST_DEBUG_OBJECT (typefind, "starting typefinding");
401 gst_pad_set_caps (typefind->src, NULL);
402 if (typefind->caps) {
403 gst_caps_replace (&typefind->caps, NULL);
405 typefind->mode = MODE_TYPEFIND;
409 stop_typefinding (GstTypeFindElement * typefind)
412 gboolean push_cached_buffers;
414 gst_element_get_state (GST_ELEMENT (typefind), &state, NULL, 0);
416 push_cached_buffers = (state >= GST_STATE_PAUSED);
418 GST_DEBUG_OBJECT (typefind, "stopping typefinding%s",
419 push_cached_buffers ? " and pushing cached buffers" : "");
421 if (typefind->store) {
422 if (!push_cached_buffers) {
423 gst_buffer_unref (typefind->store);
425 GstPad *peer = gst_pad_get_peer (typefind->src);
427 typefind->mode = MODE_NORMAL;
428 gst_buffer_set_caps (typefind->store, typefind->caps);
430 /* make sure the user gets a meaningful error message in this case,
431 * which is not a core bug or bug of any kind (as the default error
432 * message emitted by gstpad.c otherwise would make you think) */
433 if (peer && GST_PAD_CHAINFUNC (peer) == NULL) {
434 GST_DEBUG_OBJECT (typefind, "upstream only supports push mode, while "
435 "downstream element only works in pull mode, erroring out");
436 GST_ELEMENT_ERROR (typefind, STREAM, FAILED,
437 ("%s cannot work in push mode. The operation is not supported "
438 "with this source element or protocol.",
439 G_OBJECT_TYPE_NAME (GST_PAD_PARENT (peer))),
440 ("Downstream pad %s:%s has no chainfunction, and the upstream "
441 "element does not support pull mode",
442 GST_DEBUG_PAD_NAME (peer)));
443 typefind->mode = MODE_ERROR; /* make the chain function error out */
445 gst_type_find_element_send_cached_events (typefind);
446 gst_pad_push (typefind->src, typefind->store);
450 gst_object_unref (peer);
452 typefind->store = NULL;
457 gst_type_find_element_handle_event (GstPad * pad, GstEvent * event)
459 gboolean res = FALSE;
460 GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
462 GST_DEBUG_OBJECT (typefind, "got %s event in mode %d",
463 GST_EVENT_TYPE_NAME (event), typefind->mode);
465 switch (typefind->mode) {
467 switch (GST_EVENT_TYPE (event)) {
469 GstTypeFindProbability prob = 0;
470 GstCaps *caps = NULL;
472 GST_INFO_OBJECT (typefind, "Got EOS and no type found yet");
474 /* we might not have started typefinding yet because there was not
475 * enough data so far; just give it a shot now and see what we get */
476 if (typefind->store) {
477 caps = gst_type_find_helper_for_buffer (GST_OBJECT (typefind),
478 typefind->store, &prob);
480 if (caps && prob >= typefind->min_probability) {
481 g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
484 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
487 gst_caps_replace (&caps, NULL);
490 stop_typefinding (typefind);
491 res = gst_pad_event_default (pad, event);
495 GST_DEBUG_OBJECT (typefind, "Saving %s event to send later",
496 GST_EVENT_TYPE_NAME (event));
497 typefind->cached_events =
498 g_list_append (typefind->cached_events, event);
504 res = gst_pad_event_default (pad, event);
509 g_assert_not_reached ();
515 gst_type_find_element_send_cached_events (GstTypeFindElement * typefind)
519 for (l = typefind->cached_events; l != NULL; l = l->next) {
520 GstEvent *event = GST_EVENT (l->data);
522 GST_DEBUG_OBJECT (typefind, "sending cached %s event",
523 GST_EVENT_TYPE_NAME (event));
524 gst_pad_push_event (typefind->src, event);
526 g_list_free (typefind->cached_events);
527 typefind->cached_events = NULL;
531 gst_type_find_element_chain (GstPad * pad, GstBuffer * buffer)
533 GstTypeFindElement *typefind;
534 GstFlowReturn res = GST_FLOW_OK;
536 typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
538 /* Shortcircuit typefinding if we already have non-any caps */
539 if (typefind->mode == MODE_TYPEFIND) {
540 const GstCaps *caps = GST_BUFFER_CAPS (buffer);
542 if (caps && !gst_caps_is_any (caps)) {
543 GST_DEBUG_OBJECT (typefind, "Skipping typefinding, using caps from "
544 "upstream buffer: %" GST_PTR_FORMAT, caps);
545 typefind->mode = MODE_NORMAL;
546 g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
547 GST_TYPE_FIND_MAXIMUM, caps);
549 gst_type_find_element_send_cached_events (typefind);
550 if (typefind->store) {
551 GST_DEBUG_OBJECT (typefind, "Pushing store: %d",
552 GST_BUFFER_SIZE (typefind->store));
553 gst_buffer_set_caps (typefind->store, typefind->caps);
554 gst_pad_push (typefind->src, typefind->store);
555 typefind->store = NULL;
560 switch (typefind->mode) {
562 /* we should already have called GST_ELEMENT_ERROR */
563 return GST_FLOW_ERROR;
565 gst_buffer_set_caps (buffer, typefind->caps);
566 return gst_pad_push (typefind->src, buffer);
569 typefind->store = gst_buffer_join (typefind->store, buffer);
571 typefind->store = buffer;
573 res = gst_type_find_element_chain_do_typefinding (typefind);
575 if (typefind->mode == MODE_ERROR)
576 res = GST_FLOW_ERROR;
581 g_assert_not_reached ();
582 return GST_FLOW_ERROR;
589 gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind)
591 GstTypeFindProbability probability;
594 if (GST_BUFFER_SIZE (typefind->store) < TYPE_FIND_MIN_SIZE) {
595 GST_DEBUG_OBJECT (typefind, "not enough data for typefinding yet "
596 "(%u bytes)", GST_BUFFER_SIZE (typefind->store));
600 caps = gst_type_find_helper_for_buffer (GST_OBJECT (typefind),
601 typefind->store, &probability);
603 if (caps == NULL && GST_BUFFER_SIZE (typefind->store) > TYPE_FIND_MAX_SIZE) {
604 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
605 stop_typefinding (typefind);
606 return GST_FLOW_ERROR;
607 } else if (caps == NULL) {
608 GST_DEBUG_OBJECT (typefind, "no caps found with %u bytes of data, "
609 "waiting for more data", GST_BUFFER_SIZE (typefind->store));
614 if (probability < typefind->min_probability) {
615 GST_DEBUG_OBJECT (typefind, "found caps %" GST_PTR_FORMAT ", but "
616 "probability is %u which is lower than the required minimum of %u",
617 caps, probability, typefind->min_probability);
619 if (GST_BUFFER_SIZE (typefind->store) >= TYPE_FIND_MAX_SIZE) {
620 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
621 stop_typefinding (typefind);
622 return GST_FLOW_ERROR;
625 GST_DEBUG_OBJECT (typefind, "waiting for more data to try again");
629 /* probability is good enough too, so let's make it known ... */
630 g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
633 /* .. and send out the accumulated data */
634 stop_typefinding (typefind);
639 gst_type_find_element_checkgetrange (GstPad * srcpad)
641 GstTypeFindElement *typefind;
643 typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));
645 return gst_pad_check_pull_range (typefind->sink);
649 gst_type_find_element_getrange (GstPad * srcpad,
650 guint64 offset, guint length, GstBuffer ** buffer)
652 GstTypeFindElement *typefind;
655 typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));
657 ret = gst_pad_pull_range (typefind->sink, offset, length, buffer);
659 if (ret == GST_FLOW_OK && buffer && *buffer)
660 gst_buffer_set_caps (*buffer, typefind->caps);
666 gst_type_find_element_activate_src_pull (GstPad * pad, gboolean active)
668 GstTypeFindElement *typefind;
670 typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));
672 return gst_pad_activate_pull (typefind->sink, active);
676 gst_type_find_element_activate (GstPad * pad)
678 GstTypeFindProbability probability = 0;
679 GstCaps *found_caps = NULL;
680 GstTypeFindElement *typefind;
682 typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));
684 /* 1. try to activate in pull mode. if not, switch to push and succeed.
685 2. try to pull type find.
686 3. deactivate pull mode.
687 4. src pad might have been activated push by the state change. deactivate.
688 5. if we didn't find any caps, fail.
689 6. emit have-type; maybe the app connected the source pad to something.
690 7. if the sink pad is activated, we are in pull mode. succeed.
691 otherwise activate both pads in push mode and succeed.
695 if (!gst_pad_check_pull_range (pad) || !gst_pad_activate_pull (pad, TRUE)) {
696 start_typefinding (typefind);
697 return gst_pad_activate_push (pad, TRUE);
704 peer = gst_pad_get_peer (pad);
707 GstFormat format = GST_FORMAT_BYTES;
709 if (!gst_pad_query_duration (peer, &format, &size)) {
710 GST_WARNING_OBJECT (typefind, "Could not query upstream length!");
714 found_caps = gst_type_find_helper_get_range (GST_OBJECT (peer),
715 (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
716 (guint64) size, &probability);
718 gst_object_unref (peer);
723 gst_pad_activate_pull (pad, FALSE);
726 gst_pad_activate_push (typefind->src, FALSE);
729 if (!found_caps || probability < typefind->min_probability) {
730 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
731 gst_caps_replace (&found_caps, NULL);
736 g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
737 0, probability, found_caps);
738 gst_caps_unref (found_caps);
739 typefind->mode = MODE_NORMAL;
742 if (gst_pad_is_active (pad))
747 ret = gst_pad_activate_push (typefind->src, TRUE);
748 ret &= gst_pad_activate_push (pad, TRUE);
753 static GstStateChangeReturn
754 gst_type_find_element_change_state (GstElement * element,
755 GstStateChange transition)
757 GstStateChangeReturn ret;
758 GstTypeFindElement *typefind;
760 typefind = GST_TYPE_FIND_ELEMENT (element);
763 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
765 switch (transition) {
766 case GST_STATE_CHANGE_PAUSED_TO_READY:
767 gst_caps_replace (&typefind->caps, NULL);
768 g_list_foreach (typefind->cached_events,
769 (GFunc) gst_mini_object_unref, NULL);
770 g_list_free (typefind->cached_events);
771 typefind->cached_events = NULL;