/* GStreamer * Copyright (C) 2010 Stefan Kost * * capsnego.c: benchmark for caps negotiation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* This benchmark recursively builds a pipeline and measures the time to go * from READY to PAUSED state. * * The graph size and type can be controlled with a few command line options: * * -d depth: is the depth of the tree * -c children: is the number of branches on each level * -f : can be "audio" or "video" and is controlling the kind of * elements that are used. */ #include #include #include enum { FLAVOUR_AUDIO = 0, FLAVOUR_VIDEO, NUM_FLAVOURS }; enum { ELEM_SRC = 0, ELEM_MIX, ELEM_PROC, ELEM_CONV, NUM_ELEM }; static const gchar *factories[NUM_FLAVOURS][NUM_ELEM] = { {"audiotestsrc", "adder", "volume", "audioconvert"}, {"videotestsrc", "videomixer", "videoscale", "videoconvert"} }; static const gchar *sink_pads[NUM_FLAVOURS][NUM_ELEM] = { {NULL, "sink_%u", NULL, NULL}, {NULL, "sink_%u", NULL, NULL} }; static gboolean create_node (GstBin * bin, GstElement * sink, const gchar * sinkpadname, GstElement ** new_sink, gint children, gint flavour) { GstElement *mix, *proc, *conv; if (children >= 1) { mix = gst_element_factory_make (factories[flavour][ELEM_MIX], NULL); if (!mix) { GST_WARNING ("need element '%s'", factories[flavour][ELEM_MIX]); return FALSE; } } else { mix = gst_element_factory_make ("identity", NULL); } proc = gst_element_factory_make (factories[flavour][ELEM_PROC], NULL); if (!proc) { GST_WARNING ("need element '%s'", factories[flavour][ELEM_PROC]); return FALSE; } conv = gst_element_factory_make (factories[flavour][ELEM_CONV], NULL); if (!conv) { GST_WARNING ("need element '%s'", factories[flavour][ELEM_CONV]); return FALSE; } gst_bin_add_many (bin, mix, proc, conv, NULL); if (!gst_element_link_pads_full (mix, "src", proc, "sink", GST_PAD_LINK_CHECK_NOTHING) || !gst_element_link_pads_full (proc, "src", conv, "sink", GST_PAD_LINK_CHECK_NOTHING) || !gst_element_link_pads_full (conv, "src", sink, sinkpadname, GST_PAD_LINK_CHECK_NOTHING)) { GST_WARNING ("can't link elements"); return FALSE; } *new_sink = mix; return TRUE; } static gboolean create_nodes (GstBin * bin, GstElement * sink, gint depth, gint children, gint flavour) { GstElement *new_sink, *src; gint i; for (i = 0; i < children; i++) { if (depth > 0) { if (!create_node (bin, sink, sink_pads[flavour][ELEM_MIX], &new_sink, children, flavour)) { return FALSE; } if (!create_nodes (bin, new_sink, depth - 1, children, flavour)) { return FALSE; } } else { src = gst_element_factory_make (factories[flavour][ELEM_SRC], NULL); if (!src) { GST_WARNING ("need element '%s'", factories[flavour][ELEM_SRC]); return FALSE; } gst_bin_add (bin, src); if (!gst_element_link_pads_full (src, "src", sink, sink_pads[flavour][ELEM_MIX], GST_PAD_LINK_CHECK_NOTHING)) { GST_WARNING ("can't link elements"); return FALSE; } } } return TRUE; } static void event_loop (GstElement * bin) { GstBus *bus; GstMessage *msg = NULL; gboolean running = TRUE; bus = gst_element_get_bus (bin); while (running) { msg = gst_bus_poll (bus, GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR | GST_MESSAGE_WARNING, -1); switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ASYNC_DONE: running = FALSE; break; case GST_MESSAGE_WARNING:{ GError *err = NULL; gchar *dbg = NULL; gst_message_parse_warning (msg, &err, &dbg); GST_WARNING_OBJECT (GST_MESSAGE_SRC (msg), "%s (%s)", err->message, (dbg ? dbg : "no details")); g_clear_error (&err); g_free (dbg); break; } case GST_MESSAGE_ERROR:{ GError *err = NULL; gchar *dbg = NULL; gst_message_parse_error (msg, &err, &dbg); GST_ERROR_OBJECT (GST_MESSAGE_SRC (msg), "%s (%s)", err->message, (dbg ? dbg : "no details")); g_clear_error (&err); g_free (dbg); running = FALSE; break; } default: break; } gst_message_unref (msg); } gst_object_unref (bus); } gint main (gint argc, gchar * argv[]) { /* default parameters */ const gchar *flavour_str = "audio"; gint flavour = FLAVOUR_AUDIO; gint children = 3; gint depth = 4; gint loops = 50; GOptionContext *ctx; GOptionEntry options[] = { {"children", 'c', 0, G_OPTION_ARG_INT, &children, "Number of children (branches on each level) (default: 3)", NULL}, {"depth", 'd', 0, G_OPTION_ARG_INT, &depth, "Depth of pipeline hierarchy tree (default: 4)", NULL}, {"flavour", 'f', 0, G_OPTION_ARG_STRING, &flavour_str, "Flavour (video|audio) controlling the kind of elements used " "(default: audio)", NULL}, {"loops", 'l', 0, G_OPTION_ARG_INT, &loops, "How many loops to run (default: 50)", NULL}, {NULL} }; GError *err = NULL; GstBin *bin; GstClockTime start, end; GstElement *sink, *new_sink; gint i; g_set_prgname ("capsnego"); /* check command line options */ ctx = g_option_context_new (""); g_option_context_add_main_entries (ctx, options, NULL); g_option_context_add_group (ctx, gst_init_get_option_group ()); if (!g_option_context_parse (ctx, &argc, &argv, &err)) { g_print ("Error initializing: %s\n", GST_STR_NULL (err->message)); g_clear_error (&err); g_option_context_free (ctx); return 1; } g_option_context_free (ctx); if (strcmp (flavour_str, "video") == 0) flavour = FLAVOUR_VIDEO; /* build pipeline */ g_print ("building %s pipeline with depth = %d and children = %d\n", flavour_str, depth, children); start = gst_util_get_timestamp (); bin = GST_BIN (gst_pipeline_new ("pipeline")); sink = gst_element_factory_make ("fakesink", NULL); gst_bin_add (bin, sink); if (!create_node (bin, sink, "sink", &new_sink, children, flavour)) { goto Error; } if (!create_nodes (bin, new_sink, depth, children, flavour)) { goto Error; } end = gst_util_get_timestamp (); /* num-threads = num-sources = pow (children, depth) */ g_print ("%" GST_TIME_FORMAT " built pipeline with %d elements\n", GST_TIME_ARGS (end - start), GST_BIN_NUMCHILDREN (bin)); /* measure */ g_print ("starting pipeline\n"); gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY); GST_DEBUG_BIN_TO_DOT_FILE (bin, GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE, "capsnego"); start = gst_util_get_timestamp (); for (i = 0; i < loops; ++i) { gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); event_loop (GST_ELEMENT (bin)); gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY); } end = gst_util_get_timestamp (); g_print ("%" GST_TIME_FORMAT " reached PAUSED state (%d loop iterations)\n", GST_TIME_ARGS (end - start), loops); /* clean up */ Error: gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL); gst_object_unref (bin); return 0; }