diff --git a/common b/common index 1de7f6ab2d..6aec6b9716 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1de7f6ab2d4bc1af69f06079cf0f4e2cbbfdc178 +Subproject commit 6aec6b9716c184c60c4bc6a5916a2471cfa8c8cd diff --git a/configure.ac b/configure.ac index 9b89b4c751..8bd2cbd90b 100644 --- a/configure.ac +++ b/configure.ac @@ -723,7 +723,7 @@ AC_SUBST(GST_OBJ_LIBS) dnl GST_PLUGIN_LDFLAGS dnl LDFLAGS for plugins; includes GST_ALL_LDFLAGS -GST_PLUGIN_LDFLAGS="-module -avoid-version -export-symbols-regex '^[_]*gst_plugin_desc\$\$' $GST_ALL_LDFLAGS" +GST_PLUGIN_LDFLAGS="-module -avoid-version -export-symbols-regex '^[_]*gst_plugin_desc.*' $GST_ALL_LDFLAGS" AC_SUBST(GST_PLUGIN_LDFLAGS, "$GST_PLUGIN_LDFLAGS") dnl plugin scanner locations diff --git a/docs/faq/git.xml b/docs/faq/git.xml index 6cad2b11e1..c225e7b24d 100644 --- a/docs/faq/git.xml +++ b/docs/faq/git.xml @@ -76,8 +76,8 @@ you will then have to provide them with: your e-mail address a copy of your public sshv2 identity. If you do not have this yet, you can generate it by running -"ssh-keygen -t dsa". The resulting public key -will be in .ssh/id_dsa.pub +"ssh-keygen -t rsa -f ~/.ssh/id_rsa.pub-fdo". The resulting public key +will be in ~/.ssh/id_rsa.pub-fdo your GPG fingerprint. This would allow you to add and remove ssh keys to your account. diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index 74e8be07b2..5fc1632d6a 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -2551,6 +2551,7 @@ gst_uri_has_protocol gst_uri_get_protocol gst_uri_get_location gst_uri_construct +gst_filename_to_uri gst_element_make_from_uri gst_uri_handler_get_uri_type gst_uri_handler_get_protocols diff --git a/docs/gst/running.xml b/docs/gst/running.xml index bedca13c7a..3800437ec1 100644 --- a/docs/gst/running.xml +++ b/docs/gst/running.xml @@ -243,8 +243,9 @@ plugins frequently, it will save time when doing gst_init(). Useful Orc environment variable. Set ORC_CODE=debug to enable debuggers such as gdb to create useful backtraces from Orc-generated code. Set ORC_CODE=backup or ORC_CODE=emulate if you suspect Orc's SIMD code -generator is producing incorrect code. (Quite a few important +generator is producing incorrect code (Quite a few important GStreamer plugins like videotestsrc, audioconvert or audioresample use Orc). +One can also combine flags like ORC_CODE=backup,debug. diff --git a/gst/gsturi.c b/gst/gsturi.c index 247604c72f..0f5bbbb4fa 100644 --- a/gst/gsturi.c +++ b/gst/gsturi.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * Copyright (C) 2011 Tim-Philipp Müller * * gsturi.c: register URI handlers * @@ -782,3 +783,115 @@ gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri) g_signal_emit (handler, gst_uri_handler_signals[NEW_URI], 0, uri); } + +static gchar * +gst_file_utils_canonicalise_path (const gchar * path) +{ + gchar **parts, **p, *clean_path; + +#ifdef G_OS_WIN32 + { + GST_WARNING ("FIXME: canonicalise win32 path"); + return g_strdup (path); + } +#endif + + parts = g_strsplit (path, "/", -1); + + p = parts; + while (*p != NULL) { + if (strcmp (*p, ".") == 0) { + /* just move all following parts on top of this, incl. NUL terminator */ + g_free (*p); + g_memmove (p, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *)); + /* re-check the new current part again in the next iteration */ + continue; + } else if (strcmp (*p, "..") == 0 && p > parts) { + /* just move all following parts on top of the previous part, incl. + * NUL terminator */ + g_free (*(p - 1)); + g_free (*p); + g_memmove (p - 1, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *)); + /* re-check the new current part again in the next iteration */ + --p; + continue; + } + ++p; + } + if (*path == '/') { + guint num_parts; + + num_parts = g_strv_length (parts) + 1; /* incl. terminator */ + parts = g_renew (gchar *, parts, num_parts + 1); + g_memmove (parts + 1, parts, num_parts * sizeof (gchar *)); + parts[0] = g_strdup ("/"); + } + + clean_path = g_build_filenamev (parts); + g_strfreev (parts); + return clean_path; +} + +static gboolean +file_path_contains_relatives (const gchar * path) +{ + return (strstr (path, "/./") != NULL || strstr (path, "/../") != NULL || + strstr (path, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) != NULL || + strstr (path, G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) != NULL); +} + +/** + * gst_filename_to_uri: + * @filename: absolute or relative file name path + * @error: pointer to error, or NULL + * + * Similar to g_filename_to_uri(), but attempts to handle relative file paths + * as well. Before converting @filename into an URI, it will be prefixed by + * the current working directory if it is a relative path, and then the path + * will be canonicalised so that it doesn't contain any './' or '../' segments. + * + * On Windows #filename should be in UTF-8 encoding. + * + * Since: 0.10.33 + */ +gchar * +gst_filename_to_uri (const gchar * filename, GError ** error) +{ + gchar *abs_location = NULL; + gchar *uri, *abs_clean; + + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (g_path_is_absolute (filename)) { + if (!file_path_contains_relatives (filename)) { + uri = g_filename_to_uri (filename, NULL, error); + goto beach; + } + + abs_location = g_strdup (filename); + } else { + gchar *cwd; + + cwd = g_get_current_dir (); + abs_location = g_build_filename (cwd, filename, NULL); + g_free (cwd); + + if (!file_path_contains_relatives (abs_location)) { + uri = g_filename_to_uri (abs_location, NULL, error); + goto beach; + } + } + + /* path is now absolute, but contains '.' or '..' */ + abs_clean = gst_file_utils_canonicalise_path (abs_location); + GST_LOG ("'%s' -> '%s' -> '%s'", filename, abs_location, abs_clean); + uri = g_filename_to_uri (abs_clean, NULL, error); + g_free (abs_clean); + +beach: + + g_free (abs_location); + GST_DEBUG ("'%s' -> '%s'", filename, uri); + return uri; +} diff --git a/gst/gsturi.h b/gst/gsturi.h index 6193cf3014..48a09c059d 100644 --- a/gst/gsturi.h +++ b/gst/gsturi.h @@ -133,6 +133,9 @@ gchar * gst_uri_get_location (const gchar * uri); gchar * gst_uri_construct (const gchar * protocol, const gchar * location); +gchar * gst_filename_to_uri (const gchar * filename, + GError ** error); + GstElement * gst_element_make_from_uri (const GstURIType type, const gchar * uri, const gchar * elementname); diff --git a/gst/gstutils.c b/gst/gstutils.c index 9a71e6ea2b..86c4394c0f 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -1554,6 +1554,8 @@ pad_link_maybe_ghosting (GstPad * src, GstPad * sink, GstPadLinkCheck flags) * is the same as calling gst_element_link_pads() and the recommended way of * linking pads with safety checks applied. * + * This is a convenience function for gst_pad_link_full(). + * * Returns: TRUE if the pads could be linked, FALSE otherwise. * * Since: 0.10.30 @@ -2012,6 +2014,8 @@ gst_element_link_filtered (GstElement * src, GstElement * dest, * @destpadname: the name of the #GstPad in destination element. * * Unlinks the two named pads of the source and destination elements. + * + * This is a convenience function for gst_pad_unlink(). */ void gst_element_unlink_pads (GstElement * src, const gchar * srcpadname, @@ -2162,7 +2166,8 @@ gst_element_unlink (GstElement * src, GstElement * dest) * @cur: (out) (allow-none): a location in which to store the current * position, or NULL. * - * Queries an element for the stream position. + * Queries an element for the stream position. If one repeatedly calls this + * function one can also create and reuse it in gst_element_query(). * * Returns: TRUE if the query could be performed. */ diff --git a/libs/gst/controller/gsthelper.c b/libs/gst/controller/gsthelper.c index 70c04c07d8..6c958f8940 100644 --- a/libs/gst/controller/gsthelper.c +++ b/libs/gst/controller/gsthelper.c @@ -22,7 +22,7 @@ /** * SECTION:gstcontrollergobject - * @short_description: #GObject convinience methods for using dynamic properties + * @short_description: #GObject convenience methods for using dynamic properties * @see_also: #GstController * * These methods allow to use some #GstController functionallity directly from diff --git a/plugins/elements/gstfilesink.c b/plugins/elements/gstfilesink.c index 2431c14338..d5fa283298 100644 --- a/plugins/elements/gstfilesink.c +++ b/plugins/elements/gstfilesink.c @@ -302,7 +302,9 @@ gst_file_sink_set_location (GstFileSink * sink, const gchar * location) /* we store the filename as we received it from the application. On Windows * this should be in UTF8 */ sink->filename = g_strdup (location); - sink->uri = g_filename_to_uri (sink->filename, NULL, NULL); + sink->uri = gst_filename_to_uri (location, NULL); + GST_INFO ("filename : %s", sink->filename); + GST_INFO ("uri : %s", sink->uri); } else { sink->filename = NULL; sink->uri = NULL; diff --git a/plugins/elements/gstfilesrc.c b/plugins/elements/gstfilesrc.c index 9397d8d53a..e3466093af 100644 --- a/plugins/elements/gstfilesrc.c +++ b/plugins/elements/gstfilesrc.c @@ -367,10 +367,12 @@ gst_file_src_set_location (GstFileSrc * src, const gchar * location) src->filename = NULL; src->uri = NULL; } else { - /* we store the filename as received by the application. On Windoes this + /* we store the filename as received by the application. On Windows this * should be UTF8 */ src->filename = g_strdup (location); - src->uri = g_filename_to_uri (src->filename, NULL, NULL); + src->uri = gst_filename_to_uri (location, NULL); + GST_INFO ("filename : %s", src->filename); + GST_INFO ("uri : %s", src->uri); } g_object_notify (G_OBJECT (src), "location"); gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri); diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 811fbe7547..38c7a14305 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -146,12 +146,14 @@ TESTS = $(check_PROGRAMS) noinst_HEADERS = \ gst/capslist.h \ + gst/struct_arm.h \ gst/struct_i386.h \ gst/struct_hppa.h \ gst/struct_ppc32.h \ gst/struct_ppc64.h \ gst/struct_sparc.h \ gst/struct_x86_64.h \ + libs/struct_arm.h \ libs/struct_i386.h \ libs/struct_hppa.h \ libs/struct_ppc32.h \ diff --git a/tests/check/elements/filesrc.c b/tests/check/elements/filesrc.c index 71a09f0f0f..970ca9ad0b 100644 --- a/tests/check/elements/filesrc.c +++ b/tests/check/elements/filesrc.c @@ -384,6 +384,67 @@ GST_START_TEST (test_uri_interface) GST_END_TEST; +static void +check_uri_for_location (GstElement * e, const gchar * location, + const gchar * uri) +{ + GstQuery *query; + gchar *query_uri = NULL; + + g_object_set (e, "location", location, NULL); + query = gst_query_new_uri (); + fail_unless (gst_element_query (e, query)); + gst_query_parse_uri (query, &query_uri); + gst_query_unref (query); + + if (uri != NULL) { + fail_unless_equals_string (query_uri, uri); + } else { + gchar *fn; + + fail_unless (gst_uri_is_valid (query_uri)); + fn = g_filename_from_uri (query_uri, NULL, NULL); + fail_unless (g_path_is_absolute (fn)); + fail_unless (fn != NULL); + g_free (fn); + } + + g_free (query_uri); +} + +GST_START_TEST (test_uri_query) +{ + GstElement *src; + + src = setup_filesrc (); + +#ifdef G_OS_UNIX + { + GST_INFO ("*nix"); + check_uri_for_location (src, "/i/do/not/exist", "file:///i/do/not/exist"); + check_uri_for_location (src, "/i/do/not/../exist", "file:///i/do/exist"); + check_uri_for_location (src, "/i/do/not/.././exist", "file:///i/do/exist"); + check_uri_for_location (src, "/i/./do/not/../exist", "file:///i/do/exist"); + check_uri_for_location (src, "/i/do/./not/../exist", "file:///i/do/exist"); + check_uri_for_location (src, "/i/do/not/./../exist", "file:///i/do/exist"); + check_uri_for_location (src, "/i/./do/./././././exist", + "file:///i/do/exist"); + check_uri_for_location (src, "/i/do/not/../../exist", "file:///i/exist"); + check_uri_for_location (src, "/i/../not/../exist", "file:///exist"); + /* hard to test relative URIs, just make sure it returns an URI of sorts */ + check_uri_for_location (src, "foo", NULL); + check_uri_for_location (src, "foo/../bar", NULL); + check_uri_for_location (src, "./foo", NULL); + check_uri_for_location (src, "../foo", NULL); + check_uri_for_location (src, "foo/./bar", NULL); + } +#endif + + cleanup_filesrc (src); +} + +GST_END_TEST; + static Suite * filesrc_suite (void) { @@ -396,6 +457,7 @@ filesrc_suite (void) tcase_add_test (tc_chain, test_pull); tcase_add_test (tc_chain, test_coverage); tcase_add_test (tc_chain, test_uri_interface); + tcase_add_test (tc_chain, test_uri_query); return s; } diff --git a/tests/check/gst/gstabi.c b/tests/check/gst/gstabi.c index ae9482cfde..fffe062253 100644 --- a/tests/check/gst/gstabi.c +++ b/tests/check/gst/gstabi.c @@ -46,6 +46,10 @@ #include "struct_sparc.h" #define HAVE_ABI_SIZES TRUE #else +#ifdef HAVE_CPU_ARM +#include "struct_arm.h" +#define HAVE_ABI_SIZES TRUE +#else /* in case someone wants to generate a new arch */ #include "struct_i386.h" #define HAVE_ABI_SIZES FALSE @@ -55,6 +59,7 @@ #endif #endif #endif +#endif GST_START_TEST (test_ABI) { diff --git a/tests/check/gst/struct_arm.h b/tests/check/gst/struct_arm.h new file mode 100644 index 0000000000..533674f394 --- /dev/null +++ b/tests/check/gst/struct_arm.h @@ -0,0 +1,73 @@ + +GstCheckABIStruct list[] = { + {"GstBin", sizeof (GstBin), 192}, + {"GstBinClass", sizeof (GstBinClass), 288}, + {"GstBuffer", sizeof (GstBuffer), 88}, + {"GstBufferClass", sizeof (GstBufferClass), 16}, + {"GstBus", sizeof (GstBus), 80}, + {"GstBusClass", sizeof (GstBusClass), 144}, + {"GstCaps", sizeof (GstCaps), 32}, + {"GstStaticCaps", sizeof (GstStaticCaps), 52}, + {"GstChildProxyInterface", sizeof (GstChildProxyInterface), 40}, + {"GstClock", sizeof (GstClock), 176}, + {"GstClockClass", sizeof (GstClockClass), 160}, + {"GstElement", sizeof (GstElement), 136}, + {"GstElementClass", sizeof (GstElementClass), 248}, + {"GstElementFactory", sizeof (GstElementFactory), 144}, + {"GstElementFactoryClass", sizeof (GstElementFactoryClass), 152}, + {"GstElementDetails", sizeof (GstElementDetails), 32}, + {"GstEvent", sizeof (GstEvent), 48}, + {"GstEventClass", sizeof (GstEventClass), 32}, + {"GstFormatDefinition", sizeof (GstFormatDefinition), 16}, + {"GstIndexEntry", sizeof (GstIndexEntry), 20}, + {"GstIndexGroup", sizeof (GstIndexGroup), 16}, + {"GstIndex", sizeof (GstIndex), 100}, + {"GstIndexClass", sizeof (GstIndexClass), 156}, + {"GstIndexAssociation", sizeof (GstIndexAssociation), 16}, + {"GstIndexFactory", sizeof (GstIndexFactory), 96}, + {"GstIndexFactoryClass", sizeof (GstIndexFactoryClass), 152}, + {"GstDebugCategory", sizeof (GstDebugCategory), 16}, + {"GstImplementsInterfaceClass", sizeof (GstImplementsInterfaceClass), 28}, + {"GstIterator", sizeof (GstIterator), 52}, + {"GstMessage", sizeof (GstMessage), 64}, + {"GstMessageClass", sizeof (GstMessageClass), 32}, + {"GstMiniObject", sizeof (GstMiniObject), 16}, + {"GstMiniObjectClass", sizeof (GstMiniObjectClass), 16}, + {"GstObject", sizeof (GstObject), 40}, + {"GstObjectClass", sizeof (GstObjectClass), 120}, + {"GstPad", sizeof (GstPad), 188}, + {"GstPadClass", sizeof (GstPadClass), 152}, + {"GstPadTemplate", sizeof (GstPadTemplate), 72}, + {"GstPadTemplateClass", sizeof (GstPadTemplateClass), 140}, + {"GstStaticPadTemplate", sizeof (GstStaticPadTemplate), 64}, + {"GstPipeline", sizeof (GstPipeline), 232}, + {"GstPipelineClass", sizeof (GstPipelineClass), 304}, + {"GstPlugin", sizeof (GstPlugin), 152}, + {"GstPluginClass", sizeof (GstPluginClass), 136}, + {"GstPluginDesc", sizeof (GstPluginDesc), 56}, + {"GstPluginFeature", sizeof (GstPluginFeature), 72}, + {"GstPluginFeatureClass", sizeof (GstPluginFeatureClass), 136}, + {"GstQueryTypeDefinition", sizeof (GstQueryTypeDefinition), 16}, + {"GstQuery", sizeof (GstQuery), 28}, + {"GstQueryClass", sizeof (GstQueryClass), 32}, + {"GstRegistry", sizeof (GstRegistry), 72}, + {"GstRegistryClass", sizeof (GstRegistryClass), 144}, + {"GstSegment", sizeof (GstSegment), 88}, + {"GstStructure", sizeof (GstStructure), 20}, + {"GstSystemClock", sizeof (GstSystemClock), 200}, + {"GstSystemClockClass", sizeof (GstSystemClockClass), 176}, + {"GstTagSetterIFace", sizeof (GstTagSetterIFace), 8}, + {"GstTask", sizeof (GstTask), 80}, + {"GstTaskClass", sizeof (GstTaskClass), 140}, + {"GstTrace", sizeof (GstTrace), 20}, + {"GstTraceEntry", sizeof (GstTraceEntry), 128}, + {"GstAllocTrace", sizeof (GstAllocTrace), 16}, + {"GstTypeFind", sizeof (GstTypeFind), 32}, + {"GstTypeFindFactory", sizeof (GstTypeFindFactory), 108}, + {"GstTypeFindFactoryClass", sizeof (GstTypeFindFactoryClass), 152}, + {"GstURIHandlerInterface", sizeof (GstURIHandlerInterface), 44}, + {"GstValueTable", sizeof (GstValueTable), 32}, + {"GstXML", sizeof (GstXML), 64}, + {"GstXMLClass", sizeof (GstXMLClass), 144}, + {NULL, 0, 0} +}; diff --git a/tests/check/libs/libsabi.c b/tests/check/libs/libsabi.c index d4a4fdd91d..b90a82c7eb 100644 --- a/tests/check/libs/libsabi.c +++ b/tests/check/libs/libsabi.c @@ -59,6 +59,10 @@ #include "struct_sparc.h" #define HAVE_ABI_SIZES TRUE #else +#ifdef HAVE_CPU_ARM +#include "struct_arm.h" +#define HAVE_ABI_SIZES TRUE +#else /* in case someone wants to generate a new arch */ #include "struct_i386.h" #define HAVE_ABI_SIZES FALSE @@ -68,6 +72,7 @@ #endif #endif #endif +#endif GST_START_TEST (test_ABI) { diff --git a/tests/check/libs/struct_arm.h b/tests/check/libs/struct_arm.h new file mode 100644 index 0000000000..d63c71af46 --- /dev/null +++ b/tests/check/libs/struct_arm.h @@ -0,0 +1,26 @@ + +GstCheckABIStruct list[] = { + {"GstAdapter", sizeof (GstAdapter), 52}, + {"GstAdapterClass", sizeof (GstAdapterClass), 84}, + {"GstBaseSink", sizeof (GstBaseSink), 408}, + {"GstBaseSinkClass", sizeof (GstBaseSinkClass), 368}, + {"GstBaseSrc", sizeof (GstBaseSrc), 392}, + {"GstBaseSrcClass", sizeof (GstBaseSrcClass), 376}, + {"GstBaseTransform", sizeof (GstBaseTransform), 368}, + {"GstBaseTransformClass", sizeof (GstBaseTransformClass), 376}, + {"GstCollectData", sizeof (GstCollectData), 120}, + {"GstCollectPads", sizeof (GstCollectPads), 92}, + {"GstCollectPadsClass", sizeof (GstCollectPadsClass), 136}, + {"GstPushSrc", sizeof (GstPushSrc), 408}, + {"GstPushSrcClass", sizeof (GstPushSrcClass), 396}, + {"GstTimedValue", sizeof (GstTimedValue), 32}, + {"GstValueArray", sizeof (GstValueArray), 24}, + {"GstController", sizeof (GstController), 40}, + {"GstControllerClass", sizeof (GstControllerClass), 84}, + {"GstNetClientClock", sizeof (GstNetClientClock), 256}, + {"GstNetClientClockClass", sizeof (GstNetClientClockClass), 192}, + {"GstNetTimePacket", sizeof (GstNetTimePacket), 16}, + {"GstNetTimeProvider", sizeof (GstNetTimeProvider), 84}, + {"GstNetTimeProviderClass", sizeof (GstNetTimeProviderClass), 120}, + {NULL, 0, 0} +}; diff --git a/tools/gst-inspect.c b/tools/gst-inspect.c index 36dde68def..222c276fbe 100644 --- a/tools/gst-inspect.c +++ b/tools/gst-inspect.c @@ -865,13 +865,19 @@ print_pad_info (GstElement * element) } } -#if 0 -static gint -compare_signal_names (GSignalQuery * a, GSignalQuery * b) +static gboolean +has_sometimes_template (GstElement * element) { - return strcmp (a->signal_name, b->signal_name); + GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); + GList *l; + + for (l = klass->padtemplates; l != NULL; l = l->next) { + if (GST_PAD_TEMPLATE (l->data)->presence == GST_PAD_SOMETIMES) + return TRUE; + } + + return FALSE; } -#endif static void print_signal_info (GstElement * element) @@ -886,6 +892,22 @@ print_signal_info (GstElement * element) for (k = 0; k < 2; k++) { found_signals = NULL; + + /* For elements that have sometimes pads, also list a few useful GstElement + * signals. Put these first, so element-specific ones come later. */ + if (k == 0 && has_sometimes_template (element)) { + query = g_new0 (GSignalQuery, 1); + g_signal_query (g_signal_lookup ("pad-added", GST_TYPE_ELEMENT), query); + found_signals = g_slist_append (found_signals, query); + query = g_new0 (GSignalQuery, 1); + g_signal_query (g_signal_lookup ("pad-removed", GST_TYPE_ELEMENT), query); + found_signals = g_slist_append (found_signals, query); + query = g_new0 (GSignalQuery, 1); + g_signal_query (g_signal_lookup ("no-more-pads", GST_TYPE_ELEMENT), + query); + found_signals = g_slist_append (found_signals, query); + } + for (type = G_OBJECT_TYPE (element); type; type = g_type_parent (type)) { if (type == GST_TYPE_ELEMENT || type == GST_TYPE_OBJECT) break; diff --git a/tools/gst-launch.1.in b/tools/gst-launch.1.in index c130e8bf45..b352fc734b 100644 --- a/tools/gst-launch.1.in +++ b/tools/gst-launch.1.in @@ -4,7 +4,7 @@ gst\-launch \- build and run a GStreamer pipeline .SH "SYNOPSIS" \fBgst\-launch\fR \fI[OPTION...]\fR PIPELINE\-DESCRIPTION .SH "DESCRIPTION" -.LP +.LP \fIgst\-launch\fP is a tool that builds and runs basic \fIGStreamer\fP pipelines. @@ -44,6 +44,10 @@ Force an EOS event on sources before shutting the pipeline down. This is useful to make sure muxers create readable files when a muxing pipeline is shut down forcefully via Control-C. .TP 8 +.B \-i, \-\-index +Gather and print index statistics. This is mostly useful for playback or +recording pipelines. +.TP 8 .B \-o FILE, \-\-output=FILE Save XML representation of pipeline to FILE and exit (DEPRECATED, DO NOT USE) .TP 8 @@ -114,7 +118,7 @@ plugins to preload is to use the environment variable GST_PLUGIN_PATH .SH "PIPELINE DESCRIPTION" -A pipeline consists \fIelements\fR and \fIlinks\fR. \fIElements\fR can be put +A pipeline consists \fIelements\fR and \fIlinks\fR. \fIElements\fR can be put into \fIbins\fR of different sorts. \fIElements\fR, \fIlinks\fR and \fIbins\fR can be specified in a pipeline description in any order. @@ -138,7 +142,7 @@ Enumeration properties can be set by name, nick or value. \fI[BINTYPE.]\fR ( \fI[PROPERTY1 ...]\fR PIPELINE-DESCRIPTION ) .br -Specifies that a bin of type BINTYPE is created and the given properties are +Specifies that a bin of type BINTYPE is created and the given properties are set. Every element between the braces is put into the bin. Please note the dot that has to be used after the BINTYPE. You will almost never need this functionality, it is only really useful for applications using the @@ -194,11 +198,11 @@ and the type can have the following case-insensitive values: .br - \fBl\fR or \fBlist\fR for lists .br -If no type was given, the following order is tried: integer, float, boolean, +If no type was given, the following order is tried: integer, float, boolean, string. .br Integer values must be parsable by \fBstrtol()\fP, floats by \fBstrtod()\fP. FOURCC values may -either be integers or strings. Boolean values are (case insensitive) \fIyes\fR, +either be integers or strings. Boolean values are (case insensitive) \fIyes\fR, \fIno\fR, \fItrue\fR or \fIfalse\fR and may like strings be escaped with " or '. .br Ranges are in this format: [ VALUE, VALUE ] @@ -390,7 +394,7 @@ automatically. To make this even easier, you can use the playbin element: .B gst\-launch playbin uri=file:///home/joe/foo.avi .br - + .B Filtered connections @@ -438,7 +442,7 @@ Specifies a list of directories to scan for additional plugins. These take precedence over the system plugins. .TP \fBGST_PLUGIN_SYSTEM_PATH\fR -Specifies a list of plugins that are always loaded by default. If not set, +Specifies a list of plugins that are always loaded by default. If not set, this defaults to the system-installed path, and the plugins installed in the user's home directory .TP diff --git a/tools/gst-launch.c b/tools/gst-launch.c index 4b08ed2f16..eb37b43233 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -177,6 +177,159 @@ fault_setup (void) } #endif /* DISABLE_FAULT_HANDLER */ +typedef struct _GstIndexStats +{ + gint id; + gchar *desc; + + guint num_frames; + guint num_keyframes; + guint num_dltframes; + GstClockTime last_keyframe; + GstClockTime last_dltframe; + GstClockTime min_keyframe_gap; + GstClockTime max_keyframe_gap; + GstClockTime avg_keyframe_gap; +} GstIndexStats; + +static void +entry_added (GstIndex * index, GstIndexEntry * entry, gpointer user_data) +{ + GPtrArray *index_stats = (GPtrArray *) user_data; + GstIndexStats *s; + + switch (entry->type) { + case GST_INDEX_ENTRY_ID: + /* we have a new writer */ + GST_DEBUG_OBJECT (index, "id %d: describes writer %s", entry->id, + GST_INDEX_ID_DESCRIPTION (entry)); + if (entry->id >= index_stats->len) { + g_ptr_array_set_size (index_stats, entry->id + 1); + } + s = g_new (GstIndexStats, 1); + s->id = entry->id; + s->desc = g_strdup (GST_INDEX_ID_DESCRIPTION (entry)); + s->num_frames = s->num_keyframes = s->num_dltframes = 0; + s->last_keyframe = s->last_dltframe = GST_CLOCK_TIME_NONE; + s->min_keyframe_gap = s->max_keyframe_gap = s->avg_keyframe_gap = + GST_CLOCK_TIME_NONE; + g_ptr_array_index (index_stats, entry->id) = s; + break; + case GST_INDEX_ENTRY_FORMAT: + /* have not found any code calling this */ + GST_DEBUG_OBJECT (index, "id %d: registered format %d for %s\n", + entry->id, GST_INDEX_FORMAT_FORMAT (entry), + GST_INDEX_FORMAT_KEY (entry)); + break; + case GST_INDEX_ENTRY_ASSOCIATION: + { + gint64 ts; + GstAssocFlags flags = GST_INDEX_ASSOC_FLAGS (entry); + + s = g_ptr_array_index (index_stats, entry->id); + gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &ts); + + if (flags & GST_ASSOCIATION_FLAG_KEY_UNIT) { + s->num_keyframes++; + + if (GST_CLOCK_TIME_IS_VALID (ts)) { + if (GST_CLOCK_TIME_IS_VALID (s->last_keyframe)) { + GstClockTimeDiff d = GST_CLOCK_DIFF (s->last_keyframe, ts); + + if (G_UNLIKELY (d < 0)) { + GST_WARNING ("received out-of-order keyframe at %" + GST_TIME_FORMAT, GST_TIME_ARGS (ts)); + /* FIXME: does it still make sense to use that for the statistics */ + d = GST_CLOCK_DIFF (ts, s->last_keyframe); + } + + if (GST_CLOCK_TIME_IS_VALID (s->min_keyframe_gap)) { + if (d < s->min_keyframe_gap) + s->min_keyframe_gap = d; + } else { + s->min_keyframe_gap = d; + } + if (GST_CLOCK_TIME_IS_VALID (s->max_keyframe_gap)) { + if (d > s->max_keyframe_gap) + s->max_keyframe_gap = d; + } else { + s->max_keyframe_gap = d; + } + if (GST_CLOCK_TIME_IS_VALID (s->avg_keyframe_gap)) { + s->avg_keyframe_gap = (d + s->num_frames * s->avg_keyframe_gap) / + (s->num_frames + 1); + } else { + s->avg_keyframe_gap = d; + } + } + s->last_keyframe = ts; + } + } + if (flags & GST_ASSOCIATION_FLAG_DELTA_UNIT) { + s->num_dltframes++; + if (GST_CLOCK_TIME_IS_VALID (ts)) { + s->last_dltframe = ts; + } + } + s->num_frames++; + + break; + } + default: + break; + } +} + +/* print statistics from the entry_added callback, free the entries */ +static void +print_index_stats (GPtrArray * index_stats) +{ + gint i; + + if (index_stats->len) { + g_print (_("Index statistics\n")); + } + + for (i = 0; i < index_stats->len; i++) { + GstIndexStats *s = g_ptr_array_index (index_stats, i); + if (s) { + g_print ("id %d, %s\n", s->id, s->desc); + if (s->num_frames) { + GstClockTime last_frame = s->last_keyframe; + + if (GST_CLOCK_TIME_IS_VALID (s->last_dltframe)) { + if (!GST_CLOCK_TIME_IS_VALID (last_frame) || + (s->last_dltframe > last_frame)) + last_frame = s->last_dltframe; + } + + if (GST_CLOCK_TIME_IS_VALID (last_frame)) { + g_print (" total time = %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (last_frame)); + } + g_print (" frame/keyframe rate = %u / %u = ", s->num_frames, + s->num_keyframes); + if (s->num_keyframes) + g_print ("%lf\n", s->num_frames / (gdouble) s->num_keyframes); + else + g_print ("-\n"); + if (s->num_keyframes) { + g_print (" min/avg/max keyframe gap = %" GST_TIME_FORMAT ", %" + GST_TIME_FORMAT ", %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (s->min_keyframe_gap), + GST_TIME_ARGS (s->avg_keyframe_gap), + GST_TIME_ARGS (s->max_keyframe_gap)); + } + } else { + g_print (" no stats\n"); + } + + g_free (s->desc); + g_free (s); + } + } +} + static void print_error_message (GstMessage * msg) { @@ -668,6 +821,7 @@ main (int argc, char *argv[]) gboolean no_sigusr_handler = FALSE; gboolean trace = FALSE; gboolean eos_on_shutdown = FALSE; + gboolean check_index = FALSE; gchar *savefile = NULL; gchar *exclude_args = NULL; #ifndef GST_DISABLE_OPTION_PARSING @@ -690,12 +844,16 @@ main (int argc, char *argv[]) N_("Print alloc trace (if enabled at compile time)"), NULL}, {"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown, N_("Force EOS on sources before shutting the pipeline down"), NULL}, + {"index", 'i', 0, G_OPTION_ARG_NONE, &check_index, + N_("Gather and print index statistics"), NULL}, GST_TOOLS_GOPTION_VERSION, {NULL} }; GOptionContext *ctx; GError *err = NULL; #endif + GstIndex *index; + GPtrArray *index_stats = NULL; gchar **argvn; GError *error = NULL; gint res = 0; @@ -798,6 +956,21 @@ main (int argc, char *argv[]) pipeline = real_pipeline; } + if (check_index) { + /* gst_index_new() creates a null-index, it does not store anything, but + * the entry-added signal works and this is what we use to build the + * statistics */ + index = gst_index_new (); + if (index) { + index_stats = g_ptr_array_new (); + g_signal_connect (G_OBJECT (index), "entry-added", + G_CALLBACK (entry_added), index_stats); + g_object_set (G_OBJECT (index), "resolver", GST_INDEX_RESOLVER_GTYPE, + NULL); + gst_element_set_index (pipeline, index); + } + } + bus = gst_element_get_bus (pipeline); gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) pipeline); gst_object_unref (bus); @@ -890,6 +1063,11 @@ main (int argc, char *argv[]) gst_element_set_state (pipeline, GST_STATE_READY); gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE); + if (check_index) { + print_index_stats (index_stats); + g_ptr_array_free (index_stats, TRUE); + } + end: PRINT (_("Setting pipeline to NULL ...\n")); gst_element_set_state (pipeline, GST_STATE_NULL); diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index 12d9861fcb..28094d693c 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -407,6 +407,7 @@ EXPORTS gst_event_type_get_name gst_event_type_get_type gst_event_type_to_quark + gst_filename_to_uri gst_filter_run gst_flow_get_name gst_flow_return_get_type