16 * You should have received a copy of the GNU Library General Public |
16 * You should have received a copy of the GNU Library General Public |
17 * License along with this library; if not, write to the |
17 * License along with this library; if not, write to the |
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
19 * Boston, MA 02111-1307, USA. |
19 * Boston, MA 02111-1307, USA. |
20 */ |
20 */ |
21 /** |
|
22 * SECTION:element-typefind |
|
23 * |
|
24 * Determines the media-type of a stream. It applies typefind functions in the |
|
25 * order of their rank. One the type has been deteted it sets its src pad caps |
|
26 * to the found media type. |
|
27 * |
|
28 * Plugins can register custom typefinders by using #GstTypeFindFactory. |
|
29 */ |
|
30 |
21 |
31 /* FIXME: need a better solution for non-seekable streams */ |
22 /* FIXME: need a better solution for non-seekable streams */ |
32 |
23 |
33 /* way of operation: |
24 /* way of operation: |
34 * 1) get a list of all typefind functions sorted best to worst |
25 * 1) get a list of all typefind functions sorted best to worst |
35 * 2) if all elements have been called with all requested data goto 8 |
26 * 2) if all elements have been called with all requested data goto 8 |
36 * 3) call all functions once with all available data |
27 * 3) call all functions once with all available data |
37 * 4) if a function returns a value >= PROP_MAXIMUM goto 8 |
28 * 4) if a function returns a value >= ARG_MAXIMUM goto 8 |
38 * 5) all functions with a result > PROP_MINIMUM or functions that did not get |
29 * 5) all functions with a result > ARG_MINIMUM or functions that did not get |
39 * all requested data (where peek returned NULL) stay in list |
30 * all requested data (where peek returned NULL) stay in list |
40 * 6) seek to requested offset of best function that still has open data |
31 * 6) seek to requested offset of best function that still has open data |
41 * requests |
32 * requests |
42 * 7) goto 2 |
33 * 7) goto 2 |
43 * 8) take best available result and use its caps |
34 * 8) take best available result and use its caps |
160 gst_type_find_element_have_type (GstTypeFindElement * typefind, |
152 gst_type_find_element_have_type (GstTypeFindElement * typefind, |
161 guint probability, const GstCaps * caps) |
153 guint probability, const GstCaps * caps) |
162 { |
154 { |
163 g_assert (caps != NULL); |
155 g_assert (caps != NULL); |
164 |
156 |
165 GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT ", probability=%u", |
157 GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT, caps); |
166 caps, probability); |
|
167 if (typefind->caps) |
158 if (typefind->caps) |
168 gst_caps_unref (typefind->caps); |
159 gst_caps_unref (typefind->caps); |
169 typefind->caps = gst_caps_copy (caps); |
160 typefind->caps = gst_caps_copy (caps); |
170 gst_pad_set_caps (typefind->src, (GstCaps *) caps); |
161 gst_pad_set_caps (typefind->src, (GstCaps *) caps); |
171 } |
162 } |
198 GST_DEBUG_FUNCPTR (gst_type_find_element_get_property); |
189 GST_DEBUG_FUNCPTR (gst_type_find_element_get_property); |
199 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose); |
190 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_type_find_element_dispose); |
200 |
191 |
201 typefind_class->have_type = gst_type_find_element_have_type; |
192 typefind_class->have_type = gst_type_find_element_have_type; |
202 |
193 |
203 g_object_class_install_property (gobject_class, PROP_CAPS, |
194 g_object_class_install_property (gobject_class, ARG_CAPS, |
204 g_param_spec_boxed ("caps", _("caps"), |
195 g_param_spec_boxed ("caps", _("caps"), |
205 _("detected capabilities in stream"), gst_caps_get_type (), |
196 _("detected capabilities in stream"), gst_caps_get_type (), |
206 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); |
197 G_PARAM_READABLE)); |
207 g_object_class_install_property (gobject_class, PROP_MINIMUM, |
198 g_object_class_install_property (gobject_class, ARG_MINIMUM, |
208 g_param_spec_uint ("minimum", _("minimum"), |
199 g_param_spec_uint ("minimum", _("minimum"), |
209 "minimum probability required to accept caps", GST_TYPE_FIND_MINIMUM, |
200 "minimum probability required to accept caps", GST_TYPE_FIND_MINIMUM, |
210 GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, |
201 GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM, G_PARAM_READWRITE)); |
211 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
202 g_object_class_install_property (gobject_class, ARG_MAXIMUM, |
212 g_object_class_install_property (gobject_class, PROP_MAXIMUM, |
|
213 g_param_spec_uint ("maximum", _("maximum"), |
203 g_param_spec_uint ("maximum", _("maximum"), |
214 "probability to stop typefinding (deprecated; non-functional)", |
204 "probability to stop typefinding (deprecated; non-functional)", |
215 GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, |
205 GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM, |
216 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
206 G_PARAM_READWRITE)); |
217 g_object_class_install_property (gobject_class, PROP_FORCE_CAPS, |
|
218 g_param_spec_boxed ("force-caps", _("force caps"), |
|
219 _("force caps without doing a typefind"), gst_caps_get_type (), |
|
220 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
|
221 /** |
207 /** |
222 * GstTypeFindElement::have-type: |
208 * GstTypeFindElement::have-type: |
223 * @typefind: the typefind instance |
209 * @typefind: the typefind instance |
224 * @probability: the probability of the type found |
210 * @probability: the probability of the type found |
225 * @caps: the caps of the type found |
211 * @caps: the caps of the type found |
278 typefind->min_probability = 1; |
263 typefind->min_probability = 1; |
279 typefind->max_probability = GST_TYPE_FIND_MAXIMUM; |
264 typefind->max_probability = GST_TYPE_FIND_MAXIMUM; |
280 |
265 |
281 typefind->store = NULL; |
266 typefind->store = NULL; |
282 } |
267 } |
283 |
|
284 static void |
268 static void |
285 gst_type_find_element_dispose (GObject * object) |
269 gst_type_find_element_dispose (GObject * object) |
286 { |
270 { |
287 GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object); |
271 GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object); |
|
272 |
|
273 G_OBJECT_CLASS (parent_class)->dispose (object); |
288 |
274 |
289 if (typefind->store) { |
275 if (typefind->store) { |
290 gst_buffer_unref (typefind->store); |
276 gst_buffer_unref (typefind->store); |
291 typefind->store = NULL; |
277 typefind->store = NULL; |
292 } |
278 } |
293 if (typefind->force_caps) { |
279 } |
294 gst_caps_unref (typefind->force_caps); |
|
295 typefind->force_caps = NULL; |
|
296 } |
|
297 |
|
298 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
299 } |
|
300 |
|
301 static void |
280 static void |
302 gst_type_find_element_set_property (GObject * object, guint prop_id, |
281 gst_type_find_element_set_property (GObject * object, guint prop_id, |
303 const GValue * value, GParamSpec * pspec) |
282 const GValue * value, GParamSpec * pspec) |
304 { |
283 { |
305 GstTypeFindElement *typefind; |
284 GstTypeFindElement *typefind; |
306 |
285 |
307 typefind = GST_TYPE_FIND_ELEMENT (object); |
286 typefind = GST_TYPE_FIND_ELEMENT (object); |
308 |
287 |
309 switch (prop_id) { |
288 switch (prop_id) { |
310 case PROP_MINIMUM: |
289 case ARG_MINIMUM: |
311 typefind->min_probability = g_value_get_uint (value); |
290 typefind->min_probability = g_value_get_uint (value); |
312 break; |
291 break; |
313 case PROP_MAXIMUM: |
292 case ARG_MAXIMUM: |
314 typefind->max_probability = g_value_get_uint (value); |
293 typefind->max_probability = g_value_get_uint (value); |
315 break; |
|
316 case PROP_FORCE_CAPS: |
|
317 GST_OBJECT_LOCK (typefind); |
|
318 if (typefind->force_caps) |
|
319 gst_caps_unref (typefind->force_caps); |
|
320 typefind->force_caps = g_value_dup_boxed (value); |
|
321 GST_OBJECT_UNLOCK (typefind); |
|
322 break; |
294 break; |
323 default: |
295 default: |
324 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
296 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
325 break; |
297 break; |
326 } |
298 } |
327 } |
299 } |
328 |
|
329 static void |
300 static void |
330 gst_type_find_element_get_property (GObject * object, guint prop_id, |
301 gst_type_find_element_get_property (GObject * object, guint prop_id, |
331 GValue * value, GParamSpec * pspec) |
302 GValue * value, GParamSpec * pspec) |
332 { |
303 { |
333 GstTypeFindElement *typefind; |
304 GstTypeFindElement *typefind; |
334 |
305 |
335 typefind = GST_TYPE_FIND_ELEMENT (object); |
306 typefind = GST_TYPE_FIND_ELEMENT (object); |
336 |
307 |
337 switch (prop_id) { |
308 switch (prop_id) { |
338 case PROP_CAPS: |
309 case ARG_CAPS: |
339 g_value_set_boxed (value, typefind->caps); |
310 g_value_set_boxed (value, typefind->caps); |
340 break; |
311 break; |
341 case PROP_MINIMUM: |
312 case ARG_MINIMUM: |
342 g_value_set_uint (value, typefind->min_probability); |
313 g_value_set_uint (value, typefind->min_probability); |
343 break; |
314 break; |
344 case PROP_MAXIMUM: |
315 case ARG_MAXIMUM: |
345 g_value_set_uint (value, typefind->max_probability); |
316 g_value_set_uint (value, typefind->max_probability); |
346 break; |
|
347 case PROP_FORCE_CAPS: |
|
348 GST_OBJECT_LOCK (typefind); |
|
349 g_value_set_boxed (value, typefind->force_caps); |
|
350 GST_OBJECT_UNLOCK (typefind); |
|
351 break; |
317 break; |
352 default: |
318 default: |
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
319 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
354 break; |
320 break; |
355 } |
321 } |
530 (_("Stream contains no data.")), |
496 (_("Stream contains no data.")), |
531 ("Can't typefind empty stream")); |
497 ("Can't typefind empty stream")); |
532 } |
498 } |
533 |
499 |
534 stop_typefinding (typefind); |
500 stop_typefinding (typefind); |
535 res = gst_pad_push_event (typefind->src, event); |
501 res = gst_pad_event_default (pad, event); |
536 break; |
502 break; |
537 } |
503 } |
538 case GST_EVENT_FLUSH_STOP: |
|
539 g_list_foreach (typefind->cached_events, |
|
540 (GFunc) gst_mini_object_unref, NULL); |
|
541 g_list_free (typefind->cached_events); |
|
542 typefind->cached_events = NULL; |
|
543 gst_buffer_replace (&typefind->store, NULL); |
|
544 /* fall through */ |
|
545 case GST_EVENT_FLUSH_START: |
|
546 res = gst_pad_push_event (typefind->src, event); |
|
547 break; |
|
548 default: |
504 default: |
549 GST_DEBUG_OBJECT (typefind, "Saving %s event to send later", |
505 GST_DEBUG_OBJECT (typefind, "Saving %s event to send later", |
550 GST_EVENT_TYPE_NAME (event)); |
506 GST_EVENT_TYPE_NAME (event)); |
551 typefind->cached_events = |
507 typefind->cached_events = |
552 g_list_append (typefind->cached_events, event); |
508 g_list_append (typefind->cached_events, event); |
553 res = TRUE; |
509 res = TRUE; |
554 break; |
510 break; |
555 } |
511 } |
556 break; |
512 break; |
557 case MODE_NORMAL: |
513 case MODE_NORMAL: |
558 res = gst_pad_push_event (typefind->src, event); |
514 res = gst_pad_event_default (pad, event); |
559 break; |
515 break; |
560 case MODE_ERROR: |
516 case MODE_ERROR: |
561 break; |
517 break; |
562 default: |
518 default: |
563 g_assert_not_reached (); |
519 g_assert_not_reached (); |
610 typefind->store = NULL; |
566 typefind->store = NULL; |
611 } |
567 } |
612 } |
568 } |
613 |
569 |
614 return TRUE; |
570 return TRUE; |
615 } |
|
616 |
|
617 static GstCaps * |
|
618 gst_type_find_guess_by_extension (GstTypeFindElement * typefind, GstPad * pad, |
|
619 GstTypeFindProbability * probability) |
|
620 { |
|
621 GstQuery *query; |
|
622 gchar *uri; |
|
623 size_t len; |
|
624 gint find; |
|
625 GstCaps *caps; |
|
626 |
|
627 query = gst_query_new_uri (); |
|
628 |
|
629 /* try getting the caps with an uri query and from the extension */ |
|
630 if (!gst_pad_peer_query (pad, query)) |
|
631 goto peer_query_failed; |
|
632 |
|
633 gst_query_parse_uri (query, &uri); |
|
634 if (uri == NULL) |
|
635 goto no_uri; |
|
636 |
|
637 GST_DEBUG_OBJECT (typefind, "finding extension of %s", uri); |
|
638 |
|
639 /* find the extension on the uri, this is everything after a '.' */ |
|
640 len = strlen (uri); |
|
641 find = len - 1; |
|
642 |
|
643 while (find >= 0) { |
|
644 if (uri[find] == '.') |
|
645 break; |
|
646 find--; |
|
647 } |
|
648 if (find < 0) |
|
649 goto no_extension; |
|
650 |
|
651 GST_DEBUG_OBJECT (typefind, "found extension %s", &uri[find + 1]); |
|
652 |
|
653 caps = |
|
654 gst_type_find_helper_for_extension (GST_OBJECT_CAST (typefind), |
|
655 &uri[find + 1]); |
|
656 if (caps) |
|
657 *probability = GST_TYPE_FIND_MAXIMUM; |
|
658 |
|
659 gst_query_unref (query); |
|
660 |
|
661 return caps; |
|
662 |
|
663 /* ERRORS */ |
|
664 peer_query_failed: |
|
665 { |
|
666 GST_WARNING_OBJECT (typefind, "failed to query peer uri"); |
|
667 gst_query_unref (query); |
|
668 return NULL; |
|
669 } |
|
670 no_uri: |
|
671 { |
|
672 GST_WARNING_OBJECT (typefind, "could not parse the peer uri"); |
|
673 gst_query_unref (query); |
|
674 return NULL; |
|
675 } |
|
676 no_extension: |
|
677 { |
|
678 GST_WARNING_OBJECT (typefind, "could not find uri extension in %s", uri); |
|
679 gst_query_unref (query); |
|
680 return NULL; |
|
681 } |
|
682 } |
571 } |
683 |
572 |
684 static GstFlowReturn |
573 static GstFlowReturn |
685 gst_type_find_element_chain (GstPad * pad, GstBuffer * buffer) |
574 gst_type_find_element_chain (GstPad * pad, GstBuffer * buffer) |
686 { |
575 { |
814 GstCaps *found_caps = NULL; |
703 GstCaps *found_caps = NULL; |
815 GstTypeFindElement *typefind; |
704 GstTypeFindElement *typefind; |
816 |
705 |
817 typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad)); |
706 typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad)); |
818 |
707 |
819 /* if we have force caps, use those */ |
|
820 if (typefind->force_caps) { |
|
821 found_caps = gst_caps_ref (typefind->force_caps); |
|
822 probability = GST_TYPE_FIND_MAXIMUM; |
|
823 goto done; |
|
824 } |
|
825 |
|
826 /* 1. try to activate in pull mode. if not, switch to push and succeed. |
708 /* 1. try to activate in pull mode. if not, switch to push and succeed. |
827 2. try to pull type find. |
709 2. try to pull type find. |
828 3. deactivate pull mode. |
710 3. deactivate pull mode. |
829 4. src pad might have been activated push by the state change. deactivate. |
711 4. src pad might have been activated push by the state change. deactivate. |
830 5. if we didn't find any caps, try getting the uri extension by doing an uri |
712 5. if we didn't find any caps, fail. |
831 query. |
713 6. emit have-type; maybe the app connected the source pad to something. |
832 6. if we didn't find any caps, fail. |
714 7. if the sink pad is activated, we are in pull mode. succeed. |
833 7. emit have-type; maybe the app connected the source pad to something. |
|
834 8. if the sink pad is activated, we are in pull mode. succeed. |
|
835 otherwise activate both pads in push mode and succeed. |
715 otherwise activate both pads in push mode and succeed. |
836 */ |
716 */ |
837 |
717 |
838 /* 1 */ |
718 /* 1 */ |
839 if (!gst_pad_check_pull_range (pad) || !gst_pad_activate_pull (pad, TRUE)) { |
719 if (!gst_pad_check_pull_range (pad) || !gst_pad_activate_pull (pad, TRUE)) { |
850 gint64 size; |
730 gint64 size; |
851 GstFormat format = GST_FORMAT_BYTES; |
731 GstFormat format = GST_FORMAT_BYTES; |
852 |
732 |
853 if (!gst_pad_query_duration (peer, &format, &size)) { |
733 if (!gst_pad_query_duration (peer, &format, &size)) { |
854 GST_WARNING_OBJECT (typefind, "Could not query upstream length!"); |
734 GST_WARNING_OBJECT (typefind, "Could not query upstream length!"); |
855 gst_object_unref (peer); |
|
856 return FALSE; |
735 return FALSE; |
857 } |
736 } |
858 |
737 |
859 /* the size if 0, we cannot continue */ |
738 if (size > 0) { |
860 if (size == 0) { |
739 found_caps = gst_type_find_helper_get_range (GST_OBJECT_CAST (peer), |
|
740 (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)), |
|
741 (guint64) size, &probability); |
|
742 } else { |
861 /* keep message in sync with message in sink event handler */ |
743 /* keep message in sync with message in sink event handler */ |
862 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, |
744 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, |
863 (_("Stream contains no data.")), ("Can't typefind empty stream")); |
745 (_("Stream contains no data.")), ("Can't typefind empty stream")); |
864 gst_object_unref (peer); |
|
865 return FALSE; |
|
866 } |
746 } |
867 |
|
868 found_caps = gst_type_find_helper_get_range (GST_OBJECT_CAST (peer), |
|
869 (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)), |
|
870 (guint64) size, &probability); |
|
871 |
747 |
872 gst_object_unref (peer); |
748 gst_object_unref (peer); |
873 } |
749 } |
874 } |
750 } |
875 |
751 |
878 |
754 |
879 /* 4 */ |
755 /* 4 */ |
880 gst_pad_activate_push (typefind->src, FALSE); |
756 gst_pad_activate_push (typefind->src, FALSE); |
881 |
757 |
882 /* 5 */ |
758 /* 5 */ |
883 if (!found_caps || probability < typefind->min_probability) { |
|
884 found_caps = gst_type_find_guess_by_extension (typefind, pad, &probability); |
|
885 } |
|
886 |
|
887 /* 6 */ |
|
888 if (!found_caps || probability < typefind->min_probability) { |
759 if (!found_caps || probability < typefind->min_probability) { |
889 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL)); |
760 GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL)); |
890 gst_caps_replace (&found_caps, NULL); |
761 gst_caps_replace (&found_caps, NULL); |
891 return FALSE; |
762 return FALSE; |
892 } |
763 } |
893 |
764 |
894 done: |
765 /* 6 */ |
895 /* 7 */ |
|
896 g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], |
766 g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], |
897 0, probability, found_caps); |
767 0, probability, found_caps); |
898 gst_caps_unref (found_caps); |
768 gst_caps_unref (found_caps); |
899 typefind->mode = MODE_NORMAL; |
769 typefind->mode = MODE_NORMAL; |
900 |
770 |
901 /* 8 */ |
771 /* 7 */ |
902 if (gst_pad_is_active (pad)) |
772 if (gst_pad_is_active (pad)) |
903 return TRUE; |
773 return TRUE; |
904 else { |
774 else { |
905 gboolean ret; |
775 gboolean ret; |
906 |
776 |
924 |
794 |
925 switch (transition) { |
795 switch (transition) { |
926 case GST_STATE_CHANGE_PAUSED_TO_READY: |
796 case GST_STATE_CHANGE_PAUSED_TO_READY: |
927 case GST_STATE_CHANGE_READY_TO_NULL: |
797 case GST_STATE_CHANGE_READY_TO_NULL: |
928 gst_caps_replace (&typefind->caps, NULL); |
798 gst_caps_replace (&typefind->caps, NULL); |
929 |
|
930 g_list_foreach (typefind->cached_events, |
799 g_list_foreach (typefind->cached_events, |
931 (GFunc) gst_mini_object_unref, NULL); |
800 (GFunc) gst_mini_object_unref, NULL); |
932 g_list_free (typefind->cached_events); |
801 g_list_free (typefind->cached_events); |
933 typefind->cached_events = NULL; |
802 typefind->cached_events = NULL; |
934 break; |
803 break; |