| /* GStreamer |
| * Copyright (C) 2010 Stefan Kost <ensonic@users.sf.net> |
| * |
| * 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 <flavour>: can be "audio" or "video" and is controlling the kind of |
| * elements that are used. |
| */ |
| |
| #include <gst/gst.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| 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_error_free (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_error_free (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)); |
| 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; |
| } |