gstreamer/tools/gst-device-monitor.c
Olivier Crête 41c1064f5f tools: gst-device-monitor: Print gst-launch example
Print a gst-launch-1.0 line that could get to this device,
useful as we don't have other ways to see what it does exactly.
This may not work if the create element has configurations other than
properties.

https://bugzilla.gnome.org/show_bug.cgi?id=781152
2017-04-11 14:27:59 -04:00

370 lines
9.8 KiB
C

/* GStreamer command line device monitor testing utility
* Copyright (C) 2014 Tim-Philipp Müller <tim@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <locale.h>
#include <gst/gst.h>
#include <gst/gst-i18n-app.h>
#include <gst/math-compat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
GST_DEBUG_CATEGORY (devmon_debug);
#define GST_CAT_DEFAULT devmon_debug
typedef struct
{
GMainLoop *loop;
GstDeviceMonitor *monitor;
guint bus_watch_id;
} DevMonApp;
static gboolean bus_msg_handler (GstBus * bus, GstMessage * msg, gpointer data);
static gchar *
get_launch_line (GstDevice * device)
{
static const char *const ignored_propnames[] =
{ "name", "parent", "direction", "template", "caps", NULL };
GString *launch_line;
GstElement *element;
GstElement *pureelement;
GParamSpec **properties, *property;
GValue value = G_VALUE_INIT;
GValue pvalue = G_VALUE_INIT;
guint i, number_of_properties;
GstElementFactory *factory;
element = gst_device_create_element (device, NULL);
if (!element)
return NULL;
factory = gst_element_get_factory (element);
if (!factory) {
gst_object_unref (element);
return NULL;
}
if (!gst_plugin_feature_get_name (factory)) {
gst_object_unref (element);
return NULL;
}
launch_line = g_string_new (gst_plugin_feature_get_name (factory));
pureelement = gst_element_factory_create (factory, NULL);
/* get paramspecs and show non-default properties */
properties =
g_object_class_list_properties (G_OBJECT_GET_CLASS (element),
&number_of_properties);
if (properties) {
for (i = 0; i < number_of_properties; i++) {
gint j;
gboolean ignore = FALSE;
property = properties[i];
/* skip some properties */
if ((property->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE)
continue;
for (j = 0; ignored_propnames[j]; j++)
if (!g_strcmp0 (ignored_propnames[j], property->name))
ignore = TRUE;
if (ignore)
continue;
/* Can't use _param_value_defaults () because sub-classes modify the
* values already.
*/
g_value_init (&value, property->value_type);
g_value_init (&pvalue, property->value_type);
g_object_get_property (G_OBJECT (element), property->name, &value);
g_object_get_property (G_OBJECT (pureelement), property->name, &pvalue);
if (gst_value_compare (&value, &pvalue) != GST_VALUE_EQUAL) {
gchar *valuestr = gst_value_serialize (&value);
if (!valuestr) {
GST_WARNING ("Could not serialize property %s:%s",
GST_OBJECT_NAME (element), property->name);
g_free (valuestr);
goto next;
}
g_string_append_printf (launch_line, " %s=%s",
property->name, valuestr);
g_free (valuestr);
}
next:
g_value_unset (&value);
g_value_unset (&pvalue);
}
g_free (properties);
}
gst_object_unref (element);
gst_object_unref (pureelement);
return g_string_free (launch_line, FALSE);
}
static gboolean
print_structure_field (GQuark field_id, const GValue * value,
gpointer user_data)
{
gchar *val;
if (G_VALUE_HOLDS_UINT (value)) {
val = g_strdup_printf ("%u (0x%08x)", g_value_get_uint (value),
g_value_get_uint (value));
} else {
val = gst_value_serialize (value);
}
if (val != NULL)
g_print ("\n\t\t%s = %s", g_quark_to_string (field_id), val);
else
g_print ("\n\t\t%s - could not serialise field of type %s",
g_quark_to_string (field_id), G_VALUE_TYPE_NAME (value));
g_free (val);
return TRUE;
}
static void
device_added (GstDevice * device)
{
gchar *device_class, *str, *name;
GstCaps *caps;
GstStructure *props;
guint i, size = 0;
caps = gst_device_get_caps (device);
if (caps != NULL)
size = gst_caps_get_size (caps);
name = gst_device_get_display_name (device);
device_class = gst_device_get_device_class (device);
props = gst_device_get_properties (device);
g_print ("\nDevice found:\n\n");
g_print ("\tname : %s\n", name);
g_print ("\tclass : %s\n", device_class);
for (i = 0; i < size; ++i) {
GstStructure *s = gst_caps_get_structure (caps, i);
str = gst_structure_to_string (s);
g_print ("\t%s %s\n", (i == 0) ? "caps :" : " ", str);
g_free (str);
}
if (props) {
g_print ("\tproperties:");
gst_structure_foreach (props, print_structure_field, NULL);
gst_structure_free (props);
g_print ("\n");
}
str = get_launch_line (device);
if (gst_device_has_classes (device, "Source"))
g_print ("\tgst-launch-1.0 %s ! ...\n", str);
if (gst_device_has_classes (device, "Sink"))
g_print ("\tgst-launch-1.0 ... ! %s\n", str);
g_free (str);
g_print ("\n");
g_free (name);
g_free (device_class);
if (caps != NULL)
gst_caps_unref (caps);
}
static void
device_removed (GstDevice * device)
{
gchar *name;
name = gst_device_get_display_name (device);
g_print ("Device removed:\n");
g_print ("\tname : %s\n", name);
g_free (name);
}
static gboolean
bus_msg_handler (GstBus * bus, GstMessage * msg, gpointer user_data)
{
GstDevice *device;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_DEVICE_ADDED:
gst_message_parse_device_added (msg, &device);
device_added (device);
gst_object_unref (device);
break;
case GST_MESSAGE_DEVICE_REMOVED:
gst_message_parse_device_removed (msg, &device);
device_removed (device);
gst_object_unref (device);
break;
default:
g_print ("%s message\n", GST_MESSAGE_TYPE_NAME (msg));
break;
}
return TRUE;
}
int
main (int argc, char **argv)
{
gboolean print_version = FALSE;
GError *err = NULL;
gchar **arg, **args = NULL;
gboolean follow = FALSE;
GOptionContext *ctx;
GOptionEntry options[] = {
{"version", 0, 0, G_OPTION_ARG_NONE, &print_version,
N_("Print version information and exit"), NULL},
{"follow", 'f', 0, G_OPTION_ARG_NONE, &follow,
N_("Don't exit after showing the initial device list, but wait "
"for devices to added/removed."), NULL},
{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL},
{NULL}
};
GTimer *timer;
DevMonApp app;
GstBus *bus;
GList *devices;
setlocale (LC_ALL, "");
#ifdef ENABLE_NLS
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#endif
g_set_prgname ("gst-device-monitor-" GST_API_VERSION);
ctx = g_option_context_new ("[DEVICE_CLASSES[:FILTER_CAPS]] "
"[DEVICE_CLASSES[:FILTER_CAPS]] …");
g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
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_option_context_free (ctx);
g_clear_error (&err);
return 1;
}
g_option_context_free (ctx);
GST_DEBUG_CATEGORY_INIT (devmon_debug, "device-monitor", 0,
"gst-device-monitor");
if (print_version) {
gchar *version_str;
version_str = gst_version_string ();
g_print ("%s version %s\n", g_get_prgname (), PACKAGE_VERSION);
g_print ("%s\n", version_str);
g_print ("%s\n", GST_PACKAGE_ORIGIN);
g_free (version_str);
return 0;
}
app.loop = g_main_loop_new (NULL, FALSE);
app.monitor = gst_device_monitor_new ();
bus = gst_device_monitor_get_bus (app.monitor);
app.bus_watch_id = gst_bus_add_watch (bus, bus_msg_handler, &app);
gst_object_unref (bus);
/* process optional remaining arguments in the form
* DEVICE_CLASSES or DEVICE_CLASSES:FILTER_CAPS */
for (arg = args; arg != NULL && *arg != NULL; ++arg) {
gchar **filters = g_strsplit (*arg, ":", -1);
if (filters != NULL && filters[0] != NULL) {
GstCaps *caps = NULL;
if (filters[1] != NULL) {
caps = gst_caps_from_string (filters[1]);
if (caps == NULL)
g_warning ("Couldn't parse device filter caps '%s'", filters[1]);
}
gst_device_monitor_add_filter (app.monitor, filters[0], caps);
if (caps)
gst_caps_unref (caps);
g_strfreev (filters);
}
}
g_strfreev (args);
g_print ("Probing devices...\n\n");
timer = g_timer_new ();
if (!gst_device_monitor_start (app.monitor)) {
g_printerr ("Failed to start device monitor!\n");
return -1;
}
GST_INFO ("Took %.2f seconds", g_timer_elapsed (timer, NULL));
devices = gst_device_monitor_get_devices (app.monitor);
if (devices != NULL) {
while (devices != NULL) {
GstDevice *device = devices->data;
device_added (device);
gst_object_unref (device);
devices = g_list_delete_link (devices, devices);
}
} else {
g_print ("No devices found!\n");
}
if (follow) {
g_print ("Monitoring devices, waiting for devices to be removed or "
"new devices to be added...\n");
g_main_loop_run (app.loop);
}
gst_device_monitor_stop (app.monitor);
gst_object_unref (app.monitor);
g_source_remove (app.bus_watch_id);
g_main_loop_unref (app.loop);
g_timer_destroy (timer);
return 0;
}